1 /*	$NetBSD: rd_cred.c,v 1.1.1.1 2011/04/13 18:15:37 elric Exp $	*/
2 
3 /*
4  * Copyright (c) 1997 - 2007 Kungliga Tekniska Högskolan
5  * (Royal Institute of Technology, Stockholm, Sweden).
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  *
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * 3. Neither the name of the Institute nor the names of its contributors
20  *    may be used to endorse or promote products derived from this software
21  *    without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  */
35 
36 #include "krb5_locl.h"
37 
38 static krb5_error_code
39 compare_addrs(krb5_context context,
40 	      krb5_address *a,
41 	      krb5_address *b,
42 	      const char *message)
43 {
44     char a_str[64], b_str[64];
45     size_t len;
46 
47     if(krb5_address_compare (context, a, b))
48 	return 0;
49 
50     krb5_print_address (a, a_str, sizeof(a_str), &len);
51     krb5_print_address (b, b_str, sizeof(b_str), &len);
52     krb5_set_error_message(context, KRB5KRB_AP_ERR_BADADDR,
53 			   "%s: %s != %s", message, b_str, a_str);
54     return KRB5KRB_AP_ERR_BADADDR;
55 }
56 
57 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
58 krb5_rd_cred(krb5_context context,
59 	     krb5_auth_context auth_context,
60 	     krb5_data *in_data,
61 	     krb5_creds ***ret_creds,
62 	     krb5_replay_data *outdata)
63 {
64     krb5_error_code ret;
65     size_t len;
66     KRB_CRED cred;
67     EncKrbCredPart enc_krb_cred_part;
68     krb5_data enc_krb_cred_part_data;
69     krb5_crypto crypto;
70     int i;
71 
72     memset(&enc_krb_cred_part, 0, sizeof(enc_krb_cred_part));
73 
74     if ((auth_context->flags &
75 	 (KRB5_AUTH_CONTEXT_RET_TIME | KRB5_AUTH_CONTEXT_RET_SEQUENCE)) &&
76 	outdata == NULL)
77 	return KRB5_RC_REQUIRED; /* XXX better error, MIT returns this */
78 
79     *ret_creds = NULL;
80 
81     ret = decode_KRB_CRED(in_data->data, in_data->length,
82 			  &cred, &len);
83     if(ret) {
84 	krb5_clear_error_message(context);
85 	return ret;
86     }
87 
88     if (cred.pvno != 5) {
89 	ret = KRB5KRB_AP_ERR_BADVERSION;
90 	krb5_clear_error_message (context);
91 	goto out;
92     }
93 
94     if (cred.msg_type != krb_cred) {
95 	ret = KRB5KRB_AP_ERR_MSG_TYPE;
96 	krb5_clear_error_message (context);
97 	goto out;
98     }
99 
100     if (cred.enc_part.etype == ETYPE_NULL) {
101 	/* DK: MIT GSS-API Compatibility */
102 	enc_krb_cred_part_data.length = cred.enc_part.cipher.length;
103 	enc_krb_cred_part_data.data   = cred.enc_part.cipher.data;
104     } else {
105 	/* Try both subkey and session key.
106 	 *
107 	 * RFC4120 claims we should use the session key, but Heimdal
108 	 * before 0.8 used the remote subkey if it was send in the
109 	 * auth_context.
110 	 */
111 
112 	if (auth_context->remote_subkey) {
113 	    ret = krb5_crypto_init(context, auth_context->remote_subkey,
114 				   0, &crypto);
115 	    if (ret)
116 		goto out;
117 
118 	    ret = krb5_decrypt_EncryptedData(context,
119 					     crypto,
120 					     KRB5_KU_KRB_CRED,
121 					     &cred.enc_part,
122 					     &enc_krb_cred_part_data);
123 
124 	    krb5_crypto_destroy(context, crypto);
125 	}
126 
127 	/*
128 	 * If there was not subkey, or we failed using subkey,
129 	 * retry using the session key
130 	 */
131 	if (auth_context->remote_subkey == NULL || ret == KRB5KRB_AP_ERR_BAD_INTEGRITY)
132 	{
133 
134 	    ret = krb5_crypto_init(context, auth_context->keyblock,
135 				   0, &crypto);
136 
137 	    if (ret)
138 		goto out;
139 
140 	    ret = krb5_decrypt_EncryptedData(context,
141 					     crypto,
142 					     KRB5_KU_KRB_CRED,
143 					     &cred.enc_part,
144 					     &enc_krb_cred_part_data);
145 
146 	    krb5_crypto_destroy(context, crypto);
147 	}
148 	if (ret)
149 	    goto out;
150     }
151 
152     ret = decode_EncKrbCredPart(enc_krb_cred_part_data.data,
153 				enc_krb_cred_part_data.length,
154 				&enc_krb_cred_part,
155 				&len);
156     if (enc_krb_cred_part_data.data != cred.enc_part.cipher.data)
157 	krb5_data_free(&enc_krb_cred_part_data);
158     if (ret) {
159 	krb5_set_error_message(context, ret,
160 			       N_("Failed to decode "
161 				  "encrypte credential part", ""));
162 	goto out;
163     }
164 
165     /* check sender address */
166 
167     if (enc_krb_cred_part.s_address
168 	&& auth_context->remote_address
169 	&& auth_context->remote_port) {
170 	krb5_address *a;
171 
172 	ret = krb5_make_addrport (context, &a,
173 				  auth_context->remote_address,
174 				  auth_context->remote_port);
175 	if (ret)
176 	    goto out;
177 
178 
179 	ret = compare_addrs(context, a, enc_krb_cred_part.s_address,
180 			    N_("sender address is wrong "
181 			       "in received creds", ""));
182 	krb5_free_address(context, a);
183 	free(a);
184 	if(ret)
185 	    goto out;
186     }
187 
188     /* check receiver address */
189 
190     if (enc_krb_cred_part.r_address
191 	&& auth_context->local_address) {
192 	if(auth_context->local_port &&
193 	   enc_krb_cred_part.r_address->addr_type == KRB5_ADDRESS_ADDRPORT) {
194 	    krb5_address *a;
195 	    ret = krb5_make_addrport (context, &a,
196 				      auth_context->local_address,
197 				      auth_context->local_port);
198 	    if (ret)
199 		goto out;
200 
201 	    ret = compare_addrs(context, a, enc_krb_cred_part.r_address,
202 				N_("receiver address is wrong "
203 				   "in received creds", ""));
204 	    krb5_free_address(context, a);
205 	    free(a);
206 	    if(ret)
207 		goto out;
208 	} else {
209 	    ret = compare_addrs(context, auth_context->local_address,
210 				enc_krb_cred_part.r_address,
211 				N_("receiver address is wrong "
212 				   "in received creds", ""));
213 	    if(ret)
214 		goto out;
215 	}
216     }
217 
218     /* check timestamp */
219     if (auth_context->flags & KRB5_AUTH_CONTEXT_DO_TIME) {
220 	krb5_timestamp sec;
221 
222 	krb5_timeofday (context, &sec);
223 
224 	if (enc_krb_cred_part.timestamp == NULL ||
225 	    enc_krb_cred_part.usec      == NULL ||
226 	    abs(*enc_krb_cred_part.timestamp - sec)
227 	    > context->max_skew) {
228 	    krb5_clear_error_message (context);
229 	    ret = KRB5KRB_AP_ERR_SKEW;
230 	    goto out;
231 	}
232     }
233 
234     if ((auth_context->flags &
235 	 (KRB5_AUTH_CONTEXT_RET_TIME | KRB5_AUTH_CONTEXT_RET_SEQUENCE))) {
236 	/* if these fields are not present in the cred-part, silently
237            return zero */
238 	memset(outdata, 0, sizeof(*outdata));
239 	if(enc_krb_cred_part.timestamp)
240 	    outdata->timestamp = *enc_krb_cred_part.timestamp;
241 	if(enc_krb_cred_part.usec)
242 	    outdata->usec = *enc_krb_cred_part.usec;
243 	if(enc_krb_cred_part.nonce)
244 	    outdata->seq = *enc_krb_cred_part.nonce;
245     }
246 
247     /* Convert to NULL terminated list of creds */
248 
249     *ret_creds = calloc(enc_krb_cred_part.ticket_info.len + 1,
250 			sizeof(**ret_creds));
251 
252     if (*ret_creds == NULL) {
253 	ret = ENOMEM;
254 	krb5_set_error_message(context, ret,
255 			       N_("malloc: out of memory", ""));
256 	goto out;
257     }
258 
259     for (i = 0; i < enc_krb_cred_part.ticket_info.len; ++i) {
260 	KrbCredInfo *kci = &enc_krb_cred_part.ticket_info.val[i];
261 	krb5_creds *creds;
262 
263 	creds = calloc(1, sizeof(*creds));
264 	if(creds == NULL) {
265 	    ret = ENOMEM;
266 	    krb5_set_error_message(context, ret,
267 				   N_("malloc: out of memory", ""));
268 	    goto out;
269 	}
270 
271 	ASN1_MALLOC_ENCODE(Ticket, creds->ticket.data, creds->ticket.length,
272 			   &cred.tickets.val[i], &len, ret);
273 	if (ret) {
274 	    free(creds);
275 	    goto out;
276 	}
277 	if(creds->ticket.length != len)
278 	    krb5_abortx(context, "internal error in ASN.1 encoder");
279 	copy_EncryptionKey (&kci->key, &creds->session);
280 	if (kci->prealm && kci->pname)
281 	    _krb5_principalname2krb5_principal (context,
282 						&creds->client,
283 						*kci->pname,
284 						*kci->prealm);
285 	if (kci->flags)
286 	    creds->flags.b = *kci->flags;
287 	if (kci->authtime)
288 	    creds->times.authtime = *kci->authtime;
289 	if (kci->starttime)
290 	    creds->times.starttime = *kci->starttime;
291 	if (kci->endtime)
292 	    creds->times.endtime = *kci->endtime;
293 	if (kci->renew_till)
294 	    creds->times.renew_till = *kci->renew_till;
295 	if (kci->srealm && kci->sname)
296 	    _krb5_principalname2krb5_principal (context,
297 						&creds->server,
298 						*kci->sname,
299 						*kci->srealm);
300 	if (kci->caddr)
301 	    krb5_copy_addresses (context,
302 				 kci->caddr,
303 				 &creds->addresses);
304 
305 	(*ret_creds)[i] = creds;
306 
307     }
308     (*ret_creds)[i] = NULL;
309 
310     free_KRB_CRED (&cred);
311     free_EncKrbCredPart(&enc_krb_cred_part);
312 
313     return 0;
314 
315   out:
316     free_EncKrbCredPart(&enc_krb_cred_part);
317     free_KRB_CRED (&cred);
318     if(*ret_creds) {
319 	for(i = 0; (*ret_creds)[i]; i++)
320 	    krb5_free_creds(context, (*ret_creds)[i]);
321 	free(*ret_creds);
322 	*ret_creds = NULL;
323     }
324     return ret;
325 }
326 
327 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
328 krb5_rd_cred2 (krb5_context      context,
329 	       krb5_auth_context auth_context,
330 	       krb5_ccache       ccache,
331 	       krb5_data         *in_data)
332 {
333     krb5_error_code ret;
334     krb5_creds **creds;
335     int i;
336 
337     ret = krb5_rd_cred(context, auth_context, in_data, &creds, NULL);
338     if(ret)
339 	return ret;
340 
341     /* Store the creds in the ccache */
342 
343     for(i = 0; creds && creds[i]; i++) {
344 	krb5_cc_store_cred(context, ccache, creds[i]);
345 	krb5_free_creds(context, creds[i]);
346     }
347     free(creds);
348     return 0;
349 }
350