Beispiel #1
0
    def handle_csr(self, parsed_args):
        """
        Process a --csr flag. This needs to happen early enough that the
        webroot plugin can know about the calls to process_domain
        """
        if parsed_args.verb != "certonly":
            raise errors.Error("Currently, a CSR file may only be specified "
                               "when obtaining a new or replacement "
                               "via the certonly command. Please try the "
                               "certonly command instead.")

        try:
            csr = le_util.CSR(file=parsed_args.csr[0],
                              data=parsed_args.csr[1],
                              form="der")
            typ = OpenSSL.crypto.FILETYPE_ASN1
            domains = crypto_util.get_sans_from_csr(
                csr.data, OpenSSL.crypto.FILETYPE_ASN1)
        except OpenSSL.crypto.Error:
            try:
                e1 = traceback.format_exc()
                typ = OpenSSL.crypto.FILETYPE_PEM
                csr = le_util.CSR(file=parsed_args.csr[0],
                                  data=parsed_args.csr[1],
                                  form="pem")
                domains = crypto_util.get_sans_from_csr(csr.data, typ)
            except OpenSSL.crypto.Error:
                logger.debug("DER CSR parse error %s", e1)
                logger.debug("PEM CSR parse error %s", traceback.format_exc())
                raise errors.Error("Failed to parse CSR file: {0}".format(
                    parsed_args.csr[0]))
        for d in domains:
            process_domain(parsed_args, d)

        for d in domains:
            sanitised = le_util.enforce_domain_sanity(d)
            if d.lower() != sanitised:
                raise errors.ConfigurationError(
                    "CSR domain {0} needs to be sanitised to {1}.".format(
                        d, sanitised))

        if not domains:
            # TODO: add CN to domains instead:
            raise errors.Error(
                "Unfortunately, your CSR %s needs to have a SubjectAltName for every domain"
                % parsed_args.csr[0])

        parsed_args.actual_csr = (csr, typ)
        csr_domains, config_domains = set(domains), set(parsed_args.domains)
        if csr_domains != config_domains:
            raise errors.ConfigurationError(
                "Inconsistent domain requests:\nFrom the CSR: {0}\nFrom command line/config: {1}"
                .format(", ".join(csr_domains), ", ".join(config_domains)))
def init_save_csr(privkey, names, path, csrname="csr-letsencrypt.pem"):
    """Initialize a CSR with the given private key.

    :param privkey: Key to include in the CSR
    :type privkey: :class:`letsencrypt.le_util.Key`

    :param set names: `str` names to include in the CSR

    :param str path: Certificate save directory.

    :returns: CSR
    :rtype: :class:`letsencrypt.le_util.CSR`

    """
    csr_pem, csr_der = make_csr(privkey.pem, names)

    # Save CSR
    le_util.make_or_verify_dir(path, 0o755, os.geteuid())
    csr_f, csr_filename = le_util.unique_file(os.path.join(path, csrname),
                                              0o644)
    csr_f.write(csr_pem)
    csr_f.close()

    logger.info("Creating CSR: %s", csr_filename)

    return le_util.CSR(csr_filename, csr_der, "der")
Beispiel #3
0
    def test_obtain_certificate(self, mock_crypto_util):
        self._mock_obtain_certificate()

        csr = le_util.CSR(form="der", file=None, data=CSR_SAN)
        mock_crypto_util.init_save_csr.return_value = csr
        mock_crypto_util.init_save_key.return_value = mock.sentinel.key
        domains = ["example.com", "www.example.com"]

        # return_value is essentially set to (None, None) in
        # _mock_obtain_certificate(), which breaks this test.
        # Thus fixed by the next line.

        authzr = []

        for domain in domains:
            authzr.append(
                mock.MagicMock(
                    body=mock.MagicMock(
                        identifier=mock.MagicMock(
                            value=domain))))

        self.client.auth_handler.get_authorizations.return_value = authzr

        self.assertEqual(
            self.client.obtain_certificate(domains),
            (mock.sentinel.certr, mock.sentinel.chain, mock.sentinel.key, csr))

        mock_crypto_util.init_save_key.assert_called_once_with(
            self.config.rsa_key_size, self.config.key_dir)
        mock_crypto_util.init_save_csr.assert_called_once_with(
            mock.sentinel.key, domains, self.config.csr_dir)
        self._check_obtain_certificate()
