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