xref: /freebsd/crypto/heimdal/lib/krb5/rd_req.c (revision 7bd6fde3)
1 /*
2  * Copyright (c) 1997 - 2001, 2003 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.47.8.3 2003/10/21 20:10:33 lha 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     ret = krb5_crypto_init(context, key, 0, &crypto);
50     if (ret)
51 	return ret;
52     ret = krb5_decrypt_EncryptedData (context,
53 				      crypto,
54 				      KRB5_KU_TICKET,
55 				      enc_part,
56 				      &plain);
57     krb5_crypto_destroy(context, crypto);
58     if (ret)
59 	return ret;
60 
61     ret = krb5_decode_EncTicketPart(context, plain.data, plain.length,
62 				    decr_part, &len);
63     krb5_data_free (&plain);
64     return ret;
65 }
66 
67 static krb5_error_code
68 decrypt_authenticator (krb5_context context,
69 		       EncryptionKey *key,
70 		       EncryptedData *enc_part,
71 		       Authenticator *authenticator,
72 		       krb5_key_usage usage)
73 {
74     krb5_error_code ret;
75     krb5_data plain;
76     size_t len;
77     krb5_crypto crypto;
78 
79     ret = krb5_crypto_init(context, key, 0, &crypto);
80     if (ret)
81 	return ret;
82     ret = krb5_decrypt_EncryptedData (context,
83 				      crypto,
84 				      usage /* KRB5_KU_AP_REQ_AUTH */,
85 				      enc_part,
86 				      &plain);
87     /* for backwards compatibility, also try the old usage */
88     if (ret && usage == KRB5_KU_TGS_REQ_AUTH)
89 	ret = krb5_decrypt_EncryptedData (context,
90 					  crypto,
91 					  KRB5_KU_AP_REQ_AUTH,
92 					  enc_part,
93 					  &plain);
94     krb5_crypto_destroy(context, crypto);
95     if (ret)
96 	return ret;
97 
98     ret = krb5_decode_Authenticator(context, plain.data, plain.length,
99 				    authenticator, &len);
100     krb5_data_free (&plain);
101     return ret;
102 }
103 
104 krb5_error_code
105 krb5_decode_ap_req(krb5_context context,
106 		   const krb5_data *inbuf,
107 		   krb5_ap_req *ap_req)
108 {
109     krb5_error_code ret;
110     size_t len;
111     ret = decode_AP_REQ(inbuf->data, inbuf->length, ap_req, &len);
112     if (ret)
113 	return ret;
114     if (ap_req->pvno != 5){
115 	free_AP_REQ(ap_req);
116 	krb5_clear_error_string (context);
117 	return KRB5KRB_AP_ERR_BADVERSION;
118     }
119     if (ap_req->msg_type != krb_ap_req){
120 	free_AP_REQ(ap_req);
121 	krb5_clear_error_string (context);
122 	return KRB5KRB_AP_ERR_MSG_TYPE;
123     }
124     if (ap_req->ticket.tkt_vno != 5){
125 	free_AP_REQ(ap_req);
126 	krb5_clear_error_string (context);
127 	return KRB5KRB_AP_ERR_BADVERSION;
128     }
129     return 0;
130 }
131 
132 static krb5_error_code
133 check_transited(krb5_context context, Ticket *ticket, EncTicketPart *enc)
134 {
135     char **realms;
136     int num_realms;
137     krb5_error_code ret;
138 
139     if(enc->transited.tr_type != DOMAIN_X500_COMPRESS)
140 	return KRB5KDC_ERR_TRTYPE_NOSUPP;
141 
142     if(enc->transited.contents.length == 0)
143 	return 0;
144 
145     ret = krb5_domain_x500_decode(context, enc->transited.contents,
146 				  &realms, &num_realms,
147 				  enc->crealm,
148 				  ticket->realm);
149     if(ret)
150 	return ret;
151     ret = krb5_check_transited(context, enc->crealm,
152 			       ticket->realm,
153 			       realms, num_realms, NULL);
154     free(realms);
155     return ret;
156 }
157 
158 krb5_error_code
159 krb5_decrypt_ticket(krb5_context context,
160 		    Ticket *ticket,
161 		    krb5_keyblock *key,
162 		    EncTicketPart *out,
163 		    krb5_flags flags)
164 {
165     EncTicketPart t;
166     krb5_error_code ret;
167     ret = decrypt_tkt_enc_part (context, key, &ticket->enc_part, &t);
168     if (ret)
169 	return ret;
170 
171     {
172 	krb5_timestamp now;
173 	time_t start = t.authtime;
174 
175 	krb5_timeofday (context, &now);
176 	if(t.starttime)
177 	    start = *t.starttime;
178 	if(start - now > context->max_skew
179 	   || (t.flags.invalid
180 	       && !(flags & KRB5_VERIFY_AP_REQ_IGNORE_INVALID))) {
181 	    free_EncTicketPart(&t);
182 	    krb5_clear_error_string (context);
183 	    return KRB5KRB_AP_ERR_TKT_NYV;
184 	}
185 	if(now - t.endtime > context->max_skew) {
186 	    free_EncTicketPart(&t);
187 	    krb5_clear_error_string (context);
188 	    return KRB5KRB_AP_ERR_TKT_EXPIRED;
189 	}
190 
191 	if(!t.flags.transited_policy_checked) {
192 	    ret = check_transited(context, ticket, &t);
193 	    if(ret) {
194 		free_EncTicketPart(&t);
195 		return ret;
196 	    }
197 	}
198     }
199 
200     if(out)
201 	*out = t;
202     else
203 	free_EncTicketPart(&t);
204     return 0;
205 }
206 
207 krb5_error_code
208 krb5_verify_authenticator_checksum(krb5_context context,
209 				   krb5_auth_context ac,
210 				   void *data,
211 				   size_t len)
212 {
213     krb5_error_code ret;
214     krb5_keyblock *key;
215     krb5_authenticator authenticator;
216     krb5_crypto crypto;
217 
218     ret = krb5_auth_con_getauthenticator (context,
219 				      ac,
220 				      &authenticator);
221     if(ret)
222 	return ret;
223     if(authenticator->cksum == NULL)
224 	return -17;
225     ret = krb5_auth_con_getkey(context, ac, &key);
226     if(ret) {
227 	krb5_free_authenticator(context, &authenticator);
228 	return ret;
229     }
230     ret = krb5_crypto_init(context, key, 0, &crypto);
231     if(ret)
232 	goto out;
233     ret = krb5_verify_checksum (context,
234 				crypto,
235 				KRB5_KU_AP_REQ_AUTH_CKSUM,
236 				data,
237 				len,
238 				authenticator->cksum);
239     krb5_crypto_destroy(context, crypto);
240 out:
241     krb5_free_authenticator(context, &authenticator);
242     krb5_free_keyblock(context, key);
243     return ret;
244 }
245 
246 
247 krb5_error_code
248 krb5_verify_ap_req(krb5_context context,
249 		   krb5_auth_context *auth_context,
250 		   krb5_ap_req *ap_req,
251 		   krb5_const_principal server,
252 		   krb5_keyblock *keyblock,
253 		   krb5_flags flags,
254 		   krb5_flags *ap_req_options,
255 		   krb5_ticket **ticket)
256 {
257     return krb5_verify_ap_req2 (context,
258 				auth_context,
259 				ap_req,
260 				server,
261 				keyblock,
262 				flags,
263 				ap_req_options,
264 				ticket,
265 				KRB5_KU_AP_REQ_AUTH);
266 }
267 
268 krb5_error_code
269 krb5_verify_ap_req2(krb5_context context,
270 		    krb5_auth_context *auth_context,
271 		    krb5_ap_req *ap_req,
272 		    krb5_const_principal server,
273 		    krb5_keyblock *keyblock,
274 		    krb5_flags flags,
275 		    krb5_flags *ap_req_options,
276 		    krb5_ticket **ticket,
277 		    krb5_key_usage usage)
278 {
279     krb5_ticket t;
280     krb5_auth_context ac;
281     krb5_error_code ret;
282 
283     if (auth_context && *auth_context) {
284 	ac = *auth_context;
285     } else {
286 	ret = krb5_auth_con_init (context, &ac);
287 	if (ret)
288 	    return ret;
289     }
290 
291     if (ap_req->ap_options.use_session_key && ac->keyblock){
292 	ret = krb5_decrypt_ticket(context, &ap_req->ticket,
293 				  ac->keyblock,
294 				  &t.ticket,
295 				  flags);
296 	krb5_free_keyblock(context, ac->keyblock);
297 	ac->keyblock = NULL;
298     }else
299 	ret = krb5_decrypt_ticket(context, &ap_req->ticket,
300 				  keyblock,
301 				  &t.ticket,
302 				  flags);
303 
304     if(ret)
305 	goto out;
306 
307     principalname2krb5_principal(&t.server, ap_req->ticket.sname,
308 				 ap_req->ticket.realm);
309     principalname2krb5_principal(&t.client, t.ticket.cname,
310 				 t.ticket.crealm);
311 
312     /* save key */
313 
314     krb5_copy_keyblock(context, &t.ticket.key, &ac->keyblock);
315 
316     ret = decrypt_authenticator (context,
317 				 &t.ticket.key,
318 				 &ap_req->authenticator,
319 				 ac->authenticator,
320 				 usage);
321     if (ret)
322 	goto out2;
323 
324     {
325 	krb5_principal p1, p2;
326 	krb5_boolean res;
327 
328 	principalname2krb5_principal(&p1,
329 				     ac->authenticator->cname,
330 				     ac->authenticator->crealm);
331 	principalname2krb5_principal(&p2,
332 				     t.ticket.cname,
333 				     t.ticket.crealm);
334 	res = krb5_principal_compare (context, p1, p2);
335 	krb5_free_principal (context, p1);
336 	krb5_free_principal (context, p2);
337 	if (!res) {
338 	    ret = KRB5KRB_AP_ERR_BADMATCH;
339 	    krb5_clear_error_string (context);
340 	    goto out2;
341 	}
342     }
343 
344     /* check addresses */
345 
346     if (t.ticket.caddr
347 	&& ac->remote_address
348 	&& !krb5_address_search (context,
349 				 ac->remote_address,
350 				 t.ticket.caddr)) {
351 	ret = KRB5KRB_AP_ERR_BADADDR;
352 	krb5_clear_error_string (context);
353 	goto out2;
354     }
355 
356     if (ac->authenticator->seq_number)
357 	krb5_auth_con_setremoteseqnumber(context, ac,
358 					 *ac->authenticator->seq_number);
359 
360     /* XXX - Xor sequence numbers */
361 
362     if (ac->authenticator->subkey) {
363 	ret = krb5_auth_con_setremotesubkey(context, ac,
364 					    ac->authenticator->subkey);
365 	if (ret)
366 	    goto out2;
367     }
368 
369     if (ap_req_options) {
370 	*ap_req_options = 0;
371 	if (ap_req->ap_options.use_session_key)
372 	    *ap_req_options |= AP_OPTS_USE_SESSION_KEY;
373 	if (ap_req->ap_options.mutual_required)
374 	    *ap_req_options |= AP_OPTS_MUTUAL_REQUIRED;
375     }
376 
377     if(ticket){
378 	*ticket = malloc(sizeof(**ticket));
379 	**ticket = t;
380     } else
381 	krb5_free_ticket (context, &t);
382     if (auth_context) {
383 	if (*auth_context == NULL)
384 	    *auth_context = ac;
385     } else
386 	krb5_auth_con_free (context, ac);
387     return 0;
388  out2:
389     krb5_free_ticket (context, &t);
390  out:
391     if (auth_context == NULL || *auth_context == NULL)
392 	krb5_auth_con_free (context, ac);
393     return ret;
394 }
395 
396 
397 krb5_error_code
398 krb5_rd_req_with_keyblock(krb5_context context,
399 			  krb5_auth_context *auth_context,
400 			  const krb5_data *inbuf,
401 			  krb5_const_principal server,
402 			  krb5_keyblock *keyblock,
403 			  krb5_flags *ap_req_options,
404 			  krb5_ticket **ticket)
405 {
406     krb5_error_code ret;
407     krb5_ap_req ap_req;
408 
409     if (*auth_context == NULL) {
410 	ret = krb5_auth_con_init(context, auth_context);
411 	if (ret)
412 	    return ret;
413     }
414 
415     ret = krb5_decode_ap_req(context, inbuf, &ap_req);
416     if(ret)
417 	return ret;
418 
419     ret = krb5_verify_ap_req(context,
420 			     auth_context,
421 			     &ap_req,
422 			     server,
423 			     keyblock,
424 			     0,
425 			     ap_req_options,
426 			     ticket);
427 
428     free_AP_REQ(&ap_req);
429     return ret;
430 }
431 
432 static krb5_error_code
433 get_key_from_keytab(krb5_context context,
434 		    krb5_auth_context *auth_context,
435 		    krb5_ap_req *ap_req,
436 		    krb5_const_principal server,
437 		    krb5_keytab keytab,
438 		    krb5_keyblock **out_key)
439 {
440     krb5_keytab_entry entry;
441     krb5_error_code ret;
442     int kvno;
443     krb5_keytab real_keytab;
444 
445     if(keytab == NULL)
446 	krb5_kt_default(context, &real_keytab);
447     else
448 	real_keytab = keytab;
449 
450     if (ap_req->ticket.enc_part.kvno)
451 	kvno = *ap_req->ticket.enc_part.kvno;
452     else
453 	kvno = 0;
454 
455     ret = krb5_kt_get_entry (context,
456 			     real_keytab,
457 			     server,
458 			     kvno,
459 			     ap_req->ticket.enc_part.etype,
460 			     &entry);
461     if(ret)
462 	goto out;
463     ret = krb5_copy_keyblock(context, &entry.keyblock, out_key);
464     krb5_kt_free_entry (context, &entry);
465 out:
466     if(keytab == NULL)
467 	krb5_kt_close(context, real_keytab);
468 
469     return ret;
470 }
471 
472 krb5_error_code
473 krb5_rd_req(krb5_context context,
474 	    krb5_auth_context *auth_context,
475 	    const krb5_data *inbuf,
476 	    krb5_const_principal server,
477 	    krb5_keytab keytab,
478 	    krb5_flags *ap_req_options,
479 	    krb5_ticket **ticket)
480 {
481     krb5_error_code ret;
482     krb5_ap_req ap_req;
483     krb5_keyblock *keyblock = NULL;
484     krb5_principal service = NULL;
485 
486     if (*auth_context == NULL) {
487 	ret = krb5_auth_con_init(context, auth_context);
488 	if (ret)
489 	    return ret;
490     }
491 
492     ret = krb5_decode_ap_req(context, inbuf, &ap_req);
493     if(ret)
494 	return ret;
495 
496     if(server == NULL){
497 	principalname2krb5_principal(&service,
498 				     ap_req.ticket.sname,
499 				     ap_req.ticket.realm);
500 	server = service;
501     }
502     if (ap_req.ap_options.use_session_key &&
503 	(*auth_context)->keyblock == NULL) {
504 	krb5_set_error_string(context, "krb5_rd_req: user to user auth "
505 			      "without session key given");
506 	ret = KRB5KRB_AP_ERR_NOKEY;
507 	goto out;
508     }
509 
510     if((*auth_context)->keyblock == NULL){
511 	ret = get_key_from_keytab(context,
512 				  auth_context,
513 				  &ap_req,
514 				  server,
515 				  keytab,
516 				  &keyblock);
517 	if(ret)
518 	    goto out;
519     } else {
520 	ret = krb5_copy_keyblock(context,
521 				 (*auth_context)->keyblock,
522 				 &keyblock);
523 	if (ret)
524 	    goto out;
525     }
526 
527     ret = krb5_verify_ap_req(context,
528 			     auth_context,
529 			     &ap_req,
530 			     server,
531 			     keyblock,
532 			     0,
533 			     ap_req_options,
534 			     ticket);
535 
536     if(keyblock != NULL)
537 	krb5_free_keyblock(context, keyblock);
538 
539 out:
540     free_AP_REQ(&ap_req);
541     if(service)
542 	krb5_free_principal(context, service);
543     return ret;
544 }
545