1from k5test import * 2import time 3 4# Run kdbtest against the non-LDAP KDB modules. 5for realm in multidb_realms(create_kdb=False): 6 realm.run(['./kdbtest']) 7 8# Set up an OpenLDAP test server if we can. 9 10if (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 14if 'SLAPD' not in os.environ and not which('slapd'): 15 skip_rest('LDAP KDB tests', 'slapd not found') 16 17slapadd = which('slapadd') 18if not slapadd: 19 skip_rest('LDAP KDB tests', 'slapadd not found') 20 21ldapdir = os.path.abspath('ldap') 22dbdir = os.path.join(ldapdir, 'ldap') 23slapd_conf = os.path.join(ldapdir, 'slapd.d') 24slapd_out = os.path.join(ldapdir, 'slapd.out') 25slapd_pidfile = os.path.join(ldapdir, 'pid') 26ldap_pwfile = os.path.join(ldapdir, 'pw') 27ldap_sock = os.path.join(ldapdir, 'sock') 28ldap_uri = 'ldapi://%s/' % ldap_sock.replace(os.path.sep, '%2F') 29schema = os.path.join(srctop, 'plugins', 'kdb', 'ldap', 'libkdb_ldap', 30 'kerberos.openldap.ldif') 31top_dn = 'cn=krb5' 32admin_dn = 'cn=admin,cn=krb5' 33admin_pw = 'admin' 34 35shutil.rmtree(ldapdir, True) 36os.mkdir(ldapdir) 37os.mkdir(slapd_conf) 38os.mkdir(dbdir) 39 40if 'SLAPD' in os.environ: 41 slapd = os.environ['SLAPD'] 42else: 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 50def 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. 61if 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). 73for 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 90else: 91 skip_rest('LDAP KDB tests', 'could not find working slapd db type') 92 93if 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. 97ldap_homes = ['/etc/ldap', '/etc/openldap', '/usr/local/etc/openldap', 98 '/usr/local/etc/ldap'] 99local_schema_path = '/schema/core.ldif' 100core_schema = next((i for i in map(lambda x:x+local_schema_path, ldap_homes) 101 if os.path.isfile(i)), None) 102if core_schema: 103 if slap_add('include: file://%s\n' % core_schema) != 0: 104 core_schema = None 105 106slapd_pid = -1 107def kill_slapd(): 108 global slapd_pid 109 if slapd_pid != -1: 110 os.kill(slapd_pid, signal.SIGTERM) 111 slapd_pid = -1 112atexit.register(kill_slapd) 113 114out = open(slapd_out, 'w') 115subprocess.call([slapd, '-h', ldap_uri, '-F', slapd_conf], stdout=out, 116 stderr=out, universal_newlines=True) 117out.close() 118pidf = open(slapd_pidfile, 'r') 119slapd_pid = int(pidf.read()) 120pidf.close() 121output('*** 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. 126time.sleep(1) 127 128# Run kdbtest against the LDAP module. 129conf = {'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}}} 136realm = K5Realm(create_kdb=False, kdc_conf=conf) 137input = admin_pw + '\n' + admin_pw + '\n' 138realm.run([kdb5_ldap_util, 'stashsrvpw', admin_dn], input=input) 139realm.run(['./kdbtest']) 140 141# Run a kdb5_ldap_util command using the test server's admin DN and password. 142def 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. 149kldaputil(['destroy', '-f']) 150 151ldapmodify = which('ldapmodify') 152ldapsearch = which('ldapsearch') 153if not ldapmodify or not ldapsearch: 154 skip_rest('some LDAP KDB tests', 'ldapmodify or ldapsearch not found') 155 156def 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 164def 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 172def 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. 178ldap_add('cn=t1,cn=krb5', 'krbContainer') 179ldap_add('cn=t2,cn=krb5', 'krbContainer') 180ldap_add('cn=x,cn=t1,cn=krb5', 'krbContainer') 181ldap_add('cn=y,cn=t2,cn=krb5', 'krbContainer') 182 183# Create a realm, exercising all of the realm options. 184kldaputil(['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. 189kldaputil(['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 194out = kldaputil(['list']) 195if 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. 202mark('LDAP specified dn') 203realm.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. 209realm.run([kadminl, 'ank', '-randkey', '-x', 'dn=xcn=t1,cn=krb5', 'princ1'], 210 expected_code=1, expected_msg='DN is out of the realm subtree') 211realm.run([kadminl, 'ank', '-randkey', '-x', 'dn=cn=t2,cn=krb5', 'princ1']) 212realm.run([kadminl, 'getprinc', 'princ1'], expected_msg='Principal: princ1') 213realm.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. 216realm.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. 220mark('LDAP specified linkdn') 221realm.run([kadminl, 'ank', '-randkey', '-x', 'linkdn=cn=krb5', 'princ2'], 222 expected_code=1, expected_msg='DN is out of the realm subtree') 223realm.run([kadminl, 'ank', '-randkey', '-x', 'linkdn=cn=t1,cn=krb5', 'princ2']) 224# Check that we can't reset linkdn. 225realm.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. 229mark('LDAP specified containerdn') 230realm.run([kadminl, 'ank', '-randkey', '-x', 'containerdn=cn=krb5', 'princ3'], 231 expected_code=1, expected_msg='DN is out of the realm subtree') 232realm.run([kadminl, 'ank', '-randkey', '-x', 'containerdn=cn=t1,cn=krb5', 233 'princ3']) 234realm.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). 238realm.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 242mark('LDAP ticket policy') 243 244# Create and modify a ticket policy. 245kldaputil(['create_policy', '-maxtktlife', '3hour', '-maxrenewlife', '6hour', 246 '-allow_forwardable', 'tktpol']) 247kldaputil(['modify_policy', '-maxtktlife', '4hour', '-maxrenewlife', '8hour', 248 '+requires_preauth', 'tktpol']) 249out = kldaputil(['view_policy', 'tktpol']) 250if ('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 256out = kldaputil(['list_policy']) 257if out != 'tktpol\n': 258 fail('Unexpected kdb5_ldap_util list_policy output') 259 260# Associate the ticket policy to a principal. 261realm.run([kadminl, 'ank', '-randkey', '-x', 'tktpolicy=tktpol', 'princ4']) 262out = realm.run([kadminl, 'getprinc', 'princ4']) 263if ('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. 269kldaputil(['destroy_policy', '-force', 'tktpol'], expected_code=1) 270 271# Dissociate the ticket policy from the principal. 272realm.run([kadminl, 'modprinc', '-x', 'tktpolicy=', 'princ4']) 273out = realm.run([kadminl, 'getprinc', 'princ4']) 274if ('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. 280kldaputil(['destroy_policy', '-force', 'tktpol']) 281kldaputil(['view_policy', 'tktpol'], expected_code=1) 282out = kldaputil(['list_policy']) 283if out: 284 fail('Unexpected kdb5_ldap_util list_policy output after destroy') 285 286# Create another ticket policy to be destroyed with the realm. 287kldaputil(['create_policy', 'tktpol2']) 288 289# Try to create a password policy conflicting with a ticket policy. 290realm.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. 294realm.run([kadminl, 'addpol', 'pwpol']) 295out = kldaputil(['create_policy', 'pwpol'], expected_code=1) 296if '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. 300realm.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. 305realm.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. 310mark('LDAP KDC operation') 311realm.start_kdc(['-x', 'nconns=3', '-x', 'host=' + ldap_uri, 312 '-x', 'binddn=' + admin_dn, '-x', 'bindpwd=' + admin_pw]) 313realm.addprinc(realm.user_princ, password('user')) 314realm.addprinc(realm.host_princ) 315realm.extract_keytab(realm.host_princ, realm.keytab) 316realm.kinit(realm.user_princ, password('user')) 317realm.run([kvno, realm.host_princ]) 318realm.klist(realm.user_princ, realm.host_princ) 319 320mark('LDAP auth indicator') 321 322# Test require_auth normalization. 323realm.addprinc('authind', password('authind')) 324realm.run([kadminl, 'setstr', 'authind', 'require_auth', 'otp radius']) 325 326# Check that krbPrincipalAuthInd attributes are set when the string 327# attribute it set. 328out = ldap_search('(krbPrincipalName=authind*)') 329if 'krbPrincipalAuthInd: otp' not in out: 330 fail('Expected krbPrincipalAuthInd value not in output') 331if 'krbPrincipalAuthInd: radius' not in out: 332 fail('Expected krbPrincipalAuthInd value not in output') 333 334# Check that the string attribute still appears when the principal is 335# loaded. 336realm.run([kadminl, 'getstrs', 'authind'], 337 expected_msg='require_auth: otp radius') 338 339# Modify the LDAP attributes and check that the change is reflected in 340# the string attribute. 341ldap_modify('dn: krbPrincipalName=authind@KRBTEST.COM,cn=t1,cn=krb5\n' 342 'changetype: modify\n' 343 'replace: krbPrincipalAuthInd\n' 344 'krbPrincipalAuthInd: radius\n' 345 'krbPrincipalAuthInd: pkinit\n') 346realm.run([kadminl, 'getstrs', 'authind'], 347 expected_msg='require_auth: radius pkinit') 348 349# Regression test for #8877: remove the string attribute and check 350# that it is reflected in the LDAP attributes and by getstrs. 351realm.run([kadminl, 'delstr', 'authind', 'require_auth']) 352out = ldap_search('(krbPrincipalName=authind*)') 353if 'krbPrincipalAuthInd' in out: 354 fail('krbPrincipalAuthInd attribute still present after delstr') 355out = realm.run([kadminl, 'getstrs', 'authind']) 356if 'require_auth' in out: 357 fail('require_auth string attribute still visible after delstr') 358 359mark('LDAP service principal aliases') 360 361# Test service principal aliases. 362realm.addprinc('canon', password('canon')) 363realm.run([kadminl, 'alias', 'alias', 'canon']) 364realm.run([kadminl, 'alias', 'ent\\@abc', 'canon']) 365out = ldap_search('(krbPrincipalName=canon*)') 366if ('krbPrincipalName: canon@KRBTEST.COM' not in out or 367 'krbPrincipalName: alias@KRBTEST.COM' not in out or 368 'krbPrincipalName: ent@abc@KRBTEST.COM' not in out or 369 'krbCanonicalName: canon@KRBTEST.COM' not in out): 370 fail('expected names not found in canon object') 371realm.run([kadminl, 'getprinc', 'alias'], 372 expected_msg='Principal: canon@KRBTEST.COM\n') 373realm.run([kadminl, 'getprinc', 'ent\\@abc'], 374 expected_msg='Principal: canon@KRBTEST.COM\n') 375realm.run([kadminl, 'getprinc', 'canon'], 376 expected_msg='Principal: canon@KRBTEST.COM\n') 377realm.run([kvno, 'alias', 'canon']) 378out = realm.run([klist]) 379if 'alias@KRBTEST.COM\n' not in out or 'canon@KRBTEST.COM' not in out: 380 fail('After fetching alias and canon, klist is missing one or both') 381realm.kinit(realm.user_princ, password('user'), ['-S', 'alias']) 382realm.klist(realm.user_princ, 'alias@KRBTEST.COM') 383 384# Make sure an alias to the local TGS is still treated like an alias. 385realm.run([kadminl, 'alias', 'tgtalias', 'krbtgt/KRBTEST.COM']) 386realm.run([kadminl, 'getprinc', 'tgtalias'], 387 expected_msg='Principal: krbtgt/KRBTEST.COM@KRBTEST.COM') 388realm.kinit(realm.user_princ, password('user')) 389realm.run([kvno, 'tgtalias']) 390realm.klist(realm.user_princ, 'tgtalias@KRBTEST.COM') 391 392# Make sure aliases work in header tickets. 393realm.run([kadminl, 'modprinc', '-maxrenewlife', '3 hours', 'user']) 394realm.run([kadminl, 'modprinc', '-maxrenewlife', '3 hours', 395 'krbtgt/KRBTEST.COM']) 396realm.kinit(realm.user_princ, password('user'), ['-l', '1h', '-r', '2h']) 397realm.run([kvno, 'alias']) 398realm.kinit(realm.user_princ, flags=['-R', '-S', 'alias']) 399realm.klist(realm.user_princ, 'alias@KRBTEST.COM') 400 401# Test client principal aliases, with and without preauth. 402realm.kinit('canon', password('canon')) 403realm.kinit('alias', password('canon')) 404realm.run([kvno, 'alias']) 405realm.klist('alias@KRBTEST.COM', 'alias@KRBTEST.COM') 406realm.kinit('alias', password('canon'), ['-C']) 407realm.run([kvno, 'alias']) 408realm.klist('canon@KRBTEST.COM', 'alias@KRBTEST.COM') 409realm.run([kadminl, 'modprinc', '+requires_preauth', 'canon']) 410realm.kinit('canon', password('canon')) 411realm.kinit('alias', password('canon'), ['-C']) 412 413# Test enterprise alias with and without canonicalization. 414realm.kinit('ent@abc', password('canon'), ['-E', '-C']) 415realm.run([kvno, 'alias']) 416realm.klist('canon@KRBTEST.COM', 'alias@KRBTEST.COM') 417 418realm.kinit('ent@abc', password('canon'), ['-E']) 419realm.run([kvno, 'alias']) 420realm.klist('ent\\@abc@KRBTEST.COM', 'alias@KRBTEST.COM') 421 422# Test client name canonicalization in non-krbtgt AS reply 423realm.kinit('alias', password('canon'), ['-C', '-S', 'kadmin/changepw']) 424 425# Test deleting an alias. 426mark('LDAP alias deletion') 427realm.run([kadminl, 'delprinc', 'alias']) 428realm.run([kadminl, 'getprinc', 'alias'], expected_code=1, 429 expected_msg='Principal does not exist') 430realm.run([kadminl, 'getprinc', 'ent\\@abc'], 431 expected_msg='Principal: canon@KRBTEST.COM\n') 432realm.run([kadminl, 'getprinc', 'canon'], 433 expected_msg='Principal: canon@KRBTEST.COM\n') 434 435# Test deleting a canonical name when an alias is present. 436realm.run([kadminl, 'delprinc', 'canon']) 437realm.run([kadminl, 'getprinc', 'canon'], expected_code=1, 438 expected_msg='Principal does not exist') 439realm.run([kadminl, 'getprinc', 'ent\\@abc'], expected_code=1, 440 expected_msg='Principal does not exist') 441 442mark('LDAP password history') 443 444# Test password history. 445def test_pwhist(nhist): 446 def cpw(n, **kwargs): 447 realm.run([kadminl, 'cpw', '-pw', str(n), princ], **kwargs) 448 def cpw_fail(n): 449 cpw(n, expected_code=1) 450 output('*** Testing password history of size %d\n' % nhist) 451 princ = 'pwhistprinc' + str(nhist) 452 pol = 'pwhistpol' + str(nhist) 453 realm.run([kadminl, 'addpol', '-history', str(nhist), pol]) 454 realm.run([kadminl, 'addprinc', '-policy', pol, '-nokey', princ]) 455 for i in range(nhist): 456 # Set a password, then check that all previous passwords fail. 457 cpw(i) 458 for j in range(i + 1): 459 cpw_fail(j) 460 # Set one more new password, and make sure the oldest key is 461 # rotated out. 462 cpw(nhist) 463 cpw_fail(1) 464 cpw(0) 465 466for n in (1, 2, 3, 4, 5): 467 test_pwhist(n) 468 469# Regression test for #8193: test password character class requirements. 470princ = 'charclassprinc' 471pol = 'charclasspol' 472realm.run([kadminl, 'addpol', '-minclasses', '3', pol]) 473realm.run([kadminl, 'addprinc', '-policy', pol, '-nokey', princ]) 474realm.run([kadminl, 'cpw', '-pw', 'abcdef', princ], expected_code=1) 475realm.run([kadminl, 'cpw', '-pw', 'Abcdef', princ], expected_code=1) 476realm.run([kadminl, 'cpw', '-pw', 'Abcdef1', princ]) 477 478# Test principal renaming and make sure last modified is changed 479def get_princ(princ): 480 out = realm.run([kadminl, 'getprinc', princ]) 481 return dict(map(str.strip, x.split(":", 1)) for x in out.splitlines()) 482 483mark('LDAP principal renaming') 484realm.addprinc("rename", password('rename')) 485renameprinc = get_princ("rename") 486realm.run([kadminl, '-p', 'fake@KRBTEST.COM', 'renprinc', 'rename', 'renamed']) 487renamedprinc = get_princ("renamed") 488if renameprinc['Last modified'] == renamedprinc['Last modified']: 489 fail('Last modified data not updated when principal was renamed') 490 491# Regression test for #7980 (fencepost when dividing keys up by kvno). 492mark('#7980 regression test') 493realm.run([kadminl, 'addprinc', '-randkey', '-e', 'aes256-cts,aes128-cts', 494 'kvnoprinc']) 495realm.run([kadminl, 'cpw', '-randkey', '-keepold', '-e', 496 'aes256-cts,aes128-cts', 'kvnoprinc']) 497realm.run([kadminl, 'getprinc', 'kvnoprinc'], expected_msg='Number of keys: 4') 498realm.run([kadminl, 'cpw', '-randkey', '-keepold', '-e', 499 'aes256-cts,aes128-cts', 'kvnoprinc']) 500realm.run([kadminl, 'getprinc', 'kvnoprinc'], expected_msg='Number of keys: 6') 501 502# Regression test for #8041 (NULL dereference on keyless principals). 503mark('#8041 regression test') 504realm.run([kadminl, 'addprinc', '-nokey', 'keylessprinc']) 505realm.run([kadminl, 'getprinc', 'keylessprinc'], 506 expected_msg='Number of keys: 0') 507realm.run([kadminl, 'cpw', '-randkey', '-e', 'aes256-cts,aes128-cts', 508 'keylessprinc']) 509realm.run([kadminl, 'cpw', '-randkey', '-keepold', '-e', 510 'aes256-cts,aes128-cts', 'keylessprinc']) 511realm.run([kadminl, 'getprinc', 'keylessprinc'], 512 expected_msg='Number of keys: 4') 513realm.run([kadminl, 'purgekeys', '-all', 'keylessprinc']) 514realm.run([kadminl, 'getprinc', 'keylessprinc'], 515 expected_msg='Number of keys: 0') 516 517# Test for 8354 (old password history entries when -keepold is used) 518mark('#8354 regression test') 519realm.run([kadminl, 'addpol', '-history', '2', 'keepoldpasspol']) 520realm.run([kadminl, 'addprinc', '-policy', 'keepoldpasspol', '-pw', 'aaaa', 521 'keepoldpassprinc']) 522for p in ('bbbb', 'cccc', 'aaaa'): 523 realm.run([kadminl, 'cpw', '-keepold', '-pw', p, 'keepoldpassprinc']) 524 525if runenv.sizeof_time_t <= 4: 526 skipped('y2038 LDAP test', 'platform has 32-bit time_t') 527else: 528 # Test storage of timestamps after y2038. 529 realm.run([kadminl, 'modprinc', '-pwexpire', '2040-02-03', 'user']) 530 realm.run([kadminl, 'getprinc', 'user'], expected_msg=' 2040\n') 531 532# Regression test for #8861 (pw_expiration policy enforcement). 533mark('pw_expiration propogation') 534# Create a policy with a max life and verify its application. 535realm.run([kadminl, 'addpol', '-maxlife', '1s', 'pw_e']) 536realm.run([kadminl, 'addprinc', '-policy', 'pw_e', '-pw', 'password', 537 'pwuser']) 538out = realm.run([kadminl, 'getprinc', 'pwuser'], 539 expected_msg='Password expiration date: ') 540if 'Password expiration date: [never]' in out: 541 fail('pw_expiration not applied at principal creation') 542# Unset the policy max life and verify its application during password 543# change. 544realm.run([kadminl, 'modpol', '-maxlife', '0', 'pw_e']) 545realm.run([kadminl, 'cpw', '-pw', 'password_', 'pwuser']) 546realm.run([kadminl, 'getprinc', 'pwuser'], 547 expected_msg='Password expiration date: [never]') 548 549realm.stop() 550 551# Test dump and load. Include a regression test for #8882 552# (pw_expiration not set during load operation). 553mark('LDAP dump and load') 554realm.run([kadminl, 'modprinc', '-pwexpire', 'now', 'pwuser']) 555dumpfile = os.path.join(realm.testdir, 'dump') 556realm.run([kdb5_util, 'dump', dumpfile]) 557realm.run([kdb5_util, 'load', dumpfile], expected_code=1, 558 expected_msg='KDB module requires -update argument') 559realm.run([kadminl, 'delprinc', 'pwuser']) 560realm.run([kdb5_util, 'load', '-update', dumpfile]) 561out = realm.run([kadminl, 'getprinc', 'pwuser']) 562if 'Password expiration date: [never]' in out: 563 fail('pw_expiration not preserved across dump and load') 564realm.run([kadminl, 'getprinc', 'tgtalias'], 565 expected_msg='Principal: krbtgt/KRBTEST.COM@KRBTEST.COM') 566 567# Destroy the realm. 568kldaputil(['destroy', '-f']) 569out = kldaputil(['list']) 570if out: 571 fail('Unexpected kdb5_ldap_util list output after destroy') 572 573if not core_schema: 574 skip_rest('LDAP SASL tests', 'core schema not found') 575 576if runenv.have_sasl != 'yes': 577 skip_rest('LDAP SASL tests', 'SASL support not built') 578 579# Test SASL EXTERNAL auth. Remove the DNs and service password file 580# from the DB module config. 581mark('LDAP SASL EXTERNAL auth') 582os.remove(ldap_pwfile) 583dbmod = conf['dbmodules']['ldap'] 584dbmod['ldap_kdc_sasl_mech'] = dbmod['ldap_kadmind_sasl_mech'] = 'EXTERNAL' 585del dbmod['ldap_service_password_file'] 586del dbmod['ldap_kdc_dn'], dbmod['ldap_kadmind_dn'] 587realm = K5Realm(create_kdb=False, kdc_conf=conf) 588realm.run([kdb5_ldap_util, 'create', '-s', '-P', 'master']) 589realm.start_kdc() 590realm.addprinc(realm.user_princ, password('user')) 591realm.kinit(realm.user_princ, password('user')) 592realm.stop() 593realm.run([kdb5_ldap_util, 'destroy', '-f']) 594 595# Test SASL DIGEST-MD5 auth. We need to set a clear-text password for 596# the admin DN, so create a person entry (requires the core schema). 597# Restore the service password file in the config and set authcids. 598mark('LDAP SASL DIGEST-MD5 auth') 599ldap_add('cn=admin,cn=krb5', 'person', 600 ['sn: dummy', 'userPassword: admin']) 601dbmod['ldap_kdc_sasl_mech'] = dbmod['ldap_kadmind_sasl_mech'] = 'DIGEST-MD5' 602dbmod['ldap_kdc_sasl_authcid'] = 'digestuser' 603dbmod['ldap_kadmind_sasl_authcid'] = 'digestuser' 604dbmod['ldap_service_password_file'] = ldap_pwfile 605realm = K5Realm(create_kdb=False, kdc_conf=conf) 606# Work around https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1091694 607if runenv.asan == 'yes': 608 realm.env['ASAN_OPTIONS'] = 'detect_leaks=false' 609input = admin_pw + '\n' + admin_pw + '\n' 610realm.run([kdb5_ldap_util, 'stashsrvpw', 'digestuser'], input=input) 611realm.run([kdb5_ldap_util, 'create', '-s', '-P', 'master']) 612realm.start_kdc() 613realm.addprinc(realm.user_princ, password('user')) 614realm.kinit(realm.user_princ, password('user')) 615realm.stop() 616# Exercise DB options, which should cause binding to fail. 617realm.run([kadminl, '-x', 'sasl_authcid=ab', 'getprinc', 'user'], 618 expected_code=1, expected_msg='Cannot bind to LDAP server') 619realm.run([kadminl, '-x', 'bindpwd=wrong', 'getprinc', 'user'], 620 expected_code=1, expected_msg='Cannot bind to LDAP server') 621realm.run([kdb5_ldap_util, 'destroy', '-f']) 622 623# We could still use tests to exercise: 624# * DB arg handling in krb5_ldap_create 625# * krbAllowedToDelegateTo attribute processing 626# * A load operation overwriting a standalone principal entry which 627# already exists but doesn't have a krbPrincipalName attribute 628# matching the principal name. 629# * A bunch of invalid-input error conditions 630# 631# There is no coverage for the following because it would be difficult: 632# * Out-of-memory error conditions 633# * Handling of failures from slapd (including krb5_retry_get_ldap_handle) 634# * Handling of servers which don't support mod-increment 635# * krb5_ldap_delete_krbcontainer (only happens if krb5_ldap_create fails) 636 637success('LDAP and DB2 KDB tests') 638