xref: /freebsd/crypto/krb5/src/tests/t_sn2princ.py (revision 7f2fe78b9dd5f51c821d771b63d2e096f6fd49e9)
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