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