URL: https://github.com/freeipa/freeipa/pull/769
Author: Rezney
 Title: #769: test_caless: add pkinit option and test it
Action: synchronized

To pull the PR as Git branch:
git remote add ghfreeipa https://github.com/freeipa/freeipa
git fetch ghfreeipa pull/769/head:pr769
git checkout pr769
From 1c517ee628fb31f765e21149fcaa3d26fd07d0fa Mon Sep 17 00:00:00 2001
From: Michal Reznik <mrez...@redhat.com>
Date: Tue, 9 May 2017 16:39:45 +0200
Subject: [PATCH] test_caless: add pkinit option and test it

change "caless-create-pki" so pkinit certificates can be
generated.

See https://web.mit.edu/kerberos/krb5-1.13/doc/admin/pkinit.html for details.

add pkinit option to the ipa installer and test both master and replica
install with pkinit.

Signed-off-by: Michal Reznik <mrez...@redhat.com>
---
 .../test_integration/scripts/caless-create-pki     | 112 ++++++++++++++++-----
 ipatests/test_integration/test_caless.py           |  76 ++++++++++++--
 2 files changed, 151 insertions(+), 37 deletions(-)

diff --git a/ipatests/test_integration/scripts/caless-create-pki b/ipatests/test_integration/scripts/caless-create-pki
index 8928e95..816e7dc 100644
--- a/ipatests/test_integration/scripts/caless-create-pki
+++ b/ipatests/test_integration/scripts/caless-create-pki
@@ -1,14 +1,29 @@
 #!/bin/bash -e
 
