KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > javax > security > auth > kerberos > KerberosTicket


1 /*
2  * @(#)KerberosTicket.java 1.17 06/06/01
3  *
4  * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
5  * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
6  */

7   
8 package javax.security.auth.kerberos;
9
10 import java.io.*;
11 import java.util.Date JavaDoc;
12 import java.util.Arrays JavaDoc;
13 import java.net.InetAddress JavaDoc;
14 import javax.crypto.SecretKey;
15 import javax.security.auth.Refreshable JavaDoc;
16 import javax.security.auth.Destroyable JavaDoc;
17 import javax.security.auth.RefreshFailedException JavaDoc;
18 import javax.security.auth.DestroyFailedException JavaDoc;
19 import sun.misc.HexDumpEncoder;
20 import sun.security.krb5.EncryptionKey;
21 import sun.security.krb5.Asn1Exception;
22 import sun.security.util.*;
23
24 /**
25  * This class encapsulates a Kerberos ticket and associated
26  * information as viewed from the client's point of view. It captures all
27  * information that the Key Distribution Center (KDC) sends to the client
28  * in the reply message KDC-REP defined in the Kerberos Protocol
29  * Specification (<a HREF=http://www.ietf.org/rfc/rfc1510.txt>RFC 1510</a>).
30  * <p>
31  * All Kerberos JAAS login modules that authenticate a user to a KDC should
32  * use this class. Where available, the login module might even read this
33  * information from a ticket cache in the operating system instead of
34  * directly communicating with the KDC. During the commit phase of the JAAS
35  * authentication process, the JAAS login module should instantiate this
36  * class and store the instance in the private credential set of a
37  * {@link javax.security.auth.Subject Subject}.<p>
38  *
39  * It might be necessary for the application to be granted a
40  * {@link javax.security.auth.PrivateCredentialPermission
41  * PrivateCredentialPermission} if it needs to access a KerberosTicket
42  * instance from a Subject. This permission is not needed when the
43  * application depends on the default JGSS Kerberos mechanism to access the
44  * KerberosTicket. In that case, however, the application will need an
45  * appropriate
46  * {@link javax.security.auth.kerberos.ServicePermission ServicePermission}.
47  * <p>
48  * Note that this class is applicable to both ticket granting tickets and
49  * other regular service tickets. A ticket granting ticket is just a
50  * special case of a more generalized service ticket.
51  *
52  * @see javax.security.auth.Subject
53  * @see javax.security.auth.PrivateCredentialPermission
54  * @see javax.security.auth.login.LoginContext
55  * @see org.ietf.jgss.GSSCredential
56  * @see org.ietf.jgss.GSSManager
57  *
58  * @author Mayank Upadhyay
59  * @version 1.17, 06/01/06
60  * @since 1.4
61  */

