r10149: Update Samba4 to current lorikeet-heimdal.
[sfrench/samba-autobuild/.git] / source4 / heimdal / lib / krb5 / rd_req.c
1 /*
2  * Copyright (c) 1997 - 2001, 2003 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.58 2005/08/27 05:48:57 lha 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 KRB5_LIB_FUNCTION
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 static krb5_error_code
133 check_transited(krb5_context context, Ticket *ticket, EncTicketPart *enc)
134 {
135     char **realms;
136     int num_realms;
137     krb5_error_code ret;
138             
139     /* Windows w2k and w2k3 uses this */
140     if(enc->transited.tr_type == 0 && enc->transited.contents.length == 0)
141         return 0;
142
143     if(enc->transited.tr_type != DOMAIN_X500_COMPRESS)
144         return KRB5KDC_ERR_TRTYPE_NOSUPP;
145
146     if(enc->transited.contents.length == 0)
147         return 0;
148
149     ret = krb5_domain_x500_decode(context, enc->transited.contents, 
150                                   &realms, &num_realms, 
151                                   enc->crealm,
152                                   ticket->realm);
153     if(ret)
154         return ret;
155     ret = krb5_check_transited(context, enc->crealm, 
156                                ticket->realm, 
157                                realms, num_realms, NULL);
158     free(realms);
159     return ret;
160 }
161
162 static krb5_error_code
163 find_etypelist(krb5_context context,
164                krb5_auth_context auth_context,
165                EtypeList *etypes)
166 {
167     krb5_error_code ret;
168     krb5_authdata *ad;
169     krb5_authdata adIfRelevant;
170     unsigned i;
171
172     adIfRelevant.len = 0;
173
174     etypes->len = 0;
175     etypes->val = NULL;
176
177     ad = auth_context->authenticator->authorization_data;
178     if (ad == NULL)
179         return 0;
180
181     for (i = 0; i < ad->len; i++) {
182         if (ad->val[i].ad_type == KRB5_AUTHDATA_IF_RELEVANT) {
183             ret = decode_AD_IF_RELEVANT(ad->val[i].ad_data.data,
184                                         ad->val[i].ad_data.length,
185                                         &adIfRelevant,
186                                         NULL);
187             if (ret)
188                 return ret;
189
190             if (adIfRelevant.len == 1 &&
191                 adIfRelevant.val[0].ad_type ==
192                         KRB5_AUTHDATA_GSS_API_ETYPE_NEGOTIATION) {
193                 break;
194             }
195             free_AD_IF_RELEVANT(&adIfRelevant);
196             adIfRelevant.len = 0;
197         }
198     }
199
200     if (adIfRelevant.len == 0)
201         return 0;
202
203     ret = decode_EtypeList(adIfRelevant.val[0].ad_data.data,
204                            adIfRelevant.val[0].ad_data.length,
205                            etypes,
206                            NULL);
207
208     free_AD_IF_RELEVANT(&adIfRelevant);
209
210     return ret;
211 }
212
213 krb5_error_code KRB5_LIB_FUNCTION
214 krb5_decrypt_ticket(krb5_context context,
215                     Ticket *ticket,
216                     krb5_keyblock *key,
217                     EncTicketPart *out,
218                     krb5_flags flags)
219 {
220     EncTicketPart t;
221     krb5_error_code ret;
222     ret = decrypt_tkt_enc_part (context, key, &ticket->enc_part, &t);
223     if (ret)
224         return ret;
225     
226     {
227         krb5_timestamp now;
228         time_t start = t.authtime;
229
230         krb5_timeofday (context, &now);
231         if(t.starttime)
232             start = *t.starttime;
233         if(start - now > context->max_skew
234            || (t.flags.invalid
235                && !(flags & KRB5_VERIFY_AP_REQ_IGNORE_INVALID))) {
236             free_EncTicketPart(&t);
237             krb5_clear_error_string (context);
238             return KRB5KRB_AP_ERR_TKT_NYV;
239         }
240         if(now - t.endtime > context->max_skew) {
241             free_EncTicketPart(&t);
242             krb5_clear_error_string (context);
243             return KRB5KRB_AP_ERR_TKT_EXPIRED;
244         }
245         
246         if(!t.flags.transited_policy_checked) {
247             ret = check_transited(context, ticket, &t);
248             if(ret) {
249                 free_EncTicketPart(&t);
250                 return ret;
251             }
252         }
253     }
254     
255     if(out)
256         *out = t;
257     else
258         free_EncTicketPart(&t);
259     return 0;
260 }
261
262 krb5_error_code KRB5_LIB_FUNCTION
263 krb5_verify_authenticator_checksum(krb5_context context,
264                                    krb5_auth_context ac,
265                                    void *data,
266                                    size_t len)
267 {
268     krb5_error_code ret;
269     krb5_keyblock *key;
270     krb5_authenticator authenticator;
271     krb5_crypto crypto;
272     
273     ret = krb5_auth_con_getauthenticator (context,
274                                       ac,
275                                       &authenticator);
276     if(ret)
277         return ret;
278     if(authenticator->cksum == NULL)
279         return -17;
280     ret = krb5_auth_con_getkey(context, ac, &key);
281     if(ret) {
282         krb5_free_authenticator(context, &authenticator);
283         return ret;
284     }
285     ret = krb5_crypto_init(context, key, 0, &crypto);
286     if(ret)
287         goto out;
288     ret = krb5_verify_checksum (context,
289                                 crypto,
290                                 KRB5_KU_AP_REQ_AUTH_CKSUM,
291                                 data,
292                                 len,
293                                 authenticator->cksum);
294     krb5_crypto_destroy(context, crypto);
295 out:
296     krb5_free_authenticator(context, &authenticator);
297     krb5_free_keyblock(context, key);
298     return ret;
299 }
300
301
302 krb5_error_code KRB5_LIB_FUNCTION
303 krb5_verify_ap_req(krb5_context context,
304                    krb5_auth_context *auth_context,
305                    krb5_ap_req *ap_req,
306                    krb5_const_principal server,
307                    krb5_keyblock *keyblock,
308                    krb5_flags flags,
309                    krb5_flags *ap_req_options,
310                    krb5_ticket **ticket)
311 {
312     return krb5_verify_ap_req2 (context,
313                                 auth_context,
314                                 ap_req,
315                                 server,
316                                 keyblock,
317                                 flags,
318                                 ap_req_options,
319                                 ticket,
320                                 KRB5_KU_AP_REQ_AUTH);
321 }
322
323 krb5_error_code KRB5_LIB_FUNCTION
324 krb5_verify_ap_req2(krb5_context context,
325                     krb5_auth_context *auth_context,
326                     krb5_ap_req *ap_req,
327                     krb5_const_principal server,
328                     krb5_keyblock *keyblock,
329                     krb5_flags flags,
330                     krb5_flags *ap_req_options,
331                     krb5_ticket **ticket,
332                     krb5_key_usage usage)
333 {
334     krb5_ticket *t;
335     krb5_auth_context ac;
336     krb5_error_code ret;
337     EtypeList etypes;
338     
339     if (auth_context && *auth_context) {
340         ac = *auth_context;
341     } else {
342         ret = krb5_auth_con_init (context, &ac);
343         if (ret)
344             return ret;
345     }
346
347     t = malloc(sizeof(*t));
348     if (t == NULL) {
349         ret = ENOMEM;
350         krb5_clear_error_string (context);
351         goto out;
352     }
353     memset(t, 0, sizeof(*t));
354
355     if (ap_req->ap_options.use_session_key && ac->keyblock){
356         ret = krb5_decrypt_ticket(context, &ap_req->ticket, 
357                                   ac->keyblock, 
358                                   &t->ticket,
359                                   flags);
360         krb5_free_keyblock(context, ac->keyblock);
361         ac->keyblock = NULL;
362     }else
363         ret = krb5_decrypt_ticket(context, &ap_req->ticket, 
364                                   keyblock, 
365                                   &t->ticket,
366                                   flags);
367     
368     if(ret)
369         goto out;
370
371     _krb5_principalname2krb5_principal(&t->server, ap_req->ticket.sname, 
372                                        ap_req->ticket.realm);
373     _krb5_principalname2krb5_principal(&t->client, t->ticket.cname, 
374                                        t->ticket.crealm);
375
376     /* save key */
377
378     krb5_copy_keyblock(context, &t->ticket.key, &ac->keyblock);
379
380     ret = decrypt_authenticator (context,
381                                  &t->ticket.key,
382                                  &ap_req->authenticator,
383                                  ac->authenticator,
384                                  usage);
385     if (ret)
386         goto out;
387
388     {
389         krb5_principal p1, p2;
390         krb5_boolean res;
391         
392         _krb5_principalname2krb5_principal(&p1,
393                                            ac->authenticator->cname,
394                                            ac->authenticator->crealm);
395         _krb5_principalname2krb5_principal(&p2, 
396                                            t->ticket.cname,
397                                            t->ticket.crealm);
398         res = krb5_principal_compare (context, p1, p2);
399         krb5_free_principal (context, p1);
400         krb5_free_principal (context, p2);
401         if (!res) {
402             ret = KRB5KRB_AP_ERR_BADMATCH;
403             krb5_clear_error_string (context);
404             goto out;
405         }
406     }
407
408     /* check addresses */
409
410     if (t->ticket.caddr
411         && ac->remote_address
412         && !krb5_address_search (context,
413                                  ac->remote_address,
414                                  t->ticket.caddr)) {
415         ret = KRB5KRB_AP_ERR_BADADDR;
416         krb5_clear_error_string (context);
417         goto out;
418     }
419
420     if (ac->authenticator->seq_number)
421         krb5_auth_con_setremoteseqnumber(context, ac,
422                                          *ac->authenticator->seq_number);
423
424     /* XXX - Xor sequence numbers */
425
426     if (ac->authenticator->subkey) {
427         ret = krb5_auth_con_setremotesubkey(context, ac,
428                                             ac->authenticator->subkey);
429         if (ret)
430             goto out;
431     }
432
433     ret = find_etypelist(context, ac, &etypes);
434     if (ret)
435         goto out;
436
437     ac->keytype = ETYPE_NULL;
438
439     if (etypes.val) {
440         int i;
441
442         for (i = 0; i < etypes.len; i++) {
443             if (krb5_enctype_valid(context, etypes.val[i]) == 0) {
444                 ac->keytype = etypes.val[i];
445                 break;
446             }
447         }
448     }
449
450     if (ap_req_options) {
451         *ap_req_options = 0;
452         if (ac->keytype != ETYPE_NULL)
453             *ap_req_options |= AP_OPTS_USE_SUBKEY;
454         if (ap_req->ap_options.use_session_key)
455             *ap_req_options |= AP_OPTS_USE_SESSION_KEY;
456         if (ap_req->ap_options.mutual_required)
457             *ap_req_options |= AP_OPTS_MUTUAL_REQUIRED;
458     }
459
460     if(ticket)
461         *ticket = t;
462     else
463         krb5_free_ticket (context, t);
464     if (auth_context) {
465         if (*auth_context == NULL)
466             *auth_context = ac;
467     } else
468         krb5_auth_con_free (context, ac);
469     free_EtypeList(&etypes);
470     return 0;
471  out:
472     if (t)
473         krb5_free_ticket (context, t);
474     if (auth_context == NULL || *auth_context == NULL)
475         krb5_auth_con_free (context, ac);
476     return ret;
477 }
478                    
479
480 krb5_error_code KRB5_LIB_FUNCTION
481 krb5_rd_req_with_keyblock(krb5_context context,
482                           krb5_auth_context *auth_context,
483                           const krb5_data *inbuf,
484                           krb5_const_principal server,
485                           krb5_keyblock *keyblock,
486                           krb5_flags *ap_req_options,
487                           krb5_ticket **ticket)
488 {
489     krb5_error_code ret;
490     krb5_ap_req ap_req;
491
492     if (*auth_context == NULL) {
493         ret = krb5_auth_con_init(context, auth_context);
494         if (ret)
495             return ret;
496     }
497
498     ret = krb5_decode_ap_req(context, inbuf, &ap_req);
499     if(ret)
500         return ret;
501
502     ret = krb5_verify_ap_req(context,
503                              auth_context,
504                              &ap_req,
505                              server,
506                              keyblock,
507                              0,
508                              ap_req_options,
509                              ticket);
510
511     free_AP_REQ(&ap_req);
512     return ret;
513 }
514
515 static krb5_error_code
516 get_key_from_keytab(krb5_context context,
517                     krb5_auth_context *auth_context,
518                     krb5_ap_req *ap_req,
519                     krb5_const_principal server,
520                     krb5_keytab keytab,
521                     krb5_keyblock **out_key)
522 {
523     krb5_keytab_entry entry;
524     krb5_error_code ret;
525     int kvno;
526     krb5_keytab real_keytab;
527
528     if(keytab == NULL)
529         krb5_kt_default(context, &real_keytab);
530     else
531         real_keytab = keytab;
532     
533     if (ap_req->ticket.enc_part.kvno)
534         kvno = *ap_req->ticket.enc_part.kvno;
535     else
536         kvno = 0;
537
538     ret = krb5_kt_get_entry (context,
539                              real_keytab,
540                              server,
541                              kvno,
542                              ap_req->ticket.enc_part.etype,
543                              &entry);
544     if(ret)
545         goto out;
546     ret = krb5_copy_keyblock(context, &entry.keyblock, out_key);
547     krb5_kt_free_entry (context, &entry);
548 out:    
549     if(keytab == NULL)
550         krb5_kt_close(context, real_keytab);
551     
552     return ret;
553 }
554
555 krb5_error_code KRB5_LIB_FUNCTION
556 krb5_rd_req_return_keyblock(krb5_context context,
557                             krb5_auth_context *auth_context,
558                             const krb5_data *inbuf,
559                             krb5_const_principal server,
560                             krb5_keytab keytab,
561                             krb5_flags *ap_req_options,
562                             krb5_ticket **ticket, 
563                             krb5_keyblock **return_keyblock)
564 {
565     krb5_error_code ret;
566     krb5_ap_req ap_req;
567     krb5_keyblock *keyblock = NULL;
568     krb5_principal service = NULL;
569
570     if (return_keyblock)
571         *return_keyblock = NULL;
572
573     if (*auth_context == NULL) {
574         ret = krb5_auth_con_init(context, auth_context);
575         if (ret)
576             return ret;
577     }
578
579     ret = krb5_decode_ap_req(context, inbuf, &ap_req);
580     if(ret)
581         return ret;
582
583     if(server == NULL){
584         _krb5_principalname2krb5_principal(&service,
585                                            ap_req.ticket.sname,
586                                            ap_req.ticket.realm);
587         server = service;
588     }
589     if (ap_req.ap_options.use_session_key &&
590         (*auth_context)->keyblock == NULL) {
591         krb5_set_error_string(context, "krb5_rd_req: user to user auth "
592                               "without session key given");
593         ret = KRB5KRB_AP_ERR_NOKEY;
594         goto out;
595     }
596
597     if((*auth_context)->keyblock == NULL){
598         ret = get_key_from_keytab(context, 
599                                   auth_context, 
600                                   &ap_req,
601                                   server,
602                                   keytab,
603                                   &keyblock);
604         if(ret)
605             goto out;
606     } else {
607         ret = krb5_copy_keyblock(context,
608                                  (*auth_context)->keyblock,
609                                  &keyblock);
610         if (ret)
611             goto out;
612     }
613
614     ret = krb5_verify_ap_req(context,
615                              auth_context,
616                              &ap_req,
617                              server,
618                              keyblock,
619                              0,
620                              ap_req_options,
621                              ticket);
622
623     if (ret == 0 && return_keyblock)
624         *return_keyblock = keyblock;
625     else
626         krb5_free_keyblock(context, keyblock);
627
628 out:
629     free_AP_REQ(&ap_req);
630     if(service)
631         krb5_free_principal(context, service);
632     return ret;
633 }
634
635 krb5_error_code KRB5_LIB_FUNCTION
636 krb5_rd_req(krb5_context context,
637             krb5_auth_context *auth_context,
638             const krb5_data *inbuf,
639             krb5_const_principal server,
640             krb5_keytab keytab,
641             krb5_flags *ap_req_options,
642             krb5_ticket **ticket)
643 {
644     return krb5_rd_req_return_keyblock(context,
645                                        auth_context,
646                                        inbuf,
647                                        server,
648                                        keytab,
649                                        ap_req_options,
650                                        ticket,
651                                        NULL);
652
653 }
654