-profile_ca=(-t CT,C,C -v 120)
-profile_server=(-t ,, -v 12)
-
-crl_path=${crl_path-$(readlink -f $dbdir)}
-
-serial_number=0
+profile_ca_request_options=(-1 -2 -4)
+profile_ca_request_input="\$'0\n1\n5\n6\n9\ny\ny\n\ny\n1\n7\nfile://'\$(readlink -f \$dbdir)/\$ca.crl\$'\n-1\n-1\n-1\nn\nn\n'"
+profile_ca_create_options=(-v 120)
+profile_ca_add_options=(-t CT,C,C)
+profile_server_request_options=(-4)
+profile_server_request_input="\$'1\n7\nfile://'\$(readlink -f \$dbdir)/\$ca.crl\$'\n-1\n-1\n-1\nn\nn\n'"
+profile_server_create_options=(-v 12)
+profile_server_add_options=(-t ,,)
+
+write_chain() {
+    local nick="$1"
+
+    chain=`certutil -O -d $dbdir -n "$nick" |
+             sed -e '/^\s*$/d' -e "s/\s*\"\(.*\)\" \[.*/\1/g"`
+
+    while read -r name; do
+        # OpenSSL requires a reverse order to what we get from NSS
+        echo -e "`certutil -L -d "$dbdir" -n "$name" -a`\n`cat $dbdir/$nick.pem`
+        " > "$dbdir/$nick.pem"
+    done <<< "$chain"
+}
 
 gen_cert() {
-    local profile="$1" nick="$2" subject="$3" ca options pwfile noise csr crt
+    local profile="$1" nick="$2" subject="$3" ca request_options request_input create_options serial add_options pwfile noise csr crt
     shift 3
 
     echo "gen_cert(profile=$profile nick=$nick subject=$subject)"
@@ -18,13 +33,20 @@ gen_cert() {
         ca="$nick"
     fi
 
-    eval "options=(\"\${profile_$profile[@]}\")"
+    eval "request_options=(\"\${profile_${profile}_request_options[@]}\")"
+    eval "eval request_input=\"\${profile_${profile}_request_input}\""
+
+    eval "create_options=(\"\${profile_${profile}_create_options[@]}\")"
     if [ "$ca" = "$nick" ]; then
-        options=("${options[@]}" -x -m 1)
+        create_options=("${create_options[@]}" -x -m 1)
     else
-        options=("${options[@]}" -c "$ca")
+        eval "serial_${ca//\//_}=\$((\${serial_${ca//\//_}:-1}+1))"
+        eval "serial=\$serial_${ca//\//_}"
+        create_options=("${create_options[@]}" -c "$ca" -m "$serial")
     fi
 
+    eval "add_options=(\"\${profile_${profile}_add_options[@]}\")"
+
     pwfile="$(mktemp)"
     echo "$dbpassword" >"$pwfile"
 
@@ -38,22 +60,14 @@ gen_cert() {
 
     csr="$(mktemp)"
     crt="$(mktemp)"
-    certutil -R -d "$dbdir" -s "$subject" -f "$pwfile" -z "$noise" -o "$csr" -4 -2 >/dev/null <<EOF
-y
-0
-N
-1
-7
-file://$crl_path/$ca.crl
--1
--1
--1
-n
-n
-EOF
-    serial_number=$(($serial_number+1))
-    certutil -C -d "$dbdir" -f "$pwfile" -m "$serial_number" -i "$csr" -o "$crt" "${options[@]}" "$@"
-    certutil -A -d "$dbdir" -n "$nick" -f "$pwfile" -i "$crt" "${options[@]}"
+
+    certutil -R -d "$dbdir" -s "$subject" -f "$pwfile" -z "$noise" -o "$csr" "${request_options[@]}" >/dev/null <<<"$request_input"
+    certutil -C -d "$dbdir" -f "$pwfile" -i "$csr" -o "$crt" "${create_options[@]}" "$@"
+    certutil -A -d "$dbdir" -n "$nick" -f "$pwfile" -i "$crt" "${add_options[@]}"
+
+    mkdir -p "$(dirname $dbdir/$nick.pem)"
+    write_chain "$nick"
+    pk12util -o "$dbdir/$nick.p12" -n "$nick" -d "$dbdir" -k "$pwfile" -w "$pwfile"
 
     rm -f "$pwfile" "$noise" "$csr" "$crt"
 }
@@ -102,6 +116,49 @@ gen_server_certs() {
     revoke_cert "$nick-revoked"
 }
 
+gen_pkinit_extensions() {
+   echo "[kdc_cert]
+basicConstraints=CA:FALSE
+keyUsage=nonRepudiation,digitalSignature,keyEncipherment,keyAgreement
+extendedKeyUsage=TLS Web Server Authentication, 1.3.6.1.5.2.3.5
+subjectKeyIdentifier=hash
+authorityKeyIdentifier=keyid,issuer
+issuerAltName=issuer:copy
+subjectAltName=otherName:1.3.6.1.5.2.2;SEQUENCE:kdc_princ_name
+[kdc_princ_name]
+realm=EXP:0,GeneralString:${realm}
+principal_name=EXP:1,SEQUENCE:kdc_principal_seq
+[kdc_principal_seq]
+name_type=EXP:0,INTEGER:1
+name_string=EXP:1,SEQUENCE:kdc_principals
+[kdc_principals]
+princ1=GeneralString:krbtgt
+princ2=GeneralString:${realm}" > "$dbdir/ext.kdc"
+}
+
+gen_pkinit_cert() {
+    local nick="$1" subj="$2" outname="$3"
+    shift 3
+
+    openssl genrsa -out "$dbdir/$nick/kdc.key" 2048 > /dev/null
+    openssl req -new -out "$dbdir/$nick/kdc.req" -key "$dbdir/$nick/kdc.key" \
+    -subj "$subj"
+
+    openssl pkcs12 -in "$dbdir/$nick.p12" -passin "pass:$dbpassword" \
+    -nodes -nocerts -out "$dbdir/$nick.key" > /dev/null
+
+    openssl x509 -req -in "$dbdir/$nick/kdc.req" \
+    -CAkey "$dbdir/$nick.key" -CA "$dbdir/$nick.pem" \
+    -out "$dbdir/$nick/kdc.crt" -days 365 \
+    -extfile "$dbdir/ext.kdc" -extensions kdc_cert -CAcreateserial > /dev/null
+
+    rm "$dbdir/$nick/kdc.req"
+
+    openssl pkcs12 -export -in "$dbdir/$nick/kdc.crt" \
+    -inkey "$dbdir/$nick/kdc.key" -password "pass:$dbpassword" \
+    -out "$dbdir/$nick/$outname.p12" -chain -CAfile "$dbdir/$nick.pem"
+}
+
 gen_subtree() {
     local nick="$1" org="$2"
     shift 2
@@ -110,6 +167,8 @@ gen_subtree() {
 
     gen_cert ca "$nick" "CN=CA,O=$org" "$@"
     gen_cert server "$nick/wildcard" "CN=*.$domain,O=$org"
+    gen_pkinit_cert "$nick" "/O=$realm/CN=$server1" "pkinit-server"
+    gen_pkinit_cert "$nick" "/O=$realm/CN=$server2" "pkinit-replica"
     gen_server_certs "$nick/server" "$server1" "$org"
     gen_server_certs "$nick/replica" "$server2" "$org"
     gen_server_certs "$nick/client" "$client" "$org"
@@ -117,6 +176,7 @@ gen_subtree() {
 
 gen_cert server server-selfsign "CN=$server1,O=Self-signed"
 gen_cert server replica-selfsign "CN=$server2,O=Self-signed"
+gen_pkinit_extensions
 gen_cert server noca "CN=$server1,O=No-CA"
 gen_subtree ca1 'Example Organization'
 gen_subtree ca1/subca 'Subsidiary Example Organization'
diff --git a/ipatests/test_integration/test_caless.py b/ipatests/test_integration/test_caless.py
index d7692ec..dde592f 100644
--- a/ipatests/test_integration/test_caless.py
+++ b/ipatests/test_integration/test_caless.py
@@ -61,11 +61,11 @@ def ipa_certs_cleanup(host):
                      raiseonerr=False)
     # A workaround for https://fedorahosted.org/freeipa/ticket/4639
     result = host.run_command(['certutil', '-L', '-d',
-                               paths.HTTPD_ALIAS_DIR])
+                               paths.HTTPD_ALIAS_DIR], raiseonerr=False)
     for rawcert in result.stdout_text.split('\n')[4: -1]:
         cert = rawcert.split('    ')[0]
         host.run_command(['certutil', '-D', '-d', paths.HTTPD_ALIAS_DIR,
-                          '-n', cert])
+                          '-n', cert], raiseonerr=False)
 
 
 def server_install_teardown(func):