Beispiel #4
0
def auth(args, config, plugins):
    """Authenticate & obtain cert, but do not install it."""
    # XXX: Update for renewer / RenewableCert

    if args.domains is not None and args.csr is not None:
        # TODO: --csr could have a priority, when --domains is
        # supplied, check if CSR matches given domains?
        return "--domains and --csr are mutually exclusive"

    authenticator = display_ops.pick_authenticator(config, args.authenticator,
                                                   plugins)
    if authenticator is None:
        return "Authenticator could not be determined"

    if args.installer is not None:
        installer = display_ops.pick_installer(config, args.installer, plugins)
    else:
        installer = None

    # TODO: Handle errors from _init_le_client?
    le_client = _init_le_client(args, config, authenticator, installer)

    if args.csr is not None:
        certr, chain = le_client.obtain_certificate_from_csr(
            le_util.CSR(file=args.csr[0], data=args.csr[1], form="der"))
        le_client.save_certificate(certr, chain, args.cert_path,
                                   args.chain_path)
    else:
        domains = _find_domains(args, installer)
        if not le_client.obtain_and_enroll_certificate(domains, authenticator,
                                                       installer, plugins):
            return "Certificate could not be obtained"
Beispiel #5
0
 def test_obtain_certificate_from_csr(self):
     self._mock_obtain_certificate()
     self.assertEqual(
         (mock.sentinel.certr, mock.sentinel.chain),
         self.client.obtain_certificate_from_csr(le_util.CSR(
             form="der", file=None, data=CSR_SAN)))
     self._check_obtain_certificate()
Beispiel #6
0
def auth(args, config, plugins):
    """Authenticate & obtain cert, but do not install it."""

    if args.domains is not None and args.csr is not None:
        # TODO: --csr could have a priority, when --domains is
        # supplied, check if CSR matches given domains?
        return "--domains and --csr are mutually exclusive"

    authenticator = display_ops.pick_authenticator(config, args.authenticator,
                                                   plugins)
    if authenticator is None:
        return "Authenticator could not be determined"

    if args.installer is not None:
        installer = display_ops.pick_installer(config, args.installer, plugins)
    else:
        installer = None

    # TODO: Handle errors from _init_le_client?
    le_client = _init_le_client(args, config, authenticator, installer)

    # This is a special case; cert and chain are simply saved
    if args.csr is not None:
        certr, chain = le_client.obtain_certificate_from_csr(
            le_util.CSR(file=args.csr[0], data=args.csr[1], form="der"))
        le_client.save_certificate(certr, chain, args.cert_path,
                                   args.chain_path)
        _report_new_cert(args.cert_path)
    else:
        domains = _find_domains(args, installer)
        _auth_from_domains(le_client, config, domains, plugins)
Beispiel #7
0
    def test_obtain_certificate_from_csr(self, mock_process_domain):
        self._mock_obtain_certificate()
        from letsencrypt import cli
        test_csr = le_util.CSR(form="der", file=None, data=CSR_SAN)
        mock_parsed_args = mock.MagicMock()
        with mock.patch("letsencrypt.client.le_util.CSR") as mock_CSR:
            mock_CSR.return_value = test_csr
            mock_parsed_args.domains = self.eg_domains[:]
            mock_parser = mock.MagicMock(cli.HelpfulArgumentParser)
            cli.HelpfulArgumentParser.handle_csr(mock_parser, mock_parsed_args)

            # make sure cli processing occurred
            cli_processed = (call[0][1]
                             for call in mock_process_domain.call_args_list)
            self.assertEqual(set(cli_processed),
                             set(("example.com", "www.example.com")))
            # Now provoke an inconsistent domains error...
            mock_parsed_args.domains.append("hippopotamus.io")
            self.assertRaises(errors.ConfigurationError,
                              cli.HelpfulArgumentParser.handle_csr,
                              mock_parser, mock_parsed_args)

            self.assertEqual((mock.sentinel.certr, mock.sentinel.chain),
                             self.client.obtain_certificate_from_csr(
                                 self.eg_domains, test_csr))
            # and that the cert was obtained correctly
            self._check_obtain_certificate()
