Initial import from FreeBSD RELENG_4:
[dragonfly.git] / crypto / heimdal / lib / krb5 / rd_req.c
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 }