@@ -123,10 +123,12 @@ def install(cls, mh):
             client_hostname = 'unused-client.test'
         cls.env = {
             'domain': cls.master.domain.name,
+            'realm': cls.master.domain.name.upper(),
             'server1': cls.master.hostname,
             'server2': replica_hostname,
             'client': client_hostname,
             'dbdir': 'nssdb',
+            'dbpassword': cls.cert_password,
             'crl_path': cls.crl_path,
             'dirman_password': cls.master.config.dirman_password,
         }
@@ -154,8 +156,9 @@ def uninstall(cls, mh):
     def install_server(cls, host=None,
                        http_pkcs12='server.p12', dirsrv_pkcs12='server.p12',
                        http_pkcs12_exists=True, dirsrv_pkcs12_exists=True,
-                       http_pin=_DEFAULT, dirsrv_pin=_DEFAULT,
-                       root_ca_file='root.pem', unattended=True,
+                       http_pin=_DEFAULT, dirsrv_pin=_DEFAULT, pkinit_pin=None,
+                       root_ca_file='root.pem', pkinit_pkcs12_exists=False,
+                       pkinit_pkcs12='pkinit-server.p12', unattended=True,
                        stdin_text=None):
         """Install a CA-less server
 
@@ -163,16 +166,29 @@ def install_server(cls, host=None,
         """
         if host is None:
             host = cls.master
+
+        extra_args = ['--http-cert-file', http_pkcs12,
+                      '--dirsrv-cert-file', dirsrv_pkcs12,
+                      '--ca-cert-file', root_ca_file,
+                      '--ip-address', host.ip]
+
         if http_pin is _DEFAULT:
             http_pin = cls.cert_password
         if dirsrv_pin is _DEFAULT:
             dirsrv_pin = cls.cert_password
+        if pkinit_pin is _DEFAULT:
+            pkinit_pin = cls.cert_password
         tasks.prepare_host(host)
         files_to_copy = ['root.pem']
         if http_pkcs12_exists:
             files_to_copy.append(http_pkcs12)
         if dirsrv_pkcs12_exists:
             files_to_copy.append(dirsrv_pkcs12)
+
+        if pkinit_pkcs12_exists:
+            extra_args.extend(['--pkinit-cert-file', pkinit_pkcs12])
+        else:
+            extra_args.append('--no-pkinit')
         for filename in set(files_to_copy):
             cls.copy_cert(host, filename)
 
