001/* KerberosTicket.java -- a kerberos ticket
002   Copyright (C) 2006 Free Software Foundation, Inc.
003
004This file is part of GNU Classpath.
005
006GNU Classpath is free software; you can redistribute it and/or modify
007it under the terms of the GNU General Public License as published by
008the Free Software Foundation; either version 2, or (at your option)
009any later version.
010
011GNU Classpath is distributed in the hope that it will be useful, but
012WITHOUT ANY WARRANTY; without even the implied warranty of
013MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014General Public License for more details.
015
016You should have received a copy of the GNU General Public License
017along with GNU Classpath; see the file COPYING.  If not, write to the
018Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
01902110-1301 USA.
020
021Linking this library statically or dynamically with other modules is
022making a combined work based on this library.  Thus, the terms and
023conditions of the GNU General Public License cover the whole
024combination.
025
026As a special exception, the copyright holders of this library give you
027permission to link this library with independent modules to produce an
028executable, regardless of the license terms of these independent
029modules, and to copy and distribute the resulting executable under
030terms of your choice, provided that you also meet, for each linked
031independent module, the terms and conditions of the license of that
032module.  An independent module is a module which is not derived from
033or based on this library.  If you modify this library, you may extend
034this exception to your version of the library, but you are not
035obligated to do so.  If you do not wish to do so, delete this
036exception statement from your version. */
037
038
039package javax.security.auth.kerberos;
040
041import gnu.classpath.NotImplementedException;
042
043import java.io.Serializable;
044import java.net.InetAddress;
045import java.util.Date;
046
047import javax.crypto.SecretKey;
048import javax.security.auth.DestroyFailedException;
049import javax.security.auth.Destroyable;
050import javax.security.auth.RefreshFailedException;
051import 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 */
060public 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}