1from k5test import * 2 3offline = (len(args) > 0 and args[0] != "no") 4 5conf = {'libdefaults': {'dns_canonicalize_hostname': 'true'}, 6 'domain_realm': {'kerberos.org': 'R1', 7 'example.com': 'R2', 8 'mit.edu': 'R3'}} 9no_rdns_conf = {'libdefaults': {'rdns': 'false'}} 10no_canon_conf = {'libdefaults': {'dns_canonicalize_hostname': 'false', 11 'qualify_shortname': 'example.com'}} 12fallback_canon_conf = {'libdefaults': 13 {'rdns': 'false', 14 'dns_canonicalize_hostname': 'fallback'}} 15 16realm = K5Realm(realm='R1', create_host=False, krb5_conf=conf) 17no_rdns = realm.special_env('no_rdns', False, krb5_conf=no_rdns_conf) 18no_canon = realm.special_env('no_canon', False, krb5_conf=no_canon_conf) 19fallback_canon = realm.special_env('fallback_canon', False, 20 krb5_conf=fallback_canon_conf) 21 22def testbase(host, nametype, princhost, princrealm, env=None): 23 # Run the sn2princ harness with a specified host and name type and 24 # the fixed service string 'svc', and compare the result to the 25 # expected hostname and realm part. 26 out = realm.run(['./s2p', host, 'SVC', nametype], env=env).rstrip() 27 expected = 'SVC/%s@%s' % (princhost, princrealm) 28 if out != expected: 29 fail('Expected %s, got %s' % (expected, out)) 30 31def test(host, princhost, princrealm): 32 # Test with the host-based name type with canonicalization enabled. 33 testbase(host, 'srv-hst', princhost, princrealm) 34 35def testnc(host, princhost, princrealm): 36 # Test with the host-based name type with canonicalization disabled. 37 testbase(host, 'srv-hst', princhost, princrealm, env=no_canon) 38 39def testnr(host, princhost, princrealm): 40 # Test with the host-based name type with reverse lookup disabled. 41 testbase(host, 'srv-hst', princhost, princrealm, env=no_rdns) 42 43def testu(host, princhost, princrealm): 44 # Test with the unknown name type. 45 testbase(host, 'unknown', princhost, princrealm) 46 47def testfc(host, princhost, princrealm): 48 # Test with the host-based name type with canonicalization fallback. 49 testbase(host, 'srv-hst', princhost, princrealm, env=fallback_canon) 50 51# With the unknown principal type, we do not canonicalize or downcase, 52# but we do remove a trailing period and look up the realm. 53mark('unknown type') 54testu('ptr-mismatch.kerberos.org', 'ptr-mismatch.kerberos.org', 'R1') 55testu('Example.COM', 'Example.COM', 'R2') 56testu('abcde', 'abcde', '') 57 58# A ':port' or ':instance' trailer should be ignored for realm lookup. 59# If there is more than one colon in the name, we assume it's an IPv6 60# address and don't treat it as having a trailer. 61mark('port trailer') 62testu('example.com.:123', 'example.com.:123', 'R2') 63testu('Example.COM:xyZ', 'Example.COM:xyZ', 'R2') 64testu('example.com.::123', 'example.com.::123', '') 65 66# With dns_canonicalize_hostname=false, we downcase and remove 67# trailing dots but do not canonicalize the hostname. 68# Single-component names are qualified with the configured suffix 69# (defaulting to the first OS search domain, but Python cannot easily 70# retrieve that value so we don't test it). Trailers do not get 71# downcased. 72mark('dns_canonicalize_host=false') 73testnc('ptr-mismatch.kerberos.org', 'ptr-mismatch.kerberos.org', 'R1') 74testnc('Example.COM', 'example.com', 'R2') 75testnc('abcde', 'abcde.example.com', 'R2') 76testnc('example.com.:123', 'example.com:123', 'R2') 77testnc('Example.COM:xyZ', 'example.com:xyZ', 'R2') 78testnc('example.com.::123', 'example.com.::123', '') 79 80if offline: 81 skip_rest('sn2princ tests', 'offline mode requested') 82 83# For the online tests, we rely on ptr-mismatch.kerberos.org forward 84# and reverse resolving to these names. 85oname = 'ptr-mismatch.kerberos.org' 86fname = 'www.kerberos.org' 87 88# Test fallback canonicalization krb5_sname_to_principal() results. 89mark('dns_canonicalize_host=fallback') 90testfc(oname, oname, '') 91 92# Verify forward resolution before testing for it. 93try: 94 ai = socket.getaddrinfo(oname, None, 0, 0, 0, socket.AI_CANONNAME) 95except socket.gaierror: 96 skip_rest('sn2princ tests', 'cannot forward resolve %s' % oname) 97(family, socktype, proto, canonname, sockaddr) = ai[0] 98if canonname.lower() != fname: 99 skip_rest('sn2princ tests', 100 '%s forward resolves to %s, not %s' % (oname, canonname, fname)) 101 102# Test fallback canonicalization in krb5_get_credentials(). 103oprinc = 'host/' + oname 104fprinc = 'host/' + fname 105shutil.copy(realm.ccache, realm.ccache + '.save') 106# Test that we only try fprinc once if we enter it as input. 107out, trace = realm.run(['./gcred', 'srv-hst', fprinc + '@'], 108 env=fallback_canon, expected_code=1, return_trace=True) 109msg = 'Requesting tickets for %s@R1, referrals on' % fprinc 110if trace.count(msg) != 1: 111 fail('Expected one try for %s' % fprinc) 112# Create fprinc, and verify that we get it as the canonicalized 113# fallback for oprinc. 114realm.addprinc(fprinc) 115msgs = ('Getting credentials user@R1 -> %s@ using' % oprinc, 116 'Requesting tickets for %s@R1' % oprinc, 117 'Requesting tickets for %s@R1' % fprinc, 118 'Received creds for desired service %s@R1' % fprinc) 119realm.run(['./gcred', 'srv-hst', oprinc + '@'], env=fallback_canon, 120 expected_msg=fprinc, expected_trace=msgs) 121realm.addprinc(oprinc) 122# oprinc now exists, but we still get the fprinc ticket from the cache. 123realm.run(['./gcred', 'srv-hst', oprinc + '@'], env=fallback_canon, 124 expected_msg=fprinc) 125# Without the cached result, we should get oprinc in preference to fprinc. 126os.rename(realm.ccache + '.save', realm.ccache) 127realm.run(['./gcred', 'srv-hst', oprinc], env=fallback_canon, 128 expected_msg=oprinc) 129 130# Test fallback canonicalization for krb5_rd_req(). 131realm.run([kadminl, 'ktadd', fprinc]) 132msgs = ('Decrypted AP-REQ with server principal %s@R1' % fprinc, 133 'AP-REQ ticket: user@R1 -> %s@R1' % fprinc) 134realm.run(['./rdreq', fprinc, oprinc + '@'], env=fallback_canon, 135 expected_trace=msgs) 136 137# Test fallback canonicalization for getting initial creds with a keytab. 138msgs = ('Getting initial credentials for %s@' % oprinc, 139 'Found entries for %s@R1 in keytab' % fprinc, 140 'Retrieving %s@R1 from ' % fprinc) 141realm.run(['./icred', '-k', realm.keytab, '-S', 'host', oname], 142 env=fallback_canon, expected_trace=msgs) 143 144# Test forward-only canonicalization (rdns=false). 145mark('rdns=false') 146testnr(oname, fname, 'R1') 147testnr(oname + ':123', fname + ':123', 'R1') 148testnr(oname + ':xyZ', fname + ':xyZ', 'R1') 149 150# Verify reverse resolution before testing for it. 151try: 152 names = socket.getnameinfo(sockaddr, socket.NI_NAMEREQD) 153except socket.gaierror: 154 skip_rest('reverse sn2princ tests', 'cannot reverse resolve %s' % oname) 155rname = names[0].lower() 156if rname == fname: 157 skip_rest('reverse sn2princ tests', 158 '%s reverse resolves to %s ' 159 'which should be different from %s' % (oname, rname, fname)) 160 161# Test default canonicalization (forward and reverse lookup). 162mark('default') 163test(oname, rname, 'R3') 164test(oname + ':123', rname + ':123', 'R3') 165test(oname + ':xyZ', rname + ':xyZ', 'R3') 166 167success('krb5_sname_to_principal tests') 168