blob: c493d405f51c7ebaf27b7584562ad31bf47ec350 [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Portions Copyright 2000-2007 Sun Microsystems, Inc. All Rights Reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26/*
27 *
28 * (C) Copyright IBM Corp. 1999 All Rights Reserved.
29 * Copyright 1997 The Open Group Research Institute. All rights reserved.
30 */
31
32package sun.security.krb5;
33
34import sun.security.krb5.internal.*;
35import sun.security.krb5.internal.crypto.EType;
36import sun.security.krb5.internal.crypto.Nonce;
37import sun.security.krb5.internal.crypto.KeyUsage;
38import sun.security.util.*;
39import java.io.IOException;
40import java.io.ByteArrayInputStream;
41import java.net.UnknownHostException;
42import java.util.StringTokenizer;
43
44/**
45 * This class encapsulates the KRB-AS-REQ message that the client
46 * sends to the KDC.
47 */
48public class KrbAsReq extends KrbKdcReq {
49 private PrincipalName princName;
50 private ASReq asReqMessg;
51
52 private boolean DEBUG = Krb5.DEBUG;
53 private static KDCOptions defaultKDCOptions = new KDCOptions();
54
55 // pre-auth info
56 private boolean PA_ENC_TIMESTAMP_REQUIRED = false;
57 private boolean pa_exists = false;
58 private int pa_etype = 0;
59 private byte[] pa_salt = null;
60 private byte[] pa_s2kparams = null;
61
62 // default is address-less tickets
63 private boolean KDC_EMPTY_ADDRESSES_ALLOWED = true;
64
65 /**
66 * Creates a KRB-AS-REQ to send to the default KDC
67 * @throws KrbException
68 * @throws IOException
69 */
70 // Called by Credentials
71 KrbAsReq(PrincipalName principal, EncryptionKey[] keys)
72 throws KrbException, IOException {
73 this(keys, // for pre-authentication
74 false, 0, null, null, // pre-auth values
75 defaultKDCOptions,
76 principal,
77 null, // PrincipalName sname
78 null, // KerberosTime from
79 null, // KerberosTime till
80 null, // KerberosTime rtime
81 null, // int[] eTypes
82 null, // HostAddresses addresses
83 null); // Ticket[] additionalTickets
84 }
85
86 /**
87 * Creates a KRB-AS-REQ to send to the default KDC
88 * with pre-authentication values
89 */
90 KrbAsReq(PrincipalName principal, EncryptionKey[] keys,
91 boolean pa_exists, int etype, byte[] salt, byte[] s2kparams)
92 throws KrbException, IOException {
93 this(keys, // for pre-authentication
94 pa_exists, etype, salt, s2kparams, // pre-auth values
95 defaultKDCOptions,
96 principal,
97 null, // PrincipalName sname
98 null, // KerberosTime from
99 null, // KerberosTime till
100 null, // KerberosTime rtime
101 null, // int[] eTypes
102 null, // HostAddresses addresses
103 null); // Ticket[] additionalTickets
104 }
105
106 private static int[] getETypesFromKeys(EncryptionKey[] keys) {
107 int[] types = new int[keys.length];
108 for (int i = 0; i < keys.length; i++) {
109 types[i] = keys[i].getEType();
110 }
111 return types;
112 }
113
114 // update with pre-auth info
115 public void updatePA(int etype, byte[] salt, byte[] params, PrincipalName name) {
116 // set the pre-auth values
117 pa_exists = true;
118 pa_etype = etype;
119 pa_salt = salt;
120 pa_s2kparams = params;
121
122 // update salt in PrincipalName
123 if (salt != null && salt.length > 0) {
124 String newSalt = new String(salt);
125 name.setSalt(newSalt);
126 if (DEBUG) {
127 System.out.println("Updated salt from pre-auth = " + name.getSalt());
128 }
129 }
130 PA_ENC_TIMESTAMP_REQUIRED = true;
131 }
132
133 // Used by Kinit
134 public KrbAsReq(
135 char[] password,
136 KDCOptions options,
137 PrincipalName cname,
138 PrincipalName sname,
139 KerberosTime from,
140 KerberosTime till,
141 KerberosTime rtime,
142 int[] eTypes,
143 HostAddresses addresses,
144 Ticket[] additionalTickets)
145 throws KrbException, IOException {
146 this(password,
147 false, 0, null, null, // pre-auth values
148 options,
149 cname,
150 sname, // PrincipalName sname
151 from, // KerberosTime from
152 till, // KerberosTime till
153 rtime, // KerberosTime rtime
154 eTypes, // int[] eTypes
155 addresses, // HostAddresses addresses
156 additionalTickets); // Ticket[] additionalTickets
157 }
158
159 // Used by Kinit
160 public KrbAsReq(
161 char[] password,
162 boolean pa_exists,
163 int etype,
164 byte[] salt,
165 byte[] s2kparams,
166 KDCOptions options,
167 PrincipalName cname,
168 PrincipalName sname,
169 KerberosTime from,
170 KerberosTime till,
171 KerberosTime rtime,
172 int[] eTypes,
173 HostAddresses addresses,
174 Ticket[] additionalTickets)
175 throws KrbException, IOException {
176
177 EncryptionKey[] keys = null;
178
179 // update with preauth info
180 if (pa_exists) {
181 updatePA(etype, salt, s2kparams, cname);
182 }
183
184 if (password != null) {
185 keys = EncryptionKey.acquireSecretKeys(password, cname.getSalt(), pa_exists,
186 pa_etype, pa_s2kparams);
187 }
188 if (DEBUG) {
189 System.out.println(">>>KrbAsReq salt is " + cname.getSalt());
190 }
191
192 try {
193 init(
194 keys,
195 options,
196 cname,
197 sname,
198 from,
199 till,
200 rtime,
201 eTypes,
202 addresses,
203 additionalTickets);
204 }
205 finally {
206 /*
207 * Its ok to destroy the key here because we created it and are
208 * now done with it.
209 */
210 if (keys != null) {
211 for (int i = 0; i < keys.length; i++) {
212 keys[i].destroy();
213 }
214 }
215 }
216 }
217
218 // Used in Kinit
219 public KrbAsReq(
220 EncryptionKey[] keys,
221 KDCOptions options,
222 PrincipalName cname,
223 PrincipalName sname,
224 KerberosTime from,
225 KerberosTime till,
226 KerberosTime rtime,
227 int[] eTypes,
228 HostAddresses addresses,
229 Ticket[] additionalTickets)
230 throws KrbException, IOException {
231 this(keys,
232 false, 0, null, null, // pre-auth values
233 options,
234 cname,
235 sname, // PrincipalName sname
236 from, // KerberosTime from
237 till, // KerberosTime till
238 rtime, // KerberosTime rtime
239 eTypes, // int[] eTypes
240 addresses, // HostAddresses addresses
241 additionalTickets); // Ticket[] additionalTickets
242 }
243
244 // Used by Kinit
245 public KrbAsReq(
246 EncryptionKey[] keys,
247 boolean pa_exists,
248 int etype,
249 byte[] salt,
250 byte[] s2kparams,
251 KDCOptions options,
252 PrincipalName cname,
253 PrincipalName sname,
254 KerberosTime from,
255 KerberosTime till,
256 KerberosTime rtime,
257 int[] eTypes,
258 HostAddresses addresses,
259 Ticket[] additionalTickets)
260 throws KrbException, IOException {
261
262 // update with preauth info
263 if (pa_exists) {
264 // update pre-auth info
265 updatePA(etype, salt, s2kparams, cname);
266
267 if (DEBUG) {
268 System.out.println(">>>KrbAsReq salt is " + cname.getSalt());
269 }
270 }
271
272 init(
273 keys,
274 options,
275 cname,
276 sname,
277 from,
278 till,
279 rtime,
280 eTypes,
281 addresses,
282 additionalTickets);
283 }
284
285 /*
286 private KrbAsReq(KDCOptions options,
287 PrincipalName cname,
288 PrincipalName sname,
289 KerberosTime from,
290 KerberosTime till,
291 KerberosTime rtime,
292 int[] eTypes,
293 HostAddresses addresses,
294 Ticket[] additionalTickets)
295 throws KrbException, IOException {
296 init(null,
297 options,
298 cname,
299 sname,
300 from,
301 till,
302 rtime,
303 eTypes,
304 addresses,
305 additionalTickets);
306 }
307*/
308
309 private void init(EncryptionKey[] keys,
310 KDCOptions options,
311 PrincipalName cname,
312 PrincipalName sname,
313 KerberosTime from,
314 KerberosTime till,
315 KerberosTime rtime,
316 int[] eTypes,
317 HostAddresses addresses,
318 Ticket[] additionalTickets )
319 throws KrbException, IOException {
320
321 // check if they are valid arguments. The optional fields should be
322 // consistent with settings in KDCOptions. Mar 17 2000
323 if (options.get(KDCOptions.FORWARDED) ||
324 options.get(KDCOptions.PROXY) ||
325 options.get(KDCOptions.ENC_TKT_IN_SKEY) ||
326 options.get(KDCOptions.RENEW) ||
327 options.get(KDCOptions.VALIDATE)) {
328 // this option is only specified in a request to the
329 // ticket-granting server
330 throw new KrbException(Krb5.KRB_AP_ERR_REQ_OPTIONS);
331 }
332 if (options.get(KDCOptions.POSTDATED)) {
333 // if (from == null)
334 // throw new KrbException(Krb5.KRB_AP_ERR_REQ_OPTIONS);
335 } else {
336 if (from != null) from = null;
337 }
338 if (options.get(KDCOptions.RENEWABLE)) {
339 // if (rtime == null)
340 // throw new KrbException(Krb5.KRB_AP_ERR_REQ_OPTIONS);
341 } else {
342 if (rtime != null) rtime = null;
343 }
344
345 princName = cname;
346
347 EncryptionKey key = null;
348 int[] tktETypes = null;
349 if (pa_exists && pa_etype != EncryptedData.ETYPE_NULL) {
350 if (DEBUG) {
351 System.out.println("Pre-Authenticaton: find key for etype = " + pa_etype);
352 }
353 key = EncryptionKey.findKey(pa_etype, keys);
354 tktETypes = new int[1];
355 tktETypes[0] = pa_etype;
356 } else {
357 tktETypes = EType.getDefaults("default_tkt_enctypes", keys);
358 key = EncryptionKey.findKey(tktETypes[0], keys);
359 }
360
361 PAData[] paData = null;
362 if (PA_ENC_TIMESTAMP_REQUIRED) {
363 if (DEBUG) {
364 System.out.println("AS-REQ: Add PA_ENC_TIMESTAMP now");
365 }
366 PAEncTSEnc ts = new PAEncTSEnc();
367 byte[] temp = ts.asn1Encode();
368 if (key != null) {
369 // Use first key in list
370 EncryptedData encTs = new EncryptedData(key, temp,
371 KeyUsage.KU_PA_ENC_TS);
372 paData = new PAData[1];
373 paData[0] = new PAData( Krb5.PA_ENC_TIMESTAMP,
374 encTs.asn1Encode());
375 }
376 }
377
378 if (DEBUG) {
379 System.out.println(">>> KrbAsReq calling createMessage");
380 }
381
382 if (eTypes == null) {
383 eTypes = tktETypes;
384 }
385
386 // check to use addresses in tickets
387 if (Config.getInstance().useAddresses()) {
388 KDC_EMPTY_ADDRESSES_ALLOWED = false;
389 }
390 // get the local InetAddress if required
391 if (addresses == null && !KDC_EMPTY_ADDRESSES_ALLOWED) {
392 addresses = HostAddresses.getLocalAddresses();
393 }
394
395 asReqMessg = createMessage(
396 paData,
397 options,
398 cname,
399 cname.getRealm(),
400 sname,
401 from,
402 till,
403 rtime,
404 eTypes,
405 addresses,
406 additionalTickets);
407 obuf = asReqMessg.asn1Encode();
408 }
409
410 /**
411 * Returns an AS-REP message corresponding to the AS-REQ that
412 * was sent.
413 * @param password The password that will be used to derive the
414 * secret key that will decrypt the AS-REP from the KDC.
415 * @exception KrbException if an error occurs while reading the data.
416 * @exception IOException if an I/O error occurs while reading encoded data.
417 */
418 public KrbAsRep getReply(char[] password)
419 throws KrbException, IOException {
420
421 if (password == null)
422 throw new KrbException(Krb5.API_INVALID_ARG);
423 KrbAsRep temp = null;
424 EncryptionKey[] keys = null;
425 try {
426 keys = EncryptionKey.acquireSecretKeys(password,
427 princName.getSalt(), pa_exists, pa_etype, pa_s2kparams);
428 temp = getReply(keys);
429 } finally {
430 /*
431 * Its ok to destroy the key here because we created it and are
432 * now done with it.
433 */
434 if (keys != null) {
435 for (int i = 0; i < keys.length; i++) {
436 keys[i].destroy();
437 }
438 }
439 }
440 return temp;
441 }
442
443 /**
444 * Sends an AS request to the realm of the client.
445 * returns the KDC hostname that the request was sent to
446 */
447
448 public String send()
449 throws IOException, KrbException
450 {
451 String realmStr = null;
452 if (princName != null)
453 realmStr = princName.getRealmString();
454
455 return (send(realmStr));
456 }
457
458 /**
459 * Returns an AS-REP message corresponding to the AS-REQ that
460 * was sent.
461 * @param keys The secret keys that will decrypt the AS-REP from
462 * the KDC; key selected depends on etype used to encrypt data.
463 * @exception KrbException if an error occurs while reading the data.
464 * @exception IOException if an I/O error occurs while reading encoded
465 * data.
466 *
467 */
468 public KrbAsRep getReply(EncryptionKey[] keys)
469 throws KrbException,IOException {
470 return new KrbAsRep(ibuf, keys, this);
471 }
472
473 private ASReq createMessage(
474 PAData[] paData,
475 KDCOptions kdc_options,
476 PrincipalName cname,
477 Realm crealm,
478 PrincipalName sname,
479 KerberosTime from,
480 KerberosTime till,
481 KerberosTime rtime,
482 int[] eTypes,
483 HostAddresses addresses,
484 Ticket[] additionalTickets
485 ) throws Asn1Exception, KrbApErrException,
486 RealmException, UnknownHostException, IOException {
487
488 if (DEBUG) {
489 System.out.println(">>> KrbAsReq in createMessage");
490 }
491
492 PrincipalName req_sname = null;
493 if (sname == null) {
494 if (crealm == null) {
495 throw new RealmException(Krb5.REALM_NULL,
496 "default realm not specified ");
497 }
498 req_sname = new PrincipalName(
499 "krbtgt" +
500 PrincipalName.NAME_COMPONENT_SEPARATOR +
501 crealm.toString(),
502 PrincipalName.KRB_NT_SRV_INST);
503 } else
504 req_sname = sname;
505
506 KerberosTime req_till = null;
507 if (till == null) {
508 req_till = new KerberosTime();
509 } else {
510 req_till = till;
511 }
512
513 KDCReqBody kdc_req_body = new KDCReqBody(kdc_options,
514 cname,
515 crealm,
516 req_sname,
517 from,
518 req_till,
519 rtime,
520 Nonce.value(),
521 eTypes,
522 addresses,
523 null,
524 additionalTickets);
525
526 return new ASReq(
527 paData,
528 kdc_req_body);
529 }
530
531 ASReq getMessage() {
532 return asReqMessg;
533 }
534}