001    /* KerberosTicket.java -- a kerberos ticket
002       Copyright (C) 2006 Free Software Foundation, Inc.
003    
004    This file is part of GNU Classpath.
005    
006    GNU Classpath is free software; you can redistribute it and/or modify
007    it under the terms of the GNU General Public License as published by
008    the Free Software Foundation; either version 2, or (at your option)
009    any later version.
010    
011    GNU Classpath is distributed in the hope that it will be useful, but
012    WITHOUT ANY WARRANTY; without even the implied warranty of
013    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014    General Public License for more details.
015    
016    You should have received a copy of the GNU General Public License
017    along with GNU Classpath; see the file COPYING.  If not, write to the
018    Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
019    02110-1301 USA.
020    
021    Linking this library statically or dynamically with other modules is
022    making a combined work based on this library.  Thus, the terms and
023    conditions of the GNU General Public License cover the whole
024    combination.
025    
026    As a special exception, the copyright holders of this library give you
027    permission to link this library with independent modules to produce an
028    executable, regardless of the license terms of these independent
029    modules, and to copy and distribute the resulting executable under
030    terms of your choice, provided that you also meet, for each linked
031    independent module, the terms and conditions of the license of that
032    module.  An independent module is a module which is not derived from
033    or based on this library.  If you modify this library, you may extend
034    this exception to your version of the library, but you are not
035    obligated to do so.  If you do not wish to do so, delete this
036    exception statement from your version. */
037    
038    
039    package javax.security.auth.kerberos;
040    
041    import gnu.classpath.NotImplementedException;
042    
043    import java.io.Serializable;
044    import java.net.InetAddress;
045    import java.util.Date;
046    
047    import javax.crypto.SecretKey;
048    import javax.security.auth.DestroyFailedException;
049    import javax.security.auth.Destroyable;
050    import javax.security.auth.RefreshFailedException;
051    import javax.security.auth.Refreshable;
052    
053    /**
054     * This class represents a Kerberos ticket.  See the Kerberos
055     * authentication RFC for more information:
056     * <a href="http://www.ietf.org/rfc/rfc1510.txt">RFC 1510</a>.
057     *
058     * @since 1.4
059     */
060    public class KerberosTicket
061        implements Destroyable, Serializable, Refreshable
062    {
063      private static final long serialVersionUID = 7395334370157380539L;
064    
065      // Indices of the various flags.  From the kerberos spec.
066      // We only list the ones we use.
067      private static final int FORWARDABLE = 1;
068      private static final int FORWARDED = 2;
069      private static final int PROXIABLE = 3;
070      private static final int PROXY = 4;
071      private static final int POSTDATED = 6;
072      private static final int RENEWABLE = 8;
073      private static final int INITIAL = 9;
074      private static final int NUM_FLAGS = 12;
075    
076      private byte[] asn1Encoding;
077      private KeyImpl sessionKey;
078      private boolean[] flags;
079      private Date authTime;
080      private Date startTime;
081      private Date endTime;
082      private Date renewTill;
083      private KerberosPrincipal client;
084      private KerberosPrincipal server;
085      private InetAddress[] clientAddresses;
086    
087      /**
088       * Create a new ticket given all the facts about it.
089       *
090       * Note that flags may be null or "short"; any flags not specified
091       * will be taken to be false.
092       *
093       * If the key is not renewable, then renewTill may be null.
094       *
095       * If authTime is null, then it is taken to be the same as startTime.
096       *
097       * If clientAddresses is null, then the ticket can be used anywhere.
098       *
099       * @param asn1Encoding the contents of the ticket, as ASN1
100       * @param client the client principal
101       * @param server the server principal
102       * @param key the contents of the session key
103       * @param type the type of the key
104       * @param flags an array of flags, as specified by the RFC
105       * @param authTime when the client was authenticated
106       * @param startTime starting time at which the ticket is valid
107       * @param endTime ending time, after which the ticket is invalid
108       * @param renewTill for a rewewable ticket, the time before which it must
109       * be renewed
110       * @param clientAddresses a possibly-null array of addresses where this
111       * ticket may be used
112       */
113      public KerberosTicket(byte[] asn1Encoding, KerberosPrincipal client,
114                            KerberosPrincipal server, byte[] key, int type,
115                            boolean[] flags, Date authTime, Date startTime,
116                            Date endTime, Date renewTill,
117                            InetAddress[] clientAddresses)
118      {
119        this.asn1Encoding = (byte[]) asn1Encoding.clone();
120        this.sessionKey = new KeyImpl(key, type);
121        this.flags = new boolean[NUM_FLAGS];
122        if (flags != null)
123          System.arraycopy(flags, 0, this.flags, 0,
124                           Math.min(flags.length, NUM_FLAGS));
125        this.flags = (boolean[]) flags.clone();
126        this.authTime = (Date) authTime.clone();
127        this.startTime = (Date) ((startTime == null)
128                                  ? authTime : startTime).clone();
129        this.endTime = (Date) endTime.clone();
130        this.renewTill = (Date) renewTill.clone();
131        this.client = client;
132        this.server = server;
133        this.clientAddresses = (clientAddresses == null
134                                ? null
135                                : (InetAddress[]) clientAddresses.clone());
136      }
137    
138      /**
139       * Destroy this ticket.  This discards secret information.  After this
140       * method is called, other methods will throw IllegalStateException.
141       */
142      public void destroy() throws DestroyFailedException
143      {
144        if (sessionKey == null)
145          throw new DestroyFailedException("already destroyed");
146        sessionKey = null;
147        asn1Encoding = null;
148      }
149    
150      /**
151       * Return true if this ticket has been destroyed.
152       */
153      public boolean isDestroyed()
154      {
155        return sessionKey == null;
156      }
157    
158      /**
159       * Return true if the ticket is currently valid.  This is true if
160       * the system time is between the ticket's start and end times.
161       */
162      public boolean isCurrent()
163      {
164        long now = System.currentTimeMillis();
165        return startTime.getTime() <= now && now <= endTime.getTime();
166      }
167    
168      /**
169       * If the ticket is renewable, and the renewal time has not yet elapsed,
170       * attempt to renew the ticket.
171       * @throws RefreshFailedException if the renewal fails for any reason
172       */
173      public void refresh() throws RefreshFailedException, NotImplementedException
174      {
175        if (! isRenewable())
176          throw new RefreshFailedException("not renewable");
177        if (renewTill != null
178            && System.currentTimeMillis() >= renewTill.getTime())
179          throw new RefreshFailedException("renewal time elapsed");
180        // FIXME: must contact the KDC.
181        // Use the java.security.krb5.kdc property...
182        throw new RefreshFailedException("not implemented");
183      }
184    
185      /**
186       * Return the client principal for this ticket.
187       */
188      public final KerberosPrincipal getClient()
189      {
190        return client;
191      }
192    
193      /**
194       * Return the server principal for this ticket.
195       */
196      public final KerberosPrincipal getServer()
197      {
198        return server;
199      }
200    
201      /**
202       * Return true if this ticket is forwardable.
203       */
204      public final boolean isForwardable()
205      {
206        return flags[FORWARDABLE];
207      }
208    
209      /**
210       * Return true if this ticket has been forwarded.
211       */
212      public final boolean isForwarded()
213      {
214        return flags[FORWARDED];
215      }
216    
217      /**
218       * Return true if this ticket is proxiable.
219       */
220      public final boolean isProxiable()
221      {
222        return flags[PROXIABLE];
223      }
224    
225      /**
226       * Return true if this ticket is a proxy ticket.
227       */
228      public final boolean isProxy()
229      {
230        return flags[PROXY];
231      }
232    
233      /**
234       * Return true if this ticket was post-dated.
235       */
236      public final boolean isPostdated()
237      {
238        return flags[POSTDATED];
239      }
240    
241      /**
242       * Return true if this ticket is renewable.
243       */
244      public final boolean isRenewable()
245      {
246        return flags[RENEWABLE];
247      }
248    
249      /**
250       * Return true if this ticket was granted by an application
251       * server, and not via a ticket-granting ticket.
252       */
253      public final boolean isInitial()
254      {
255        return flags[INITIAL];
256      }
257    
258      /**
259       * Return the flags for this ticket as a boolean array.
260       * See the RFC to understand what the different entries mean.
261       */
262      public final boolean[] getFlags()
263      {
264        return (boolean[]) flags.clone();
265      }
266    
267      /**
268       * Return the authentication time for this ticket.
269       */
270      public final Date getAuthTime()
271      {
272        return (Date) authTime.clone();
273      }
274    
275      /**
276       * Return the start time for this ticket.
277       */
278      public final Date getStartTime()
279      {
280        return (Date) startTime.clone();
281      }
282    
283      /**
284       * Return the end time for this ticket.
285       */
286      public final Date getEndTime()
287      {
288        return (Date) endTime.clone();
289      }
290    
291      /**
292       * Return the renewal time for this ticket.  For a non-renewable
293       * ticket, this will return null.
294       */
295      public final Date getRenewTill()
296      {
297        return flags[RENEWABLE] ? ((Date) renewTill.clone()) : null;
298      }
299    
300      /**
301       * Return the allowable client addresses for this ticket.  This will
302       * return null if the ticket can be used anywhere.
303       */
304      public final InetAddress[] getClientAddresses()
305      {
306        return (clientAddresses == null
307                ? null
308                : (InetAddress[]) clientAddresses.clone());
309      }
310    
311      /**
312       * Return the encoded form of this ticket.
313       */
314      public final byte[] getEncoded()
315      {
316        checkDestroyed();
317        return (byte[]) sessionKey.key.clone();
318      }
319    
320      /**
321       * Return the secret key associated with this ticket.
322       */
323      public final SecretKey getSessionKey()
324      {
325        checkDestroyed();
326        return sessionKey;
327      }
328    
329      private void checkDestroyed()
330      {
331        if (sessionKey == null)
332          throw new IllegalStateException("key is destroyed");
333      }
334    
335      public String toString()
336      {
337        return getClass().getName() +
338          "[client=" + client +
339          ",server=" + server +
340          ",sessionKey=" + sessionKey +
341          ",flags=" + flags +
342          ",authTime=" + authTime +
343          ",startTime= " + startTime +
344          ",endTime=" + endTime +
345          ",renewTill=" + renewTill +
346          ",clientAddresses=" + clientAddresses +
347          "]";
348      }
349    
350      /**
351       * <p>
352       * Returns the type of the session key in accordance with
353       * RFC1510.  This usually corresponds to the encryption
354       * algorithm used by the key, though more than one algorithm
355       * may use the same key type (e.g. DES with different checksum
356       * mechanisms and chaining modes). Negative values are reserved
357       * for local use.  Non-negative values are for officially assigned
358       * type fields.  The RFC defines:
359       * </p>
360       * <ul>
361       * <li>0 &mdash; null</li>
362       * <li>1 &mdash; DES (in CBC mode with either MD4 or MD5 checksums)</li>
363       * </ul>
364       *
365       * @return the type of session key used by this ticket.
366       */
367      public final int getSessionKeyType()
368      {
369        return sessionKey.type;
370      }
371    
372    }