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