Пример #1
0
def create_hook(file_path):
    """Creates an executable file at the specified path.

    :param str file_path: path to create the file at

    """
    util.safe_open(file_path, mode="w", chmod=0o744).close()
Пример #2
0
    def test_has_same_ownership(self):
        path1 = os.path.join(self.tempdir, 'test1')
        path2 = os.path.join(self.tempdir, 'test2')

        util.safe_open(path1, 'w').close()
        util.safe_open(path2, 'w').close()

        self.assertTrue(filesystem.has_same_ownership(path1, path2))
Пример #3
0
    def test_valid_file_with_unsafe_permissions(self):
        log = self._MockLoggingHandler()
        dns_common.logger.addHandler(log)

        path = os.path.join(self.tempdir, 'too-permissive-file.ini')
        util.safe_open(path, "wb", 0o744).close()

        dns_common.CredentialsConfiguration(path)

        self.assertEqual(1, len([_ for _ in log.messages['warning'] if _.startswith("Unsafe")]))
Пример #4
0
 def _save(self, account, acme, regr_only):
     account_dir_path = self._account_dir_path(account.id)
     util.make_or_verify_dir(account_dir_path, 0o700, misc.os_geteuid(),
                             self.config.strict_permissions)
     try:
         with open(self._regr_path(account_dir_path), "w") as regr_file:
             regr = account.regr
             # If we have a value for new-authz, save it for forwards
             # compatibility with older versions of Certbot. If we don't
             # have a value for new-authz, this is an ACMEv2 directory where
             # an older version of Certbot won't work anyway.
             if hasattr(acme.directory, "new-authz"):
                 regr = RegistrationResourceWithNewAuthzrURI(
                     new_authzr_uri=acme.directory.new_authz,
                     body={},
                     uri=regr.uri)
             else:
                 regr = messages.RegistrationResource(
                     body={},
                     uri=regr.uri)
             regr_file.write(regr.json_dumps())
         if not regr_only:
             with util.safe_open(self._key_path(account_dir_path),
                                 "w", chmod=0o400) as key_file:
                 key_file.write(account.key.json_dumps())
             with open(self._metadata_path(
                     account_dir_path), "w") as metadata_file:
                 metadata_file.write(account.meta.json_dumps())
     except IOError as error:
         raise errors.AccountStorageError(error)
Пример #5
0
 def _save(self, account, acme, regr_only):
     account_dir_path = self._account_dir_path(account.id)
     util.make_or_verify_dir(account_dir_path, 0o700, os.geteuid(),
                             self.config.strict_permissions)
     try:
         with open(self._regr_path(account_dir_path), "w") as regr_file:
             regr = account.regr
             # If we have a value for new-authz, save it for forwards
             # compatibility with older versions of Certbot. If we don't
             # have a value for new-authz, this is an ACMEv2 directory where
             # an older version of Certbot won't work anyway.
             if hasattr(acme.directory, "new-authz"):
                 regr = RegistrationResourceWithNewAuthzrURI(
                     new_authzr_uri=acme.directory.new_authz,
                     body={},
                     uri=regr.uri)
             else:
                 regr = messages.RegistrationResource(body={}, uri=regr.uri)
             regr_file.write(regr.json_dumps())
         if not regr_only:
             with util.safe_open(self._key_path(account_dir_path),
                                 "w",
                                 chmod=0o400) as key_file:
                 key_file.write(account.key.json_dumps())
             with open(self._metadata_path(account_dir_path),
                       "w") as metadata_file:
                 metadata_file.write(account.meta.json_dumps())
     except IOError as error:
         raise errors.AccountStorageError(error)
Пример #6
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)
Пример #7
0
 def __init__(self) -> None:
     self._workdir = tempfile.mkdtemp()
     self.path = os.path.join(self._workdir, 'log')
     stream = util.safe_open(self.path, mode='w', chmod=0o600)
     super().__init__(stream)
     # Super constructor assigns the provided stream object to self.stream.
     # Let's help mypy be aware of this by giving a type hint.
     self.stream: IO[str]
     self._delete = True
