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