Beispiel #8
0
    def handle_csr(self, parsed_args):
        """Process a --csr flag."""
        if parsed_args.verb != "certonly":
            raise errors.Error("Currently, a CSR file may only be specified "
                               "when obtaining a new or replacement "
                               "via the certonly command. Please try the "
                               "certonly command instead.")

        try:
            csr = le_util.CSR(file=parsed_args.csr[0],
                              data=parsed_args.csr[1],
                              form="der")
            typ = OpenSSL.crypto.FILETYPE_ASN1
            domains = crypto_util.get_sans_from_csr(
                csr.data, OpenSSL.crypto.FILETYPE_ASN1)
        except OpenSSL.crypto.Error:
            try:
                e1 = traceback.format_exc()
                typ = OpenSSL.crypto.FILETYPE_PEM
                csr = le_util.CSR(file=parsed_args.csr[0],
                                  data=parsed_args.csr[1],
                                  form="pem")
                domains = crypto_util.get_sans_from_csr(csr.data, typ)
            except OpenSSL.crypto.Error:
                logger.debug("DER CSR parse error %s", e1)
                logger.debug("PEM CSR parse error %s", traceback.format_exc())
                raise errors.Error("Failed to parse CSR file: {0}".format(
                    parsed_args.csr[0]))

        # This is not necessary for webroot to work, however,
        # obtain_certificate_from_csr requires parsed_args.domains to be set
        for domain in domains:
            add_domains(parsed_args, domain)

        if not domains:
            # TODO: add CN to domains instead:
            raise errors.Error(
                "Unfortunately, your CSR %s needs to have a SubjectAltName for every domain"
                % parsed_args.csr[0])

        parsed_args.actual_csr = (csr, typ)
        csr_domains, config_domains = set(domains), set(parsed_args.domains)
        if csr_domains != config_domains:
            raise errors.ConfigurationError(
                "Inconsistent domain requests:\nFrom the CSR: {0}\nFrom command line/config: {1}"
                .format(", ".join(csr_domains), ", ".join(config_domains)))
Beispiel #9
0
    def test_obtain_certificate_from_csr(self, mock_process_domain,
                                         mock_logger):
        self._mock_obtain_certificate()
        from letsencrypt import cli
        test_csr = le_util.CSR(form="der", file=None, data=CSR_SAN)
        mock_parsed_args = mock.MagicMock()
        # The CLI should believe that this is a certonly request, because
        # a CSR would not be allowed with other kinds of requests!
        mock_parsed_args.verb = "certonly"
        with mock.patch("letsencrypt.client.le_util.CSR") as mock_CSR:
            mock_CSR.return_value = test_csr
            mock_parsed_args.domains = self.eg_domains[:]
            mock_parser = mock.MagicMock(cli.HelpfulArgumentParser)
            cli.HelpfulArgumentParser.handle_csr(mock_parser, mock_parsed_args)

            # make sure cli processing occurred
            cli_processed = (call[0][1]
                             for call in mock_process_domain.call_args_list)
            self.assertEqual(set(cli_processed),
                             set(("example.com", "www.example.com")))
            # Now provoke an inconsistent domains error...
            mock_parsed_args.domains.append("hippopotamus.io")
            self.assertRaises(errors.ConfigurationError,
                              cli.HelpfulArgumentParser.handle_csr,
                              mock_parser, mock_parsed_args)

            authzr = self.client.auth_handler.get_authorizations(
                self.eg_domains, False)

            self.assertEqual(
                (mock.sentinel.certr, mock.sentinel.chain),
                self.client.obtain_certificate_from_csr(self.eg_domains,
                                                        test_csr,
                                                        authzr=authzr))
            # and that the cert was obtained correctly
            self._check_obtain_certificate()

            # Test for authzr=None
            self.assertEqual(
                (mock.sentinel.certr, mock.sentinel.chain),
                self.client.obtain_certificate_from_csr(self.eg_domains,
                                                        test_csr,
                                                        authzr=None))

            self.client.auth_handler.get_authorizations.assert_called_with(
                self.eg_domains)

            # Test for no auth_handler
            self.client.auth_handler = None
            self.assertRaises(errors.Error,
                              self.client.obtain_certificate_from_csr,
                              self.eg_domains, test_csr)
            mock_logger.warning.assert_called_once_with(mock.ANY)
