001: /*
002: * Portions Copyright 2000-2007 Sun Microsystems, Inc. All Rights Reserved.
003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004: *
005: * This code is free software; you can redistribute it and/or modify it
006: * under the terms of the GNU General Public License version 2 only, as
007: * published by the Free Software Foundation. Sun designates this
008: * particular file as subject to the "Classpath" exception as provided
009: * by Sun in the LICENSE file that accompanied this code.
010: *
011: * This code is distributed in the hope that it will be useful, but WITHOUT
012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014: * version 2 for more details (a copy is included in the LICENSE file that
015: * accompanied this code).
016: *
017: * You should have received a copy of the GNU General Public License version
018: * 2 along with this work; if not, write to the Free Software Foundation,
019: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020: *
021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022: * CA 95054 USA or visit www.sun.com if you need additional information or
023: * have any questions.
024: */
025:
026: /*
027: * @(#)KrbApReq.java 1.24 07/05/05
028: *
029: * (C) Copyright IBM Corp. 1999 All Rights Reserved.
030: * Copyright 1997 The Open Group Research Institute. All rights reserved.
031: */
032:
033: package sun.security.krb5;
034:
035: import sun.security.krb5.internal.*;
036: import sun.security.krb5.internal.crypto.*;
037: import sun.security.krb5.internal.rcache.*;
038: import java.net.InetAddress;
039: import sun.security.util.*;
040: import java.io.IOException;
041:
042: /**
043: * This class encapsulates a KRB-AP-REQ that a client sends to a
044: * server for authentication.
045: */
046: public class KrbApReq {
047:
048: private byte[] obuf;
049: private KerberosTime ctime;
050: private int cusec;
051: private Authenticator authenticator;
052: private Credentials creds;
053: private APReq apReqMessg;
054:
055: private static CacheTable table = new CacheTable();
056: private static boolean DEBUG = Krb5.DEBUG;
057:
058: // default is address-less tickets
059: private boolean KDC_EMPTY_ADDRESSES_ALLOWED = true;
060:
061: /**
062: * Contructs a AP-REQ message to send to the peer.
063: * @param tgsCred the <code>Credentials</code> to be used to construct the
064: * AP Request protocol message.
065: * @param mutualRequired Whether mutual authentication is required
066: * @param useSubkey Whether the subkey is to be used to protect this
067: * specific application session. If this is not set then the
068: * session key from the ticket will be used.
069: * @throws KrbException for any Kerberos protocol specific error
070: * @throws IOException for any IO related errors
071: * (e.g. socket operations)
072: */
073: /*
074: // Not Used
075: public KrbApReq(Credentials tgsCred,
076: boolean mutualRequired,
077: boolean useSubKey,
078: boolean useSeqNumber) throws Asn1Exception,
079: KrbCryptoException, KrbException, IOException {
080:
081: this(tgsCred, mutualRequired, useSubKey, useSeqNumber, null);
082: }
083: */
084:
085: /**
086: * Contructs a AP-REQ message to send to the peer.
087: * @param tgsCred the <code>Credentials</code> to be used to construct the
088: * AP Request protocol message.
089: * @param mutualRequired Whether mutual authentication is required
090: * @param useSubkey Whether the subkey is to be used to protect this
091: * specific application session. If this is not set then the
092: * session key from the ticket will be used.
093: * @param checksum checksum of the the application data that accompanies
094: * the KRB_AP_REQ.
095: * @throws KrbException for any Kerberos protocol specific error
096: * @throws IOException for any IO related errors
097: * (e.g. socket operations)
098: */
099: // Used in InitSecContextToken
100: public KrbApReq(Credentials tgsCred, boolean mutualRequired,
101: boolean useSubKey, boolean useSeqNumber, Checksum cksum)
102: throws Asn1Exception, KrbCryptoException, KrbException,
103: IOException {
104:
105: APOptions apOptions = (mutualRequired ? new APOptions(
106: Krb5.AP_OPTS_MUTUAL_REQUIRED) : new APOptions());
107: if (DEBUG)
108: System.out.println(">>> KrbApReq: APOptions are "
109: + apOptions);
110:
111: EncryptionKey subKey = (useSubKey ? new EncryptionKey(tgsCred
112: .getSessionKey()) : null);
113:
114: SeqNumber seqNum = new LocalSeqNumber();
115:
116: init(apOptions, tgsCred, cksum, subKey, seqNum, null, // AuthorizationData authzData
117: KeyUsage.KU_AP_REQ_AUTHENTICATOR);
118:
119: }
120:
121: /**
122: * Contructs a AP-REQ message from the bytes received from the
123: * peer.
124: * @param message The message received from the peer
125: * @param keys <code>EncrtyptionKey</code>s to decrypt the message;
126: * key selected will depend on etype used to encrypte data
127: * @throws KrbException for any Kerberos protocol specific error
128: * @throws IOException for any IO related errors
129: * (e.g. socket operations)
130: */
131: // Used in InitSecContextToken (for AP_REQ and not TGS REQ)
132: public KrbApReq(byte[] message, EncryptionKey[] keys,
133: InetAddress initiator) throws KrbException, IOException {
134: obuf = message;
135: if (apReqMessg == null)
136: decode();
137: authenticate(keys, initiator);
138: }
139:
140: /**
141: * Contructs a AP-REQ message from the bytes received from the
142: * peer.
143: * @param value The <code>DerValue</code> that contains the
144: * DER enoded AP-REQ protocol message
145: * @param keys <code>EncrtyptionKey</code>s to decrypt the message;
146: *
147: * @throws KrbException for any Kerberos protocol specific error
148: * @throws IOException for any IO related errors
149: * (e.g. socket operations)
150: */
151: /*
152: public KrbApReq(DerValue value, EncryptionKey[] key, InetAddress initiator)
153: throws KrbException, IOException {
154: obuf = value.toByteArray();
155: if (apReqMessg == null)
156: decode(value);
157: authenticate(keys, initiator);
158: }
159:
160: KrbApReq(APOptions options,
161: Credentials tgs_creds,
162: Checksum cksum,
163: EncryptionKey subKey,
164: SeqNumber seqNumber,
165: AuthorizationData authorizationData)
166: throws KrbException, IOException {
167: init(options, tgs_creds, cksum, subKey, seqNumber, authorizationData);
168: }
169: */
170:
171: /** used by KrbTgsReq **/
172: KrbApReq(APOptions apOptions, Ticket ticket, EncryptionKey key,
173: Realm crealm, PrincipalName cname, Checksum cksum,
174: KerberosTime ctime, EncryptionKey subKey,
175: SeqNumber seqNumber, AuthorizationData authorizationData)
176: throws Asn1Exception, IOException, KdcErrException,
177: KrbCryptoException {
178:
179: init(apOptions, ticket, key, crealm, cname, cksum, ctime,
180: subKey, seqNumber, authorizationData,
181: KeyUsage.KU_PA_TGS_REQ_AUTHENTICATOR);
182:
183: }
184:
185: private void init(APOptions options, Credentials tgs_creds,
186: Checksum cksum, EncryptionKey subKey, SeqNumber seqNumber,
187: AuthorizationData authorizationData, int usage)
188: throws KrbException, IOException {
189:
190: ctime = new KerberosTime(KerberosTime.NOW);
191: init(options, tgs_creds.ticket, tgs_creds.key, tgs_creds.client
192: .getRealm(), tgs_creds.client, cksum, ctime, subKey,
193: seqNumber, authorizationData, usage);
194: }
195:
196: private void init(APOptions apOptions, Ticket ticket,
197: EncryptionKey key, Realm crealm, PrincipalName cname,
198: Checksum cksum, KerberosTime ctime, EncryptionKey subKey,
199: SeqNumber seqNumber, AuthorizationData authorizationData,
200: int usage) throws Asn1Exception, IOException,
201: KdcErrException, KrbCryptoException {
202:
203: createMessage(apOptions, ticket, key, crealm, cname, cksum,
204: ctime, subKey, seqNumber, authorizationData, usage);
205: obuf = apReqMessg.asn1Encode();
206: }
207:
208: void decode() throws KrbException, IOException {
209: DerValue encoding = new DerValue(obuf);
210: decode(encoding);
211: }
212:
213: void decode(DerValue encoding) throws KrbException, IOException {
214: apReqMessg = null;
215: try {
216: apReqMessg = new APReq(encoding);
217: } catch (Asn1Exception e) {
218: apReqMessg = null;
219: KRBError err = new KRBError(encoding);
220: String errStr = err.getErrorString();
221: String eText;
222: if (errStr.charAt(errStr.length() - 1) == 0)
223: eText = errStr.substring(0, errStr.length() - 1);
224: else
225: eText = errStr;
226: KrbException ke = new KrbException(err.getErrorCode(),
227: eText);
228: ke.initCause(e);
229: throw ke;
230: }
231: }
232:
233: private void authenticate(EncryptionKey[] keys,
234: InetAddress initiator) throws KrbException, IOException {
235: int encPartKeyType = apReqMessg.ticket.encPart.getEType();
236: EncryptionKey dkey = EncryptionKey
237: .findKey(encPartKeyType, keys);
238:
239: if (dkey == null) {
240: throw new KrbException(Krb5.API_INVALID_ARG,
241: "Cannot find key of appropriate type to decrypt AP REP - "
242: + EType.toString(encPartKeyType));
243: }
244:
245: byte[] bytes = apReqMessg.ticket.encPart.decrypt(dkey,
246: KeyUsage.KU_TICKET);
247: byte[] temp = apReqMessg.ticket.encPart.reset(bytes, true);
248: EncTicketPart enc_ticketPart = new EncTicketPart(temp);
249:
250: checkPermittedEType(enc_ticketPart.key.getEType());
251:
252: byte[] bytes2 = apReqMessg.authenticator.decrypt(
253: enc_ticketPart.key, KeyUsage.KU_AP_REQ_AUTHENTICATOR);
254: byte[] temp2 = apReqMessg.authenticator.reset(bytes2, true);
255: authenticator = new Authenticator(temp2);
256: ctime = authenticator.ctime;
257: cusec = authenticator.cusec;
258: authenticator.ctime.setMicroSeconds(authenticator.cusec);
259: authenticator.cname.setRealm(authenticator.crealm);
260: apReqMessg.ticket.sname.setRealm(apReqMessg.ticket.realm);
261: enc_ticketPart.cname.setRealm(enc_ticketPart.crealm);
262:
263: Config.getInstance().resetDefaultRealm(
264: apReqMessg.ticket.realm.toString());
265:
266: if (!authenticator.cname.equals(enc_ticketPart.cname))
267: throw new KrbApErrException(Krb5.KRB_AP_ERR_BADMATCH);
268:
269: KerberosTime currTime = new KerberosTime(KerberosTime.NOW);
270: if (!authenticator.ctime.inClockSkew(currTime))
271: throw new KrbApErrException(Krb5.KRB_AP_ERR_SKEW);
272:
273: // start to check if it is a replay attack.
274: AuthTime time = new AuthTime(authenticator.ctime.getTime(),
275: authenticator.cusec);
276: String client = authenticator.cname.toString();
277: if (table.get(time, authenticator.cname.toString()) != null) {
278: throw new KrbApErrException(Krb5.KRB_AP_ERR_REPEAT);
279: } else {
280: table.put(client, time, currTime.getTime());
281: }
282:
283: // check to use addresses in tickets
284: if (Config.getInstance().useAddresses()) {
285: KDC_EMPTY_ADDRESSES_ALLOWED = false;
286: }
287:
288: // sender host address
289: HostAddress sender = null;
290: if (initiator != null) {
291: sender = new HostAddress(initiator);
292: }
293:
294: if (sender != null || !KDC_EMPTY_ADDRESSES_ALLOWED) {
295: if (enc_ticketPart.caddr != null) {
296: if (sender == null)
297: throw new KrbApErrException(Krb5.KRB_AP_ERR_BADADDR);
298: if (!enc_ticketPart.caddr.inList(sender))
299: throw new KrbApErrException(Krb5.KRB_AP_ERR_BADADDR);
300: }
301: }
302:
303: // XXX check for repeated authenticator
304: // if found
305: // throw new KrbApErrException(Krb5.KRB_AP_ERR_REPEAT);
306: // else
307: // save authenticator to check for later
308:
309: KerberosTime now = new KerberosTime(KerberosTime.NOW);
310:
311: if ((enc_ticketPart.starttime != null && enc_ticketPart.starttime
312: .greaterThanWRTClockSkew(now))
313: || enc_ticketPart.flags.get(Krb5.TKT_OPTS_INVALID))
314: throw new KrbApErrException(Krb5.KRB_AP_ERR_TKT_NYV);
315:
316: // if the current time is later than end time by more
317: // than the allowable clock skew, throws ticket expired exception.
318: if (enc_ticketPart.endtime != null
319: && now.greaterThanWRTClockSkew(enc_ticketPart.endtime)) {
320: throw new KrbApErrException(Krb5.KRB_AP_ERR_TKT_EXPIRED);
321: }
322:
323: creds = new Credentials(apReqMessg.ticket, authenticator.cname,
324: apReqMessg.ticket.sname, enc_ticketPart.key, null,
325: enc_ticketPart.authtime, enc_ticketPart.starttime,
326: enc_ticketPart.endtime, enc_ticketPart.renewTill,
327: enc_ticketPart.caddr);
328: if (DEBUG) {
329: System.out.println(">>> KrbApReq: authenticate succeed.");
330: }
331: }
332:
333: /**
334: * Returns the credentials that are contained in the ticket that
335: * is part of this this AP-REP.
336: */
337: public Credentials getCreds() {
338: return creds;
339: }
340:
341: KerberosTime getCtime() {
342: if (ctime != null)
343: return ctime;
344: return authenticator.ctime;
345: }
346:
347: int cusec() {
348: return cusec;
349: }
350:
351: APOptions getAPOptions() throws KrbException, IOException {
352: if (apReqMessg == null)
353: decode();
354: if (apReqMessg != null)
355: return apReqMessg.apOptions;
356: return null;
357: }
358:
359: /**
360: * Returns true if mutual authentication is required and hence an
361: * AP-REP will need to be generated.
362: * @throws KrbException
363: * @throws IOException
364: */
365: public boolean getMutualAuthRequired() throws KrbException,
366: IOException {
367: if (apReqMessg == null)
368: decode();
369: if (apReqMessg != null)
370: return apReqMessg.apOptions
371: .get(Krb5.AP_OPTS_MUTUAL_REQUIRED);
372: return false;
373: }
374:
375: boolean useSessionKey() throws KrbException, IOException {
376: if (apReqMessg == null)
377: decode();
378: if (apReqMessg != null)
379: return apReqMessg.apOptions
380: .get(Krb5.AP_OPTS_USE_SESSION_KEY);
381: return false;
382: }
383:
384: /**
385: * Returns the optional subkey stored in the Authenticator for
386: * this message. Returns null if none is stored.
387: */
388: public EncryptionKey getSubKey() {
389: // XXX Can authenticator be null
390: return authenticator.getSubKey();
391: }
392:
393: /**
394: * Returns the optional sequence number stored in the
395: * Authenticator for this message. Returns null if none is
396: * stored.
397: */
398: public Integer getSeqNumber() {
399: // XXX Can authenticator be null
400: return authenticator.getSeqNumber();
401: }
402:
403: /**
404: * Returns the optional Checksum stored in the
405: * Authenticator for this message. Returns null if none is
406: * stored.
407: */
408: public Checksum getChecksum() {
409: return authenticator.getChecksum();
410: }
411:
412: /**
413: * Returns the ASN.1 encoding that should be sent to the peer.
414: */
415: public byte[] getMessage() {
416: return obuf;
417: }
418:
419: /**
420: * Returns the principal name of the client that generated this
421: * message.
422: */
423: public PrincipalName getClient() {
424: return creds.getClient();
425: }
426:
427: private void createMessage(APOptions apOptions, Ticket ticket,
428: EncryptionKey key, Realm crealm, PrincipalName cname,
429: Checksum cksum, KerberosTime ctime, EncryptionKey subKey,
430: SeqNumber seqNumber, AuthorizationData authorizationData,
431: int usage) throws Asn1Exception, IOException,
432: KdcErrException, KrbCryptoException {
433:
434: Integer seqno = null;
435:
436: if (seqNumber != null)
437: seqno = new Integer(seqNumber.current());
438:
439: authenticator = new Authenticator(crealm, cname, cksum, ctime
440: .getMicroSeconds(), ctime, subKey, seqno,
441: authorizationData);
442:
443: byte[] temp = authenticator.asn1Encode();
444:
445: EncryptedData encAuthenticator = new EncryptedData(key, temp,
446: usage);
447:
448: apReqMessg = new APReq(apOptions, ticket, encAuthenticator);
449: }
450:
451: // Check that key is one of the permitted types
452: private static void checkPermittedEType(int target)
453: throws KrbException {
454: int[] etypes = EType.getDefaults("permitted_enctypes");
455: if (etypes == null) {
456: throw new KrbException(
457: "No supported encryption types listed in permitted_enctypes");
458: }
459: if (!EType.isSupported(target, etypes)) {
460: throw new KrbException(EType.toString(target)
461: + " encryption type not in permitted_enctypes list");
462: }
463: }
464: }
|