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.

https://pagure.io/freeipa/issue/6854

Signed-off-by: Michal Reznik <mreznik@redhat.com>
Reviewed-By: Stanislav Laznicka <slaznick@redhat.com>
This commit is contained in:
Michal Reznik
2017-05-09 16:39:45 +02:00
committed by Martin Basti
parent 96ca62f81d
commit f7c4039e41
2 changed files with 152 additions and 35 deletions

View File

@@ -1,14 +1,32 @@
#!/bin/bash -e
#
# Copyright (C) 2017 FreeIPA Contributors see COPYING for license
#
profile_ca=(-t CT,C,C -v 120)
profile_server=(-t ,, -v 12)
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 ,,)
crl_path=${crl_path-$(readlink -f $dbdir)}
write_chain() {
local nick="$1"
serial_number=0
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 +36,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 +63,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 +119,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 +170,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 +179,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'

View File

@@ -63,11 +63,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):
@@ -125,10 +125,12 @@ class CALessBase(IntegrationTest):
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,
}
@@ -156,8 +158,9 @@ class CALessBase(IntegrationTest):
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
@@ -165,16 +168,29 @@ class CALessBase(IntegrationTest):
"""
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)
@@ -183,15 +199,12 @@ class CALessBase(IntegrationTest):
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,
@@ -201,12 +214,19 @@ class CALessBase(IntegrationTest):
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
@@ -223,6 +243,9 @@ class CALessBase(IntegrationTest):
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']
@@ -250,11 +273,18 @@ class CALessBase(IntegrationTest):
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,
@@ -1468,3 +1498,27 @@ class TestCertinstall(CALessBase):
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()