xref: /freebsd/crypto/heimdal/lib/krb5/rd_req.c (revision b528cefc6b8f9670b31a865051741d946cb37085)
1 /*
2  * Copyright (c) 1997, 1998, 1999 Kungliga Tekniska H�gskolan
3  * (Royal Institute of Technology, Stockholm, Sweden).
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  *
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * 3. Neither the name of the Institute nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  */
33 
34 #include <krb5_locl.h>
35 
36 RCSID("$Id: rd_req.c,v 1.38 1999/12/02 17:05:12 joda Exp $");
37 
38 static krb5_error_code
39 decrypt_tkt_enc_part (krb5_context context,
40 		      krb5_keyblock *key,
41 		      EncryptedData *enc_part,
42 		      EncTicketPart *decr_part)
43 {
44     krb5_error_code ret;
45     krb5_data plain;
46     size_t len;
47     krb5_crypto crypto;
48 
49     krb5_crypto_init(context, key, 0, &crypto);
50     ret = krb5_decrypt_EncryptedData (context,
51 				      crypto,
52 				      KRB5_KU_TICKET,
53 				      enc_part,
54 				      &plain);
55     krb5_crypto_destroy(context, crypto);
56     if (ret)
57 	return ret;
58 
59     ret = krb5_decode_EncTicketPart(context, plain.data, plain.length,
60 				    decr_part, &len);
61     krb5_data_free (&plain);
62     return ret;
63 }
64 
65 static krb5_error_code
66 decrypt_authenticator (krb5_context context,
67 		       EncryptionKey *key,
68 		       EncryptedData *enc_part,
69 		       Authenticator *authenticator)
70 {
71     krb5_error_code ret;
72     krb5_data plain;
73     size_t len;
74     krb5_crypto crypto;
75 
76     krb5_crypto_init(context, key, 0, &crypto);
77     ret = krb5_decrypt_EncryptedData (context,
78 				      crypto,
79 				      KRB5_KU_AP_REQ_AUTH,
80 				      enc_part,
81 				      &plain);
82     krb5_crypto_destroy(context, crypto);
83     if (ret)
84 	return ret;
85 
86     ret = krb5_decode_Authenticator(context, plain.data, plain.length,
87 				    authenticator, &len);
88     krb5_data_free (&plain);
89     return ret;
90 }
91 
92 krb5_error_code
93 krb5_decode_ap_req(krb5_context context,
94 		   const krb5_data *inbuf,
95 		   krb5_ap_req *ap_req)
96 {
97     krb5_error_code ret;
98     size_t len;
99     ret = decode_AP_REQ(inbuf->data, inbuf->length, ap_req, &len);
100     if (ret)
101 	return ret;
102     if (ap_req->pvno != 5){
103 	free_AP_REQ(ap_req);
104 	return KRB5KRB_AP_ERR_BADVERSION;
105     }
106     if (ap_req->msg_type != krb_ap_req){
107 	free_AP_REQ(ap_req);
108 	return KRB5KRB_AP_ERR_MSG_TYPE;
109     }
110     if (ap_req->ticket.tkt_vno != 5){
111 	free_AP_REQ(ap_req);
112 	return KRB5KRB_AP_ERR_BADVERSION;
113     }
114     return 0;
115 }
116 
117 krb5_error_code
118 krb5_decrypt_ticket(krb5_context context,
119 		    Ticket *ticket,
120 		    krb5_keyblock *key,
121 		    EncTicketPart *out,
122 		    krb5_flags flags)
123 {
124     EncTicketPart t;
125     krb5_error_code ret;
126     ret = decrypt_tkt_enc_part (context, key, &ticket->enc_part, &t);
127     if (ret)
128 	return ret;
129 
130     {
131 	int32_t now;
132 	time_t start = t.authtime;
133 
134 	krb5_timeofday (context, &now);
135 	if(t.starttime)
136 	    start = *t.starttime;
137 	if(start - now > context->max_skew
138 	   || (t.flags.invalid
139 	       && !(flags & KRB5_VERIFY_AP_REQ_IGNORE_INVALID)))
140 	    return KRB5KRB_AP_ERR_TKT_NYV;
141 	if(now - t.endtime > context->max_skew)
142 	    return KRB5KRB_AP_ERR_TKT_EXPIRED;
143     }
144 
145     if(out)
146 	*out = t;
147     else
148 	free_EncTicketPart(&t);
149     return 0;
150 }
151 
152 krb5_error_code
153 krb5_verify_authenticator_checksum(krb5_context context,
154 				   krb5_auth_context ac,
155 				   void *data,
156 				   size_t len)
157 {
158     krb5_error_code ret;
159     krb5_keyblock *key;
160     krb5_authenticator authenticator;
161     krb5_crypto crypto;
162 
163     ret = krb5_auth_getauthenticator (context,
164 				      ac,
165 				      &authenticator);
166     if(ret)
167 	return ret;
168     if(authenticator->cksum == NULL)
169 	return -17;
170     ret = krb5_auth_con_getkey(context, ac, &key);
171     if(ret) {
172 	krb5_free_authenticator(context, &authenticator);
173 	return ret;
174     }
175     ret = krb5_crypto_init(context, key, 0, &crypto);
176     if(ret)
177 	goto out;
178     ret = krb5_verify_checksum (context,
179 				crypto,
180 				KRB5_KU_AP_REQ_AUTH_CKSUM,
181 				data,
182 				len,
183 				authenticator->cksum);
184     krb5_crypto_destroy(context, crypto);
185 out:
186     krb5_free_authenticator(context, &authenticator);
187     krb5_free_keyblock(context, key);
188     return ret;
189 }
190 
191 krb5_error_code
192 krb5_verify_ap_req(krb5_context context,
193 		   krb5_auth_context *auth_context,
194 		   krb5_ap_req *ap_req,
195 		   krb5_const_principal server,
196 		   krb5_keyblock *keyblock,
197 		   krb5_flags flags,
198 		   krb5_flags *ap_req_options,
199 		   krb5_ticket **ticket)
200 {
201     krb5_ticket t;
202     krb5_auth_context ac;
203     krb5_error_code ret;
204 
205     if(auth_context){
206 	if(*auth_context == NULL){
207 	    krb5_auth_con_init(context, &ac);
208 	    *auth_context = ac;
209 	}else
210 	    ac = *auth_context;
211     }else
212 	krb5_auth_con_init(context, &ac);
213 
214     if (ap_req->ap_options.use_session_key && ac->keyblock){
215 	ret = krb5_decrypt_ticket(context, &ap_req->ticket,
216 				  ac->keyblock,
217 				  &t.ticket,
218 				  flags);
219 	krb5_free_keyblock(context, ac->keyblock);
220 	ac->keyblock = NULL;
221     }else
222 	ret = krb5_decrypt_ticket(context, &ap_req->ticket,
223 				  keyblock,
224 				  &t.ticket,
225 				  flags);
226 
227     if(ret)
228 	return ret;
229 
230     principalname2krb5_principal(&t.server, ap_req->ticket.sname,
231 				 ap_req->ticket.realm);
232     principalname2krb5_principal(&t.client, t.ticket.cname,
233 				 t.ticket.crealm);
234 
235     /* save key */
236 
237     krb5_copy_keyblock(context, &t.ticket.key, &ac->keyblock);
238 
239     ret = decrypt_authenticator (context,
240 				 &t.ticket.key,
241 				 &ap_req->authenticator,
242 				 ac->authenticator);
243     if (ret){
244 	/* XXX free data */
245 	return ret;
246     }
247 
248     {
249 	krb5_principal p1, p2;
250 	krb5_boolean res;
251 
252 	principalname2krb5_principal(&p1,
253 				     ac->authenticator->cname,
254 				     ac->authenticator->crealm);
255 	principalname2krb5_principal(&p2,
256 				     t.ticket.cname,
257 				     t.ticket.crealm);
258 	res = krb5_principal_compare (context, p1, p2);
259 	krb5_free_principal (context, p1);
260 	krb5_free_principal (context, p2);
261 	if (!res)
262 	    return KRB5KRB_AP_ERR_BADMATCH;
263     }
264 
265     /* check addresses */
266 
267     if (t.ticket.caddr
268 	&& ac->remote_address
269 	&& !krb5_address_search (context,
270 				 ac->remote_address,
271 				 t.ticket.caddr))
272 	return KRB5KRB_AP_ERR_BADADDR;
273 
274     if (ac->authenticator->seq_number)
275 	ac->remote_seqnumber = *ac->authenticator->seq_number;
276 
277     /* XXX - Xor sequence numbers */
278 
279     /* XXX - subkeys? */
280     /* And where should it be stored? */
281 
282     if (ac->authenticator->subkey) {
283 	krb5_copy_keyblock(context,
284 			   ac->authenticator->subkey,
285 			   &ac->remote_subkey);
286     }
287 
288     if (ap_req_options) {
289 	*ap_req_options = 0;
290 	if (ap_req->ap_options.use_session_key)
291 	    *ap_req_options |= AP_OPTS_USE_SESSION_KEY;
292 	if (ap_req->ap_options.mutual_required)
293 	    *ap_req_options |= AP_OPTS_MUTUAL_REQUIRED;
294     }
295 
296     if(ticket){
297 	*ticket = malloc(sizeof(**ticket));
298 	**ticket = t;
299     } else
300 	krb5_free_ticket (context, &t);
301     return 0;
302 }
303 
304 
305 krb5_error_code
306 krb5_rd_req_with_keyblock(krb5_context context,
307 			  krb5_auth_context *auth_context,
308 			  const krb5_data *inbuf,
309 			  krb5_const_principal server,
310 			  krb5_keyblock *keyblock,
311 			  krb5_flags *ap_req_options,
312 			  krb5_ticket **ticket)
313 {
314     krb5_error_code ret;
315     krb5_ap_req ap_req;
316 
317     if (*auth_context == NULL) {
318 	ret = krb5_auth_con_init(context, auth_context);
319 	if (ret)
320 	    return ret;
321     }
322 
323     ret = krb5_decode_ap_req(context, inbuf, &ap_req);
324     if(ret)
325 	return ret;
326 
327     ret = krb5_verify_ap_req(context,
328 			     auth_context,
329 			     &ap_req,
330 			     server,
331 			     keyblock,
332 			     0,
333 			     ap_req_options,
334 			     ticket);
335 
336     free_AP_REQ(&ap_req);
337     return ret;
338 }
339 
340 static krb5_error_code
341 get_key_from_keytab(krb5_context context,
342 		    krb5_auth_context *auth_context,
343 		    krb5_ap_req *ap_req,
344 		    krb5_const_principal server,
345 		    krb5_keytab keytab,
346 		    krb5_keyblock **out)
347 {
348     krb5_keytab_entry entry;
349     krb5_error_code ret;
350     int kvno;
351     krb5_keytab real_keytab;
352 
353     if(keytab == NULL)
354 	krb5_kt_default(context, &real_keytab);
355     else
356 	real_keytab = keytab;
357 
358     if (ap_req->ticket.enc_part.kvno)
359 	kvno = *ap_req->ticket.enc_part.kvno;
360     else
361 	kvno = 0;
362 
363     ret = krb5_kt_get_entry (context,
364 			     real_keytab,
365 			     server,
366 			     kvno,
367 			     ap_req->ticket.enc_part.etype,
368 			     &entry);
369     if(ret)
370 	goto out;
371     ret = krb5_copy_keyblock(context, &entry.keyblock, out);
372     krb5_kt_free_entry (context, &entry);
373 out:
374     if(keytab == NULL)
375 	krb5_kt_close(context, real_keytab);
376 
377     return ret;
378 }
379 
380 krb5_error_code
381 krb5_rd_req(krb5_context context,
382 	    krb5_auth_context *auth_context,
383 	    const krb5_data *inbuf,
384 	    krb5_const_principal server,
385 	    krb5_keytab keytab,
386 	    krb5_flags *ap_req_options,
387 	    krb5_ticket **ticket)
388 {
389     krb5_error_code ret;
390     krb5_ap_req ap_req;
391     krb5_keyblock *keyblock = NULL;
392     krb5_principal service = NULL;
393 
394     if (*auth_context == NULL) {
395 	ret = krb5_auth_con_init(context, auth_context);
396 	if (ret)
397 	    return ret;
398     }
399 
400     ret = krb5_decode_ap_req(context, inbuf, &ap_req);
401     if(ret)
402 	return ret;
403 
404     if(server == NULL){
405 	principalname2krb5_principal(&service,
406 				     ap_req.ticket.sname,
407 				     ap_req.ticket.realm);
408 	server = service;
409     }
410 
411     if(ap_req.ap_options.use_session_key == 0 ||
412        (*auth_context)->keyblock == NULL){
413 	ret = get_key_from_keytab(context,
414 				  auth_context,
415 				  &ap_req,
416 				  server,
417 				  keytab,
418 				  &keyblock);
419 	if(ret)
420 	    goto out;
421     }
422 
423 
424     ret = krb5_verify_ap_req(context,
425 			     auth_context,
426 			     &ap_req,
427 			     server,
428 			     keyblock,
429 			     0,
430 			     ap_req_options,
431 			     ticket);
432 
433     if(keyblock != NULL)
434 	krb5_free_keyblock(context, keyblock);
435 
436 out:
437     free_AP_REQ(&ap_req);
438     if(service)
439 	krb5_free_principal(context, service);
440     return ret;
441 }
442