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