def setUp(self): super(MakeOrVerifyDirTest, self).setUp() self.path = os.path.join(self.tempdir, "foo") os.mkdir(self.path, 0o600) self.uid = compat.os_geteuid()
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, compat.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)
def setup_log_file_handler(config, logfile, fmt): """Setup file debug logging. :param certbot.interface.IConfig config: Configuration object :param str logfile: basename for the log file :param str fmt: logging format string :returns: file handler and absolute path to the log file :rtype: tuple """ # TODO: logs might contain sensitive data such as contents of the # private key! #525 util.set_up_core_dir( config.logs_dir, 0o700, compat.os_geteuid(), config.strict_permissions) log_file_path = os.path.join(config.logs_dir, logfile) try: handler = logging.handlers.RotatingFileHandler( log_file_path, maxBytes=2 ** 20, backupCount=config.max_log_backups) except IOError as error: raise errors.Error(util.PERM_ERR_FMT.format(error)) # rotate on each invocation, rollover only possible when maxBytes # is nonzero and backupCount is nonzero, so we set maxBytes as big # as possible not to overrun in single CLI invocation (1MB). handler.doRollover() # TODO: creates empty letsencrypt.log.1 file handler.setLevel(logging.DEBUG) handler_formatter = logging.Formatter(fmt=fmt) handler.setFormatter(handler_formatter) return handler, log_file_path
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, compat.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)
def init_save_csr(privkey, names, path): """Initialize a CSR with the given private key. :param privkey: Key to include in the CSR :type privkey: :class:`certbot.util.Key` :param set names: `str` names to include in the CSR :param str path: Certificate save directory. :returns: CSR :rtype: :class:`certbot.util.CSR` """ config = zope.component.getUtility(interfaces.IConfig) csr_pem = acme_crypto_util.make_csr(privkey.pem, names, must_staple=config.must_staple) # Save CSR util.make_or_verify_dir(path, 0o755, compat.os_geteuid(), config.strict_permissions) csr_f, csr_filename = util.unique_file( os.path.join(path, "csr-certbot.pem"), 0o644, "wb") with csr_f: csr_f.write(csr_pem) logger.debug("Creating CSR: %s", csr_filename) return util.CSR(csr_filename, csr_pem, "pem")
def init_save_key(key_size, key_dir, keyname="key-certbot.pem"): """Initializes and saves a privkey. Inits key and saves it in PEM format on the filesystem. .. note:: keyname is the attempted filename, it may be different if a file already exists at the path. :param int key_size: RSA key size in bits :param str key_dir: Key save directory. :param str keyname: Filename of key :returns: Key :rtype: :class:`certbot.util.Key` :raises ValueError: If unable to generate the key given key_size. """ try: key_pem = make_key(key_size) except ValueError as err: logger.error("", exc_info=True) raise err config = zope.component.getUtility(interfaces.IConfig) # Save file util.make_or_verify_dir(key_dir, 0o700, compat.os_geteuid(), config.strict_permissions) key_f, key_path = util.unique_file(os.path.join(key_dir, keyname), 0o600, "wb") with key_f: key_f.write(key_pem) logger.debug("Generating key (%d bits): %s", key_size, key_path) return util.Key(key_path, key_pem)
def init_save_key(key_size, key_dir, keyname="key-certbot.pem"): """Initializes and saves a privkey. Inits key and saves it in PEM format on the filesystem. .. note:: keyname is the attempted filename, it may be different if a file already exists at the path. :param int key_size: RSA key size in bits :param str key_dir: Key save directory. :param str keyname: Filename of key :returns: Key :rtype: :class:`certbot.util.Key` :raises ValueError: If unable to generate the key given key_size. """ try: key_pem = make_key(key_size) except ValueError as err: logger.error("", exc_info=True) raise err config = zope.component.getUtility(interfaces.IConfig) # Save file util.make_or_verify_dir(key_dir, 0o700, compat.os_geteuid(), config.strict_permissions) key_f, key_path = util.unique_file( os.path.join(key_dir, keyname), 0o600, "wb") with key_f: key_f.write(key_pem) logger.debug("Generating key (%d bits): %s", key_size, key_path) return util.Key(key_path, key_pem)
def init_save_csr(privkey, names, path): """Initialize a CSR with the given private key. :param privkey: Key to include in the CSR :type privkey: :class:`certbot.util.Key` :param set names: `str` names to include in the CSR :param str path: Certificate save directory. :returns: CSR :rtype: :class:`certbot.util.CSR` """ config = zope.component.getUtility(interfaces.IConfig) csr_pem = acme_crypto_util.make_csr( privkey.pem, names, must_staple=config.must_staple) # Save CSR util.make_or_verify_dir(path, 0o755, compat.os_geteuid(), config.strict_permissions) csr_f, csr_filename = util.unique_file( os.path.join(path, "csr-certbot.pem"), 0o644, "wb") with csr_f: csr_f.write(csr_pem) logger.debug("Creating CSR: %s", csr_filename) return util.CSR(csr_filename, csr_pem, "pem")
def _search_lineages(cli_config, func, initial_rv, *args): """Iterate func over unbroken lineages, allowing custom return conditions. Allows flexible customization of return values, including multiple return values and complex checks. :param `configuration.NamespaceConfig` cli_config: parsed command line arguments :param function func: function used while searching over lineages :param initial_rv: initial return value of the function (any type) :returns: Whatever was specified by `func` if a match is found. """ configs_dir = cli_config.renewal_configs_dir # Verify the directory is there util.make_or_verify_dir(configs_dir, mode=0o755, uid=compat.os_geteuid()) rv = initial_rv for renewal_file in storage.renewal_conf_files(cli_config): try: candidate_lineage = storage.RenewableCert(renewal_file, cli_config) except (errors.CertStorageError, IOError): logger.debug("Renewal conf file %s is broken. Skipping.", renewal_file) logger.debug("Traceback was:\n%s", traceback.format_exc()) continue rv = func(candidate_lineage, rv, *args) return rv
def _get_cp_dir(self, temporary): """Return the proper reverter directory.""" if temporary: cp_dir = self.config.temp_checkpoint_dir else: cp_dir = self.config.in_progress_dir util.make_or_verify_dir( cp_dir, constants.CONFIG_DIRS_MODE, compat.os_geteuid(), self.config.strict_permissions) return cp_dir
def make_or_verify_needed_dirs(config): """Create or verify existence of config, work, and hook directories. :param config: Configuration object :type config: interfaces.IConfig :returns: `None` :rtype: None """ util.set_up_core_dir(config.config_dir, constants.CONFIG_DIRS_MODE, compat.os_geteuid(), config.strict_permissions) util.set_up_core_dir(config.work_dir, constants.CONFIG_DIRS_MODE, compat.os_geteuid(), config.strict_permissions) hook_dirs = (config.renewal_pre_hooks_dir, config.renewal_deploy_hooks_dir, config.renewal_post_hooks_dir,) for hook_dir in hook_dirs: util.make_or_verify_dir(hook_dir, uid=compat.os_geteuid(), strict=config.strict_permissions)
def lineage_for_certname(cli_config, certname): """Find a lineage object with name certname.""" configs_dir = cli_config.renewal_configs_dir # Verify the directory is there util.make_or_verify_dir(configs_dir, mode=0o755, uid=compat.os_geteuid()) try: renewal_file = storage.renewal_file_for_certname(cli_config, certname) except errors.CertStorageError: return None try: return storage.RenewableCert(renewal_file, cli_config) except (errors.CertStorageError, IOError): logger.debug("Renewal conf file %s is broken.", renewal_file) logger.debug("Traceback was:\n%s", traceback.format_exc()) return None
def _verify_setup(self): """Verify the setup to ensure safe operating environment. Make sure that files/directories are setup with appropriate permissions Aim for defensive coding... make sure all input files have permissions of root. """ uid = compat.os_geteuid() util.make_or_verify_dir( self.config.work_dir, core_constants.CONFIG_DIRS_MODE, uid) util.make_or_verify_dir( self.config.backup_dir, core_constants.CONFIG_DIRS_MODE, uid) util.make_or_verify_dir( self.config.config_dir, core_constants.CONFIG_DIRS_MODE, uid)
def _add_to_checkpoint_dir(self, cp_dir, save_files, save_notes): """Add save files to checkpoint directory. :param str cp_dir: Checkpoint directory filepath :param set save_files: set of files to save :param str save_notes: notes about changes made during the save :raises IOError: if unable to open cp_dir + FILEPATHS file :raises .ReverterError: if unable to add checkpoint """ util.make_or_verify_dir(cp_dir, constants.CONFIG_DIRS_MODE, compat.os_geteuid(), self.config.strict_permissions) op_fd, existing_filepaths = self._read_and_append( os.path.join(cp_dir, "FILEPATHS")) idx = len(existing_filepaths) for filename in save_files: # No need to copy/index already existing files # The oldest copy already exists in the directory... if filename not in existing_filepaths: # Tag files with index so multiple files can # have the same filename logger.debug("Creating backup of %s", filename) try: shutil.copy2( filename, os.path.join( cp_dir, os.path.basename(filename) + "_" + str(idx))) op_fd.write(filename + os.linesep) # http://stackoverflow.com/questions/4726260/effective-use-of-python-shutil-copy2 except IOError: op_fd.close() logger.error("Unable to add file %s to checkpoint %s", filename, cp_dir) raise errors.ReverterError( "Unable to add file {0} to checkpoint " "{1}".format(filename, cp_dir)) idx += 1 op_fd.close() with open(os.path.join(cp_dir, "CHANGES_SINCE"), "a") as notes_fd: notes_fd.write(save_notes)
def _add_to_checkpoint_dir(self, cp_dir, save_files, save_notes): """Add save files to checkpoint directory. :param str cp_dir: Checkpoint directory filepath :param set save_files: set of files to save :param str save_notes: notes about changes made during the save :raises IOError: if unable to open cp_dir + FILEPATHS file :raises .ReverterError: if unable to add checkpoint """ util.make_or_verify_dir( cp_dir, constants.CONFIG_DIRS_MODE, compat.os_geteuid(), self.config.strict_permissions) op_fd, existing_filepaths = self._read_and_append( os.path.join(cp_dir, "FILEPATHS")) idx = len(existing_filepaths) for filename in save_files: # No need to copy/index already existing files # The oldest copy already exists in the directory... if filename not in existing_filepaths: # Tag files with index so multiple files can # have the same filename logger.debug("Creating backup of %s", filename) try: shutil.copy2(filename, os.path.join( cp_dir, os.path.basename(filename) + "_" + str(idx))) op_fd.write(filename + os.linesep) # http://stackoverflow.com/questions/4726260/effective-use-of-python-shutil-copy2 except IOError: op_fd.close() logger.error( "Unable to add file %s to checkpoint %s", filename, cp_dir) raise errors.ReverterError( "Unable to add file {0} to checkpoint " "{1}".format(filename, cp_dir)) idx += 1 op_fd.close() with open(os.path.join(cp_dir, "CHANGES_SINCE"), "a") as notes_fd: notes_fd.write(save_notes)
def save_certificate(self, cert_pem, chain_pem, cert_path, chain_path, fullchain_path): """Saves the certificate received from the ACME server. :param str cert_pem: :param str chain_pem: :param str cert_path: Candidate path to a certificate. :param str chain_path: Candidate path to a certificate chain. :param str fullchain_path: Candidate path to a full cert chain. :returns: cert_path, chain_path, and fullchain_path as absolute paths to the actual files :rtype: `tuple` of `str` :raises IOError: If unable to find room to write the cert files """ for path in cert_path, chain_path, fullchain_path: util.make_or_verify_dir( os.path.dirname(path), 0o755, compat.os_geteuid(), self.config.strict_permissions) cert_file, abs_cert_path = _open_pem_file('cert_path', cert_path) try: cert_file.write(cert_pem) finally: cert_file.close() logger.info("Server issued certificate; certificate written to %s", abs_cert_path) chain_file, abs_chain_path =\ _open_pem_file('chain_path', chain_path) fullchain_file, abs_fullchain_path =\ _open_pem_file('fullchain_path', fullchain_path) _save_chain(chain_pem, chain_file) _save_chain(cert_pem + chain_pem, fullchain_file) return abs_cert_path, abs_chain_path, abs_fullchain_path
def save_certificate(self, cert_pem, chain_pem, cert_path, chain_path, fullchain_path): """Saves the certificate received from the ACME server. :param str cert_pem: :param str chain_pem: :param str cert_path: Candidate path to a certificate. :param str chain_path: Candidate path to a certificate chain. :param str fullchain_path: Candidate path to a full cert chain. :returns: cert_path, chain_path, and fullchain_path as absolute paths to the actual files :rtype: `tuple` of `str` :raises IOError: If unable to find room to write the cert files """ for path in cert_path, chain_path, fullchain_path: util.make_or_verify_dir(os.path.dirname(path), 0o755, compat.os_geteuid(), self.config.strict_permissions) cert_file, abs_cert_path = _open_pem_file('cert_path', cert_path) try: cert_file.write(cert_pem) finally: cert_file.close() logger.info("Server issued certificate; certificate written to %s", abs_cert_path) chain_file, abs_chain_path =\ _open_pem_file('chain_path', chain_path) fullchain_file, abs_fullchain_path =\ _open_pem_file('fullchain_path', fullchain_path) _save_chain(chain_pem, chain_file) _save_chain(cert_pem + chain_pem, fullchain_file) return abs_cert_path, abs_chain_path, abs_fullchain_path
def test_success(self, mock_lock): new_dir = os.path.join(self.tempdir, 'new') self._call(new_dir, 0o700, compat.os_geteuid(), False) self.assertTrue(os.path.exists(new_dir)) self.assertEqual(mock_lock.call_count, 1)
def test_failure(self, mock_make_or_verify): mock_make_or_verify.side_effect = OSError self.assertRaises(errors.Error, self._call, self.tempdir, 0o700, compat.os_geteuid(), False)
def setUp(self): super(CheckPermissionsTest, self).setUp() self.uid = compat.os_geteuid()
def __init__(self, config): self.config = config util.make_or_verify_dir(config.accounts_dir, 0o700, compat.os_geteuid(), self.config.strict_permissions)
def __init__(self, config): self.config = config util.make_or_verify_dir( config.backup_dir, constants.CONFIG_DIRS_MODE, compat.os_geteuid(), self.config.strict_permissions)
def test_permissions(self): self.assertTrue( util.check_permissions(self.handler.path, 0o600, compat.os_geteuid()))