xref: /freebsd/crypto/krb5/src/tests/t_kadmin_acl.py (revision f1c4c3daccbaf3820f0e2224de53df12fc952fcc)
1from k5test import *
2import os
3
4realm = K5Realm(create_host=False, create_user=False)
5
6def make_client(name):
7    global realm
8    realm.addprinc(name, password(name))
9    ccache = os.path.join(realm.testdir,
10                          'kadmin_ccache_' + name.replace('/', '_'))
11    realm.kinit(name, password(name),
12                flags=['-S', 'kadmin/admin', '-c', ccache])
13    return ccache
14
15def kadmin_as(client, query, **kwargs):
16    global realm
17    return realm.run([kadmin, '-c', client] + query, **kwargs)
18
19all_add = make_client('all_add')
20all_changepw = make_client('all_changepw')
21all_delete = make_client('all_delete')
22all_inquire = make_client('all_inquire')
23all_list = make_client('all_list')
24all_modify = make_client('all_modify')
25all_rename = make_client('all_rename')
26all_wildcard = make_client('all_wildcard')
27all_extract = make_client('all_extract')
28all_alias = make_client('all_alias')
29some_add = make_client('some_add')
30some_changepw = make_client('some_changepw')
31some_delete = make_client('some_delete')
32some_inquire = make_client('some_inquire')
33some_modify = make_client('some_modify')
34some_rename = make_client('some_rename')
35some_extract = make_client('some_extract')
36some_alias = make_client('some_alias')
37restricted_add = make_client('restricted_add')
38restricted_modify = make_client('restricted_modify')
39restricted_rename = make_client('restricted_rename')
40restricted_alias = make_client('restricted_alias')
41wctarget = make_client('wctarget')
42admin = make_client('user/admin')
43none = make_client('none')
44restrictions = make_client('restrictions')
45onetwothreefour = make_client('one/two/three/four')
46
47realm.run([kadminl, 'alias', 'aliastonone', 'none'])
48aliastonone = os.path.join(realm.testdir, 'kadmin_ccache_aliastonone')
49realm.kinit('aliastonone', password('none'),
50            flags=['-S', 'kadmin/admin', '-c', aliastonone])
51
52realm.run([kadminl, 'alias', 'aliastounselected', 'unselected'])
53realm.run([kadminl, 'alias', 'aliastoselected', 'selected'])
54
55realm.run([kadminl, 'addpol', '-minlife', '1 day', 'minlife'])
56
57f = open(os.path.join(realm.testdir, 'acl'), 'w')
58f.write('''
59all_add            a
60all_changepw       c
61all_delete         d
62all_inquire        i
63all_list           l
64all_modify         im
65all_rename         ad
66all_wildcard       x
67all_extract        ie
68all_alias          am
69some_add           a   selected
70some_add           a   aliastounselected
71some_changepw      c   selected
72some_changepw      c   aliastounselected
73some_delete        d   selected
74some_delete        d   aliastounselected
75some_inquire       i   selected
76some_inquire       i   aliastounselected
77some_modify        im  selected
78some_modify        im  aliastounselected
79some_extract       ie  selected
80some_extract       ie  aliastounselected
81some_rename        d   from
82some_rename        a   to
83some_alias         a   aliasname
84some_alias         m   canon
85restricted_add     a   *         +preauth
86restricted_modify  im  *         +preauth
87restricted_rename  ad  *         +preauth
88restricted_alias   ai  *         +preauth
89
90*/*                d   *2/*1
91# The next line is a regression test for #8154; it is not used directly.
92one/*/*/five       l
93*/two/*/*          d   *3/*1/*2
94*/admin            a
95wctarget           a   wild/*
96restrictions       a   type1     -policy minlife
97restrictions       a   type2     -clearpolicy
98restrictions       a   type3     -maxlife 1h -maxrenewlife 2h
99''')
100f.close()
101
102realm.start_kadmind()
103
104# cpw can generate four different RPC calls depending on options.
105realm.addprinc('selected', 'oldpw')
106realm.addprinc('unselected', 'oldpw')
107for pw in (['-pw', 'newpw'], ['-randkey']):
108    for ks in ([], ['-e', 'aes256-cts']):
109        mark('cpw: %s %s' % (repr(pw), repr(ks)))
110        args = pw + ks
111        kadmin_as(all_changepw, ['cpw'] + args + ['unselected'])
112        kadmin_as(some_changepw, ['cpw'] + args + ['selected'])
113        msg = "Operation requires ``change-password'' privilege"
114        kadmin_as(none, ['cpw'] + args + ['selected'], expected_code=1,
115                  expected_msg=msg)
116        kadmin_as(some_changepw, ['cpw'] + args + ['unselected'],
117                  expected_code=1, expected_msg=msg)
118        # Verify that the ACL check is canonicalized.
119        kadmin_as(some_changepw, ['cpw'] + args + ['aliastounselected'],
120                  expected_code=1, expected_msg=msg)
121        kadmin_as(some_changepw, ['cpw'] + args + ['aliastoselected'])
122        kadmin_as(none, ['cpw'] + args + ['none'])
123        kadmin_as(aliastonone, ['cpw'] + args + ['none'],
124                  expected_code=1, expected_msg=msg)
125        realm.run([kadminl, 'modprinc', '-policy', 'minlife', 'none'])
126        msg = "Current password's minimum life has not expired"
127        kadmin_as(none, ['cpw'] + args + ['none'], expected_code=1,
128                  expected_msg=msg)
129        realm.run([kadminl, 'modprinc', '-clearpolicy', 'none'])
130realm.run([kadminl, 'delprinc', 'selected'])
131realm.run([kadminl, 'delprinc', 'unselected'])
132
133mark('addpol')
134kadmin_as(all_add, ['addpol', 'policy'])
135realm.run([kadminl, 'delpol', 'policy'])
136kadmin_as(none, ['addpol', 'policy'], expected_code=1,
137          expected_msg="Operation requires ``add'' privilege")
138
139# addprinc can generate two different RPC calls depending on options.
140for ks in ([], ['-e', 'aes256-cts']):
141    mark('addprinc: %s' % repr(ks))
142    args = ['-pw', 'pw'] + ks
143    kadmin_as(all_add, ['addprinc'] + args + ['unselected'])
144    realm.run([kadminl, 'delprinc', 'unselected'])
145    kadmin_as(some_add, ['addprinc'] + args + ['selected'])
146    realm.run([kadminl, 'delprinc', 'selected'])
147    kadmin_as(restricted_add, ['addprinc'] + args + ['unselected'])
148    realm.run([kadminl, 'getprinc', 'unselected'],
149              expected_msg='REQUIRES_PRE_AUTH')
150    realm.run([kadminl, 'delprinc', 'unselected'])
151    kadmin_as(none, ['addprinc'] + args + ['selected'], expected_code=1,
152              expected_msg="Operation requires ``add'' privilege")
153    kadmin_as(some_add, ['addprinc'] + args + ['unselected'], expected_code=1,
154              expected_msg="Operation requires ``add'' privilege")
155    # Verify that the ACL check isn't canonicalized.  (We need the alias
156    # to resolve or we will overwrite it, currently.)
157    realm.addprinc('unselected')
158    kadmin_as(some_add, ['addprinc'] + args + ['aliastounselected'],
159              expected_code=1, expected_msg='already exists')
160    realm.run([kadminl, 'delprinc', 'unselected'])
161
162mark('delprinc')
163realm.addprinc('unselected', 'pw')
164kadmin_as(all_delete, ['delprinc', 'unselected'])
165realm.addprinc('selected', 'pw')
166kadmin_as(some_delete, ['delprinc', 'selected'])
167realm.addprinc('unselected', 'pw')
168kadmin_as(none, ['delprinc', 'unselected'], expected_code=1,
169          expected_msg="Operation requires ``delete'' privilege")
170kadmin_as(some_delete, ['delprinc', 'unselected'], expected_code=1,
171          expected_msg="Operation requires ``delete'' privilege")
172# Verify that the ACL check isn't canonicalized.
173kadmin_as(some_delete, ['delprinc', 'aliastounselected'])
174realm.run([kadminl, 'alias', 'aliastounselected', 'unselected'])
175kadmin_as(some_delete, ['delprinc', 'aliastoselected'], expected_code=1,
176          expected_msg="Operation requires ``delete'' privilege")
177realm.run([kadminl, 'delprinc', 'unselected'])
178
179mark('getpol')
180kadmin_as(all_inquire, ['getpol', 'minlife'], expected_msg='Policy: minlife')
181kadmin_as(none, ['getpol', 'minlife'], expected_code=1,
182          expected_msg="Operation requires ``get'' privilege")
183realm.run([kadminl, 'modprinc', '-policy', 'minlife', 'none'])
184kadmin_as(none, ['getpol', 'minlife'], expected_msg='Policy: minlife')
185realm.run([kadminl, 'modprinc', '-clearpolicy', 'none'])
186
187mark('getprinc')
188realm.addprinc('selected', 'pw')
189realm.addprinc('unselected', 'pw')
190kadmin_as(all_inquire, ['getprinc', 'unselected'],
191          expected_msg='Principal: unselected@KRBTEST.COM')
192kadmin_as(some_inquire, ['getprinc', 'selected'],
193          expected_msg='Principal: selected@KRBTEST.COM')
194kadmin_as(none, ['getprinc', 'selected'], expected_code=1,
195          expected_msg="Operation requires ``get'' privilege")
196kadmin_as(some_inquire, ['getprinc', 'unselected'], expected_code=1,
197          expected_msg="Operation requires ``get'' privilege")
198# Verify that the ACL check is canonicalized.
199kadmin_as(some_inquire, ['getprinc', 'aliastounselected'], expected_code=1,
200          expected_msg="Operation requires ``get'' privilege")
201kadmin_as(some_inquire, ['getprinc', 'aliastoselected'],
202          expected_msg='Principal: selected@KRBTEST.COM')
203kadmin_as(none, ['getprinc', 'none'],
204          expected_msg='Principal: none@KRBTEST.COM')
205realm.run([kadminl, 'delprinc', 'selected'])
206realm.run([kadminl, 'delprinc', 'unselected'])
207
208mark('listprincs')
209kadmin_as(all_list, ['listprincs'], expected_msg='K/M@KRBTEST.COM')
210kadmin_as(none, ['listprincs'], expected_code=1,
211          expected_msg="Operation requires ``list'' privilege")
212
213mark('getstrs')
214realm.addprinc('selected', 'pw')
215realm.addprinc('unselected', 'pw')
216realm.run([kadminl, 'setstr', 'selected', 'key', 'value'])
217realm.run([kadminl, 'setstr', 'unselected', 'key', 'value'])
218kadmin_as(all_inquire, ['getstrs', 'unselected'], expected_msg='key: value')
219kadmin_as(some_inquire, ['getstrs', 'selected'], expected_msg='key: value')
220kadmin_as(none, ['getstrs', 'selected'], expected_code=1,
221          expected_msg="Operation requires ``get'' privilege")
222kadmin_as(some_inquire, ['getstrs', 'unselected'], expected_code=1,
223          expected_msg="Operation requires ``get'' privilege")
224# Verify that the ACL check is canonicalized.
225kadmin_as(some_inquire, ['getstrs', 'aliastounselected'], expected_code=1,
226          expected_msg="Operation requires ``get'' privilege")
227kadmin_as(some_inquire, ['getstrs', 'aliastoselected'],
228          expected_msg='key: value')
229kadmin_as(none, ['getstrs', 'none'], expected_msg='(No string attributes.)')
230realm.run([kadminl, 'delprinc', 'selected'])
231realm.run([kadminl, 'delprinc', 'unselected'])
232
233mark('modpol')
234out = kadmin_as(all_modify, ['modpol', '-maxlife', '1 hour', 'policy'],
235                expected_code=1)
236if 'Operation requires' in out:
237    fail('modpol success (acl)')
238kadmin_as(none, ['modpol', '-maxlife', '1 hour', 'policy'], expected_code=1,
239          expected_msg="Operation requires ``modify'' privilege")
240
241mark('modprinc')
242realm.addprinc('selected', 'pw')
243realm.addprinc('unselected', 'pw')
244kadmin_as(all_modify, ['modprinc', '-maxlife', '1 hour',  'unselected'])
245kadmin_as(some_modify, ['modprinc', '-maxlife', '1 hour', 'selected'])
246kadmin_as(restricted_modify, ['modprinc', '-maxlife', '1 hour', 'unselected'])
247realm.run([kadminl, 'getprinc', 'unselected'],
248          expected_msg='REQUIRES_PRE_AUTH')
249kadmin_as(all_inquire, ['modprinc', '-maxlife', '1 hour', 'selected'],
250          expected_code=1,
251          expected_msg="Operation requires ``modify'' privilege")
252kadmin_as(some_modify, ['modprinc', '-maxlife', '1 hour', 'unselected'],
253          expected_code=1, expected_msg='Operation requires')
254# Verify that the ACL check is canonicalized.
255kadmin_as(some_modify, ['modprinc', '-maxlife', '1 hour', 'aliastounselected'],
256          expected_code=1, expected_msg='Operation requires')
257kadmin_as(some_modify, ['modprinc', '-maxlife', '2 hours', 'aliastoselected'])
258realm.run([kadminl, 'delprinc', 'selected'])
259realm.run([kadminl, 'delprinc', 'unselected'])
260
261mark('purgekeys')
262realm.addprinc('selected', 'pw')
263realm.addprinc('unselected', 'pw')
264kadmin_as(all_modify, ['purgekeys', 'unselected'])
265kadmin_as(some_modify, ['purgekeys', 'selected'])
266kadmin_as(none, ['purgekeys', 'selected'], expected_code=1,
267          expected_msg="Operation requires ``modify'' privilege")
268kadmin_as(some_modify, ['purgekeys', 'unselected'], expected_code=1,
269          expected_msg="Operation requires ``modify'' privilege")
270# Verify that the ACL check is canonicalized.
271kadmin_as(some_modify, ['purgekeys', 'aliastounselected'], expected_code=1,
272          expected_msg="Operation requires ``modify'' privilege")
273kadmin_as(some_modify, ['purgekeys', 'aliastoselected'])
274kadmin_as(none, ['purgekeys', 'none'])
275realm.run([kadminl, 'delprinc', 'selected'])
276realm.run([kadminl, 'delprinc', 'unselected'])
277
278mark('renprinc')
279realm.addprinc('from', 'pw')
280kadmin_as(all_rename, ['renprinc', 'from', 'to'])
281realm.run([kadminl, 'renprinc', 'to', 'from'])
282kadmin_as(some_rename, ['renprinc', 'from', 'to'])
283realm.run([kadminl, 'renprinc', 'to', 'from'])
284kadmin_as(all_add, ['renprinc', 'from', 'to'], expected_code=1,
285          expected_msg="Insufficient authorization for operation")
286kadmin_as(all_delete, ['renprinc', 'from', 'to'], expected_code=1,
287          expected_msg="Insufficient authorization for operation")
288kadmin_as(some_rename, ['renprinc', 'from', 'notto'], expected_code=1,
289          expected_msg="Insufficient authorization for operation")
290realm.run([kadminl, 'renprinc', 'from', 'notfrom'])
291kadmin_as(some_rename, ['renprinc', 'notfrom', 'to'], expected_code=1,
292          expected_msg="Insufficient authorization for operation")
293# Verify that the ACL check isn't canonicalized.
294realm.run([kadminl, 'alias', 'aliastofrom', 'from'])
295realm.run([kadminl, 'alias', 'aliastoto', 'to'])
296kadmin_as(some_rename, ['renprinc', 'aliastofrom', 'to'], expected_code=1,
297          expected_msg="Insufficient authorization for operation")
298kadmin_as(some_rename, ['renprinc', 'from', 'aliastoto'], expected_code=1,
299          expected_msg="Insufficient authorization for operation")
300realm.run([kadminl, 'delprinc', 'aliastofrom'])
301realm.run([kadminl, 'delprinc', 'aliastoto'])
302kadmin_as(restricted_rename, ['renprinc', 'notfrom', 'to'], expected_code=1,
303          expected_msg="Insufficient authorization for operation")
304realm.run([kadminl, 'delprinc', 'notfrom'])
305
306mark('setstr')
307realm.addprinc('selected', 'pw')
308realm.addprinc('unselected', 'pw')
309kadmin_as(all_modify, ['setstr', 'unselected', 'key', 'value'])
310kadmin_as(some_modify, ['setstr', 'selected', 'key', 'value'])
311kadmin_as(none, ['setstr', 'selected',  'key', 'value'], expected_code=1,
312          expected_msg="Operation requires ``modify'' privilege")
313kadmin_as(some_modify, ['setstr', 'unselected', 'key', 'value'],
314          expected_code=1, expected_msg='Operation requires')
315# Verify that the ACL check is canonicalized.
316kadmin_as(some_modify, ['setstr', 'aliastounselected', 'key', 'value'],
317          expected_code=1, expected_msg='Operation requires')
318kadmin_as(some_modify, ['setstr', 'aliastoselected', 'key', 'value'])
319realm.run([kadminl, 'delprinc', 'selected'])
320realm.run([kadminl, 'delprinc', 'unselected'])
321
322mark('addprinc/delprinc (wildcard)')
323kadmin_as(admin, ['addprinc', '-pw', 'pw', 'anytarget'])
324realm.run([kadminl, 'delprinc', 'anytarget'])
325kadmin_as(wctarget, ['addprinc', '-pw', 'pw', 'wild/card'])
326realm.run([kadminl, 'delprinc', 'wild/card'])
327kadmin_as(wctarget, ['addprinc', '-pw', 'pw', 'wild/card/extra'],
328          expected_code=1, expected_msg='Operation requires')
329realm.addprinc('admin/user', 'pw')
330kadmin_as(admin, ['delprinc', 'admin/user'])
331kadmin_as(admin, ['delprinc', 'none'], expected_code=1,
332          expected_msg='Operation requires')
333realm.addprinc('four/one/three', 'pw')
334kadmin_as(onetwothreefour, ['delprinc', 'four/one/three'])
335
336mark('addprinc (restrictions)')
337kadmin_as(restrictions, ['addprinc', '-pw', 'pw', 'type1'])
338realm.run([kadminl, 'getprinc', 'type1'], expected_msg='Policy: minlife')
339realm.run([kadminl, 'delprinc', 'type1'])
340kadmin_as(restrictions, ['addprinc', '-pw', 'pw', '-policy', 'minlife',
341                         'type2'])
342realm.run([kadminl, 'getprinc', 'type2'], expected_msg='Policy: [none]')
343realm.run([kadminl, 'delprinc', 'type2'])
344kadmin_as(restrictions, ['addprinc', '-pw', 'pw', '-maxlife', '1 minute',
345                         'type3'])
346out = realm.run([kadminl, 'getprinc', 'type3'])
347if ('Maximum ticket life: 0 days 00:01:00' not in out or
348    'Maximum renewable life: 0 days 02:00:00' not in out):
349    fail('restriction (maxlife low, maxrenewlife unspec)')
350realm.run([kadminl, 'delprinc', 'type3'])
351kadmin_as(restrictions, ['addprinc', '-pw', 'pw', '-maxrenewlife', '1 day',
352                         'type3'])
353realm.run([kadminl, 'getprinc', 'type3'],
354          expected_msg='Maximum renewable life: 0 days 02:00:00')
355
356mark('extract')
357realm.addprinc('selected')
358realm.addprinc('unselected')
359realm.run([kadminl, 'addprinc', '-pw', 'pw', 'extractkeys'])
360msg = "Operation requires ``extract-keys'' privilege"
361kadmin_as(all_wildcard, ['ktadd', '-norandkey', 'extractkeys'],
362          expected_code=1, expected_msg=msg)
363kadmin_as(all_extract, ['ktadd', '-norandkey', 'extractkeys'])
364realm.kinit('extractkeys', flags=['-k'])
365kadmin_as(some_extract, ['ktadd', '-norandkey', 'selected'])
366kadmin_as(some_extract, ['ktadd', '-norandkey', 'unselected'], expected_code=1,
367          expected_msg=msg)
368# Verify that the ACL check is canonicalized.
369kadmin_as(some_extract, ['ktadd', '-norandkey', 'aliastounselected'],
370          expected_code=1, expected_msg=msg)
371kadmin_as(some_extract, ['ktadd', '-norandkey', 'aliastoselected'])
372os.remove(realm.keytab)
373realm.run([kadminl, 'delprinc', 'selected'])
374realm.run([kadminl, 'delprinc', 'unselected'])
375
376mark('lockdown_keys')
377kadmin_as(all_modify, ['modprinc', '+lockdown_keys', 'extractkeys'])
378kadmin_as(all_changepw, ['cpw', '-pw', 'newpw', 'extractkeys'],
379          expected_code=1,
380          expected_msg="Operation requires ``change-password'' privilege")
381kadmin_as(all_changepw, ['cpw', '-randkey', 'extractkeys'])
382kadmin_as(all_extract, ['ktadd', '-norandkey', 'extractkeys'], expected_code=1,
383          expected_msg="Operation requires ``extract-keys'' privilege")
384kadmin_as(all_delete, ['delprinc', 'extractkeys'], expected_code=1,
385          expected_msg="Operation requires ``delete'' privilege")
386kadmin_as(all_rename, ['renprinc', 'extractkeys', 'renamedprinc'],
387          expected_code=1,
388          expected_msg="Operation requires ``delete'' privilege")
389kadmin_as(all_modify, ['modprinc', '-lockdown_keys', 'extractkeys'],
390          expected_code=1,
391          expected_msg="Operation requires ``modify'' privilege")
392realm.run([kadminl, 'modprinc', '-lockdown_keys', 'extractkeys'])
393kadmin_as(all_extract, ['ktadd', '-norandkey', 'extractkeys'])
394realm.kinit('extractkeys', flags=['-k'])
395os.remove(realm.keytab)
396
397mark('alias')
398kadmin_as(all_alias, ['alias', 'aliasname', 'canon'])
399realm.run([kadminl, 'delprinc', 'aliasname'])
400kadmin_as(some_alias, ['alias', 'aliasname', 'canon'])
401realm.run([kadminl, 'delprinc', 'aliasname'])
402kadmin_as(all_add, ['alias', 'aliasname', 'canon'], expected_code=1,
403          expected_msg="Insufficient authorization for operation")
404kadmin_as(all_inquire, ['alias', 'aliasname', 'canon'], expected_code=1,
405          expected_msg="Insufficient authorization for operation")
406kadmin_as(some_alias, ['alias', 'aliasname', 'notcanon'], expected_code=1,
407          expected_msg="Insufficient authorization for operation")
408kadmin_as(some_alias, ['alias', 'notaliasname', 'canon'], expected_code=1,
409          expected_msg="Insufficient authorization for operation")
410kadmin_as(restricted_alias, ['alias', 'aliasname', 'canon'], expected_code=1,
411          expected_msg="Insufficient authorization for operation")
412
413# Verify that self-service key changes require an initial ticket.
414mark('self-service initial ticket')
415realm.run([kadminl, 'cpw', '-pw', password('none'), 'none'])
416realm.run([kadminl, 'modprinc', '+allow_tgs_req', 'kadmin/admin'])
417realm.kinit('none', password('none'))
418realm.run([kvno, 'kadmin/admin'])
419msg = 'Operation requires initial ticket'
420realm.run([kadmin, '-c', realm.ccache, 'cpw', '-pw', 'newpw', 'none'],
421          expected_code=1, expected_msg=msg)
422realm.run([kadmin, '-c', realm.ccache, 'cpw', '-pw', 'newpw',
423           '-e', 'aes256-cts', 'none'], expected_code=1, expected_msg=msg)
424realm.run([kadmin, '-c', realm.ccache, 'cpw', '-randkey', 'none'],
425          expected_code=1, expected_msg=msg)
426realm.run([kadmin, '-c', realm.ccache, 'cpw', '-randkey', '-e', 'aes256-cts',
427           'none'], expected_code=1, expected_msg=msg)
428
429# Test authentication to kadmin/hostname.
430mark('authentication to kadmin/hostname')
431kadmin_hostname = 'kadmin/' + hostname
432realm.addprinc(kadmin_hostname)
433realm.run([kadminl, 'delprinc', 'kadmin/admin'])
434msgs = ('Getting initial credentials for user/admin@KRBTEST.COM',
435        'Setting initial creds service to kadmin/admin',
436        '/Server not found in Kerberos database',
437        'Getting initial credentials for user/admin@KRBTEST.COM',
438        'Setting initial creds service to ' + kadmin_hostname,
439        'Decrypted AS reply')
440realm.run([kadmin, '-p', 'user/admin', 'listprincs'], expected_code=1,
441          expected_msg="Operation requires ``list'' privilege",
442          input=password('user/admin'), expected_trace=msgs)
443
444# Test operations disallowed at the libkadm5 layer.
445realm.run([kadminl, 'delprinc', 'K/M'],
446          expected_code=1, expected_msg='Cannot change protected principal')
447realm.run([kadminl, 'cpw', '-pw', 'pw', 'kadmin/history'],
448          expected_code=1, expected_msg='Cannot change protected principal')
449
450success('kadmin ACL enforcement')
451