xref: /freebsd/crypto/heimdal/lib/krb5/rd_req.c (revision 42c159fe388a3765f69860c84183700af37aca8a)
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.47 2001/06/18 02:48:18 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_con_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 	krb5_auth_con_setremoteseqnumber(context, ac,
347 					 *ac->authenticator->seq_number);
348 
349     /* XXX - Xor sequence numbers */
350 
351     if (ac->authenticator->subkey) {
352 	ret = krb5_auth_con_setremotesubkey(context, ac,
353 					    ac->authenticator->subkey);
354 	if (ret)
355 	    goto out2;
356     }
357 
358     if (ap_req_options) {
359 	*ap_req_options = 0;
360 	if (ap_req->ap_options.use_session_key)
361 	    *ap_req_options |= AP_OPTS_USE_SESSION_KEY;
362 	if (ap_req->ap_options.mutual_required)
363 	    *ap_req_options |= AP_OPTS_MUTUAL_REQUIRED;
364     }
365 
366     if(ticket){
367 	*ticket = malloc(sizeof(**ticket));
368 	**ticket = t;
369     } else
370 	krb5_free_ticket (context, &t);
371     if (auth_context) {
372 	if (*auth_context == NULL)
373 	    *auth_context = ac;
374     } else
375 	krb5_auth_con_free (context, ac);
376     return 0;
377  out2:
378     krb5_free_ticket (context, &t);
379  out:
380     if (auth_context == NULL || *auth_context == NULL)
381 	krb5_auth_con_free (context, ac);
382     return ret;
383 }
384 
385 
386 krb5_error_code
387 krb5_rd_req_with_keyblock(krb5_context context,
388 			  krb5_auth_context *auth_context,
389 			  const krb5_data *inbuf,
390 			  krb5_const_principal server,
391 			  krb5_keyblock *keyblock,
392 			  krb5_flags *ap_req_options,
393 			  krb5_ticket **ticket)
394 {
395     krb5_error_code ret;
396     krb5_ap_req ap_req;
397 
398     if (*auth_context == NULL) {
399 	ret = krb5_auth_con_init(context, auth_context);
400 	if (ret)
401 	    return ret;
402     }
403 
404     ret = krb5_decode_ap_req(context, inbuf, &ap_req);
405     if(ret)
406 	return ret;
407 
408     ret = krb5_verify_ap_req(context,
409 			     auth_context,
410 			     &ap_req,
411 			     server,
412 			     keyblock,
413 			     0,
414 			     ap_req_options,
415 			     ticket);
416 
417     free_AP_REQ(&ap_req);
418     return ret;
419 }
420 
421 static krb5_error_code
422 get_key_from_keytab(krb5_context context,
423 		    krb5_auth_context *auth_context,
424 		    krb5_ap_req *ap_req,
425 		    krb5_const_principal server,
426 		    krb5_keytab keytab,
427 		    krb5_keyblock **out_key)
428 {
429     krb5_keytab_entry entry;
430     krb5_error_code ret;
431     int kvno;
432     krb5_keytab real_keytab;
433 
434     if(keytab == NULL)
435 	krb5_kt_default(context, &real_keytab);
436     else
437 	real_keytab = keytab;
438 
439     if (ap_req->ticket.enc_part.kvno)
440 	kvno = *ap_req->ticket.enc_part.kvno;
441     else
442 	kvno = 0;
443 
444     ret = krb5_kt_get_entry (context,
445 			     real_keytab,
446 			     server,
447 			     kvno,
448 			     ap_req->ticket.enc_part.etype,
449 			     &entry);
450     if(ret)
451 	goto out;
452     ret = krb5_copy_keyblock(context, &entry.keyblock, out_key);
453     krb5_kt_free_entry (context, &entry);
454 out:
455     if(keytab == NULL)
456 	krb5_kt_close(context, real_keytab);
457 
458     return ret;
459 }
460 
461 krb5_error_code
462 krb5_rd_req(krb5_context context,
463 	    krb5_auth_context *auth_context,
464 	    const krb5_data *inbuf,
465 	    krb5_const_principal server,
466 	    krb5_keytab keytab,
467 	    krb5_flags *ap_req_options,
468 	    krb5_ticket **ticket)
469 {
470     krb5_error_code ret;
471     krb5_ap_req ap_req;
472     krb5_keyblock *keyblock = NULL;
473     krb5_principal service = NULL;
474 
475     if (*auth_context == NULL) {
476 	ret = krb5_auth_con_init(context, auth_context);
477 	if (ret)
478 	    return ret;
479     }
480 
481     ret = krb5_decode_ap_req(context, inbuf, &ap_req);
482     if(ret)
483 	return ret;
484 
485     if(server == NULL){
486 	principalname2krb5_principal(&service,
487 				     ap_req.ticket.sname,
488 				     ap_req.ticket.realm);
489 	server = service;
490     }
491 
492     if(ap_req.ap_options.use_session_key == 0 ||
493        (*auth_context)->keyblock == NULL){
494 	ret = get_key_from_keytab(context,
495 				  auth_context,
496 				  &ap_req,
497 				  server,
498 				  keytab,
499 				  &keyblock);
500 	if(ret)
501 	    goto out;
502     }
503 
504 
505     ret = krb5_verify_ap_req(context,
506 			     auth_context,
507 			     &ap_req,
508 			     server,
509 			     keyblock,
510 			     0,
511 			     ap_req_options,
512 			     ticket);
513 
514     if(keyblock != NULL)
515 	krb5_free_keyblock(context, keyblock);
516 
517 out:
518     free_AP_REQ(&ap_req);
519     if(service)
520 	krb5_free_principal(context, service);
521     return ret;
522 }
523