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 — null</li>
362 * <li>1 — 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 }