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