blob: 48293772b9d770d788759a9674a17ae50e6e8e18 [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Portions Copyright 2000-2007 Sun Microsystems, Inc. All Rights Reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26/*
27 *
28 * (C) Copyright IBM Corp. 1999 All Rights Reserved.
29 * Copyright 1997 The Open Group Research Institute. All rights reserved.
30 */
31
32package sun.security.krb5;
33
34import sun.security.krb5.internal.*;
35import sun.security.krb5.internal.ccache.CredentialsCache;
36import java.util.StringTokenizer;
37import sun.security.krb5.internal.ktab.*;
38import sun.security.krb5.internal.crypto.EType;
39import java.io.File;
40import java.io.IOException;
41import java.util.Date;
42import java.util.Vector;
43import java.io.BufferedReader;
44import java.io.InputStreamReader;
45import java.io.UnsupportedEncodingException;
46import java.net.InetAddress;
47
48/**
49 * This class encapsulates the concept of a Kerberos service
50 * credential. That includes a Kerberos ticket and an associated
51 * session key.
52 */
53public class Credentials {
54
55 Ticket ticket;
56 PrincipalName client;
57 PrincipalName server;
58 EncryptionKey key;
59 TicketFlags flags;
60 KerberosTime authTime;
61 KerberosTime startTime;
62 KerberosTime endTime;
63 KerberosTime renewTill;
64 HostAddresses cAddr;
65 EncryptionKey serviceKey;
66 private static boolean DEBUG = Krb5.DEBUG;
67 private static CredentialsCache cache;
68 static boolean alreadyLoaded = false;
69 private static boolean alreadyTried = false;
70 private static native Credentials acquireDefaultNativeCreds();
71
72 public Credentials(Ticket new_ticket,
73 PrincipalName new_client,
74 PrincipalName new_server,
75 EncryptionKey new_key,
76 TicketFlags new_flags,
77 KerberosTime authTime,
78 KerberosTime new_startTime,
79 KerberosTime new_endTime,
80 KerberosTime renewTill,
81 HostAddresses cAddr) {
82 ticket = new_ticket;
83 client = new_client;
84 server = new_server;
85 key = new_key;
86 flags = new_flags;
87 this.authTime = authTime;
88 startTime = new_startTime;
89 endTime = new_endTime;
90 this.renewTill = renewTill;
91 this.cAddr = cAddr;
92 }
93
94 public Credentials(byte[] encoding,
95 String client,
96 String server,
97 byte[] keyBytes,
98 int keyType,
99 boolean[] flags,
100 Date authTime,
101 Date startTime,
102 Date endTime,
103 Date renewTill,
104 InetAddress[] cAddrs) throws KrbException, IOException {
105 this(new Ticket(encoding),
106 new PrincipalName(client, PrincipalName.KRB_NT_PRINCIPAL),
107 new PrincipalName(server, PrincipalName.KRB_NT_SRV_INST),
108 new EncryptionKey(keyType, keyBytes),
109 (flags == null? null: new TicketFlags(flags)),
110 (authTime == null? null: new KerberosTime(authTime)),
111 (startTime == null? null: new KerberosTime(startTime)),
112 (endTime == null? null: new KerberosTime(endTime)),
113 (renewTill == null? null: new KerberosTime(renewTill)),
114 null); // caddrs are in the encoding at this point
115 }
116
117 /**
118 * Acquires a service ticket for the specified service
119 * principal. If the service ticket is not already available, it
120 * obtains a new one from the KDC.
121 */
122 /*
123 public Credentials(Credentials tgt, PrincipalName service)
124 throws KrbException {
125 }
126 */
127
128 public final PrincipalName getClient() {
129 return client;
130 }
131
132 public final PrincipalName getServer() {
133 return server;
134 }
135
136 public final EncryptionKey getSessionKey() {
137 return key;
138 }
139
140 public final Date getAuthTime() {
141 if (authTime != null) {
142 return authTime.toDate();
143 } else {
144 return null;
145 }
146 }
147
148 public final Date getStartTime() {
149 if (startTime != null)
150 {
151 return startTime.toDate();
152 }
153 return null;
154 }
155
156 public final Date getEndTime() {
157 if (endTime != null)
158 {
159 return endTime.toDate();
160 }
161 return null;
162 }
163
164 public final Date getRenewTill() {
165 if (renewTill != null)
166 {
167 return renewTill.toDate();
168 }
169 return null;
170 }
171
172 public final boolean[] getFlags() {
173 if (flags == null) // Can be in a KRB-CRED
174 return null;
175 return flags.toBooleanArray();
176 }
177
178 public final InetAddress[] getClientAddresses() {
179
180 if (cAddr == null)
181 return null;
182
183 return cAddr.getInetAddresses();
184 }
185
186 public final byte[] getEncoded() {
187 byte[] retVal = null;
188 try {
189 retVal = ticket.asn1Encode();
190 } catch (Asn1Exception e) {
191 if (DEBUG)
192 System.out.println(e);
193 } catch (IOException ioe) {
194 if (DEBUG)
195 System.out.println(ioe);
196 }
197 return retVal;
198 }
199
200 public boolean isForwardable() {
201 return flags.get(Krb5.TKT_OPTS_FORWARDABLE);
202 }
203
204 public boolean isRenewable() {
205 return flags.get(Krb5.TKT_OPTS_RENEWABLE);
206 }
207
208 public Ticket getTicket() {
209 return ticket;
210 }
211
212 public TicketFlags getTicketFlags() {
213 return flags;
214 }
215
216 /**
217 * Checks if the service ticket returned by the KDC has the OK-AS-DELEGATE
218 * flag set
219 * @return true if OK-AS_DELEGATE flag is set, otherwise, return false.
220 */
221 public boolean checkDelegate() {
222 return (flags.get(Krb5.TKT_OPTS_DELEGATE));
223 }
224
225 public Credentials renew() throws KrbException, IOException {
226 KDCOptions options = new KDCOptions();
227 options.set(KDCOptions.RENEW, true);
228 /*
229 * Added here to pass KrbKdcRep.check:73
230 */
231 options.set(KDCOptions.RENEWABLE, true);
232
233 return new KrbTgsReq(options,
234 this,
235 server,
236 null, // from
237 null, // till
238 null, // rtime
239 null, // eTypes
240 cAddr,
241 null,
242 null,
243 null).sendAndGetCreds();
244 }
245
246 /**
247 * Returns a TGT for the given client principal from a ticket cache.
248 *
249 * @param princ the client principal. A value of null means that the
250 * default principal name in the credentials cache will be used.
251 * @param ticketCache the path to the tickets file. A value
252 * of null will be accepted to indicate that the default
253 * path should be searched
254 * @returns the TGT credentials or null if none were found. If the tgt
255 * expired, it is the responsibility of the caller to determine this.
256 */
257 public static Credentials acquireTGTFromCache(PrincipalName princ,
258 String ticketCache)
259 throws KrbException, IOException {
260
261 if (ticketCache == null) {
262 // The default ticket cache on Windows is not a file.
263 String os = java.security.AccessController.doPrivileged(
264 new sun.security.action.GetPropertyAction("os.name"));
265 if (os.toUpperCase().startsWith("WINDOWS")) {
266 Credentials creds = acquireDefaultCreds();
267 if (creds == null) {
268 if (DEBUG) {
269 System.out.println(">>> Found no TGT's in LSA");
270 }
271 return null;
272 }
273 if (princ != null) {
274 if (creds.getClient().equals(princ)) {
275 if (DEBUG) {
276 System.out.println(">>> Obtained TGT from LSA: "
277 + creds);
278 }
279 return creds;
280 } else {
281 if (DEBUG) {
282 System.out.println(">>> LSA contains TGT for "
283 + creds.getClient()
284 + " not "
285 + princ);
286 }
287 return null;
288 }
289 } else {
290 if (DEBUG) {
291 System.out.println(">>> Obtained TGT from LSA: "
292 + creds);
293 }
294 return creds;
295 }
296 }
297 }
298
299 /*
300 * Returns the appropriate cache. If ticketCache is null, it is the
301 * default cache otherwise it is the cache filename contained in it.
302 */
303 CredentialsCache ccache =
304 CredentialsCache.getInstance(princ, ticketCache);
305
306 if (ccache == null)
307 return null;
308
309 sun.security.krb5.internal.ccache.Credentials tgtCred =
310 ccache.getDefaultCreds();
311
312 if (EType.isSupported(tgtCred.getEType())) {
313 return tgtCred.setKrbCreds();
314 } else {
315 if (DEBUG) {
316 System.out.println(
317 ">>> unsupported key type found the default TGT: " +
318 tgtCred.getEType());
319 }
320 return null;
321 }
322 }
323
324 /**
325 * Returns a TGT for the given client principal via an AS-Exchange.
326 * This method causes pre-authentication data to be sent in the
327 * AS-REQ.
328 *
329 * @param princ the client principal. This value cannot be null.
330 * @param secretKey the secret key of the client principal.This value
331 * cannot be null.
332 * @returns the TGT credentials
333 */
334 public static Credentials acquireTGT(PrincipalName princ,
335 EncryptionKey[] secretKeys,
336 char[] password)
337 throws KrbException, IOException {
338
339 if (princ == null)
340 throw new IllegalArgumentException(
341 "Cannot have null principal to do AS-Exchange");
342
343 if (secretKeys == null)
344 throw new IllegalArgumentException(
345 "Cannot have null secretKey to do AS-Exchange");
346
347 KrbAsRep asRep = null;
348 try {
349 asRep = sendASRequest(princ, secretKeys, null);
350 } catch (KrbException ke) {
351 if ((ke.returnCode() == Krb5.KDC_ERR_PREAUTH_FAILED) ||
352 (ke.returnCode() == Krb5.KDC_ERR_PREAUTH_REQUIRED)) {
353 // process pre-auth info
354 if (DEBUG) {
355 System.out.println("AcquireTGT: PREAUTH FAILED/REQUIRED," +
356 " re-send AS-REQ");
357 }
358
359 KRBError error = ke.getError();
360 // update salt in PrincipalName
361 byte[] newSalt = error.getSalt();
362 if (newSalt != null && newSalt.length > 0) {
363 princ.setSalt(new String(newSalt));
364 }
365
366 // refresh keys
367 if (password != null) {
368 secretKeys = EncryptionKey.acquireSecretKeys(password,
369 princ.getSalt(), true,
370 error.getEType(), error.getParams());
371 }
372 asRep = sendASRequest(princ, secretKeys, ke.getError());
373 } else {
374 throw ke;
375 }
376 }
377 return asRep.getCreds();
378 }
379
380 /**
381 * Sends the AS-REQ
382 */
383 private static KrbAsRep sendASRequest(PrincipalName princ,
384 EncryptionKey[] secretKeys, KRBError error)
385 throws KrbException, IOException {
386
387 // %%%
388 KrbAsReq asReq = null;
389 if (error == null) {
390 asReq = new KrbAsReq(princ, secretKeys);
391 } else {
392 asReq = new KrbAsReq(princ, secretKeys, true,
393 error.getEType(), error.getSalt(), error.getParams());
394 }
395
396 String kdc = null;
397 KrbAsRep asRep = null;
398 try {
399 kdc = asReq.send();
400 asRep = asReq.getReply(secretKeys);
401 } catch (KrbException ke) {
402 if (ke.returnCode() == Krb5.KRB_ERR_RESPONSE_TOO_BIG) {
403 asReq.send(princ.getRealmString(), kdc, true);
404 asRep = asReq.getReply(secretKeys);
405 } else {
406 throw ke;
407 }
408 }
409 return asRep;
410 }
411
412 /**
413 * Acquires default credentials.
414 * <br>The possible locations for default credentials cache is searched in
415 * the following order:
416 * <ol>
417 * <li> The directory and cache file name specified by "KRB5CCNAME" system.
418 * property.
419 * <li> The directory and cache file name specified by "KRB5CCNAME"
420 * environment variable.
421 * <li> A cache file named krb5cc_{user.name} at {user.home} directory.
422 * </ol>
423 * @return a <code>KrbCreds</code> object if the credential is found,
424 * otherwise return null.
425 */
426
427 // this method is intentionally changed to not check if the caller's
428 // principal name matches cache file's principal name.
429 // It assumes that the GSS call has
430 // the privilege to access the default cache file.
431
432 public static synchronized Credentials acquireDefaultCreds() {
433 Credentials result = null;
434
435 if (cache == null) {
436 cache = CredentialsCache.getInstance();
437 }
438 if (cache != null) {
439 if (DEBUG) {
440 System.out.println(">>> KrbCreds found the default ticket " +
441 "granting ticket in credential cache.");
442 }
443 sun.security.krb5.internal.ccache.Credentials temp =
444 cache.getDefaultCreds();
445 if (EType.isSupported(temp.getEType())) {
446 result = temp.setKrbCreds();
447 } else {
448 if (DEBUG) {
449 System.out.println(
450 ">>> unsupported key type found the default TGT: " +
451 temp.getEType());
452 }
453 }
454 }
455 if (result == null) {
456 // Doesn't seem to be a default cache on this system or
457 // TGT has unsupported encryption type
458
459 if (!alreadyTried) {
460 // See if there's any native code to load
461 try {
462 ensureLoaded();
463 } catch (Exception e) {
464 if (DEBUG) {
465 System.out.println("Can not load credentials cache");
466 e.printStackTrace();
467 }
468 alreadyTried = true;
469 }
470 }
471 if (alreadyLoaded) {
472 // There is some native code
473 if (DEBUG)
474 System.out.println(">> Acquire default native Credentials");
475 result = acquireDefaultNativeCreds();
476 // only TGT with DES key will be returned by native method
477 }
478 }
479 return result;
480 }
481
482
483 /**
484 * Gets service credential from key table. The credential is used to
485 * decrypt the received client message
486 * and authenticate the client by verifying the client's credential.
487 *
488 * @param serviceName the name of service, using format component@realm
489 * @param keyTabFile the file of key table.
490 * @return a <code>KrbCreds</code> object.
491 */
492 public static Credentials getServiceCreds(String serviceName,
493 File keyTabFile) {
494 EncryptionKey k = null;
495 PrincipalName service = null;
496 Credentials result = null;
497 try {
498 service = new PrincipalName(serviceName);
499 if (service.getRealm() == null) {
500 String realm = Config.getInstance().getDefaultRealm();
501 if (realm == null) {
502 return null;
503 } else {
504 service.setRealm(realm);
505 }
506 }
507 } catch (RealmException e) {
508 if (DEBUG) {
509 e.printStackTrace();
510 }
511 return null;
512 } catch (KrbException e) {
513 if (DEBUG) {
514 e.printStackTrace();
515 }
516 return null;
517 }
518 KeyTab kt;
519 if (keyTabFile == null) {
520 kt = KeyTab.getInstance();
521 } else {
522 kt = KeyTab.getInstance(keyTabFile);
523 }
524 if ((kt != null) && (kt.findServiceEntry(service))) {
525 k = kt.readServiceKey(service);
526 result = new Credentials(null, service, null, null, null,
527 null, null, null, null, null);
528 result.serviceKey = k;
529 }
530 return result;
531 }
532
533
534
535 /**
536 * Acquires credentials for a specified service using initial credential.
537 * When the service has a different realm
538 * from the initial credential, we do cross-realm authentication
539 * - first, we use the current credential to get
540 * a cross-realm credential from the local KDC, then use that
541 * cross-realm credential to request service credential
542 * from the foreigh KDC.
543 *
544 * @param service the name of service principal using format
545 * components@realm
546 * @param ccreds client's initial credential.
547 * @exception IOException if an error occurs in reading the credentials
548 * cache
549 * @exception KrbException if an error occurs specific to Kerberos
550 * @return a <code>Credentials</code> object.
551 */
552
553 public static Credentials acquireServiceCreds(String service,
554 Credentials ccreds)
555 throws KrbException, IOException {
556 return CredentialsUtil.acquireServiceCreds(service, ccreds);
557 }
558
559
560 /*
561 * This method does the real job to request the service credential.
562 */
563
564 private static Credentials serviceCreds(ServiceName service,
565 Credentials ccreds)
566 throws KrbException, IOException {
567 return new KrbTgsReq(
568 new KDCOptions(),
569 ccreds,
570 service,
571 null, // KerberosTime from
572 null, // KerberosTime till
573 null, // KerberosTime rtime
574 null, // int[] eTypes
575 null, // HostAddresses addresses
576 null, // AuthorizationData
577 null, // Ticket[] additionalTickets
578 null // EncryptionKey subSessionKey
579 ).sendAndGetCreds();
580 }
581
582 public CredentialsCache getCache() {
583 return cache;
584 }
585
586 public EncryptionKey getServiceKey() {
587 return serviceKey;
588 }
589
590 /*
591 * Prints out debug info.
592 */
593 public static void printDebug(Credentials c) {
594 System.out.println(">>> DEBUG: ----Credentials----");
595 System.out.println("\tclient: " + c.client.toString());
596 System.out.println("\tserver: " + c.server.toString());
597 System.out.println("\tticket: realm: " + c.ticket.realm.toString());
598 System.out.println("\t sname: " + c.ticket.sname.toString());
599 if (c.startTime != null) {
600 System.out.println("\tstartTime: " + c.startTime.getTime());
601 }
602 System.out.println("\tendTime: " + c.endTime.getTime());
603 System.out.println(" ----Credentials end----");
604 }
605
606
607 static void ensureLoaded() {
608 java.security.AccessController.doPrivileged(
609 new java.security.PrivilegedAction<Void> () {
610 public Void run() {
611 System.loadLibrary("w2k_lsa_auth");
612 return null;
613 }
614 });
615 alreadyLoaded = true;
616 }
617
618 public String toString() {
619 StringBuffer buffer = new StringBuffer("Credentials:");
620 buffer.append("\nclient=").append(client);
621 buffer.append("\nserver=").append(server);
622 if (authTime != null) {
623 buffer.append("\nauthTime=").append(authTime);
624 }
625 if (startTime != null) {
626 buffer.append("\nstartTime=").append(startTime);
627 }
628 buffer.append("\nendTime=").append(endTime);
629 buffer.append("\nrenewTill=").append(renewTill);
630 buffer.append("\nflags: ").append(flags);
631 buffer.append("\nEType (int): ").append(key.getEType());
632 return buffer.toString();
633 }
634
635}