Пример #1
0
    def test_copy_ownership_windows(self):
        system = win32security.ConvertStringSidToSid(SYSTEM_SID)
        security = win32security.SECURITY_ATTRIBUTES().SECURITY_DESCRIPTOR
        security.SetSecurityDescriptorOwner(system, False)

        with mock.patch('win32security.GetFileSecurity') as mock_get:
            with mock.patch('win32security.SetFileSecurity') as mock_set:
                mock_get.return_value = security
                filesystem.copy_ownership_and_apply_mode('dummy',
                                                         self.probe_path,
                                                         0o700,
                                                         copy_user=True,
                                                         copy_group=False)

        self.assertEqual(mock_set.call_count, 2)

        first_call = mock_set.call_args_list[0]
        security = first_call[0][2]
        self.assertEqual(system, security.GetSecurityDescriptorOwner())

        second_call = mock_set.call_args_list[1]
        security = second_call[0][2]
        dacl = security.GetSecurityDescriptorDacl()
        everybody = win32security.ConvertStringSidToSid(EVERYBODY_SID)
        self.assertTrue(dacl.GetAceCount())
        self.assertFalse([
            dacl.GetAce(index) for index in range(0, dacl.GetAceCount())
            if dacl.GetAce(index)[2] == everybody
        ])
Пример #2
0
    def _create_challenge_dirs(self):
        path_map = self.conf("map")
        if not path_map:
            raise errors.PluginError(
                "Missing parts of webroot configuration; please set either "
                "--webroot-path and --domains, or --webroot-map. Run with "
                " --help webroot for examples.")
        for name, path in path_map.items():
            self.full_roots[name] = os.path.join(path, os.path.normcase(
                challenges.HTTP01.URI_ROOT_PATH))
            logger.debug("Creating root challenges validation dir at %s",
                         self.full_roots[name])

            # Change the permissions to be writable (GH #1389)
            # Umask is used instead of chmod to ensure the client can also
            # run as non-root (GH #1795)
            old_umask = filesystem.umask(0o022)
            try:
                # We ignore the last prefix in the next iteration,
                # as it does not correspond to a folder path ('/' or 'C:')
                for prefix in sorted(util.get_prefixes(self.full_roots[name])[:-1], key=len):
                    if os.path.isdir(prefix):
                        # Don't try to create directory if it already exists, as some filesystems
                        # won't reliably raise EEXIST or EISDIR if directory exists.
                        continue
                    try:
                        # Set owner as parent directory if possible, apply mode for Linux/Windows.
                        # For Linux, this is coupled with the "umask" call above because
                        # os.mkdir's "mode" parameter may not always work:
                        # https://docs.python.org/3/library/os.html#os.mkdir
                        filesystem.mkdir(prefix, 0o755)
                        self._created_dirs.append(prefix)
                        try:
                            filesystem.copy_ownership_and_apply_mode(
                                path, prefix, 0o755, copy_user=True, copy_group=True)
                        except (OSError, AttributeError) as exception:
                            logger.warning("Unable to change owner and uid of webroot directory")
                            logger.debug("Error was: %s", exception)
                    except OSError as exception:
                        raise errors.PluginError(
                            "Couldn't create root for {0} http-01 "
                            "challenge responses: {1}".format(name, exception))
            finally:
                filesystem.umask(old_umask)

            # On Windows, generate a local web.config file that allows IIS to serve expose
            # challenge files despite the fact they do not have a file extension.
            if not filesystem.POSIX_MODE:
                web_config_path = os.path.join(self.full_roots[name], "web.config")
                if os.path.exists(web_config_path):
                    logger.info("A web.config file has not been created in "
                                "%s because another one already exists.", self.full_roots[name])
                    continue
                logger.info("Creating a web.config file in %s to allow IIS "
                            "to serve challenge files.", self.full_roots[name])
                with safe_open(web_config_path, mode="w", chmod=0o644) as web_config:
                    web_config.write(_WEB_CONFIG_CONTENT)
Пример #3
0
    def test_copy_ownership_and_apply_mode_linux(self):
        with mock.patch('os.chown') as mock_chown:
            with mock.patch('os.chmod') as mock_chmod:
                with mock.patch('os.stat') as mock_stat:
                    mock_stat.return_value.st_uid = 50
                    mock_stat.return_value.st_gid = 51
                    filesystem.copy_ownership_and_apply_mode(
                        'dummy', self.probe_path, 0o700, copy_user=True, copy_group=True)

        mock_chown.assert_called_once_with(self.probe_path, 50, 51)
        mock_chmod.assert_called_once_with(self.probe_path, 0o700)
