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