xref: /freebsd/crypto/heimdal/lib/krb5/rd_cred.c (revision 6a068746777241722b2b32c5d0bc443a2a64d80b)
1b528cefcSMark Murray /*
2*ae771770SStanislav Sedov  * Copyright (c) 1997 - 2007 Kungliga Tekniska Högskolan
3b528cefcSMark Murray  * (Royal Institute of Technology, Stockholm, Sweden).
4b528cefcSMark Murray  * All rights reserved.
5b528cefcSMark Murray  *
6b528cefcSMark Murray  * Redistribution and use in source and binary forms, with or without
7b528cefcSMark Murray  * modification, are permitted provided that the following conditions
8b528cefcSMark Murray  * are met:
9b528cefcSMark Murray  *
10b528cefcSMark Murray  * 1. Redistributions of source code must retain the above copyright
11b528cefcSMark Murray  *    notice, this list of conditions and the following disclaimer.
12b528cefcSMark Murray  *
13b528cefcSMark Murray  * 2. Redistributions in binary form must reproduce the above copyright
14b528cefcSMark Murray  *    notice, this list of conditions and the following disclaimer in the
15b528cefcSMark Murray  *    documentation and/or other materials provided with the distribution.
16b528cefcSMark Murray  *
17b528cefcSMark Murray  * 3. Neither the name of the Institute nor the names of its contributors
18b528cefcSMark Murray  *    may be used to endorse or promote products derived from this software
19b528cefcSMark Murray  *    without specific prior written permission.
20b528cefcSMark Murray  *
21b528cefcSMark Murray  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22b528cefcSMark Murray  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23b528cefcSMark Murray  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24b528cefcSMark Murray  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25b528cefcSMark Murray  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26b528cefcSMark Murray  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27b528cefcSMark Murray  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28b528cefcSMark Murray  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29b528cefcSMark Murray  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30b528cefcSMark Murray  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31b528cefcSMark Murray  * SUCH DAMAGE.
32b528cefcSMark Murray  */
33b528cefcSMark Murray 
34*ae771770SStanislav Sedov #include "krb5_locl.h"
35b528cefcSMark Murray 
36c19800e8SDoug Rabson static krb5_error_code
compare_addrs(krb5_context context,krb5_address * a,krb5_address * b,const char * message)37c19800e8SDoug Rabson compare_addrs(krb5_context context,
38c19800e8SDoug Rabson 	      krb5_address *a,
39c19800e8SDoug Rabson 	      krb5_address *b,
40c19800e8SDoug Rabson 	      const char *message)
41c19800e8SDoug Rabson {
42c19800e8SDoug Rabson     char a_str[64], b_str[64];
43c19800e8SDoug Rabson     size_t len;
44c19800e8SDoug Rabson 
45c19800e8SDoug Rabson     if(krb5_address_compare (context, a, b))
46c19800e8SDoug Rabson 	return 0;
47c19800e8SDoug Rabson 
48c19800e8SDoug Rabson     krb5_print_address (a, a_str, sizeof(a_str), &len);
49c19800e8SDoug Rabson     krb5_print_address (b, b_str, sizeof(b_str), &len);
50*ae771770SStanislav Sedov     krb5_set_error_message(context, KRB5KRB_AP_ERR_BADADDR,
51*ae771770SStanislav Sedov 			   "%s: %s != %s", message, b_str, a_str);
52c19800e8SDoug Rabson     return KRB5KRB_AP_ERR_BADADDR;
53c19800e8SDoug Rabson }
54c19800e8SDoug Rabson 
55*ae771770SStanislav Sedov KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_rd_cred(krb5_context context,krb5_auth_context auth_context,krb5_data * in_data,krb5_creds *** ret_creds,krb5_replay_data * outdata)56b528cefcSMark Murray krb5_rd_cred(krb5_context context,
57b528cefcSMark Murray 	     krb5_auth_context auth_context,
585e9cd1aeSAssar Westerlund 	     krb5_data *in_data,
595e9cd1aeSAssar Westerlund 	     krb5_creds ***ret_creds,
60c19800e8SDoug Rabson 	     krb5_replay_data *outdata)
61b528cefcSMark Murray {
62b528cefcSMark Murray     krb5_error_code ret;
63b528cefcSMark Murray     size_t len;
64b528cefcSMark Murray     KRB_CRED cred;
65b528cefcSMark Murray     EncKrbCredPart enc_krb_cred_part;
66b528cefcSMark Murray     krb5_data enc_krb_cred_part_data;
67b528cefcSMark Murray     krb5_crypto crypto;
68*ae771770SStanislav Sedov     size_t i;
69b528cefcSMark Murray 
70c19800e8SDoug Rabson     memset(&enc_krb_cred_part, 0, sizeof(enc_krb_cred_part));
71*ae771770SStanislav Sedov     krb5_data_zero(&enc_krb_cred_part_data);
72c19800e8SDoug Rabson 
73c19800e8SDoug Rabson     if ((auth_context->flags &
74c19800e8SDoug Rabson 	 (KRB5_AUTH_CONTEXT_RET_TIME | KRB5_AUTH_CONTEXT_RET_SEQUENCE)) &&
75c19800e8SDoug Rabson 	outdata == NULL)
76c19800e8SDoug Rabson 	return KRB5_RC_REQUIRED; /* XXX better error, MIT returns this */
77c19800e8SDoug Rabson 
78adb0ddaeSAssar Westerlund     *ret_creds = NULL;
79adb0ddaeSAssar Westerlund 
80b528cefcSMark Murray     ret = decode_KRB_CRED(in_data->data, in_data->length,
81b528cefcSMark Murray 			  &cred, &len);
82c19800e8SDoug Rabson     if(ret) {
83*ae771770SStanislav Sedov 	krb5_clear_error_message(context);
84b528cefcSMark Murray 	return ret;
85c19800e8SDoug Rabson     }
86b528cefcSMark Murray 
87b528cefcSMark Murray     if (cred.pvno != 5) {
88b528cefcSMark Murray 	ret = KRB5KRB_AP_ERR_BADVERSION;
89*ae771770SStanislav Sedov 	krb5_clear_error_message (context);
90b528cefcSMark Murray 	goto out;
91b528cefcSMark Murray     }
92b528cefcSMark Murray 
93b528cefcSMark Murray     if (cred.msg_type != krb_cred) {
94b528cefcSMark Murray 	ret = KRB5KRB_AP_ERR_MSG_TYPE;
95*ae771770SStanislav Sedov 	krb5_clear_error_message (context);
96b528cefcSMark Murray 	goto out;
97b528cefcSMark Murray     }
98b528cefcSMark Murray 
995e9cd1aeSAssar Westerlund     if (cred.enc_part.etype == ETYPE_NULL) {
1005e9cd1aeSAssar Westerlund 	/* DK: MIT GSS-API Compatibility */
1015e9cd1aeSAssar Westerlund 	enc_krb_cred_part_data.length = cred.enc_part.cipher.length;
1025e9cd1aeSAssar Westerlund 	enc_krb_cred_part_data.data   = cred.enc_part.cipher.data;
1035e9cd1aeSAssar Westerlund     } else {
104c19800e8SDoug Rabson 	/* Try both subkey and session key.
105c19800e8SDoug Rabson 	 *
106c19800e8SDoug Rabson 	 * RFC4120 claims we should use the session key, but Heimdal
107c19800e8SDoug Rabson 	 * before 0.8 used the remote subkey if it was send in the
108c19800e8SDoug Rabson 	 * auth_context.
109c19800e8SDoug Rabson 	 */
110c19800e8SDoug Rabson 
111c19800e8SDoug Rabson 	if (auth_context->remote_subkey) {
1125e9cd1aeSAssar Westerlund 	    ret = krb5_crypto_init(context, auth_context->remote_subkey,
1135e9cd1aeSAssar Westerlund 				   0, &crypto);
114c19800e8SDoug Rabson 	    if (ret)
115c19800e8SDoug Rabson 		goto out;
116c19800e8SDoug Rabson 
117c19800e8SDoug Rabson 	    ret = krb5_decrypt_EncryptedData(context,
118c19800e8SDoug Rabson 					     crypto,
119c19800e8SDoug Rabson 					     KRB5_KU_KRB_CRED,
120c19800e8SDoug Rabson 					     &cred.enc_part,
121c19800e8SDoug Rabson 					     &enc_krb_cred_part_data);
122c19800e8SDoug Rabson 
123c19800e8SDoug Rabson 	    krb5_crypto_destroy(context, crypto);
124c19800e8SDoug Rabson 	}
125c19800e8SDoug Rabson 
126c19800e8SDoug Rabson 	/*
127c19800e8SDoug Rabson 	 * If there was not subkey, or we failed using subkey,
128c19800e8SDoug Rabson 	 * retry using the session key
129c19800e8SDoug Rabson 	 */
130c19800e8SDoug Rabson 	if (auth_context->remote_subkey == NULL || ret == KRB5KRB_AP_ERR_BAD_INTEGRITY)
131c19800e8SDoug Rabson 	{
132c19800e8SDoug Rabson 
1335e9cd1aeSAssar Westerlund 	    ret = krb5_crypto_init(context, auth_context->keyblock,
1345e9cd1aeSAssar Westerlund 				   0, &crypto);
1355e9cd1aeSAssar Westerlund 
1365e9cd1aeSAssar Westerlund 	    if (ret)
1375e9cd1aeSAssar Westerlund 		goto out;
1385e9cd1aeSAssar Westerlund 
139b528cefcSMark Murray 	    ret = krb5_decrypt_EncryptedData(context,
140b528cefcSMark Murray 					     crypto,
141b528cefcSMark Murray 					     KRB5_KU_KRB_CRED,
142b528cefcSMark Murray 					     &cred.enc_part,
143b528cefcSMark Murray 					     &enc_krb_cred_part_data);
1445e9cd1aeSAssar Westerlund 
145b528cefcSMark Murray 	    krb5_crypto_destroy(context, crypto);
146c19800e8SDoug Rabson 	}
147b528cefcSMark Murray 	if (ret)
148b528cefcSMark Murray 	    goto out;
1495e9cd1aeSAssar Westerlund     }
150b528cefcSMark Murray 
151*ae771770SStanislav Sedov     ret = decode_EncKrbCredPart(enc_krb_cred_part_data.data,
152b528cefcSMark Murray 				enc_krb_cred_part_data.length,
153b528cefcSMark Murray 				&enc_krb_cred_part,
154b528cefcSMark Murray 				&len);
155c19800e8SDoug Rabson     if (enc_krb_cred_part_data.data != cred.enc_part.cipher.data)
156c19800e8SDoug Rabson 	krb5_data_free(&enc_krb_cred_part_data);
157*ae771770SStanislav Sedov     if (ret) {
158*ae771770SStanislav Sedov 	krb5_set_error_message(context, ret,
159*ae771770SStanislav Sedov 			       N_("Failed to decode "
160*ae771770SStanislav Sedov 				  "encrypte credential part", ""));
161b528cefcSMark Murray 	goto out;
162*ae771770SStanislav Sedov     }
163b528cefcSMark Murray 
164b528cefcSMark Murray     /* check sender address */
165b528cefcSMark Murray 
166b528cefcSMark Murray     if (enc_krb_cred_part.s_address
1675e9cd1aeSAssar Westerlund 	&& auth_context->remote_address
1685e9cd1aeSAssar Westerlund 	&& auth_context->remote_port) {
169b528cefcSMark Murray 	krb5_address *a;
170b528cefcSMark Murray 
171adb0ddaeSAssar Westerlund 	ret = krb5_make_addrport (context, &a,
172b528cefcSMark Murray 				  auth_context->remote_address,
173b528cefcSMark Murray 				  auth_context->remote_port);
174b528cefcSMark Murray 	if (ret)
175b528cefcSMark Murray 	    goto out;
176b528cefcSMark Murray 
177b528cefcSMark Murray 
178c19800e8SDoug Rabson 	ret = compare_addrs(context, a, enc_krb_cred_part.s_address,
179*ae771770SStanislav Sedov 			    N_("sender address is wrong "
180*ae771770SStanislav Sedov 			       "in received creds", ""));
181b528cefcSMark Murray 	krb5_free_address(context, a);
182b528cefcSMark Murray 	free(a);
183c19800e8SDoug Rabson 	if(ret)
184b528cefcSMark Murray 	    goto out;
185b528cefcSMark Murray     }
186b528cefcSMark Murray 
187b528cefcSMark Murray     /* check receiver address */
188b528cefcSMark Murray 
189b528cefcSMark Murray     if (enc_krb_cred_part.r_address
1908373020dSJacques Vidrine 	&& auth_context->local_address) {
1918373020dSJacques Vidrine 	if(auth_context->local_port &&
1928373020dSJacques Vidrine 	   enc_krb_cred_part.r_address->addr_type == KRB5_ADDRESS_ADDRPORT) {
1938373020dSJacques Vidrine 	    krb5_address *a;
1948373020dSJacques Vidrine 	    ret = krb5_make_addrport (context, &a,
1958373020dSJacques Vidrine 				      auth_context->local_address,
1968373020dSJacques Vidrine 				      auth_context->local_port);
1978373020dSJacques Vidrine 	    if (ret)
1988373020dSJacques Vidrine 		goto out;
1998373020dSJacques Vidrine 
200c19800e8SDoug Rabson 	    ret = compare_addrs(context, a, enc_krb_cred_part.r_address,
201*ae771770SStanislav Sedov 				N_("receiver address is wrong "
202*ae771770SStanislav Sedov 				   "in received creds", ""));
2038373020dSJacques Vidrine 	    krb5_free_address(context, a);
2048373020dSJacques Vidrine 	    free(a);
205c19800e8SDoug Rabson 	    if(ret)
2068373020dSJacques Vidrine 		goto out;
2078373020dSJacques Vidrine 	} else {
208c19800e8SDoug Rabson 	    ret = compare_addrs(context, auth_context->local_address,
209c19800e8SDoug Rabson 				enc_krb_cred_part.r_address,
210*ae771770SStanislav Sedov 				N_("receiver address is wrong "
211*ae771770SStanislav Sedov 				   "in received creds", ""));
212c19800e8SDoug Rabson 	    if(ret)
213b528cefcSMark Murray 		goto out;
214b528cefcSMark Murray 	}
2158373020dSJacques Vidrine     }
216b528cefcSMark Murray 
217b528cefcSMark Murray     /* check timestamp */
218b528cefcSMark Murray     if (auth_context->flags & KRB5_AUTH_CONTEXT_DO_TIME) {
21913e3f4d6SMark Murray 	krb5_timestamp sec;
220b528cefcSMark Murray 
221b528cefcSMark Murray 	krb5_timeofday (context, &sec);
222b528cefcSMark Murray 
223b528cefcSMark Murray 	if (enc_krb_cred_part.timestamp == NULL ||
224b528cefcSMark Murray 	    enc_krb_cred_part.usec      == NULL ||
225b528cefcSMark Murray 	    abs(*enc_krb_cred_part.timestamp - sec)
226b528cefcSMark Murray 	    > context->max_skew) {
227*ae771770SStanislav Sedov 	    krb5_clear_error_message (context);
228b528cefcSMark Murray 	    ret = KRB5KRB_AP_ERR_SKEW;
229b528cefcSMark Murray 	    goto out;
230b528cefcSMark Murray 	}
231b528cefcSMark Murray     }
232b528cefcSMark Murray 
233c19800e8SDoug Rabson     if ((auth_context->flags &
234c19800e8SDoug Rabson 	 (KRB5_AUTH_CONTEXT_RET_TIME | KRB5_AUTH_CONTEXT_RET_SEQUENCE))) {
235c19800e8SDoug Rabson 	/* if these fields are not present in the cred-part, silently
236c19800e8SDoug Rabson            return zero */
237c19800e8SDoug Rabson 	memset(outdata, 0, sizeof(*outdata));
2385e9cd1aeSAssar Westerlund 	if(enc_krb_cred_part.timestamp)
239c19800e8SDoug Rabson 	    outdata->timestamp = *enc_krb_cred_part.timestamp;
2405e9cd1aeSAssar Westerlund 	if(enc_krb_cred_part.usec)
241c19800e8SDoug Rabson 	    outdata->usec = *enc_krb_cred_part.usec;
2425e9cd1aeSAssar Westerlund 	if(enc_krb_cred_part.nonce)
243c19800e8SDoug Rabson 	    outdata->seq = *enc_krb_cred_part.nonce;
2445e9cd1aeSAssar Westerlund     }
245b528cefcSMark Murray 
2465e9cd1aeSAssar Westerlund     /* Convert to NULL terminated list of creds */
2475e9cd1aeSAssar Westerlund 
2485e9cd1aeSAssar Westerlund     *ret_creds = calloc(enc_krb_cred_part.ticket_info.len + 1,
2495e9cd1aeSAssar Westerlund 			sizeof(**ret_creds));
250b528cefcSMark Murray 
2514137ff4cSJacques Vidrine     if (*ret_creds == NULL) {
2524137ff4cSJacques Vidrine 	ret = ENOMEM;
253*ae771770SStanislav Sedov 	krb5_set_error_message(context, ret,
254*ae771770SStanislav Sedov 			       N_("malloc: out of memory", ""));
2554137ff4cSJacques Vidrine 	goto out;
2564137ff4cSJacques Vidrine     }
2574137ff4cSJacques Vidrine 
258b528cefcSMark Murray     for (i = 0; i < enc_krb_cred_part.ticket_info.len; ++i) {
259b528cefcSMark Murray 	KrbCredInfo *kci = &enc_krb_cred_part.ticket_info.val[i];
2605e9cd1aeSAssar Westerlund 	krb5_creds *creds;
261b528cefcSMark Murray 
2625e9cd1aeSAssar Westerlund 	creds = calloc(1, sizeof(*creds));
2635e9cd1aeSAssar Westerlund 	if(creds == NULL) {
2645e9cd1aeSAssar Westerlund 	    ret = ENOMEM;
265*ae771770SStanislav Sedov 	    krb5_set_error_message(context, ret,
266*ae771770SStanislav Sedov 				   N_("malloc: out of memory", ""));
2675e9cd1aeSAssar Westerlund 	    goto out;
2685e9cd1aeSAssar Westerlund 	}
269b528cefcSMark Murray 
2700cadf2f4SJacques Vidrine 	ASN1_MALLOC_ENCODE(Ticket, creds->ticket.data, creds->ticket.length,
2710cadf2f4SJacques Vidrine 			   &cred.tickets.val[i], &len, ret);
272c19800e8SDoug Rabson 	if (ret) {
273c19800e8SDoug Rabson 	    free(creds);
274b528cefcSMark Murray 	    goto out;
275c19800e8SDoug Rabson 	}
2760cadf2f4SJacques Vidrine 	if(creds->ticket.length != len)
2770cadf2f4SJacques Vidrine 	    krb5_abortx(context, "internal error in ASN.1 encoder");
2785e9cd1aeSAssar Westerlund 	copy_EncryptionKey (&kci->key, &creds->session);
279b528cefcSMark Murray 	if (kci->prealm && kci->pname)
280c19800e8SDoug Rabson 	    _krb5_principalname2krb5_principal (context,
281c19800e8SDoug Rabson 						&creds->client,
282b528cefcSMark Murray 						*kci->pname,
283b528cefcSMark Murray 						*kci->prealm);
284b528cefcSMark Murray 	if (kci->flags)
2855e9cd1aeSAssar Westerlund 	    creds->flags.b = *kci->flags;
286b528cefcSMark Murray 	if (kci->authtime)
2875e9cd1aeSAssar Westerlund 	    creds->times.authtime = *kci->authtime;
288b528cefcSMark Murray 	if (kci->starttime)
2895e9cd1aeSAssar Westerlund 	    creds->times.starttime = *kci->starttime;
290b528cefcSMark Murray 	if (kci->endtime)
2915e9cd1aeSAssar Westerlund 	    creds->times.endtime = *kci->endtime;
292b528cefcSMark Murray 	if (kci->renew_till)
2935e9cd1aeSAssar Westerlund 	    creds->times.renew_till = *kci->renew_till;
294b528cefcSMark Murray 	if (kci->srealm && kci->sname)
295c19800e8SDoug Rabson 	    _krb5_principalname2krb5_principal (context,
296c19800e8SDoug Rabson 						&creds->server,
297b528cefcSMark Murray 						*kci->sname,
298b528cefcSMark Murray 						*kci->srealm);
299b528cefcSMark Murray 	if (kci->caddr)
300b528cefcSMark Murray 	    krb5_copy_addresses (context,
301b528cefcSMark Murray 				 kci->caddr,
3025e9cd1aeSAssar Westerlund 				 &creds->addresses);
3035e9cd1aeSAssar Westerlund 
3045e9cd1aeSAssar Westerlund 	(*ret_creds)[i] = creds;
3055e9cd1aeSAssar Westerlund 
306b528cefcSMark Murray     }
3075e9cd1aeSAssar Westerlund     (*ret_creds)[i] = NULL;
308c19800e8SDoug Rabson 
309c19800e8SDoug Rabson     free_KRB_CRED (&cred);
310c19800e8SDoug Rabson     free_EncKrbCredPart(&enc_krb_cred_part);
311c19800e8SDoug Rabson 
3125e9cd1aeSAssar Westerlund     return 0;
313b528cefcSMark Murray 
314b528cefcSMark Murray   out:
315c19800e8SDoug Rabson     free_EncKrbCredPart(&enc_krb_cred_part);
316b528cefcSMark Murray     free_KRB_CRED (&cred);
3175e9cd1aeSAssar Westerlund     if(*ret_creds) {
3185e9cd1aeSAssar Westerlund 	for(i = 0; (*ret_creds)[i]; i++)
3195e9cd1aeSAssar Westerlund 	    krb5_free_creds(context, (*ret_creds)[i]);
3205e9cd1aeSAssar Westerlund 	free(*ret_creds);
321c19800e8SDoug Rabson 	*ret_creds = NULL;
3225e9cd1aeSAssar Westerlund     }
323b528cefcSMark Murray     return ret;
324b528cefcSMark Murray }
3255e9cd1aeSAssar Westerlund 
326*ae771770SStanislav Sedov KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_rd_cred2(krb5_context context,krb5_auth_context auth_context,krb5_ccache ccache,krb5_data * in_data)3275e9cd1aeSAssar Westerlund krb5_rd_cred2 (krb5_context      context,
3285e9cd1aeSAssar Westerlund 	       krb5_auth_context auth_context,
3295e9cd1aeSAssar Westerlund 	       krb5_ccache       ccache,
3305e9cd1aeSAssar Westerlund 	       krb5_data         *in_data)
3315e9cd1aeSAssar Westerlund {
3325e9cd1aeSAssar Westerlund     krb5_error_code ret;
3335e9cd1aeSAssar Westerlund     krb5_creds **creds;
3345e9cd1aeSAssar Westerlund     int i;
3355e9cd1aeSAssar Westerlund 
3365e9cd1aeSAssar Westerlund     ret = krb5_rd_cred(context, auth_context, in_data, &creds, NULL);
3375e9cd1aeSAssar Westerlund     if(ret)
3385e9cd1aeSAssar Westerlund 	return ret;
3395e9cd1aeSAssar Westerlund 
3405e9cd1aeSAssar Westerlund     /* Store the creds in the ccache */
3415e9cd1aeSAssar Westerlund 
3425e9cd1aeSAssar Westerlund     for(i = 0; creds && creds[i]; i++) {
3435e9cd1aeSAssar Westerlund 	krb5_cc_store_cred(context, ccache, creds[i]);
3445e9cd1aeSAssar Westerlund 	krb5_free_creds(context, creds[i]);
3455e9cd1aeSAssar Westerlund     }
3465e9cd1aeSAssar Westerlund     free(creds);
3475e9cd1aeSAssar Westerlund     return 0;
3485e9cd1aeSAssar Westerlund }
349