|
|
6d47df |
From 468bcf90cb985e2b1eb394bd752dc39aa4b75582 Mon Sep 17 00:00:00 2001
|
|
|
6d47df |
From: Rob Crittenden <rcritten@redhat.com>
|
|
|
6d47df |
Date: Thu, 19 Jul 2018 18:37:18 -0400
|
|
|
6d47df |
Subject: [PATCH] Remove csrgen
|
|
|
6d47df |
|
|
|
6d47df |
This reverts commits:
|
|
|
6d47df |
* 72de679eb445c975ec70cd265d37d4927823ce5b
|
|
|
6d47df |
* 177f07e163d6d591a1e609d35e0a6f6f5347551e
|
|
|
6d47df |
* 80be18162921268be9c8981495c9e8a4de0c85cd
|
|
|
6d47df |
* 83e2c2b65eeb5a3aa4a59c0535e9177aac5e4637
|
|
|
6d47df |
* ada91c20588046bb147fc701718d3da4d2c080ca
|
|
|
6d47df |
* 4350dcdea22fd2284836315d0ae7d38733a7620e
|
|
|
6d47df |
* 39a5d9c5aae77687f67d9be02457733bdfb99ead
|
|
|
6d47df |
* a26cf0d7910dd4c0a4da08682b4be8d3d94ba520
|
|
|
6d47df |
* afd7c05d11432304bfdf183832a21d419f363689
|
|
|
6d47df |
* f1a1c6eca1b294f24174d7b0e1f78de46d9d5b05
|
|
|
6d47df |
* fc58eff6a3d7fe805e612b8b002304d8b9cd4ba9
|
|
|
6d47df |
* 10ef5947860f5098182b1f95c08c1158e2da15f9
|
|
|
6d47df |
|
|
|
6d47df |
https://bugzilla.redhat.com/show_bug.cgi?id=1432630
|
|
|
6d47df |
---
|
|
|
6d47df |
freeipa.spec.in | 14 -
|
|
|
6d47df |
ipaclient/csrgen.py | 488 ---------------------
|
|
|
6d47df |
ipaclient/csrgen/profiles/caIPAserviceCert.json | 15 -
|
|
|
6d47df |
ipaclient/csrgen/profiles/userCert.json | 15 -
|
|
|
6d47df |
ipaclient/csrgen/rules/dataDNS.json | 8 -
|
|
|
6d47df |
ipaclient/csrgen/rules/dataEmail.json | 8 -
|
|
|
6d47df |
ipaclient/csrgen/rules/dataHostCN.json | 8 -
|
|
|
6d47df |
ipaclient/csrgen/rules/dataSubjectBase.json | 8 -
|
|
|
6d47df |
ipaclient/csrgen/rules/dataUsernameCN.json | 8 -
|
|
|
6d47df |
ipaclient/csrgen/rules/syntaxSAN.json | 8 -
|
|
|
6d47df |
ipaclient/csrgen/rules/syntaxSubject.json | 9 -
|
|
|
6d47df |
ipaclient/csrgen/templates/openssl_base.tmpl | 17 -
|
|
|
6d47df |
ipaclient/csrgen/templates/openssl_macros.tmpl | 29 --
|
|
|
6d47df |
ipaclient/csrgen_ffi.py | 331 --------------
|
|
|
6d47df |
ipaclient/plugins/cert.py | 80 ----
|
|
|
6d47df |
ipaclient/plugins/csrgen.py | 128 ------
|
|
|
6d47df |
ipaclient/setup.py | 8 -
|
|
|
6d47df |
.../data/test_csrgen/configs/caIPAserviceCert.conf | 16 -
|
|
|
6d47df |
.../data/test_csrgen/configs/userCert.conf | 16 -
|
|
|
6d47df |
.../data/test_csrgen/profiles/profile.json | 8 -
|
|
|
6d47df |
.../data/test_csrgen/rules/basic.json | 5 -
|
|
|
6d47df |
.../data/test_csrgen/rules/options.json | 8 -
|
|
|
6d47df |
.../data/test_csrgen/templates/identity_base.tmpl | 1 -
|
|
|
6d47df |
ipatests/test_ipaclient/test_csrgen.py | 304 -------------
|
|
|
6d47df |
24 files changed, 1540 deletions(-)
|
|
|
6d47df |
delete mode 100644 ipaclient/csrgen.py
|
|
|
6d47df |
delete mode 100644 ipaclient/csrgen/profiles/caIPAserviceCert.json
|
|
|
6d47df |
delete mode 100644 ipaclient/csrgen/profiles/userCert.json
|
|
|
6d47df |
delete mode 100644 ipaclient/csrgen/rules/dataDNS.json
|
|
|
6d47df |
delete mode 100644 ipaclient/csrgen/rules/dataEmail.json
|
|
|
6d47df |
delete mode 100644 ipaclient/csrgen/rules/dataHostCN.json
|
|
|
6d47df |
delete mode 100644 ipaclient/csrgen/rules/dataSubjectBase.json
|
|
|
6d47df |
delete mode 100644 ipaclient/csrgen/rules/dataUsernameCN.json
|
|
|
6d47df |
delete mode 100644 ipaclient/csrgen/rules/syntaxSAN.json
|
|
|
6d47df |
delete mode 100644 ipaclient/csrgen/rules/syntaxSubject.json
|
|
|
6d47df |
delete mode 100644 ipaclient/csrgen/templates/openssl_base.tmpl
|
|
|
6d47df |
delete mode 100644 ipaclient/csrgen/templates/openssl_macros.tmpl
|
|
|
6d47df |
delete mode 100644 ipaclient/csrgen_ffi.py
|
|
|
6d47df |
delete mode 100644 ipaclient/plugins/csrgen.py
|
|
|
6d47df |
delete mode 100644 ipatests/test_ipaclient/data/test_csrgen/configs/caIPAserviceCert.conf
|
|
|
6d47df |
delete mode 100644 ipatests/test_ipaclient/data/test_csrgen/configs/userCert.conf
|
|
|
6d47df |
delete mode 100644 ipatests/test_ipaclient/data/test_csrgen/profiles/profile.json
|
|
|
6d47df |
delete mode 100644 ipatests/test_ipaclient/data/test_csrgen/rules/basic.json
|
|
|
6d47df |
delete mode 100644 ipatests/test_ipaclient/data/test_csrgen/rules/options.json
|
|
|
6d47df |
delete mode 100644 ipatests/test_ipaclient/data/test_csrgen/templates/identity_base.tmpl
|
|
|
6d47df |
delete mode 100644 ipatests/test_ipaclient/test_csrgen.py
|
|
|
6d47df |
|
|
|
6d47df |
diff --git a/freeipa.spec.in b/freeipa.spec.in
|
|
|
6d47df |
index a01fae7..cd1773a 100644
|
|
|
6d47df |
--- a/freeipa.spec.in
|
|
|
6d47df |
+++ b/freeipa.spec.in
|
|
|
6d47df |
@@ -1538,13 +1538,6 @@ fi
|
|
|
6d47df |
%{python2_sitelib}/ipaclient/remote_plugins/*.py*
|
|
|
6d47df |
%dir %{python2_sitelib}/ipaclient/remote_plugins/2_*
|
|
|
6d47df |
%{python2_sitelib}/ipaclient/remote_plugins/2_*/*.py*
|
|
|
6d47df |
-%dir %{python2_sitelib}/ipaclient/csrgen
|
|
|
6d47df |
-%dir %{python2_sitelib}/ipaclient/csrgen/profiles
|
|
|
6d47df |
-%{python2_sitelib}/ipaclient/csrgen/profiles/*.json
|
|
|
6d47df |
-%dir %{python2_sitelib}/ipaclient/csrgen/rules
|
|
|
6d47df |
-%{python2_sitelib}/ipaclient/csrgen/rules/*.json
|
|
|
6d47df |
-%dir %{python2_sitelib}/ipaclient/csrgen/templates
|
|
|
6d47df |
-%{python2_sitelib}/ipaclient/csrgen/templates/*.tmpl
|
|
|
6d47df |
%{python2_sitelib}/ipaclient-*.egg-info
|
|
|
6d47df |
|
|
|
6d47df |
%endif # with_python2
|
|
|
6d47df |
@@ -1567,13 +1560,6 @@ fi
|
|
|
6d47df |
%dir %{python3_sitelib}/ipaclient/remote_plugins/2_*
|
|
|
6d47df |
%{python3_sitelib}/ipaclient/remote_plugins/2_*/*.py
|
|
|
6d47df |
%{python3_sitelib}/ipaclient/remote_plugins/2_*/__pycache__/*.py*
|
|
|
6d47df |
-%dir %{python3_sitelib}/ipaclient/csrgen
|
|
|
6d47df |
-%dir %{python3_sitelib}/ipaclient/csrgen/profiles
|
|
|
6d47df |
-%{python3_sitelib}/ipaclient/csrgen/profiles/*.json
|
|
|
6d47df |
-%dir %{python3_sitelib}/ipaclient/csrgen/rules
|
|
|
6d47df |
-%{python3_sitelib}/ipaclient/csrgen/rules/*.json
|
|
|
6d47df |
-%dir %{python3_sitelib}/ipaclient/csrgen/templates
|
|
|
6d47df |
-%{python3_sitelib}/ipaclient/csrgen/templates/*.tmpl
|
|
|
6d47df |
%{python3_sitelib}/ipaclient-*.egg-info
|
|
|
6d47df |
|
|
|
6d47df |
|
|
|
6d47df |
diff --git a/ipaclient/csrgen.py b/ipaclient/csrgen.py
|
|
|
6d47df |
deleted file mode 100644
|
|
|
6d47df |
index e7573be..0000000
|
|
|
6d47df |
--- a/ipaclient/csrgen.py
|
|
|
6d47df |
+++ /dev/null
|
|
|
6d47df |
@@ -1,488 +0,0 @@
|
|
|
6d47df |
-#
|
|
|
6d47df |
-# Copyright (C) 2016 FreeIPA Contributors see COPYING for license
|
|
|
6d47df |
-#
|
|
|
6d47df |
-
|
|
|
6d47df |
-import base64
|
|
|
6d47df |
-import collections
|
|
|
6d47df |
-import errno
|
|
|
6d47df |
-import json
|
|
|
6d47df |
-import logging
|
|
|
6d47df |
-import os
|
|
|
6d47df |
-import os.path
|
|
|
6d47df |
-import pipes
|
|
|
6d47df |
-import subprocess
|
|
|
6d47df |
-import traceback
|
|
|
6d47df |
-import codecs
|
|
|
6d47df |
-
|
|
|
6d47df |
-import pkg_resources
|
|
|
6d47df |
-
|
|
|
6d47df |
-from cryptography.hazmat.backends import default_backend
|
|
|
6d47df |
-from cryptography.hazmat.primitives.asymmetric import padding
|
|
|
6d47df |
-from cryptography.hazmat.primitives import hashes
|
|
|
6d47df |
-from cryptography.hazmat.primitives.serialization import (
|
|
|
6d47df |
- load_pem_private_key, Encoding, PublicFormat)
|
|
|
6d47df |
-from cryptography.x509 import load_pem_x509_certificate
|
|
|
6d47df |
-import jinja2
|
|
|
6d47df |
-import jinja2.ext
|
|
|
6d47df |
-import jinja2.sandbox
|
|
|
6d47df |
-from pyasn1.codec.der import decoder, encoder
|
|
|
6d47df |
-from pyasn1.type import univ
|
|
|
6d47df |
-from pyasn1_modules import rfc2314
|
|
|
6d47df |
-import six
|
|
|
6d47df |
-
|
|
|
6d47df |
-from ipalib import api
|
|
|
6d47df |
-from ipalib import errors
|
|
|
6d47df |
-from ipalib.text import _
|
|
|
6d47df |
-
|
|
|
6d47df |
-if six.PY3:
|
|
|
6d47df |
- unicode = str
|
|
|
6d47df |
-
|
|
|
6d47df |
-__doc__ = _("""
|
|
|
6d47df |
-Routines for constructing certificate signing requests using IPA data and
|
|
|
6d47df |
-stored templates.
|
|
|
6d47df |
-""")
|
|
|
6d47df |
-
|
|
|
6d47df |
-logger = logging.getLogger(__name__)
|
|
|
6d47df |
-
|
|
|
6d47df |
-
|
|
|
6d47df |
-class IndexableUndefined(jinja2.Undefined):
|
|
|
6d47df |
- def __getitem__(self, key):
|
|
|
6d47df |
- return jinja2.Undefined(
|
|
|
6d47df |
- hint=self._undefined_hint, obj=self._undefined_obj,
|
|
|
6d47df |
- name=self._undefined_name, exc=self._undefined_exception)
|
|
|
6d47df |
-
|
|
|
6d47df |
-
|
|
|
6d47df |
-class IPAExtension(jinja2.ext.Extension):
|
|
|
6d47df |
- """Jinja2 extension providing useful features for CSR generation rules."""
|
|
|
6d47df |
-
|
|
|
6d47df |
- def __init__(self, environment):
|
|
|
6d47df |
- super(IPAExtension, self).__init__(environment)
|
|
|
6d47df |
-
|
|
|
6d47df |
- environment.filters.update(
|
|
|
6d47df |
- quote=self.quote,
|
|
|
6d47df |
- required=self.required,
|
|
|
6d47df |
- )
|
|
|
6d47df |
-
|
|
|
6d47df |
- def quote(self, data):
|
|
|
6d47df |
- return pipes.quote(data)
|
|
|
6d47df |
-
|
|
|
6d47df |
- def required(self, data, name):
|
|
|
6d47df |
- if not data:
|
|
|
6d47df |
- raise errors.CSRTemplateError(
|
|
|
6d47df |
- reason=_(
|
|
|
6d47df |
- 'Required CSR generation rule %(name)s is missing data') %
|
|
|
6d47df |
- {'name': name})
|
|
|
6d47df |
- return data
|
|
|
6d47df |
-
|
|
|
6d47df |
-
|
|
|
6d47df |
-class Formatter(object):
|
|
|
6d47df |
- """
|
|
|
6d47df |
- Class for processing a set of CSR generation rules into a template.
|
|
|
6d47df |
-
|
|
|
6d47df |
- The template can be rendered with user and database data to produce a
|
|
|
6d47df |
- config, which specifies how to build a CSR.
|
|
|
6d47df |
-
|
|
|
6d47df |
- Subclasses of Formatter should set the value of base_template_name to the
|
|
|
6d47df |
- filename of a base template with spaces for the processed rules.
|
|
|
6d47df |
- Additionally, they should override the _get_template_params method to
|
|
|
6d47df |
- produce the correct output for the base template.
|
|
|
6d47df |
- """
|
|
|
6d47df |
- base_template_name = None
|
|
|
6d47df |
-
|
|
|
6d47df |
- def __init__(self, csr_data_dir=None):
|
|
|
6d47df |
- # chain loaders:
|
|
|
6d47df |
- # 1) csr_data_dir/templates
|
|
|
6d47df |
- # 2) /etc/ipa/csrgen/templates
|
|
|
6d47df |
- # 3) ipaclient/csrgen/templates
|
|
|
6d47df |
- loaders = []
|
|
|
6d47df |
- if csr_data_dir is not None:
|
|
|
6d47df |
- loaders.append(jinja2.FileSystemLoader(
|
|
|
6d47df |
- os.path.join(csr_data_dir, 'templates'))
|
|
|
6d47df |
- )
|
|
|
6d47df |
- loaders.append(jinja2.FileSystemLoader(
|
|
|
6d47df |
- os.path.join(api.env.confdir, 'csrgen/templates'))
|
|
|
6d47df |
- )
|
|
|
6d47df |
- loaders.append(jinja2.PackageLoader('ipaclient', 'csrgen/templates'))
|
|
|
6d47df |
-
|
|
|
6d47df |
- self.jinja2 = jinja2.sandbox.SandboxedEnvironment(
|
|
|
6d47df |
- loader=jinja2.ChoiceLoader(loaders),
|
|
|
6d47df |
- extensions=[jinja2.ext.ExprStmtExtension, IPAExtension],
|
|
|
6d47df |
- keep_trailing_newline=True, undefined=IndexableUndefined)
|
|
|
6d47df |
-
|
|
|
6d47df |
- self.passthrough_globals = {}
|
|
|
6d47df |
-
|
|
|
6d47df |
- def _define_passthrough(self, call):
|
|
|
6d47df |
- """Some macros are meant to be interpreted during the final render, not
|
|
|
6d47df |
- when data rules are interpolated into syntax rules. This method allows
|
|
|
6d47df |
- those macros to be registered so that calls to them are passed through
|
|
|
6d47df |
- to the prepared rule rather than interpreted.
|
|
|
6d47df |
- """
|
|
|
6d47df |
-
|
|
|
6d47df |
- def passthrough(caller):
|
|
|
6d47df |
- return u'{%% call %s() %%}%s{%% endcall %%}' % (call, caller())
|
|
|
6d47df |
-
|
|
|
6d47df |
- parts = call.split('.')
|
|
|
6d47df |
- current_level = self.passthrough_globals
|
|
|
6d47df |
- for part in parts[:-1]:
|
|
|
6d47df |
- if part not in current_level:
|
|
|
6d47df |
- current_level[part] = {}
|
|
|
6d47df |
- current_level = current_level[part]
|
|
|
6d47df |
- current_level[parts[-1]] = passthrough
|
|
|
6d47df |
-
|
|
|
6d47df |
- def build_template(self, rules):
|
|
|
6d47df |
- """
|
|
|
6d47df |
- Construct a template that can produce CSR generator strings.
|
|
|
6d47df |
-
|
|
|
6d47df |
- :param rules: list of FieldMapping to use to populate the template.
|
|
|
6d47df |
-
|
|
|
6d47df |
- :returns: jinja2.Template that can be rendered to produce the CSR data.
|
|
|
6d47df |
- """
|
|
|
6d47df |
- syntax_rules = []
|
|
|
6d47df |
- for field_mapping in rules:
|
|
|
6d47df |
- data_rules_prepared = [
|
|
|
6d47df |
- self._prepare_data_rule(rule)
|
|
|
6d47df |
- for rule in field_mapping.data_rules]
|
|
|
6d47df |
-
|
|
|
6d47df |
- data_sources = []
|
|
|
6d47df |
- for xrule in field_mapping.data_rules:
|
|
|
6d47df |
- data_source = xrule.options.get('data_source')
|
|
|
6d47df |
- if data_source:
|
|
|
6d47df |
- data_sources.append(data_source)
|
|
|
6d47df |
-
|
|
|
6d47df |
- syntax_rules.append(self._prepare_syntax_rule(
|
|
|
6d47df |
- field_mapping.syntax_rule, data_rules_prepared,
|
|
|
6d47df |
- field_mapping.description, data_sources))
|
|
|
6d47df |
-
|
|
|
6d47df |
- template_params = self._get_template_params(syntax_rules)
|
|
|
6d47df |
- base_template = self.jinja2.get_template(
|
|
|
6d47df |
- self.base_template_name, globals=self.passthrough_globals)
|
|
|
6d47df |
-
|
|
|
6d47df |
- try:
|
|
|
6d47df |
- combined_template_source = base_template.render(**template_params)
|
|
|
6d47df |
- except jinja2.UndefinedError:
|
|
|
6d47df |
- logger.debug(traceback.format_exc())
|
|
|
6d47df |
- raise errors.CSRTemplateError(reason=_(
|
|
|
6d47df |
- 'Template error when formatting certificate data'))
|
|
|
6d47df |
-
|
|
|
6d47df |
- logger.debug(
|
|
|
6d47df |
- 'Formatting with template: %s', combined_template_source)
|
|
|
6d47df |
- combined_template = self.jinja2.from_string(combined_template_source)
|
|
|
6d47df |
-
|
|
|
6d47df |
- return combined_template
|
|
|
6d47df |
-
|
|
|
6d47df |
- def _wrap_conditional(self, rule, condition):
|
|
|
6d47df |
- rule = '{%% if %s %%}%s{%% endif %%}' % (condition, rule)
|
|
|
6d47df |
- return rule
|
|
|
6d47df |
-
|
|
|
6d47df |
- def _wrap_required(self, rule, description):
|
|
|
6d47df |
- template = '{%% filter required("%s") %%}%s{%% endfilter %%}' % (
|
|
|
6d47df |
- description, rule)
|
|
|
6d47df |
-
|
|
|
6d47df |
- return template
|
|
|
6d47df |
-
|
|
|
6d47df |
- def _prepare_data_rule(self, data_rule):
|
|
|
6d47df |
- template = data_rule.template
|
|
|
6d47df |
-
|
|
|
6d47df |
- data_source = data_rule.options.get('data_source')
|
|
|
6d47df |
- if data_source:
|
|
|
6d47df |
- template = self._wrap_conditional(template, data_source)
|
|
|
6d47df |
-
|
|
|
6d47df |
- return template
|
|
|
6d47df |
-
|
|
|
6d47df |
- def _prepare_syntax_rule(
|
|
|
6d47df |
- self, syntax_rule, data_rules, description, data_sources):
|
|
|
6d47df |
- logger.debug('Syntax rule template: %s', syntax_rule.template)
|
|
|
6d47df |
- template = self.jinja2.from_string(
|
|
|
6d47df |
- syntax_rule.template, globals=self.passthrough_globals)
|
|
|
6d47df |
- is_required = syntax_rule.options.get('required', False)
|
|
|
6d47df |
- try:
|
|
|
6d47df |
- prepared_template = template.render(datarules=data_rules)
|
|
|
6d47df |
- except jinja2.UndefinedError:
|
|
|
6d47df |
- logger.debug(traceback.format_exc())
|
|
|
6d47df |
- raise errors.CSRTemplateError(reason=_(
|
|
|
6d47df |
- 'Template error when formatting certificate data'))
|
|
|
6d47df |
-
|
|
|
6d47df |
- if data_sources:
|
|
|
6d47df |
- combinator = ' %s ' % syntax_rule.options.get(
|
|
|
6d47df |
- 'data_source_combinator', 'or')
|
|
|
6d47df |
- condition = combinator.join(data_sources)
|
|
|
6d47df |
- prepared_template = self._wrap_conditional(
|
|
|
6d47df |
- prepared_template, condition)
|
|
|
6d47df |
-
|
|
|
6d47df |
- if is_required:
|
|
|
6d47df |
- prepared_template = self._wrap_required(
|
|
|
6d47df |
- prepared_template, description)
|
|
|
6d47df |
-
|
|
|
6d47df |
- return prepared_template
|
|
|
6d47df |
-
|
|
|
6d47df |
- def _get_template_params(self, syntax_rules):
|
|
|
6d47df |
- """
|
|
|
6d47df |
- Package the syntax rules into fields expected by the base template.
|
|
|
6d47df |
-
|
|
|
6d47df |
- :param syntax_rules: list of prepared syntax rules to be included in
|
|
|
6d47df |
- the template.
|
|
|
6d47df |
-
|
|
|
6d47df |
- :returns: dict of values needed to render the base template.
|
|
|
6d47df |
- """
|
|
|
6d47df |
- raise NotImplementedError('Formatter class must be subclassed')
|
|
|
6d47df |
-
|
|
|
6d47df |
-
|
|
|
6d47df |
-class OpenSSLFormatter(Formatter):
|
|
|
6d47df |
- """Formatter class generating the openssl config-file format."""
|
|
|
6d47df |
-
|
|
|
6d47df |
- base_template_name = 'openssl_base.tmpl'
|
|
|
6d47df |
-
|
|
|
6d47df |
- # Syntax rules are wrapped in this data structure, to keep track of whether
|
|
|
6d47df |
- # each goes in the extension or the root section
|
|
|
6d47df |
- SyntaxRule = collections.namedtuple(
|
|
|
6d47df |
- 'SyntaxRule', ['template', 'is_extension'])
|
|
|
6d47df |
-
|
|
|
6d47df |
- def __init__(self, *args, **kwargs):
|
|
|
6d47df |
- super(OpenSSLFormatter, self).__init__(*args, **kwargs)
|
|
|
6d47df |
- self._define_passthrough('openssl.section')
|
|
|
6d47df |
-
|
|
|
6d47df |
- def _get_template_params(self, syntax_rules):
|
|
|
6d47df |
- parameters = [rule.template for rule in syntax_rules
|
|
|
6d47df |
- if not rule.is_extension]
|
|
|
6d47df |
- extensions = [rule.template for rule in syntax_rules
|
|
|
6d47df |
- if rule.is_extension]
|
|
|
6d47df |
-
|
|
|
6d47df |
- return {'parameters': parameters, 'extensions': extensions}
|
|
|
6d47df |
-
|
|
|
6d47df |
- def _prepare_syntax_rule(
|
|
|
6d47df |
- self, syntax_rule, data_rules, description, data_sources):
|
|
|
6d47df |
- """Overrides method to pull out whether rule is an extension or not."""
|
|
|
6d47df |
- prepared_template = super(OpenSSLFormatter, self)._prepare_syntax_rule(
|
|
|
6d47df |
- syntax_rule, data_rules, description, data_sources)
|
|
|
6d47df |
- is_extension = syntax_rule.options.get('extension', False)
|
|
|
6d47df |
- return self.SyntaxRule(prepared_template, is_extension)
|
|
|
6d47df |
-
|
|
|
6d47df |
-
|
|
|
6d47df |
-class FieldMapping(object):
|
|
|
6d47df |
- """Representation of the rules needed to construct a complete cert field.
|
|
|
6d47df |
-
|
|
|
6d47df |
- Attributes:
|
|
|
6d47df |
- description: str, a name or description of this field, to be used in
|
|
|
6d47df |
- messages
|
|
|
6d47df |
- syntax_rule: Rule, the rule defining the syntax of this field
|
|
|
6d47df |
- data_rules: list of Rule, the rules that produce data to be stored in
|
|
|
6d47df |
- this field
|
|
|
6d47df |
- """
|
|
|
6d47df |
- __slots__ = ['description', 'syntax_rule', 'data_rules']
|
|
|
6d47df |
-
|
|
|
6d47df |
- def __init__(self, description, syntax_rule, data_rules):
|
|
|
6d47df |
- self.description = description
|
|
|
6d47df |
- self.syntax_rule = syntax_rule
|
|
|
6d47df |
- self.data_rules = data_rules
|
|
|
6d47df |
-
|
|
|
6d47df |
-
|
|
|
6d47df |
-class Rule(object):
|
|
|
6d47df |
- __slots__ = ['name', 'template', 'options']
|
|
|
6d47df |
-
|
|
|
6d47df |
- def __init__(self, name, template, options):
|
|
|
6d47df |
- self.name = name
|
|
|
6d47df |
- self.template = template
|
|
|
6d47df |
- self.options = options
|
|
|
6d47df |
-
|
|
|
6d47df |
-
|
|
|
6d47df |
-class RuleProvider(object):
|
|
|
6d47df |
- def rules_for_profile(self, profile_id):
|
|
|
6d47df |
- """
|
|
|
6d47df |
- Return the rules needed to build a CSR using the given profile.
|
|
|
6d47df |
-
|
|
|
6d47df |
- :param profile_id: str, name of the CSR generation profile to use
|
|
|
6d47df |
-
|
|
|
6d47df |
- :returns: list of FieldMapping, filled out with the appropriate rules
|
|
|
6d47df |
- """
|
|
|
6d47df |
- raise NotImplementedError('RuleProvider class must be subclassed')
|
|
|
6d47df |
-
|
|
|
6d47df |
-
|
|
|
6d47df |
-class FileRuleProvider(RuleProvider):
|
|
|
6d47df |
- def __init__(self, csr_data_dir=None):
|
|
|
6d47df |
- self.rules = {}
|
|
|
6d47df |
- self._csrgen_data_dirs = []
|
|
|
6d47df |
- if csr_data_dir is not None:
|
|
|
6d47df |
- self._csrgen_data_dirs.append(csr_data_dir)
|
|
|
6d47df |
- self._csrgen_data_dirs.append(
|
|
|
6d47df |
- os.path.join(api.env.confdir, 'csrgen')
|
|
|
6d47df |
- )
|
|
|
6d47df |
- self._csrgen_data_dirs.append(
|
|
|
6d47df |
- pkg_resources.resource_filename('ipaclient', 'csrgen')
|
|
|
6d47df |
- )
|
|
|
6d47df |
-
|
|
|
6d47df |
- def _open(self, subdir, filename):
|
|
|
6d47df |
- for data_dir in self._csrgen_data_dirs:
|
|
|
6d47df |
- path = os.path.join(data_dir, subdir, filename)
|
|
|
6d47df |
- try:
|
|
|
6d47df |
- return open(path)
|
|
|
6d47df |
- except IOError as e:
|
|
|
6d47df |
- if e.errno != errno.ENOENT:
|
|
|
6d47df |
- raise
|
|
|
6d47df |
- raise IOError(
|
|
|
6d47df |
- errno.ENOENT,
|
|
|
6d47df |
- "'{}' not found in {}".format(
|
|
|
6d47df |
- os.path.join(subdir, filename),
|
|
|
6d47df |
- ", ".join(self._csrgen_data_dirs)
|
|
|
6d47df |
- )
|
|
|
6d47df |
- )
|
|
|
6d47df |
-
|
|
|
6d47df |
- def _rule(self, rule_name):
|
|
|
6d47df |
- if rule_name not in self.rules:
|
|
|
6d47df |
- try:
|
|
|
6d47df |
- with self._open('rules', '%s.json' % rule_name) as f:
|
|
|
6d47df |
- ruleconf = json.load(f)
|
|
|
6d47df |
- except IOError:
|
|
|
6d47df |
- raise errors.NotFound(
|
|
|
6d47df |
- reason=_('No generation rule %(rulename)s found.') %
|
|
|
6d47df |
- {'rulename': rule_name})
|
|
|
6d47df |
-
|
|
|
6d47df |
- try:
|
|
|
6d47df |
- rule = ruleconf['rule']
|
|
|
6d47df |
- except KeyError:
|
|
|
6d47df |
- raise errors.EmptyResult(
|
|
|
6d47df |
- reason=_('Generation rule "%(rulename)s" is missing the'
|
|
|
6d47df |
- ' "rule" key') % {'rulename': rule_name})
|
|
|
6d47df |
-
|
|
|
6d47df |
- options = ruleconf.get('options', {})
|
|
|
6d47df |
-
|
|
|
6d47df |
- self.rules[rule_name] = Rule(
|
|
|
6d47df |
- rule_name, rule['template'], options)
|
|
|
6d47df |
-
|
|
|
6d47df |
- return self.rules[rule_name]
|
|
|
6d47df |
-
|
|
|
6d47df |
- def rules_for_profile(self, profile_id):
|
|
|
6d47df |
- try:
|
|
|
6d47df |
- with self._open('profiles', '%s.json' % profile_id) as f:
|
|
|
6d47df |
- profile = json.load(f)
|
|
|
6d47df |
- except IOError:
|
|
|
6d47df |
- raise errors.NotFound(
|
|
|
6d47df |
- reason=_('No CSR generation rules are defined for profile'
|
|
|
6d47df |
- ' %(profile_id)s') % {'profile_id': profile_id})
|
|
|
6d47df |
-
|
|
|
6d47df |
- field_mappings = []
|
|
|
6d47df |
- for field in profile:
|
|
|
6d47df |
- syntax_rule = self._rule(field['syntax'])
|
|
|
6d47df |
- data_rules = [self._rule(name) for name in field['data']]
|
|
|
6d47df |
- field_mappings.append(FieldMapping(
|
|
|
6d47df |
- syntax_rule.name, syntax_rule, data_rules))
|
|
|
6d47df |
- return field_mappings
|
|
|
6d47df |
-
|
|
|
6d47df |
-
|
|
|
6d47df |
-class CSRGenerator(object):
|
|
|
6d47df |
- def __init__(self, rule_provider, formatter_class=OpenSSLFormatter):
|
|
|
6d47df |
- self.rule_provider = rule_provider
|
|
|
6d47df |
- self.formatter = formatter_class()
|
|
|
6d47df |
-
|
|
|
6d47df |
- def csr_config(self, principal, config, profile_id):
|
|
|
6d47df |
- render_data = {'subject': principal, 'config': config}
|
|
|
6d47df |
-
|
|
|
6d47df |
- rules = self.rule_provider.rules_for_profile(profile_id)
|
|
|
6d47df |
- template = self.formatter.build_template(rules)
|
|
|
6d47df |
-
|
|
|
6d47df |
- try:
|
|
|
6d47df |
- config = template.render(render_data)
|
|
|
6d47df |
- except jinja2.UndefinedError:
|
|
|
6d47df |
- logger.debug(traceback.format_exc())
|
|
|
6d47df |
- raise errors.CSRTemplateError(reason=_(
|
|
|
6d47df |
- 'Template error when formatting certificate data'))
|
|
|
6d47df |
-
|
|
|
6d47df |
- return config
|
|
|
6d47df |
-
|
|
|
6d47df |
-
|
|
|
6d47df |
-class CSRLibraryAdaptor(object):
|
|
|
6d47df |
- def get_subject_public_key_info(self):
|
|
|
6d47df |
- raise NotImplementedError('Use a subclass of CSRLibraryAdaptor')
|
|
|
6d47df |
-
|
|
|
6d47df |
- def sign_csr(self, certification_request_info):
|
|
|
6d47df |
- """Sign a CertificationRequestInfo.
|
|
|
6d47df |
-
|
|
|
6d47df |
- :returns: bytes, a DER-encoded signed CSR.
|
|
|
6d47df |
- """
|
|
|
6d47df |
- raise NotImplementedError('Use a subclass of CSRLibraryAdaptor')
|
|
|
6d47df |
-
|
|
|
6d47df |
-
|
|
|
6d47df |
-class OpenSSLAdaptor(object):
|
|
|
6d47df |
- def __init__(self, key=None, key_filename=None, password_filename=None):
|
|
|
6d47df |
- """
|
|
|
6d47df |
- Must provide either ``key_filename`` or ``key``.
|
|
|
6d47df |
-
|
|
|
6d47df |
- """
|
|
|
6d47df |
- if key_filename is not None:
|
|
|
6d47df |
- with open(key_filename, 'rb') as key_file:
|
|
|
6d47df |
- key_bytes = key_file.read()
|
|
|
6d47df |
-
|
|
|
6d47df |
- password = None
|
|
|
6d47df |
- if password_filename is not None:
|
|
|
6d47df |
- with open(password_filename, 'rb') as password_file:
|
|
|
6d47df |
- password = password_file.read().strip()
|
|
|
6d47df |
-
|
|
|
6d47df |
- self._key = load_pem_private_key(
|
|
|
6d47df |
- key_bytes, password, default_backend())
|
|
|
6d47df |
-
|
|
|
6d47df |
- elif key is not None:
|
|
|
6d47df |
- self._key = key
|
|
|
6d47df |
-
|
|
|
6d47df |
- else:
|
|
|
6d47df |
- raise ValueError("Must provide 'key' or 'key_filename'")
|
|
|
6d47df |
-
|
|
|
6d47df |
- def key(self):
|
|
|
6d47df |
- return self._key
|
|
|
6d47df |
-
|
|
|
6d47df |
- def get_subject_public_key_info(self):
|
|
|
6d47df |
- pubkey_info = self.key().public_key().public_bytes(
|
|
|
6d47df |
- Encoding.DER, PublicFormat.SubjectPublicKeyInfo)
|
|
|
6d47df |
- return pubkey_info
|
|
|
6d47df |
-
|
|
|
6d47df |
- def sign_csr(self, certification_request_info):
|
|
|
6d47df |
- reqinfo = decoder.decode(
|
|
|
6d47df |
- certification_request_info, rfc2314.CertificationRequestInfo())[0]
|
|
|
6d47df |
- csr = rfc2314.CertificationRequest()
|
|
|
6d47df |
- csr.setComponentByName('certificationRequestInfo', reqinfo)
|
|
|
6d47df |
-
|
|
|
6d47df |
- algorithm = rfc2314.SignatureAlgorithmIdentifier()
|
|
|
6d47df |
- algorithm.setComponentByName(
|
|
|
6d47df |
- 'algorithm', univ.ObjectIdentifier(
|
|
|
6d47df |
- '1.2.840.113549.1.1.11')) # sha256WithRSAEncryption
|
|
|
6d47df |
- csr.setComponentByName('signatureAlgorithm', algorithm)
|
|
|
6d47df |
-
|
|
|
6d47df |
- signature = self.key().sign(
|
|
|
6d47df |
- certification_request_info,
|
|
|
6d47df |
- padding.PKCS1v15(),
|
|
|
6d47df |
- hashes.SHA256()
|
|
|
6d47df |
- )
|
|
|
6d47df |
- asn1sig = univ.BitString("'{sig}'H".format(
|
|
|
6d47df |
- sig=codecs.encode(signature, 'hex')
|
|
|
6d47df |
- .decode('ascii'))
|
|
|
6d47df |
- )
|
|
|
6d47df |
- csr.setComponentByName('signature', asn1sig)
|
|
|
6d47df |
- return encoder.encode(csr)
|
|
|
6d47df |
-
|
|
|
6d47df |
-
|
|
|
6d47df |
-class NSSAdaptor(object):
|
|
|
6d47df |
- def __init__(self, database, password_filename):
|
|
|
6d47df |
- self.database = database
|
|
|
6d47df |
- self.password_filename = password_filename
|
|
|
6d47df |
- self.nickname = base64.b32encode(os.urandom(40))
|
|
|
6d47df |
-
|
|
|
6d47df |
- def get_subject_public_key_info(self):
|
|
|
6d47df |
- temp_cn = base64.b32encode(os.urandom(40)).decode('ascii')
|
|
|
6d47df |
-
|
|
|
6d47df |
- password_args = []
|
|
|
6d47df |
- if self.password_filename is not None:
|
|
|
6d47df |
- password_args = ['-f', self.password_filename]
|
|
|
6d47df |
-
|
|
|
6d47df |
- subprocess.check_call(
|
|
|
6d47df |
- ['certutil', '-S', '-n', self.nickname, '-s', 'CN=%s' % temp_cn,
|
|
|
6d47df |
- '-x', '-t', ',,', '-d', self.database] + password_args)
|
|
|
6d47df |
- cert_pem = subprocess.check_output(
|
|
|
6d47df |
- ['certutil', '-L', '-n', self.nickname, '-a',
|
|
|
6d47df |
- '-d', self.database] + password_args)
|
|
|
6d47df |
-
|
|
|
6d47df |
- cert = load_pem_x509_certificate(cert_pem, default_backend())
|
|
|
6d47df |
- pubkey_info = cert.public_key().public_bytes(
|
|
|
6d47df |
- Encoding.DER, PublicFormat.SubjectPublicKeyInfo)
|
|
|
6d47df |
-
|
|
|
6d47df |
- return pubkey_info
|
|
|
6d47df |
-
|
|
|
6d47df |
- def sign_csr(self, certification_request_info):
|
|
|
6d47df |
- raise NotImplementedError('NSS is not yet supported')
|
|
|
6d47df |
diff --git a/ipaclient/csrgen/profiles/caIPAserviceCert.json b/ipaclient/csrgen/profiles/caIPAserviceCert.json
|
|
|
6d47df |
deleted file mode 100644
|
|
|
6d47df |
index 114d2ff..0000000
|
|
|
6d47df |
--- a/ipaclient/csrgen/profiles/caIPAserviceCert.json
|
|
|
6d47df |
+++ /dev/null
|
|
|
6d47df |
@@ -1,15 +0,0 @@
|
|
|
6d47df |
-[
|
|
|
6d47df |
- {
|
|
|
6d47df |
- "syntax": "syntaxSubject",
|
|
|
6d47df |
- "data": [
|
|
|
6d47df |
- "dataHostCN",
|
|
|
6d47df |
- "dataSubjectBase"
|
|
|
6d47df |
- ]
|
|
|
6d47df |
- },
|
|
|
6d47df |
- {
|
|
|
6d47df |
- "syntax": "syntaxSAN",
|
|
|
6d47df |
- "data": [
|
|
|
6d47df |
- "dataDNS"
|
|
|
6d47df |
- ]
|
|
|
6d47df |
- }
|
|
|
6d47df |
-]
|
|
|
6d47df |
diff --git a/ipaclient/csrgen/profiles/userCert.json b/ipaclient/csrgen/profiles/userCert.json
|
|
|
6d47df |
deleted file mode 100644
|
|
|
6d47df |
index d6cf5cf..0000000
|
|
|
6d47df |
--- a/ipaclient/csrgen/profiles/userCert.json
|
|
|
6d47df |
+++ /dev/null
|
|
|
6d47df |
@@ -1,15 +0,0 @@
|
|
|
6d47df |
-[
|
|
|
6d47df |
- {
|
|
|
6d47df |
- "syntax": "syntaxSubject",
|
|
|
6d47df |
- "data": [
|
|
|
6d47df |
- "dataUsernameCN",
|
|
|
6d47df |
- "dataSubjectBase"
|
|
|
6d47df |
- ]
|
|
|
6d47df |
- },
|
|
|
6d47df |
- {
|
|
|
6d47df |
- "syntax": "syntaxSAN",
|
|
|
6d47df |
- "data": [
|
|
|
6d47df |
- "dataEmail"
|
|
|
6d47df |
- ]
|
|
|
6d47df |
- }
|
|
|
6d47df |
-]
|
|
|
6d47df |
diff --git a/ipaclient/csrgen/rules/dataDNS.json b/ipaclient/csrgen/rules/dataDNS.json
|
|
|
6d47df |
deleted file mode 100644
|
|
|
6d47df |
index a79a3d7..0000000
|
|
|
6d47df |
--- a/ipaclient/csrgen/rules/dataDNS.json
|
|
|
6d47df |
+++ /dev/null
|
|
|
6d47df |
@@ -1,8 +0,0 @@
|
|
|
6d47df |
-{
|
|
|
6d47df |
- "rule": {
|
|
|
6d47df |
- "template": "DNS = {{subject.krbprincipalname.0.partition('/')[2].partition('@')[0]}}"
|
|
|
6d47df |
- },
|
|
|
6d47df |
- "options": {
|
|
|
6d47df |
- "data_source": "subject.krbprincipalname.0.partition('/')[2].partition('@')[0]"
|
|
|
6d47df |
- }
|
|
|
6d47df |
-}
|
|
|
6d47df |
diff --git a/ipaclient/csrgen/rules/dataEmail.json b/ipaclient/csrgen/rules/dataEmail.json
|
|
|
6d47df |
deleted file mode 100644
|
|
|
6d47df |
index 4be6cec..0000000
|
|
|
6d47df |
--- a/ipaclient/csrgen/rules/dataEmail.json
|
|
|
6d47df |
+++ /dev/null
|
|
|
6d47df |
@@ -1,8 +0,0 @@
|
|
|
6d47df |
-{
|
|
|
6d47df |
- "rule": {
|
|
|
6d47df |
- "template": "email = {{subject.mail.0}}"
|
|
|
6d47df |
- },
|
|
|
6d47df |
- "options": {
|
|
|
6d47df |
- "data_source": "subject.mail.0"
|
|
|
6d47df |
- }
|
|
|
6d47df |
-}
|
|
|
6d47df |
diff --git a/ipaclient/csrgen/rules/dataHostCN.json b/ipaclient/csrgen/rules/dataHostCN.json
|
|
|
6d47df |
deleted file mode 100644
|
|
|
6d47df |
index f30c50f..0000000
|
|
|
6d47df |
--- a/ipaclient/csrgen/rules/dataHostCN.json
|
|
|
6d47df |
+++ /dev/null
|
|
|
6d47df |
@@ -1,8 +0,0 @@
|
|
|
6d47df |
-{
|
|
|
6d47df |
- "rule": {
|
|
|
6d47df |
- "template": "CN={{subject.krbprincipalname.0.partition('/')[2].partition('@')[0]}}"
|
|
|
6d47df |
- },
|
|
|
6d47df |
- "options": {
|
|
|
6d47df |
- "data_source": "subject.krbprincipalname.0.partition('/')[2].partition('@')[0]"
|
|
|
6d47df |
- }
|
|
|
6d47df |
-}
|
|
|
6d47df |
diff --git a/ipaclient/csrgen/rules/dataSubjectBase.json b/ipaclient/csrgen/rules/dataSubjectBase.json
|
|
|
6d47df |
deleted file mode 100644
|
|
|
6d47df |
index 31a38b4..0000000
|
|
|
6d47df |
--- a/ipaclient/csrgen/rules/dataSubjectBase.json
|
|
|
6d47df |
+++ /dev/null
|
|
|
6d47df |
@@ -1,8 +0,0 @@
|
|
|
6d47df |
-{
|
|
|
6d47df |
- "rule": {
|
|
|
6d47df |
- "template": "{{config.ipacertificatesubjectbase.0}}"
|
|
|
6d47df |
- },
|
|
|
6d47df |
- "options": {
|
|
|
6d47df |
- "data_source": "config.ipacertificatesubjectbase.0"
|
|
|
6d47df |
- }
|
|
|
6d47df |
-}
|
|
|
6d47df |
diff --git a/ipaclient/csrgen/rules/dataUsernameCN.json b/ipaclient/csrgen/rules/dataUsernameCN.json
|
|
|
6d47df |
deleted file mode 100644
|
|
|
6d47df |
index acbb524..0000000
|
|
|
6d47df |
--- a/ipaclient/csrgen/rules/dataUsernameCN.json
|
|
|
6d47df |
+++ /dev/null
|
|
|
6d47df |
@@ -1,8 +0,0 @@
|
|
|
6d47df |
-{
|
|
|
6d47df |
- "rule": {
|
|
|
6d47df |
- "template": "CN={{subject.uid.0}}"
|
|
|
6d47df |
- },
|
|
|
6d47df |
- "options": {
|
|
|
6d47df |
- "data_source": "subject.uid.0"
|
|
|
6d47df |
- }
|
|
|
6d47df |
-}
|
|
|
6d47df |
diff --git a/ipaclient/csrgen/rules/syntaxSAN.json b/ipaclient/csrgen/rules/syntaxSAN.json
|
|
|
6d47df |
deleted file mode 100644
|
|
|
6d47df |
index c6943ed..0000000
|
|
|
6d47df |
--- a/ipaclient/csrgen/rules/syntaxSAN.json
|
|
|
6d47df |
+++ /dev/null
|
|
|
6d47df |
@@ -1,8 +0,0 @@
|
|
|
6d47df |
-{
|
|
|
6d47df |
- "rule": {
|
|
|
6d47df |
- "template": "subjectAltName = @{% call openssl.section() %}{{ datarules|join('\n') }}{% endcall %}"
|
|
|
6d47df |
- },
|
|
|
6d47df |
- "options": {
|
|
|
6d47df |
- "extension": true
|
|
|
6d47df |
- }
|
|
|
6d47df |
-}
|
|
|
6d47df |
diff --git a/ipaclient/csrgen/rules/syntaxSubject.json b/ipaclient/csrgen/rules/syntaxSubject.json
|
|
|
6d47df |
deleted file mode 100644
|
|
|
6d47df |
index c609e01..0000000
|
|
|
6d47df |
--- a/ipaclient/csrgen/rules/syntaxSubject.json
|
|
|
6d47df |
+++ /dev/null
|
|
|
6d47df |
@@ -1,9 +0,0 @@
|
|
|
6d47df |
-{
|
|
|
6d47df |
- "rule": {
|
|
|
6d47df |
- "template": "distinguished_name = {% call openssl.section() %}{{ datarules|reverse|join('\n') }}{% endcall %}"
|
|
|
6d47df |
- },
|
|
|
6d47df |
- "options": {
|
|
|
6d47df |
- "required": true,
|
|
|
6d47df |
- "data_source_combinator": "and"
|
|
|
6d47df |
- }
|
|
|
6d47df |
-}
|
|
|
6d47df |
diff --git a/ipaclient/csrgen/templates/openssl_base.tmpl b/ipaclient/csrgen/templates/openssl_base.tmpl
|
|
|
6d47df |
deleted file mode 100644
|
|
|
6d47df |
index 8d37994..0000000
|
|
|
6d47df |
--- a/ipaclient/csrgen/templates/openssl_base.tmpl
|
|
|
6d47df |
+++ /dev/null
|
|
|
6d47df |
@@ -1,17 +0,0 @@
|
|
|
6d47df |
-{% raw -%}
|
|
|
6d47df |
-{% import "openssl_macros.tmpl" as openssl -%}
|
|
|
6d47df |
-{% endraw -%}
|
|
|
6d47df |
-[ req ]
|
|
|
6d47df |
-prompt = no
|
|
|
6d47df |
-encrypt_key = no
|
|
|
6d47df |
-
|
|
|
6d47df |
-{{ parameters|join('\n') }}
|
|
|
6d47df |
-{% raw %}{% set rendered_extensions -%}{% endraw %}
|
|
|
6d47df |
-{{ extensions|join('\n') }}
|
|
|
6d47df |
-{% raw -%}
|
|
|
6d47df |
-{%- endset -%}
|
|
|
6d47df |
-{% if rendered_extensions -%}
|
|
|
6d47df |
-req_extensions = {% call openssl.section() %}{{ rendered_extensions }}{% endcall %}
|
|
|
6d47df |
-{% endif %}
|
|
|
6d47df |
-{{ openssl.openssl_sections|join('\n\n') }}
|
|
|
6d47df |
-{%- endraw %}
|
|
|
6d47df |
diff --git a/ipaclient/csrgen/templates/openssl_macros.tmpl b/ipaclient/csrgen/templates/openssl_macros.tmpl
|
|
|
6d47df |
deleted file mode 100644
|
|
|
6d47df |
index d31b8fe..0000000
|
|
|
6d47df |
--- a/ipaclient/csrgen/templates/openssl_macros.tmpl
|
|
|
6d47df |
+++ /dev/null
|
|
|
6d47df |
@@ -1,29 +0,0 @@
|
|
|
6d47df |
-{# List containing rendered sections to be included at end #}
|
|
|
6d47df |
-{% set openssl_sections = [] %}
|
|
|
6d47df |
-
|
|
|
6d47df |
-{#
|
|
|
6d47df |
-List containing one entry for each section name allocated. Because of
|
|
|
6d47df |
-scoping rules, we need to use a list so that it can be a "per-render global"
|
|
|
6d47df |
-that gets updated in place. Real globals are shared by all templates with the
|
|
|
6d47df |
-same environment, and variables defined in the macro don't persist after the
|
|
|
6d47df |
-macro invocation ends.
|
|
|
6d47df |
-#}
|
|
|
6d47df |
-{% set openssl_section_num = [] %}
|
|
|
6d47df |
-
|
|
|
6d47df |
-{% macro section() -%}
|
|
|
6d47df |
-{% set name -%}
|
|
|
6d47df |
-sec{{ openssl_section_num|length -}}
|
|
|
6d47df |
-{% endset -%}
|
|
|
6d47df |
-{% do openssl_section_num.append('') -%}
|
|
|
6d47df |
-{% set contents %}{{ caller() }}{% endset -%}
|
|
|
6d47df |
-{% if contents -%}
|
|
|
6d47df |
-{% set sectiondata = formatsection(name, contents) -%}
|
|
|
6d47df |
-{% do openssl_sections.append(sectiondata) -%}
|
|
|
6d47df |
-{% endif -%}
|
|
|
6d47df |
-{{ name -}}
|
|
|
6d47df |
-{% endmacro %}
|
|
|
6d47df |
-
|
|
|
6d47df |
-{% macro formatsection(name, contents) -%}
|
|
|
6d47df |
-[ {{ name }} ]
|
|
|
6d47df |
-{{ contents -}}
|
|
|
6d47df |
-{% endmacro %}
|
|
|
6d47df |
diff --git a/ipaclient/csrgen_ffi.py b/ipaclient/csrgen_ffi.py
|
|
|
6d47df |
deleted file mode 100644
|
|
|
6d47df |
index 10a20bd..0000000
|
|
|
6d47df |
--- a/ipaclient/csrgen_ffi.py
|
|
|
6d47df |
+++ /dev/null
|
|
|
6d47df |
@@ -1,331 +0,0 @@
|
|
|
6d47df |
-from cffi import FFI
|
|
|
6d47df |
-import ctypes.util
|
|
|
6d47df |
-
|
|
|
6d47df |
-from ipalib import errors
|
|
|
6d47df |
-
|
|
|
6d47df |
-_ffi = FFI()
|
|
|
6d47df |
-
|
|
|
6d47df |
-_ffi.cdef('''
|
|
|
6d47df |
-typedef ... CONF;
|
|
|
6d47df |
-typedef ... CONF_METHOD;
|
|
|
6d47df |
-typedef ... BIO;
|
|
|
6d47df |
-typedef ... ipa_STACK_OF_CONF_VALUE;
|
|
|
6d47df |
-
|
|
|
6d47df |
-/* openssl/conf.h */
|
|
|
6d47df |
-typedef struct {
|
|
|
6d47df |
- char *section;
|
|
|
6d47df |
- char *name;
|
|
|
6d47df |
- char *value;
|
|
|
6d47df |
-} CONF_VALUE;
|
|
|
6d47df |
-
|
|
|
6d47df |
-CONF *NCONF_new(CONF_METHOD *meth);
|
|
|
6d47df |
-void NCONF_free(CONF *conf);
|
|
|
6d47df |
-int NCONF_load_bio(CONF *conf, BIO *bp, long *eline);
|
|
|
6d47df |
-ipa_STACK_OF_CONF_VALUE *NCONF_get_section(const CONF *conf,
|
|
|
6d47df |
- const char *section);
|
|
|
6d47df |
-char *NCONF_get_string(const CONF *conf, const char *group, const char *name);
|
|
|
6d47df |
-
|
|
|
6d47df |
-/* openssl/safestack.h */
|
|
|
6d47df |
-// int sk_CONF_VALUE_num(ipa_STACK_OF_CONF_VALUE *);
|
|
|
6d47df |
-// CONF_VALUE *sk_CONF_VALUE_value(ipa_STACK_OF_CONF_VALUE *, int);
|
|
|
6d47df |
-
|
|
|
6d47df |
-/* openssl/stack.h */
|
|
|
6d47df |
-typedef ... _STACK;
|
|
|
6d47df |
-
|
|
|
6d47df |
-int OPENSSL_sk_num(const _STACK *);
|
|
|
6d47df |
-void *OPENSSL_sk_value(const _STACK *, int);
|
|
|
6d47df |
-
|
|
|
6d47df |
-int sk_num(const _STACK *);
|
|
|
6d47df |
-void *sk_value(const _STACK *, int);
|
|
|
6d47df |
-
|
|
|
6d47df |
-/* openssl/bio.h */
|
|
|
6d47df |
-BIO *BIO_new_mem_buf(const void *buf, int len);
|
|
|
6d47df |
-int BIO_free(BIO *a);
|
|
|
6d47df |
-
|
|
|
6d47df |
-/* openssl/asn1.h */
|
|
|
6d47df |
-typedef struct ASN1_ENCODING_st {
|
|
|
6d47df |
- unsigned char *enc; /* DER encoding */
|
|
|
6d47df |
- long len; /* Length of encoding */
|
|
|
6d47df |
- int modified; /* set to 1 if 'enc' is invalid */
|
|
|
6d47df |
-} ASN1_ENCODING;
|
|
|
6d47df |
-
|
|
|
6d47df |
-/* openssl/evp.h */
|
|
|
6d47df |
-typedef ... EVP_PKEY;
|
|
|
6d47df |
-
|
|
|
6d47df |
-void EVP_PKEY_free(EVP_PKEY *pkey);
|
|
|
6d47df |
-
|
|
|
6d47df |
-/* openssl/x509.h */
|
|
|
6d47df |
-typedef ... ASN1_INTEGER;
|
|
|
6d47df |
-typedef ... ASN1_BIT_STRING;
|
|
|
6d47df |
-typedef ... ASN1_OBJECT;
|
|
|
6d47df |
-typedef ... X509;
|
|
|
6d47df |
-typedef ... X509_ALGOR;
|
|
|
6d47df |
-typedef ... X509_CRL;
|
|
|
6d47df |
-typedef ... X509_NAME;
|
|
|
6d47df |
-typedef ... X509_PUBKEY;
|
|
|
6d47df |
-typedef ... ipa_STACK_OF_X509_ATTRIBUTE;
|
|
|
6d47df |
-
|
|
|
6d47df |
-typedef struct X509_req_info_st {
|
|
|
6d47df |
- ASN1_ENCODING enc;
|
|
|
6d47df |
- ASN1_INTEGER *version;
|
|
|
6d47df |
- X509_NAME *subject;
|
|
|
6d47df |
- X509_PUBKEY *pubkey;
|
|
|
6d47df |
- /* d=2 hl=2 l= 0 cons: cont: 00 */
|
|
|
6d47df |
- ipa_STACK_OF_X509_ATTRIBUTE *attributes; /* [ 0 ] */
|
|
|
6d47df |
-} X509_REQ_INFO;
|
|
|
6d47df |
-
|
|
|
6d47df |
-typedef struct X509_req_st {
|
|
|
6d47df |
- X509_REQ_INFO *req_info;
|
|
|
6d47df |
- X509_ALGOR *sig_alg;
|
|
|
6d47df |
- ASN1_BIT_STRING *signature;
|
|
|
6d47df |
- int references;
|
|
|
6d47df |
-} X509_REQ;
|
|
|
6d47df |
-
|
|
|
6d47df |
-X509_REQ *X509_REQ_new(void);
|
|
|
6d47df |
-void X509_REQ_free(X509_REQ *);
|
|
|
6d47df |
-EVP_PKEY *d2i_PUBKEY_bio(BIO *bp, EVP_PKEY **a);
|
|
|
6d47df |
-int X509_REQ_set_pubkey(X509_REQ *x, EVP_PKEY *pkey);
|
|
|
6d47df |
-int X509_NAME_add_entry_by_OBJ(X509_NAME *name, const ASN1_OBJECT *obj, int type,
|
|
|
6d47df |
- const unsigned char *bytes, int len, int loc,
|
|
|
6d47df |
- int set);
|
|
|
6d47df |
-int X509_NAME_entry_count(X509_NAME *name);
|
|
|
6d47df |
-int i2d_X509_REQ_INFO(X509_REQ_INFO *a, unsigned char **out);
|
|
|
6d47df |
-
|
|
|
6d47df |
-/* openssl/objects.h */
|
|
|
6d47df |
-ASN1_OBJECT *OBJ_txt2obj(const char *s, int no_name);
|
|
|
6d47df |
-
|
|
|
6d47df |
-/* openssl/x509v3.h */
|
|
|
6d47df |
-typedef ... X509V3_CONF_METHOD;
|
|
|
6d47df |
-
|
|
|
6d47df |
-typedef struct v3_ext_ctx {
|
|
|
6d47df |
- int flags;
|
|
|
6d47df |
- X509 *issuer_cert;
|
|
|
6d47df |
- X509 *subject_cert;
|
|
|
6d47df |
- X509_REQ *subject_req;
|
|
|
6d47df |
- X509_CRL *crl;
|
|
|
6d47df |
- X509V3_CONF_METHOD *db_meth;
|
|
|
6d47df |
- void *db;
|
|
|
6d47df |
-} X509V3_CTX;
|
|
|
6d47df |
-
|
|
|
6d47df |
-void X509V3_set_ctx(X509V3_CTX *ctx, X509 *issuer, X509 *subject,
|
|
|
6d47df |
- X509_REQ *req, X509_CRL *crl, int flags);
|
|
|
6d47df |
-void X509V3_set_nconf(X509V3_CTX *ctx, CONF *conf);
|
|
|
6d47df |
-int X509V3_EXT_REQ_add_nconf(CONF *conf, X509V3_CTX *ctx, char *section,
|
|
|
6d47df |
- X509_REQ *req);
|
|
|
6d47df |
-
|
|
|
6d47df |
-/* openssl/x509v3.h */
|
|
|
6d47df |
-unsigned long ERR_get_error(void);
|
|
|
6d47df |
-char *ERR_error_string(unsigned long e, char *buf);
|
|
|
6d47df |
-''') # noqa: E501
|
|
|
6d47df |
-
|
|
|
6d47df |
-_libcrypto = _ffi.dlopen(ctypes.util.find_library('crypto'))
|
|
|
6d47df |
-
|
|
|
6d47df |
-NULL = _ffi.NULL
|
|
|
6d47df |
-
|
|
|
6d47df |
-# openssl/conf.h
|
|
|
6d47df |
-NCONF_new = _libcrypto.NCONF_new
|
|
|
6d47df |
-NCONF_free = _libcrypto.NCONF_free
|
|
|
6d47df |
-NCONF_load_bio = _libcrypto.NCONF_load_bio
|
|
|
6d47df |
-NCONF_get_section = _libcrypto.NCONF_get_section
|
|
|
6d47df |
-NCONF_get_string = _libcrypto.NCONF_get_string
|
|
|
6d47df |
-
|
|
|
6d47df |
-# openssl/stack.h
|
|
|
6d47df |
-try:
|
|
|
6d47df |
- sk_num = _libcrypto.OPENSSL_sk_num
|
|
|
6d47df |
- sk_value = _libcrypto.OPENSSL_sk_value
|
|
|
6d47df |
-except AttributeError:
|
|
|
6d47df |
- sk_num = _libcrypto.sk_num
|
|
|
6d47df |
- sk_value = _libcrypto.sk_value
|
|
|
6d47df |
-
|
|
|
6d47df |
-
|
|
|
6d47df |
-def sk_CONF_VALUE_num(sk):
|
|
|
6d47df |
- return sk_num(_ffi.cast("_STACK *", sk))
|
|
|
6d47df |
-
|
|
|
6d47df |
-
|
|
|
6d47df |
-def sk_CONF_VALUE_value(sk, i):
|
|
|
6d47df |
- return _ffi.cast("CONF_VALUE *", sk_value(_ffi.cast("_STACK *", sk), i))
|
|
|
6d47df |
-
|
|
|
6d47df |
-
|
|
|
6d47df |
-# openssl/bio.h
|
|
|
6d47df |
-BIO_new_mem_buf = _libcrypto.BIO_new_mem_buf
|
|
|
6d47df |
-BIO_free = _libcrypto.BIO_free
|
|
|
6d47df |
-
|
|
|
6d47df |
-# openssl/x509.h
|
|
|
6d47df |
-X509_REQ_new = _libcrypto.X509_REQ_new
|
|
|
6d47df |
-X509_REQ_free = _libcrypto.X509_REQ_free
|
|
|
6d47df |
-X509_REQ_set_pubkey = _libcrypto.X509_REQ_set_pubkey
|
|
|
6d47df |
-d2i_PUBKEY_bio = _libcrypto.d2i_PUBKEY_bio
|
|
|
6d47df |
-i2d_X509_REQ_INFO = _libcrypto.i2d_X509_REQ_INFO
|
|
|
6d47df |
-X509_NAME_add_entry_by_OBJ = _libcrypto.X509_NAME_add_entry_by_OBJ
|
|
|
6d47df |
-X509_NAME_entry_count = _libcrypto.X509_NAME_entry_count
|
|
|
6d47df |
-
|
|
|
6d47df |
-
|
|
|
6d47df |
-def X509_REQ_get_subject_name(req):
|
|
|
6d47df |
- return req.req_info.subject
|
|
|
6d47df |
-
|
|
|
6d47df |
-
|
|
|
6d47df |
-# openssl/objects.h
|
|
|
6d47df |
-OBJ_txt2obj = _libcrypto.OBJ_txt2obj
|
|
|
6d47df |
-
|
|
|
6d47df |
-# openssl/evp.h
|
|
|
6d47df |
-EVP_PKEY_free = _libcrypto.EVP_PKEY_free
|
|
|
6d47df |
-
|
|
|
6d47df |
-# openssl/asn1.h
|
|
|
6d47df |
-MBSTRING_UTF8 = 0x1000
|
|
|
6d47df |
-
|
|
|
6d47df |
-# openssl/x509v3.h
|
|
|
6d47df |
-X509V3_set_ctx = _libcrypto.X509V3_set_ctx
|
|
|
6d47df |
-X509V3_set_nconf = _libcrypto.X509V3_set_nconf
|
|
|
6d47df |
-X509V3_EXT_REQ_add_nconf = _libcrypto.X509V3_EXT_REQ_add_nconf
|
|
|
6d47df |
-
|
|
|
6d47df |
-# openssl/err.h
|
|
|
6d47df |
-ERR_get_error = _libcrypto.ERR_get_error
|
|
|
6d47df |
-ERR_error_string = _libcrypto.ERR_error_string
|
|
|
6d47df |
-
|
|
|
6d47df |
-
|
|
|
6d47df |
-def _raise_openssl_errors():
|
|
|
6d47df |
- msgs = []
|
|
|
6d47df |
-
|
|
|
6d47df |
- code = ERR_get_error()
|
|
|
6d47df |
- while code != 0:
|
|
|
6d47df |
- msg = _ffi.string(ERR_error_string(code, NULL))
|
|
|
6d47df |
- try:
|
|
|
6d47df |
- strmsg = msg.decode('utf-8')
|
|
|
6d47df |
- except UnicodeDecodeError:
|
|
|
6d47df |
- strmsg = repr(msg)
|
|
|
6d47df |
- msgs.append(strmsg)
|
|
|
6d47df |
- code = ERR_get_error()
|
|
|
6d47df |
-
|
|
|
6d47df |
- raise errors.CSRTemplateError(reason='\n'.join(msgs))
|
|
|
6d47df |
-
|
|
|
6d47df |
-
|
|
|
6d47df |
-def _parse_dn_section(subj, dn_sk):
|
|
|
6d47df |
- for i in range(sk_CONF_VALUE_num(dn_sk)):
|
|
|
6d47df |
- v = sk_CONF_VALUE_value(dn_sk, i)
|
|
|
6d47df |
- rdn_type = _ffi.string(v.name)
|
|
|
6d47df |
-
|
|
|
6d47df |
- # Skip past any leading X. X: X, etc to allow for multiple instances
|
|
|
6d47df |
- for idx, c in enumerate(rdn_type):
|
|
|
6d47df |
- if c in b':,.':
|
|
|
6d47df |
- if idx+1 < len(rdn_type):
|
|
|
6d47df |
- rdn_type = rdn_type[idx+1:]
|
|
|
6d47df |
- break
|
|
|
6d47df |
- if rdn_type.startswith(b'+'):
|
|
|
6d47df |
- rdn_type = rdn_type[1:]
|
|
|
6d47df |
- mval = -1
|
|
|
6d47df |
- else:
|
|
|
6d47df |
- mval = 0
|
|
|
6d47df |
-
|
|
|
6d47df |
- # convert rdn_type to an OID
|
|
|
6d47df |
- #
|
|
|
6d47df |
- # OpenSSL is fussy about the case of the string. For example,
|
|
|
6d47df |
- # lower-case 'o' (for "organization name") is not recognised.
|
|
|
6d47df |
- # Therefore, try to convert the given string into an OID. If
|
|
|
6d47df |
- # that fails, convert it upper case and try again.
|
|
|
6d47df |
- #
|
|
|
6d47df |
- oid = OBJ_txt2obj(rdn_type, 0)
|
|
|
6d47df |
- if oid == NULL:
|
|
|
6d47df |
- oid = OBJ_txt2obj(rdn_type.upper(), 0)
|
|
|
6d47df |
- if oid == NULL:
|
|
|
6d47df |
- raise errors.CSRTemplateError(
|
|
|
6d47df |
- reason='unrecognised attribute type: {}'
|
|
|
6d47df |
- .format(rdn_type.decode('utf-8')))
|
|
|
6d47df |
-
|
|
|
6d47df |
- if not X509_NAME_add_entry_by_OBJ(
|
|
|
6d47df |
- subj, oid, MBSTRING_UTF8,
|
|
|
6d47df |
- _ffi.cast("unsigned char *", v.value), -1, -1, mval):
|
|
|
6d47df |
- _raise_openssl_errors()
|
|
|
6d47df |
-
|
|
|
6d47df |
- if not X509_NAME_entry_count(subj):
|
|
|
6d47df |
- raise errors.CSRTemplateError(
|
|
|
6d47df |
- reason='error, subject in config file is empty')
|
|
|
6d47df |
-
|
|
|
6d47df |
-
|
|
|
6d47df |
-def build_requestinfo(config, public_key_info):
|
|
|
6d47df |
- '''
|
|
|
6d47df |
- Return a cffi buffer containing a DER-encoded CertificationRequestInfo.
|
|
|
6d47df |
-
|
|
|
6d47df |
- The returned object implements the buffer protocol.
|
|
|
6d47df |
-
|
|
|
6d47df |
- '''
|
|
|
6d47df |
- reqdata = NULL
|
|
|
6d47df |
- req = NULL
|
|
|
6d47df |
- nconf_bio = NULL
|
|
|
6d47df |
- pubkey_bio = NULL
|
|
|
6d47df |
- pubkey = NULL
|
|
|
6d47df |
-
|
|
|
6d47df |
- try:
|
|
|
6d47df |
- reqdata = NCONF_new(NULL)
|
|
|
6d47df |
- if reqdata == NULL:
|
|
|
6d47df |
- _raise_openssl_errors()
|
|
|
6d47df |
-
|
|
|
6d47df |
- nconf_bio = BIO_new_mem_buf(config, len(config))
|
|
|
6d47df |
- errorline = _ffi.new('long[1]', [-1])
|
|
|
6d47df |
- i = NCONF_load_bio(reqdata, nconf_bio, errorline)
|
|
|
6d47df |
- if i < 0:
|
|
|
6d47df |
- if errorline[0] < 0:
|
|
|
6d47df |
- raise errors.CSRTemplateError(reason="Can't load config file")
|
|
|
6d47df |
- else:
|
|
|
6d47df |
- raise errors.CSRTemplateError(
|
|
|
6d47df |
- reason='Error on line %d of config file' % errorline[0])
|
|
|
6d47df |
-
|
|
|
6d47df |
- dn_sect = NCONF_get_string(reqdata, b'req', b'distinguished_name')
|
|
|
6d47df |
- if dn_sect == NULL:
|
|
|
6d47df |
- raise errors.CSRTemplateError(
|
|
|
6d47df |
- reason='Unable to find "distinguished_name" key in config')
|
|
|
6d47df |
-
|
|
|
6d47df |
- dn_sk = NCONF_get_section(reqdata, dn_sect)
|
|
|
6d47df |
- if dn_sk == NULL:
|
|
|
6d47df |
- raise errors.CSRTemplateError(
|
|
|
6d47df |
- reason='Unable to find "%s" section in config' %
|
|
|
6d47df |
- _ffi.string(dn_sect))
|
|
|
6d47df |
-
|
|
|
6d47df |
- pubkey_bio = BIO_new_mem_buf(public_key_info, len(public_key_info))
|
|
|
6d47df |
- pubkey = d2i_PUBKEY_bio(pubkey_bio, NULL)
|
|
|
6d47df |
- if pubkey == NULL:
|
|
|
6d47df |
- _raise_openssl_errors()
|
|
|
6d47df |
-
|
|
|
6d47df |
- req = X509_REQ_new()
|
|
|
6d47df |
- if req == NULL:
|
|
|
6d47df |
- _raise_openssl_errors()
|
|
|
6d47df |
-
|
|
|
6d47df |
- subject = X509_REQ_get_subject_name(req)
|
|
|
6d47df |
-
|
|
|
6d47df |
- _parse_dn_section(subject, dn_sk)
|
|
|
6d47df |
-
|
|
|
6d47df |
- if not X509_REQ_set_pubkey(req, pubkey):
|
|
|
6d47df |
- _raise_openssl_errors()
|
|
|
6d47df |
-
|
|
|
6d47df |
- ext_ctx = _ffi.new("X509V3_CTX[1]")
|
|
|
6d47df |
- X509V3_set_ctx(ext_ctx, NULL, NULL, req, NULL, 0)
|
|
|
6d47df |
- X509V3_set_nconf(ext_ctx, reqdata)
|
|
|
6d47df |
-
|
|
|
6d47df |
- extn_section = NCONF_get_string(reqdata, b"req", b"req_extensions")
|
|
|
6d47df |
- if extn_section != NULL:
|
|
|
6d47df |
- if not X509V3_EXT_REQ_add_nconf(
|
|
|
6d47df |
- reqdata, ext_ctx, extn_section, req):
|
|
|
6d47df |
- _raise_openssl_errors()
|
|
|
6d47df |
-
|
|
|
6d47df |
- der_len = i2d_X509_REQ_INFO(req.req_info, NULL)
|
|
|
6d47df |
- if der_len < 0:
|
|
|
6d47df |
- _raise_openssl_errors()
|
|
|
6d47df |
-
|
|
|
6d47df |
- der_buf = _ffi.new("unsigned char[%d]" % der_len)
|
|
|
6d47df |
- der_out = _ffi.new("unsigned char **", der_buf)
|
|
|
6d47df |
- der_len = i2d_X509_REQ_INFO(req.req_info, der_out)
|
|
|
6d47df |
- if der_len < 0:
|
|
|
6d47df |
- _raise_openssl_errors()
|
|
|
6d47df |
-
|
|
|
6d47df |
- return _ffi.buffer(der_buf, der_len)
|
|
|
6d47df |
-
|
|
|
6d47df |
- finally:
|
|
|
6d47df |
- if reqdata != NULL:
|
|
|
6d47df |
- NCONF_free(reqdata)
|
|
|
6d47df |
- if req != NULL:
|
|
|
6d47df |
- X509_REQ_free(req)
|
|
|
6d47df |
- if nconf_bio != NULL:
|
|
|
6d47df |
- BIO_free(nconf_bio)
|
|
|
6d47df |
- if pubkey_bio != NULL:
|
|
|
6d47df |
- BIO_free(pubkey_bio)
|
|
|
6d47df |
- if pubkey != NULL:
|
|
|
6d47df |
- EVP_PKEY_free(pubkey)
|
|
|
6d47df |
diff --git a/ipaclient/plugins/cert.py b/ipaclient/plugins/cert.py
|
|
|
6d47df |
index 6d453d0..8a4db81 100644
|
|
|
6d47df |
--- a/ipaclient/plugins/cert.py
|
|
|
6d47df |
+++ b/ipaclient/plugins/cert.py
|
|
|
6d47df |
@@ -21,8 +21,6 @@
|
|
|
6d47df |
|
|
|
6d47df |
import base64
|
|
|
6d47df |
|
|
|
6d47df |
-import six
|
|
|
6d47df |
-
|
|
|
6d47df |
from ipaclient.frontend import MethodOverride
|
|
|
6d47df |
from ipalib import errors
|
|
|
6d47df |
from ipalib import x509
|
|
|
6d47df |
@@ -31,9 +29,6 @@ from ipalib.parameters import BinaryFile, File, Flag, Str
|
|
|
6d47df |
from ipalib.plugable import Registry
|
|
|
6d47df |
from ipalib.text import _
|
|
|
6d47df |
|
|
|
6d47df |
-if six.PY3:
|
|
|
6d47df |
- unicode = str
|
|
|
6d47df |
-
|
|
|
6d47df |
register = Registry()
|
|
|
6d47df |
|
|
|
6d47df |
|
|
|
6d47df |
@@ -74,87 +69,12 @@ class CertRetrieveOverride(MethodOverride):
|
|
|
6d47df |
|
|
|
6d47df |
@register(override=True, no_fail=True)
|
|
|
6d47df |
class cert_request(CertRetrieveOverride):
|
|
|
6d47df |
- takes_options = CertRetrieveOverride.takes_options + (
|
|
|
6d47df |
- Str(
|
|
|
6d47df |
- 'database?',
|
|
|
6d47df |
- label=_('Path to NSS database'),
|
|
|
6d47df |
- doc=_('Path to NSS database to use for private key'),
|
|
|
6d47df |
- ),
|
|
|
6d47df |
- Str(
|
|
|
6d47df |
- 'private_key?',
|
|
|
6d47df |
- label=_('Path to private key file'),
|
|
|
6d47df |
- doc=_('Path to PEM file containing a private key'),
|
|
|
6d47df |
- ),
|
|
|
6d47df |
- Str(
|
|
|
6d47df |
- 'password_file?',
|
|
|
6d47df |
- label=_(
|
|
|
6d47df |
- 'File containing a password for the private key or database'),
|
|
|
6d47df |
- ),
|
|
|
6d47df |
- Str(
|
|
|
6d47df |
- 'csr_profile_id?',
|
|
|
6d47df |
- label=_('Name of CSR generation profile (if not the same as'
|
|
|
6d47df |
- ' profile_id)'),
|
|
|
6d47df |
- ),
|
|
|
6d47df |
- )
|
|
|
6d47df |
-
|
|
|
6d47df |
def get_args(self):
|
|
|
6d47df |
for arg in super(cert_request, self).get_args():
|
|
|
6d47df |
if arg.name == 'csr':
|
|
|
6d47df |
arg = arg.clone_retype(arg.name, File, required=False)
|
|
|
6d47df |
yield arg
|
|
|
6d47df |
|
|
|
6d47df |
- def forward(self, csr=None, **options):
|
|
|
6d47df |
- database = options.pop('database', None)
|
|
|
6d47df |
- private_key = options.pop('private_key', None)
|
|
|
6d47df |
- csr_profile_id = options.pop('csr_profile_id', None)
|
|
|
6d47df |
- password_file = options.pop('password_file', None)
|
|
|
6d47df |
-
|
|
|
6d47df |
- if csr is None:
|
|
|
6d47df |
- # Deferred import, ipaclient.csrgen is expensive to load.
|
|
|
6d47df |
- # see https://pagure.io/freeipa/issue/7484
|
|
|
6d47df |
- from ipaclient import csrgen
|
|
|
6d47df |
-
|
|
|
6d47df |
- if database:
|
|
|
6d47df |
- adaptor = csrgen.NSSAdaptor(database, password_file)
|
|
|
6d47df |
- elif private_key:
|
|
|
6d47df |
- adaptor = csrgen.OpenSSLAdaptor(
|
|
|
6d47df |
- key_filename=private_key, password_filename=password_file)
|
|
|
6d47df |
- else:
|
|
|
6d47df |
- raise errors.InvocationError(
|
|
|
6d47df |
- message=u"One of 'database' or 'private_key' is required")
|
|
|
6d47df |
-
|
|
|
6d47df |
- pubkey_info = adaptor.get_subject_public_key_info()
|
|
|
6d47df |
- pubkey_info_b64 = base64.b64encode(pubkey_info)
|
|
|
6d47df |
-
|
|
|
6d47df |
- # If csr_profile_id is passed, that takes precedence.
|
|
|
6d47df |
- # Otherwise, use profile_id. If neither are passed, the default
|
|
|
6d47df |
- # in cert_get_requestdata will be used.
|
|
|
6d47df |
- profile_id = csr_profile_id
|
|
|
6d47df |
- if profile_id is None:
|
|
|
6d47df |
- profile_id = options.get('profile_id')
|
|
|
6d47df |
-
|
|
|
6d47df |
- response = self.api.Command.cert_get_requestdata(
|
|
|
6d47df |
- profile_id=profile_id,
|
|
|
6d47df |
- principal=options.get('principal'),
|
|
|
6d47df |
- public_key_info=pubkey_info_b64)
|
|
|
6d47df |
-
|
|
|
6d47df |
- req_info_b64 = response['result']['request_info']
|
|
|
6d47df |
- req_info = base64.b64decode(req_info_b64)
|
|
|
6d47df |
-
|
|
|
6d47df |
- csr = adaptor.sign_csr(req_info)
|
|
|
6d47df |
-
|
|
|
6d47df |
- if not csr:
|
|
|
6d47df |
- raise errors.CertificateOperationError(
|
|
|
6d47df |
- error=(_('Generated CSR was empty')))
|
|
|
6d47df |
-
|
|
|
6d47df |
- else:
|
|
|
6d47df |
- if database is not None or private_key is not None:
|
|
|
6d47df |
- raise errors.MutuallyExclusiveError(reason=_(
|
|
|
6d47df |
- "Options 'database' and 'private_key' are not compatible"
|
|
|
6d47df |
- " with 'csr'"))
|
|
|
6d47df |
-
|
|
|
6d47df |
- return super(cert_request, self).forward(csr, **options)
|
|
|
6d47df |
-
|
|
|
6d47df |
|
|
|
6d47df |
@register(override=True, no_fail=True)
|
|
|
6d47df |
class cert_show(CertRetrieveOverride):
|
|
|
6d47df |
diff --git a/ipaclient/plugins/csrgen.py b/ipaclient/plugins/csrgen.py
|
|
|
6d47df |
deleted file mode 100644
|
|
|
6d47df |
index 5aad636..0000000
|
|
|
6d47df |
--- a/ipaclient/plugins/csrgen.py
|
|
|
6d47df |
+++ /dev/null
|
|
|
6d47df |
@@ -1,128 +0,0 @@
|
|
|
6d47df |
-#
|
|
|
6d47df |
-# Copyright (C) 2016 FreeIPA Contributors see COPYING for license
|
|
|
6d47df |
-#
|
|
|
6d47df |
-
|
|
|
6d47df |
-import base64
|
|
|
6d47df |
-
|
|
|
6d47df |
-import six
|
|
|
6d47df |
-
|
|
|
6d47df |
-from ipalib import api
|
|
|
6d47df |
-from ipalib import errors
|
|
|
6d47df |
-from ipalib import output
|
|
|
6d47df |
-from ipalib import util
|
|
|
6d47df |
-from ipalib.frontend import Local, Str
|
|
|
6d47df |
-from ipalib.parameters import Bytes, Principal
|
|
|
6d47df |
-from ipalib.plugable import Registry
|
|
|
6d47df |
-from ipalib.text import _
|
|
|
6d47df |
-from ipapython import dogtag
|
|
|
6d47df |
-
|
|
|
6d47df |
-
|
|
|
6d47df |
-if six.PY3:
|
|
|
6d47df |
- unicode = str
|
|
|
6d47df |
-
|
|
|
6d47df |
-register = Registry()
|
|
|
6d47df |
-
|
|
|
6d47df |
-__doc__ = _("""
|
|
|
6d47df |
-Commands to build certificate requests automatically
|
|
|
6d47df |
-""")
|
|
|
6d47df |
-
|
|
|
6d47df |
-
|
|
|
6d47df |
-@register()
|
|
|
6d47df |
-class cert_get_requestdata(Local):
|
|
|
6d47df |
- __doc__ = _('Gather data for a certificate signing request.')
|
|
|
6d47df |
-
|
|
|
6d47df |
- NO_CLI = True
|
|
|
6d47df |
-
|
|
|
6d47df |
- takes_options = (
|
|
|
6d47df |
- Principal(
|
|
|
6d47df |
- 'principal',
|
|
|
6d47df |
- label=_('Principal'),
|
|
|
6d47df |
- doc=_('Principal for this certificate (e.g.'
|
|
|
6d47df |
- ' HTTP/test.example.com)'),
|
|
|
6d47df |
- ),
|
|
|
6d47df |
- Str(
|
|
|
6d47df |
- 'profile_id?',
|
|
|
6d47df |
- label=_('Profile ID'),
|
|
|
6d47df |
- doc=_('CSR Generation Profile to use'),
|
|
|
6d47df |
- ),
|
|
|
6d47df |
- Bytes(
|
|
|
6d47df |
- 'public_key_info',
|
|
|
6d47df |
- label=_('Subject Public Key Info'),
|
|
|
6d47df |
- doc=_('DER-encoded SubjectPublicKeyInfo structure'),
|
|
|
6d47df |
- ),
|
|
|
6d47df |
- Str(
|
|
|
6d47df |
- 'out?',
|
|
|
6d47df |
- doc=_('Write CertificationRequestInfo to file'),
|
|
|
6d47df |
- ),
|
|
|
6d47df |
- )
|
|
|
6d47df |
-
|
|
|
6d47df |
- has_output = (
|
|
|
6d47df |
- output.Output(
|
|
|
6d47df |
- 'result',
|
|
|
6d47df |
- type=dict,
|
|
|
6d47df |
- doc=_('Dictionary mapping variable name to value'),
|
|
|
6d47df |
- ),
|
|
|
6d47df |
- )
|
|
|
6d47df |
-
|
|
|
6d47df |
- has_output_params = (
|
|
|
6d47df |
- Str(
|
|
|
6d47df |
- 'request_info',
|
|
|
6d47df |
- label=_('CertificationRequestInfo structure'),
|
|
|
6d47df |
- )
|
|
|
6d47df |
- )
|
|
|
6d47df |
-
|
|
|
6d47df |
- def execute(self, *args, **options):
|
|
|
6d47df |
- # Deferred import, ipaclient.csrgen is expensive to load.
|
|
|
6d47df |
- # see https://pagure.io/freeipa/issue/7484
|
|
|
6d47df |
- from ipaclient import csrgen
|
|
|
6d47df |
- from ipaclient import csrgen_ffi
|
|
|
6d47df |
-
|
|
|
6d47df |
- if 'out' in options:
|
|
|
6d47df |
- util.check_writable_file(options['out'])
|
|
|
6d47df |
-
|
|
|
6d47df |
- principal = options.get('principal')
|
|
|
6d47df |
- profile_id = options.get('profile_id')
|
|
|
6d47df |
- if profile_id is None:
|
|
|
6d47df |
- profile_id = dogtag.DEFAULT_PROFILE
|
|
|
6d47df |
- public_key_info = options.get('public_key_info')
|
|
|
6d47df |
- public_key_info = base64.b64decode(public_key_info)
|
|
|
6d47df |
-
|
|
|
6d47df |
- if self.api.env.in_server:
|
|
|
6d47df |
- backend = self.api.Backend.ldap2
|
|
|
6d47df |
- else:
|
|
|
6d47df |
- backend = self.api.Backend.rpcclient
|
|
|
6d47df |
- if not backend.isconnected():
|
|
|
6d47df |
- backend.connect()
|
|
|
6d47df |
-
|
|
|
6d47df |
- try:
|
|
|
6d47df |
- if principal.is_host:
|
|
|
6d47df |
- principal_obj = api.Command.host_show(
|
|
|
6d47df |
- principal.hostname, all=True)
|
|
|
6d47df |
- elif principal.is_service:
|
|
|
6d47df |
- principal_obj = api.Command.service_show(
|
|
|
6d47df |
- unicode(principal), all=True)
|
|
|
6d47df |
- elif principal.is_user:
|
|
|
6d47df |
- principal_obj = api.Command.user_show(
|
|
|
6d47df |
- principal.username, all=True)
|
|
|
6d47df |
- except errors.NotFound:
|
|
|
6d47df |
- raise errors.NotFound(
|
|
|
6d47df |
- reason=_("The principal for this request doesn't exist."))
|
|
|
6d47df |
- principal_obj = principal_obj['result']
|
|
|
6d47df |
- config = api.Command.config_show()['result']
|
|
|
6d47df |
-
|
|
|
6d47df |
- generator = csrgen.CSRGenerator(csrgen.FileRuleProvider())
|
|
|
6d47df |
-
|
|
|
6d47df |
- csr_config = generator.csr_config(principal_obj, config, profile_id)
|
|
|
6d47df |
- request_info = base64.b64encode(csrgen_ffi.build_requestinfo(
|
|
|
6d47df |
- csr_config.encode('utf8'), public_key_info))
|
|
|
6d47df |
-
|
|
|
6d47df |
- result = {}
|
|
|
6d47df |
- if 'out' in options:
|
|
|
6d47df |
- with open(options['out'], 'wb') as f:
|
|
|
6d47df |
- f.write(request_info)
|
|
|
6d47df |
- else:
|
|
|
6d47df |
- result = dict(request_info=request_info)
|
|
|
6d47df |
-
|
|
|
6d47df |
- return dict(
|
|
|
6d47df |
- result=result
|
|
|
6d47df |
- )
|
|
|
6d47df |
diff --git a/ipaclient/setup.py b/ipaclient/setup.py
|
|
|
6d47df |
index 8eb9aee..cd03685 100644
|
|
|
6d47df |
--- a/ipaclient/setup.py
|
|
|
6d47df |
+++ b/ipaclient/setup.py
|
|
|
6d47df |
@@ -41,13 +41,6 @@ if __name__ == '__main__':
|
|
|
6d47df |
"ipaclient.remote_plugins.2_156",
|
|
|
6d47df |
"ipaclient.remote_plugins.2_164",
|
|
|
6d47df |
],
|
|
|
6d47df |
- package_data={
|
|
|
6d47df |
- 'ipaclient': [
|
|
|
6d47df |
- 'csrgen/profiles/*.json',
|
|
|
6d47df |
- 'csrgen/rules/*.json',
|
|
|
6d47df |
- 'csrgen/templates/*.tmpl',
|
|
|
6d47df |
- ],
|
|
|
6d47df |
- },
|
|
|
6d47df |
install_requires=[
|
|
|
6d47df |
"cryptography",
|
|
|
6d47df |
"ipalib",
|
|
|
6d47df |
@@ -63,7 +56,6 @@ if __name__ == '__main__':
|
|
|
6d47df |
extras_require={
|
|
|
6d47df |
"install": ["ipaplatform"],
|
|
|
6d47df |
"otptoken_yubikey": ["python-yubico", "pyusb"],
|
|
|
6d47df |
- "csrgen": ["cffi", "jinja2"],
|
|
|
6d47df |
},
|
|
|
6d47df |
zip_safe=False,
|
|
|
6d47df |
)
|
|
|
6d47df |
diff --git a/ipatests/test_ipaclient/data/test_csrgen/configs/caIPAserviceCert.conf b/ipatests/test_ipaclient/data/test_csrgen/configs/caIPAserviceCert.conf
|
|
|
6d47df |
deleted file mode 100644
|
|
|
6d47df |
index 3724bdc..0000000
|
|
|
6d47df |
--- a/ipatests/test_ipaclient/data/test_csrgen/configs/caIPAserviceCert.conf
|
|
|
6d47df |
+++ /dev/null
|
|
|
6d47df |
@@ -1,16 +0,0 @@
|
|
|
6d47df |
-[ req ]
|
|
|
6d47df |
-prompt = no
|
|
|
6d47df |
-encrypt_key = no
|
|
|
6d47df |
-
|
|
|
6d47df |
-distinguished_name = sec0
|
|
|
6d47df |
-req_extensions = sec2
|
|
|
6d47df |
-
|
|
|
6d47df |
-[ sec0 ]
|
|
|
6d47df |
-O=DOMAIN.EXAMPLE.COM
|
|
|
6d47df |
-CN=machine.example.com
|
|
|
6d47df |
-
|
|
|
6d47df |
-[ sec1 ]
|
|
|
6d47df |
-DNS = machine.example.com
|
|
|
6d47df |
-
|
|
|
6d47df |
-[ sec2 ]
|
|
|
6d47df |
-subjectAltName = @sec1
|
|
|
6d47df |
diff --git a/ipatests/test_ipaclient/data/test_csrgen/configs/userCert.conf b/ipatests/test_ipaclient/data/test_csrgen/configs/userCert.conf
|
|
|
6d47df |
deleted file mode 100644
|
|
|
6d47df |
index 00d63de..0000000
|
|
|
6d47df |
--- a/ipatests/test_ipaclient/data/test_csrgen/configs/userCert.conf
|
|
|
6d47df |
+++ /dev/null
|
|
|
6d47df |
@@ -1,16 +0,0 @@
|
|
|
6d47df |
-[ req ]
|
|
|
6d47df |
-prompt = no
|
|
|
6d47df |
-encrypt_key = no
|
|
|
6d47df |
-
|
|
|
6d47df |
-distinguished_name = sec0
|
|
|
6d47df |
-req_extensions = sec2
|
|
|
6d47df |
-
|
|
|
6d47df |
-[ sec0 ]
|
|
|
6d47df |
-O=DOMAIN.EXAMPLE.COM
|
|
|
6d47df |
-CN=testuser
|
|
|
6d47df |
-
|
|
|
6d47df |
-[ sec1 ]
|
|
|
6d47df |
-email = testuser@example.com
|
|
|
6d47df |
-
|
|
|
6d47df |
-[ sec2 ]
|
|
|
6d47df |
-subjectAltName = @sec1
|
|
|
6d47df |
diff --git a/ipatests/test_ipaclient/data/test_csrgen/profiles/profile.json b/ipatests/test_ipaclient/data/test_csrgen/profiles/profile.json
|
|
|
6d47df |
deleted file mode 100644
|
|
|
6d47df |
index 676f91b..0000000
|
|
|
6d47df |
--- a/ipatests/test_ipaclient/data/test_csrgen/profiles/profile.json
|
|
|
6d47df |
+++ /dev/null
|
|
|
6d47df |
@@ -1,8 +0,0 @@
|
|
|
6d47df |
-[
|
|
|
6d47df |
- {
|
|
|
6d47df |
- "syntax": "basic",
|
|
|
6d47df |
- "data": [
|
|
|
6d47df |
- "options"
|
|
|
6d47df |
- ]
|
|
|
6d47df |
- }
|
|
|
6d47df |
-]
|
|
|
6d47df |
diff --git a/ipatests/test_ipaclient/data/test_csrgen/rules/basic.json b/ipatests/test_ipaclient/data/test_csrgen/rules/basic.json
|
|
|
6d47df |
deleted file mode 100644
|
|
|
6d47df |
index 094ef71..0000000
|
|
|
6d47df |
--- a/ipatests/test_ipaclient/data/test_csrgen/rules/basic.json
|
|
|
6d47df |
+++ /dev/null
|
|
|
6d47df |
@@ -1,5 +0,0 @@
|
|
|
6d47df |
-{
|
|
|
6d47df |
- "rule": {
|
|
|
6d47df |
- "template": "openssl_rule"
|
|
|
6d47df |
- }
|
|
|
6d47df |
-}
|
|
|
6d47df |
diff --git a/ipatests/test_ipaclient/data/test_csrgen/rules/options.json b/ipatests/test_ipaclient/data/test_csrgen/rules/options.json
|
|
|
6d47df |
deleted file mode 100644
|
|
|
6d47df |
index 393ed8c..0000000
|
|
|
6d47df |
--- a/ipatests/test_ipaclient/data/test_csrgen/rules/options.json
|
|
|
6d47df |
+++ /dev/null
|
|
|
6d47df |
@@ -1,8 +0,0 @@
|
|
|
6d47df |
-{
|
|
|
6d47df |
- "rule": {
|
|
|
6d47df |
- "template": "openssl_rule"
|
|
|
6d47df |
- },
|
|
|
6d47df |
- "options": {
|
|
|
6d47df |
- "rule_option": true
|
|
|
6d47df |
- }
|
|
|
6d47df |
-}
|
|
|
6d47df |
diff --git a/ipatests/test_ipaclient/data/test_csrgen/templates/identity_base.tmpl b/ipatests/test_ipaclient/data/test_csrgen/templates/identity_base.tmpl
|
|
|
6d47df |
deleted file mode 100644
|
|
|
6d47df |
index 79111ab..0000000
|
|
|
6d47df |
--- a/ipatests/test_ipaclient/data/test_csrgen/templates/identity_base.tmpl
|
|
|
6d47df |
+++ /dev/null
|
|
|
6d47df |
@@ -1 +0,0 @@
|
|
|
6d47df |
-{{ options|join(";") }}
|
|
|
6d47df |
diff --git a/ipatests/test_ipaclient/test_csrgen.py b/ipatests/test_ipaclient/test_csrgen.py
|
|
|
6d47df |
deleted file mode 100644
|
|
|
6d47df |
index b58596b..0000000
|
|
|
6d47df |
--- a/ipatests/test_ipaclient/test_csrgen.py
|
|
|
6d47df |
+++ /dev/null
|
|
|
6d47df |
@@ -1,304 +0,0 @@
|
|
|
6d47df |
-#
|
|
|
6d47df |
-# Copyright (C) 2016 FreeIPA Contributors see COPYING for license
|
|
|
6d47df |
-#
|
|
|
6d47df |
-
|
|
|
6d47df |
-import os
|
|
|
6d47df |
-import pytest
|
|
|
6d47df |
-
|
|
|
6d47df |
-from cryptography.hazmat.backends import default_backend
|
|
|
6d47df |
-from cryptography.hazmat.primitives.asymmetric import rsa
|
|
|
6d47df |
-from cryptography import x509
|
|
|
6d47df |
-
|
|
|
6d47df |
-from ipaclient import csrgen, csrgen_ffi
|
|
|
6d47df |
-from ipalib import errors
|
|
|
6d47df |
-
|
|
|
6d47df |
-BASE_DIR = os.path.dirname(__file__)
|
|
|
6d47df |
-CSR_DATA_DIR = os.path.join(BASE_DIR, 'data', 'test_csrgen')
|
|
|
6d47df |
-
|
|
|
6d47df |
-
|
|
|
6d47df |
-@pytest.fixture
|
|
|
6d47df |
-def formatter():
|
|
|
6d47df |
- return csrgen.Formatter(csr_data_dir=CSR_DATA_DIR)
|
|
|
6d47df |
-
|
|
|
6d47df |
-
|
|
|
6d47df |
-@pytest.fixture
|
|
|
6d47df |
-def rule_provider():
|
|
|
6d47df |
- return csrgen.FileRuleProvider(csr_data_dir=CSR_DATA_DIR)
|
|
|
6d47df |
-
|
|
|
6d47df |
-
|
|
|
6d47df |
-@pytest.fixture
|
|
|
6d47df |
-def generator():
|
|
|
6d47df |
- return csrgen.CSRGenerator(csrgen.FileRuleProvider())
|
|
|
6d47df |
-
|
|
|
6d47df |
-
|
|
|
6d47df |
-class StubRuleProvider(csrgen.RuleProvider):
|
|
|
6d47df |
- def __init__(self):
|
|
|
6d47df |
- self.syntax_rule = csrgen.Rule(
|
|
|
6d47df |
- 'syntax', '{{datarules|join(",")}}', {})
|
|
|
6d47df |
- self.data_rule = csrgen.Rule('data', 'data_template', {})
|
|
|
6d47df |
- self.field_mapping = csrgen.FieldMapping(
|
|
|
6d47df |
- 'example', self.syntax_rule, [self.data_rule])
|
|
|
6d47df |
- self.rules = [self.field_mapping]
|
|
|
6d47df |
-
|
|
|
6d47df |
- def rules_for_profile(self, profile_id):
|
|
|
6d47df |
- return self.rules
|
|
|
6d47df |
-
|
|
|
6d47df |
-
|
|
|
6d47df |
-class IdentityFormatter(csrgen.Formatter):
|
|
|
6d47df |
- base_template_name = 'identity_base.tmpl'
|
|
|
6d47df |
-
|
|
|
6d47df |
- def __init__(self):
|
|
|
6d47df |
- super(IdentityFormatter, self).__init__(csr_data_dir=CSR_DATA_DIR)
|
|
|
6d47df |
-
|
|
|
6d47df |
- def _get_template_params(self, syntax_rules):
|
|
|
6d47df |
- return {'options': syntax_rules}
|
|
|
6d47df |
-
|
|
|
6d47df |
-
|
|
|
6d47df |
-class test_Formatter(object):
|
|
|
6d47df |
- def test_prepare_data_rule_with_data_source(self, formatter):
|
|
|
6d47df |
- data_rule = csrgen.Rule('uid', '{{subject.uid.0}}',
|
|
|
6d47df |
- {'data_source': 'subject.uid.0'})
|
|
|
6d47df |
- prepared = formatter._prepare_data_rule(data_rule)
|
|
|
6d47df |
- assert prepared == '{% if subject.uid.0 %}{{subject.uid.0}}{% endif %}'
|
|
|
6d47df |
-
|
|
|
6d47df |
- def test_prepare_data_rule_no_data_source(self, formatter):
|
|
|
6d47df |
- """Not a normal case, but we should handle it anyway"""
|
|
|
6d47df |
- data_rule = csrgen.Rule('uid', 'static_text', {})
|
|
|
6d47df |
- prepared = formatter._prepare_data_rule(data_rule)
|
|
|
6d47df |
- assert prepared == 'static_text'
|
|
|
6d47df |
-
|
|
|
6d47df |
- def test_prepare_syntax_rule_with_data_sources(self, formatter):
|
|
|
6d47df |
- syntax_rule = csrgen.Rule(
|
|
|
6d47df |
- 'example', '{{datarules|join(",")}}', {})
|
|
|
6d47df |
- data_rules = ['{{subject.field1}}', '{{subject.field2}}']
|
|
|
6d47df |
- data_sources = ['subject.field1', 'subject.field2']
|
|
|
6d47df |
- prepared = formatter._prepare_syntax_rule(
|
|
|
6d47df |
- syntax_rule, data_rules, 'example', data_sources)
|
|
|
6d47df |
-
|
|
|
6d47df |
- assert prepared == (
|
|
|
6d47df |
- '{% if subject.field1 or subject.field2 %}{{subject.field1}},'
|
|
|
6d47df |
- '{{subject.field2}}{% endif %}')
|
|
|
6d47df |
-
|
|
|
6d47df |
- def test_prepare_syntax_rule_with_combinator(self, formatter):
|
|
|
6d47df |
- syntax_rule = csrgen.Rule('example', '{{datarules|join(",")}}',
|
|
|
6d47df |
- {'data_source_combinator': 'and'})
|
|
|
6d47df |
- data_rules = ['{{subject.field1}}', '{{subject.field2}}']
|
|
|
6d47df |
- data_sources = ['subject.field1', 'subject.field2']
|
|
|
6d47df |
- prepared = formatter._prepare_syntax_rule(
|
|
|
6d47df |
- syntax_rule, data_rules, 'example', data_sources)
|
|
|
6d47df |
-
|
|
|
6d47df |
- assert prepared == (
|
|
|
6d47df |
- '{% if subject.field1 and subject.field2 %}{{subject.field1}},'
|
|
|
6d47df |
- '{{subject.field2}}{% endif %}')
|
|
|
6d47df |
-
|
|
|
6d47df |
- def test_prepare_syntax_rule_required(self, formatter):
|
|
|
6d47df |
- syntax_rule = csrgen.Rule('example', '{{datarules|join(",")}}',
|
|
|
6d47df |
- {'required': True})
|
|
|
6d47df |
- data_rules = ['{{subject.field1}}']
|
|
|
6d47df |
- data_sources = ['subject.field1']
|
|
|
6d47df |
- prepared = formatter._prepare_syntax_rule(
|
|
|
6d47df |
- syntax_rule, data_rules, 'example', data_sources)
|
|
|
6d47df |
-
|
|
|
6d47df |
- assert prepared == (
|
|
|
6d47df |
- '{% filter required("example") %}{% if subject.field1 %}'
|
|
|
6d47df |
- '{{subject.field1}}{% endif %}{% endfilter %}')
|
|
|
6d47df |
-
|
|
|
6d47df |
- def test_prepare_syntax_rule_passthrough(self, formatter):
|
|
|
6d47df |
- """
|
|
|
6d47df |
- Calls to macros defined as passthrough are still call tags in the final
|
|
|
6d47df |
- template.
|
|
|
6d47df |
- """
|
|
|
6d47df |
- formatter._define_passthrough('example.macro')
|
|
|
6d47df |
-
|
|
|
6d47df |
- syntax_rule = csrgen.Rule(
|
|
|
6d47df |
- 'example',
|
|
|
6d47df |
- '{% call example.macro() %}{{datarules|join(",")}}{% endcall %}',
|
|
|
6d47df |
- {})
|
|
|
6d47df |
- data_rules = ['{{subject.field1}}']
|
|
|
6d47df |
- data_sources = ['subject.field1']
|
|
|
6d47df |
- prepared = formatter._prepare_syntax_rule(
|
|
|
6d47df |
- syntax_rule, data_rules, 'example', data_sources)
|
|
|
6d47df |
-
|
|
|
6d47df |
- assert prepared == (
|
|
|
6d47df |
- '{% if subject.field1 %}{% call example.macro() %}'
|
|
|
6d47df |
- '{{subject.field1}}{% endcall %}{% endif %}')
|
|
|
6d47df |
-
|
|
|
6d47df |
- def test_prepare_syntax_rule_no_data_sources(self, formatter):
|
|
|
6d47df |
- """Not a normal case, but we should handle it anyway"""
|
|
|
6d47df |
- syntax_rule = csrgen.Rule(
|
|
|
6d47df |
- 'example', '{{datarules|join(",")}}', {})
|
|
|
6d47df |
- data_rules = ['rule1', 'rule2']
|
|
|
6d47df |
- data_sources = []
|
|
|
6d47df |
- prepared = formatter._prepare_syntax_rule(
|
|
|
6d47df |
- syntax_rule, data_rules, 'example', data_sources)
|
|
|
6d47df |
-
|
|
|
6d47df |
- assert prepared == 'rule1,rule2'
|
|
|
6d47df |
-
|
|
|
6d47df |
-
|
|
|
6d47df |
-class test_FileRuleProvider(object):
|
|
|
6d47df |
- def test_rule_basic(self, rule_provider):
|
|
|
6d47df |
- rule_name = 'basic'
|
|
|
6d47df |
-
|
|
|
6d47df |
- rule = rule_provider._rule(rule_name)
|
|
|
6d47df |
-
|
|
|
6d47df |
- assert rule.template == 'openssl_rule'
|
|
|
6d47df |
-
|
|
|
6d47df |
- def test_rule_global_options(self, rule_provider):
|
|
|
6d47df |
- rule_name = 'options'
|
|
|
6d47df |
-
|
|
|
6d47df |
- rule = rule_provider._rule(rule_name)
|
|
|
6d47df |
-
|
|
|
6d47df |
- assert rule.options['rule_option'] is True
|
|
|
6d47df |
-
|
|
|
6d47df |
- def test_rule_nosuchrule(self, rule_provider):
|
|
|
6d47df |
- with pytest.raises(errors.NotFound):
|
|
|
6d47df |
- rule_provider._rule('nosuchrule')
|
|
|
6d47df |
-
|
|
|
6d47df |
- def test_rules_for_profile_success(self, rule_provider):
|
|
|
6d47df |
- rules = rule_provider.rules_for_profile('profile')
|
|
|
6d47df |
-
|
|
|
6d47df |
- assert len(rules) == 1
|
|
|
6d47df |
- field_mapping = rules[0]
|
|
|
6d47df |
- assert field_mapping.syntax_rule.name == 'basic'
|
|
|
6d47df |
- assert len(field_mapping.data_rules) == 1
|
|
|
6d47df |
- assert field_mapping.data_rules[0].name == 'options'
|
|
|
6d47df |
-
|
|
|
6d47df |
- def test_rules_for_profile_nosuchprofile(self, rule_provider):
|
|
|
6d47df |
- with pytest.raises(errors.NotFound):
|
|
|
6d47df |
- rule_provider.rules_for_profile('nosuchprofile')
|
|
|
6d47df |
-
|
|
|
6d47df |
-
|
|
|
6d47df |
-class test_CSRGenerator(object):
|
|
|
6d47df |
- def test_userCert_OpenSSL(self, generator):
|
|
|
6d47df |
- principal = {
|
|
|
6d47df |
- 'uid': ['testuser'],
|
|
|
6d47df |
- 'mail': ['testuser@example.com'],
|
|
|
6d47df |
- }
|
|
|
6d47df |
- config = {
|
|
|
6d47df |
- 'ipacertificatesubjectbase': [
|
|
|
6d47df |
- 'O=DOMAIN.EXAMPLE.COM'
|
|
|
6d47df |
- ],
|
|
|
6d47df |
- }
|
|
|
6d47df |
-
|
|
|
6d47df |
- script = generator.csr_config(principal, config, 'userCert')
|
|
|
6d47df |
- with open(os.path.join(
|
|
|
6d47df |
- CSR_DATA_DIR, 'configs', 'userCert.conf')) as f:
|
|
|
6d47df |
- expected_script = f.read()
|
|
|
6d47df |
- assert script == expected_script
|
|
|
6d47df |
-
|
|
|
6d47df |
- def test_caIPAserviceCert_OpenSSL(self, generator):
|
|
|
6d47df |
- principal = {
|
|
|
6d47df |
- 'krbprincipalname': [
|
|
|
6d47df |
- 'HTTP/machine.example.com@DOMAIN.EXAMPLE.COM'
|
|
|
6d47df |
- ],
|
|
|
6d47df |
- }
|
|
|
6d47df |
- config = {
|
|
|
6d47df |
- 'ipacertificatesubjectbase': [
|
|
|
6d47df |
- 'O=DOMAIN.EXAMPLE.COM'
|
|
|
6d47df |
- ],
|
|
|
6d47df |
- }
|
|
|
6d47df |
-
|
|
|
6d47df |
- script = generator.csr_config(
|
|
|
6d47df |
- principal, config, 'caIPAserviceCert')
|
|
|
6d47df |
- with open(os.path.join(
|
|
|
6d47df |
- CSR_DATA_DIR, 'configs', 'caIPAserviceCert.conf')) as f:
|
|
|
6d47df |
- expected_script = f.read()
|
|
|
6d47df |
- assert script == expected_script
|
|
|
6d47df |
-
|
|
|
6d47df |
- def test_works_with_lowercase_attr_type_shortname(self, generator):
|
|
|
6d47df |
- principal = {
|
|
|
6d47df |
- 'uid': ['testuser'],
|
|
|
6d47df |
- 'mail': ['testuser@example.com'],
|
|
|
6d47df |
- }
|
|
|
6d47df |
- template_env = {
|
|
|
6d47df |
- 'ipacertificatesubjectbase': [
|
|
|
6d47df |
- 'o=DOMAIN.EXAMPLE.COM' # lower-case attr type shortname
|
|
|
6d47df |
- ],
|
|
|
6d47df |
- }
|
|
|
6d47df |
- config = generator.csr_config(principal, template_env, 'userCert')
|
|
|
6d47df |
-
|
|
|
6d47df |
- key = rsa.generate_private_key(
|
|
|
6d47df |
- public_exponent=65537,
|
|
|
6d47df |
- key_size=2048,
|
|
|
6d47df |
- backend=default_backend(),
|
|
|
6d47df |
- )
|
|
|
6d47df |
- adaptor = csrgen.OpenSSLAdaptor(key=key)
|
|
|
6d47df |
-
|
|
|
6d47df |
- reqinfo = bytes(csrgen_ffi.build_requestinfo(
|
|
|
6d47df |
- config.encode('utf-8'), adaptor.get_subject_public_key_info()))
|
|
|
6d47df |
- csr_der = adaptor.sign_csr(reqinfo)
|
|
|
6d47df |
- csr = x509.load_der_x509_csr(csr_der, default_backend())
|
|
|
6d47df |
- assert (
|
|
|
6d47df |
- csr.subject.get_attributes_for_oid(x509.NameOID.COMMON_NAME)
|
|
|
6d47df |
- == [x509.NameAttribute(x509.NameOID.COMMON_NAME, u'testuser')]
|
|
|
6d47df |
- )
|
|
|
6d47df |
- assert (
|
|
|
6d47df |
- csr.subject.get_attributes_for_oid(x509.NameOID.ORGANIZATION_NAME)
|
|
|
6d47df |
- == [x509.NameAttribute(
|
|
|
6d47df |
- x509.NameOID.ORGANIZATION_NAME, u'DOMAIN.EXAMPLE.COM')]
|
|
|
6d47df |
- )
|
|
|
6d47df |
-
|
|
|
6d47df |
- def test_unrecognised_attr_type_raises(self, generator):
|
|
|
6d47df |
- principal = {
|
|
|
6d47df |
- 'uid': ['testuser'],
|
|
|
6d47df |
- 'mail': ['testuser@example.com'],
|
|
|
6d47df |
- }
|
|
|
6d47df |
- template_env = {
|
|
|
6d47df |
- 'ipacertificatesubjectbase': [
|
|
|
6d47df |
- 'X=DOMAIN.EXAMPLE.COM' # unrecognised attr type
|
|
|
6d47df |
- ],
|
|
|
6d47df |
- }
|
|
|
6d47df |
- config = generator.csr_config(principal, template_env, 'userCert')
|
|
|
6d47df |
-
|
|
|
6d47df |
- key = rsa.generate_private_key(
|
|
|
6d47df |
- public_exponent=65537,
|
|
|
6d47df |
- key_size=2048,
|
|
|
6d47df |
- backend=default_backend(),
|
|
|
6d47df |
- )
|
|
|
6d47df |
- adaptor = csrgen.OpenSSLAdaptor(key=key)
|
|
|
6d47df |
-
|
|
|
6d47df |
- with pytest.raises(
|
|
|
6d47df |
- errors.CSRTemplateError,
|
|
|
6d47df |
- message='unrecognised attribute type: X'):
|
|
|
6d47df |
- csrgen_ffi.build_requestinfo(
|
|
|
6d47df |
- config.encode('utf-8'), adaptor.get_subject_public_key_info())
|
|
|
6d47df |
-
|
|
|
6d47df |
-
|
|
|
6d47df |
-class test_rule_handling(object):
|
|
|
6d47df |
- def test_optionalAttributeMissing(self, generator):
|
|
|
6d47df |
- principal = {'uid': 'testuser'}
|
|
|
6d47df |
- rule_provider = StubRuleProvider()
|
|
|
6d47df |
- rule_provider.data_rule.template = '{{subject.mail}}'
|
|
|
6d47df |
- rule_provider.data_rule.options = {'data_source': 'subject.mail'}
|
|
|
6d47df |
- generator = csrgen.CSRGenerator(
|
|
|
6d47df |
- rule_provider, formatter_class=IdentityFormatter)
|
|
|
6d47df |
-
|
|
|
6d47df |
- script = generator.csr_config(
|
|
|
6d47df |
- principal, {}, 'example')
|
|
|
6d47df |
- assert script == '\n'
|
|
|
6d47df |
-
|
|
|
6d47df |
- def test_twoDataRulesOneMissing(self, generator):
|
|
|
6d47df |
- principal = {'uid': 'testuser'}
|
|
|
6d47df |
- rule_provider = StubRuleProvider()
|
|
|
6d47df |
- rule_provider.data_rule.template = '{{subject.mail}}'
|
|
|
6d47df |
- rule_provider.data_rule.options = {'data_source': 'subject.mail'}
|
|
|
6d47df |
- rule_provider.field_mapping.data_rules.append(csrgen.Rule(
|
|
|
6d47df |
- 'data2', '{{subject.uid}}', {'data_source': 'subject.uid'}))
|
|
|
6d47df |
- generator = csrgen.CSRGenerator(
|
|
|
6d47df |
- rule_provider, formatter_class=IdentityFormatter)
|
|
|
6d47df |
-
|
|
|
6d47df |
- script = generator.csr_config(principal, {}, 'example')
|
|
|
6d47df |
- assert script == ',testuser\n'
|
|
|
6d47df |
-
|
|
|
6d47df |
- def test_requiredAttributeMissing(self):
|
|
|
6d47df |
- principal = {'uid': 'testuser'}
|
|
|
6d47df |
- rule_provider = StubRuleProvider()
|
|
|
6d47df |
- rule_provider.data_rule.template = '{{subject.mail}}'
|
|
|
6d47df |
- rule_provider.data_rule.options = {'data_source': 'subject.mail'}
|
|
|
6d47df |
- rule_provider.syntax_rule.options = {'required': True}
|
|
|
6d47df |
- generator = csrgen.CSRGenerator(
|
|
|
6d47df |
- rule_provider, formatter_class=IdentityFormatter)
|
|
|
6d47df |
-
|
|
|
6d47df |
- with pytest.raises(errors.CSRTemplateError):
|
|
|
6d47df |
- _script = generator.csr_config(
|
|
|
6d47df |
- principal, {}, 'example')
|
|
|
6d47df |
--
|
|
|
6d47df |
2.13.6
|
|
|
6d47df |
|