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