Beispiel #10
0
    def test_obtain_certificate(self, mock_crypto_util):
        self._mock_obtain_certificate()

        csr = le_util.CSR(form="der", file=None, data=CSR_SAN)
        mock_crypto_util.init_save_csr.return_value = csr
        mock_crypto_util.init_save_key.return_value = mock.sentinel.key
        domains = ["example.com", "www.example.com"]

        self.assertEqual(
            self.client.obtain_certificate(domains),
            (mock.sentinel.certr, mock.sentinel.chain, mock.sentinel.key, csr))

        mock_crypto_util.init_save_key.assert_called_once_with(
            self.config.rsa_key_size, self.config.key_dir)
        mock_crypto_util.init_save_csr.assert_called_once_with(
            mock.sentinel.key, domains, self.config.csr_dir)
        self._check_obtain_certificate()
def validate_key_csr(privkey, csr=None):
    """Validate Key and CSR files.

    Verifies that the client key and csr arguments are valid and correspond to
    one another. This does not currently check the names in the CSR due to
    the inability to read SANs from CSRs in python crypto libraries.

    If csr is left as None, only the key will be validated.

    :param privkey: Key associated with CSR
    :type privkey: :class:`letsencrypt.le_util.Key`

    :param csr: CSR
    :type csr: :class:`letsencrypt.le_util.CSR`

    :raises letsencrypt.errors.LetsEncryptClientError: when
        validation fails

    """
    # TODO: Handle all of these problems appropriately
    # The client can eventually do things like prompt the user
    # and allow the user to take more appropriate actions

    # Key must be readable and valid.
    if privkey.pem and not crypto_util.valid_privkey(privkey.pem):
        raise errors.LetsEncryptClientError(
            "The provided key is not a valid key")

    if csr:
        if csr.form == "der":
            csr_obj = M2Crypto.X509.load_request_der_string(csr.data)
            csr = le_util.CSR(csr.file, csr_obj.as_pem(), "der")

        # If CSR is provided, it must be readable and valid.
        if csr.data and not crypto_util.valid_csr(csr.data):
            raise errors.LetsEncryptClientError(
                "The provided CSR is not a valid CSR")

        # If both CSR and key are provided, the key must be the same key used
        # in the CSR.
        if csr.data and privkey.pem:
            if not crypto_util.csr_matches_pubkey(csr.data, privkey.pem):
                raise errors.LetsEncryptClientError(
                    "The key and CSR do not match")
Beispiel #12
0
        return "--domains and --csr are mutually exclusive"

    try:
        # installers are used in auth mode to determine domain names
        installer, authenticator = choose_configurator_plugins(
            args, config, plugins, "auth")
    except PluginSelectionError, e:
        return e.message

    # TODO: Handle errors from _init_le_client?
    le_client = _init_le_client(args, config, authenticator, installer)

    # This is a special case; cert and chain are simply saved
    if args.csr is not None:
        certr, chain = le_client.obtain_certificate_from_csr(
            le_util.CSR(file=args.csr[0], data=args.csr[1], form="der"))
        cert_path, _, cert_fullchain = le_client.save_certificate(
            certr, chain, args.cert_path, args.chain_path, args.fullchain_path)
        _report_new_cert(cert_path, cert_fullchain)
    else:
        domains = _find_domains(args, installer)
        _auth_from_domains(le_client, config, domains, plugins)


def install(args, config, plugins):
    """Install a previously obtained cert in a server."""
    # XXX: Update for renewer/RenewableCert

    try:
        installer, _ = choose_configurator_plugins(args, config, plugins,
                                                   "auth")
Beispiel #13
0
        # TODO: --csr could have a priority, when --domains is
        # supplied, check if CSR matches given domains?
        return "--domains and --csr are mutually exclusive"

    try:
        # installers are used in auth mode to determine domain names
        installer, authenticator = choose_configurator_plugins(args, config, plugins, "auth")
    except PluginSelectionError, e:
        return e.message

    # TODO: Handle errors from _init_le_client?
    le_client = _init_le_client(args, config, authenticator, installer)

    # This is a special case; cert and chain are simply saved
    if args.csr is not None:
        certr, chain = le_client.obtain_certificate_from_csr(le_util.CSR(
            file=args.csr[0], data=args.csr[1], form="der"))
        le_client.save_certificate(
            certr, chain, args.cert_path, args.chain_path)
        _report_new_cert(args.cert_path)
    else:
        domains = _find_domains(args, installer)
        _auth_from_domains(le_client, config, domains, plugins)


def install(args, config, plugins):
    """Install a previously obtained cert in a server."""
    # XXX: Update for renewer/RenewableCert

    try:
        installer, _ = choose_configurator_plugins(args, config, plugins, "auth")
    except PluginSelectionError, e: