xref: /freebsd/crypto/krb5/src/tests/t_mkey.py (revision 7f2fe78b9dd5f51c821d771b63d2e096f6fd49e9)
1*7f2fe78bSCy Schubertfrom k5test import *
2*7f2fe78bSCy Schubertimport random
3*7f2fe78bSCy Schubertimport re
4*7f2fe78bSCy Schubertimport struct
5*7f2fe78bSCy Schubert
6*7f2fe78bSCy Schubert# Convenience constants for use as expected enctypes.  defetype is the
7*7f2fe78bSCy Schubert# default enctype for master keys.
8*7f2fe78bSCy Schubertaes256 = 'aes256-cts-hmac-sha1-96'
9*7f2fe78bSCy Schubertaes128 = 'aes128-cts-hmac-sha1-96'
10*7f2fe78bSCy Schubertdes3 = 'des3-cbc-sha1'
11*7f2fe78bSCy Schubertdefetype = aes256
12*7f2fe78bSCy Schubert
13*7f2fe78bSCy Schubertrealm = K5Realm(create_host=False, start_kadmind=True)
14*7f2fe78bSCy Schubertrealm.prep_kadmin()
15*7f2fe78bSCy Schubertstash_file = os.path.join(realm.testdir, 'stash')
16*7f2fe78bSCy Schubert
17*7f2fe78bSCy Schubert# Count the number of principals in the realm.
18*7f2fe78bSCy Schubertnprincs = len(realm.run([kadminl, 'listprincs']).splitlines())
19*7f2fe78bSCy Schubert
20*7f2fe78bSCy Schubert# List the currently active mkeys and compare against expected
21*7f2fe78bSCy Schubert# results.  Each argument must be a sequence of four elements: an
22*7f2fe78bSCy Schubert# expected kvno, an expected enctype, whether the key is expected to
23*7f2fe78bSCy Schubert# have an activation time, and whether the key is expected to be
24*7f2fe78bSCy Schubert# currently active.
25*7f2fe78bSCy Schubertlist_mkeys_re = re.compile(r'^KVNO: (\d+), Enctype: (\S+), '
26*7f2fe78bSCy Schubert                           r'(Active on: [^\*]+|No activate time set)( \*)?$')
27*7f2fe78bSCy Schubertdef check_mkey_list(*expected):
28*7f2fe78bSCy Schubert    # Split the output of kdb5_util list_mkeys into lines and ignore the first.
29*7f2fe78bSCy Schubert    outlines = realm.run([kdb5_util, 'list_mkeys']).splitlines()[1:]
30*7f2fe78bSCy Schubert    if len(outlines) != len(expected):
31*7f2fe78bSCy Schubert        fail('Unexpected number of list_mkeys output lines')
32*7f2fe78bSCy Schubert    for line, ex in zip(outlines, expected):
33*7f2fe78bSCy Schubert        m = list_mkeys_re.match(line)
34*7f2fe78bSCy Schubert        if not m:
35*7f2fe78bSCy Schubert            fail('Unrecognized list_mkeys output line')
36*7f2fe78bSCy Schubert        kvno, enctype, act_time, active = m.groups()
37*7f2fe78bSCy Schubert        exp_kvno, exp_enctype, exp_act_time_present, exp_active = ex
38*7f2fe78bSCy Schubert        if kvno != str(exp_kvno):
39*7f2fe78bSCy Schubert            fail('Unexpected master key version')
40*7f2fe78bSCy Schubert        if enctype != exp_enctype:
41*7f2fe78bSCy Schubert            fail('Unexpected master key enctype')
42*7f2fe78bSCy Schubert        if act_time.startswith('Active on: ') != exp_act_time_present:
43*7f2fe78bSCy Schubert            fail('Unexpected presence or absence of mkey activation time')
44*7f2fe78bSCy Schubert        if (active == ' *') != exp_active:
45*7f2fe78bSCy Schubert            fail('Master key unexpectedly active or inactive')
46*7f2fe78bSCy Schubert
47*7f2fe78bSCy Schubert
48*7f2fe78bSCy Schubert# Get the K/M principal.  Verify that it has the expected mkvno.  Each
49*7f2fe78bSCy Schubert# remaining argument must be a sequence of two elements: an expected
50*7f2fe78bSCy Schubert# key version and an expected enctype.
51*7f2fe78bSCy Schubertkeyline_re = re.compile(r'^Key: vno (\d+), (\S+)$')
52*7f2fe78bSCy Schubertdef check_master_dbent(expected_mkvno, *expected_keys):
53*7f2fe78bSCy Schubert    outlines = realm.run([kadminl, 'getprinc', 'K/M']).splitlines()
54*7f2fe78bSCy Schubert    mkeyline = [l for l in outlines if l.startswith('MKey: vno ')]
55*7f2fe78bSCy Schubert    if len(mkeyline) != 1 or mkeyline[0] != ('MKey: vno %d' % expected_mkvno):
56*7f2fe78bSCy Schubert        fail('Unexpected mkvno in K/M DB entry')
57*7f2fe78bSCy Schubert    keylines = [l for l in outlines if l.startswith('Key: vno ')]
58*7f2fe78bSCy Schubert    if len(keylines) != len(expected_keys):
59*7f2fe78bSCy Schubert        fail('Unexpected number of key lines in K/M DB entry')
60*7f2fe78bSCy Schubert    for line, ex in zip(keylines, expected_keys):
61*7f2fe78bSCy Schubert        m = keyline_re.match(line)
62*7f2fe78bSCy Schubert        if not m:
63*7f2fe78bSCy Schubert            fail('Unrecognized key line in K/M DB entry')
64*7f2fe78bSCy Schubert        kvno, enctype = m.groups()
65*7f2fe78bSCy Schubert        exp_kvno, exp_enctype = ex
66*7f2fe78bSCy Schubert        if kvno != str(exp_kvno):
67*7f2fe78bSCy Schubert            fail('Unexpected key version in K/M DB entry')
68*7f2fe78bSCy Schubert        if enctype != exp_enctype:
69*7f2fe78bSCy Schubert            fail('Unexpected enctype in K/M DB entry')
70*7f2fe78bSCy Schubert
71*7f2fe78bSCy Schubert
72*7f2fe78bSCy Schubert# Check the stash file.  Each argument must be a sequence of two
73*7f2fe78bSCy Schubert# elements: an expected key version and an expected enctype.
74*7f2fe78bSCy Schubertklist_re = re.compile(r'^\s*(\d+) K/M@KRBTEST.COM \((\S+)\)')
75*7f2fe78bSCy Schubertdef check_stash(*expected):
76*7f2fe78bSCy Schubert    # Split the output of klist -e -k into lines and ignore the first three.
77*7f2fe78bSCy Schubert    outlines = realm.run([klist, '-e', '-k', stash_file]).splitlines()[3:]
78*7f2fe78bSCy Schubert    if len(outlines) != len(expected):
79*7f2fe78bSCy Schubert        fail('Unexpected number of lines in stash file klist')
80*7f2fe78bSCy Schubert    for line, ex in zip(outlines, expected):
81*7f2fe78bSCy Schubert        m = klist_re.match(line)
82*7f2fe78bSCy Schubert        if not m:
83*7f2fe78bSCy Schubert            fail('Unrecognized stash file klist line')
84*7f2fe78bSCy Schubert        kvno, enctype = m.groups()
85*7f2fe78bSCy Schubert        exp_kvno, exp_enctype = ex
86*7f2fe78bSCy Schubert        if kvno != str(exp_kvno):
87*7f2fe78bSCy Schubert            fail('Unexpected stash file klist kvno')
88*7f2fe78bSCy Schubert        if enctype != exp_enctype:
89*7f2fe78bSCy Schubert            fail('Unexpected stash file klist enctype')
90*7f2fe78bSCy Schubert
91*7f2fe78bSCy Schubert
92*7f2fe78bSCy Schubert# Verify that the user principal has the expected mkvno.
93*7f2fe78bSCy Schubertdef check_mkvno(princ, expected_mkvno):
94*7f2fe78bSCy Schubert    msg = 'MKey: vno %d\n' % expected_mkvno
95*7f2fe78bSCy Schubert    realm.run([kadminl, 'getprinc', princ], expected_msg=msg)
96*7f2fe78bSCy Schubert
97*7f2fe78bSCy Schubert
98*7f2fe78bSCy Schubert# Change the password using either kadmin.local or kadmin, then check
99*7f2fe78bSCy Schubert# the mkvno of the principal against expected_mkvno and verify that
100*7f2fe78bSCy Schubert# the running KDC can access the new key.
101*7f2fe78bSCy Schubertdef change_password_check_mkvno(local, princ, password, expected_mkvno):
102*7f2fe78bSCy Schubert    cmd = ['cpw', '-pw', password, princ]
103*7f2fe78bSCy Schubert    if local:
104*7f2fe78bSCy Schubert        realm.run([kadminl] + cmd)
105*7f2fe78bSCy Schubert    else:
106*7f2fe78bSCy Schubert        realm.run_kadmin(cmd)
107*7f2fe78bSCy Schubert    check_mkvno(princ, expected_mkvno)
108*7f2fe78bSCy Schubert    realm.kinit(princ, password)
109*7f2fe78bSCy Schubert
110*7f2fe78bSCy Schubert
111*7f2fe78bSCy Schubert# Add a master key with the specified options and a random password.
112*7f2fe78bSCy Schubertdef add_mkey(options):
113*7f2fe78bSCy Schubert    pw = ''.join(random.choice(string.ascii_uppercase) for x in range(5))
114*7f2fe78bSCy Schubert    realm.run([kdb5_util, 'add_mkey'] + options, input=(pw + '\n' + pw + '\n'))
115*7f2fe78bSCy Schubert
116*7f2fe78bSCy Schubert
117*7f2fe78bSCy Schubert# Run kdb5_util update_princ_encryption (with the dry-run option if
118*7f2fe78bSCy Schubert# specified) and verify the output against the expected mkvno, number
119*7f2fe78bSCy Schubert# of updated principals, and number of already-current principals.
120*7f2fe78bSCy Schubertmkvno_re = {False: re.compile(r'^Principals whose keys are being re-encrypted '
121*7f2fe78bSCy Schubert                              r'to master key vno (\d+) if necessary:$'),
122*7f2fe78bSCy Schubert            True: re.compile(r'^Principals whose keys WOULD BE re-encrypted '
123*7f2fe78bSCy Schubert                             r'to master key vno (\d+):$')}
124*7f2fe78bSCy Schubertcount_re = {False: re.compile(r'^(\d+) principals processed: (\d+) updated, '
125*7f2fe78bSCy Schubert                              r'(\d+) already current$'),
126*7f2fe78bSCy Schubert            True: re.compile(r'^(\d+) principals processed: (\d+) would be '
127*7f2fe78bSCy Schubert                             r'updated, (\d+) already current$')}
128*7f2fe78bSCy Schubertdef update_princ_encryption(dry_run, expected_mkvno, expected_updated,
129*7f2fe78bSCy Schubert                            expected_current):
130*7f2fe78bSCy Schubert    opts = ['-f', '-v']
131*7f2fe78bSCy Schubert    if dry_run:
132*7f2fe78bSCy Schubert        opts += ['-n']
133*7f2fe78bSCy Schubert    out = realm.run([kdb5_util, 'update_princ_encryption'] + opts)
134*7f2fe78bSCy Schubert    lines = out.splitlines()
135*7f2fe78bSCy Schubert    # Parse the first line to get the target mkvno.
136*7f2fe78bSCy Schubert    m = mkvno_re[dry_run].match(lines[0])
137*7f2fe78bSCy Schubert    if not m:
138*7f2fe78bSCy Schubert        fail('Unexpected first line of update_princ_encryption output')
139*7f2fe78bSCy Schubert    if m.group(1) != str(expected_mkvno):
140*7f2fe78bSCy Schubert        fail('Unexpected master key version in update_princ_encryption output')
141*7f2fe78bSCy Schubert    # Parse the last line to get the principal counts.
142*7f2fe78bSCy Schubert    m = count_re[dry_run].match(lines[-1])
143*7f2fe78bSCy Schubert    if not m:
144*7f2fe78bSCy Schubert        fail('Unexpected last line of update_princ_encryption output')
145*7f2fe78bSCy Schubert    total, updated, current = m.groups()
146*7f2fe78bSCy Schubert    if (total != str(expected_updated + expected_current) or
147*7f2fe78bSCy Schubert        updated != str(expected_updated) or current != str(expected_current)):
148*7f2fe78bSCy Schubert        fail('Unexpected counts from update_princ_encryption')
149*7f2fe78bSCy Schubert
150*7f2fe78bSCy Schubert
151*7f2fe78bSCy Schubert# Check the initial state of the realm.
152*7f2fe78bSCy Schubertmark('initial state')
153*7f2fe78bSCy Schubertcheck_mkey_list((1, defetype, True, True))
154*7f2fe78bSCy Schubertcheck_master_dbent(1, (1, defetype))
155*7f2fe78bSCy Schubertcheck_stash((1, defetype))
156*7f2fe78bSCy Schubertcheck_mkvno(realm.user_princ, 1)
157*7f2fe78bSCy Schubert
158*7f2fe78bSCy Schubert# Check that stash will fail if a temp stash file is already present.
159*7f2fe78bSCy Schubertmark('temp stash collision')
160*7f2fe78bSCy Schubertcollisionfile = os.path.join(realm.testdir, 'stash_tmp')
161*7f2fe78bSCy Schubertf = open(collisionfile, 'w')
162*7f2fe78bSCy Schubertf.close()
163*7f2fe78bSCy Schubertrealm.run([kdb5_util, 'stash'], expected_code=1,
164*7f2fe78bSCy Schubert          expected_msg='Temporary stash file already exists')
165*7f2fe78bSCy Schubertos.unlink(collisionfile)
166*7f2fe78bSCy Schubert
167*7f2fe78bSCy Schubert# Add a new master key with no options.  Verify that:
168*7f2fe78bSCy Schubert# 1. The new key appears in list_mkeys but has no activation time and
169*7f2fe78bSCy Schubert#    is not active.
170*7f2fe78bSCy Schubert# 2. The new key appears in the K/M DB entry and is the current key to
171*7f2fe78bSCy Schubert#    encrypt that entry.
172*7f2fe78bSCy Schubert# 3. The stash file is not modified (since we did not pass -s).
173*7f2fe78bSCy Schubert# 4. The old key is used for password changes.
174*7f2fe78bSCy Schubertmark('add_mkey (second master key)')
175*7f2fe78bSCy Schubertadd_mkey([])
176*7f2fe78bSCy Schubertcheck_mkey_list((2, defetype, False, False), (1, defetype, True, True))
177*7f2fe78bSCy Schubertcheck_master_dbent(2, (2, defetype), (1, defetype))
178*7f2fe78bSCy Schubertchange_password_check_mkvno(True, realm.user_princ, 'abcd', 1)
179*7f2fe78bSCy Schubertchange_password_check_mkvno(False, realm.user_princ, 'user', 1)
180*7f2fe78bSCy Schubert
181*7f2fe78bSCy Schubert# Verify that use_mkey won't make all master keys inactive.
182*7f2fe78bSCy Schubertmark('use_mkey (no active keys)')
183*7f2fe78bSCy Schubertrealm.run([kdb5_util, 'use_mkey', '1', 'now+1day'], expected_code=1,
184*7f2fe78bSCy Schubert          expected_msg='there must be one master key currently active')
185*7f2fe78bSCy Schubertcheck_mkey_list((2, defetype, False, False), (1, defetype, True, True))
186*7f2fe78bSCy Schubert
187*7f2fe78bSCy Schubert# Make the new master key active.  Verify that:
188*7f2fe78bSCy Schubert# 1. The new key has an activation time in list_mkeys and is active.
189*7f2fe78bSCy Schubert# 2. The new key is used for password changes.
190*7f2fe78bSCy Schubert# 3. The running KDC can access the new key.
191*7f2fe78bSCy Schubertmark('use_mkey')
192*7f2fe78bSCy Schubertrealm.run([kdb5_util, 'use_mkey', '2', 'now-1day'])
193*7f2fe78bSCy Schubertcheck_mkey_list((2, defetype, True, True), (1, defetype, True, False))
194*7f2fe78bSCy Schubertchange_password_check_mkvno(True, realm.user_princ, 'abcd', 2)
195*7f2fe78bSCy Schubertchange_password_check_mkvno(False, realm.user_princ, 'user', 2)
196*7f2fe78bSCy Schubert
197*7f2fe78bSCy Schubert# Check purge_mkeys behavior with both master keys still in use.
198*7f2fe78bSCy Schubertmark('purge_mkeys (nothing to purge)')
199*7f2fe78bSCy Schubertrealm.run([kdb5_util, 'purge_mkeys', '-f', '-v'],
200*7f2fe78bSCy Schubert          expected_msg='All keys in use, nothing purged.')
201*7f2fe78bSCy Schubert
202*7f2fe78bSCy Schubert# Do an update_princ_encryption dry run and for real.  Verify that:
203*7f2fe78bSCy Schubert# 1. The target master key is 2 (the active mkvno).
204*7f2fe78bSCy Schubert# 2. nprincs - 2 principals were updated and one principal was
205*7f2fe78bSCy Schubert#    skipped (K/M is not included in the output and user was updated
206*7f2fe78bSCy Schubert#    above).
207*7f2fe78bSCy Schubert# 3. The dry run doesn't change user/admin's mkvno but the real update
208*7f2fe78bSCy Schubert#    does.
209*7f2fe78bSCy Schubert# 4. The old stashed master key is sufficient to access the DB (via
210*7f2fe78bSCy Schubert#    MKEY_AUX tl-data which keeps the current master key encrypted in
211*7f2fe78bSCy Schubert#    each of the old master keys).
212*7f2fe78bSCy Schubertmark('update_princ_encryption')
213*7f2fe78bSCy Schubertupdate_princ_encryption(True, 2, nprincs - 2, 1)
214*7f2fe78bSCy Schubertcheck_mkvno(realm.admin_princ, 1)
215*7f2fe78bSCy Schubertupdate_princ_encryption(False, 2, nprincs - 2, 1)
216*7f2fe78bSCy Schubertcheck_mkvno(realm.admin_princ, 2)
217*7f2fe78bSCy Schubertrealm.stop_kdc()
218*7f2fe78bSCy Schubertrealm.start_kdc()
219*7f2fe78bSCy Schubertrealm.kinit(realm.user_princ, 'user')
220*7f2fe78bSCy Schubert
221*7f2fe78bSCy Schubert# Update all principals back to mkvno 1 and to mkvno 2 again, to
222*7f2fe78bSCy Schubert# verify that update_princ_encryption targets the active master key.
223*7f2fe78bSCy Schubertmark('update_princ_encryption (back and forth)')
224*7f2fe78bSCy Schubertrealm.run([kdb5_util, 'use_mkey', '2', 'now+1day'])
225*7f2fe78bSCy Schubertupdate_princ_encryption(False, 1, nprincs - 1, 0)
226*7f2fe78bSCy Schubertcheck_mkvno(realm.user_princ, 1)
227*7f2fe78bSCy Schubertrealm.run([kdb5_util, 'use_mkey', '2', 'now-1day'])
228*7f2fe78bSCy Schubertupdate_princ_encryption(False, 2, nprincs - 1, 0)
229*7f2fe78bSCy Schubertcheck_mkvno(realm.user_princ, 2)
230*7f2fe78bSCy Schubert
231*7f2fe78bSCy Schubert# Test the safety check for purging with an outdated stash file.
232*7f2fe78bSCy Schubertmark('purge_mkeys (outdated stash file)')
233*7f2fe78bSCy Schubertrealm.run([kdb5_util, 'purge_mkeys', '-f'], expected_code=1,
234*7f2fe78bSCy Schubert          expected_msg='stash file needs updating')
235*7f2fe78bSCy Schubert
236*7f2fe78bSCy Schubert# Update the master stash file and check it.  Save a copy of the old
237*7f2fe78bSCy Schubert# one for a later test.
238*7f2fe78bSCy Schubertmark('update stash file')
239*7f2fe78bSCy Schubertshutil.copy(stash_file, stash_file + '.old')
240*7f2fe78bSCy Schubertrealm.run([kdb5_util, 'stash'])
241*7f2fe78bSCy Schubertcheck_stash((2, defetype), (1, defetype))
242*7f2fe78bSCy Schubert
243*7f2fe78bSCy Schubert# Do a purge_mkeys dry run and for real.  Verify that:
244*7f2fe78bSCy Schubert# 1. Master key 1 is purged.
245*7f2fe78bSCy Schubert# 2. The dry run doesn't remove mkvno 1 but the real one does.
246*7f2fe78bSCy Schubert# 3. The old stash file is no longer sufficient to access the DB.
247*7f2fe78bSCy Schubert# 4. If the stash file is updated, it no longer contains mkvno 1.
248*7f2fe78bSCy Schubert# 5. use_mkey now gives an error if we refer to mkvno 1.
249*7f2fe78bSCy Schubert# 6. A second purge_mkeys gives the right message.
250*7f2fe78bSCy Schubertmark('purge_mkeys')
251*7f2fe78bSCy Schubertout = realm.run([kdb5_util, 'purge_mkeys', '-v', '-n', '-f'])
252*7f2fe78bSCy Schubertif 'KVNO: 1' not in out or '1 key(s) would be purged' not in out:
253*7f2fe78bSCy Schubert    fail('Unexpected output from purge_mkeys dry-run')
254*7f2fe78bSCy Schubertcheck_mkey_list((2, defetype, True, True), (1, defetype, True, False))
255*7f2fe78bSCy Schubertcheck_master_dbent(2, (2, defetype), (1, defetype))
256*7f2fe78bSCy Schubertout = realm.run([kdb5_util, 'purge_mkeys', '-v', '-f'])
257*7f2fe78bSCy Schubertcheck_mkey_list((2, defetype, True, True))
258*7f2fe78bSCy Schubertcheck_master_dbent(2, (2, defetype))
259*7f2fe78bSCy Schubertos.rename(stash_file, stash_file + '.save')
260*7f2fe78bSCy Schubertos.rename(stash_file + '.old', stash_file)
261*7f2fe78bSCy Schubertrealm.run([kadminl, 'getprinc', 'user'], expected_code=1,
262*7f2fe78bSCy Schubert          expected_msg='Unable to decrypt latest master key')
263*7f2fe78bSCy Schubertos.rename(stash_file + '.save', stash_file)
264*7f2fe78bSCy Schubertrealm.run([kdb5_util, 'stash'])
265*7f2fe78bSCy Schubertcheck_stash((2, defetype))
266*7f2fe78bSCy Schubertrealm.run([kdb5_util, 'use_mkey', '1'], expected_code=1,
267*7f2fe78bSCy Schubert          expected_msg='1 is an invalid KVNO value')
268*7f2fe78bSCy Schubertrealm.run([kdb5_util, 'purge_mkeys', '-f', '-v'],
269*7f2fe78bSCy Schubert          expected_msg='There is only one master key which can not be purged.')
270*7f2fe78bSCy Schubert
271*7f2fe78bSCy Schubert# Add a third master key with a specified enctype.  Verify that:
272*7f2fe78bSCy Schubert# 1. The new master key receives the correct number.
273*7f2fe78bSCy Schubert# 2. The enctype argument is respected.
274*7f2fe78bSCy Schubert# 3. The new master key is stashed (by itself, at the moment).
275*7f2fe78bSCy Schubert# 4. We can roll over to the new master key and use it.
276*7f2fe78bSCy Schubertmark('add_mkey and update_princ_encryption (third master key)')
277*7f2fe78bSCy Schubertadd_mkey(['-s', '-e', aes128])
278*7f2fe78bSCy Schubertcheck_mkey_list((3, aes128, False, False), (2, defetype, True, True))
279*7f2fe78bSCy Schubertcheck_master_dbent(3, (3, aes128), (2, defetype))
280*7f2fe78bSCy Schubertcheck_stash((3, aes128))
281*7f2fe78bSCy Schubertrealm.run([kdb5_util, 'use_mkey', '3', 'now-1day'])
282*7f2fe78bSCy Schubertupdate_princ_encryption(False, 3, nprincs - 1, 0)
283*7f2fe78bSCy Schubertcheck_mkey_list((3, aes128, True, True), (2, defetype, True, False))
284*7f2fe78bSCy Schubertcheck_mkvno(realm.user_princ, 3)
285*7f2fe78bSCy Schubert
286*7f2fe78bSCy Schubert# Regression test for #7994 (randkey does not update principal mkvno)
287*7f2fe78bSCy Schubert# and #7995 (-keepold does not re-encrypt old keys).
288*7f2fe78bSCy Schubertmark('#7994 and #7995 regression test')
289*7f2fe78bSCy Schubertadd_mkey(['-s'])
290*7f2fe78bSCy Schubertrealm.run([kdb5_util, 'use_mkey', '4', 'now-1day'])
291*7f2fe78bSCy Schubertrealm.run([kadminl, 'cpw', '-randkey', '-keepold', realm.user_princ])
292*7f2fe78bSCy Schubert# With #7994 unfixed, mkvno of user will still be 3.
293*7f2fe78bSCy Schubertcheck_mkvno(realm.user_princ, 4)
294*7f2fe78bSCy Schubert# With #7995 unfixed, old keys are still encrypted with mkvno 3.
295*7f2fe78bSCy Schubertupdate_princ_encryption(False, 4, nprincs - 2, 1)
296*7f2fe78bSCy Schubertrealm.run([kdb5_util, 'purge_mkeys', '-f'])
297*7f2fe78bSCy Schubertout = realm.run([kadminl, 'xst', '-norandkey', realm.user_princ])
298*7f2fe78bSCy Schubertif 'Decrypt integrity check failed' in out or 'added to keytab' not in out:
299*7f2fe78bSCy Schubert    fail('Preserved old key data not updated to new master key')
300*7f2fe78bSCy Schubert
301*7f2fe78bSCy Schubertrealm.stop()
302*7f2fe78bSCy Schubert
303*7f2fe78bSCy Schubert# Load a dump file created with krb5 1.6, before the master key
304*7f2fe78bSCy Schubert# rollover changes were introduced.  Write out an old-format stash
305*7f2fe78bSCy Schubert# file consistent with the dump's master password ("footes").  The K/M
306*7f2fe78bSCy Schubert# entry in this database will not have actkvno tl-data because it was
307*7f2fe78bSCy Schubert# created prior to master key rollover support.  Verify that:
308*7f2fe78bSCy Schubert# 1. We can access the database using the old-format stash file.
309*7f2fe78bSCy Schubert# 2. list_mkeys displays the same list as for a post-1.7 KDB.
310*7f2fe78bSCy Schubertmark('pre-1.7 stash file')
311*7f2fe78bSCy Schubertdumpfile = os.path.join(srctop, 'tests', 'dumpfiles', 'dump.16')
312*7f2fe78bSCy Schubertos.remove(stash_file)
313*7f2fe78bSCy Schubertf = open(stash_file, 'wb')
314*7f2fe78bSCy Schubertf.write(struct.pack('=HL24s', 16, 24,
315*7f2fe78bSCy Schubert                    b'\xF8\x3E\xFB\xBA\x6D\x80\xD9\x54\xE5\x5D\xF2\xE0'
316*7f2fe78bSCy Schubert                    b'\x94\xAD\x6D\x86\xB5\x16\x37\xEC\x7C\x8A\xBC\x86'))
317*7f2fe78bSCy Schubertf.close()
318*7f2fe78bSCy Schubertrealm.run([kdb5_util, 'load', dumpfile])
319*7f2fe78bSCy Schubertnprincs = len(realm.run([kadminl, 'listprincs']).splitlines())
320*7f2fe78bSCy Schubertcheck_mkvno('K/M', 1)
321*7f2fe78bSCy Schubertcheck_mkey_list((1, des3, True, True))
322*7f2fe78bSCy Schubert
323*7f2fe78bSCy Schubert# Create a new master key and verify that, without actkvkno tl-data:
324*7f2fe78bSCy Schubert# 1. list_mkeys displays the same as for a post-1.7 KDB.
325*7f2fe78bSCy Schubert# 2. update_princ_encryption still targets mkvno 1.
326*7f2fe78bSCy Schubert# 3. libkadm5 still uses mkvno 1 for key changes.
327*7f2fe78bSCy Schubert# 4. use_mkey creates the same list as for a post-1.7 KDB.
328*7f2fe78bSCy Schubertmark('rollover from pre-1.7 KDB')
329*7f2fe78bSCy Schubertadd_mkey([])
330*7f2fe78bSCy Schubertcheck_mkey_list((2, defetype, False, False), (1, des3, True, True))
331*7f2fe78bSCy Schubertupdate_princ_encryption(False, 1, 0, nprincs - 1)
332*7f2fe78bSCy Schubertrealm.run([kadminl, 'addprinc', '-randkey', realm.user_princ])
333*7f2fe78bSCy Schubertcheck_mkvno(realm.user_princ, 1)
334*7f2fe78bSCy Schubertrealm.run([kdb5_util, 'use_mkey', '2', 'now-1day'])
335*7f2fe78bSCy Schubertcheck_mkey_list((2, defetype, True, True), (1, des3, True, False))
336*7f2fe78bSCy Schubert
337*7f2fe78bSCy Schubert# Regression test for #8395.  Purge the master key and verify that a
338*7f2fe78bSCy Schubert# master key fetch does not segfault.
339*7f2fe78bSCy Schubertmark('#8395 regression test')
340*7f2fe78bSCy Schubertrealm.run([kadminl, 'purgekeys', '-all', 'K/M'])
341*7f2fe78bSCy Schubertrealm.run([kadminl, 'getprinc', realm.user_princ], expected_code=1,
342*7f2fe78bSCy Schubert          expected_msg='Cannot find master key record in database')
343*7f2fe78bSCy Schubert
344*7f2fe78bSCy Schubertsuccess('Master key rollover tests')
345