rd_req.c revision 78527
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
36RCSID("$Id: rd_req.c,v 1.45 2001/05/14 06:14:50 assar Exp $");
37
38static krb5_error_code
39decrypt_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
67static krb5_error_code
68decrypt_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
104krb5_error_code
105krb5_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
132krb5_error_code
133krb5_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
173krb5_error_code
174krb5_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);
206out:
207    krb5_free_authenticator(context, &authenticator);
208    krb5_free_keyblock(context, key);
209    return ret;
210}
211
212#if 0
213static krb5_error_code
214check_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
236krb5_error_code
237krb5_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
257krb5_error_code
258krb5_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
387krb5_error_code
388krb5_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
422static krb5_error_code
423get_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);
455out:
456    if(keytab == NULL)
457	krb5_kt_close(context, real_keytab);
458
459    return ret;
460}
461
462krb5_error_code
463krb5_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
518out:
519    free_AP_REQ(&ap_req);
520    if(service)
521	krb5_free_principal(context, service);
522    return ret;
523}
524