PageRenderTime 44ms CodeModel.GetById 14ms RepoModel.GetById 1ms app.codeStats 0ms

/projects/jre-1.6.0/src/javax/security/auth/kerberos/KerberosTicket.java

https://gitlab.com/essere.lab.public/qualitas.class-corpus
Java | 731 lines | 336 code | 85 blank | 310 comment | 71 complexity | 89c2c74c6a3ce18d2b39fb34a241cae6 MD5 | raw file
  1. /*
  2. * %W% %E%
  3. *
  4. * Copyright (c) 2006, Oracle and/or its affiliates. All rights reserved.
  5. * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package javax.security.auth.kerberos;
  8. import java.io.*;
  9. import java.util.Date;
  10. import java.util.Arrays;
  11. import java.net.InetAddress;
  12. import javax.crypto.SecretKey;
  13. import javax.security.auth.Refreshable;
  14. import javax.security.auth.Destroyable;
  15. import javax.security.auth.RefreshFailedException;
  16. import javax.security.auth.DestroyFailedException;
  17. import sun.misc.HexDumpEncoder;
  18. import sun.security.krb5.EncryptionKey;
  19. import sun.security.krb5.Asn1Exception;
  20. import sun.security.util.*;
  21. /**
  22. * This class encapsulates a Kerberos ticket and associated
  23. * information as viewed from the client's point of view. It captures all
  24. * information that the Key Distribution Center (KDC) sends to the client
  25. * in the reply message KDC-REP defined in the Kerberos Protocol
  26. * Specification (<a href=http://www.ietf.org/rfc/rfc1510.txt>RFC 1510</a>).
  27. * <p>
  28. * All Kerberos JAAS login modules that authenticate a user to a KDC should
  29. * use this class. Where available, the login module might even read this
  30. * information from a ticket cache in the operating system instead of
  31. * directly communicating with the KDC. During the commit phase of the JAAS
  32. * authentication process, the JAAS login module should instantiate this
  33. * class and store the instance in the private credential set of a
  34. * {@link javax.security.auth.Subject Subject}.<p>
  35. *
  36. * It might be necessary for the application to be granted a
  37. * {@link javax.security.auth.PrivateCredentialPermission
  38. * PrivateCredentialPermission} if it needs to access a KerberosTicket
  39. * instance from a Subject. This permission is not needed when the
  40. * application depends on the default JGSS Kerberos mechanism to access the
  41. * KerberosTicket. In that case, however, the application will need an
  42. * appropriate
  43. * {@link javax.security.auth.kerberos.ServicePermission ServicePermission}.
  44. * <p>
  45. * Note that this class is applicable to both ticket granting tickets and
  46. * other regular service tickets. A ticket granting ticket is just a
  47. * special case of a more generalized service ticket.
  48. *
  49. * @see javax.security.auth.Subject
  50. * @see javax.security.auth.PrivateCredentialPermission
  51. * @see javax.security.auth.login.LoginContext
  52. * @see org.ietf.jgss.GSSCredential
  53. * @see org.ietf.jgss.GSSManager
  54. *
  55. * @author Mayank Upadhyay
  56. * @version %I%, %G%
  57. * @since 1.4
  58. */
  59. public class KerberosTicket implements Destroyable, Refreshable,
  60. java.io.Serializable {
  61. private static final long serialVersionUID = 7395334370157380539L;
  62. // XXX Make these flag indices public
  63. private static final int FORWARDABLE_TICKET_FLAG = 1;
  64. private static final int FORWARDED_TICKET_FLAG = 2;
  65. private static final int PROXIABLE_TICKET_FLAG = 3;
  66. private static final int PROXY_TICKET_FLAG = 4;
  67. private static final int POSTDATED_TICKET_FLAG = 6;
  68. private static final int RENEWABLE_TICKET_FLAG = 8;
  69. private static final int INITIAL_TICKET_FLAG = 9;
  70. private static final int NUM_FLAGS = 32;
  71. /**
  72. *
  73. * ASN.1 DER Encoding of the Ticket as defined in the
  74. * Kerberos Protocol Specification RFC1510.
  75. *
  76. * @serial
  77. */
  78. private byte[] asn1Encoding;
  79. /**
  80. *<code>KeyImpl</code> is serialized by writing out the ASN1 Encoded bytes
  81. * of the encryption key. The ASN1 encoding is defined in RFC1510 and as
  82. * follows:
  83. * <pre>
  84. * EncryptionKey ::= SEQUENCE {
  85. * keytype[0] INTEGER,
  86. * keyvalue[1] OCTET STRING
  87. * }
  88. * </pre>
  89. *
  90. * @serial
  91. */
  92. private KeyImpl sessionKey;
  93. /**
  94. *
  95. * Ticket Flags as defined in the Kerberos Protocol Specification RFC1510.
  96. *
  97. * @serial
  98. */
  99. private boolean[] flags;
  100. /**
  101. *
  102. * Time of initial authentication
  103. *
  104. * @serial
  105. */
  106. private Date authTime;
  107. /**
  108. *
  109. * Time after which the ticket is valid.
  110. * @serial
  111. */
  112. private Date startTime;
  113. /**
  114. *
  115. * Time after which the ticket will not be honored. (its expiration time).
  116. *
  117. * @serial
  118. */
  119. private Date endTime;
  120. /**
  121. *
  122. * For renewable Tickets it indicates the maximum endtime that may be
  123. * included in a renewal. It can be thought of as the absolute expiration
  124. * time for the ticket, including all renewals. This field may be null
  125. * for tickets that are not renewable.
  126. *
  127. * @serial
  128. */
  129. private Date renewTill;
  130. /**
  131. *
  132. * Client that owns the service ticket
  133. *
  134. * @serial
  135. */
  136. private KerberosPrincipal client;
  137. /**
  138. *
  139. * The service for which the ticket was issued.
  140. *
  141. * @serial
  142. */
  143. private KerberosPrincipal server;
  144. /**
  145. *
  146. * The addresses from where the ticket may be used by the client.
  147. * This field may be null when the ticket is usable from any address.
  148. *
  149. * @serial
  150. */
  151. private InetAddress[] clientAddresses;
  152. private transient boolean destroyed = false;
  153. /**
  154. * Constructs a KerberosTicket using credentials information that a
  155. * client either receives from a KDC or reads from a cache.
  156. *
  157. * @param asn1Encoding the ASN.1 encoding of the ticket as defined by
  158. * the Kerberos protocol specification.
  159. * @param client the client that owns this service
  160. * ticket
  161. * @param server the service that this ticket is for
  162. * @param sessionKey the raw bytes for the session key that must be
  163. * used to encrypt the authenticator that will be sent to the server
  164. * @param keyType the key type for the session key as defined by the
  165. * Kerberos protocol specification.
  166. * @param flags the ticket flags. Each element in this array indicates
  167. * the value for the corresponding bit in the ASN.1 BitString that
  168. * represents the ticket flags. If the number of elements in this array
  169. * is less than the number of flags used by the Kerberos protocol,
  170. * then the missing flags will be filled in with false.
  171. * @param authTime the time of initial authentication for the client
  172. * @param startTime the time after which the ticket will be valid. This
  173. * may be null in which case the value of authTime is treated as the
  174. * startTime.
  175. * @param endTime the time after which the ticket will no longer be
  176. * valid
  177. * @param renewTill an absolute expiration time for the ticket,
  178. * including all renewal that might be possible. This field may be null
  179. * for tickets that are not renewable.
  180. * @param clientAddresses the addresses from where the ticket may be
  181. * used by the client. This field may be null when the ticket is usable
  182. * from any address.
  183. */
  184. public KerberosTicket(byte[] asn1Encoding,
  185. KerberosPrincipal client,
  186. KerberosPrincipal server,
  187. byte[] sessionKey,
  188. int keyType,
  189. boolean[] flags,
  190. Date authTime,
  191. Date startTime,
  192. Date endTime,
  193. Date renewTill,
  194. InetAddress[] clientAddresses) {
  195. init(asn1Encoding, client, server, sessionKey, keyType, flags,
  196. authTime, startTime, endTime, renewTill, clientAddresses);
  197. }
  198. private void init(byte[] asn1Encoding,
  199. KerberosPrincipal client,
  200. KerberosPrincipal server,
  201. byte[] sessionKey,
  202. int keyType,
  203. boolean[] flags,
  204. Date authTime,
  205. Date startTime,
  206. Date endTime,
  207. Date renewTill,
  208. InetAddress[] clientAddresses) {
  209. if (asn1Encoding == null)
  210. throw new IllegalArgumentException("ASN.1 encoding of ticket"
  211. + " cannot be null");
  212. this.asn1Encoding = asn1Encoding.clone();
  213. if (client == null)
  214. throw new IllegalArgumentException("Client name in ticket"
  215. + " cannot be null");
  216. this.client = client;
  217. if (server == null)
  218. throw new IllegalArgumentException("Server name in ticket"
  219. + " cannot be null");
  220. this.server = server;
  221. if (sessionKey == null)
  222. throw new IllegalArgumentException("Session key for ticket"
  223. + " cannot be null");
  224. this.sessionKey = new KeyImpl(sessionKey, keyType);
  225. if (flags != null) {
  226. if (flags.length >= NUM_FLAGS)
  227. this.flags = (boolean[]) flags.clone();
  228. else {
  229. this.flags = new boolean[NUM_FLAGS];
  230. // Fill in whatever we have
  231. for (int i = 0; i < flags.length; i++)
  232. this.flags[i] = flags[i];
  233. }
  234. } else
  235. this.flags = new boolean[NUM_FLAGS];
  236. if (this.flags[RENEWABLE_TICKET_FLAG]) {
  237. if (renewTill == null)
  238. throw new IllegalArgumentException("The renewable period "
  239. + "end time cannot be null for renewable tickets.");
  240. this.renewTill = renewTill;
  241. }
  242. this.authTime = authTime;
  243. this.startTime = (startTime != null? startTime: authTime);
  244. if (endTime == null)
  245. throw new IllegalArgumentException("End time for ticket validity"
  246. + " cannot be null");
  247. this.endTime = endTime;
  248. if (clientAddresses != null)
  249. this.clientAddresses = (InetAddress[]) clientAddresses.clone();
  250. }
  251. /**
  252. * Returns the client principal associated with this ticket.
  253. *
  254. * @return the client principal.
  255. */
  256. public final KerberosPrincipal getClient() {
  257. return client;
  258. }
  259. /**
  260. * Returns the service principal associated with this ticket.
  261. *
  262. * @return the service principal.
  263. */
  264. public final KerberosPrincipal getServer() {
  265. return server;
  266. }
  267. /**
  268. * Returns the session key associated with this ticket.
  269. *
  270. * @return the session key.
  271. */
  272. public final SecretKey getSessionKey() {
  273. if (destroyed)
  274. throw new IllegalStateException("This ticket is no longer valid");
  275. return sessionKey;
  276. }
  277. /**
  278. * Returns the key type of the session key associated with this
  279. * ticket as defined by the Kerberos Protocol Specification.
  280. *
  281. * @return the key type of the session key associated with this
  282. * ticket.
  283. *
  284. * @see #getSessionKey()
  285. */
  286. public final int getSessionKeyType() {
  287. if (destroyed)
  288. throw new IllegalStateException("This ticket is no longer valid");
  289. return sessionKey.getKeyType();
  290. }
  291. /**
  292. * Determines if this ticket is forwardable.
  293. *
  294. * @return true if this ticket is forwardable, false if not.
  295. */
  296. public final boolean isForwardable() {
  297. return flags[FORWARDABLE_TICKET_FLAG];
  298. }
  299. /**
  300. * Determines if this ticket had been forwarded or was issued based on
  301. * authentication involving a forwarded ticket-granting ticket.
  302. *
  303. * @return true if this ticket had been forwarded or was issued based on
  304. * authentication involving a forwarded ticket-granting ticket,
  305. * false otherwise.
  306. */
  307. public final boolean isForwarded() {
  308. return flags[FORWARDED_TICKET_FLAG];
  309. }
  310. /**
  311. * Determines if this ticket is proxiable.
  312. *
  313. * @return true if this ticket is proxiable, false if not.
  314. */
  315. public final boolean isProxiable() {
  316. return flags[PROXIABLE_TICKET_FLAG];
  317. }
  318. /**
  319. * Determines is this ticket is a proxy-ticket.
  320. *
  321. * @return true if this ticket is a proxy-ticket, false if not.
  322. */
  323. public final boolean isProxy() {
  324. return flags[PROXY_TICKET_FLAG];
  325. }
  326. /**
  327. * Determines is this ticket is post-dated.
  328. *
  329. * @return true if this ticket is post-dated, false if not.
  330. */
  331. public final boolean isPostdated() {
  332. return flags[POSTDATED_TICKET_FLAG];
  333. }
  334. /**
  335. * Determines is this ticket is renewable. If so, the {@link #refresh()
  336. * refresh} method can be called, assuming the validity period for
  337. * renewing is not already over.
  338. *
  339. * @return true if this ticket is renewable, false if not.
  340. */
  341. public final boolean isRenewable() {
  342. return flags[RENEWABLE_TICKET_FLAG];
  343. }
  344. /**
  345. * Determines if this ticket was issued using the Kerberos AS-Exchange
  346. * protocol, and not issued based on some ticket-granting ticket.
  347. *
  348. * @return true if this ticket was issued using the Kerberos AS-Exchange
  349. * protocol, false if not.
  350. */
  351. public final boolean isInitial() {
  352. return flags[INITIAL_TICKET_FLAG];
  353. }
  354. /**
  355. * Returns the flags associated with this ticket. Each element in the
  356. * returned array indicates the value for the corresponding bit in the
  357. * ASN.1 BitString that represents the ticket flags.
  358. *
  359. * @return the flags associated with this ticket.
  360. */
  361. public final boolean[] getFlags() {
  362. return (flags == null? null: (boolean[]) flags.clone());
  363. }
  364. /**
  365. * Returns the time that the client was authenticated.
  366. *
  367. * @return the time that the client was authenticated
  368. * or null if not set.
  369. */
  370. public final java.util.Date getAuthTime() {
  371. return (authTime == null) ? null : new Date(authTime.getTime());
  372. }
  373. /**
  374. * Returns the start time for this ticket's validity period.
  375. *
  376. * @return the start time for this ticket's validity period
  377. * or null if not set.
  378. */
  379. public final java.util.Date getStartTime() {
  380. return (startTime == null) ? null : new Date(startTime.getTime());
  381. }
  382. /**
  383. * Returns the expiration time for this ticket's validity period.
  384. *
  385. * @return the expiration time for this ticket's validity period.
  386. */
  387. public final java.util.Date getEndTime() {
  388. return endTime;
  389. }
  390. /**
  391. * Returns the latest expiration time for this ticket, including all
  392. * renewals. This will return a null value for non-renewable tickets.
  393. *
  394. * @return the latest expiration time for this ticket.
  395. */
  396. public final java.util.Date getRenewTill() {
  397. return (renewTill == null) ? null: new Date(renewTill.getTime());
  398. }
  399. /**
  400. * Returns a list of addresses from where the ticket can be used.
  401. *
  402. * @return ths list of addresses or null, if the field was not
  403. * provided.
  404. */
  405. public final java.net.InetAddress[] getClientAddresses() {
  406. return (clientAddresses == null?
  407. null: (InetAddress[]) clientAddresses.clone());
  408. }
  409. /**
  410. * Returns an ASN.1 encoding of the entire ticket.
  411. *
  412. * @return an ASN.1 encoding of the entire ticket.
  413. */
  414. public final byte[] getEncoded() {
  415. if (destroyed)
  416. throw new IllegalStateException("This ticket is no longer valid");
  417. return (byte[]) asn1Encoding.clone();
  418. }
  419. /** Determines if this ticket is still current. */
  420. public boolean isCurrent() {
  421. return (System.currentTimeMillis() <= getEndTime().getTime());
  422. }
  423. /**
  424. * Extends the validity period of this ticket. The ticket will contain
  425. * a new session key if the refresh operation succeeds. The refresh
  426. * operation will fail if the ticket is not renewable or the latest
  427. * allowable renew time has passed. Any other error returned by the
  428. * KDC will also cause this method to fail.
  429. *
  430. * Note: This method is not synchronized with the the accessor
  431. * methods of this object. Hence callers need to be aware of multiple
  432. * threads that might access this and try to renew it at the same
  433. * time.
  434. *
  435. * @throws RefreshFailedException if the ticket is not renewable, or
  436. * the latest allowable renew time has passed, or the KDC returns some
  437. * error.
  438. *
  439. * @see #isRenewable()
  440. * @see #getRenewTill()
  441. */
  442. public void refresh() throws RefreshFailedException {
  443. if (destroyed)
  444. throw new RefreshFailedException("A destroyed ticket "
  445. + "cannot be renewd.");
  446. if (!isRenewable())
  447. throw new RefreshFailedException("This ticket is not renewable");
  448. if (System.currentTimeMillis() > getRenewTill().getTime())
  449. throw new RefreshFailedException("This ticket is past "
  450. + "its last renewal time.");
  451. Throwable e = null;
  452. sun.security.krb5.Credentials krb5Creds = null;
  453. try {
  454. krb5Creds = new sun.security.krb5.Credentials(asn1Encoding,
  455. client.toString(),
  456. server.toString(),
  457. sessionKey.getEncoded(),
  458. sessionKey.getKeyType(),
  459. flags,
  460. authTime,
  461. startTime,
  462. endTime,
  463. renewTill,
  464. clientAddresses);
  465. krb5Creds = krb5Creds.renew();
  466. } catch (sun.security.krb5.KrbException krbException) {
  467. e = krbException;
  468. } catch (java.io.IOException ioException) {
  469. e = ioException;
  470. }
  471. if (e != null) {
  472. RefreshFailedException rfException
  473. = new RefreshFailedException("Failed to renew Kerberos Ticket "
  474. + "for client " + client
  475. + " and server " + server
  476. + " - " + e.getMessage());
  477. rfException.initCause(e);
  478. throw rfException;
  479. }
  480. /*
  481. * In case multiple threads try to refresh it at the same time.
  482. */
  483. synchronized (this) {
  484. try {
  485. this.destroy();
  486. } catch (DestroyFailedException dfException) {
  487. // Squelch it since we don't care about the old ticket.
  488. }
  489. init(krb5Creds.getEncoded(),
  490. new KerberosPrincipal(krb5Creds.getClient().getName()),
  491. new KerberosPrincipal(krb5Creds.getServer().getName()),
  492. krb5Creds.getSessionKey().getBytes(),
  493. krb5Creds.getSessionKey().getEType(),
  494. krb5Creds.getFlags(),
  495. krb5Creds.getAuthTime(),
  496. krb5Creds.getStartTime(),
  497. krb5Creds.getEndTime(),
  498. krb5Creds.getRenewTill(),
  499. krb5Creds.getClientAddresses());
  500. destroyed = false;
  501. }
  502. }
  503. /**
  504. * Destroys the ticket and destroys any sensitive information stored in
  505. * it.
  506. */
  507. public void destroy() throws DestroyFailedException {
  508. if (!destroyed) {
  509. Arrays.fill(asn1Encoding, (byte) 0);
  510. client = null;
  511. server = null;
  512. sessionKey.destroy();
  513. flags = null;
  514. authTime = null;
  515. startTime = null;
  516. endTime = null;
  517. renewTill = null;
  518. clientAddresses = null;
  519. destroyed = true;
  520. }
  521. }
  522. /**
  523. * Determines if this ticket has been destroyed.
  524. */
  525. public boolean isDestroyed() {
  526. return destroyed;
  527. }
  528. public String toString() {
  529. if (destroyed)
  530. throw new IllegalStateException("This ticket is no longer valid");
  531. StringBuffer caddrBuf = new StringBuffer();
  532. if (clientAddresses != null) {
  533. for (int i = 0; i < clientAddresses.length; i++) {
  534. caddrBuf.append("clientAddresses[" + i + "] = " +
  535. clientAddresses[i].toString());
  536. }
  537. }
  538. return ("Ticket (hex) = " + "\n" +
  539. (new HexDumpEncoder()).encodeBuffer(asn1Encoding) + "\n" +
  540. "Client Principal = " + client.toString() + "\n" +
  541. "Server Principal = " + server.toString() + "\n" +
  542. "Session Key = " + sessionKey.toString() + "\n" +
  543. "Forwardable Ticket " + flags[FORWARDABLE_TICKET_FLAG] + "\n" +
  544. "Forwarded Ticket " + flags[FORWARDED_TICKET_FLAG] + "\n" +
  545. "Proxiable Ticket " + flags[PROXIABLE_TICKET_FLAG] + "\n" +
  546. "Proxy Ticket " + flags[PROXY_TICKET_FLAG] + "\n" +
  547. "Postdated Ticket " + flags[POSTDATED_TICKET_FLAG] + "\n" +
  548. "Renewable Ticket " + flags[RENEWABLE_TICKET_FLAG] + "\n" +
  549. "Initial Ticket " + flags[RENEWABLE_TICKET_FLAG] + "\n" +
  550. "Auth Time = " + String.valueOf(authTime) + "\n" +
  551. "Start Time = " + String.valueOf(startTime) + "\n" +
  552. "End Time = " + endTime.toString() + "\n" +
  553. "Renew Till = " + String.valueOf(renewTill) + "\n" +
  554. "Client Addresses " +
  555. (clientAddresses == null ? " Null " : caddrBuf.toString() +
  556. "\n"));
  557. }
  558. /**
  559. * Returns a hashcode for this KerberosTicket.
  560. *
  561. * @return a hashCode() for the <code>KerberosTicket</code>
  562. * @since 1.6
  563. */
  564. public int hashCode() {
  565. int result = 17;
  566. if (isDestroyed()) {
  567. return result;
  568. }
  569. result = result * 37 + Arrays.hashCode(getEncoded());
  570. result = result * 37 + endTime.hashCode();
  571. result = result * 37 + client.hashCode();
  572. result = result * 37 + server.hashCode();
  573. result = result * 37 + sessionKey.hashCode();
  574. // authTime may be null
  575. if (authTime != null) {
  576. result = result * 37 + authTime.hashCode();
  577. }
  578. // startTime may be null
  579. if (startTime != null) {
  580. result = result * 37 + startTime.hashCode();
  581. }
  582. // renewTill may be null
  583. if (renewTill != null) {
  584. result = result * 37 + renewTill.hashCode();
  585. }
  586. // clientAddress may be null, the array's hashCode is 0
  587. result = result * 37 + Arrays.hashCode(clientAddresses);
  588. return result * 37 + Arrays.hashCode(flags);
  589. }
  590. /**
  591. * Compares the specified Object with this KerberosTicket for equality.
  592. * Returns true if the given object is also a
  593. * <code>KerberosTicket</code> and the two
  594. * <code>KerberosTicket</code> instances are equivalent.
  595. *
  596. * @param other the Object to compare to
  597. * @return true if the specified object is equal to this KerberosTicket,
  598. * false otherwise. NOTE: Returns false if either of the KerberosTicket
  599. * objects has been destroyed.
  600. * @since 1.6
  601. */
  602. public boolean equals(Object other) {
  603. if (other == this)
  604. return true;
  605. if (! (other instanceof KerberosTicket)) {
  606. return false;
  607. }
  608. KerberosTicket otherTicket = ((KerberosTicket) other);
  609. if (isDestroyed() || otherTicket.isDestroyed()) {
  610. return false;
  611. }
  612. if (!Arrays.equals(getEncoded(), otherTicket.getEncoded()) ||
  613. !endTime.equals(otherTicket.getEndTime()) ||
  614. !server.equals(otherTicket.getServer()) ||
  615. !client.equals(otherTicket.getClient()) ||
  616. !sessionKey.equals(otherTicket.getSessionKey()) ||
  617. !Arrays.equals(clientAddresses, otherTicket.getClientAddresses()) ||
  618. !Arrays.equals(flags, otherTicket.getFlags())) {
  619. return false;
  620. }
  621. // authTime may be null
  622. if (authTime == null) {
  623. if (otherTicket.getAuthTime() != null)
  624. return false;
  625. } else {
  626. if (!authTime.equals(otherTicket.getAuthTime()))
  627. return false;
  628. }
  629. // startTime may be null
  630. if (startTime == null) {
  631. if (otherTicket.getStartTime() != null)
  632. return false;
  633. } else {
  634. if (!startTime.equals(otherTicket.getStartTime()))
  635. return false;
  636. }
  637. if (renewTill == null) {
  638. if (otherTicket.getRenewTill() != null)
  639. return false;
  640. } else {
  641. if (!renewTill.equals(otherTicket.getRenewTill()))
  642. return false;
  643. }
  644. return true;
  645. }
  646. }