62 public class KerberosTicket implements Destroyable JavaDoc, Refreshable JavaDoc,
63      java.io.Serializable JavaDoc {
64
65     private static final long serialVersionUID = 7395334370157380539L;
66
67     // TBD: Make these flag indices public
68
private static final int FORWARDABLE_TICKET_FLAG = 1;
69     private static final int FORWARDED_TICKET_FLAG = 2;
70     private static final int PROXIABLE_TICKET_FLAG = 3;
71     private static final int PROXY_TICKET_FLAG = 4;
72     private static final int POSTDATED_TICKET_FLAG = 6;
73     private static final int RENEWABLE_TICKET_FLAG = 8;
74     private static final int INITIAL_TICKET_FLAG = 9;
75
76     private static final int NUM_FLAGS = 32;
77
78     /**
79      *
80      * ASN.1 DER Encoding of the Ticket as defined in the
81      * Kerberos Protocol Specification RFC1510.
82      *
83      * @serial
84      */

85
86     private byte[] asn1Encoding;
87
88     /**
89      *<code>KeyImpl</code> is serialized by writing out the ASN1 Encoded bytes
90      * of the encryption key. The ASN1 encoding is defined in RFC1510 and as
91      * follows:
92      * <pre>
93      * EncryptionKey ::= SEQUENCE {
94      * keytype[0] INTEGER,
95      * keyvalue[1] OCTET STRING
96      * }
97      * </pre>
98      *
99      * @serial
100      */

101
102     private KeyImpl JavaDoc sessionKey;
103
104     /**
105      *
106      * Ticket Flags as defined in the Kerberos Protocol Specification RFC1510.
107      *
108      * @serial
109      */

110
111     private boolean[] flags;
112
113     /**
114      *
115      * Time of initial authentication
116      *
117      * @serial
118      */

119
120     private Date JavaDoc authTime;
121
122     /**
123      *
124      * Time after which the ticket is valid.
125      * @serial
126      */

127     private Date JavaDoc startTime;
128
129     /**
130      *
131      * Time after which the ticket will not be honored. (its expiration time).
132      *
133      * @serial
134      */

135
136     private Date JavaDoc endTime;
137
138     /**
139      *
140      * For renewable Tickets it indicates the maximum endtime that may be
141      * included in a renewal. It can be thought of as the absolute expiration
142      * time for the ticket, including all renewals. This field may be null
143      * for tickets that are not renewable.
144      *
145      * @serial
146      */

147
148     private Date JavaDoc renewTill;
149
150     /**
151      *
152      * Client that owns the service ticket
153      *
154      * @serial
155      */

156
157     private KerberosPrincipal JavaDoc client;
158
159     /**
160      *
161      * The service for which the ticket was issued.
162      *
163      * @serial
164      */

165
166     private KerberosPrincipal JavaDoc server;
167     
168     /**
169      *
170      * The addresses from where the ticket may be used by the client.
171      * This field may be null when the ticket is usable from any address.
172      *
173      * @serial
174      */

175
176
177     private InetAddress JavaDoc[] clientAddresses;
178
179     private transient boolean destroyed = false;
180
181     /**
182      * Constructs a KerberosTicket using credentials information that a
183      * client either receives from a KDC or reads from a cache.
184      *
185      * @param asn1Encoding the ASN.1 encoding of the ticket as defined by
186      * the Kerberos protocol specification.
187      * @param client the client that owns this service
188      * ticket
189      * @param server the service that this ticket is for
190      * @param sessionKey the raw bytes for the session key that must be
191      * used to encrypt the authenticator that will be sent to the server
192      * @param keyType the key type for the session key as defined by the
193      * Kerberos protocol specification.
194      * @param flags the ticket flags. Each element in this array indicates
195      * the value for the corresponding bit in the ASN.1 BitString that
196      * represents the ticket flags. If the number of elements in this array
197      * is less than the number of flags used by the Kerberos protocol,
198      * then the missing flags will be filled in with false.
199      * @param authTime the time of initial authentication for the client
200      * @param startTime the time after which the ticket will be valid. This
201      * may be null in which case the value of authTime is treated as the
202      * startTime.
203      * @param endTime the time after which the ticket will no longer be
204      * valid
205      * @param renewTill an absolute expiration time for the ticket,
206      * including all renewal that might be possible. This field may be null
207      * for tickets that are not renewable.
208      * @param clientAddresses the addresses from where the ticket may be
209      * used by the client. This field may be null when the ticket is usable
210      * from any address.
211      */

212     public KerberosTicket(byte[] asn1Encoding,
213              KerberosPrincipal JavaDoc client,
214              KerberosPrincipal JavaDoc server,
215              byte[] sessionKey,
216              int keyType,
217              boolean[] flags,
218              Date JavaDoc authTime,
219              Date JavaDoc startTime,
220              Date JavaDoc endTime,
221              Date JavaDoc renewTill,
222              InetAddress JavaDoc[] clientAddresses) {
223        
224     init(asn1Encoding, client, server, sessionKey, keyType, flags,
225         authTime, startTime, endTime, renewTill, clientAddresses);
226     }
227     
228     private void init(byte[] asn1Encoding,
229              KerberosPrincipal JavaDoc client,
230              KerberosPrincipal JavaDoc server,
231              byte[] sessionKey,
232              int keyType,
233              boolean[] flags,
234              Date JavaDoc authTime,
235              Date JavaDoc startTime,
236              Date JavaDoc endTime,
237              Date JavaDoc renewTill,
238              InetAddress JavaDoc[] clientAddresses) {
239
240     if (asn1Encoding == null)
241        throw new IllegalArgumentException JavaDoc("ASN.1 encoding of ticket"
242                           + " cannot be null");
243     this.asn1Encoding = asn1Encoding.clone();
244
245     if (client == null)
246        throw new IllegalArgumentException JavaDoc("Client name in ticket"
247                           + " cannot be null");
248     this.client = client;
249
250     if (server == null)
251        throw new IllegalArgumentException JavaDoc("Server name in ticket"
252                           + " cannot be null");
253     this.server = server;
254
255     if (sessionKey == null)
256        throw new IllegalArgumentException JavaDoc("Session key for ticket"
257                           + " cannot be null");
258     this.sessionKey = new KeyImpl JavaDoc(sessionKey, keyType);
259
260     if (flags != null) {
261        if (flags.length >= NUM_FLAGS)
262         this.flags = (boolean[]) flags.clone();
263        else {
264         this.flags = new boolean[NUM_FLAGS];
265         // Fill in whatever we have
266
for (int i = 0; i < flags.length; i++)
267             this.flags[i] = flags[i];
268        }
269     } else
270        this.flags = new boolean[NUM_FLAGS];
271
272     if (this.flags[RENEWABLE_TICKET_FLAG]) {
273        if (renewTill == null)
274         throw new IllegalArgumentException JavaDoc("The renewable period "
275                + "end time cannot be null for renewable tickets.");
276
277        this.renewTill = renewTill;
278     }
279
280     this.authTime = authTime;
281
282     this.startTime = (startTime != null? startTime: authTime);
283
284     if (endTime == null)
285        throw new IllegalArgumentException JavaDoc("End time for ticket validity"
286                           + " cannot be null");
287     this.endTime = endTime;
288
289     if (clientAddresses != null)
290        this.clientAddresses = (InetAddress JavaDoc[]) clientAddresses.clone();
291     }
292
293     /**
294      * Returns the client principal associated with this ticket.
295      *
296      * @return the client principal.
297      */

298     public final KerberosPrincipal JavaDoc getClient() {
299     return client;
300     }
301     
302     /**
303      * Returns the service principal associated with this ticket.
304      *
305      * @return the service principal.
306      */

307     public final KerberosPrincipal JavaDoc getServer() {
308     return server;
309     }
310     
311     /**
312      * Returns the session key associated with this ticket.
313      *
314      * @return the session key.
315      */

316     public final SecretKey getSessionKey() {
317     if (destroyed)
318         throw new IllegalStateException JavaDoc("This ticket is no longer valid");
319     return sessionKey;
320     }
321
322     /**
323      * Returns the key type of the session key associated with this
324      * ticket as defined by the Kerberos Protocol Specification.
325      *
326      * @return the key type of the session key associated with this
327      * ticket.
328      *
329      * @see #getSessionKey()
330      */

331     public final int getSessionKeyType() {
332     if (destroyed)
333         throw new IllegalStateException JavaDoc("This ticket is no longer valid");
334     return sessionKey.getKeyType();
335     }
336
337     /**
338      * Determines if this ticket is forwardable.
339      *
340      * @return true if this ticket is forwardable, false if not.
341      */

342     public final boolean isForwardable() {
343     return flags[FORWARDABLE_TICKET_FLAG];
344     }
345
346     /**
347      * Determines if this ticket had been forwarded or was issued based on
348      * authentication involving a forwarded ticket-granting ticket.
349      *
350      * @return true if this ticket had been forwarded or was issued based on
351      * authentication involving a forwarded ticket-granting ticket,
352      * false otherwise.
353      */

354     public final boolean isForwarded() {
355     return flags[FORWARDED_TICKET_FLAG];
356     }
357
358     /**
359      * Determines if this ticket is proxiable.
360      *
361      * @return true if this ticket is proxiable, false if not.
362      */

363     public final boolean isProxiable() {
364     return flags[PROXIABLE_TICKET_FLAG];
365     }
366
367     /**
368      * Determines is this ticket is a proxy-ticket.
369      *
370      * @return true if this ticket is a proxy-ticket, false if not.
371      */

372     public final boolean isProxy() {
373     return flags[PROXY_TICKET_FLAG];
374     }
375
376
377     /**
378      * Determines is this ticket is post-dated.
379      *
380      * @return true if this ticket is post-dated, false if not.
381      */

382     public final boolean isPostdated() {
383     return flags[POSTDATED_TICKET_FLAG];
384     }
385
386     /**
387      * Determines is this ticket is renewable. If so, the {@link #refresh()
388      * refresh} method can be called, assuming the validity period for
389      * renewing is not already over.
390      *
391      * @return true if this ticket is renewable, false if not.
392      */

393     public final boolean isRenewable() {
394     return flags[RENEWABLE_TICKET_FLAG];
395     }
396
397     /**
398      * Determines if this ticket was issued using the Kerberos AS-Exchange
399      * protocol, and not issued based on some ticket-granting ticket.
400      *
401      * @return true if this ticket was issued using the Kerberos AS-Exchange
402      * protocol, false if not.
403      */

404     public final boolean isInitial() {
405     return flags[INITIAL_TICKET_FLAG];
406     }
407
408     /**
409      * Returns the flags associated with this ticket. Each element in the
410      * returned array indicates the value for the corresponding bit in the
411      * ASN.1 BitString that represents the ticket flags.
412      *
413      * @return the flags associated with this ticket.
414      */

415     public final boolean[] getFlags() {
416     return (flags == null? null: (boolean[]) flags.clone());
417     }
418
419     /**
420      * Returns the time that the client was authenticated.
421      *
422      * @return the time that the client was authenticated
423      * or null if not set.
424      */

425     public final java.util.Date JavaDoc getAuthTime() {
426     return (authTime == null) ? null : new Date JavaDoc(authTime.getTime());
427     }
428
429     /**
430      * Returns the start time for this ticket's validity period.
431      *
432      * @return the start time for this ticket's validity period
433      * or null if not set.
434      */

435     public final java.util.Date JavaDoc getStartTime() {
436     return (startTime == null) ? null : new Date JavaDoc(startTime.getTime());
437     }
438
439     /**
440      * Returns the expiration time for this ticket's validity period.
441      *
442      * @return the expiration time for this ticket's validity period.
443      */

444     public final java.util.Date JavaDoc getEndTime() {
445     return endTime;
446     }
447
448     /**
449      * Returns the latest expiration time for this ticket, including all
450      * renewals. This will return a null value for non-renewable tickets.
451      *
452      * @return the latest expiration time for this ticket.
453      */

454     public final java.util.Date JavaDoc getRenewTill() {
455     return (renewTill == null) ? null: new Date JavaDoc(renewTill.getTime());
456     }
457     
458     /**
459      * Returns a list of addresses from where the ticket can be used.
460      *
461      * @return ths list of addresses or null, if the field was not
462      * provided.
463      */

464     public final java.net.InetAddress JavaDoc[] getClientAddresses() {
465     return (clientAddresses == null?
466         null: (InetAddress JavaDoc[]) clientAddresses.clone());
467     }
468
469     /**
470      * Returns an ASN.1 encoding of the entire ticket.
471      *
472      * @return an ASN.1 encoding of the entire ticket.
473      */

474     public final byte[] getEncoded() {
475     if (destroyed)
476         throw new IllegalStateException JavaDoc("This ticket is no longer valid");
477     return (byte[]) asn1Encoding.clone();
478     }
479
480     /** Determines if this ticket is still current. */
481     public boolean isCurrent() {
482     return (System.currentTimeMillis() <= getEndTime().getTime());
483     }
484
485     /**
486      * Extends the validity period of this ticket. The ticket will contain
487      * a new session key if the refresh operation succeeds. The refresh
488      * operation will fail if the ticket is not renewable or the latest
489      * allowable renew time has passed. Any other error returned by the
490      * KDC will also cause this method to fail.
491      *
492      * Note: This method is not synchronized with the the accessor
493      * methods of this object. Hence callers need to be aware of multiple
494      * threads that might access this and try to renew it at the same
495      * time.
496      *
497      * @throws RefreshFailedException if the ticket is not renewable, or
498      * the latest allowable renew time has passed, or the KDC returns some
499      * error.
500      *
501      * @see #isRenewable()
502      * @see #getRenewTill()
503      */

504     public void refresh() throws RefreshFailedException JavaDoc {
505
506     if (destroyed)
507         throw new RefreshFailedException JavaDoc("A destroyed ticket "
508                          + "cannot be renewd.");
509
510     if (!isRenewable())
511         throw new RefreshFailedException JavaDoc("This ticket is not renewable");
512
513     if (System.currentTimeMillis() > getRenewTill().getTime())
514         throw new RefreshFailedException JavaDoc("This ticket is past "
515                          + "its last renewal time.");
516     Throwable JavaDoc e = null;
517     sun.security.krb5.Credentials krb5Creds = null;
518
519     try {
520         krb5Creds = new sun.security.krb5.Credentials(asn1Encoding,
521                             client.toString(),
522                             server.toString(),
523                             sessionKey.getEncoded(),
524                             sessionKey.getKeyType(),
525                             flags,
526                             authTime,
527                             startTime,
528                             endTime,
529                             renewTill,
530                             clientAddresses);
531         krb5Creds = krb5Creds.renew();
532     } catch (sun.security.krb5.KrbException krbException) {
533         e = krbException;
534     } catch (java.io.IOException JavaDoc ioException) {
535         e = ioException;
536     }
537
538     if (e != null) {
539         RefreshFailedException JavaDoc rfException
540         = new RefreshFailedException JavaDoc("Failed to renew Kerberos Ticket "
541                          + "for client " + client
542                          + " and server " + server
543                          + " - " + e.getMessage());
544         rfException.initCause(e);
545         throw rfException;
546     }
547
548     /*
549      * In case multiple threads try to refresh it at the same time.
550      */

551     synchronized (this) {
552         try {
553         this.destroy();
554         } catch (DestroyFailedException JavaDoc dfException) {
555         // Squelch it since we don't care about the old ticket.
556
}
557         init(krb5Creds.getEncoded(),
558          new KerberosPrincipal JavaDoc(krb5Creds.getClient().getName()),
559          new KerberosPrincipal JavaDoc(krb5Creds.getServer().getName()),
560          krb5Creds.getSessionKey().getBytes(),
561          krb5Creds.getSessionKey().getEType(),
562          krb5Creds.getFlags(),
563          krb5Creds.getAuthTime(),
564          krb5Creds.getStartTime(),
565          krb5Creds.getEndTime(),
566          krb5Creds.getRenewTill(),
567          krb5Creds.getClientAddresses());
568         destroyed = false;
569     }
570     }
571
572     /**
573      * Destroys the ticket and destroys any sensitive information stored in
574      * it.
575      */

576     public void destroy() throws DestroyFailedException JavaDoc {
577     if (!destroyed) {
578         Arrays.fill(asn1Encoding, (byte) 0);
579         client = null;
580         server = null;
581         sessionKey.destroy();
582         flags = null;
583         authTime = null;
584         startTime = null;
585         endTime = null;
586         renewTill = null;
587         clientAddresses = null;
588         destroyed = true;
589     }
590     }
591
592     /**
593      * Determines if this ticket has been destroyed.
594      */

595     public boolean isDestroyed() {
596     return destroyed;
597     }
598
599     public String JavaDoc toString() {
600     if (destroyed)
601         throw new IllegalStateException JavaDoc("This ticket is no longer valid");
602     StringBuffer JavaDoc caddrBuf = new StringBuffer JavaDoc();
603     if (clientAddresses != null) {
604         for (int i = 0; i < clientAddresses.length; i++) {
605         caddrBuf.append("clientAddresses[" + i + "] = " +
606                  clientAddresses[i].toString());
607         }
608     }
609     return ("Ticket (hex) = " + "\n" +
610          (new HexDumpEncoder()).encode(asn1Encoding) + "\n" +
611         "Client Principal = " + client.toString() + "\n" +
612         "Server Principal = " + server.toString() + "\n" +
613         "Session Key = " + sessionKey.toString() + "\n" +
614         "Forwardable Ticket " + flags[FORWARDABLE_TICKET_FLAG] + "\n" +
615         "Forwarded Ticket " + flags[FORWARDED_TICKET_FLAG] + "\n" +
616             "Proxiable Ticket " + flags[PROXIABLE_TICKET_FLAG] + "\n" +
617             "Proxy Ticket " + flags[PROXY_TICKET_FLAG] + "\n" +
618             "Postdated Ticket " + flags[POSTDATED_TICKET_FLAG] + "\n" +
619             "Renewable Ticket " + flags[RENEWABLE_TICKET_FLAG] + "\n" +
620             "Initial Ticket " + flags[RENEWABLE_TICKET_FLAG] + "\n" +
621         "Auth Time = " + String.valueOf(authTime) + "\n" +
622         "Start Time = " + String.valueOf(startTime) + "\n" +
623         "End Time = " + endTime.toString() + "\n" +
624         "Renew Till = " + String.valueOf(renewTill) + "\n" +
625         "Client Addresses " +
626         (clientAddresses == null ? " Null " : caddrBuf.toString() +
627         "\n"));
628     }
629
630 }
631
Popular Tags