Пример #4
0
    def _create_challenge_dirs(self):
        path_map = self.conf("map")
        if not path_map:
            raise errors.PluginError(
                "Missing parts of webroot configuration; please set either "
                "--webroot-path and --domains, or --webroot-map. Run with "
                " --help webroot for examples.")
        for name, path in path_map.items():
            self.full_roots[name] = os.path.join(
                path, challenges.HTTP01.URI_ROOT_PATH)
            logger.debug("Creating root challenges validation dir at %s",
                         self.full_roots[name])

            # Change the permissions to be writable (GH #1389)
            # Umask is used instead of chmod to ensure the client can also
            # run as non-root (GH #1795)
            old_umask = os.umask(0o022)
            try:
                # We ignore the last prefix in the next iteration,
                # as it does not correspond to a folder path ('/' or 'C:')
                for prefix in sorted(util.get_prefixes(
                        self.full_roots[name])[:-1],
                                     key=len):
                    try:
                        # Set owner as parent directory if possible, apply mode for Linux/Windows.
                        # For Linux, this is coupled with the "umask" call above because
                        # os.mkdir's "mode" parameter may not always work:
                        # https://docs.python.org/3/library/os.html#os.mkdir
                        filesystem.mkdir(prefix, 0o755)
                        self._created_dirs.append(prefix)
                        try:
                            filesystem.copy_ownership_and_apply_mode(
                                path,
                                prefix,
                                0o755,
                                copy_user=True,
                                copy_group=True)
                        except (OSError, AttributeError) as exception:
                            logger.info(
                                "Unable to change owner and uid of webroot directory"
                            )
                            logger.debug("Error was: %s", exception)
                    except OSError as exception:
                        if exception.errno not in (errno.EEXIST, errno.EISDIR):
                            raise errors.PluginError(
                                "Couldn't create root for {0} http-01 "
                                "challenge responses: {1}".format(
                                    name, exception))
            finally:
                os.umask(old_umask)
Пример #5
0
    def save_successor(self, prior_version, new_cert, new_privkey, new_chain,
                       cli_config):
        """Save new cert and chain as a successor of a prior version.

        Returns the new version number that was created.

        .. note:: this function does NOT update links to deploy this
                  version

        :param int prior_version: the old version to which this version
            is regarded as a successor (used to choose a privkey, if the
            key has not changed, but otherwise this information is not
            permanently recorded anywhere)
        :param bytes new_cert: the new certificate, in PEM format
        :param bytes new_privkey: the new private key, in PEM format,
            or ``None``, if the private key has not changed
        :param bytes new_chain: the new chain, in PEM format
        :param .NamespaceConfig cli_config: parsed command line
            arguments

        :returns: the new version number that was created
        :rtype: int

        """
        # XXX: assumes official archive location rather than examining links
        # XXX: consider using os.open for availability of os.O_EXCL
        # XXX: ensure file permissions are correct; also create directories
        #      if needed (ensuring their permissions are correct)
        # Figure out what the new version is and hence where to save things

        self.cli_config = cli_config
        target_version = self.next_free_version()
        target = {
            kind: os.path.join(self.archive_dir,
                               "{0}{1}.pem".format(kind, target_version))
            for kind in ALL_FOUR
        }

        old_privkey = os.path.join(self.archive_dir,
                                   "privkey{0}.pem".format(prior_version))

        # Distinguish the cases where the privkey has changed and where it
        # has not changed (in the latter case, making an appropriate symlink
        # to an earlier privkey version)
        if new_privkey is None:
            # The behavior below keeps the prior key by creating a new
            # symlink to the old key or the target of the old key symlink.
            if os.path.islink(old_privkey):
                old_privkey = filesystem.readlink(old_privkey)
            else:
                old_privkey = "privkey{0}.pem".format(prior_version)
            logger.debug("Writing symlink to old private key, %s.",
                         old_privkey)
            os.symlink(old_privkey, target["privkey"])
        else:
            with util.safe_open(target["privkey"],
                                "wb",
                                chmod=BASE_PRIVKEY_MODE) as f:
                logger.debug("Writing new private key to %s.",
                             target["privkey"])
                f.write(new_privkey)
            # Preserve gid and (mode & MASK_FOR_PRIVATE_KEY_PERMISSIONS)
            # from previous privkey in this lineage.
            mode = filesystem.compute_private_key_mode(old_privkey,
                                                       BASE_PRIVKEY_MODE)
            filesystem.copy_ownership_and_apply_mode(old_privkey,
                                                     target["privkey"],
                                                     mode,
                                                     copy_user=False,
                                                     copy_group=True)

        # Save everything else
        with open(target["cert"], "wb") as f:
            logger.debug("Writing certificate to %s.", target["cert"])
            f.write(new_cert)
        with open(target["chain"], "wb") as f:
            logger.debug("Writing chain to %s.", target["chain"])
            f.write(new_chain)
        with open(target["fullchain"], "wb") as f:
            logger.debug("Writing full chain to %s.", target["fullchain"])
            f.write(new_cert + new_chain)

        symlinks = {kind: self.configuration[kind] for kind in ALL_FOUR}
        # Update renewal config file
        self.configfile = update_configuration(self.lineagename,
                                               self.archive_dir, symlinks,
                                               cli_config)
        self.configuration = config_with_defaults(self.configfile)

        return target_version