1 /*
2 * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved. */
3 /*
4 * lib/krb5/krb/get_in_tkt.c
5 *
6 * Copyright 1990,1991, 2003 by the Massachusetts Institute of Technology.
7 * All Rights Reserved.
8 *
9 * Export of this software from the United States of America may
10 * require a specific license from the United States Government.
11 * It is the responsibility of any person or organization contemplating
12 * export to obtain such a license before exporting.
13 *
14 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
15 * distribute this software and its documentation for any purpose and
16 * without fee is hereby granted, provided that the above copyright
17 * notice appear in all copies and that both that copyright notice and
18 * this permission notice appear in supporting documentation, and that
19 * the name of M.I.T. not be used in advertising or publicity pertaining
20 * to distribution of the software without specific, written prior
21 * permission. Furthermore if you modify this software you must label
22 * your software as modified software and not distribute it in such a
23 * fashion that it might be confused with the original M.I.T. software.
24 * M.I.T. makes no representations about the suitability of
25 * this software for any purpose. It is provided "as is" without express
26 * or implied warranty.
27 *
28 *
29 * krb5_get_in_tkt()
30 */
31
32 #include <string.h>
33 #include <ctype.h>
34 #include "k5-int.h"
35 #include "int-proto.h"
36 #include "os-proto.h"
37 #include <locale.h>
38 #include <syslog.h>
39
40 /*
41 All-purpose initial ticket routine, usually called via
42 krb5_get_in_tkt_with_password or krb5_get_in_tkt_with_skey.
43
44 Attempts to get an initial ticket for creds->client to use server
45 creds->server, (realm is taken from creds->client), with options
46 options, and using creds->times.starttime, creds->times.endtime,
47 creds->times.renew_till as from, till, and rtime.
48 creds->times.renew_till is ignored unless the RENEWABLE option is requested.
49
50 key_proc is called to fill in the key to be used for decryption.
51 keyseed is passed on to key_proc.
52
53 decrypt_proc is called to perform the decryption of the response (the
54 encrypted part is in dec_rep->enc_part; the decrypted part should be
55 allocated and filled into dec_rep->enc_part2
56 arg is passed on to decrypt_proc.
57
58 If addrs is non-NULL, it is used for the addresses requested. If it is
59 null, the system standard addresses are used.
60
61 A succesful call will place the ticket in the credentials cache ccache
62 and fill in creds with the ticket information used/returned..
63
64 returns system errors, encryption errors
65
66 */
67
68 /* Solaris Kerberos */
69 #define max(a, b) ((a) > (b) ? (a) : (b))
70
71 /* some typedef's for the function args to make things look a bit cleaner */
72
73 typedef krb5_error_code (*git_key_proc) (krb5_context,
74 const krb5_enctype,
75 krb5_data *,
76 krb5_const_pointer,
77 krb5_keyblock **);
78
79 typedef krb5_error_code (*git_decrypt_proc) (krb5_context,
80 const krb5_keyblock *,
81 krb5_const_pointer,
82 krb5_kdc_rep * );
83
84 static krb5_error_code make_preauth_list (krb5_context,
85 krb5_preauthtype *,
86 int, krb5_pa_data ***);
87 static krb5_error_code sort_krb5_padata_sequence(krb5_context context,
88 krb5_data *realm,
89 krb5_pa_data **padata);
90
91 /*
92 * This function performs 32 bit bounded addition so we can generate
93 * lifetimes without overflowing krb5_int32
94 */
krb5int_addint32(krb5_int32 x,krb5_int32 y)95 static krb5_int32 krb5int_addint32 (krb5_int32 x, krb5_int32 y)
96 {
97 if ((x > 0) && (y > (KRB5_INT32_MAX - x))) {
98 /* sum will be be greater than KRB5_INT32_MAX */
99 return KRB5_INT32_MAX;
100 } else if ((x < 0) && (y < (KRB5_INT32_MIN - x))) {
101 /* sum will be less than KRB5_INT32_MIN */
102 return KRB5_INT32_MIN;
103 }
104
105 return x + y;
106 }
107
108 /*
109 * This function sends a request to the KDC, and gets back a response;
110 * the response is parsed into ret_err_reply or ret_as_reply if the
111 * reponse is a KRB_ERROR or a KRB_AS_REP packet. If it is some other
112 * unexpected response, an error is returned.
113 */
114 static krb5_error_code
send_as_request2(krb5_context context,krb5_kdc_req * request,krb5_error ** ret_err_reply,krb5_kdc_rep ** ret_as_reply,int * use_master,char ** hostname_used)115 send_as_request2(krb5_context context,
116 krb5_kdc_req *request,
117 krb5_error ** ret_err_reply,
118 krb5_kdc_rep ** ret_as_reply,
119 int *use_master,
120 char **hostname_used)
121
122 {
123 krb5_kdc_rep *as_reply = 0;
124 krb5_error_code retval;
125 krb5_data *packet = 0;
126 krb5_data reply;
127 char k4_version; /* same type as *(krb5_data::data) */
128 int tcp_only = 0;
129 krb5_timestamp time_now;
130
131 reply.data = 0;
132
133 /* set the nonce if the caller expects us to do it */
134 if (request->nonce == 0) {
135 if ((retval = krb5_timeofday(context, &time_now)))
136 goto cleanup;
137 request->nonce = (krb5_int32) time_now;
138 }
139
140 /* encode & send to KDC */
141 if ((retval = encode_krb5_as_req(request, &packet)) != 0)
142 goto cleanup;
143
144 k4_version = packet->data[0];
145 send_again:
146 retval = krb5_sendto_kdc2(context, packet,
147 krb5_princ_realm(context, request->client),
148 &reply, use_master, tcp_only, hostname_used);
149 if (retval)
150 goto cleanup;
151
152 /* now decode the reply...could be error or as_rep */
153 if (krb5_is_krb_error(&reply)) {
154 krb5_error *err_reply;
155
156 if ((retval = decode_krb5_error(&reply, &err_reply)))
157 /* some other error code--??? */
158 goto cleanup;
159
160 if (ret_err_reply) {
161 if (err_reply->error == KRB_ERR_RESPONSE_TOO_BIG
162 && tcp_only == 0) {
163 tcp_only = 1;
164 krb5_free_error(context, err_reply);
165 free(reply.data);
166 reply.data = 0;
167 goto send_again;
168 }
169 *ret_err_reply = err_reply;
170 } else {
171 krb5_free_error(context, err_reply);
172 err_reply = NULL;
173 }
174 goto cleanup;
175 }
176
177 /*
178 * Check to make sure it isn't a V4 reply.
179 */
180 if (!krb5_is_as_rep(&reply)) {
181 /* these are in <kerberosIV/prot.h> as well but it isn't worth including. */
182 #define V4_KRB_PROT_VERSION 4
183 #define V4_AUTH_MSG_ERR_REPLY (5<<1)
184 /* check here for V4 reply */
185 unsigned int t_switch;
186
187 /* From v4 g_in_tkt.c: This used to be
188 switch (pkt_msg_type(rpkt) & ~1) {
189 but SCO 3.2v4 cc compiled that incorrectly. */
190 t_switch = reply.data[1];
191 t_switch &= ~1;
192
193 if (t_switch == V4_AUTH_MSG_ERR_REPLY
194 && (reply.data[0] == V4_KRB_PROT_VERSION
195 || reply.data[0] == k4_version)) {
196 retval = KRB5KRB_AP_ERR_V4_REPLY;
197 } else {
198 retval = KRB5KRB_AP_ERR_MSG_TYPE;
199 }
200 goto cleanup;
201 }
202
203 /* It must be a KRB_AS_REP message, or an bad returned packet */
204 if ((retval = decode_krb5_as_rep(&reply, &as_reply)))
205 /* some other error code ??? */
206 goto cleanup;
207
208 if (as_reply->msg_type != KRB5_AS_REP) {
209 retval = KRB5KRB_AP_ERR_MSG_TYPE;
210 krb5_free_kdc_rep(context, as_reply);
211 goto cleanup;
212 }
213
214 if (ret_as_reply)
215 *ret_as_reply = as_reply;
216 else
217 krb5_free_kdc_rep(context, as_reply);
218
219 cleanup:
220 if (packet)
221 krb5_free_data(context, packet);
222 if (reply.data)
223 free(reply.data);
224 return retval;
225 }
226
227 static krb5_error_code
send_as_request(krb5_context context,krb5_kdc_req * request,krb5_error ** ret_err_reply,krb5_kdc_rep ** ret_as_reply,int * use_master)228 send_as_request(krb5_context context,
229 krb5_kdc_req *request,
230 krb5_error ** ret_err_reply,
231 krb5_kdc_rep ** ret_as_reply,
232 int *use_master)
233 {
234 return send_as_request2(context,
235 request,
236 ret_err_reply,
237 ret_as_reply,
238 use_master,
239 NULL);
240 }
241
242 static krb5_error_code
decrypt_as_reply(krb5_context context,krb5_kdc_req * request,krb5_kdc_rep * as_reply,git_key_proc key_proc,krb5_const_pointer keyseed,krb5_keyblock * key,git_decrypt_proc decrypt_proc,krb5_const_pointer decryptarg)243 decrypt_as_reply(krb5_context context,
244 krb5_kdc_req *request,
245 krb5_kdc_rep *as_reply,
246 git_key_proc key_proc,
247 krb5_const_pointer keyseed,
248 krb5_keyblock * key,
249 git_decrypt_proc decrypt_proc,
250 krb5_const_pointer decryptarg)
251 {
252 krb5_error_code retval;
253 krb5_keyblock * decrypt_key = 0;
254 krb5_data salt;
255
256 if (as_reply->enc_part2)
257 return 0;
258
259 if (key)
260 decrypt_key = key;
261 /* Solaris Kerberos */
262 else if (request != NULL) {
263 if ((retval = krb5_principal2salt(context, request->client, &salt)))
264 return(retval);
265
266 retval = (*key_proc)(context, as_reply->enc_part.enctype,
267 &salt, keyseed, &decrypt_key);
268 krb5_xfree(salt.data);
269 if (retval)
270 goto cleanup;
271 } else {
272 KRB5_LOG0(KRB5_ERR, "decrypt_as_reply() end, "
273 "error key == NULL and request == NULL");
274 return (EINVAL);
275 }
276
277 /*
278 * Solaris kerberos: Overwriting the decrypt_key->enctype because the
279 * decrypt key's enctype may not be an exact match with the enctype that the
280 * KDC used to encrypt this part of the AS reply. This assumes the
281 * as_reply->enc_part.enctype has been validated which is done by checking
282 * to see if the enctype that the KDC sent back in the as_reply is one of
283 * the enctypes originally requested. Note, if request is NULL then the
284 * as_reply->enc_part.enctype could not be validated.
285 */
286
287 if (request != NULL) {
288 if (is_in_keytype(request->ktype, request->nktypes,
289 as_reply->enc_part.enctype)) {
290
291 decrypt_key->enctype = as_reply->enc_part.enctype;
292
293 } else {
294 KRB5_LOG0(KRB5_ERR, "decrypt_as_reply() end, "
295 "error is_in_keytype() returned false");
296 retval = KRB5_BAD_ENCTYPE;
297 goto cleanup;
298 }
299 }
300
301 if ((retval = (*decrypt_proc)(context, decrypt_key, decryptarg, as_reply))){
302 KRB5_LOG(KRB5_ERR, "decrypt_as_reply() error (*decrypt_proc)() retval "
303 "= %d", retval);
304 goto cleanup;
305 }
306
307 cleanup:
308 if (!key && decrypt_key)
309 krb5_free_keyblock(context, decrypt_key);
310 return (retval);
311 }
312
313 static krb5_error_code
verify_as_reply(krb5_context context,krb5_timestamp time_now,krb5_kdc_req * request,krb5_kdc_rep * as_reply)314 verify_as_reply(krb5_context context,
315 krb5_timestamp time_now,
316 krb5_kdc_req *request,
317 krb5_kdc_rep *as_reply)
318 {
319 krb5_error_code retval;
320
321 /* check the contents for sanity: */
322 if (!as_reply->enc_part2->times.starttime)
323 as_reply->enc_part2->times.starttime =
324 as_reply->enc_part2->times.authtime;
325
326 if (!krb5_principal_compare(context, as_reply->client, request->client)
327 || !krb5_principal_compare(context, as_reply->enc_part2->server, request->server)
328 || !krb5_principal_compare(context, as_reply->ticket->server, request->server)
329 || (request->nonce != as_reply->enc_part2->nonce)
330 /* XXX check for extraneous flags */
331 /* XXX || (!krb5_addresses_compare(context, addrs, as_reply->enc_part2->caddrs)) */
332 || ((request->kdc_options & KDC_OPT_POSTDATED) &&
333 (request->from != 0) &&
334 (request->from != as_reply->enc_part2->times.starttime))
335 || ((request->till != 0) &&
336 (as_reply->enc_part2->times.endtime > request->till))
337 || ((request->kdc_options & KDC_OPT_RENEWABLE) &&
338 /*
339 * Solaris Kerberos: Here we error only if renewable_ok was not set.
340 */
341 !(request->kdc_options & KDC_OPT_RENEWABLE_OK) &&
342 (as_reply->enc_part2->flags & KDC_OPT_RENEWABLE) &&
343 (request->rtime != 0) &&
344 (as_reply->enc_part2->times.renew_till > request->rtime))
345 || ((request->kdc_options & KDC_OPT_RENEWABLE_OK) &&
346 !(request->kdc_options & KDC_OPT_RENEWABLE) &&
347 (as_reply->enc_part2->flags & KDC_OPT_RENEWABLE) &&
348 (request->till != 0) &&
349 (as_reply->enc_part2->times.renew_till > request->till))
350 /*
351 * Solaris Kerberos: renew_till should never be greater than till or
352 * rtime.
353 */
354 || ((request->kdc_options & KDC_OPT_RENEWABLE_OK) &&
355 (as_reply->enc_part2->flags & KDC_OPT_RENEWABLE) &&
356 (request->till != 0) &&
357 (request->rtime != 0) &&
358 (as_reply->enc_part2->times.renew_till > max(request->till,
359 request->rtime)))
360 )
361 return KRB5_KDCREP_MODIFIED;
362
363 if (context->library_options & KRB5_LIBOPT_SYNC_KDCTIME) {
364 retval = krb5_set_real_time(context,
365 as_reply->enc_part2->times.authtime, 0);
366 if (retval)
367 return retval;
368 } else {
369 if ((request->from == 0) &&
370 (labs(as_reply->enc_part2->times.starttime - time_now)
371 > context->clockskew))
372 return (KRB5_KDCREP_SKEW);
373 }
374 return 0;
375 }
376
377 /*ARGSUSED*/
378 static krb5_error_code
stash_as_reply(krb5_context context,krb5_timestamp time_now,krb5_kdc_req * request,krb5_kdc_rep * as_reply,krb5_creds * creds,krb5_ccache ccache)379 stash_as_reply(krb5_context context,
380 krb5_timestamp time_now,
381 krb5_kdc_req *request,
382 krb5_kdc_rep *as_reply,
383 krb5_creds * creds,
384 krb5_ccache ccache)
385 {
386 krb5_error_code retval;
387 krb5_data * packet;
388 krb5_principal client;
389 krb5_principal server;
390
391 client = NULL;
392 server = NULL;
393
394 if (!creds->client)
395 if ((retval = krb5_copy_principal(context, as_reply->client, &client)))
396 goto cleanup;
397
398 if (!creds->server)
399 if ((retval = krb5_copy_principal(context, as_reply->enc_part2->server,
400 &server)))
401 goto cleanup;
402
403 /* fill in the credentials */
404 if ((retval = krb5_copy_keyblock_contents(context,
405 as_reply->enc_part2->session,
406 &creds->keyblock)))
407 goto cleanup;
408
409 creds->times = as_reply->enc_part2->times;
410 creds->is_skey = FALSE; /* this is an AS_REQ, so cannot
411 be encrypted in skey */
412 creds->ticket_flags = as_reply->enc_part2->flags;
413 if ((retval = krb5_copy_addresses(context, as_reply->enc_part2->caddrs,
414 &creds->addresses)))
415 goto cleanup;
416
417 creds->second_ticket.length = 0;
418 creds->second_ticket.data = 0;
419
420 if ((retval = encode_krb5_ticket(as_reply->ticket, &packet)))
421 goto cleanup;
422
423 creds->ticket = *packet;
424 krb5_xfree(packet);
425
426 /* store it in the ccache! */
427 if (ccache) /* Solaris Kerberos */
428 if ((retval = krb5_cc_store_cred(context, ccache, creds)) !=0)
429 goto cleanup;
430
431 if (!creds->client)
432 creds->client = client;
433 if (!creds->server)
434 creds->server = server;
435
436 cleanup:
437 if (retval) {
438 if (client)
439 krb5_free_principal(context, client);
440 if (server)
441 krb5_free_principal(context, server);
442 if (creds->keyblock.contents) {
443 memset((char *)creds->keyblock.contents, 0,
444 creds->keyblock.length);
445 krb5_xfree(creds->keyblock.contents);
446 creds->keyblock.contents = 0;
447 creds->keyblock.length = 0;
448 }
449 if (creds->ticket.data) {
450 krb5_xfree(creds->ticket.data);
451 creds->ticket.data = 0;
452 }
453 if (creds->addresses) {
454 krb5_free_addresses(context, creds->addresses);
455 creds->addresses = 0;
456 }
457 }
458 return (retval);
459 }
460
461 /*ARGSUSED*/
462 static krb5_error_code
make_preauth_list(krb5_context context,krb5_preauthtype * ptypes,int nptypes,krb5_pa_data *** ret_list)463 make_preauth_list(krb5_context context,
464 krb5_preauthtype * ptypes,
465 int nptypes,
466 krb5_pa_data *** ret_list)
467 {
468 krb5_preauthtype * ptypep;
469 krb5_pa_data ** preauthp;
470 int i;
471
472 if (nptypes < 0) {
473 for (nptypes=0, ptypep = ptypes; *ptypep; ptypep++, nptypes++)
474 ;
475 }
476
477 /* allocate space for a NULL to terminate the list */
478
479 if ((preauthp =
480 (krb5_pa_data **) malloc((nptypes+1)*sizeof(krb5_pa_data *))) == NULL)
481 return(ENOMEM);
482
483 for (i=0; i<nptypes; i++) {
484 if ((preauthp[i] =
485 (krb5_pa_data *) malloc(sizeof(krb5_pa_data))) == NULL) {
486 for (; i>=0; i++)
487 free(preauthp[i]);
488 free(preauthp);
489 return (ENOMEM);
490 }
491 preauthp[i]->magic = KV5M_PA_DATA;
492 preauthp[i]->pa_type = ptypes[i];
493 preauthp[i]->length = 0;
494 preauthp[i]->contents = 0;
495 }
496
497 /* fill in the terminating NULL */
498
499 preauthp[nptypes] = NULL;
500
501 *ret_list = preauthp;
502 return 0;
503 }
504
505 #define MAX_IN_TKT_LOOPS 16
506 static const krb5_enctype get_in_tkt_enctypes[] = {
507 ENCTYPE_DES3_CBC_SHA1,
508 ENCTYPE_ARCFOUR_HMAC,
509 ENCTYPE_DES_CBC_MD5,
510 ENCTYPE_DES_CBC_MD4,
511 ENCTYPE_DES_CBC_CRC,
512 0
513 };
514
515 krb5_error_code KRB5_CALLCONV
krb5_get_in_tkt(krb5_context context,const krb5_flags options,krb5_address * const * addrs,krb5_enctype * ktypes,krb5_preauthtype * ptypes,git_key_proc key_proc,krb5_const_pointer keyseed,git_decrypt_proc decrypt_proc,krb5_const_pointer decryptarg,krb5_creds * creds,krb5_ccache ccache,krb5_kdc_rep ** ret_as_reply)516 krb5_get_in_tkt(krb5_context context,
517 const krb5_flags options,
518 krb5_address * const * addrs,
519 krb5_enctype * ktypes,
520 krb5_preauthtype * ptypes,
521 git_key_proc key_proc,
522 krb5_const_pointer keyseed,
523 git_decrypt_proc decrypt_proc,
524 krb5_const_pointer decryptarg,
525 krb5_creds * creds,
526 krb5_ccache ccache,
527 krb5_kdc_rep ** ret_as_reply)
528 {
529 krb5_error_code retval;
530 krb5_timestamp time_now;
531 krb5_keyblock * decrypt_key = 0;
532 krb5_kdc_req request;
533 krb5_pa_data **padata = 0;
534 krb5_error * err_reply;
535 krb5_kdc_rep * as_reply = 0;
536 krb5_pa_data ** preauth_to_use = 0;
537 int loopcount = 0;
538 krb5_int32 do_more = 0;
539 int use_master = 0;
540 char *hostname_used = NULL;
541
542 if (! krb5_realm_compare(context, creds->client, creds->server)) {
543 /* Solaris Kerberos */
544 char *s_name = NULL;
545 char *c_name = NULL;
546 krb5_error_code serr, cerr;
547 serr = krb5_unparse_name(context, creds->server, &s_name);
548 cerr = krb5_unparse_name(context, creds->client, &c_name);
549 krb5_set_error_message(context, KRB5_IN_TKT_REALM_MISMATCH,
550 dgettext(TEXT_DOMAIN,
551 "Client/server realm mismatch in initial ticket request: '%s' requesting ticket '%s'"),
552 cerr ? "unknown" : c_name,
553 serr ? "unknown" : s_name);
554 if (s_name)
555 krb5_free_unparsed_name(context, s_name);
556 if (c_name)
557 krb5_free_unparsed_name(context, c_name);
558 return KRB5_IN_TKT_REALM_MISMATCH;
559 }
560
561 if (ret_as_reply)
562 *ret_as_reply = 0;
563
564 /*
565 * Set up the basic request structure
566 */
567 request.magic = KV5M_KDC_REQ;
568 request.msg_type = KRB5_AS_REQ;
569 request.addresses = 0;
570 request.ktype = 0;
571 request.padata = 0;
572 if (addrs)
573 request.addresses = (krb5_address **) addrs;
574 else
575 if ((retval = krb5_os_localaddr(context, &request.addresses)))
576 goto cleanup;
577 request.kdc_options = options;
578 request.client = creds->client;
579 request.server = creds->server;
580 request.nonce = 0;
581 request.from = creds->times.starttime;
582 request.till = creds->times.endtime;
583 request.rtime = creds->times.renew_till;
584
585 request.ktype = malloc (sizeof(get_in_tkt_enctypes));
586 if (request.ktype == NULL) {
587 retval = ENOMEM;
588 goto cleanup;
589 }
590 memcpy(request.ktype, get_in_tkt_enctypes, sizeof(get_in_tkt_enctypes));
591 for (request.nktypes = 0;request.ktype[request.nktypes];request.nktypes++);
592 if (ktypes) {
593 int i, req, next = 0;
594 for (req = 0; ktypes[req]; req++) {
595 if (ktypes[req] == request.ktype[next]) {
596 next++;
597 continue;
598 }
599 for (i = next + 1; i < request.nktypes; i++)
600 if (ktypes[req] == request.ktype[i]) {
601 /* Found the enctype we want, but not in the
602 position we want. Move it, but keep the old
603 one from the desired slot around in case it's
604 later in our requested-ktypes list. */
605 krb5_enctype t;
606 t = request.ktype[next];
607 request.ktype[next] = request.ktype[i];
608 request.ktype[i] = t;
609 next++;
610 break;
611 }
612 /* If we didn't find it, don't do anything special, just
613 drop it. */
614 }
615 request.ktype[next] = 0;
616 request.nktypes = next;
617 }
618 request.authorization_data.ciphertext.length = 0;
619 request.authorization_data.ciphertext.data = 0;
620 request.unenc_authdata = 0;
621 request.second_ticket = 0;
622
623 /*
624 * If a list of preauth types are passed in, convert it to a
625 * preauth_to_use list.
626 */
627 if (ptypes) {
628 retval = make_preauth_list(context, ptypes, -1, &preauth_to_use);
629 if (retval)
630 goto cleanup;
631 }
632
633 while (1) {
634 if (loopcount++ > MAX_IN_TKT_LOOPS) {
635 retval = KRB5_GET_IN_TKT_LOOP;
636 /* Solaris Kerberos */
637 {
638 char *s_name = NULL;
639 char *c_name = NULL;
640 krb5_error_code serr, cerr;
641 serr = krb5_unparse_name(context, creds->server, &s_name);
642 cerr = krb5_unparse_name(context, creds->client, &c_name);
643 krb5_set_error_message(context, retval,
644 dgettext(TEXT_DOMAIN,
645 "Looping detected getting ticket: '%s' requesting ticket '%s'. Max loops is %d. Make sure a KDC is available"),
646 cerr ? "unknown" : c_name,
647 serr ? "unknown" : s_name,
648 MAX_IN_TKT_LOOPS);
649 if (s_name)
650 krb5_free_unparsed_name(context, s_name);
651 if (c_name)
652 krb5_free_unparsed_name(context, c_name);
653 }
654 goto cleanup;
655 }
656
657 if ((retval = krb5_obtain_padata(context, preauth_to_use, key_proc,
658 keyseed, creds, &request)) != 0)
659 goto cleanup;
660 if (preauth_to_use)
661 krb5_free_pa_data(context, preauth_to_use);
662 preauth_to_use = 0;
663
664 err_reply = 0;
665 as_reply = 0;
666
667 if ((retval = krb5_timeofday(context, &time_now)))
668 goto cleanup;
669
670 /*
671 * XXX we know they are the same size... and we should do
672 * something better than just the current time
673 */
674 request.nonce = (krb5_int32) time_now;
675
676 if ((retval = send_as_request2(context, &request, &err_reply,
677 &as_reply, &use_master,
678 &hostname_used)))
679 goto cleanup;
680
681 if (err_reply) {
682 if (err_reply->error == KDC_ERR_PREAUTH_REQUIRED &&
683 err_reply->e_data.length > 0) {
684 retval = decode_krb5_padata_sequence(&err_reply->e_data,
685 &preauth_to_use);
686 krb5_free_error(context, err_reply);
687 err_reply = NULL;
688 if (retval)
689 goto cleanup;
690 retval = sort_krb5_padata_sequence(context,
691 &request.server->realm,
692 padata);
693 if (retval)
694 goto cleanup;
695 continue;
696 } else {
697 retval = (krb5_error_code) err_reply->error
698 + ERROR_TABLE_BASE_krb5;
699 krb5_free_error(context, err_reply);
700 err_reply = NULL;
701 goto cleanup;
702 }
703 } else if (!as_reply) {
704 retval = KRB5KRB_AP_ERR_MSG_TYPE;
705 goto cleanup;
706 }
707 if ((retval = krb5_process_padata(context, &request, as_reply,
708 key_proc, keyseed, decrypt_proc,
709 &decrypt_key, creds,
710 &do_more)) != 0)
711 goto cleanup;
712
713 if (!do_more)
714 break;
715 }
716
717 if ((retval = decrypt_as_reply(context, &request, as_reply, key_proc,
718 keyseed, decrypt_key, decrypt_proc,
719 decryptarg)))
720 goto cleanup;
721
722 if ((retval = verify_as_reply(context, time_now, &request, as_reply)))
723 goto cleanup;
724
725 if ((retval = stash_as_reply(context, time_now, &request, as_reply,
726 creds, ccache)))
727 goto cleanup;
728
729 cleanup:
730 if (request.ktype)
731 free(request.ktype);
732 if (!addrs && request.addresses)
733 krb5_free_addresses(context, request.addresses);
734 if (request.padata)
735 krb5_free_pa_data(context, request.padata);
736 if (padata)
737 krb5_free_pa_data(context, padata);
738 if (preauth_to_use)
739 krb5_free_pa_data(context, preauth_to_use);
740 if (decrypt_key)
741 krb5_free_keyblock(context, decrypt_key);
742 if (as_reply) {
743 if (ret_as_reply)
744 *ret_as_reply = as_reply;
745 else
746 krb5_free_kdc_rep(context, as_reply);
747 }
748 if (hostname_used)
749 free(hostname_used);
750
751 return (retval);
752 }
753
754 /* begin libdefaults parsing code. This should almost certainly move
755 somewhere else, but I don't know where the correct somewhere else
756 is yet. */
757
758 /* XXX Duplicating this is annoying; try to work on a better way.*/
759 static const char *const conf_yes[] = {
760 "y", "yes", "true", "t", "1", "on",
761 0,
762 };
763
764 static const char *const conf_no[] = {
765 "n", "no", "false", "nil", "0", "off",
766 0,
767 };
768
769 int
_krb5_conf_boolean(const char * s)770 _krb5_conf_boolean(const char *s)
771 {
772 const char *const *p;
773
774 for(p=conf_yes; *p; p++) {
775 if (!strcasecmp(*p,s))
776 return 1;
777 }
778
779 for(p=conf_no; *p; p++) {
780 if (!strcasecmp(*p,s))
781 return 0;
782 }
783
784 /* Default to "no" */
785 return 0;
786 }
787
788 static krb5_error_code
krb5_libdefault_string(krb5_context context,const krb5_data * realm,const char * option,char ** ret_value)789 krb5_libdefault_string(krb5_context context, const krb5_data *realm,
790 const char *option, char **ret_value)
791 {
792 profile_t profile;
793 const char *names[5];
794 char **nameval = NULL;
795 krb5_error_code retval;
796 char realmstr[1024];
797
798 if (realm->length > sizeof(realmstr)-1)
799 return(EINVAL);
800
801 strncpy(realmstr, realm->data, realm->length);
802 realmstr[realm->length] = '\0';
803
804 if (!context || (context->magic != KV5M_CONTEXT))
805 return KV5M_CONTEXT;
806
807 profile = context->profile;
808
809 /* Solaris Kerberos */
810 names[0] = "realms";
811
812 /*
813 * Try number one:
814 *
815 * [realms]
816 * REALM = {
817 * option = <boolean>
818 * }
819 */
820
821 names[1] = realmstr;
822 names[2] = option;
823 names[3] = 0;
824 retval = profile_get_values(profile, names, &nameval);
825 if (retval == 0 && nameval && nameval[0])
826 goto goodbye;
827
828 /*
829 * Try number two:
830 *
831 * [libdefaults]
832 * option = <boolean>
833 */
834
835 names[0] = "libdefaults";
836 names[1] = option;
837 names[2] = 0;
838 retval = profile_get_values(profile, names, &nameval);
839 if (retval == 0 && nameval && nameval[0])
840 goto goodbye;
841
842 goodbye:
843 if (!nameval)
844 return(ENOENT);
845
846 if (!nameval[0]) {
847 retval = ENOENT;
848 } else {
849 *ret_value = malloc(strlen(nameval[0]) + 1);
850 if (!*ret_value)
851 retval = ENOMEM;
852 else
853 strcpy(*ret_value, nameval[0]);
854 }
855
856 profile_free_list(nameval);
857
858 return retval;
859 }
860
861 /* not static so verify_init_creds() can call it */
862 /* as well as the DNS code */
863
864 krb5_error_code
krb5_libdefault_boolean(krb5_context context,const krb5_data * realm,const char * option,int * ret_value)865 krb5_libdefault_boolean(krb5_context context, const krb5_data *realm,
866 const char *option, int *ret_value)
867 {
868 char *string = NULL;
869 krb5_error_code retval;
870
871 retval = krb5_libdefault_string(context, realm, option, &string);
872
873 if (retval)
874 return(retval);
875
876 *ret_value = _krb5_conf_boolean(string);
877 free(string);
878
879 return(0);
880 }
881
882 /* Sort a pa_data sequence so that types named in the "preferred_preauth_types"
883 * libdefaults entry are listed before any others. */
884 static krb5_error_code
sort_krb5_padata_sequence(krb5_context context,krb5_data * realm,krb5_pa_data ** padata)885 sort_krb5_padata_sequence(krb5_context context, krb5_data *realm,
886 krb5_pa_data **padata)
887 {
888 int i, j, base;
889 krb5_error_code ret;
890 const char *p;
891 long l;
892 char *q, *preauth_types = NULL;
893 krb5_pa_data *tmp;
894 int need_free_string = 1;
895
896 if ((padata == NULL) || (padata[0] == NULL)) {
897 return 0;
898 }
899
900 ret = krb5_libdefault_string(context, realm, "preferred_preauth_types",
901 &preauth_types);
902 if ((ret != 0) || (preauth_types == NULL)) {
903 /* Try to use PKINIT first. */
904 preauth_types = "17, 16, 15, 14";
905 need_free_string = 0;
906 }
907
908 #ifdef DEBUG
909 fprintf (stderr, "preauth data types before sorting:");
910 for (i = 0; padata[i]; i++) {
911 fprintf (stderr, " %d", padata[i]->pa_type);
912 }
913 fprintf (stderr, "\n");
914 #endif
915
916 base = 0;
917 for (p = preauth_types; *p != '\0';) {
918 /* skip whitespace to find an entry */
919 p += strspn(p, ", ");
920 if (*p != '\0') {
921 /* see if we can extract a number */
922 l = strtol(p, &q, 10);
923 if ((q != NULL) && (q > p)) {
924 /* got a valid number; search for a matchin entry */
925 for (i = base; padata[i] != NULL; i++) {
926 /* bubble the matching entry to the front of the list */
927 if (padata[i]->pa_type == l) {
928 tmp = padata[i];
929 for (j = i; j > base; j--)
930 padata[j] = padata[j - 1];
931 padata[base] = tmp;
932 base++;
933 break;
934 }
935 }
936 p = q;
937 } else {
938 break;
939 }
940 }
941 }
942 if (need_free_string)
943 free(preauth_types);
944
945 #ifdef DEBUG
946 fprintf (stderr, "preauth data types after sorting:");
947 for (i = 0; padata[i]; i++)
948 fprintf (stderr, " %d", padata[i]->pa_type);
949 fprintf (stderr, "\n");
950 #endif
951
952 return 0;
953 }
954
955 /*
956 * Solaris Kerberos
957 * Return 1 if any char in string is lower-case.
958 */
959 static int
is_lower_case(char * s)960 is_lower_case(char *s)
961 {
962 if (!s)
963 return 0;
964
965 while (*s) {
966 if (islower((int)*s))
967 return 1;
968 s++;
969 }
970 return 0;
971 }
972
973 krb5_error_code KRB5_CALLCONV
krb5_get_init_creds(krb5_context context,krb5_creds * creds,krb5_principal client,krb5_prompter_fct prompter,void * prompter_data,krb5_deltat start_time,char * in_tkt_service,krb5_gic_opt_ext * options,krb5_gic_get_as_key_fct gak_fct,void * gak_data,int * use_master,krb5_kdc_rep ** as_reply)974 krb5_get_init_creds(krb5_context context,
975 krb5_creds *creds,
976 krb5_principal client,
977 krb5_prompter_fct prompter,
978 void *prompter_data,
979 krb5_deltat start_time,
980 char *in_tkt_service,
981 krb5_gic_opt_ext *options,
982 krb5_gic_get_as_key_fct gak_fct,
983 void *gak_data,
984 int *use_master,
985 krb5_kdc_rep **as_reply)
986 {
987 krb5_error_code ret;
988 krb5_kdc_req request;
989 krb5_data *encoded_request_body, *encoded_previous_request;
990 krb5_pa_data **preauth_to_use, **kdc_padata;
991 int tempint;
992 char *tempstr = NULL;
993 krb5_deltat tkt_life;
994 krb5_deltat renew_life;
995 int loopcount;
996 krb5_data salt;
997 krb5_data s2kparams;
998 krb5_keyblock as_key;
999 krb5_error *err_reply = NULL;
1000 krb5_kdc_rep *local_as_reply;
1001 krb5_timestamp time_now;
1002 krb5_enctype etype = 0;
1003 krb5_preauth_client_rock get_data_rock;
1004 char *hostname_used = NULL;
1005
1006 /* initialize everything which will be freed at cleanup */
1007
1008 s2kparams.data = NULL;
1009 s2kparams.length = 0;
1010 request.server = NULL;
1011 request.ktype = NULL;
1012 request.addresses = NULL;
1013 request.padata = NULL;
1014 encoded_request_body = NULL;
1015 encoded_previous_request = NULL;
1016 preauth_to_use = NULL;
1017 kdc_padata = NULL;
1018 as_key.length = 0;
1019 salt.length = 0;
1020 salt.data = NULL;
1021
1022 (void) memset(&as_key, 0, sizeof(as_key));
1023
1024 local_as_reply = 0;
1025
1026 /*
1027 * Set up the basic request structure
1028 */
1029 request.magic = KV5M_KDC_REQ;
1030 request.msg_type = KRB5_AS_REQ;
1031
1032 /* request.nonce is filled in when we send a request to the kdc */
1033 request.nonce = 0;
1034
1035 /* request.padata is filled in later */
1036
1037 request.kdc_options = context->kdc_default_options;
1038
1039 /* forwardable */
1040
1041 if (options && (options->flags & KRB5_GET_INIT_CREDS_OPT_FORWARDABLE))
1042 tempint = options->forwardable;
1043 else if ((ret = krb5_libdefault_boolean(context, &client->realm,
1044 "forwardable", &tempint)) == 0)
1045 /*EMPTY*/
1046 ;
1047 else
1048 tempint = 0;
1049 if (tempint)
1050 request.kdc_options |= KDC_OPT_FORWARDABLE;
1051
1052 /* proxiable */
1053
1054 if (options && (options->flags & KRB5_GET_INIT_CREDS_OPT_PROXIABLE))
1055 tempint = options->proxiable;
1056 else if ((ret = krb5_libdefault_boolean(context, &client->realm,
1057 "proxiable", &tempint)) == 0)
1058 /*EMPTY*/
1059 ;
1060 else
1061 tempint = 0;
1062 if (tempint)
1063 request.kdc_options |= KDC_OPT_PROXIABLE;
1064
1065 /* allow_postdate */
1066
1067 if (start_time > 0)
1068 request.kdc_options |= (KDC_OPT_ALLOW_POSTDATE|KDC_OPT_POSTDATED);
1069
1070 /* ticket lifetime */
1071
1072 if ((ret = krb5_timeofday(context, &request.from)))
1073 goto cleanup;
1074 request.from = krb5int_addint32(request.from, start_time);
1075
1076 if (options && (options->flags & KRB5_GET_INIT_CREDS_OPT_TKT_LIFE)) {
1077 tkt_life = options->tkt_life;
1078 } else if ((ret = krb5_libdefault_string(context, &client->realm,
1079 "ticket_lifetime", &tempstr))
1080 == 0) {
1081 ret = krb5_string_to_deltat(tempstr, &tkt_life);
1082 free(tempstr);
1083 if (ret) {
1084 goto cleanup;
1085 }
1086 } else {
1087 /* this used to be hardcoded in kinit.c */
1088 tkt_life = 24*60*60;
1089 }
1090 request.till = krb5int_addint32(request.from, tkt_life);
1091
1092 /* renewable lifetime */
1093
1094 if (options && (options->flags & KRB5_GET_INIT_CREDS_OPT_RENEW_LIFE)) {
1095 renew_life = options->renew_life;
1096 } else if ((ret = krb5_libdefault_string(context, &client->realm,
1097 "renew_lifetime", &tempstr))
1098 == 0) {
1099 ret = krb5_string_to_deltat(tempstr, &renew_life);
1100 free(tempstr);
1101 if (ret) {
1102 goto cleanup;
1103 }
1104 } else {
1105 renew_life = 0;
1106 }
1107 if (renew_life > 0)
1108 request.kdc_options |= KDC_OPT_RENEWABLE;
1109
1110 if (renew_life > 0) {
1111 request.rtime = krb5int_addint32(request.from, renew_life);
1112 if (request.rtime < request.till) {
1113 /* don't ask for a smaller renewable time than the lifetime */
1114 request.rtime = request.till;
1115 }
1116 /* we are already asking for renewable tickets so strip this option */
1117 request.kdc_options &= ~(KDC_OPT_RENEWABLE_OK);
1118 } else {
1119 request.rtime = 0;
1120 }
1121
1122 /* client */
1123
1124 request.client = client;
1125
1126 /* service */
1127
1128 if (in_tkt_service) {
1129 /* this is ugly, because so are the data structures involved. I'm
1130 in the library, so I'm going to manipulate the data structures
1131 directly, otherwise, it will be worse. */
1132
1133 if ((ret = krb5_parse_name(context, in_tkt_service, &request.server)))
1134 goto cleanup;
1135
1136 /* stuff the client realm into the server principal.
1137 realloc if necessary */
1138 if (request.server->realm.length < request.client->realm.length)
1139 if ((request.server->realm.data =
1140 (char *) realloc(request.server->realm.data,
1141 request.client->realm.length)) == NULL) {
1142 ret = ENOMEM;
1143 goto cleanup;
1144 }
1145
1146 request.server->realm.length = request.client->realm.length;
1147 memcpy(request.server->realm.data, request.client->realm.data,
1148 request.client->realm.length);
1149 } else {
1150 if ((ret = krb5_build_principal_ext(context, &request.server,
1151 request.client->realm.length,
1152 request.client->realm.data,
1153 KRB5_TGS_NAME_SIZE,
1154 KRB5_TGS_NAME,
1155 request.client->realm.length,
1156 request.client->realm.data,
1157 0)))
1158 goto cleanup;
1159 }
1160
1161 krb5_preauth_request_context_init(context);
1162
1163 /* nonce is filled in by send_as_request if we don't take care of it */
1164
1165 if (options && (options->flags & KRB5_GET_INIT_CREDS_OPT_ETYPE_LIST)) {
1166 request.ktype = options->etype_list;
1167 request.nktypes = options->etype_list_length;
1168 } else if ((ret = krb5_get_default_in_tkt_ktypes(context,
1169 &request.ktype)) == 0) {
1170 for (request.nktypes = 0;
1171 request.ktype[request.nktypes];
1172 request.nktypes++)
1173 ;
1174 } else {
1175 /* there isn't any useful default here. ret is set from above */
1176 goto cleanup;
1177 }
1178
1179 if (options && (options->flags & KRB5_GET_INIT_CREDS_OPT_ADDRESS_LIST)) {
1180 request.addresses = options->address_list;
1181 }
1182 /* it would be nice if this parsed out an address list, but
1183 that would be work. */
1184 else if (((ret = krb5_libdefault_boolean(context, &client->realm,
1185 "no_addresses", &tempint)) == 0)
1186 || (tempint == 1)) {
1187 /*EMPTY*/
1188 ;
1189 } else if (((ret = krb5_libdefault_boolean(context, &client->realm,
1190 "noaddresses", &tempint)) == 0)
1191 || (tempint == 1)) {
1192 /*EMPTY*/
1193 ;
1194 } else {
1195 if ((ret = krb5_os_localaddr(context, &request.addresses)))
1196 goto cleanup;
1197 }
1198
1199 request.authorization_data.ciphertext.length = 0;
1200 request.authorization_data.ciphertext.data = 0;
1201 request.unenc_authdata = 0;
1202 request.second_ticket = 0;
1203
1204 /* set up the other state. */
1205
1206 if (options && (options->flags & KRB5_GET_INIT_CREDS_OPT_PREAUTH_LIST)) {
1207 if ((ret = make_preauth_list(context, options->preauth_list,
1208 options->preauth_list_length,
1209 &preauth_to_use)))
1210 goto cleanup;
1211 }
1212
1213 /* the salt is allocated from somewhere, unless it is from the caller,
1214 then it is a reference */
1215
1216 if (options && (options->flags & KRB5_GET_INIT_CREDS_OPT_SALT)) {
1217 salt = *options->salt;
1218 } else {
1219 salt.length = SALT_TYPE_AFS_LENGTH;
1220 salt.data = NULL;
1221 }
1222
1223
1224 /* set the request nonce */
1225 if ((ret = krb5_timeofday(context, &time_now)))
1226 goto cleanup;
1227 /*
1228 * XXX we know they are the same size... and we should do
1229 * something better than just the current time
1230 */
1231 request.nonce = (krb5_int32) time_now;
1232
1233 /* give the preauth plugins a chance to prep the request body */
1234 krb5_preauth_prepare_request(context, options, &request);
1235 ret = encode_krb5_kdc_req_body(&request, &encoded_request_body);
1236 if (ret)
1237 goto cleanup;
1238
1239 get_data_rock.magic = CLIENT_ROCK_MAGIC;
1240 get_data_rock.as_reply = NULL;
1241
1242 /* now, loop processing preauth data and talking to the kdc */
1243 for (loopcount = 0; loopcount < MAX_IN_TKT_LOOPS; loopcount++) {
1244 if (request.padata) {
1245 krb5_free_pa_data(context, request.padata);
1246 request.padata = NULL;
1247 }
1248 if (!err_reply) {
1249 /* either our first attempt, or retrying after PREAUTH_NEEDED */
1250 if ((ret = krb5_do_preauth(context,
1251 &request,
1252 encoded_request_body,
1253 encoded_previous_request,
1254 preauth_to_use, &request.padata,
1255 &salt, &s2kparams, &etype, &as_key,
1256 prompter, prompter_data,
1257 gak_fct, gak_data,
1258 &get_data_rock, options)))
1259 goto cleanup;
1260 } else {
1261 if (preauth_to_use != NULL) {
1262 /*
1263 * Retry after an error other than PREAUTH_NEEDED,
1264 * using e-data to figure out what to change.
1265 */
1266 ret = krb5_do_preauth_tryagain(context,
1267 &request,
1268 encoded_request_body,
1269 encoded_previous_request,
1270 preauth_to_use, &request.padata,
1271 err_reply,
1272 &salt, &s2kparams, &etype,
1273 &as_key,
1274 prompter, prompter_data,
1275 gak_fct, gak_data,
1276 &get_data_rock, options);
1277 } else {
1278 /* No preauth supplied, so can't query the plug-ins. */
1279 ret = KRB5KRB_ERR_GENERIC;
1280 }
1281 if (ret) {
1282 /* couldn't come up with anything better */
1283 ret = err_reply->error + ERROR_TABLE_BASE_krb5;
1284 }
1285 krb5_free_error(context, err_reply);
1286 err_reply = NULL;
1287 if (ret)
1288 goto cleanup;
1289 }
1290
1291 if (encoded_previous_request != NULL) {
1292 krb5_free_data(context, encoded_previous_request);
1293 encoded_previous_request = NULL;
1294 }
1295 ret = encode_krb5_as_req(&request, &encoded_previous_request);
1296 if (ret)
1297 goto cleanup;
1298
1299 err_reply = NULL;
1300 local_as_reply = 0;
1301 if ((ret = send_as_request2(context, &request, &err_reply,
1302 &local_as_reply, use_master,
1303 &hostname_used)))
1304 goto cleanup;
1305
1306 if (err_reply) {
1307 if (err_reply->error == KDC_ERR_PREAUTH_REQUIRED &&
1308 err_reply->e_data.length > 0) {
1309 /* reset the list of preauth types to try */
1310 if (preauth_to_use) {
1311 krb5_free_pa_data(context, preauth_to_use);
1312 preauth_to_use = NULL;
1313 }
1314 ret = decode_krb5_padata_sequence(&err_reply->e_data,
1315 &preauth_to_use);
1316 krb5_free_error(context, err_reply);
1317 err_reply = NULL;
1318 if (ret)
1319 goto cleanup;
1320 ret = sort_krb5_padata_sequence(context,
1321 &request.server->realm,
1322 preauth_to_use);
1323 if (ret)
1324 goto cleanup;
1325 /* continue to next iteration */
1326 } else {
1327 if (err_reply->e_data.length > 0) {
1328 /* continue to next iteration */
1329 } else {
1330 /* error + no hints = give up */
1331 ret = (krb5_error_code) err_reply->error
1332 + ERROR_TABLE_BASE_krb5;
1333 goto cleanup;
1334 }
1335 }
1336 } else if (local_as_reply) {
1337 break;
1338 } else {
1339 ret = KRB5KRB_AP_ERR_MSG_TYPE;
1340 goto cleanup;
1341 }
1342 }
1343
1344 if (loopcount == MAX_IN_TKT_LOOPS) {
1345 ret = KRB5_GET_IN_TKT_LOOP;
1346 /* Solaris Kerberos */
1347 {
1348 char *s_name = NULL;
1349 char *c_name = NULL;
1350 krb5_error_code serr, cerr;
1351 serr = krb5_unparse_name(context, creds->server, &s_name);
1352 cerr = krb5_unparse_name(context, creds->client, &c_name);
1353 krb5_set_error_message(context, ret,
1354 dgettext(TEXT_DOMAIN,
1355 "Looping detected getting initial creds: '%s' requesting ticket '%s'. Max loops is %d. Make sure a KDC is available"),
1356 cerr ? "unknown" : c_name,
1357 serr ? "unknown" : s_name,
1358 MAX_IN_TKT_LOOPS);
1359 if (s_name)
1360 krb5_free_unparsed_name(context, s_name);
1361 if (c_name)
1362 krb5_free_unparsed_name(context, c_name);
1363 }
1364 goto cleanup;
1365 }
1366
1367 /* process any preauth data in the as_reply */
1368 krb5_clear_preauth_context_use_counts(context);
1369 if ((ret = sort_krb5_padata_sequence(context, &request.server->realm,
1370 local_as_reply->padata)))
1371 goto cleanup;
1372 get_data_rock.as_reply = local_as_reply;
1373 if ((ret = krb5_do_preauth(context,
1374 &request,
1375 encoded_request_body, encoded_previous_request,
1376 local_as_reply->padata, &kdc_padata,
1377 &salt, &s2kparams, &etype, &as_key, prompter,
1378 prompter_data, gak_fct, gak_data,
1379 &get_data_rock, options)))
1380 goto cleanup;
1381
1382 /* XXX For 1.1.1 and prior KDC's, when SAM is used w/ USE_SAD_AS_KEY,
1383 the AS_REP comes back encrypted in the user's longterm key
1384 instead of in the SAD. If there was a SAM preauth, there
1385 will be an as_key here which will be the SAD. If that fails,
1386 use the gak_fct to get the password, and try again. */
1387
1388 /* XXX because etypes are handled poorly (particularly wrt SAM,
1389 where the etype is fixed by the kdc), we may want to try
1390 decrypt_as_reply twice. If there's an as_key available, try
1391 it. If decrypting the as_rep fails, or if there isn't an
1392 as_key at all yet, then use the gak_fct to get one, and try
1393 again. */
1394
1395 if (as_key.length)
1396 ret = decrypt_as_reply(context, NULL, local_as_reply, NULL,
1397 NULL, &as_key, krb5_kdc_rep_decrypt_proc,
1398 NULL);
1399 else
1400 ret = -1;
1401
1402 if (ret) {
1403 /* if we haven't get gotten a key, get it now */
1404
1405 if ((ret = ((*gak_fct)(context, request.client,
1406 local_as_reply->enc_part.enctype,
1407 prompter, prompter_data, &salt, &s2kparams,
1408 &as_key, gak_data))))
1409 goto cleanup;
1410
1411 if ((ret = decrypt_as_reply(context, NULL, local_as_reply, NULL,
1412 NULL, &as_key, krb5_kdc_rep_decrypt_proc,
1413 NULL)))
1414 goto cleanup;
1415 }
1416
1417 if ((ret = verify_as_reply(context, time_now, &request, local_as_reply)))
1418 goto cleanup;
1419
1420 /* XXX this should be inside stash_as_reply, but as long as
1421 get_in_tkt is still around using that arg as an in/out, I can't
1422 do that */
1423 /* Solaris Kerberos */
1424 (void) memset(creds, 0, sizeof(*creds));
1425
1426 /* Solaris Kerberos */
1427 if ((ret = stash_as_reply(context, time_now, &request, local_as_reply,
1428 creds, (krb5_ccache)NULL)))
1429 goto cleanup;
1430
1431 /* success */
1432
1433 ret = 0;
1434
1435 cleanup:
1436 if (ret != 0) {
1437 char *client_name = NULL;
1438 /* See if we can produce a more detailed error message. */
1439 switch (ret) {
1440 case KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN:
1441 if (krb5_unparse_name(context, client, &client_name) == 0) {
1442 krb5_set_error_message(context, ret,
1443 dgettext(TEXT_DOMAIN,
1444 "Client '%s' not found in Kerberos database"),
1445 client_name);
1446 free(client_name);
1447 }
1448 break;
1449 /* Solaris Kerberos: spruce-up the err msg */
1450 case KRB5_PREAUTH_FAILED:
1451 case KRB5KDC_ERR_PREAUTH_FAILED:
1452 if (krb5_unparse_name(context, client, &client_name) == 0) {
1453 krb5_set_error_message(context, ret,
1454 dgettext(TEXT_DOMAIN,
1455 "Client '%s' pre-authentication failed"),
1456 client_name);
1457 free(client_name);
1458 }
1459 break;
1460 /* Solaris Kerberos: spruce-up the err msg */
1461 case KRB5KRB_AP_ERR_SKEW: /* KRB_AP_ERR_SKEW + ERROR_TABLE_BASE_krb5 */
1462 {
1463 char *s_name = NULL;
1464 char *c_name = NULL;
1465 char stimestring[17];
1466 char fill = ' ';
1467 krb5_error_code c_err, s_err, s_time;
1468
1469 s_err = krb5_unparse_name(context,
1470 err_reply->server, &s_name);
1471 s_time = krb5_timestamp_to_sfstring(err_reply->stime,
1472 stimestring,
1473 sizeof (stimestring),
1474 &fill);
1475 c_err = krb5_unparse_name(context, client, &c_name);
1476 krb5_set_error_message(context, ret,
1477 dgettext(TEXT_DOMAIN,
1478 "Clock skew too great: '%s' requesting ticket '%s' from KDC '%s' (%s). Skew is %dm"),
1479 c_err == 0 ? c_name : "unknown",
1480 s_err == 0 ? s_name : "unknown",
1481 hostname_used ? hostname_used : "unknown",
1482 s_time == 0 ? stimestring : "unknown",
1483 (s_time != 0) ? 0 :
1484 (abs(err_reply->stime - time_now) / 60));
1485 if (s_name)
1486 krb5_free_unparsed_name(context, s_name);
1487 if (c_name)
1488 krb5_free_unparsed_name(context, c_name);
1489 }
1490 break;
1491 case KRB5_KDCREP_MODIFIED:
1492 if (krb5_unparse_name(context, client, &client_name) == 0) {
1493 /*
1494 * Solaris Kerberos
1495 * Extra err msg for common(?) case of
1496 * 'kinit user@lower-case-def-realm'.
1497 * DNS SRV recs will match (case insensitive) and trigger sendto
1498 * KDC and result in this error (at least w/MSFT AD KDC).
1499 */
1500 char *realm = strpbrk(client_name, "@");
1501 int set = 0;
1502 if (realm++) {
1503 if (realm && realm[0] && is_lower_case(realm)) {
1504 krb5_set_error_message(context, ret,
1505 dgettext(TEXT_DOMAIN,
1506 "KDC reply did not match expectations for client '%s': lower-case detected in realm '%s'"),
1507 client_name, realm);
1508 set = 1;
1509 }
1510 }
1511 if (!set)
1512 krb5_set_error_message(context, ret,
1513 dgettext(TEXT_DOMAIN,
1514 "KDC reply did not match expectations for client '%s'"),
1515 client_name);
1516 free(client_name);
1517 }
1518 break;
1519 default:
1520 break;
1521 }
1522 }
1523 if (err_reply)
1524 krb5_free_error(context, err_reply);
1525 krb5_preauth_request_context_fini(context);
1526 if (encoded_previous_request != NULL) {
1527 krb5_free_data(context, encoded_previous_request);
1528 encoded_previous_request = NULL;
1529 }
1530 if (encoded_request_body != NULL) {
1531 krb5_free_data(context, encoded_request_body);
1532 encoded_request_body = NULL;
1533 }
1534 if (request.server)
1535 krb5_free_principal(context, request.server);
1536 if (request.ktype &&
1537 (!(options && (options->flags & KRB5_GET_INIT_CREDS_OPT_ETYPE_LIST))))
1538 free(request.ktype);
1539 if (request.addresses &&
1540 (!(options &&
1541 (options->flags & KRB5_GET_INIT_CREDS_OPT_ADDRESS_LIST))))
1542 krb5_free_addresses(context, request.addresses);
1543 if (preauth_to_use)
1544 krb5_free_pa_data(context, preauth_to_use);
1545 if (kdc_padata)
1546 krb5_free_pa_data(context, kdc_padata);
1547 if (request.padata)
1548 krb5_free_pa_data(context, request.padata);
1549 if (as_key.length)
1550 krb5_free_keyblock_contents(context, &as_key);
1551 if (salt.data &&
1552 (!(options && (options->flags & KRB5_GET_INIT_CREDS_OPT_SALT))))
1553 krb5_xfree(salt.data);
1554 krb5_free_data_contents(context, &s2kparams);
1555 if (as_reply)
1556 *as_reply = local_as_reply;
1557 else if (local_as_reply)
1558 krb5_free_kdc_rep(context, local_as_reply);
1559 if (hostname_used)
1560 free(hostname_used);
1561 return(ret);
1562 }
1563