Пример #8
0
 def save(self, account):
     account_dir_path = self._account_dir_path(account.id)
     util.make_or_verify_dir(account_dir_path, 0o700, os.geteuid(),
                                self.config.strict_permissions)
     try:
         with open(self._regr_path(account_dir_path), "w") as regr_file:
             regr_file.write(account.regr.json_dumps())
         with util.safe_open(self._key_path(account_dir_path),
                                "w", chmod=0o400) as key_file:
             key_file.write(account.key.json_dumps())
         with open(self._metadata_path(account_dir_path), "w") as metadata_file:
             metadata_file.write(account.meta.json_dumps())
     except IOError as error:
         raise errors.AccountStorageError(error)
Пример #9
0
 def _save(self, account, regr_only):
     account_dir_path = self._account_dir_path(account.id)
     util.make_or_verify_dir(account_dir_path, 0o700, os.geteuid(),
                             self.config.strict_permissions)
     try:
         with open(self._regr_path(account_dir_path), "w") as regr_file:
             regr_file.write(account.regr.json_dumps())
         if not regr_only:
             with util.safe_open(self._key_path(account_dir_path),
                                 "w", chmod=0o400) as key_file:
                 key_file.write(account.key.json_dumps())
             with open(self._metadata_path(
                     account_dir_path), "w") as metadata_file:
                 metadata_file.write(account.meta.json_dumps())
     except IOError as error:
         raise errors.AccountStorageError(error)
Пример #10
0
def _open_pem_file(cli_arg_path, pem_path):
    """Open a pem file.

    If cli_arg_path was set by the client, open that.
    Otherwise, uniquify the file path.

    :param str cli_arg_path: the cli arg name, e.g. cert_path
    :param str pem_path: the pem file path to open

    :returns: a tuple of file object and its absolute file path

    """
    if cli.set_by_cli(cli_arg_path):
        return util.safe_open(pem_path, chmod=0o644, mode="wb"),\
            os.path.abspath(pem_path)
    uniq = util.unique_file(pem_path, 0o644, "wb")
    return uniq[0], os.path.abspath(uniq[1])
Пример #11
0
def _open_pem_file(cli_arg_path, pem_path):
    """Open a pem file.

    If cli_arg_path was set by the client, open that.
    Otherwise, uniquify the file path.

    :param str cli_arg_path: the cli arg name, e.g. cert_path
    :param str pem_path: the pem file path to open

    :returns: a tuple of file object and its absolute file path

    """
    if cli.set_by_cli(cli_arg_path):
        return util.safe_open(pem_path, chmod=0o644, mode="wb"),\
            os.path.abspath(pem_path)
    uniq = util.unique_file(pem_path, 0o644, "wb")
    return uniq[0], os.path.abspath(uniq[1])
Пример #12
0
    def _perform_single(self, achall):
        response, validation = achall.response_and_validation()

        root_path = self.full_roots[achall.domain]
        validation_path = self._get_validation_path(root_path, achall)
        logger.debug("Attempting to save validation to %s", validation_path)

        # Change permissions to be world-readable, owner-writable (GH #1795)
        old_umask = os.umask(0o022)

        try:
            with safe_open(validation_path, mode="wb", chmod=0o644) as validation_file:
                validation_file.write(validation.encode())
        finally:
            os.umask(old_umask)

        self.performed[root_path].add(achall)
        return response
Пример #13
0
    def _setup_challenge_cert(self, achall, cert_key=None):

        """Generate and write out challenge certificate."""
        cert_path = self.get_cert_path(achall)
        key_path = self.get_key_path(achall)
        # Register the path before you write out the file
        self.configurator.reverter.register_file_creation(True, key_path)
        self.configurator.reverter.register_file_creation(True, cert_path)

        response, (cert, key) = achall.response_and_validation(cert_key=cert_key)
        cert_pem = OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_PEM, cert)
        key_pem = OpenSSL.crypto.dump_privatekey(OpenSSL.crypto.FILETYPE_PEM, key)

        # Write out challenge cert and key
        with open(cert_path, "wb") as cert_chall_fd:
            cert_chall_fd.write(cert_pem)
        with util.safe_open(key_path, "wb", chmod=0o400) as key_file:
            key_file.write(key_pem)

        return response
