1 /*
2  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
3  * Use is subject to license terms.
4  */
5 
6 #pragma ident	"%Z%%M%	%I%	%E% SMI"
7 
8 /*
9  * lib/krb5/krb/get_in_tkt.c
10  *
11  * Copyright 1990,1991, 2003 by the Massachusetts Institute of Technology.
12  * All Rights Reserved.
13  *
14  * Export of this software from the United States of America may
15  *   require a specific license from the United States Government.
16  *   It is the responsibility of any person or organization contemplating
17  *   export to obtain such a license before exporting.
18  *
19  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
20  * distribute this software and its documentation for any purpose and
21  * without fee is hereby granted, provided that the above copyright
22  * notice appear in all copies and that both that copyright notice and
23  * this permission notice appear in supporting documentation, and that
24  * the name of M.I.T. not be used in advertising or publicity pertaining
25  * to distribution of the software without specific, written prior
26  * permission.  Furthermore if you modify this software you must label
27  * your software as modified software and not distribute it in such a
28  * fashion that it might be confused with the original M.I.T. software.
29  * M.I.T. makes no representations about the suitability of
30  * this software for any purpose.  It is provided "as is" without express
31  * or implied warranty.
32  *
33  *
34  * krb5_get_in_tkt()
35  */
36 
37 #include <string.h>
38 
39 #include <k5-int.h>
40 #include <krb5.h>
41 #include <int-proto.h>
42 #include <os-proto.h>
43 
44 /*
45  All-purpose initial ticket routine, usually called via
46  krb5_get_in_tkt_with_password or krb5_get_in_tkt_with_skey.
47 
48  Attempts to get an initial ticket for creds->client to use server
49  creds->server, (realm is taken from creds->client), with options
50  options, and using creds->times.starttime, creds->times.endtime,
51  creds->times.renew_till as from, till, and rtime.
52  creds->times.renew_till is ignored unless the RENEWABLE option is requested.
53 
54  key_proc is called to fill in the key to be used for decryption.
55  keyseed is passed on to key_proc.
56 
57  decrypt_proc is called to perform the decryption of the response (the
58  encrypted part is in dec_rep->enc_part; the decrypted part should be
59  allocated and filled into dec_rep->enc_part2
60  arg is passed on to decrypt_proc.
61 
62  If addrs is non-NULL, it is used for the addresses requested.  If it is
63  null, the system standard addresses are used.
64 
65  A succesful call will place the ticket in the credentials cache ccache
66  and fill in creds with the ticket information used/returned..
67 
68  returns system errors, encryption errors
69 
70  */
71 
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 
90 /*
91  * This function performs 32 bit bounded addition so we can generate
92  * lifetimes without overflowing krb5_int32
93  */
94 static krb5_int32 krb5int_addint32 (krb5_int32 x, krb5_int32 y)
95 {
96     if ((x > 0) && (y > (KRB5_INT32_MAX - x))) {
97         /* sum will be be greater than KRB5_INT32_MAX */
98         return KRB5_INT32_MAX;
99     } else if ((x < 0) && (y < (KRB5_INT32_MIN - x))) {
100         /* sum will be less than KRB5_INT32_MIN */
101         return KRB5_INT32_MIN;
102     }
103 
104     return x + y;
105 }
106 
107 /*
108  * This function sends a request to the KDC, and gets back a response;
109  * the response is parsed into ret_err_reply or ret_as_reply if the
110  * reponse is a KRB_ERROR or a KRB_AS_REP packet.  If it is some other
111  * unexpected response, an error is returned.
112  */
113 static krb5_error_code
114 send_as_request(krb5_context 		context,
115 		krb5_kdc_req		*request,
116 		krb5_timestamp 		*time_now,
117 		krb5_error ** 		ret_err_reply,
118 		krb5_kdc_rep ** 	ret_as_reply,
119 		int 			*use_master)
120 {
121     krb5_kdc_rep *as_reply = 0;
122     krb5_error_code retval;
123     krb5_data *packet = 0;
124     krb5_data reply;
125     char k4_version;		/* same type as *(krb5_data::data) */
126     int tcp_only = 0;
127 
128     reply.data = 0;
129 
130     if ((retval = krb5_timeofday(context, time_now)))
131 	goto cleanup;
132 
133     /*
134      * XXX we know they are the same size... and we should do
135      * something better than just the current time
136      */
137     request->nonce = (krb5_int32) *time_now;
138 
139     /* encode & send to KDC */
140     if ((retval = encode_krb5_as_req(request, &packet)) != 0)
141 	goto cleanup;
142 
143     k4_version = packet->data[0];
144 send_again:
145     retval = krb5_sendto_kdc(context, packet,
146 			     krb5_princ_realm(context, request->client),
147 			     &reply, use_master, tcp_only);
148     if (retval)
149 	goto cleanup;
150 
151     /* now decode the reply...could be error or as_rep */
152     if (krb5_is_krb_error(&reply)) {
153 	krb5_error *err_reply;
154 
155 	if ((retval = decode_krb5_error(&reply, &err_reply)))
156 	    /* some other error code--??? */
157 	    goto cleanup;
158 
159 	if (ret_err_reply) {
160 	    if (err_reply->error == KRB_ERR_RESPONSE_TOO_BIG
161 		&& tcp_only == 0) {
162 		tcp_only = 1;
163 		krb5_free_error(context, err_reply);
164 		free(reply.data);
165 		reply.data = 0;
166 		goto send_again;
167 	    }
168 	    *ret_err_reply = err_reply;
169 	} else
170 	    krb5_free_error(context, err_reply);
171 	goto cleanup;
172     }
173 
174     /*
175      * Check to make sure it isn't a V4 reply.
176      */
177     if (!krb5_is_as_rep(&reply)) {
178 /* these are in <kerberosIV/prot.h> as well but it isn't worth including. */
179 #define V4_KRB_PROT_VERSION	4
180 #define V4_AUTH_MSG_ERR_REPLY	(5<<1)
181 	/* check here for V4 reply */
182 	unsigned int t_switch;
183 
184 	/* From v4 g_in_tkt.c: This used to be
185 	   switch (pkt_msg_type(rpkt) & ~1) {
186 	   but SCO 3.2v4 cc compiled that incorrectly.  */
187 	t_switch = reply.data[1];
188 	t_switch &= ~1;
189 
190 	if (t_switch == V4_AUTH_MSG_ERR_REPLY
191 	    && (reply.data[0] == V4_KRB_PROT_VERSION
192 		|| reply.data[0] == k4_version)) {
193 	    retval = KRB5KRB_AP_ERR_V4_REPLY;
194 	} else {
195 	    retval = KRB5KRB_AP_ERR_MSG_TYPE;
196 	}
197 	goto cleanup;
198     }
199 
200     /* It must be a KRB_AS_REP message, or an bad returned packet */
201     if ((retval = decode_krb5_as_rep(&reply, &as_reply)))
202 	/* some other error code ??? */
203 	goto cleanup;
204 
205     if (as_reply->msg_type != KRB5_AS_REP) {
206 	retval = KRB5KRB_AP_ERR_MSG_TYPE;
207 	krb5_free_kdc_rep(context, as_reply);
208 	goto cleanup;
209     }
210 
211     if (ret_as_reply)
212 	*ret_as_reply = as_reply;
213     else
214 	krb5_free_kdc_rep(context, as_reply);
215 
216 cleanup:
217     if (packet)
218 	krb5_free_data(context, packet);
219     if (reply.data)
220 	free(reply.data);
221     return retval;
222 }
223 
224 static krb5_error_code
225 decrypt_as_reply(krb5_context 		context,
226 		 krb5_kdc_req		*request,
227 		 krb5_kdc_rep		*as_reply,
228 		 git_key_proc 		key_proc,
229 		 krb5_const_pointer 	keyseed,
230 		 krb5_keyblock *	key,
231 		 git_decrypt_proc 	decrypt_proc,
232 		 krb5_const_pointer 	decryptarg)
233 {
234     krb5_error_code		retval;
235     krb5_keyblock *		decrypt_key = 0;
236     krb5_data 			salt;
237 
238     KRB5_LOG0(KRB5_INFO, "decrypt_as_reply() start");
239 
240     if (as_reply->enc_part2)
241 	return 0;
242 
243     if (key)
244 	    decrypt_key = key;
245     else if (request != NULL) {
246 	if ((retval = krb5_principal2salt(context, request->client, &salt)))
247 	    return(retval);
248 
249 	retval = (*key_proc)(context, as_reply->enc_part.enctype,
250 			     &salt, keyseed, &decrypt_key);
251 	krb5_xfree(salt.data);
252 	if (retval)
253 	    goto cleanup;
254     } else {
255 	KRB5_LOG0(KRB5_ERR, "decrypt_as_reply() end, "
256 		"error key == NULL and request == NULL");
257 	return (EINVAL);
258     }
259 
260     /* Solaris kerberos: Overwriting the decrypt_key->enctype because the
261      * decrypt key's enctype may not be an exact match with the enctype that the
262      * KDC used to encrypt this part of the AS reply.  This assumes the
263      * as_reply->enc_part.enctype has been validated which is done by checking
264      * to see if the enctype that the KDC sent back in the as_reply is one of
265      * the enctypes originally requested.  Note, if request is NULL then the
266      * as_reply->enc_part.enctype could not be validated.
267      */
268 
269     if (request != NULL) {
270         if (is_in_keytype(request->ktype, request->nktypes,
271                 as_reply->enc_part.enctype)) {
272 
273 	    decrypt_key->enctype = as_reply->enc_part.enctype;
274 
275 	} else {
276 	    KRB5_LOG0(KRB5_ERR, "decrypt_as_reply() end, "
277 		    "error is_in_keytype() returned false");
278 	    retval = KRB5_BAD_ENCTYPE;
279 	    goto cleanup;
280 	}
281     }
282 
283     if ((retval = (*decrypt_proc)(context, decrypt_key, decryptarg, as_reply))){
284 	KRB5_LOG(KRB5_ERR, "decrypt_as_reply() error (*decrypt_proc)() retval "
285 			    "= %d", retval);
286 	goto cleanup;
287     }
288 
289 cleanup:
290     if (!key && decrypt_key)
291 	krb5_free_keyblock(context, decrypt_key);
292 
293     KRB5_LOG(KRB5_INFO, "decrypt_as_reply() end, retval = %d", retval);
294 
295     return (retval);
296 }
297 
298 static krb5_error_code
299 verify_as_reply(krb5_context 		context,
300 		krb5_timestamp 		time_now,
301 		krb5_kdc_req		*request,
302 		krb5_kdc_rep		*as_reply)
303 {
304     krb5_error_code		retval;
305 
306     /* check the contents for sanity: */
307     if (!as_reply->enc_part2->times.starttime)
308 	as_reply->enc_part2->times.starttime =
309 	    as_reply->enc_part2->times.authtime;
310 
311     if (!krb5_principal_compare(context, as_reply->client, request->client)
312 	|| !krb5_principal_compare(context, as_reply->enc_part2->server, request->server)
313 	|| !krb5_principal_compare(context, as_reply->ticket->server, request->server)
314 	|| (request->nonce != as_reply->enc_part2->nonce)
315 	/* XXX check for extraneous flags */
316 	/* XXX || (!krb5_addresses_compare(context, addrs, as_reply->enc_part2->caddrs)) */
317 	|| ((request->kdc_options & KDC_OPT_POSTDATED) &&
318 	    (request->from != 0) &&
319 	    (request->from != as_reply->enc_part2->times.starttime))
320 	|| ((request->till != 0) &&
321 	    (as_reply->enc_part2->times.endtime > request->till))
322 	|| ((request->kdc_options & KDC_OPT_RENEWABLE) &&
323 	    (request->rtime != 0) &&
324 	    (as_reply->enc_part2->times.renew_till > request->rtime))
325 	|| ((request->kdc_options & KDC_OPT_RENEWABLE_OK) &&
326 	    !(request->kdc_options & KDC_OPT_RENEWABLE) &&
327 	    (as_reply->enc_part2->flags & KDC_OPT_RENEWABLE) &&
328 	    (request->till != 0) &&
329 	    (as_reply->enc_part2->times.renew_till > request->till))
330 	)
331 	return KRB5_KDCREP_MODIFIED;
332 
333     if (context->library_options & KRB5_LIBOPT_SYNC_KDCTIME) {
334 	retval = krb5_set_real_time(context,
335 				    as_reply->enc_part2->times.authtime, 0);
336 	if (retval)
337 	    return retval;
338     } else {
339 	if ((request->from == 0) &&
340 	    (labs(as_reply->enc_part2->times.starttime - time_now)
341 	     > context->clockskew))
342 	    return (KRB5_KDCREP_SKEW);
343     }
344     return 0;
345 }
346 
347 /*ARGSUSED*/
348 static krb5_error_code
349 stash_as_reply(krb5_context 		context,
350 	       krb5_timestamp 		time_now,
351 	       krb5_kdc_req		*request,
352 	       krb5_kdc_rep		*as_reply,
353 	       krb5_creds * 		creds,
354 	       krb5_ccache 		ccache)
355 {
356     krb5_error_code 		retval;
357     krb5_data *			packet;
358     krb5_principal		client;
359     krb5_principal		server;
360 
361     client = NULL;
362     server = NULL;
363 
364     if (!creds->client)
365         if ((retval = krb5_copy_principal(context, as_reply->client, &client)))
366 	    goto cleanup;
367 
368     if (!creds->server)
369 	if ((retval = krb5_copy_principal(context, as_reply->enc_part2->server,
370 					  &server)))
371 	    goto cleanup;
372 
373     /* fill in the credentials */
374     if ((retval = krb5_copy_keyblock_contents(context,
375 					      as_reply->enc_part2->session,
376 					      &creds->keyblock)))
377 	goto cleanup;
378 
379     creds->times = as_reply->enc_part2->times;
380     creds->is_skey = FALSE;		/* this is an AS_REQ, so cannot
381 					   be encrypted in skey */
382     creds->ticket_flags = as_reply->enc_part2->flags;
383     if ((retval = krb5_copy_addresses(context, as_reply->enc_part2->caddrs,
384 				      &creds->addresses)))
385 	goto cleanup;
386 
387     creds->second_ticket.length = 0;
388     creds->second_ticket.data = 0;
389 
390     if ((retval = encode_krb5_ticket(as_reply->ticket, &packet)))
391 	goto cleanup;
392 
393     creds->ticket = *packet;
394     krb5_xfree(packet);
395 
396     /* store it in the ccache! */
397     if (ccache)
398 	if ((retval = krb5_cc_store_cred(context, ccache, creds)) !=0)
399 	    goto cleanup;
400 
401     if (!creds->client)
402 	creds->client = client;
403     if (!creds->server)
404 	creds->server = server;
405 
406 cleanup:
407     if (retval) {
408 	if (client)
409 	    krb5_free_principal(context, client);
410 	if (server)
411 	    krb5_free_principal(context, server);
412 	if (creds->keyblock.contents) {
413 	    memset((char *)creds->keyblock.contents, 0,
414 		   creds->keyblock.length);
415 	    krb5_xfree(creds->keyblock.contents);
416 	    creds->keyblock.contents = 0;
417 	    creds->keyblock.length = 0;
418 	}
419 	if (creds->ticket.data) {
420 	    krb5_xfree(creds->ticket.data);
421 	    creds->ticket.data = 0;
422 	}
423 	if (creds->addresses) {
424 	    krb5_free_addresses(context, creds->addresses);
425 	    creds->addresses = 0;
426 	}
427     }
428     return (retval);
429 }
430 
431 /*ARGSUSED*/
432 static krb5_error_code
433 make_preauth_list(krb5_context	context,
434 		  krb5_preauthtype *	ptypes,
435 		  int			nptypes,
436 		  krb5_pa_data ***	ret_list)
437 {
438     krb5_preauthtype *		ptypep;
439     krb5_pa_data **		preauthp;
440     int				i;
441 
442     if (nptypes < 0) {
443  	for (nptypes=0, ptypep = ptypes; *ptypep; ptypep++, nptypes++)
444  	    ;
445     }
446 
447     /* allocate space for a NULL to terminate the list */
448 
449     if ((preauthp =
450  	 (krb5_pa_data **) malloc((nptypes+1)*sizeof(krb5_pa_data *))) == NULL)
451  	return(ENOMEM);
452 
453     for (i=0; i<nptypes; i++) {
454  	if ((preauthp[i] =
455  	     (krb5_pa_data *) malloc(sizeof(krb5_pa_data))) == NULL) {
456  	    for (; i>=0; i++)
457  		free(preauthp[i]);
458  	    free(preauthp);
459 	    return (ENOMEM);
460 	}
461  	preauthp[i]->magic = KV5M_PA_DATA;
462  	preauthp[i]->pa_type = ptypes[i];
463  	preauthp[i]->length = 0;
464  	preauthp[i]->contents = 0;
465     }
466 
467     /* fill in the terminating NULL */
468 
469     preauthp[nptypes] = NULL;
470 
471     *ret_list = preauthp;
472     return 0;
473 }
474 
475 #define MAX_IN_TKT_LOOPS 16
476 /* SUNW14resync - Solaris krb does not use this (appearently) */
477 #if 0
478 static const krb5_enctype get_in_tkt_enctypes[] = {
479     ENCTYPE_DES3_CBC_SHA1,
480     ENCTYPE_ARCFOUR_HMAC,
481     ENCTYPE_DES_CBC_MD5,
482     ENCTYPE_DES_CBC_MD4,
483     ENCTYPE_DES_CBC_CRC,
484     0
485 };
486 #endif
487 
488 /* begin libdefaults parsing code.  This should almost certainly move
489    somewhere else, but I don't know where the correct somewhere else
490    is yet. */
491 
492 /* XXX Duplicating this is annoying; try to work on a better way.*/
493 static const char *const conf_yes[] = {
494     "y", "yes", "true", "t", "1", "on",
495     0,
496 };
497 
498 static const char *const conf_no[] = {
499     "n", "no", "false", "nil", "0", "off",
500     0,
501 };
502 
503 int
504 _krb5_conf_boolean(const char *s)
505 {
506     const char *const *p;
507 
508     for(p=conf_yes; *p; p++) {
509 	if (!strcasecmp(*p,s))
510 	    return 1;
511     }
512 
513     for(p=conf_no; *p; p++) {
514 	if (!strcasecmp(*p,s))
515 	    return 0;
516     }
517 
518     /* Default to "no" */
519     return 0;
520 }
521 
522 static krb5_error_code
523 krb5_libdefault_string(krb5_context context, const krb5_data *realm,
524 		       const char *option, char **ret_value)
525 {
526     profile_t profile;
527     const char *names[5];
528     char **nameval = NULL;
529     krb5_error_code retval;
530     char realmstr[1024];
531 
532     if (realm->length > sizeof(realmstr)-1)
533 	return(EINVAL);
534 
535     strncpy(realmstr, realm->data, realm->length);
536     realmstr[realm->length] = '\0';
537 
538     if (!context || (context->magic != KV5M_CONTEXT))
539 	return KV5M_CONTEXT;
540 
541     profile = context->profile;
542 
543     names[0] = "realms";
544 
545     /*
546      * Try number one:
547      *
548      * [realms]
549      *		REALM = {
550      *			option = <boolean>
551      *		}
552      */
553 
554     names[1] = realmstr;
555     names[2] = option;
556     names[3] = 0;
557     retval = profile_get_values(profile, names, &nameval);
558     if (retval == 0 && nameval && nameval[0])
559 	goto goodbye;
560 
561     /*
562      * Try number two:
563      *
564      * [libdefaults]
565      *		option = <boolean>
566      */
567 
568     names[0] = "libdefaults";
569     names[1] = option;
570     names[2] = 0;
571     retval = profile_get_values(profile, names, &nameval);
572     if (retval == 0 && nameval && nameval[0])
573 	goto goodbye;
574 
575 goodbye:
576     if (!nameval)
577 	return(ENOENT);
578 
579     if (!nameval[0]) {
580         retval = ENOENT;
581     } else {
582         *ret_value = malloc(strlen(nameval[0]) + 1);
583         if (!*ret_value)
584             retval = ENOMEM;
585         else
586             strcpy(*ret_value, nameval[0]);
587     }
588 
589     profile_free_list(nameval);
590 
591     return retval;
592 }
593 
594 /* not static so verify_init_creds() can call it */
595 /* as well as the DNS code */
596 
597 krb5_error_code
598 krb5_libdefault_boolean(krb5_context context, const krb5_data *realm,
599 			const char *option, int *ret_value)
600 {
601     char *string = NULL;
602     krb5_error_code retval;
603 
604     retval = krb5_libdefault_string(context, realm, option, &string);
605 
606     if (retval)
607 	return(retval);
608 
609     *ret_value = _krb5_conf_boolean(string);
610     free(string);
611 
612     return(0);
613 }
614 
615 krb5_error_code KRB5_CALLCONV
616 krb5_get_init_creds(krb5_context context,
617 		    krb5_creds *creds,
618 		    krb5_principal client,
619 		    krb5_prompter_fct prompter,
620 		    void *prompter_data,
621 		    krb5_deltat start_time,
622 		    char *in_tkt_service,
623 		    krb5_get_init_creds_opt *options,
624 		    krb5_gic_get_as_key_fct gak_fct,
625 		    void *gak_data,
626 		    int  *use_master,
627 		    krb5_kdc_rep **as_reply)
628 {
629     krb5_error_code ret;
630     krb5_kdc_req request;
631     krb5_pa_data **padata;
632     int tempint;
633     char *tempstr = NULL;
634     krb5_deltat tkt_life;
635     krb5_deltat renew_life;
636     krb5_deltat max_life;
637     int loopcount;
638     krb5_data salt;
639     krb5_data s2kparams;
640     krb5_keyblock as_key;
641     krb5_error *err_reply;
642     krb5_kdc_rep *local_as_reply;
643     krb5_timestamp time_now;
644     krb5_enctype etype = 0;
645 
646     /* initialize everything which will be freed at cleanup */
647 
648     s2kparams.data = NULL;
649     s2kparams.length = 0;
650     request.server = NULL;
651     request.ktype = NULL;
652     request.addresses = NULL;
653     request.padata = NULL;
654     padata = NULL;
655     salt.length = 0;
656     salt.data = NULL;
657 
658     (void) memset(&as_key, 0, sizeof(as_key));
659 
660     local_as_reply = 0;
661 
662     /*
663      * Set up the basic request structure
664      */
665     request.magic = KV5M_KDC_REQ;
666     request.msg_type = KRB5_AS_REQ;
667 
668     /* request.padata is filled in later */
669 
670     request.kdc_options = context->kdc_default_options;
671 
672     /* forwardable */
673 
674     if (options && (options->flags & KRB5_GET_INIT_CREDS_OPT_FORWARDABLE))
675 	tempint = options->forwardable;
676     else if ((ret = krb5_libdefault_boolean(context, &client->realm,
677 					    "forwardable", &tempint)) == 0)
678 	/*EMPTY*/
679 	;
680     else
681 	    tempint = 0;
682     if (tempint)
683 	request.kdc_options |= KDC_OPT_FORWARDABLE;
684 
685     /* proxiable */
686 
687     if (options && (options->flags & KRB5_GET_INIT_CREDS_OPT_PROXIABLE))
688 	tempint = options->proxiable;
689     else if ((ret = krb5_libdefault_boolean(context, &client->realm,
690 					    "proxiable", &tempint)) == 0)
691 	/*EMPTY*/
692 	;
693     else
694 	    tempint = 0;
695     if (tempint)
696 	request.kdc_options |= KDC_OPT_PROXIABLE;
697 
698     /* allow_postdate */
699 
700     if (start_time > 0)
701 	request.kdc_options |= (KDC_OPT_ALLOW_POSTDATE|KDC_OPT_POSTDATED);
702 
703     /* ticket lifetime */
704 
705     if ((ret = krb5_timeofday(context, &request.from)))
706 	goto cleanup;
707     request.from = krb5int_addint32(request.from, start_time);
708 
709     if (options && (options->flags & KRB5_GET_INIT_CREDS_OPT_TKT_LIFE)) {
710         tkt_life = options->tkt_life;
711     } else if ((ret = krb5_libdefault_string(context, &client->realm,
712 					     "ticket_lifetime", &tempstr))
713 	       == 0) {
714 	if ((ret = krb5_string_to_deltat(tempstr, &tkt_life))) {
715 	    free(tempstr);
716 	    tempstr = NULL;
717 	    goto cleanup;
718 	}
719 	if (tempstr) {
720 	    free(tempstr);
721 	    tempstr = NULL;
722 	}
723     } else {
724 	/* this used to be hardcoded in kinit.c */
725 	tkt_life = 24*60*60;
726     }
727     request.till = krb5int_addint32(request.from, tkt_life);
728 
729     /* renewable lifetime */
730 
731     if (options && (options->flags & KRB5_GET_INIT_CREDS_OPT_RENEW_LIFE)) {
732 	renew_life = options->renew_life;
733     } else if ((ret = krb5_libdefault_string(context, &client->realm,
734 					     "renew_lifetime", &tempstr))
735 	       == 0) {
736 	if ((ret = krb5_string_to_deltat(tempstr, &renew_life))) {
737 	    free(tempstr);
738 	    goto cleanup;
739 	}
740 	if (tempstr) {
741 	    free(tempstr);
742 	    tempstr = NULL;
743 	}
744     } else {
745 	renew_life = 0;
746     }
747     if (renew_life > 0)
748 	request.kdc_options |= KDC_OPT_RENEWABLE;
749 
750     if (renew_life > 0) {
751 	request.rtime = krb5int_addint32(request.from, renew_life);
752         if (request.rtime < request.till) {
753             /* don't ask for a smaller renewable time than the lifetime */
754             request.rtime = request.till;
755         }
756         /* we are already asking for renewable tickets so strip this option */
757 	request.kdc_options &= ~(KDC_OPT_RENEWABLE_OK);
758     } else {
759 	request.rtime = 0;
760     }
761 
762     /* client */
763 
764     request.client = client;
765 
766     /* service */
767 
768     if (in_tkt_service) {
769 	/* this is ugly, because so are the data structures involved.  I'm
770 	   in the library, so I'm going to manipulate the data structures
771 	   directly, otherwise, it will be worse. */
772 
773         if ((ret = krb5_parse_name(context, in_tkt_service, &request.server)))
774 	    goto cleanup;
775 
776 	/* stuff the client realm into the server principal.
777 	   realloc if necessary */
778 	if (request.server->realm.length < request.client->realm.length)
779 	    if ((request.server->realm.data =
780 		 (char *) realloc(request.server->realm.data,
781 				  request.client->realm.length)) == NULL) {
782 		ret = ENOMEM;
783 		goto cleanup;
784 	    }
785 
786 	request.server->realm.length = request.client->realm.length;
787 	memcpy(request.server->realm.data, request.client->realm.data,
788 	       request.client->realm.length);
789     } else {
790 	if ((ret = krb5_build_principal_ext(context, &request.server,
791 					   request.client->realm.length,
792 					   request.client->realm.data,
793 					   KRB5_TGS_NAME_SIZE,
794 					   KRB5_TGS_NAME,
795 					   request.client->realm.length,
796 					   request.client->realm.data,
797 					   0)))
798 	    goto cleanup;
799     }
800 
801     /* nonce is filled in by send_as_request */
802 
803     if (options && (options->flags & KRB5_GET_INIT_CREDS_OPT_ETYPE_LIST)) {
804 	request.ktype = options->etype_list;
805 	request.nktypes = options->etype_list_length;
806     } else if ((ret = krb5_get_default_in_tkt_ktypes(context,
807 						     &request.ktype)) == 0) {
808 	for (request.nktypes = 0;
809 	     request.ktype[request.nktypes];
810 	     request.nktypes++)
811 	    ;
812     } else {
813 	/* there isn't any useful default here.  ret is set from above */
814 	goto cleanup;
815     }
816 
817     if (options && (options->flags & KRB5_GET_INIT_CREDS_OPT_ADDRESS_LIST)) {
818 	request.addresses = options->address_list;
819     }
820     /* it would be nice if this parsed out an address list, but
821        that would be work. */
822     else if (((ret = krb5_libdefault_boolean(context, &client->realm,
823 					    "no_addresses", &tempint)) == 0)
824 	     || (tempint == 1)) {
825 	    /*EMPTY*/
826 	    ;
827     } else if (((ret = krb5_libdefault_boolean(context, &client->realm,
828 					    "noaddresses", &tempint)) == 0)
829 	     || (tempint == 1)) {
830 	    /*EMPTY*/
831 	    ;
832     } else {
833 	if ((ret = krb5_os_localaddr(context, &request.addresses)))
834 	    goto cleanup;
835     }
836 
837     request.authorization_data.ciphertext.length = 0;
838     request.authorization_data.ciphertext.data = 0;
839     request.unenc_authdata = 0;
840     request.second_ticket = 0;
841 
842     /* set up the other state.  */
843 
844     if (options && (options->flags & KRB5_GET_INIT_CREDS_OPT_PREAUTH_LIST)) {
845 	if ((ret = make_preauth_list(context, options->preauth_list,
846 				    options->preauth_list_length,
847 				    &padata)))
848 	    goto cleanup;
849     }
850 
851     /* the salt is allocated from somewhere, unless it is from the caller,
852        then it is a reference */
853 
854     if (options && (options->flags & KRB5_GET_INIT_CREDS_OPT_SALT)) {
855 	salt = *options->salt;
856     } else {
857 	salt.length = (unsigned int)-1;
858 	salt.data = NULL;
859     }
860 
861     /* now, loop processing preauth data and talking to the kdc */
862 
863     for (loopcount = 0; loopcount < MAX_IN_TKT_LOOPS; loopcount++) {
864 	if (request.padata) {
865 	    krb5_free_pa_data(context, request.padata);
866 	    request.padata = NULL;
867 	}
868 
869 	if ((ret = krb5_do_preauth(context, &request,
870 			  padata, &request.padata,
871 			  &salt, &s2kparams, &etype, &as_key, prompter,
872 			  prompter_data, gak_fct, gak_data)))
873 	    goto cleanup;
874 
875 	if (padata) {
876 	    krb5_free_pa_data(context, padata);
877 	    padata = 0;
878 	}
879 
880 	err_reply = 0;
881 	local_as_reply = 0;
882 	if ((ret = send_as_request(context, &request, &time_now, &err_reply,
883 				   &local_as_reply, use_master)))
884 	    goto cleanup;
885 
886 	if (err_reply) {
887 	    if (err_reply->error == KDC_ERR_PREAUTH_REQUIRED &&
888 		err_reply->e_data.length > 0) {
889 		ret = decode_krb5_padata_sequence(&err_reply->e_data,
890 						  &padata);
891 		krb5_free_error(context, err_reply);
892 		if (ret)
893 		    goto cleanup;
894 	    } else {
895 		ret = (krb5_error_code) err_reply->error
896 			+ ERROR_TABLE_BASE_krb5;
897 		krb5_free_error(context, err_reply);
898 		goto cleanup;
899 	    }
900 	} else if (local_as_reply) {
901 	    break;
902 	} else {
903 	    ret = KRB5KRB_AP_ERR_MSG_TYPE;
904 	    goto cleanup;
905 	}
906     }
907 
908     if (loopcount == MAX_IN_TKT_LOOPS) {
909 	ret = KRB5_GET_IN_TKT_LOOP;
910 	goto cleanup;
911     }
912 
913     /* process any preauth data in the as_reply */
914 
915     if ((ret = krb5_do_preauth(context, &request,
916 		      local_as_reply->padata, &padata,
917 		      &salt, &s2kparams, &etype, &as_key, prompter,
918 		      prompter_data, gak_fct, gak_data)))
919 	goto cleanup;
920 
921     /* XXX if there's padata on output, something is wrong, but it's
922        not obviously an error */
923 
924     /* XXX For 1.1.1 and prior KDC's, when SAM is used w/ USE_SAD_AS_KEY,
925        the AS_REP comes back encrypted in the user's longterm key
926        instead of in the SAD. If there was a SAM preauth, there
927        will be an as_key here which will be the SAD. If that fails,
928        use the gak_fct to get the password, and try again. */
929 
930     /* XXX because etypes are handled poorly (particularly wrt SAM,
931        where the etype is fixed by the kdc), we may want to try
932        decrypt_as_reply twice.  If there's an as_key available, try
933        it.  If decrypting the as_rep fails, or if there isn't an
934        as_key at all yet, then use the gak_fct to get one, and try
935        again.  */
936 
937     if (as_key.length)
938 	ret = decrypt_as_reply(context, (krb5_kdc_req *)NULL, local_as_reply,
939 			       (git_key_proc)NULL, (krb5_const_pointer)NULL,
940 			       &as_key, krb5_kdc_rep_decrypt_proc,
941 			       (krb5_const_pointer)NULL);
942     else
943 	ret = -1;
944 
945     if (ret) {
946 	/* if we haven't get gotten a key, get it now */
947 
948 	if ((ret = ((*gak_fct)(context, request.client,
949 			      local_as_reply->enc_part.enctype,
950 			      prompter, prompter_data, &salt, &s2kparams,
951 			      &as_key, gak_data))))
952 	    goto cleanup;
953 
954 	if ((ret=decrypt_as_reply(context, (krb5_kdc_req *)NULL,
955 				  local_as_reply, (git_key_proc)NULL,
956 				  (krb5_const_pointer)NULL, &as_key,
957 				  krb5_kdc_rep_decrypt_proc,
958 				  (krb5_const_pointer)NULL)))
959 	    goto cleanup;
960     }
961 
962     if ((ret = verify_as_reply(context, time_now, &request, local_as_reply)))
963 	goto cleanup;
964 
965 	/*
966 	 * XXX this should be inside stash_as_reply, but as long as
967 	 * get_in_tkt is still around using that arg as an in/out, I can't
968 	 * do that
969 	 */
970 	(void) memset(creds, 0, sizeof(*creds));
971 
972     if ((ret = stash_as_reply(context, time_now, &request, local_as_reply,
973 			      creds, (krb5_ccache)NULL)))
974 	goto cleanup;
975 
976     /* success */
977 
978     ret = 0;
979 
980 cleanup:
981     if (request.server)
982 	krb5_free_principal(context, request.server);
983     if (request.ktype &&
984 	(!(options && (options->flags & KRB5_GET_INIT_CREDS_OPT_ETYPE_LIST))))
985 	free(request.ktype);
986     if (request.addresses &&
987 	(!(options &&
988 	   (options->flags & KRB5_GET_INIT_CREDS_OPT_ADDRESS_LIST))))
989 	krb5_free_addresses(context, request.addresses);
990     if (padata)
991 	krb5_free_pa_data(context, padata);
992     if (request.padata)
993 	krb5_free_pa_data(context, request.padata);
994     if (as_key.length)
995 	krb5_free_keyblock_contents(context, &as_key);
996     if (salt.data &&
997 	(!(options && (options->flags & KRB5_GET_INIT_CREDS_OPT_SALT))))
998 	krb5_xfree(salt.data);
999     krb5_free_data_contents(context, &s2kparams);
1000     if (as_reply)
1001 	*as_reply = local_as_reply;
1002     else if (local_as_reply)
1003 	krb5_free_kdc_rep(context, local_as_reply);
1004 
1005     return(ret);
1006 }
1007