@@ -181,15 +197,12 @@ def install_server(cls, host=None,
         host.run_command(args + ["ca1"], raiseonerr=False)
         host.run_command(args + ["ca1/server"], raiseonerr=False)
 
-        extra_args = ['--http-cert-file', http_pkcs12,
-                      '--dirsrv-cert-file', dirsrv_pkcs12,
-                      '--ca-cert-file', root_ca_file,
-                      '--ip-address', host.ip]
-
         if http_pin is not None:
             extra_args.extend(['--http-pin', http_pin])
         if dirsrv_pin is not None:
             extra_args.extend(['--dirsrv-pin', dirsrv_pin])
+        if pkinit_pin is not None:
+            extra_args.extend(['--pkinit-pin', dirsrv_pin])
         return tasks.install_master(host, extra_args=extra_args,
                                     unattended=unattended,
                                     stdin_text=stdin_text,
@@ -199,12 +212,19 @@ def install_server(cls, host=None,
     def copy_cert(cls, host, filename):
         host.transport.put_file(os.path.join(cls.cert_dir, filename),
                                 os.path.join(host.config.test_dir, filename))
+    @classmethod
+    def copy_pkinit_cert(cls, host, pkinit_nick):
+        filename = pkinit_nick.split('/')[-1]
+        host.transport.put_file(os.path.join(cls.cert_dir, 'nssdb', pkinit_nick),
+                                os.path.join(host.config.test_dir, filename))
+
 
     def prepare_replica(self, _replica_number=0, replica=None, master=None,
                         http_pkcs12='replica.p12', dirsrv_pkcs12='replica.p12',
                         http_pkcs12_exists=True, dirsrv_pkcs12_exists=True,
-                        http_pin=_DEFAULT, dirsrv_pin=_DEFAULT,
-                        root_ca_file='root.pem', unattended=True,
+                        http_pin=_DEFAULT, dirsrv_pin=_DEFAULT, pkinit_pin=None,
+                        root_ca_file='root.pem', pkinit_pkcs12_exists=False,
+                        pkinit_pkcs12='pkinit-replica.p12', unattended=True,
                         stdin_text=None, domain_level=None):
         """Prepare a CA-less replica
 
@@ -221,6 +241,9 @@ def prepare_replica(self, _replica_number=0, replica=None, master=None,
             http_pin = self.cert_password
         if dirsrv_pin is _DEFAULT:
             dirsrv_pin = self.cert_password
+        if pkinit_pin is _DEFAULT:
+            pkinit_pin = self.cert_password
+
         if domain_level is None:
             domain_level = tasks.domainlevel(master)
         files_to_copy = ['root.pem']
@@ -248,11 +271,18 @@ def prepare_replica(self, _replica_number=0, replica=None, master=None,
             extra_args.extend(['--http-cert-file', http_pkcs12])
         if dirsrv_pkcs12_exists:
             extra_args.extend(['--dirsrv-cert-file', dirsrv_pkcs12])
+        if pkinit_pkcs12_exists and domain_level != DOMAIN_LEVEL_0:
+            extra_args.extend(['--pkinit-cert-file', pkinit_pkcs12])
+        else:
+            extra_args.append('--no-pkinit')
 
         if http_pin is not None:
             extra_args.extend(['--http-pin', http_pin])
         if dirsrv_pin is not None:
             extra_args.extend(['--dirsrv-pin', dirsrv_pin])
+        if pkinit_pin is not None:
+            extra_args.extend(['--pkinit-pin', dirsrv_pin])
+
         if domain_level == DOMAIN_LEVEL_0:
             result = tasks.replica_prepare(master, replica,
                                            extra_args=extra_args,
@@ -1472,3 +1502,27 @@ def test_ds_old_options(self):
         result = self.certinstall('d', 'ca1/server',
                                   args=args, stdin_text=stdin_text)
         assert_error(result, "no such option: --dirsrv-pin")
+
+
+class TestPKINIT(CALessBase):
+    num_replicas = 1
+
+    @classmethod
+    def install(cls, mh):
+        super(TestPKINIT, cls).install(mh)
+        cls.export_pkcs12('ca1/server')
+        cls.copy_pkinit_cert(cls.master, 'ca1/pkinit-server.p12')
+        with open(cls.pem_filename, 'w') as f:
+            f.write(cls.get_pem('ca1'))
+        result = cls.install_server(pkinit_pkcs12_exists=True,
+                                    pkinit_pin=_DEFAULT)
+        assert result.returncode == 0
+
+    @replica_install_teardown
+    def test_server_replica_install_pkinit(self):
+        self.export_pkcs12('ca1/replica', filename='replica.p12')
+        self.copy_pkinit_cert(self.replicas[0], 'ca1/pkinit-replica.p12')
+        result = self.prepare_replica(pkinit_pkcs12_exists=True,
+                                      pkinit_pin=_DEFAULT)
+        assert result.returncode == 0
+        self.verify_installation()
-- 
Manage your subscription for the Freeipa-devel mailing list:
https://www.redhat.com/mailman/listinfo/freeipa-devel
Contribute to FreeIPA: http://www.freeipa.org/page/Contribute/Code

Reply via email to