Пример #14
0
 def _save(self, account, acme, regr_only):
     account_dir_path = self._account_dir_path(account.id)
     util.make_or_verify_dir(account_dir_path, 0o700, os.geteuid(),
                             self.config.strict_permissions)
     try:
         with open(self._regr_path(account_dir_path), "w") as regr_file:
             regr = account.regr
             with_uri = RegistrationResourceWithNewAuthzrURI(
                 new_authzr_uri=acme.directory.new_authz,
                 body=regr.body,
                 uri=regr.uri,
                 terms_of_service=regr.terms_of_service)
             regr_file.write(with_uri.json_dumps())
         if not regr_only:
             with util.safe_open(self._key_path(account_dir_path),
                                 "w", chmod=0o400) as key_file:
                 key_file.write(account.key.json_dumps())
             with open(self._metadata_path(
                     account_dir_path), "w") as metadata_file:
                 metadata_file.write(account.meta.json_dumps())
     except IOError as error:
         raise errors.AccountStorageError(error)
Пример #15
0
    def _setup_challenge_cert(self, achall, cert_key=None):
        """Generate and write out challenge certificate."""
        cert_path = self.get_cert_path(achall)
        key_path = self.get_key_path(achall)
        # Register the path before you write out the file
        self.configurator.reverter.register_file_creation(True, key_path)
        self.configurator.reverter.register_file_creation(True, cert_path)

        response, (cert,
                   key) = achall.response_and_validation(cert_key=cert_key)
        cert_pem = OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_PEM,
                                                   cert)
        key_pem = OpenSSL.crypto.dump_privatekey(OpenSSL.crypto.FILETYPE_PEM,
                                                 key)

        # Write out challenge cert and key
        with open(cert_path, "wb") as cert_chall_fd:
            cert_chall_fd.write(cert_pem)
        with util.safe_open(key_path, 'wb', chmod=0o400) as key_file:
            key_file.write(key_pem)

        return response
Пример #16
0
 def _save(self, account, acme, regr_only):
     account_dir_path = self._account_dir_path(account.id)
     util.make_or_verify_dir(account_dir_path, 0o700, os.geteuid(),
                             self.config.strict_permissions)
     try:
         with open(self._regr_path(account_dir_path), "w") as regr_file:
             regr = account.regr
             with_uri = RegistrationResourceWithNewAuthzrURI(
                 new_authzr_uri=acme.directory.new_authz,
                 body=regr.body,
                 uri=regr.uri,
                 terms_of_service=regr.terms_of_service)
             regr_file.write(with_uri.json_dumps())
         if not regr_only:
             with util.safe_open(self._key_path(account_dir_path),
                                 "w",
                                 chmod=0o400) as key_file:
                 key_file.write(account.key.json_dumps())
             with open(self._metadata_path(account_dir_path),
                       "w") as metadata_file:
                 metadata_file.write(account.meta.json_dumps())
     except IOError as error:
         raise errors.AccountStorageError(error)
Пример #17
0
 def _create(self, account, dir_path):
     # type: (Account, str) -> None
     with util.safe_open(self._key_path(dir_path), "w",
                         chmod=0o400) as key_file:
         key_file.write(account.key.json_dumps())
