28 * @run main/othervm/timeout=120 -Dsun.security.krb5.debug=true ReferralsTest |
28 * @run main/othervm/timeout=120 -Dsun.security.krb5.debug=true ReferralsTest |
29 * @summary Test Kerberos cross-realm referrals (RFC 6806) |
29 * @summary Test Kerberos cross-realm referrals (RFC 6806) |
30 */ |
30 */ |
31 |
31 |
32 import java.io.File; |
32 import java.io.File; |
33 import sun.security.krb5.Credentials; |
33 import java.security.Principal; |
34 import sun.security.krb5.internal.CredentialsUtil; |
34 import java.util.Arrays; |
35 import sun.security.krb5.KrbAsReqBuilder; |
35 import java.util.HashMap; |
|
36 import java.util.List; |
|
37 import java.util.Map; |
|
38 import java.util.Set; |
|
39 import javax.security.auth.kerberos.KerberosTicket; |
|
40 import javax.security.auth.Subject; |
|
41 |
|
42 import org.ietf.jgss.GSSName; |
|
43 |
|
44 import sun.security.jgss.GSSUtil; |
36 import sun.security.krb5.PrincipalName; |
45 import sun.security.krb5.PrincipalName; |
37 |
46 |
38 public class ReferralsTest { |
47 public class ReferralsTest { |
39 private static final boolean DEBUG = true; |
48 private static final boolean DEBUG = true; |
40 private static final String krbConfigName = "krb5-localkdc.conf"; |
49 private static final String krbConfigName = "krb5-localkdc.conf"; |
41 private static final String realmKDC1 = "RABBIT.HOLE"; |
50 private static final String realmKDC1 = "RABBIT.HOLE"; |
42 private static final String realmKDC2 = "DEV.RABBIT.HOLE"; |
51 private static final String realmKDC2 = "DEV.RABBIT.HOLE"; |
43 private static final char[] password = "123qwe@Z".toCharArray(); |
52 private static final char[] password = "123qwe@Z".toCharArray(); |
|
53 |
|
54 // Names |
44 private static final String clientName = "test"; |
55 private static final String clientName = "test"; |
45 |
56 private static final String serviceName = "http" + |
|
57 PrincipalName.NAME_COMPONENT_SEPARATOR_STR + |
|
58 "server.dev.rabbit.hole"; |
|
59 |
|
60 // Alias |
46 private static final String clientAlias = clientName + |
61 private static final String clientAlias = clientName + |
47 PrincipalName.NAME_REALM_SEPARATOR_STR + realmKDC1; |
62 PrincipalName.NAME_REALM_SEPARATOR_STR + realmKDC1; |
48 |
63 |
49 private static final String clientKDC1QueryName = clientAlias.replaceAll( |
64 // Names + realms |
|
65 private static final String clientKDC1Name = clientAlias.replaceAll( |
50 PrincipalName.NAME_REALM_SEPARATOR_STR, "\\\\" + |
66 PrincipalName.NAME_REALM_SEPARATOR_STR, "\\\\" + |
51 PrincipalName.NAME_REALM_SEPARATOR_STR) + |
67 PrincipalName.NAME_REALM_SEPARATOR_STR) + |
52 PrincipalName.NAME_REALM_SEPARATOR_STR + realmKDC1; |
68 PrincipalName.NAME_REALM_SEPARATOR_STR + realmKDC1; |
53 private static PrincipalName clientKDC1QueryPrincipal = null; |
|
54 static { |
|
55 try { |
|
56 clientKDC1QueryPrincipal = new PrincipalName( |
|
57 clientKDC1QueryName, PrincipalName.KRB_NT_ENTERPRISE, |
|
58 null); |
|
59 } catch (Throwable t) {} |
|
60 } |
|
61 |
|
62 private static final String clientKDC2Name = clientName + |
69 private static final String clientKDC2Name = clientName + |
63 PrincipalName.NAME_REALM_SEPARATOR_STR + realmKDC2; |
70 PrincipalName.NAME_REALM_SEPARATOR_STR + realmKDC2; |
64 |
71 private static final String serviceKDC2Name = serviceName + |
65 private static final String serviceName = "http" + |
72 PrincipalName.NAME_REALM_SEPARATOR_STR + realmKDC2; |
66 PrincipalName.NAME_COMPONENT_SEPARATOR_STR + |
|
67 "server.dev.rabbit.hole"; |
|
68 |
|
69 private static Credentials tgt; |
|
70 private static Credentials tgs; |
|
71 |
73 |
72 public static void main(String[] args) throws Exception { |
74 public static void main(String[] args) throws Exception { |
73 try { |
75 try { |
74 initializeKDCs(); |
76 initializeKDCs(); |
75 getTGT(); |
77 testSubjectCredentials(); |
76 getTGS(); |
78 testDelegated(); |
77 } finally { |
79 } finally { |
78 cleanup(); |
80 cleanup(); |
79 } |
81 } |
80 } |
82 } |
81 |
83 |
118 if (f.exists()) { |
125 if (f.exists()) { |
119 f.delete(); |
126 f.delete(); |
120 } |
127 } |
121 } |
128 } |
122 |
129 |
123 private static void getTGT() throws Exception { |
130 /* |
124 KrbAsReqBuilder builder = new KrbAsReqBuilder(clientKDC1QueryPrincipal, |
131 * The client subject (whose principal is |
125 password); |
132 * test@RABBIT.HOLE@RABBIT.HOLE) will obtain a TGT after |
126 tgt = builder.action().getCreds(); |
133 * realm referral and name canonicalization (TGT cname |
127 builder.destroy(); |
134 * will be test@DEV.RABBIT.HOLE). With this TGT, the client will request |
|
135 * a TGS for service http/server.dev.rabbit.hole@RABBIT.HOLE. After |
|
136 * realm referral, a http/server.dev.rabbit.hole@DEV.RABBIT.HOLE TGS |
|
137 * will be obtained. |
|
138 * |
|
139 * Assert that we get the proper TGT and TGS tickets, and that they are |
|
140 * associated to the client subject. |
|
141 * |
|
142 * Assert that if we request a TGS for the same service again (based on the |
|
143 * original service name), we don't get a new one but the previous, |
|
144 * already in the subject credentials. |
|
145 */ |
|
146 private static void testSubjectCredentials() throws Exception { |
|
147 Subject clientSubject = new Subject(); |
|
148 Context clientContext = Context.fromUserPass(clientSubject, |
|
149 clientKDC1Name, password, false); |
|
150 |
|
151 Set<Principal> clientPrincipals = clientSubject.getPrincipals(); |
|
152 if (clientPrincipals.size() != 1) { |
|
153 throw new Exception("Only one client subject principal expected"); |
|
154 } |
|
155 Principal clientPrincipal = clientPrincipals.iterator().next(); |
128 if (DEBUG) { |
156 if (DEBUG) { |
129 System.out.println("TGT"); |
157 System.out.println("Client subject principal: " + |
130 System.out.println("----------------------"); |
158 clientPrincipal.getName()); |
131 System.out.println(tgt); |
159 } |
132 System.out.println("----------------------"); |
160 if (!clientPrincipal.getName().equals(clientKDC1Name)) { |
133 } |
161 throw new Exception("Unexpected client subject principal."); |
134 if (tgt == null) { |
162 } |
135 throw new Exception("TGT is null"); |
163 |
136 } |
164 clientContext.startAsClient(serviceName, GSSUtil.GSS_KRB5_MECH_OID); |
137 if (!tgt.getClient().getName().equals(clientKDC2Name)) { |
165 clientContext.take(new byte[0]); |
138 throw new Exception("Unexpected TGT client"); |
166 Set<KerberosTicket> clientTickets = |
139 } |
167 clientSubject.getPrivateCredentials(KerberosTicket.class); |
140 String[] tgtServerNames = tgt.getServer().getNameStrings(); |
168 boolean tgtFound = false; |
141 if (tgtServerNames.length != 2 || !tgtServerNames[0].equals( |
169 boolean tgsFound = false; |
142 PrincipalName.TGS_DEFAULT_SRV_NAME) || |
170 for (KerberosTicket clientTicket : clientTickets) { |
143 !tgtServerNames[1].equals(realmKDC2) || |
171 String cname = clientTicket.getClient().getName(); |
144 !tgt.getServer().getRealmString().equals(realmKDC2)) { |
172 String sname = clientTicket.getServer().getName(); |
145 throw new Exception("Unexpected TGT server"); |
173 if (cname.equals(clientKDC2Name)) { |
146 } |
174 if (sname.equals(PrincipalName.TGS_DEFAULT_SRV_NAME + |
147 } |
175 PrincipalName.NAME_COMPONENT_SEPARATOR_STR + |
148 |
176 realmKDC2 + PrincipalName.NAME_REALM_SEPARATOR_STR + |
149 private static void getTGS() throws Exception { |
177 realmKDC2)) { |
150 tgs = CredentialsUtil.acquireServiceCreds(serviceName + |
178 tgtFound = true; |
151 PrincipalName.NAME_REALM_SEPARATOR_STR + realmKDC1, tgt); |
179 } else if (sname.equals(serviceKDC2Name)) { |
|
180 tgsFound = true; |
|
181 } |
|
182 } |
|
183 if (DEBUG) { |
|
184 System.out.println("Client subject KerberosTicket:"); |
|
185 System.out.println(clientTicket); |
|
186 } |
|
187 } |
|
188 if (!tgtFound || !tgsFound) { |
|
189 throw new Exception("client subject tickets (TGT/TGS) not found."); |
|
190 } |
|
191 int numOfTickets = clientTickets.size(); |
|
192 clientContext.startAsClient(serviceName, GSSUtil.GSS_KRB5_MECH_OID); |
|
193 clientContext.take(new byte[0]); |
|
194 clientContext.status(); |
|
195 int newNumOfTickets = |
|
196 clientSubject.getPrivateCredentials(KerberosTicket.class).size(); |
152 if (DEBUG) { |
197 if (DEBUG) { |
153 System.out.println("TGS"); |
198 System.out.println("client subject number of tickets: " + |
154 System.out.println("----------------------"); |
199 numOfTickets); |
155 System.out.println(tgs); |
200 System.out.println("client subject new number of tickets: " + |
156 System.out.println("----------------------"); |
201 newNumOfTickets); |
157 } |
202 } |
158 if (tgs == null) { |
203 if (numOfTickets != newNumOfTickets) { |
159 throw new Exception("TGS is null"); |
204 throw new Exception("Useless client subject TGS request because" + |
160 } |
205 " TGS was not found in private credentials."); |
161 if (!tgs.getClient().getName().equals(clientKDC2Name)) { |
206 } |
162 throw new Exception("Unexpected TGS client"); |
207 } |
163 } |
208 |
164 if (!tgs.getServer().getNameString().equals(serviceName) || |
209 /* |
165 !tgs.getServer().getRealmString().equals(realmKDC2)) { |
210 * The server (http/server.dev.rabbit.hole@DEV.RABBIT.HOLE) |
166 throw new Exception("Unexpected TGS server"); |
211 * will authenticate on itself on behalf of the client |
|
212 * (test@DEV.RABBIT.HOLE). Cross-realm referrals will occur |
|
213 * when requesting different TGTs and TGSs (including the |
|
214 * request for delegated credentials). |
|
215 */ |
|
216 private static void testDelegated() throws Exception { |
|
217 Context c = Context.fromUserPass(clientKDC2Name, |
|
218 password, false); |
|
219 c.startAsClient(serviceName, GSSUtil.GSS_KRB5_MECH_OID); |
|
220 Context s = Context.fromUserPass(serviceKDC2Name, |
|
221 password, true); |
|
222 s.startAsServer(GSSUtil.GSS_KRB5_MECH_OID); |
|
223 Context.handshake(c, s); |
|
224 Context delegatedContext = s.delegated(); |
|
225 delegatedContext.startAsClient(serviceName, GSSUtil.GSS_KRB5_MECH_OID); |
|
226 delegatedContext.x().requestMutualAuth(false); |
|
227 Context s2 = Context.fromUserPass(serviceKDC2Name, |
|
228 password, true); |
|
229 s2.startAsServer(GSSUtil.GSS_KRB5_MECH_OID); |
|
230 |
|
231 // Test authentication |
|
232 Context.handshake(delegatedContext, s2); |
|
233 if (!delegatedContext.x().isEstablished() || !s2.x().isEstablished()) { |
|
234 throw new Exception("Delegated authentication failed"); |
|
235 } |
|
236 |
|
237 // Test identities |
|
238 GSSName contextInitiatorName = delegatedContext.x().getSrcName(); |
|
239 GSSName contextAcceptorName = delegatedContext.x().getTargName(); |
|
240 if (DEBUG) { |
|
241 System.out.println("Context initiator: " + contextInitiatorName); |
|
242 System.out.println("Context acceptor: " + contextAcceptorName); |
|
243 } |
|
244 if (!contextInitiatorName.toString().equals(clientKDC2Name) || |
|
245 !contextAcceptorName.toString().equals(serviceName)) { |
|
246 throw new Exception("Unexpected initiator or acceptor names"); |
167 } |
247 } |
168 } |
248 } |
169 } |
249 } |