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