xref: /freebsd/crypto/krb5/src/tests/t_crossrealm.py (revision 7f2fe78b9dd5f51c821d771b63d2e096f6fd49e9)
1*7f2fe78bSCy Schubert# Copyright (C) 2011 by the Massachusetts Institute of Technology.
2*7f2fe78bSCy Schubert# All rights reserved.
3*7f2fe78bSCy Schubert#
4*7f2fe78bSCy Schubert# Export of this software from the United States of America may
5*7f2fe78bSCy Schubert#   require a specific license from the United States Government.
6*7f2fe78bSCy Schubert#   It is the responsibility of any person or organization contemplating
7*7f2fe78bSCy Schubert#   export to obtain such a license before exporting.
8*7f2fe78bSCy Schubert#
9*7f2fe78bSCy Schubert# WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
10*7f2fe78bSCy Schubert# distribute this software and its documentation for any purpose and
11*7f2fe78bSCy Schubert# without fee is hereby granted, provided that the above copyright
12*7f2fe78bSCy Schubert# notice appear in all copies and that both that copyright notice and
13*7f2fe78bSCy Schubert# this permission notice appear in supporting documentation, and that
14*7f2fe78bSCy Schubert# the name of M.I.T. not be used in advertising or publicity pertaining
15*7f2fe78bSCy Schubert# to distribution of the software without specific, written prior
16*7f2fe78bSCy Schubert# permission.  Furthermore if you modify this software you must label
17*7f2fe78bSCy Schubert# your software as modified software and not distribute it in such a
18*7f2fe78bSCy Schubert# fashion that it might be confused with the original M.I.T. software.
19*7f2fe78bSCy Schubert# M.I.T. makes no representations about the suitability of
20*7f2fe78bSCy Schubert# this software for any purpose.  It is provided "as is" without express
21*7f2fe78bSCy Schubert# or implied warranty.
22*7f2fe78bSCy Schubert
23*7f2fe78bSCy Schubertfrom k5test import *
24*7f2fe78bSCy Schubert
25*7f2fe78bSCy Schubertdef test_kvno(r, princ, test, env=None):
26*7f2fe78bSCy Schubert    r.run([kvno, princ], env=env, expected_msg=princ)
27*7f2fe78bSCy Schubert
28*7f2fe78bSCy Schubert
29*7f2fe78bSCy Schubertdef stop(*realms):
30*7f2fe78bSCy Schubert    for r in realms:
31*7f2fe78bSCy Schubert        r.stop()
32*7f2fe78bSCy Schubert
33*7f2fe78bSCy Schubert
34*7f2fe78bSCy Schubert# Verify that the princs appear as the service principals in the klist
35*7f2fe78bSCy Schubert# output for the realm r, in order.
36*7f2fe78bSCy Schubertdef check_klist(r, princs):
37*7f2fe78bSCy Schubert    out = r.run([klist])
38*7f2fe78bSCy Schubert    count = 0
39*7f2fe78bSCy Schubert    seen_header = False
40*7f2fe78bSCy Schubert    for l in out.split('\n'):
41*7f2fe78bSCy Schubert        if l.startswith('Valid starting'):
42*7f2fe78bSCy Schubert            seen_header = True
43*7f2fe78bSCy Schubert            continue
44*7f2fe78bSCy Schubert        if not seen_header or l == '':
45*7f2fe78bSCy Schubert            continue
46*7f2fe78bSCy Schubert        if count >= len(princs):
47*7f2fe78bSCy Schubert            fail('too many entries in klist output')
48*7f2fe78bSCy Schubert        svcprinc = l.split()[4]
49*7f2fe78bSCy Schubert        if svcprinc != princs[count]:
50*7f2fe78bSCy Schubert            fail('saw service princ %s in klist output, expected %s' %
51*7f2fe78bSCy Schubert                 (svcprinc, princs[count]))
52*7f2fe78bSCy Schubert        count += 1
53*7f2fe78bSCy Schubert    if count != len(princs):
54*7f2fe78bSCy Schubert        fail('not enough entries in klist output')
55*7f2fe78bSCy Schubert
56*7f2fe78bSCy Schubert
57*7f2fe78bSCy Schubertdef tgt(r1, r2):
58*7f2fe78bSCy Schubert    return 'krbtgt/%s@%s' % (r1.realm, r2.realm)
59*7f2fe78bSCy Schubert
60*7f2fe78bSCy Schubert
61*7f2fe78bSCy Schubert# Basic two-realm test with cross TGTs in both directions.
62*7f2fe78bSCy Schubertmark('two realms')
63*7f2fe78bSCy Schubertr1, r2 = cross_realms(2)
64*7f2fe78bSCy Schuberttest_kvno(r1, r2.host_princ, 'basic r1->r2')
65*7f2fe78bSCy Schubertcheck_klist(r1, (tgt(r1, r1), tgt(r2, r1), r2.host_princ))
66*7f2fe78bSCy Schuberttest_kvno(r2, r1.host_princ, 'basic r2->r1')
67*7f2fe78bSCy Schubertcheck_klist(r2, (tgt(r2, r2), tgt(r1, r2), r1.host_princ))
68*7f2fe78bSCy Schubertstop(r1, r2)
69*7f2fe78bSCy Schubert
70*7f2fe78bSCy Schubert# Test the KDC domain walk for hierarchically arranged realms.  The
71*7f2fe78bSCy Schubert# client in A.X will ask for a cross TGT to B.X, but A.X's KDC only
72*7f2fe78bSCy Schubert# has a TGT for the intermediate realm X, so it will return that
73*7f2fe78bSCy Schubert# instead.  The client will use that to get a TGT for B.X.
74*7f2fe78bSCy Schubertmark('hierarchical realms')
75*7f2fe78bSCy Schubertr1, r2, r3 = cross_realms(3, xtgts=((0,1), (1,2)),
76*7f2fe78bSCy Schubert                          args=({'realm': 'A.X'}, {'realm': 'X'},
77*7f2fe78bSCy Schubert                                {'realm': 'B.X'}))
78*7f2fe78bSCy Schuberttest_kvno(r1, r3.host_princ, 'KDC domain walk')
79*7f2fe78bSCy Schubertcheck_klist(r1, (tgt(r1, r1), r3.host_princ))
80*7f2fe78bSCy Schubert
81*7f2fe78bSCy Schubert# Test start_realm in this setup.
82*7f2fe78bSCy Schubertr1.run([kvno, '--out-cache', r1.ccache, r2.krbtgt_princ])
83*7f2fe78bSCy Schubertr1.run([klist, '-C'], expected_msg='config: start_realm = X')
84*7f2fe78bSCy Schubertmsgs = ('Requesting TGT krbtgt/B.X@X using TGT krbtgt/X@X',
85*7f2fe78bSCy Schubert        'Received TGT for service realm: krbtgt/B.X@X')
86*7f2fe78bSCy Schubertr1.run([kvno, r3.host_princ], expected_trace=msgs)
87*7f2fe78bSCy Schubert
88*7f2fe78bSCy Schubertstop(r1, r2, r3)
89*7f2fe78bSCy Schubert
90*7f2fe78bSCy Schubert# Test client capaths.  The client in A will ask for a cross TGT to D,
91*7f2fe78bSCy Schubert# but A's KDC won't have it and won't know an intermediate to return.
92*7f2fe78bSCy Schubert# The client will walk its A->D capaths to get TGTs for B, then C,
93*7f2fe78bSCy Schubert# then D.  The KDCs for C and D need capaths settings to avoid failing
94*7f2fe78bSCy Schubert# transited checks, including a capaths for A->C.
95*7f2fe78bSCy Schubertmark('client capaths')
96*7f2fe78bSCy Schubertcapaths = {'capaths': {'A': {'D': ['B', 'C'], 'C': 'B'}}}
97*7f2fe78bSCy Schubertr1, r2, r3, r4 = cross_realms(4, xtgts=((0,1), (1,2), (2,3)),
98*7f2fe78bSCy Schubert                              args=({'realm': 'A'},
99*7f2fe78bSCy Schubert                                    {'realm': 'B'},
100*7f2fe78bSCy Schubert                                    {'realm': 'C', 'krb5_conf': capaths},
101*7f2fe78bSCy Schubert                                    {'realm': 'D', 'krb5_conf': capaths}))
102*7f2fe78bSCy Schubertr1client = r1.special_env('client', False, krb5_conf=capaths)
103*7f2fe78bSCy Schuberttest_kvno(r1, r4.host_princ, 'client capaths', r1client)
104*7f2fe78bSCy Schubertcheck_klist(r1, (tgt(r1, r1), tgt(r2, r1), tgt(r3, r2), tgt(r4, r3),
105*7f2fe78bSCy Schubert                 r4.host_princ))
106*7f2fe78bSCy Schubertstop(r1, r2, r3, r4)
107*7f2fe78bSCy Schubert
108*7f2fe78bSCy Schubert# Test KDC capaths.  The KDCs for A and B have appropriate capaths
109*7f2fe78bSCy Schubert# settings to determine intermediate TGTs to return, but the client
110*7f2fe78bSCy Schubert# has no idea.
111*7f2fe78bSCy Schubertmark('kdc capaths')
112*7f2fe78bSCy Schubertcapaths = {'capaths': {'A': {'D': ['B', 'C'], 'C': 'B'}, 'B': {'D': 'C'}}}
113*7f2fe78bSCy Schubertr1, r2, r3, r4 = cross_realms(4, xtgts=((0,1), (1,2), (2,3)),
114*7f2fe78bSCy Schubert                              args=({'realm': 'A', 'krb5_conf': capaths},
115*7f2fe78bSCy Schubert                                    {'realm': 'B', 'krb5_conf': capaths},
116*7f2fe78bSCy Schubert                                    {'realm': 'C', 'krb5_conf': capaths},
117*7f2fe78bSCy Schubert                                    {'realm': 'D', 'krb5_conf': capaths}))
118*7f2fe78bSCy Schubertr1client = r1.special_env('client', False, krb5_conf={'capaths': None})
119*7f2fe78bSCy Schuberttest_kvno(r1, r4.host_princ, 'KDC capaths', r1client)
120*7f2fe78bSCy Schubertcheck_klist(r1, (tgt(r1, r1), r4.host_princ))
121*7f2fe78bSCy Schubertstop(r1, r2, r3, r4)
122*7f2fe78bSCy Schubert
123*7f2fe78bSCy Schubert# A capaths value of '.' should enforce direct cross-realm, with no
124*7f2fe78bSCy Schubert# intermediate.
125*7f2fe78bSCy Schubertmark('direct cross-realm enforcement')
126*7f2fe78bSCy Schubertcapaths = {'capaths': {'A.X': {'B.X': '.'}}}
127*7f2fe78bSCy Schubertr1, r2, r3 = cross_realms(3, xtgts=((0,1), (1,2)),
128*7f2fe78bSCy Schubert                          args=({'realm': 'A.X', 'krb5_conf': capaths},
129*7f2fe78bSCy Schubert                                {'realm': 'X'}, {'realm': 'B.X'}))
130*7f2fe78bSCy Schubertr1.run([kvno, r3.host_princ], expected_code=1,
131*7f2fe78bSCy Schubert       expected_msg='Server krbtgt/B.X@A.X not found in Kerberos database')
132*7f2fe78bSCy Schubertstop(r1, r2, r3)
133*7f2fe78bSCy Schubert
134*7f2fe78bSCy Schubert# Test transited error.  The KDC for C does not recognize B as an
135*7f2fe78bSCy Schubert# intermediate realm for A->C, so it refuses to issue a service
136*7f2fe78bSCy Schubert# ticket.
137*7f2fe78bSCy Schubertmark('transited error (three realms)')
138*7f2fe78bSCy Schubertcapaths = {'capaths': {'A': {'C': 'B'}}}
139*7f2fe78bSCy Schubertr1, r2, r3 = cross_realms(3, xtgts=((0,1), (1,2)),
140*7f2fe78bSCy Schubert                          args=({'realm': 'A', 'krb5_conf': capaths},
141*7f2fe78bSCy Schubert                                {'realm': 'B'}, {'realm': 'C'}))
142*7f2fe78bSCy Schubertr1.run([kvno, r3.host_princ], expected_code=1,
143*7f2fe78bSCy Schubert       expected_msg='KDC policy rejects request')
144*7f2fe78bSCy Schubertcheck_klist(r1, (tgt(r1, r1), tgt(r3, r2)))
145*7f2fe78bSCy Schubertstop(r1, r2, r3)
146*7f2fe78bSCy Schubert
147*7f2fe78bSCy Schubert# Test server transited checking.  The KDC for C recognizes B as an
148*7f2fe78bSCy Schubert# intermediate realm for A->C, but the server environment does not.
149*7f2fe78bSCy Schubert# The server should honor the ticket if the transited-policy-checked
150*7f2fe78bSCy Schubert# flag is set, but not if it isn't.  (It is only possible for our KDC
151*7f2fe78bSCy Schubert# to issue a ticket without the transited-policy-checked flag with
152*7f2fe78bSCy Schubert# reject_bad_transit=false.)
153*7f2fe78bSCy Schubertmark('server transited checking')
154*7f2fe78bSCy Schubertcapaths = {'capaths': {'A': {'C': 'B'}}}
155*7f2fe78bSCy Schubertnoreject = {'realms': {'$realm': {'reject_bad_transit': 'false'}}}
156*7f2fe78bSCy Schubertr1, r2, r3 = cross_realms(3, xtgts=((0,1), (1,2)),
157*7f2fe78bSCy Schubert                          args=({'realm': 'A', 'krb5_conf': capaths},
158*7f2fe78bSCy Schubert                                {'realm': 'B'},
159*7f2fe78bSCy Schubert                                {'realm': 'C', 'krb5_conf': capaths,
160*7f2fe78bSCy Schubert                                 'kdc_conf': noreject}))
161*7f2fe78bSCy Schubertr3server = r3.special_env('server', False, krb5_conf={'capaths': None})
162*7f2fe78bSCy Schubert# Process a ticket with the transited-policy-checked flag set.
163*7f2fe78bSCy Schubertshutil.copy(r1.ccache, r1.ccache + '.copy')
164*7f2fe78bSCy Schubertr1.run(['./gcred', 'principal', r3.host_princ])
165*7f2fe78bSCy Schubertos.rename(r1.ccache, r3.ccache)
166*7f2fe78bSCy Schubertr3.run(['./rdreq', r3.host_princ], env=r3server, expected_msg='0 success')
167*7f2fe78bSCy Schubert# Try again with the transited-policy-checked flag unset.
168*7f2fe78bSCy Schubertos.rename(r1.ccache + '.copy', r1.ccache)
169*7f2fe78bSCy Schubertr1.run(['./gcred', '-t', 'principal', r3.host_princ])
170*7f2fe78bSCy Schubertos.rename(r1.ccache, r3.ccache)
171*7f2fe78bSCy Schubertr3.run(['./rdreq', r3.host_princ], env=r3server,
172*7f2fe78bSCy Schubert       expected_msg='43 Illegal cross-realm ticket')
173*7f2fe78bSCy Schubertstop(r1, r2, r3)
174*7f2fe78bSCy Schubert
175*7f2fe78bSCy Schubert# Test a four-realm scenario.  This test used to result in an "Illegal
176*7f2fe78bSCy Schubert# cross-realm ticket" error as the KDC for D would refuse to process
177*7f2fe78bSCy Schubert# the cross-realm ticket from C.  Now that we honor the
178*7f2fe78bSCy Schubert# transited-policy-checked flag in krb5_rd_req(), it instead issues a
179*7f2fe78bSCy Schubert# policy error as in the three-realm scenario.
180*7f2fe78bSCy Schubertmark('transited error (four realms)')
181*7f2fe78bSCy Schubertcapaths = {'capaths': {'A': {'D': ['B', 'C'], 'C': 'B'}, 'B': {'D': 'C'}}}
182*7f2fe78bSCy Schubertr1, r2, r3, r4 = cross_realms(4, xtgts=((0,1), (1,2), (2,3)),
183*7f2fe78bSCy Schubert                              args=({'realm': 'A', 'krb5_conf': capaths},
184*7f2fe78bSCy Schubert                                    {'realm': 'B', 'krb5_conf': capaths},
185*7f2fe78bSCy Schubert                                    {'realm': 'C', 'krb5_conf': capaths},
186*7f2fe78bSCy Schubert                                    {'realm': 'D'}))
187*7f2fe78bSCy Schubertr1.run([kvno, r4.host_princ], expected_code=1,
188*7f2fe78bSCy Schubert       expected_msg='KDC policy rejects request')
189*7f2fe78bSCy Schubertcheck_klist(r1, (tgt(r1, r1), tgt(r4, r3)))
190*7f2fe78bSCy Schubertstop(r1, r2, r3, r4)
191*7f2fe78bSCy Schubert
192*7f2fe78bSCy Schubertsuccess('Cross-realm tests')
193