Пример #18
0
    def new_lineage(cls, lineagename, cert, privkey, chain, cli_config):
        """Create a new certificate lineage.

        Attempts to create a certificate lineage -- enrolled for
        potential future renewal -- with the (suggested) lineage name
        lineagename, and the associated cert, privkey, and chain (the
        associated fullchain will be created automatically). Optional
        configurator and renewalparams record the configuration that was
        originally used to obtain this cert, so that it can be reused
        later during automated renewal.

        Returns a new RenewableCert object referring to the created
        lineage. (The actual lineage name, as well as all the relevant
        file paths, will be available within this object.)

        :param str lineagename: the suggested name for this lineage
            (normally the current cert's first subject DNS name)
        :param str cert: the initial certificate version in PEM format
        :param str privkey: the private key in PEM format
        :param str chain: the certificate chain in PEM format
        :param .NamespaceConfig cli_config: parsed command line
            arguments

        :returns: the newly-created RenewalCert object
        :rtype: :class:`storage.renewableCert`

        """

        # Examine the configuration and find the new lineage's name
        for i in (cli_config.renewal_configs_dir,
                  cli_config.default_archive_dir, cli_config.live_dir):
            if not os.path.exists(i):
                filesystem.makedirs(i, 0o700)
                logger.debug("Creating directory %s.", i)
        config_file, config_filename = util.unique_lineage_name(
            cli_config.renewal_configs_dir, lineagename)
        base_readme_path = os.path.join(cli_config.live_dir, README)
        if not os.path.exists(base_readme_path):
            _write_live_readme_to(base_readme_path, is_base_dir=True)

        # Determine where on disk everything will go
        # lineagename will now potentially be modified based on which
        # renewal configuration file could actually be created
        lineagename = lineagename_for_filename(config_filename)
        archive = full_archive_path(None, cli_config, lineagename)
        live_dir = _full_live_path(cli_config, lineagename)
        if os.path.exists(archive) and (not os.path.isdir(archive)
                                        or os.listdir(archive)):
            config_file.close()
            raise errors.CertStorageError("archive directory exists for " +
                                          lineagename)
        if os.path.exists(live_dir) and (not os.path.isdir(live_dir)
                                         or os.listdir(live_dir)):
            config_file.close()
            raise errors.CertStorageError("live directory exists for " +
                                          lineagename)
        for i in (archive, live_dir):
            if not os.path.exists(i):
                filesystem.makedirs(i)
                logger.debug("Creating directory %s.", i)

        # Put the data into the appropriate files on disk
        target = {
            kind: os.path.join(live_dir, kind + ".pem")
            for kind in ALL_FOUR
        }
        archive_target = {
            kind: os.path.join(archive, kind + "1.pem")
            for kind in ALL_FOUR
        }
        for kind in ALL_FOUR:
            os.symlink(_relpath_from_file(archive_target[kind], target[kind]),
                       target[kind])
        with open(target["cert"], "wb") as f:
            logger.debug("Writing certificate to %s.", target["cert"])
            f.write(cert)
        with util.safe_open(archive_target["privkey"],
                            "wb",
                            chmod=BASE_PRIVKEY_MODE) as f:
            logger.debug("Writing private key to %s.", target["privkey"])
            f.write(privkey)
            # XXX: Let's make sure to get the file permissions right here
        with open(target["chain"], "wb") as f:
            logger.debug("Writing chain to %s.", target["chain"])
            f.write(chain)
        with open(target["fullchain"], "wb") as f:
            # assumes that OpenSSL.crypto.dump_certificate includes
            # ending newline character
            logger.debug("Writing full chain to %s.", target["fullchain"])
            f.write(cert + chain)

        # Write a README file to the live directory
        readme_path = os.path.join(live_dir, README)
        _write_live_readme_to(readme_path)

        # Document what we've done in a new renewal config file
        config_file.close()

        # Save only the config items that are relevant to renewal
        values = relevant_values(vars(cli_config.namespace))

        new_config = write_renewal_config(config_filename, config_filename,
                                          archive, target, values)
        return cls(new_config.filename, cli_config)
Пример #19
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
Пример #20
0
def _create_probe(tempdir):
    filesystem.chmod(tempdir, 0o744)
    probe_path = os.path.join(tempdir, 'probe')
    util.safe_open(probe_path, 'w', chmod=0o744).close()
    return probe_path
