xref: /freebsd/crypto/heimdal/lib/krb5/rd_req.c (revision 5521ff5a4d1929056e7ffc982fac3341ca54df7c)
1 /*
2  * Copyright (c) 1997 - 2001 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.45 2001/05/14 06:14:50 assar 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 krb5_error_code
133 krb5_decrypt_ticket(krb5_context context,
134 		    Ticket *ticket,
135 		    krb5_keyblock *key,
136 		    EncTicketPart *out,
137 		    krb5_flags flags)
138 {
139     EncTicketPart t;
140     krb5_error_code ret;
141     ret = decrypt_tkt_enc_part (context, key, &ticket->enc_part, &t);
142     if (ret)
143 	return ret;
144 
145     {
146 	krb5_timestamp now;
147 	time_t start = t.authtime;
148 
149 	krb5_timeofday (context, &now);
150 	if(t.starttime)
151 	    start = *t.starttime;
152 	if(start - now > context->max_skew
153 	   || (t.flags.invalid
154 	       && !(flags & KRB5_VERIFY_AP_REQ_IGNORE_INVALID))) {
155 	    free_EncTicketPart(&t);
156 	    krb5_clear_error_string (context);
157 	    return KRB5KRB_AP_ERR_TKT_NYV;
158 	}
159 	if(now - t.endtime > context->max_skew) {
160 	    free_EncTicketPart(&t);
161 	    krb5_clear_error_string (context);
162 	    return KRB5KRB_AP_ERR_TKT_EXPIRED;
163 	}
164     }
165 
166     if(out)
167 	*out = t;
168     else
169 	free_EncTicketPart(&t);
170     return 0;
171 }
172 
173 krb5_error_code
174 krb5_verify_authenticator_checksum(krb5_context context,
175 				   krb5_auth_context ac,
176 				   void *data,
177 				   size_t len)
178 {
179     krb5_error_code ret;
180     krb5_keyblock *key;
181     krb5_authenticator authenticator;
182     krb5_crypto crypto;
183 
184     ret = krb5_auth_getauthenticator (context,
185 				      ac,
186 				      &authenticator);
187     if(ret)
188 	return ret;
189     if(authenticator->cksum == NULL)
190 	return -17;
191     ret = krb5_auth_con_getkey(context, ac, &key);
192     if(ret) {
193 	krb5_free_authenticator(context, &authenticator);
194 	return ret;
195     }
196     ret = krb5_crypto_init(context, key, 0, &crypto);
197     if(ret)
198 	goto out;
199     ret = krb5_verify_checksum (context,
200 				crypto,
201 				KRB5_KU_AP_REQ_AUTH_CKSUM,
202 				data,
203 				len,
204 				authenticator->cksum);
205     krb5_crypto_destroy(context, crypto);
206 out:
207     krb5_free_authenticator(context, &authenticator);
208     krb5_free_keyblock(context, key);
209     return ret;
210 }
211 
212 #if 0
213 static krb5_error_code
214 check_transited(krb5_context context,
215 		krb5_ticket *ticket)
216 {
217     char **realms;
218     int num_realms;
219     krb5_error_code ret;
220 
221     if(ticket->ticket.transited.tr_type != DOMAIN_X500_COMPRESS)
222 	return KRB5KDC_ERR_TRTYPE_NOSUPP;
223 
224     ret = krb5_domain_x500_decode(ticket->ticket.transited.contents,
225 				  &realms, &num_realms,
226 				  ticket->client->realm,
227 				  ticket->server->realm);
228     if(ret)
229 	return ret;
230     ret = krb5_check_transited_realms(context, realms, num_realms, NULL);
231     free(realms);
232     return ret;
233 }
234 #endif
235 
236 krb5_error_code
237 krb5_verify_ap_req(krb5_context context,
238 		   krb5_auth_context *auth_context,
239 		   krb5_ap_req *ap_req,
240 		   krb5_const_principal server,
241 		   krb5_keyblock *keyblock,
242 		   krb5_flags flags,
243 		   krb5_flags *ap_req_options,
244 		   krb5_ticket **ticket)
245 {
246     return krb5_verify_ap_req2 (context,
247 				auth_context,
248 				ap_req,
249 				server,
250 				keyblock,
251 				flags,
252 				ap_req_options,
253 				ticket,
254 				KRB5_KU_AP_REQ_AUTH);
255 }
256 
257 krb5_error_code
258 krb5_verify_ap_req2(krb5_context context,
259 		    krb5_auth_context *auth_context,
260 		    krb5_ap_req *ap_req,
261 		    krb5_const_principal server,
262 		    krb5_keyblock *keyblock,
263 		    krb5_flags flags,
264 		    krb5_flags *ap_req_options,
265 		    krb5_ticket **ticket,
266 		    krb5_key_usage usage)
267 {
268     krb5_ticket t;
269     krb5_auth_context ac;
270     krb5_error_code ret;
271 
272     if (auth_context && *auth_context) {
273 	ac = *auth_context;
274     } else {
275 	ret = krb5_auth_con_init (context, &ac);
276 	if (ret)
277 	    return ret;
278     }
279 
280     if (ap_req->ap_options.use_session_key && ac->keyblock){
281 	ret = krb5_decrypt_ticket(context, &ap_req->ticket,
282 				  ac->keyblock,
283 				  &t.ticket,
284 				  flags);
285 	krb5_free_keyblock(context, ac->keyblock);
286 	ac->keyblock = NULL;
287     }else
288 	ret = krb5_decrypt_ticket(context, &ap_req->ticket,
289 				  keyblock,
290 				  &t.ticket,
291 				  flags);
292 
293     if(ret)
294 	goto out;
295 
296     principalname2krb5_principal(&t.server, ap_req->ticket.sname,
297 				 ap_req->ticket.realm);
298     principalname2krb5_principal(&t.client, t.ticket.cname,
299 				 t.ticket.crealm);
300 
301     /* save key */
302 
303     krb5_copy_keyblock(context, &t.ticket.key, &ac->keyblock);
304 
305     ret = decrypt_authenticator (context,
306 				 &t.ticket.key,
307 				 &ap_req->authenticator,
308 				 ac->authenticator,
309 				 usage);
310     if (ret)
311 	goto out2;
312 
313     {
314 	krb5_principal p1, p2;
315 	krb5_boolean res;
316 
317 	principalname2krb5_principal(&p1,
318 				     ac->authenticator->cname,
319 				     ac->authenticator->crealm);
320 	principalname2krb5_principal(&p2,
321 				     t.ticket.cname,
322 				     t.ticket.crealm);
323 	res = krb5_principal_compare (context, p1, p2);
324 	krb5_free_principal (context, p1);
325 	krb5_free_principal (context, p2);
326 	if (!res) {
327 	    ret = KRB5KRB_AP_ERR_BADMATCH;
328 	    krb5_clear_error_string (context);
329 	    goto out2;
330 	}
331     }
332 
333     /* check addresses */
334 
335     if (t.ticket.caddr
336 	&& ac->remote_address
337 	&& !krb5_address_search (context,
338 				 ac->remote_address,
339 				 t.ticket.caddr)) {
340 	ret = KRB5KRB_AP_ERR_BADADDR;
341 	krb5_clear_error_string (context);
342 	goto out2;
343     }
344 
345     if (ac->authenticator->seq_number)
346 	ac->remote_seqnumber = *ac->authenticator->seq_number;
347 
348     /* XXX - Xor sequence numbers */
349 
350     /* XXX - subkeys? */
351     /* And where should it be stored? */
352 
353     if (ac->authenticator->subkey) {
354 	krb5_copy_keyblock(context,
355 			   ac->authenticator->subkey,
356 			   &ac->remote_subkey);
357     }
358 
359     if (ap_req_options) {
360 	*ap_req_options = 0;
361 	if (ap_req->ap_options.use_session_key)
362 	    *ap_req_options |= AP_OPTS_USE_SESSION_KEY;
363 	if (ap_req->ap_options.mutual_required)
364 	    *ap_req_options |= AP_OPTS_MUTUAL_REQUIRED;
365     }
366 
367     if(ticket){
368 	*ticket = malloc(sizeof(**ticket));
369 	**ticket = t;
370     } else
371 	krb5_free_ticket (context, &t);
372     if (auth_context) {
373 	if (*auth_context == NULL)
374 	    *auth_context = ac;
375     } else
376 	krb5_auth_con_free (context, ac);
377     return 0;
378  out2:
379     krb5_free_ticket (context, &t);
380  out:
381     if (auth_context == NULL || *auth_context == NULL)
382 	krb5_auth_con_free (context, ac);
383     return ret;
384 }
385 
386 
387 krb5_error_code
388 krb5_rd_req_with_keyblock(krb5_context context,
389 			  krb5_auth_context *auth_context,
390 			  const krb5_data *inbuf,
391 			  krb5_const_principal server,
392 			  krb5_keyblock *keyblock,
393 			  krb5_flags *ap_req_options,
394 			  krb5_ticket **ticket)
395 {
396     krb5_error_code ret;
397     krb5_ap_req ap_req;
398 
399     if (*auth_context == NULL) {
400 	ret = krb5_auth_con_init(context, auth_context);
401 	if (ret)
402 	    return ret;
403     }
404 
405     ret = krb5_decode_ap_req(context, inbuf, &ap_req);
406     if(ret)
407 	return ret;
408 
409     ret = krb5_verify_ap_req(context,
410 			     auth_context,
411 			     &ap_req,
412 			     server,
413 			     keyblock,
414 			     0,
415 			     ap_req_options,
416 			     ticket);
417 
418     free_AP_REQ(&ap_req);
419     return ret;
420 }
421 
422 static krb5_error_code
423 get_key_from_keytab(krb5_context context,
424 		    krb5_auth_context *auth_context,
425 		    krb5_ap_req *ap_req,
426 		    krb5_const_principal server,
427 		    krb5_keytab keytab,
428 		    krb5_keyblock **out_key)
429 {
430     krb5_keytab_entry entry;
431     krb5_error_code ret;
432     int kvno;
433     krb5_keytab real_keytab;
434 
435     if(keytab == NULL)
436 	krb5_kt_default(context, &real_keytab);
437     else
438 	real_keytab = keytab;
439 
440     if (ap_req->ticket.enc_part.kvno)
441 	kvno = *ap_req->ticket.enc_part.kvno;
442     else
443 	kvno = 0;
444 
445     ret = krb5_kt_get_entry (context,
446 			     real_keytab,
447 			     server,
448 			     kvno,
449 			     ap_req->ticket.enc_part.etype,
450 			     &entry);
451     if(ret)
452 	goto out;
453     ret = krb5_copy_keyblock(context, &entry.keyblock, out_key);
454     krb5_kt_free_entry (context, &entry);
455 out:
456     if(keytab == NULL)
457 	krb5_kt_close(context, real_keytab);
458 
459     return ret;
460 }
461 
462 krb5_error_code
463 krb5_rd_req(krb5_context context,
464 	    krb5_auth_context *auth_context,
465 	    const krb5_data *inbuf,
466 	    krb5_const_principal server,
467 	    krb5_keytab keytab,
468 	    krb5_flags *ap_req_options,
469 	    krb5_ticket **ticket)
470 {
471     krb5_error_code ret;
472     krb5_ap_req ap_req;
473     krb5_keyblock *keyblock = NULL;
474     krb5_principal service = NULL;
475 
476     if (*auth_context == NULL) {
477 	ret = krb5_auth_con_init(context, auth_context);
478 	if (ret)
479 	    return ret;
480     }
481 
482     ret = krb5_decode_ap_req(context, inbuf, &ap_req);
483     if(ret)
484 	return ret;
485 
486     if(server == NULL){
487 	principalname2krb5_principal(&service,
488 				     ap_req.ticket.sname,
489 				     ap_req.ticket.realm);
490 	server = service;
491     }
492 
493     if(ap_req.ap_options.use_session_key == 0 ||
494        (*auth_context)->keyblock == NULL){
495 	ret = get_key_from_keytab(context,
496 				  auth_context,
497 				  &ap_req,
498 				  server,
499 				  keytab,
500 				  &keyblock);
501 	if(ret)
502 	    goto out;
503     }
504 
505 
506     ret = krb5_verify_ap_req(context,
507 			     auth_context,
508 			     &ap_req,
509 			     server,
510 			     keyblock,
511 			     0,
512 			     ap_req_options,
513 			     ticket);
514 
515     if(keyblock != NULL)
516 	krb5_free_keyblock(context, keyblock);
517 
518 out:
519     free_AP_REQ(&ap_req);
520     if(service)
521 	krb5_free_principal(context, service);
522     return ret;
523 }
524