xref: /freebsd/crypto/heimdal/lib/krb5/rd_req.c (revision daf1cffce2e07931f27c6c6998652e90df6ba87e)
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.41 2000/02/07 13:31:55 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 	krb5_timestamp 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 #if 0
192 static krb5_error_code
193 check_transited(krb5_context context,
194 		krb5_ticket *ticket)
195 {
196     char **realms;
197     int num_realms;
198     krb5_error_code ret;
199 
200     if(ticket->ticket.transited.tr_type != DOMAIN_X500_COMPRESS)
201 	return KRB5KDC_ERR_TRTYPE_NOSUPP;
202 
203     ret = krb5_domain_x500_decode(ticket->ticket.transited.contents,
204 				  &realms, &num_realms,
205 				  ticket->client->realm,
206 				  ticket->server->realm);
207     if(ret)
208 	return ret;
209     ret = krb5_check_transited_realms(context, realms, num_realms, NULL);
210     free(realms);
211     return ret;
212 }
213 #endif
214 
215 krb5_error_code
216 krb5_verify_ap_req(krb5_context context,
217 		   krb5_auth_context *auth_context,
218 		   krb5_ap_req *ap_req,
219 		   krb5_const_principal server,
220 		   krb5_keyblock *keyblock,
221 		   krb5_flags flags,
222 		   krb5_flags *ap_req_options,
223 		   krb5_ticket **ticket)
224 {
225     krb5_ticket t;
226     krb5_auth_context ac;
227     krb5_error_code ret;
228 
229     if(auth_context) {
230 	if(*auth_context == NULL){
231 	    krb5_auth_con_init(context, &ac);
232 	    *auth_context = ac;
233 	}else
234 	    ac = *auth_context;
235     } else
236 	krb5_auth_con_init(context, &ac);
237 
238     if (ap_req->ap_options.use_session_key && ac->keyblock){
239 	ret = krb5_decrypt_ticket(context, &ap_req->ticket,
240 				  ac->keyblock,
241 				  &t.ticket,
242 				  flags);
243 	krb5_free_keyblock(context, ac->keyblock);
244 	ac->keyblock = NULL;
245     }else
246 	ret = krb5_decrypt_ticket(context, &ap_req->ticket,
247 				  keyblock,
248 				  &t.ticket,
249 				  flags);
250 
251     if(ret)
252 	return ret;
253 
254     principalname2krb5_principal(&t.server, ap_req->ticket.sname,
255 				 ap_req->ticket.realm);
256     principalname2krb5_principal(&t.client, t.ticket.cname,
257 				 t.ticket.crealm);
258 
259     /* save key */
260 
261     krb5_copy_keyblock(context, &t.ticket.key, &ac->keyblock);
262 
263     ret = decrypt_authenticator (context,
264 				 &t.ticket.key,
265 				 &ap_req->authenticator,
266 				 ac->authenticator);
267     if (ret){
268 	/* XXX free data */
269 	return ret;
270     }
271 
272     {
273 	krb5_principal p1, p2;
274 	krb5_boolean res;
275 
276 	principalname2krb5_principal(&p1,
277 				     ac->authenticator->cname,
278 				     ac->authenticator->crealm);
279 	principalname2krb5_principal(&p2,
280 				     t.ticket.cname,
281 				     t.ticket.crealm);
282 	res = krb5_principal_compare (context, p1, p2);
283 	krb5_free_principal (context, p1);
284 	krb5_free_principal (context, p2);
285 	if (!res)
286 	    return KRB5KRB_AP_ERR_BADMATCH;
287     }
288 
289     /* check addresses */
290 
291     if (t.ticket.caddr
292 	&& ac->remote_address
293 	&& !krb5_address_search (context,
294 				 ac->remote_address,
295 				 t.ticket.caddr))
296 	return KRB5KRB_AP_ERR_BADADDR;
297 
298     if (ac->authenticator->seq_number)
299 	ac->remote_seqnumber = *ac->authenticator->seq_number;
300 
301     /* XXX - Xor sequence numbers */
302 
303     /* XXX - subkeys? */
304     /* And where should it be stored? */
305 
306     if (ac->authenticator->subkey) {
307 	krb5_copy_keyblock(context,
308 			   ac->authenticator->subkey,
309 			   &ac->remote_subkey);
310     }
311 
312     if (ap_req_options) {
313 	*ap_req_options = 0;
314 	if (ap_req->ap_options.use_session_key)
315 	    *ap_req_options |= AP_OPTS_USE_SESSION_KEY;
316 	if (ap_req->ap_options.mutual_required)
317 	    *ap_req_options |= AP_OPTS_MUTUAL_REQUIRED;
318     }
319 
320     if(ticket){
321 	*ticket = malloc(sizeof(**ticket));
322 	**ticket = t;
323     } else
324 	krb5_free_ticket (context, &t);
325     return 0;
326 }
327 
328 
329 krb5_error_code
330 krb5_rd_req_with_keyblock(krb5_context context,
331 			  krb5_auth_context *auth_context,
332 			  const krb5_data *inbuf,
333 			  krb5_const_principal server,
334 			  krb5_keyblock *keyblock,
335 			  krb5_flags *ap_req_options,
336 			  krb5_ticket **ticket)
337 {
338     krb5_error_code ret;
339     krb5_ap_req ap_req;
340 
341     if (*auth_context == NULL) {
342 	ret = krb5_auth_con_init(context, auth_context);
343 	if (ret)
344 	    return ret;
345     }
346 
347     ret = krb5_decode_ap_req(context, inbuf, &ap_req);
348     if(ret)
349 	return ret;
350 
351     ret = krb5_verify_ap_req(context,
352 			     auth_context,
353 			     &ap_req,
354 			     server,
355 			     keyblock,
356 			     0,
357 			     ap_req_options,
358 			     ticket);
359 
360     free_AP_REQ(&ap_req);
361     return ret;
362 }
363 
364 static krb5_error_code
365 get_key_from_keytab(krb5_context context,
366 		    krb5_auth_context *auth_context,
367 		    krb5_ap_req *ap_req,
368 		    krb5_const_principal server,
369 		    krb5_keytab keytab,
370 		    krb5_keyblock **out_key)
371 {
372     krb5_keytab_entry entry;
373     krb5_error_code ret;
374     int kvno;
375     krb5_keytab real_keytab;
376 
377     if(keytab == NULL)
378 	krb5_kt_default(context, &real_keytab);
379     else
380 	real_keytab = keytab;
381 
382     if (ap_req->ticket.enc_part.kvno)
383 	kvno = *ap_req->ticket.enc_part.kvno;
384     else
385 	kvno = 0;
386 
387     ret = krb5_kt_get_entry (context,
388 			     real_keytab,
389 			     server,
390 			     kvno,
391 			     ap_req->ticket.enc_part.etype,
392 			     &entry);
393     if(ret)
394 	goto out;
395     ret = krb5_copy_keyblock(context, &entry.keyblock, out_key);
396     krb5_kt_free_entry (context, &entry);
397 out:
398     if(keytab == NULL)
399 	krb5_kt_close(context, real_keytab);
400 
401     return ret;
402 }
403 
404 krb5_error_code
405 krb5_rd_req(krb5_context context,
406 	    krb5_auth_context *auth_context,
407 	    const krb5_data *inbuf,
408 	    krb5_const_principal server,
409 	    krb5_keytab keytab,
410 	    krb5_flags *ap_req_options,
411 	    krb5_ticket **ticket)
412 {
413     krb5_error_code ret;
414     krb5_ap_req ap_req;
415     krb5_keyblock *keyblock = NULL;
416     krb5_principal service = NULL;
417 
418     if (*auth_context == NULL) {
419 	ret = krb5_auth_con_init(context, auth_context);
420 	if (ret)
421 	    return ret;
422     }
423 
424     ret = krb5_decode_ap_req(context, inbuf, &ap_req);
425     if(ret)
426 	return ret;
427 
428     if(server == NULL){
429 	principalname2krb5_principal(&service,
430 				     ap_req.ticket.sname,
431 				     ap_req.ticket.realm);
432 	server = service;
433     }
434 
435     if(ap_req.ap_options.use_session_key == 0 ||
436        (*auth_context)->keyblock == NULL){
437 	ret = get_key_from_keytab(context,
438 				  auth_context,
439 				  &ap_req,
440 				  server,
441 				  keytab,
442 				  &keyblock);
443 	if(ret)
444 	    goto out;
445     }
446 
447 
448     ret = krb5_verify_ap_req(context,
449 			     auth_context,
450 			     &ap_req,
451 			     server,
452 			     keyblock,
453 			     0,
454 			     ap_req_options,
455 			     ticket);
456 
457     if(keyblock != NULL)
458 	krb5_free_keyblock(context, keyblock);
459 
460 out:
461     free_AP_REQ(&ap_req);
462     if(service)
463 	krb5_free_principal(context, service);
464     return ret;
465 }
466