Пример #21
0
    def new_lineage(cls, lineagename, cert, privkey, chain, cli_config):
        # pylint: disable=too-many-locals
        """Create a new certificate lineage.

        Attempts to create a certificate lineage -- enrolled for
        potential future renewal -- with the (suggested) lineage name
        lineagename, and the associated cert, privkey, and chain (the
        associated fullchain will be created automatically). Optional
        configurator and renewalparams record the configuration that was
        originally used to obtain this cert, so that it can be reused
        later during automated renewal.

        Returns a new RenewableCert object referring to the created
        lineage. (The actual lineage name, as well as all the relevant
        file paths, will be available within this object.)

        :param str lineagename: the suggested name for this lineage
            (normally the current cert's first subject DNS name)
        :param str cert: the initial certificate version in PEM format
        :param str privkey: the private key in PEM format
        :param str chain: the certificate chain in PEM format
        :param .NamespaceConfig cli_config: parsed command line
            arguments

        :returns: the newly-created RenewalCert object
        :rtype: :class:`storage.renewableCert`

        """

        # Examine the configuration and find the new lineage's name
        for i in (cli_config.renewal_configs_dir, cli_config.default_archive_dir,
                  cli_config.live_dir):
            if not os.path.exists(i):
                os.makedirs(i, 0o700)
                logger.debug("Creating directory %s.", i)
        config_file, config_filename = util.unique_lineage_name(
            cli_config.renewal_configs_dir, lineagename)
        base_readme_path = os.path.join(cli_config.live_dir, README)
        if not os.path.exists(base_readme_path):
            _write_live_readme_to(base_readme_path, is_base_dir=True)

        # Determine where on disk everything will go
        # lineagename will now potentially be modified based on which
        # renewal configuration file could actually be created
        lineagename = lineagename_for_filename(config_filename)
        archive = full_archive_path(None, cli_config, lineagename)
        live_dir = _full_live_path(cli_config, lineagename)
        if os.path.exists(archive):
            config_file.close()
            raise errors.CertStorageError(
                "archive directory exists for " + lineagename)
        if os.path.exists(live_dir):
            config_file.close()
            raise errors.CertStorageError(
                "live directory exists for " + lineagename)
        os.mkdir(archive)
        os.mkdir(live_dir)
        logger.debug("Archive directory %s and live "
                     "directory %s created.", archive, live_dir)

        # Put the data into the appropriate files on disk
        target = dict([(kind, os.path.join(live_dir, kind + ".pem"))
                       for kind in ALL_FOUR])
        archive_target = dict([(kind, os.path.join(archive, kind + "1.pem"))
                               for kind in ALL_FOUR])
        for kind in ALL_FOUR:
            os.symlink(_relpath_from_file(archive_target[kind], target[kind]), target[kind])
        with open(target["cert"], "wb") as f:
            logger.debug("Writing certificate to %s.", target["cert"])
            f.write(cert)
        with util.safe_open(archive_target["privkey"], "wb", chmod=BASE_PRIVKEY_MODE) as f:
            logger.debug("Writing private key to %s.", target["privkey"])
            f.write(privkey)
            # XXX: Let's make sure to get the file permissions right here
        with open(target["chain"], "wb") as f:
            logger.debug("Writing chain to %s.", target["chain"])
            f.write(chain)
        with open(target["fullchain"], "wb") as f:
            # assumes that OpenSSL.crypto.dump_certificate includes
            # ending newline character
            logger.debug("Writing full chain to %s.", target["fullchain"])
            f.write(cert + chain)

        # Write a README file to the live directory
        readme_path = os.path.join(live_dir, README)
        _write_live_readme_to(readme_path)

        # Document what we've done in a new renewal config file
        config_file.close()

        # Save only the config items that are relevant to renewal
        values = relevant_values(vars(cli_config.namespace))

        new_config = write_renewal_config(config_filename, config_filename, archive,
            target, values)
        return cls(new_config.filename, cli_config)
Пример #22
0
 def __init__(self):
     self._workdir = tempfile.mkdtemp()
     self.path = os.path.join(self._workdir, 'log')
     stream = util.safe_open(self.path, mode='w', chmod=0o600)
     super().__init__(stream)
     self._delete = True
Пример #23
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 = dict(
            [(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 = os.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 & 074) from previous privkey in this lineage.
            old_mode = stat.S_IMODE(os.stat(old_privkey).st_mode) & \
                (stat.S_IRGRP | stat.S_IWGRP | stat.S_IXGRP | \
                 stat.S_IROTH)
            mode = BASE_PRIVKEY_MODE | old_mode
            os.chown(target["privkey"], -1, os.stat(old_privkey).st_gid)
            os.chmod(target["privkey"], mode)

        # 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 = dict((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