xref: /freebsd/crypto/krb5/src/tests/t_pkinit.py (revision f1c4c3daccbaf3820f0e2224de53df12fc952fcc)
1from k5test import *
2import re
3
4# Skip this test if pkinit wasn't built.
5if not pkinit_enabled:
6    skip_rest('PKINIT tests', 'PKINIT module not built')
7
8# Construct a krb5.conf fragment configuring pkinit.
9user_pem = os.path.join(pkinit_certs, 'user.pem')
10ecuser_pem = os.path.join(pkinit_certs, 'ecuser.pem')
11privkey_pem = os.path.join(pkinit_certs, 'privkey.pem')
12privkey_enc_pem = os.path.join(pkinit_certs, 'privkey-enc.pem')
13privkey_ec_pem = os.path.join(pkinit_certs, 'eckey.pem')
14user_p12 = os.path.join(pkinit_certs, 'user.p12')
15user_enc_p12 = os.path.join(pkinit_certs, 'user-enc.p12')
16user_upn_p12 = os.path.join(pkinit_certs, 'user-upn.p12')
17user_upn2_p12 = os.path.join(pkinit_certs, 'user-upn2.p12')
18user_upn3_p12 = os.path.join(pkinit_certs, 'user-upn3.p12')
19generic_p12 = os.path.join(pkinit_certs, 'generic.p12')
20path = os.path.join(os.getcwd(), 'testdir', 'tmp-pkinit-certs')
21path_enc = os.path.join(os.getcwd(), 'testdir', 'tmp-pkinit-certs-enc')
22
23pkinit_kdc_conf = {'realms': {'$realm': {
24            'default_principal_flags': '+preauth',
25            'pkinit_eku_checking': 'none',
26            'pkinit_indicator': ['indpkinit1', 'indpkinit2']}}}
27restrictive_kdc_conf = {'realms': {'$realm': {
28            'restrict_anonymous_to_tgt': 'true' }}}
29freshness_kdc_conf = {'realms': {'$realm': {
30            'pkinit_require_freshness': 'true'}}}
31
32testprincs = {'krbtgt/KRBTEST.COM': {'keys': 'aes128-cts'},
33              'user': {'keys': 'aes128-cts', 'flags': '+preauth'},
34              'user2': {'keys': 'aes128-cts', 'flags': '+preauth'}}
35alias_kdc_conf = {'realms': {'$realm': {
36            'default_principal_flags': '+preauth',
37            'pkinit_eku_checking': 'none',
38            'pkinit_allow_upn': 'true',
39            'database_module': 'test'}},
40                  'dbmodules': {'test': {
41                      'db_library': 'test',
42                      'alias': {'user@krbtest.com': 'user'},
43                      'princs': testprincs}}}
44
45file_identity = 'FILE:%s,%s' % (user_pem, privkey_pem)
46file_enc_identity = 'FILE:%s,%s' % (user_pem, privkey_enc_pem)
47ec_identity = 'FILE:%s,%s' % (ecuser_pem, privkey_ec_pem)
48dir_identity = 'DIR:%s' % path
49dir_enc_identity = 'DIR:%s' % path_enc
50dir_file_identity = 'FILE:%s,%s' % (os.path.join(path, 'user.crt'),
51                                    os.path.join(path, 'user.key'))
52dir_file_enc_identity = 'FILE:%s,%s' % (os.path.join(path_enc, 'user.crt'),
53                                        os.path.join(path_enc, 'user.key'))
54p12_identity = 'PKCS12:%s' % user_p12
55p12_upn_identity = 'PKCS12:%s' % user_upn_p12
56p12_upn2_identity = 'PKCS12:%s' % user_upn2_p12
57p12_upn3_identity = 'PKCS12:%s' % user_upn3_p12
58p12_generic_identity = 'PKCS12:%s' % generic_p12
59p12_enc_identity = 'PKCS12:%s' % user_enc_p12
60
61# Start a realm with the test kdb module for the following UPN SAN tests.
62realm = K5Realm(kdc_conf=alias_kdc_conf, create_kdb=False, pkinit=True)
63realm.start_kdc()
64
65mark('UPN SANs')
66
67# Compatibility check: cert contains UPN "user", which matches the
68# request principal user@KRBTEST.COM if parsed as a normal principal.
69realm.kinit(realm.user_princ,
70            flags=['-X', 'X509_user_identity=%s' % p12_upn2_identity])
71
72# Compatibility check: cert contains UPN "user@KRBTEST.COM", which matches
73# the request principal user@KRBTEST.COM if parsed as a normal principal.
74realm.kinit(realm.user_princ,
75            flags=['-X', 'X509_user_identity=%s' % p12_upn3_identity])
76
77# Cert contains UPN "user@krbtest.com" which is aliased to the request
78# principal.
79realm.kinit(realm.user_princ,
80            flags=['-X', 'X509_user_identity=%s' % p12_upn_identity])
81
82# Test an id-pkinit-san match to a post-canonical principal.
83realm.kinit('user@krbtest.com',
84            flags=['-E', '-X', 'X509_user_identity=%s' % p12_identity])
85
86# Test a UPN match to a post-canonical principal.  (This only works
87# for the cert with the UPN containing just "user", as we don't allow
88# UPN reparsing when comparing to the canonicalized client principal.)
89realm.kinit('user@krbtest.com',
90            flags=['-E', '-X', 'X509_user_identity=%s' % p12_upn2_identity])
91
92# Test a mismatch.
93msg = 'kinit: Client name mismatch while getting initial credentials'
94realm.run([kinit, '-X', 'X509_user_identity=%s' % p12_upn2_identity, 'user2'],
95          expected_code=1, expected_msg=msg)
96realm.stop()
97
98realm = K5Realm(kdc_conf=pkinit_kdc_conf, get_creds=False, pkinit=True)
99
100# Sanity check - password-based preauth should still work.
101mark('password preauth sanity check')
102realm.run(['./responder', '-r', 'password=%s' % password('user'),
103           realm.user_princ])
104realm.kinit(realm.user_princ, password=password('user'))
105realm.klist(realm.user_princ)
106realm.run([kvno, realm.host_princ])
107
108# Having tested password preauth, remove the keys for better error
109# reporting.
110realm.run([kadminl, 'purgekeys', '-all', realm.user_princ])
111
112# Test anonymous PKINIT.
113mark('anonymous')
114realm.kinit('@%s' % realm.realm, flags=['-n'], expected_code=1,
115            expected_msg='not found in Kerberos database')
116realm.addprinc('WELLKNOWN/ANONYMOUS')
117realm.kinit('@%s' % realm.realm, flags=['-n'])
118realm.klist('WELLKNOWN/ANONYMOUS@WELLKNOWN:ANONYMOUS')
119realm.run([kvno, realm.host_princ])
120out = realm.run(['./adata', realm.host_princ])
121if '97:' in out:
122    fail('auth indicators seen in anonymous PKINIT ticket')
123# Verify start_realm setting and test referrals TGS request.
124realm.run([klist, '-C'], expected_msg='start_realm = KRBTEST.COM')
125realm.run([kvno, '-S', 'host', hostname])
126
127# Test anonymous kadmin.
128mark('anonymous kadmin')
129f = open(os.path.join(realm.testdir, 'acl'), 'a')
130f.write('WELLKNOWN/ANONYMOUS@WELLKNOWN:ANONYMOUS a *')
131f.close()
132realm.start_kadmind()
133realm.run([kadmin, '-n', 'addprinc', '-pw', 'test', 'testadd'])
134realm.run([kadmin, '-n', 'getprinc', 'testadd'], expected_code=1,
135          expected_msg="Operation requires ``get'' privilege")
136realm.stop_kadmind()
137
138# Test with anonymous restricted; FAST should work but kvno should fail.
139mark('anonymous restricted')
140r_env = realm.special_env('restrict', True, kdc_conf=restrictive_kdc_conf)
141realm.stop_kdc()
142realm.start_kdc(env=r_env)
143realm.kinit('@%s' % realm.realm, flags=['-n'])
144realm.kinit('@%s' % realm.realm, flags=['-n', '-T', realm.ccache])
145realm.run([kvno, realm.host_princ], expected_code=1,
146          expected_msg='KDC policy rejects request')
147
148# Regression test for #8458: S4U2Self requests crash the KDC if
149# anonymous is restricted.
150mark('#8458 regression test')
151realm.kinit(realm.host_princ, flags=['-k'])
152realm.run([kvno, '-U', 'user', realm.host_princ])
153
154# Go back to the normal KDC environment.
155realm.stop_kdc()
156realm.start_kdc()
157
158# Run the basic test - PKINIT with FILE: identity, with no password on the key.
159mark('FILE identity, no password')
160msgs = ('Sending unauthenticated request',
161        '/Additional pre-authentication required',
162        'Preauthenticating using KDC method data',
163        'PKINIT client received freshness token from KDC',
164        'PKINIT loading CA certs and CRLs from FILE',
165        'PKINIT client making DH request',
166        ' preauth for next request: PA-FX-COOKIE (133), PA-PK-AS-REQ (16)',
167        'PKINIT client verified DH reply',
168        'PKINIT client found id-pkinit-san in KDC cert',
169        'PKINIT client matched KDC principal krbtgt/')
170realm.pkinit(realm.user_princ, expected_trace=msgs)
171realm.klist(realm.user_princ)
172realm.run([kvno, realm.host_princ])
173
174# Test each Diffie-Hellman group except 1024-bit (which doesn't work
175# in OpenSSL 3.0) and the default 2048-bit group.
176for g in ('4096', 'P-256', 'P-384', 'P-521'):
177    mark('Diffie-Hellman group ' + g)
178    group_conf = {'realms': {'$realm': {'pkinit_dh_min_bits': g}}}
179    group_env = realm.special_env(g, True, krb5_conf=group_conf)
180    realm.pkinit(realm.user_princ, expected_trace=('PKINIT using ' + g,),
181                 env=group_env)
182
183# Test with an EC client cert.
184mark('EC client cert')
185realm.kinit(realm.user_princ,
186            flags=['-X', 'X509_user_identity=%s' % ec_identity])
187
188# Try using multiple configured pkinit_identities, to make sure we
189# fall back to the second one when the first one cannot be read.
190id_conf = {'realms': {'$realm': {'pkinit_identities': [file_identity + 'X',
191                                                       file_identity]}}}
192id_env = realm.special_env('idconf', False, krb5_conf=id_conf)
193realm.kinit(realm.user_princ, expected_trace=msgs, env=id_env)
194
195# Test a DH parameter renegotiation by temporarily setting a 4096-bit
196# minimum on the KDC.  (Preauth type 16 is PKINIT PA_PK_AS_REQ;
197# 109 is PKINIT TD_DH_PARAMETERS; 133 is FAST PA-FX-COOKIE.)
198mark('DH parameter renegotiation')
199minbits_kdc_conf = {'realms': {'$realm': {'pkinit_dh_min_bits': '4096'}}}
200minbits_env = realm.special_env('restrict', True, kdc_conf=minbits_kdc_conf)
201realm.stop_kdc()
202realm.start_kdc(env=minbits_env)
203msgs = ('Sending unauthenticated request',
204        '/Additional pre-authentication required',
205        'Preauthenticating using KDC method data',
206        'PKINIT using 2048-bit DH key exchange group',
207        'Preauth module pkinit (16) (real) returned: 0/Success',
208        ' preauth for next request: PA-FX-COOKIE (133), PA-PK-AS-REQ (16)',
209        '/Key parameters not accepted',
210        'Preauth tryagain input types (16): 109, PA-FX-COOKIE (133)',
211        'PKINIT accepting KDC key exchange group preference P-384',
212        'trying again with KDC-provided parameters',
213        'PKINIT using P-384 key exchange group',
214        'Preauth module pkinit (16) tryagain returned: 0/Success',
215        ' preauth for next request: PA-PK-AS-REQ (16), PA-FX-COOKIE (133)')
216realm.pkinit(realm.user_princ, expected_trace=msgs)
217
218# Test enforcement of required freshness tokens.  (We can leave
219# freshness tokens required after this test.)
220mark('freshness token enforcement')
221realm.pkinit(realm.user_princ, flags=['-X', 'disable_freshness=yes'])
222f_env = realm.special_env('freshness', True, kdc_conf=freshness_kdc_conf)
223realm.stop_kdc()
224realm.start_kdc(env=f_env)
225realm.pkinit(realm.user_princ)
226realm.pkinit(realm.user_princ, flags=['-X', 'disable_freshness=yes'],
227             expected_code=1, expected_msg='Preauthentication failed')
228# Anonymous should never require a freshness token.
229realm.kinit('@%s' % realm.realm, flags=['-n', '-X', 'disable_freshness=yes'])
230
231# Run the basic test - PKINIT with FILE: identity, with a password on the key,
232# supplied by the prompter.
233# Expect failure if the responder does nothing, and we have no prompter.
234mark('FILE identity, password on key (prompter)')
235realm.run(['./responder', '-x', 'pkinit={"%s": 0}' % file_enc_identity,
236          '-X', 'X509_user_identity=%s' % file_enc_identity, realm.user_princ],
237          expected_code=2)
238realm.kinit(realm.user_princ,
239            flags=['-X', 'X509_user_identity=%s' % file_enc_identity],
240            password='encrypted')
241realm.klist(realm.user_princ)
242realm.run([kvno, realm.host_princ])
243realm.run(['./adata', realm.host_princ],
244          expected_msg='+97: [indpkinit1, indpkinit2]')
245
246# Run the basic test - PKINIT with FILE: identity, with a password on the key,
247# supplied by the responder.
248# Supply the response in raw form.
249mark('FILE identity, password on key (responder)')
250out = realm.run(['./responder', '-x', 'pkinit={"%s": 0}' % file_enc_identity,
251                 '-r', 'pkinit={"%s": "encrypted"}' % file_enc_identity,
252                 '-X', 'X509_user_identity=%s' % file_enc_identity,
253                 realm.user_princ])
254# Regression test for #8885 (password question asked twice).
255if out.count('OK: ') != 1:
256    fail('Wrong number of responder calls')
257# Supply the response through the convenience API.
258realm.run(['./responder', '-X', 'X509_user_identity=%s' % file_enc_identity,
259           '-p', '%s=%s' % (file_enc_identity, 'encrypted'), realm.user_princ])
260realm.klist(realm.user_princ)
261realm.run([kvno, realm.host_princ])
262
263# PKINIT with DIR: identity, with no password on the key.
264mark('DIR identity, no password')
265os.mkdir(path)
266os.mkdir(path_enc)
267shutil.copy(privkey_pem, os.path.join(path, 'user.key'))
268shutil.copy(privkey_enc_pem, os.path.join(path_enc, 'user.key'))
269shutil.copy(user_pem, os.path.join(path, 'user.crt'))
270shutil.copy(user_pem, os.path.join(path_enc, 'user.crt'))
271realm.kinit(realm.user_princ,
272            flags=['-X', 'X509_user_identity=%s' % dir_identity])
273realm.klist(realm.user_princ)
274realm.run([kvno, realm.host_princ])
275
276# PKINIT with DIR: identity, with a password on the key, supplied by the
277# prompter.
278# Expect failure if the responder does nothing, and we have no prompter.
279mark('DIR identity, password on key (prompter)')
280realm.run(['./responder', '-x', 'pkinit={"%s": 0}' % dir_file_enc_identity,
281           '-X', 'X509_user_identity=%s' % dir_enc_identity, realm.user_princ],
282           expected_code=2)
283realm.kinit(realm.user_princ,
284            flags=['-X', 'X509_user_identity=%s' % dir_enc_identity],
285            password='encrypted')
286realm.klist(realm.user_princ)
287realm.run([kvno, realm.host_princ])
288
289# PKINIT with DIR: identity, with a password on the key, supplied by the
290# responder.
291# Supply the response in raw form.
292mark('DIR identity, password on key (responder)')
293realm.run(['./responder', '-x', 'pkinit={"%s": 0}' % dir_file_enc_identity,
294           '-r', 'pkinit={"%s": "encrypted"}' % dir_file_enc_identity,
295           '-X', 'X509_user_identity=%s' % dir_enc_identity, realm.user_princ])
296# Supply the response through the convenience API.
297realm.run(['./responder', '-X', 'X509_user_identity=%s' % dir_enc_identity,
298           '-p', '%s=%s' % (dir_file_enc_identity, 'encrypted'),
299           realm.user_princ])
300realm.klist(realm.user_princ)
301realm.run([kvno, realm.host_princ])
302
303# PKINIT with PKCS12: identity, with no password on the bundle.
304mark('PKCS12 identity, no password')
305realm.kinit(realm.user_princ,
306            flags=['-X', 'X509_user_identity=%s' % p12_identity])
307realm.klist(realm.user_princ)
308realm.run([kvno, realm.host_princ])
309
310# PKINIT with PKCS12: identity, with a password on the bundle, supplied by the
311# prompter.
312# Expect failure if the responder does nothing, and we have no prompter.
313mark('PKCS12 identity, password on bundle (prompter)')
314realm.run(['./responder', '-x', 'pkinit={"%s": 0}' % p12_enc_identity,
315           '-X', 'X509_user_identity=%s' % p12_enc_identity, realm.user_princ],
316           expected_code=2)
317realm.kinit(realm.user_princ,
318            flags=['-X', 'X509_user_identity=%s' % p12_enc_identity],
319            password='encrypted')
320realm.klist(realm.user_princ)
321realm.run([kvno, realm.host_princ])
322
323# PKINIT with PKCS12: identity, with a password on the bundle, supplied by the
324# responder.
325# Supply the response in raw form.
326mark('PKCS12 identity, password on bundle (responder)')
327realm.run(['./responder', '-x', 'pkinit={"%s": 0}' % p12_enc_identity,
328           '-r', 'pkinit={"%s": "encrypted"}' % p12_enc_identity,
329           '-X', 'X509_user_identity=%s' % p12_enc_identity, realm.user_princ])
330# Supply the response through the convenience API.
331realm.run(['./responder', '-X', 'X509_user_identity=%s' % p12_enc_identity,
332           '-p', '%s=%s' % (p12_enc_identity, 'encrypted'),
333           realm.user_princ])
334realm.klist(realm.user_princ)
335realm.run([kvno, realm.host_princ])
336
337mark('pkinit_cert_match rules')
338
339# Match a single rule.
340rule = '<SAN>^user@KRBTEST.COM$'
341realm.run([kadminl, 'setstr', realm.user_princ, 'pkinit_cert_match', rule])
342realm.kinit(realm.user_princ,
343            flags=['-X', 'X509_user_identity=%s' % p12_identity])
344realm.klist(realm.user_princ)
345
346# Regression test for #8670: match a UPN SAN with a single rule.
347rule = '<SAN>^user@krbtest.com$'
348realm.run([kadminl, 'setstr', realm.user_princ, 'pkinit_cert_match', rule])
349realm.kinit(realm.user_princ,
350            flags=['-X', 'X509_user_identity=%s' % p12_upn_identity])
351realm.klist(realm.user_princ)
352
353# Match a combined rule (default prefix is &&).
354rule = '<SUBJECT>CN=user$<KU>digitalSignature,keyEncipherment'
355realm.run([kadminl, 'setstr', realm.user_princ, 'pkinit_cert_match', rule])
356realm.kinit(realm.user_princ,
357            flags=['-X', 'X509_user_identity=%s' % p12_identity])
358realm.klist(realm.user_princ)
359
360# Fail an && rule.
361rule = '&&<SUBJECT>O=OTHER.COM<SAN>^user@KRBTEST.COM$'
362realm.run([kadminl, 'setstr', realm.user_princ, 'pkinit_cert_match', rule])
363msg = 'kinit: Certificate mismatch while getting initial credentials'
364realm.kinit(realm.user_princ,
365            flags=['-X', 'X509_user_identity=%s' % p12_identity],
366            expected_code=1, expected_msg=msg)
367
368# Pass an || rule.
369rule = '||<SUBJECT>O=KRBTEST.COM<SAN>^otheruser@KRBTEST.COM$'
370realm.run([kadminl, 'setstr', realm.user_princ, 'pkinit_cert_match', rule])
371realm.kinit(realm.user_princ,
372            flags=['-X', 'X509_user_identity=%s' % p12_identity])
373realm.klist(realm.user_princ)
374
375# Fail an || rule.
376rule = '||<SUBJECT>O=OTHER.COM<SAN>^otheruser@KRBTEST.COM$'
377realm.run([kadminl, 'setstr', realm.user_princ, 'pkinit_cert_match', rule])
378msg = 'kinit: Certificate mismatch while getting initial credentials'
379realm.kinit(realm.user_princ,
380            flags=['-X', 'X509_user_identity=%s' % p12_identity],
381            expected_code=1, expected_msg=msg)
382
383# Authorize a client cert with no PKINIT extensions using subject and
384# issuer.  (Relies on EKU checking being turned off.)
385rule = '&&<SUBJECT>CN=user$<ISSUER>O=MIT,'
386realm.run([kadminl, 'setstr', realm.user_princ, 'pkinit_cert_match', rule])
387realm.kinit(realm.user_princ,
388            flags=['-X', 'X509_user_identity=%s' % p12_generic_identity])
389realm.klist(realm.user_princ)
390
391# Regression test for #8726: null deref when parsing a FILE residual
392# beginning with a comma.
393realm.kinit(realm.user_princ, flags=['-X', 'X509_user_identity=,'],
394            expected_code=1, expected_msg='Preauthentication failed while')
395
396softhsm2 = '/usr/lib/softhsm/libsofthsm2.so'
397if not os.path.exists(softhsm2):
398    skip_rest('PKCS11 tests', 'SoftHSMv2 required')
399pkcs11_tool = which('pkcs11-tool')
400if not pkcs11_tool:
401    skip_rest('PKCS11 tests', 'pkcs11-tool from OpenSC required')
402tool_cmd = [pkcs11_tool, '--module', softhsm2]
403
404# Prepare a SoftHSM token.
405softhsm2_conf = os.path.join(realm.testdir, 'softhsm2.conf')
406softhsm2_tokens = os.path.join(realm.testdir, 'tokens')
407os.mkdir(softhsm2_tokens)
408realm.env['SOFTHSM2_CONF'] = softhsm2_conf
409with open(softhsm2_conf, 'w') as f:
410    f.write('directories.tokendir = %s\n' % softhsm2_tokens)
411realm.run(tool_cmd + ['--init-token', '--label', 'user',
412                      '--so-pin', 'sopin', '--init-pin', '--pin', 'userpin'])
413realm.run(tool_cmd + ['-w', user_pem, '-y', 'cert'])
414realm.run(tool_cmd + ['-w', privkey_pem, '-y', 'privkey',
415                      '-l', '--pin', 'userpin'])
416
417# Extract the slot ID generated by SoftHSM.
418out = realm.run(tool_cmd + ['-L'])
419m = re.search(r'slot ID 0x([0-9a-f]+)\n', out)
420if not m:
421    fail('could not extract slot ID from SoftHSM token')
422slot_id = int(m.group(1), 16)
423
424p11_attr = 'X509_user_identity=PKCS11:' + softhsm2
425p11_token_identity = ('PKCS11:module_name=%s:slotid=%d:token=user' %
426                      (softhsm2, slot_id))
427
428mark('PKCS11 identity, with PIN (prompter)')
429realm.kinit(realm.user_princ, flags=['-X', p11_attr], password='userpin')
430realm.klist(realm.user_princ)
431realm.run([kvno, realm.host_princ])
432
433mark('PKCS11 identity, unavailable PIN')
434realm.run(['./responder', '-x', 'pkinit={"%s": 0}' % p11_token_identity,
435           '-X', p11_attr, realm.user_princ], expected_code=2)
436
437mark('PKCS11 identity, wrong PIN')
438expected_trace = ('PKINIT client has no configured identity; giving up',)
439realm.kinit(realm.user_princ,
440            flags=['-X', p11_attr],
441            password='wrong', expected_code=1, expected_trace=expected_trace)
442
443# PKINIT with PKCS11: identity, with a PIN supplied by the responder.
444# Supply the response in raw form.  Expect the PIN_COUNT_LOW flag (1)
445# to be set due to the previous test.
446mark('PKCS11 identity, with PIN (responder)')
447realm.run(['./responder', '-x', 'pkinit={"%s": 1}' % p11_token_identity,
448           '-r', 'pkinit={"%s": "userpin"}' % p11_token_identity,
449           '-X', p11_attr, realm.user_princ])
450# Supply the response through the convenience API.
451realm.run(['./responder', '-X', p11_attr,
452           '-p', '%s=%s' % (p11_token_identity, 'userpin'),
453           realm.user_princ])
454realm.klist(realm.user_princ)
455realm.run([kvno, realm.host_princ])
456
457mark('PKCS11 identity, EC client cert')
458shutil.rmtree(softhsm2_tokens)
459os.mkdir(softhsm2_tokens)
460realm.run(tool_cmd + ['--init-token', '--label', 'user',
461                      '--so-pin', 'sopin', '--init-pin', '--pin', 'userpin'])
462realm.run(tool_cmd + ['-w', ecuser_pem, '-y', 'cert'])
463realm.run(tool_cmd + ['-w', privkey_ec_pem, '-y', 'privkey',
464                      '-l', '--pin', 'userpin'])
465realm.kinit(realm.user_princ, flags=['-X', p11_attr], password='userpin')
466realm.klist(realm.user_princ)
467realm.run([kvno, realm.host_princ])
468
469success('PKINIT tests')
470