Fix autoconf 2.70 compatibility
[platform/upstream/krb5.git] / src / tests / t_kdb.py
1 from k5test import *
2 import time
3
4 # Run kdbtest against the non-LDAP KDB modules.
5 for realm in multidb_realms(create_kdb=False):
6     realm.run(['./kdbtest'])
7
8 # Set up an OpenLDAP test server if we can.
9
10 if (not os.path.exists(os.path.join(plugins, 'kdb', 'kldap.so')) and
11     not os.path.exists(os.path.join(buildtop, 'lib', 'libkdb_ldap.a'))):
12     skip_rest('LDAP KDB tests', 'LDAP KDB module not built')
13
14 if 'SLAPD' not in os.environ and not which('slapd'):
15     skip_rest('LDAP KDB tests', 'slapd not found')
16
17 slapadd = which('slapadd')
18 if not slapadd:
19     skip_rest('LDAP KDB tests', 'slapadd not found')
20
21 ldapdir = os.path.abspath('ldap')
22 dbdir = os.path.join(ldapdir, 'ldap')
23 slapd_conf = os.path.join(ldapdir, 'slapd.d')
24 slapd_out = os.path.join(ldapdir, 'slapd.out')
25 slapd_pidfile = os.path.join(ldapdir, 'pid')
26 ldap_pwfile = os.path.join(ldapdir, 'pw')
27 ldap_sock = os.path.join(ldapdir, 'sock')
28 ldap_uri = 'ldapi://%s/' % ldap_sock.replace(os.path.sep, '%2F')
29 schema = os.path.join(srctop, 'plugins', 'kdb', 'ldap', 'libkdb_ldap',
30                       'kerberos.openldap.ldif')
31 top_dn = 'cn=krb5'
32 admin_dn = 'cn=admin,cn=krb5'
33 admin_pw = 'admin'
34
35 shutil.rmtree(ldapdir, True)
36 os.mkdir(ldapdir)
37 os.mkdir(slapd_conf)
38 os.mkdir(dbdir)
39
40 if 'SLAPD' in os.environ:
41     slapd = os.environ['SLAPD']
42 else:
43     # Some Linux installations have AppArmor or similar restrictions
44     # on the slapd binary, which would prevent it from accessing the
45     # build directory.  Try to defeat this by copying the binary.
46     system_slapd = which('slapd')
47     slapd = os.path.join(ldapdir, 'slapd')
48     shutil.copy(system_slapd, slapd)
49
50 def slap_add(ldif):
51     proc = subprocess.Popen([slapadd, '-b', 'cn=config', '-F', slapd_conf],
52                             stdin=subprocess.PIPE, stdout=subprocess.PIPE,
53                             stderr=subprocess.STDOUT, universal_newlines=True)
54     (out, dummy) = proc.communicate(ldif)
55     output(out)
56     return proc.wait()
57
58
59 # Configure the pid file and some authorization rules we will need for
60 # SASL testing.
61 if slap_add('dn: cn=config\n'
62             'objectClass: olcGlobal\n'
63             'olcPidFile: %s\n'
64             'olcAuthzRegexp: '
65             '".*uidNumber=%d,cn=peercred,cn=external,cn=auth" "%s"\n'
66             'olcAuthzRegexp: "uid=digestuser,cn=digest-md5,cn=auth" "%s"\n' %
67             (slapd_pidfile, os.geteuid(), admin_dn, admin_dn)) != 0:
68     skip_rest('LDAP KDB tests', 'slapd basic configuration failed')
69
70 # Find a working writable database type, trying mdb (added in OpenLDAP
71 # 2.4.27) and bdb (deprecated and sometimes not built due to licensing
72 # incompatibilities).
73 for dbtype in ('mdb', 'bdb'):
74     # Try to load the module.  This could fail if OpenLDAP is built
75     # without module support, so ignore errors.
76     slap_add('dn: cn=module,cn=config\n'
77              'objectClass: olcModuleList\n'
78              'olcModuleLoad: back_%s\n' % dbtype)
79
80     dbclass = 'olc%sConfig' % dbtype.capitalize()
81     if slap_add('dn: olcDatabase=%s,cn=config\n'
82                 'objectClass: olcDatabaseConfig\n'
83                 'objectClass: %s\n'
84                 'olcSuffix: %s\n'
85                 'olcRootDN: %s\n'
86                 'olcRootPW: %s\n'
87                 'olcDbDirectory: %s\n' %
88                 (dbtype, dbclass, top_dn, admin_dn, admin_pw, dbdir)) == 0:
89         break
90 else:
91     skip_rest('LDAP KDB tests', 'could not find working slapd db type')
92
93 if slap_add('include: file://%s\n' % schema) != 0:
94     skip_rest('LDAP KDB tests', 'failed to load Kerberos schema')
95
96 # Load the core schema if we can.
97 ldap_homes = ['/etc/ldap', '/etc/openldap', '/usr/local/etc/openldap',
98               '/usr/local/etc/ldap']
99 local_schema_path = '/schema/core.ldif'
100 core_schema = next((i for i in map(lambda x:x+local_schema_path, ldap_homes)
101                     if os.path.isfile(i)), None)
102 if core_schema:
103     if slap_add('include: file://%s\n' % core_schema) != 0:
104         core_schema = None
105
106 slapd_pid = -1
107 def kill_slapd():
108     global slapd_pid
109     if slapd_pid != -1:
110         os.kill(slapd_pid, signal.SIGTERM)
111         slapd_pid = -1
112 atexit.register(kill_slapd)
113
114 out = open(slapd_out, 'w')
115 subprocess.call([slapd, '-h', ldap_uri, '-F', slapd_conf], stdout=out,
116                 stderr=out, universal_newlines=True)
117 out.close()
118 pidf = open(slapd_pidfile, 'r')
119 slapd_pid = int(pidf.read())
120 pidf.close()
121 output('*** Started slapd (pid %d, output in %s)\n' % (slapd_pid, slapd_out))
122
123 # slapd detaches before it finishes setting up its listener sockets
124 # (they are bound but listen() has not been called).  Give it a second
125 # to finish.
126 time.sleep(1)
127
128 # Run kdbtest against the LDAP module.
129 conf = {'realms': {'$realm': {'database_module': 'ldap'}},
130         'dbmodules': {'ldap': {'db_library': 'kldap',
131                                'ldap_kerberos_container_dn': top_dn,
132                                'ldap_kdc_dn': admin_dn,
133                                'ldap_kadmind_dn': admin_dn,
134                                'ldap_service_password_file': ldap_pwfile,
135                                'ldap_servers': ldap_uri}}}
136 realm = K5Realm(create_kdb=False, kdc_conf=conf)
137 input = admin_pw + '\n' + admin_pw + '\n'
138 realm.run([kdb5_ldap_util, 'stashsrvpw', admin_dn], input=input)
139 realm.run(['./kdbtest'])
140
141 # Run a kdb5_ldap_util command using the test server's admin DN and password.
142 def kldaputil(args, **kw):
143     return realm.run([kdb5_ldap_util, '-D', admin_dn, '-w', admin_pw] + args,
144                      **kw)
145
146 # kdbtest can't currently clean up after itself since the LDAP module
147 # doesn't support krb5_db_destroy.  So clean up after it with
148 # kdb5_ldap_util before proceeding.
149 kldaputil(['destroy', '-f'])
150
151 ldapmodify = which('ldapmodify')
152 ldapsearch = which('ldapsearch')
153 if not ldapmodify or not ldapsearch:
154     skip_rest('some LDAP KDB tests', 'ldapmodify or ldapsearch not found')
155
156 def ldap_search(args):
157     proc = subprocess.Popen([ldapsearch, '-H', ldap_uri, '-b', top_dn,
158                              '-D', admin_dn, '-w', admin_pw, args],
159                             stdin=subprocess.PIPE, stdout=subprocess.PIPE,
160                             stderr=subprocess.STDOUT, universal_newlines=True)
161     (out, dummy) = proc.communicate()
162     return out
163
164 def ldap_modify(ldif, args=[]):
165     proc = subprocess.Popen([ldapmodify, '-H', ldap_uri, '-D', admin_dn,
166                              '-x', '-w', admin_pw] + args,
167                             stdin=subprocess.PIPE, stdout=subprocess.PIPE,
168                             stderr=subprocess.STDOUT, universal_newlines=True)
169     (out, dummy) = proc.communicate(ldif)
170     output(out)
171
172 def ldap_add(dn, objectclass, attrs=[]):
173     in_data = 'dn: %s\nobjectclass: %s\n' % (dn, objectclass)
174     in_data += '\n'.join(attrs) + '\n'
175     ldap_modify(in_data, ['-a'])
176
177 # Create krbContainer objects for use as subtrees.
178 ldap_add('cn=t1,cn=krb5', 'krbContainer')
179 ldap_add('cn=t2,cn=krb5', 'krbContainer')
180 ldap_add('cn=x,cn=t1,cn=krb5', 'krbContainer')
181 ldap_add('cn=y,cn=t2,cn=krb5', 'krbContainer')
182
183 # Create a realm, exercising all of the realm options.
184 kldaputil(['create', '-s', '-P', 'master', '-subtrees', 'cn=t2,cn=krb5',
185            '-containerref', 'cn=t2,cn=krb5', '-sscope', 'one',
186            '-maxtktlife', '5min', '-maxrenewlife', '10min', '-allow_svr'])
187
188 # Modify the realm, exercising overlapping subtree pruning.
189 kldaputil(['modify', '-subtrees',
190            'cn=x,cn=t1,cn=krb5:cn=t1,cn=krb5:cn=t2,cn=krb5:cn=y,cn=t2,cn=krb5',
191            '-containerref', 'cn=t1,cn=krb5', '-sscope', 'sub',
192            '-maxtktlife', '5hour', '-maxrenewlife', '10hour', '+allow_svr'])
193
194 out = kldaputil(['list'])
195 if out != 'KRBTEST.COM\n':
196     fail('Unexpected kdb5_ldap_util list output')
197
198 # Create a principal at a specified DN.  This is a little dodgy
199 # because we're sticking a krbPrincipalAux objectclass onto a subtree
200 # krbContainer, but it works and it avoids having to load core.schema
201 # in the test LDAP server.
202 mark('LDAP specified dn')
203 realm.run([kadminl, 'ank', '-randkey', '-x', 'dn=cn=krb5', 'princ1'],
204           expected_code=1, expected_msg='DN is out of the realm subtree')
205 # Check that the DN container check is a hierarchy test, not a simple
206 # suffix match (CVE-2018-5730).  We expect this operation to fail
207 # either way (because "xcn" isn't a valid DN tag) but the container
208 # check should happen before the DN is parsed.
209 realm.run([kadminl, 'ank', '-randkey', '-x', 'dn=xcn=t1,cn=krb5', 'princ1'],
210           expected_code=1, expected_msg='DN is out of the realm subtree')
211 realm.run([kadminl, 'ank', '-randkey', '-x', 'dn=cn=t2,cn=krb5', 'princ1'])
212 realm.run([kadminl, 'getprinc', 'princ1'], expected_msg='Principal: princ1')
213 realm.run([kadminl, 'ank', '-randkey', '-x', 'dn=cn=t2,cn=krb5', 'again'],
214           expected_code=1, expected_msg='ldap object is already kerberized')
215 # Check that we can't set linkdn on a non-standalone object.
216 realm.run([kadminl, 'modprinc', '-x', 'linkdn=cn=t1,cn=krb5', 'princ1'],
217           expected_code=1, expected_msg='link information can not be set')
218
219 # Create a principal with a specified linkdn.
220 mark('LDAP specified linkdn')
221 realm.run([kadminl, 'ank', '-randkey', '-x', 'linkdn=cn=krb5', 'princ2'],
222           expected_code=1, expected_msg='DN is out of the realm subtree')
223 realm.run([kadminl, 'ank', '-randkey', '-x', 'linkdn=cn=t1,cn=krb5', 'princ2'])
224 # Check that we can't reset linkdn.
225 realm.run([kadminl, 'modprinc', '-x', 'linkdn=cn=t2,cn=krb5', 'princ2'],
226           expected_code=1, expected_msg='kerberos principal is already linked')
227
228 # Create a principal with a specified containerdn.
229 mark('LDAP specified containerdn')
230 realm.run([kadminl, 'ank', '-randkey', '-x', 'containerdn=cn=krb5', 'princ3'],
231           expected_code=1, expected_msg='DN is out of the realm subtree')
232 realm.run([kadminl, 'ank', '-randkey', '-x', 'containerdn=cn=t1,cn=krb5',
233            'princ3'])
234 realm.run([kadminl, 'modprinc', '-x', 'containerdn=cn=t2,cn=krb5', 'princ3'],
235           expected_code=1, expected_msg='containerdn option not supported')
236 # Verify that containerdn is checked when linkdn is also supplied
237 # (CVE-2018-5730).
238 realm.run([kadminl, 'ank', '-randkey', '-x', 'containerdn=cn=krb5',
239            '-x', 'linkdn=cn=t2,cn=krb5', 'princ4'], expected_code=1,
240           expected_msg='DN is out of the realm subtree')
241
242 mark('LDAP ticket policy')
243
244 # Create and modify a ticket policy.
245 kldaputil(['create_policy', '-maxtktlife', '3hour', '-maxrenewlife', '6hour',
246            '-allow_forwardable', 'tktpol'])
247 kldaputil(['modify_policy', '-maxtktlife', '4hour', '-maxrenewlife', '8hour',
248            '+requires_preauth', 'tktpol'])
249 out = kldaputil(['view_policy', 'tktpol'])
250 if ('Ticket policy: tktpol\n' not in out or
251     'Maximum ticket life: 0 days 04:00:00\n' not in out or
252     'Maximum renewable life: 0 days 08:00:00\n' not in out or
253     'Ticket flags: DISALLOW_FORWARDABLE REQUIRES_PRE_AUTH' not in out):
254     fail('Unexpected kdb5_ldap_util view_policy output')
255
256 out = kldaputil(['list_policy'])
257 if out != 'tktpol\n':
258     fail('Unexpected kdb5_ldap_util list_policy output')
259
260 # Associate the ticket policy to a principal.
261 realm.run([kadminl, 'ank', '-randkey', '-x', 'tktpolicy=tktpol', 'princ4'])
262 out = realm.run([kadminl, 'getprinc', 'princ4'])
263 if ('Maximum ticket life: 0 days 04:00:00\n' not in out or
264     'Maximum renewable life: 0 days 08:00:00\n' not in out or
265     'Attributes: DISALLOW_FORWARDABLE REQUIRES_PRE_AUTH\n' not in out):
266     fail('Unexpected getprinc output with ticket policy')
267
268 # Destroying the policy should fail while a principal references it.
269 kldaputil(['destroy_policy', '-force', 'tktpol'], expected_code=1)
270
271 # Dissociate the ticket policy from the principal.
272 realm.run([kadminl, 'modprinc', '-x', 'tktpolicy=', 'princ4'])
273 out = realm.run([kadminl, 'getprinc', 'princ4'])
274 if ('Maximum ticket life: 0 days 05:00:00\n' not in out or
275     'Maximum renewable life: 0 days 10:00:00\n' not in out or
276     'Attributes:\n' not in out):
277     fail('Unexpected getprinc output without ticket policy')
278
279 # Destroy the ticket policy.
280 kldaputil(['destroy_policy', '-force', 'tktpol'])
281 kldaputil(['view_policy', 'tktpol'], expected_code=1)
282 out = kldaputil(['list_policy'])
283 if out:
284     fail('Unexpected kdb5_ldap_util list_policy output after destroy')
285
286 # Create another ticket policy to be destroyed with the realm.
287 kldaputil(['create_policy', 'tktpol2'])
288
289 # Try to create a password policy conflicting with a ticket policy.
290 realm.run([kadminl, 'addpol', 'tktpol2'], expected_code=1,
291           expected_msg='Already exists while creating policy "tktpol2"')
292
293 # Try to create a ticket policy conflicting with a password policy.
294 realm.run([kadminl, 'addpol', 'pwpol'])
295 out = kldaputil(['create_policy', 'pwpol'], expected_code=1)
296 if 'Already exists while creating policy object' not in out:
297     fail('Expected error not seen in kdb5_ldap_util output')
298
299 # Try to use a password policy as a ticket policy.
300 realm.run([kadminl, 'modprinc', '-x', 'tktpolicy=pwpol', 'princ4'],
301           expected_code=1, expected_msg='Object class violation')
302
303 # Use a ticket policy as a password policy (CVE-2014-5353).  This
304 # works with a warning; use kadmin.local -q so the warning is shown.
305 realm.run([kadminl, '-q', 'modprinc -policy tktpol2 princ4'],
306           expected_msg='WARNING: policy "tktpol2" does not exist')
307
308 # Do some basic tests with a KDC against the LDAP module, exercising the
309 # db_args processing code.
310 mark('LDAP KDC operation')
311 realm.start_kdc(['-x', 'nconns=3', '-x', 'host=' + ldap_uri,
312                  '-x', 'binddn=' + admin_dn, '-x', 'bindpwd=' + admin_pw])
313 realm.addprinc(realm.user_princ, password('user'))
314 realm.addprinc(realm.host_princ)
315 realm.extract_keytab(realm.host_princ, realm.keytab)
316 realm.kinit(realm.user_princ, password('user'))
317 realm.run([kvno, realm.host_princ])
318 realm.klist(realm.user_princ, realm.host_princ)
319
320 mark('LDAP auth indicator')
321
322 # Test auth indicator support
323 realm.addprinc('authind', password('authind'))
324 realm.run([kadminl, 'setstr', 'authind', 'require_auth', 'otp radius'])
325
326 out = ldap_search('(krbPrincipalName=authind*)')
327 if 'krbPrincipalAuthInd: otp' not in out:
328     fail('Expected krbPrincipalAuthInd value not in output')
329 if 'krbPrincipalAuthInd: radius' not in out:
330     fail('Expected krbPrincipalAuthInd value not in output')
331
332 realm.run([kadminl, 'getstrs', 'authind'],
333           expected_msg='require_auth: otp radius')
334
335 mark('LDAP service principal aliases')
336
337 # Test service principal aliases.
338 realm.addprinc('canon', password('canon'))
339 ldap_modify('dn: krbPrincipalName=canon@KRBTEST.COM,cn=t1,cn=krb5\n'
340             'changetype: modify\n'
341             'add: krbPrincipalName\n'
342             'krbPrincipalName: alias@KRBTEST.COM\n'
343             '-\n'
344             'add: krbCanonicalName\n'
345             'krbCanonicalName: canon@KRBTEST.COM\n')
346 realm.run([kadminl, 'getprinc', 'alias'],
347           expected_msg='Principal: canon@KRBTEST.COM\n')
348 realm.run([kadminl, 'getprinc', 'canon'],
349           expected_msg='Principal: canon@KRBTEST.COM\n')
350 realm.run([kvno, 'alias', 'canon'])
351 out = realm.run([klist])
352 if 'alias@KRBTEST.COM\n' not in out or 'canon@KRBTEST.COM' not in out:
353     fail('After fetching alias and canon, klist is missing one or both')
354 realm.kinit(realm.user_princ, password('user'), ['-S', 'alias'])
355 realm.klist(realm.user_princ, 'alias@KRBTEST.COM')
356
357 # Make sure an alias to the local TGS is still treated like an alias.
358 ldap_modify('dn: krbPrincipalName=krbtgt/KRBTEST.COM@KRBTEST.COM,'
359             'cn=KRBTEST.COM,cn=krb5\n'
360             'changetype: modify\n'
361             'add:krbPrincipalName\n'
362             'krbPrincipalName: tgtalias@KRBTEST.COM\n'
363             '-\n'
364             'add: krbCanonicalName\n'
365             'krbCanonicalName: krbtgt/KRBTEST.COM@KRBTEST.COM\n')
366 realm.run([kadminl, 'getprinc', 'tgtalias'],
367           expected_msg='Principal: krbtgt/KRBTEST.COM@KRBTEST.COM')
368 realm.kinit(realm.user_princ, password('user'))
369 realm.run([kvno, 'tgtalias'])
370 realm.klist(realm.user_princ, 'tgtalias@KRBTEST.COM')
371
372 # Make sure aliases work in header tickets.
373 realm.run([kadminl, 'modprinc', '-maxrenewlife', '3 hours', 'user'])
374 realm.run([kadminl, 'modprinc', '-maxrenewlife', '3 hours',
375            'krbtgt/KRBTEST.COM'])
376 realm.kinit(realm.user_princ, password('user'), ['-l', '1h', '-r', '2h'])
377 realm.run([kvno, 'alias'])
378 realm.kinit(realm.user_princ, flags=['-R', '-S', 'alias'])
379 realm.klist(realm.user_princ, 'alias@KRBTEST.COM')
380
381 # Test client principal aliases, with and without preauth.
382 realm.kinit('canon', password('canon'))
383 realm.kinit('alias', password('canon'), expected_code=1,
384             expected_msg='not found in Kerberos database')
385 realm.kinit('alias', password('canon'), ['-C'])
386 realm.run([kvno, 'alias'])
387 realm.klist('canon@KRBTEST.COM', 'alias@KRBTEST.COM')
388 realm.run([kadminl, 'modprinc', '+requires_preauth', 'canon'])
389 realm.kinit('canon', password('canon'))
390 realm.kinit('alias', password('canon'), ['-C'])
391
392 mark('LDAP password history')
393
394 # Test password history.
395 def test_pwhist(nhist):
396     def cpw(n, **kwargs):
397         realm.run([kadminl, 'cpw', '-pw', str(n), princ], **kwargs)
398     def cpw_fail(n):
399         cpw(n, expected_code=1)
400     output('*** Testing password history of size %d\n' % nhist)
401     princ = 'pwhistprinc' + str(nhist)
402     pol = 'pwhistpol' + str(nhist)
403     realm.run([kadminl, 'addpol', '-history', str(nhist), pol])
404     realm.run([kadminl, 'addprinc', '-policy', pol, '-nokey', princ])
405     for i in range(nhist):
406         # Set a password, then check that all previous passwords fail.
407         cpw(i)
408         for j in range(i + 1):
409             cpw_fail(j)
410     # Set one more new password, and make sure the oldest key is
411     # rotated out.
412     cpw(nhist)
413     cpw_fail(1)
414     cpw(0)
415
416 for n in (1, 2, 3, 4, 5):
417     test_pwhist(n)
418
419 # Regression test for #8193: test password character class requirements.
420 princ = 'charclassprinc'
421 pol = 'charclasspol'
422 realm.run([kadminl, 'addpol', '-minclasses', '3', pol])
423 realm.run([kadminl, 'addprinc', '-policy', pol, '-nokey', princ])
424 realm.run([kadminl, 'cpw', '-pw', 'abcdef', princ], expected_code=1)
425 realm.run([kadminl, 'cpw', '-pw', 'Abcdef', princ], expected_code=1)
426 realm.run([kadminl, 'cpw', '-pw', 'Abcdef1', princ])
427
428 # Test principal renaming and make sure last modified is changed
429 def get_princ(princ):
430     out = realm.run([kadminl, 'getprinc', princ])
431     return dict(map(str.strip, x.split(":", 1)) for x in out.splitlines())
432
433 mark('LDAP principal renaming')
434 realm.addprinc("rename", password('rename'))
435 renameprinc = get_princ("rename")
436 realm.run([kadminl, '-p', 'fake@KRBTEST.COM', 'renprinc', 'rename', 'renamed'])
437 renamedprinc = get_princ("renamed")
438 if renameprinc['Last modified'] == renamedprinc['Last modified']:
439     fail('Last modified data not updated when principal was renamed')
440
441 # Regression test for #7980 (fencepost when dividing keys up by kvno).
442 mark('#7980 regression test')
443 realm.run([kadminl, 'addprinc', '-randkey', '-e', 'aes256-cts,aes128-cts',
444            'kvnoprinc'])
445 realm.run([kadminl, 'cpw', '-randkey', '-keepold', '-e',
446            'aes256-cts,aes128-cts', 'kvnoprinc'])
447 realm.run([kadminl, 'getprinc', 'kvnoprinc'], expected_msg='Number of keys: 4')
448 realm.run([kadminl, 'cpw', '-randkey', '-keepold', '-e',
449            'aes256-cts,aes128-cts', 'kvnoprinc'])
450 realm.run([kadminl, 'getprinc', 'kvnoprinc'], expected_msg='Number of keys: 6')
451
452 # Regression test for #8041 (NULL dereference on keyless principals).
453 mark('#8041 regression test')
454 realm.run([kadminl, 'addprinc', '-nokey', 'keylessprinc'])
455 realm.run([kadminl, 'getprinc', 'keylessprinc'],
456           expected_msg='Number of keys: 0')
457 realm.run([kadminl, 'cpw', '-randkey', '-e', 'aes256-cts,aes128-cts',
458            'keylessprinc'])
459 realm.run([kadminl, 'cpw', '-randkey', '-keepold', '-e',
460            'aes256-cts,aes128-cts', 'keylessprinc'])
461 realm.run([kadminl, 'getprinc', 'keylessprinc'],
462           expected_msg='Number of keys: 4')
463 realm.run([kadminl, 'purgekeys', '-all', 'keylessprinc'])
464 realm.run([kadminl, 'getprinc', 'keylessprinc'],
465           expected_msg='Number of keys: 0')
466
467 # Test for 8354 (old password history entries when -keepold is used)
468 mark('#8354 regression test')
469 realm.run([kadminl, 'addpol', '-history', '2', 'keepoldpasspol'])
470 realm.run([kadminl, 'addprinc', '-policy', 'keepoldpasspol', '-pw', 'aaaa',
471            'keepoldpassprinc'])
472 for p in ('bbbb', 'cccc', 'aaaa'):
473     realm.run([kadminl, 'cpw', '-keepold', '-pw', p, 'keepoldpassprinc'])
474
475 if runenv.sizeof_time_t <= 4:
476     skipped('y2038 LDAP test', 'platform has 32-bit time_t')
477 else:
478     # Test storage of timestamps after y2038.
479     realm.run([kadminl, 'modprinc', '-pwexpire', '2040-02-03', 'user'])
480     realm.run([kadminl, 'getprinc', 'user'], expected_msg=' 2040\n')
481
482 realm.stop()
483
484 # Briefly test dump and load.
485 mark('LDAP dump and load')
486 dumpfile = os.path.join(realm.testdir, 'dump')
487 realm.run([kdb5_util, 'dump', dumpfile])
488 realm.run([kdb5_util, 'load', dumpfile], expected_code=1,
489           expected_msg='KDB module requires -update argument')
490 realm.run([kdb5_util, 'load', '-update', dumpfile])
491
492 # Destroy the realm.
493 kldaputil(['destroy', '-f'])
494 out = kldaputil(['list'])
495 if out:
496     fail('Unexpected kdb5_ldap_util list output after destroy')
497
498 if not core_schema:
499     skip_rest('LDAP SASL tests', 'core schema not found')
500
501 if runenv.have_sasl != 'yes':
502     skip_rest('LDAP SASL tests', 'SASL support not built')
503
504 # Test SASL EXTERNAL auth.  Remove the DNs and service password file
505 # from the DB module config.
506 mark('LDAP SASL EXTERNAL auth')
507 os.remove(ldap_pwfile)
508 dbmod = conf['dbmodules']['ldap']
509 dbmod['ldap_kdc_sasl_mech'] = dbmod['ldap_kadmind_sasl_mech'] = 'EXTERNAL'
510 del dbmod['ldap_service_password_file']
511 del dbmod['ldap_kdc_dn'], dbmod['ldap_kadmind_dn']
512 realm = K5Realm(create_kdb=False, kdc_conf=conf)
513 realm.run([kdb5_ldap_util, 'create', '-s', '-P', 'master'])
514 realm.start_kdc()
515 realm.addprinc(realm.user_princ, password('user'))
516 realm.kinit(realm.user_princ, password('user'))
517 realm.stop()
518 realm.run([kdb5_ldap_util, 'destroy', '-f'])
519
520 # Test SASL DIGEST-MD5 auth.  We need to set a clear-text password for
521 # the admin DN, so create a person entry (requires the core schema).
522 # Restore the service password file in the config and set authcids.
523 mark('LDAP SASL DIGEST-MD5 auth')
524 ldap_add('cn=admin,cn=krb5', 'person',
525          ['sn: dummy', 'userPassword: admin'])
526 dbmod['ldap_kdc_sasl_mech'] = dbmod['ldap_kadmind_sasl_mech'] = 'DIGEST-MD5'
527 dbmod['ldap_kdc_sasl_authcid'] = 'digestuser'
528 dbmod['ldap_kadmind_sasl_authcid'] = 'digestuser'
529 dbmod['ldap_service_password_file'] = ldap_pwfile
530 realm = K5Realm(create_kdb=False, kdc_conf=conf)
531 input = admin_pw + '\n' + admin_pw + '\n'
532 realm.run([kdb5_ldap_util, 'stashsrvpw', 'digestuser'], input=input)
533 realm.run([kdb5_ldap_util, 'create', '-s', '-P', 'master'])
534 realm.start_kdc()
535 realm.addprinc(realm.user_princ, password('user'))
536 realm.kinit(realm.user_princ, password('user'))
537 realm.stop()
538 # Exercise DB options, which should cause binding to fail.
539 realm.run([kadminl, '-x', 'sasl_authcid=ab', 'getprinc', 'user'],
540           expected_code=1, expected_msg='Cannot bind to LDAP server')
541 realm.run([kadminl, '-x', 'bindpwd=wrong', 'getprinc', 'user'],
542           expected_code=1, expected_msg='Cannot bind to LDAP server')
543 realm.run([kdb5_ldap_util, 'destroy', '-f'])
544
545 # We could still use tests to exercise:
546 # * DB arg handling in krb5_ldap_create
547 # * krbAllowedToDelegateTo attribute processing
548 # * A load operation overwriting a standalone principal entry which
549 #   already exists but doesn't have a krbPrincipalName attribute
550 #   matching the principal name.
551 # * A bunch of invalid-input error conditions
552 #
553 # There is no coverage for the following because it would be difficult:
554 # * Out-of-memory error conditions
555 # * Handling of failures from slapd (including krb5_retry_get_ldap_handle)
556 # * Handling of servers which don't support mod-increment
557 # * krb5_ldap_delete_krbcontainer (only happens if krb5_ldap_create fails)
558
559 success('LDAP and DB2 KDB tests')