Add keytab kinit test cases
[krb5.git] / src / kdc / kdc_authdata.c
1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* kdc/kdc_authdata.c - Authorization data routines for the KDC */
3 /*
4  * Copyright (C) 2007 Apple Inc.  All Rights Reserved.
5  * Copyright (C) 2008, 2009 by the Massachusetts Institute of Technology.
6  *
7  * Export of this software from the United States of America may
8  *   require a specific license from the United States Government.
9  *   It is the responsibility of any person or organization contemplating
10  *   export to obtain such a license before exporting.
11  *
12  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
13  * distribute this software and its documentation for any purpose and
14  * without fee is hereby granted, provided that the above copyright
15  * notice appear in all copies and that both that copyright notice and
16  * this permission notice appear in supporting documentation, and that
17  * the name of M.I.T. not be used in advertising or publicity pertaining
18  * to distribution of the software without specific, written prior
19  * permission.  Furthermore if you modify this software you must label
20  * your software as modified software and not distribute it in such a
21  * fashion that it might be confused with the original M.I.T. software.
22  * M.I.T. makes no representations about the suitability of
23  * this software for any purpose.  It is provided "as is" without express
24  * or implied warranty.
25  */
26
27 #include "k5-int.h"
28 #include "kdc_util.h"
29 #include "extern.h"
30 #include <stdio.h>
31 #include "adm_proto.h"
32
33 #include <syslog.h>
34
35 #include <assert.h>
36 #include <krb5/authdata_plugin.h>
37
38 #if TARGET_OS_MAC
39 static const char *objdirs[] = { KRB5_AUTHDATA_PLUGIN_BUNDLE_DIR,
40                                  LIBDIR "/krb5/plugins/authdata",
41                                  NULL }; /* should be a list */
42 #else
43 static const char *objdirs[] = { LIBDIR "/krb5/plugins/authdata", NULL };
44 #endif
45
46 /* MIT Kerberos 1.6 (V0) authdata plugin callback */
47 typedef krb5_error_code (*authdata_proc_0)(
48     krb5_context,
49     krb5_db_entry *client,
50     krb5_data *req_pkt,
51     krb5_kdc_req *request,
52     krb5_enc_tkt_part * enc_tkt_reply);
53 /* MIT Kerberos 1.8 (V2) authdata plugin callback */
54 typedef krb5_error_code (*authdata_proc_2)(
55     krb5_context, unsigned int flags,
56     krb5_db_entry *client, krb5_db_entry *server,
57     krb5_db_entry *krbtgt,
58     krb5_keyblock *client_key,
59     krb5_keyblock *server_key,
60     krb5_keyblock *krbtgt_key,
61     krb5_data *req_pkt,
62     krb5_kdc_req *request,
63     krb5_const_principal for_user_princ,
64     krb5_enc_tkt_part *enc_tkt_request,
65     krb5_enc_tkt_part *enc_tkt_reply);
66 typedef krb5_error_code (*init_proc)(krb5_context, void **);
67 typedef void (*fini_proc)(krb5_context, void *);
68
69 static krb5_error_code handle_request_authdata(
70     krb5_context context,
71     unsigned int flags,
72     krb5_db_entry *client,
73     krb5_db_entry *server,
74     krb5_db_entry *krbtgt,
75     krb5_keyblock *client_key,
76     krb5_keyblock *server_key,
77     krb5_keyblock *krbtgt_key,
78     krb5_data *req_pkt,
79     krb5_kdc_req *request,
80     krb5_const_principal for_user_princ,
81     krb5_enc_tkt_part *enc_tkt_request,
82     krb5_enc_tkt_part *enc_tkt_reply);
83
84 static krb5_error_code handle_tgt_authdata(
85     krb5_context context,
86     unsigned int flags,
87     krb5_db_entry *client,
88     krb5_db_entry *server,
89     krb5_db_entry *krbtgt,
90     krb5_keyblock *client_key,
91     krb5_keyblock *server_key,
92     krb5_keyblock *krbtgt_key,
93     krb5_data *req_pkt,
94     krb5_kdc_req *request,
95     krb5_const_principal for_user_princ,
96     krb5_enc_tkt_part *enc_tkt_request,
97     krb5_enc_tkt_part *enc_tkt_reply);
98
99 static krb5_error_code
100 handle_kdb_authdata(krb5_context context, unsigned int flags,
101                     krb5_db_entry *client, krb5_db_entry *server,
102                     krb5_db_entry *krbtgt, krb5_keyblock *client_key,
103                     krb5_keyblock *server_key, krb5_keyblock *krbtgt_key,
104                     krb5_data *req_pkt, krb5_kdc_req *request,
105                     krb5_const_principal for_user_princ,
106                     krb5_enc_tkt_part *enc_tkt_request,
107                     krb5_enc_tkt_part *enc_tkt_reply);
108
109 static krb5_error_code
110 handle_signedpath_authdata(krb5_context context, unsigned int flags,
111                            krb5_db_entry *client, krb5_db_entry *server,
112                            krb5_db_entry *krbtgt, krb5_keyblock *client_key,
113                            krb5_keyblock *server_key,
114                            krb5_keyblock *krbtgt_key,
115                            krb5_data *req_pkt, krb5_kdc_req *request,
116                            krb5_const_principal for_user_princ,
117                            krb5_enc_tkt_part *enc_tkt_request,
118                            krb5_enc_tkt_part *enc_tkt_reply);
119
120 typedef struct _krb5_authdata_systems {
121     const char *name;
122 #define AUTHDATA_SYSTEM_UNKNOWN -1
123 #define AUTHDATA_SYSTEM_V0      0
124 #define AUTHDATA_SYSTEM_V2      2
125     int         type;
126 #define AUTHDATA_FLAG_CRITICAL  0x1
127 #define AUTHDATA_FLAG_PRE_PLUGIN 0x2
128 #define AUTHDATA_FLAG_ANONYMOUS 0x4 /* Use plugin even for anonymous tickets */
129     int         flags;
130     void       *plugin_context;
131     init_proc   init;
132     fini_proc   fini;
133     union {
134         authdata_proc_2 v2;
135         authdata_proc_0 v0;
136     } handle_authdata;
137 } krb5_authdata_systems;
138
139 static krb5_authdata_systems static_authdata_systems[] = {
140     {
141         /* Propagate client-submitted authdata */
142         "tgs_req",
143         AUTHDATA_SYSTEM_V2,
144         AUTHDATA_FLAG_CRITICAL | AUTHDATA_FLAG_PRE_PLUGIN |
145         AUTHDATA_FLAG_ANONYMOUS,
146         NULL,
147         NULL,
148         NULL,
149         { handle_request_authdata }
150     },
151     {
152         /* Propagate TGT authdata */
153         "tgt",
154         AUTHDATA_SYSTEM_V2,
155         AUTHDATA_FLAG_CRITICAL | AUTHDATA_FLAG_ANONYMOUS,
156         NULL,
157         NULL,
158         NULL,
159         { handle_tgt_authdata }
160     },
161     {
162         /* Verify and issue KDB issued authdata */
163         "kdb",
164         AUTHDATA_SYSTEM_V2,
165         AUTHDATA_FLAG_CRITICAL,
166         NULL,
167         NULL,
168         NULL,
169         { handle_kdb_authdata }
170     },
171     {
172         /* Verify and issue signed delegation path */
173         "signedpath",
174         AUTHDATA_SYSTEM_V2,
175         AUTHDATA_FLAG_CRITICAL,
176         NULL,
177         NULL,
178         NULL,
179         { handle_signedpath_authdata }
180     }
181 };
182
183 static krb5_authdata_systems *authdata_systems;
184 static int n_authdata_systems;
185 static struct plugin_dir_handle authdata_plugins;
186
187 /* Load both v0 and v2 authdata plugins */
188 krb5_error_code
189 load_authdata_plugins(krb5_context context)
190 {
191     void **authdata_plugins_ftables_v0 = NULL;
192     void **authdata_plugins_ftables_v2 = NULL;
193     size_t module_count;
194     size_t i, k;
195     init_proc server_init_proc = NULL;
196     krb5_error_code code;
197
198     /* Attempt to load all of the authdata plugins we can find. */
199     PLUGIN_DIR_INIT(&authdata_plugins);
200     if (PLUGIN_DIR_OPEN(&authdata_plugins) == 0) {
201         if (krb5int_open_plugin_dirs(objdirs, NULL,
202                                      &authdata_plugins, &context->err) != 0) {
203             return KRB5_PLUGIN_NO_HANDLE;
204         }
205     }
206
207     /* Get the method tables provided by the loaded plugins. */
208     authdata_plugins_ftables_v0 = NULL;
209     authdata_plugins_ftables_v2 = NULL;
210     n_authdata_systems = 0;
211
212     if (krb5int_get_plugin_dir_data(&authdata_plugins,
213                                     "authdata_server_2",
214                                     &authdata_plugins_ftables_v2,
215                                     &context->err) != 0 ||
216         krb5int_get_plugin_dir_data(&authdata_plugins,
217                                     "authdata_server_0",
218                                     &authdata_plugins_ftables_v0,
219                                     &context->err) != 0) {
220         code = KRB5_PLUGIN_NO_HANDLE;
221         goto cleanup;
222     }
223
224     /* Count the valid modules. */
225     module_count = 0;
226
227     if (authdata_plugins_ftables_v2 != NULL) {
228         struct krb5plugin_authdata_server_ftable_v2 *ftable;
229
230         for (i = 0; authdata_plugins_ftables_v2[i] != NULL; i++) {
231             ftable = authdata_plugins_ftables_v2[i];
232             if (ftable->authdata_proc != NULL)
233                 module_count++;
234         }
235     }
236
237     if (authdata_plugins_ftables_v0 != NULL) {
238         struct krb5plugin_authdata_server_ftable_v0 *ftable;
239
240         for (i = 0; authdata_plugins_ftables_v0[i] != NULL; i++) {
241             ftable = authdata_plugins_ftables_v0[i];
242             if (ftable->authdata_proc != NULL)
243                 module_count++;
244         }
245     }
246
247     module_count += sizeof(static_authdata_systems)
248         / sizeof(static_authdata_systems[0]);
249
250     /* Build the complete list of supported authdata options, and
251      * leave room for a terminator entry.
252      */
253     authdata_systems = calloc(module_count + 1, sizeof(krb5_authdata_systems));
254     if (authdata_systems == NULL) {
255         code = ENOMEM;
256         goto cleanup;
257     }
258
259     k = 0;
260
261     /*
262      * Special case to ensure that handle_request_authdata is
263      * first in the list, to make unenc_authdata available to
264      * plugins.
265      */
266     for (i = 0; i < (sizeof(static_authdata_systems) /
267                      sizeof(static_authdata_systems[0])); i++) {
268         if ((static_authdata_systems[i].flags & AUTHDATA_FLAG_PRE_PLUGIN) == 0)
269             continue;
270         assert(static_authdata_systems[i].init == NULL);
271         authdata_systems[k++] = static_authdata_systems[i];
272     }
273
274     /* Add dynamically loaded V2 plugins */
275     if (authdata_plugins_ftables_v2 != NULL) {
276         struct krb5plugin_authdata_server_ftable_v2 *ftable;
277
278         for (i = 0; authdata_plugins_ftables_v2[i] != NULL; i++) {
279             krb5_error_code initerr;
280             void *pctx = NULL;
281
282             ftable = authdata_plugins_ftables_v2[i];
283             if ((ftable->authdata_proc == NULL)) {
284                 continue;
285             }
286             server_init_proc = ftable->init_proc;
287             if ((server_init_proc != NULL) &&
288                 ((initerr = (*server_init_proc)(context, &pctx)) != 0)) {
289                 const char *emsg;
290                 emsg = krb5_get_error_message(context, initerr);
291                 krb5_klog_syslog(LOG_ERR,
292                                  _("authdata %s failed to initialize: %s"),
293                                  ftable->name, emsg);
294                 krb5_free_error_message(context, emsg);
295                 memset(&authdata_systems[k], 0, sizeof(authdata_systems[k]));
296
297                 continue;
298             }
299
300             authdata_systems[k].name = ftable->name;
301             authdata_systems[k].type = AUTHDATA_SYSTEM_V2;
302             authdata_systems[k].init = server_init_proc;
303             authdata_systems[k].fini = ftable->fini_proc;
304             authdata_systems[k].handle_authdata.v2 = ftable->authdata_proc;
305             authdata_systems[k].plugin_context = pctx;
306             k++;
307         }
308     }
309
310     /* Add dynamically loaded V0 plugins */
311     if (authdata_plugins_ftables_v0 != NULL) {
312         struct krb5plugin_authdata_server_ftable_v0 *ftable;
313
314         for (i = 0; authdata_plugins_ftables_v0[i] != NULL; i++) {
315             krb5_error_code initerr;
316             void *pctx = NULL;
317
318             ftable = authdata_plugins_ftables_v0[i];
319             if ((ftable->authdata_proc == NULL)) {
320                 continue;
321             }
322             server_init_proc = ftable->init_proc;
323             if ((server_init_proc != NULL) &&
324                 ((initerr = (*server_init_proc)(context, &pctx)) != 0)) {
325                 const char *emsg;
326                 emsg = krb5_get_error_message(context, initerr);
327                 krb5_klog_syslog(LOG_ERR,
328                                  _("authdata %s failed to initialize: %s"),
329                                  ftable->name, emsg);
330                 krb5_free_error_message(context, emsg);
331                 memset(&authdata_systems[k], 0, sizeof(authdata_systems[k]));
332
333                 continue;
334             }
335
336             authdata_systems[k].name = ftable->name;
337             authdata_systems[k].type = AUTHDATA_SYSTEM_V0;
338             authdata_systems[k].init = server_init_proc;
339             authdata_systems[k].fini = ftable->fini_proc;
340             authdata_systems[k].handle_authdata.v0 = ftable->authdata_proc;
341             authdata_systems[k].plugin_context = pctx;
342             k++;
343         }
344     }
345
346     for (i = 0;
347          i < sizeof(static_authdata_systems) / sizeof(static_authdata_systems[0]);
348          i++) {
349         if (static_authdata_systems[i].flags & AUTHDATA_FLAG_PRE_PLUGIN)
350             continue;
351         assert(static_authdata_systems[i].init == NULL);
352         authdata_systems[k++] = static_authdata_systems[i];
353     }
354
355     n_authdata_systems = k;
356     /* Add the end-of-list marker. */
357     authdata_systems[k].name = "[end]";
358     authdata_systems[k].type = AUTHDATA_SYSTEM_UNKNOWN;
359     code = 0;
360
361 cleanup:
362     if (authdata_plugins_ftables_v2 != NULL)
363         krb5int_free_plugin_dir_data(authdata_plugins_ftables_v2);
364     if (authdata_plugins_ftables_v0 != NULL)
365         krb5int_free_plugin_dir_data(authdata_plugins_ftables_v0);
366
367     return code;
368 }
369
370 krb5_error_code
371 unload_authdata_plugins(krb5_context context)
372 {
373     int i;
374     if (authdata_systems != NULL) {
375         for (i = 0; i < n_authdata_systems; i++) {
376             if (authdata_systems[i].fini != NULL) {
377                 (*authdata_systems[i].fini)(context,
378                                             authdata_systems[i].plugin_context);
379             }
380             memset(&authdata_systems[i], 0, sizeof(authdata_systems[i]));
381         }
382         free(authdata_systems);
383         authdata_systems = NULL;
384         n_authdata_systems = 0;
385         krb5int_close_plugin_dirs(&authdata_plugins);
386     }
387     return 0;
388 }
389
390 /*
391  * Returns TRUE if authdata should be filtered when copying from
392  * untrusted authdata.
393  */
394 static krb5_boolean
395 is_kdc_issued_authdatum (krb5_context context,
396                          krb5_authdata *authdata,
397                          krb5_authdatatype desired_type)
398 {
399     krb5_boolean ret = FALSE;
400     krb5_authdatatype ad_type;
401     unsigned int i, count = 0;
402     krb5_authdatatype *ad_types = NULL;
403
404     if (authdata->ad_type == KRB5_AUTHDATA_IF_RELEVANT) {
405         if (krb5int_get_authdata_containee_types(context,
406                                                  authdata,
407                                                  &count,
408                                                  &ad_types) != 0)
409             goto cleanup;
410     } else {
411         ad_type = authdata->ad_type;
412         count = 1;
413         ad_types = &ad_type;
414     }
415
416     for (i = 0; i < count; i++) {
417         switch (ad_types[i]) {
418         case KRB5_AUTHDATA_SIGNTICKET:
419         case KRB5_AUTHDATA_KDC_ISSUED:
420         case KRB5_AUTHDATA_WIN2K_PAC:
421             ret = desired_type ? (desired_type == ad_types[i]) : TRUE;
422             break;
423         default:
424             ret = FALSE;
425             break;
426         }
427         if (ret)
428             break;
429     }
430
431 cleanup:
432     if (authdata->ad_type == KRB5_AUTHDATA_IF_RELEVANT &&
433         ad_types != NULL)
434         free(ad_types);
435
436     return ret;
437 }
438
439 static krb5_boolean
440 has_kdc_issued_authdata (krb5_context context,
441                          krb5_authdata **authdata,
442                          krb5_authdatatype desired_type)
443 {
444     int i;
445     krb5_boolean ret = FALSE;
446
447     if (authdata != NULL) {
448         for (i = 0; authdata[i] != NULL; i++) {
449             if (is_kdc_issued_authdatum(context, authdata[i], desired_type)) {
450                 ret = TRUE;
451                 break;
452             }
453         }
454     }
455
456     return ret;
457 }
458
459 static krb5_boolean
460 has_mandatory_for_kdc_authdata (krb5_context context,
461                                 krb5_authdata **authdata)
462 {
463     int i;
464     krb5_boolean ret = FALSE;
465
466     if (authdata != NULL) {
467         for (i = 0; authdata[i] != NULL; i++) {
468             if (authdata[i]->ad_type == KRB5_AUTHDATA_MANDATORY_FOR_KDC) {
469                 ret = TRUE;
470                 break;
471             }
472         }
473     }
474
475     return ret;
476 }
477
478 /*
479  * Merge authdata.
480  *
481  * If copy is FALSE, in_authdata is invalid on successful return.
482  * If ignore_kdc_issued is TRUE, KDC-issued authdata is not copied.
483  */
484 static krb5_error_code
485 merge_authdata (krb5_context context,
486                 krb5_authdata **in_authdata,
487                 krb5_authdata ***out_authdata,
488                 krb5_boolean copy,
489                 krb5_boolean ignore_kdc_issued)
490 {
491     size_t i, j, nadata = 0;
492     krb5_authdata **authdata = *out_authdata;
493
494     if (in_authdata == NULL || in_authdata[0] == NULL)
495         return 0;
496
497     if (authdata != NULL) {
498         for (nadata = 0; authdata[nadata] != NULL; nadata++)
499             ;
500     }
501
502     for (i = 0; in_authdata[i] != NULL; i++)
503         ;
504
505     if (authdata == NULL) {
506         authdata = (krb5_authdata **)calloc(i + 1, sizeof(krb5_authdata *));
507     } else {
508         authdata = (krb5_authdata **)realloc(authdata,
509                                              ((nadata + i + 1) * sizeof(krb5_authdata *)));
510     }
511     if (authdata == NULL)
512         return ENOMEM;
513
514     if (copy) {
515         krb5_error_code code;
516         krb5_authdata **tmp;
517
518         code = krb5_copy_authdata(context, in_authdata, &tmp);
519         if (code != 0)
520             return code;
521
522         in_authdata = tmp;
523     }
524
525     for (i = 0, j = 0; in_authdata[i] != NULL; i++) {
526         if (ignore_kdc_issued &&
527             is_kdc_issued_authdatum(context, in_authdata[i], 0)) {
528             free(in_authdata[i]->contents);
529             free(in_authdata[i]);
530         } else
531             authdata[nadata + j++] = in_authdata[i];
532     }
533
534     authdata[nadata + j] = NULL;
535
536     free(in_authdata);
537
538     if (authdata[0] == NULL) {
539         free(authdata);
540         authdata = NULL;
541     }
542
543     *out_authdata = authdata;
544
545     return 0;
546 }
547
548 /* Handle copying TGS-REQ authorization data into reply */
549 static krb5_error_code
550 handle_request_authdata (krb5_context context,
551                          unsigned int flags,
552                          krb5_db_entry *client,
553                          krb5_db_entry *server,
554                          krb5_db_entry *krbtgt,
555                          krb5_keyblock *client_key,
556                          krb5_keyblock *server_key,
557                          krb5_keyblock *krbtgt_key,
558                          krb5_data *req_pkt,
559                          krb5_kdc_req *request,
560                          krb5_const_principal for_user_princ,
561                          krb5_enc_tkt_part *enc_tkt_request,
562                          krb5_enc_tkt_part *enc_tkt_reply)
563 {
564     krb5_error_code code;
565     krb5_data scratch;
566
567     if (request->msg_type != KRB5_TGS_REQ ||
568         request->authorization_data.ciphertext.data == NULL)
569         return 0;
570
571     assert(enc_tkt_request != NULL);
572
573     scratch.length = request->authorization_data.ciphertext.length;
574     scratch.data = malloc(scratch.length);
575     if (scratch.data == NULL)
576         return ENOMEM;
577
578     /*
579      * RFC 4120 requires authdata in the TGS body to be encrypted in
580      * the subkey with usage 5 if a subkey is present, and in the TGS
581      * session key with key usage 4 if it is not.  Prior to krb5 1.7,
582      * we got this wrong, always decrypting the authorization data
583      * with the TGS session key and usage 4.  For the sake of
584      * conservatism, try the decryption the old way (wrong if
585      * client_key is a subkey) first, and then try again the right way
586      * (in the case where client_key is a subkey) if the first way
587      * fails.
588      */
589     code = krb5_c_decrypt(context,
590                           enc_tkt_request->session,
591                           KRB5_KEYUSAGE_TGS_REQ_AD_SESSKEY,
592                           0, &request->authorization_data,
593                           &scratch);
594     if (code != 0)
595         code = krb5_c_decrypt(context,
596                               client_key,
597                               KRB5_KEYUSAGE_TGS_REQ_AD_SUBKEY,
598                               0, &request->authorization_data,
599                               &scratch);
600
601     if (code != 0) {
602         free(scratch.data);
603         return code;
604     }
605
606     /* scratch now has the authorization data, so we decode it, and make
607      * it available to subsequent authdata plugins
608      */
609     code = decode_krb5_authdata(&scratch, &request->unenc_authdata);
610     if (code != 0) {
611         free(scratch.data);
612         return code;
613     }
614
615     free(scratch.data);
616
617     if (has_mandatory_for_kdc_authdata(context, request->unenc_authdata))
618         return KRB5KDC_ERR_POLICY;
619
620     code = merge_authdata(context,
621                           request->unenc_authdata,
622                           &enc_tkt_reply->authorization_data,
623                           TRUE,            /* copy */
624                           TRUE);    /* ignore_kdc_issued */
625
626     return code;
627 }
628
629 /* Handle copying TGT authorization data into reply */
630 static krb5_error_code
631 handle_tgt_authdata (krb5_context context,
632                      unsigned int flags,
633                      krb5_db_entry *client,
634                      krb5_db_entry *server,
635                      krb5_db_entry *krbtgt,
636                      krb5_keyblock *client_key,
637                      krb5_keyblock *server_key,
638                      krb5_keyblock *krbtgt_key,
639                      krb5_data *req_pkt,
640                      krb5_kdc_req *request,
641                      krb5_const_principal for_user_princ,
642                      krb5_enc_tkt_part *enc_tkt_request,
643                      krb5_enc_tkt_part *enc_tkt_reply)
644 {
645     if (request->msg_type != KRB5_TGS_REQ)
646         return 0;
647
648     if (has_mandatory_for_kdc_authdata(context,
649                                        enc_tkt_request->authorization_data))
650         return KRB5KDC_ERR_POLICY;
651
652     return merge_authdata(context,
653                           enc_tkt_request->authorization_data,
654                           &enc_tkt_reply->authorization_data,
655                           TRUE,            /* copy */
656                           TRUE);    /* ignore_kdc_issued */
657 }
658
659 /* Handle backend-managed authorization data */
660 static krb5_error_code
661 handle_kdb_authdata (krb5_context context,
662                      unsigned int flags,
663                      krb5_db_entry *client,
664                      krb5_db_entry *server,
665                      krb5_db_entry *krbtgt,
666                      krb5_keyblock *client_key,
667                      krb5_keyblock *server_key,
668                      krb5_keyblock *krbtgt_key,
669                      krb5_data *req_pkt,
670                      krb5_kdc_req *request,
671                      krb5_const_principal for_user_princ,
672                      krb5_enc_tkt_part *enc_tkt_request,
673                      krb5_enc_tkt_part *enc_tkt_reply)
674 {
675     krb5_error_code code;
676     krb5_authdata **tgt_authdata, **db_authdata = NULL;
677     krb5_boolean tgs_req = (request->msg_type == KRB5_TGS_REQ);
678     krb5_const_principal actual_client;
679
680     /*
681      * Check whether KDC issued authorization data should be included.
682      * A server can explicitly disable the inclusion of authorization
683      * data by setting the KRB5_KDB_NO_AUTH_DATA_REQUIRED flag on its
684      * principal entry. Otherwise authorization data will be included
685      * if it was present in the TGT, the client is from another realm
686      * or protocol transition/constrained delegation was used, or, in
687      * the AS-REQ case, if the pre-auth data indicated the PAC should
688      * be present.
689      */
690     if (tgs_req) {
691         assert(enc_tkt_request != NULL);
692
693         if (isflagset(server->attributes, KRB5_KDB_NO_AUTH_DATA_REQUIRED))
694             return 0;
695
696         if (enc_tkt_request->authorization_data == NULL &&
697             !isflagset(flags, KRB5_KDB_FLAG_CROSS_REALM | KRB5_KDB_FLAGS_S4U))
698             return 0;
699
700         assert(enc_tkt_reply->times.authtime == enc_tkt_request->times.authtime);
701     } else {
702         if (!isflagset(flags, KRB5_KDB_FLAG_INCLUDE_PAC))
703             return 0;
704     }
705
706     /*
707      * We have this special case for protocol transition, because for
708      * cross-realm protocol transition the ticket reply client will
709      * not be changed until the final hop.
710      */
711     if (isflagset(flags, KRB5_KDB_FLAG_PROTOCOL_TRANSITION))
712         actual_client = for_user_princ;
713     else
714         actual_client = enc_tkt_reply->client;
715
716     tgt_authdata = tgs_req ? enc_tkt_request->authorization_data : NULL;
717     code = krb5_db_sign_authdata(context, flags, actual_client, client,
718                                  server, krbtgt, client_key, server_key,
719                                  krbtgt_key, enc_tkt_reply->session,
720                                  enc_tkt_reply->times.authtime, tgt_authdata,
721                                  &db_authdata);
722     if (code == 0) {
723         code = merge_authdata(context,
724                               db_authdata,
725                               &enc_tkt_reply->authorization_data,
726                               FALSE,        /* !copy */
727                               FALSE);        /* !ignore_kdc_issued */
728         if (code != 0)
729             krb5_free_authdata(context, db_authdata);
730     } else if (code == KRB5_PLUGIN_OP_NOTSUPP)
731         code = 0;
732
733     return code;
734 }
735
736 krb5_error_code
737 handle_authdata (krb5_context context,
738                  unsigned int flags,
739                  krb5_db_entry *client,
740                  krb5_db_entry *server,
741                  krb5_db_entry *krbtgt,
742                  krb5_keyblock *client_key,
743                  krb5_keyblock *server_key,
744                  krb5_keyblock *krbtgt_key,
745                  krb5_data *req_pkt,
746                  krb5_kdc_req *request,
747                  krb5_const_principal for_user_princ,
748                  krb5_enc_tkt_part *enc_tkt_request,
749                  krb5_enc_tkt_part *enc_tkt_reply)
750 {
751     krb5_error_code code = 0;
752     int i;
753
754     for (i = 0; i < n_authdata_systems; i++) {
755         const krb5_authdata_systems *asys = &authdata_systems[i];
756         if (isflagset(enc_tkt_reply->flags, TKT_FLG_ANONYMOUS) &&
757             !isflagset(asys->flags, AUTHDATA_FLAG_ANONYMOUS))
758             continue;
759
760         switch (asys->type) {
761         case AUTHDATA_SYSTEM_V0:
762             /* V0 was only in AS-REQ code path */
763             if (request->msg_type != KRB5_AS_REQ)
764                 continue;
765
766             code = (*asys->handle_authdata.v0)(context, client, req_pkt,
767                                                request, enc_tkt_reply);
768             break;
769         case AUTHDATA_SYSTEM_V2:
770             code = (*asys->handle_authdata.v2)(context, flags,
771                                                client, server, krbtgt,
772                                                client_key, server_key, krbtgt_key,
773                                                req_pkt, request, for_user_princ,
774                                                enc_tkt_request,
775                                                enc_tkt_reply);
776             break;
777         default:
778             code = 0;
779             break;
780         }
781         if (code != 0) {
782             const char *emsg;
783
784             emsg = krb5_get_error_message (context, code);
785             krb5_klog_syslog(LOG_INFO, _("authdata (%s) handling failure: %s"),
786                              asys->name, emsg);
787             krb5_free_error_message (context, emsg);
788
789             if (asys->flags & AUTHDATA_FLAG_CRITICAL)
790                 break;
791         }
792     }
793
794     return code;
795 }
796
797 static krb5_error_code
798 make_ad_signedpath_data(krb5_context context,
799                         krb5_const_principal client,
800                         krb5_timestamp authtime,
801                         krb5_principal *deleg_path,
802                         krb5_pa_data **method_data,
803                         krb5_authdata **authdata,
804                         krb5_data **data)
805 {
806     krb5_ad_signedpath_data         sp_data;
807     krb5_authdata                 **sign_authdata = NULL;
808     int                             i, j;
809     krb5_error_code                 code;
810
811     memset(&sp_data, 0, sizeof(sp_data));
812
813     if (authdata != NULL) {
814         for (i = 0; authdata[i] != NULL; i++)
815             ;
816     } else
817         i = 0;
818
819     if (i != 0) {
820         sign_authdata = k5alloc((i + 1) * sizeof(krb5_authdata *), &code);
821         if (sign_authdata == NULL)
822             return code;
823
824         for (i = 0, j = 0; authdata[i] != NULL; i++) {
825             if (is_kdc_issued_authdatum(context, authdata[i],
826                                         KRB5_AUTHDATA_SIGNTICKET))
827                 continue;
828
829             sign_authdata[j++] = authdata[i];
830         }
831
832         sign_authdata[j] = NULL;
833     }
834
835     sp_data.client = (krb5_principal)client;
836     sp_data.authtime = authtime;
837     sp_data.delegated = deleg_path;
838     sp_data.method_data = method_data;
839     sp_data.authorization_data = sign_authdata;
840
841     code = encode_krb5_ad_signedpath_data(&sp_data, data);
842
843     if (sign_authdata != NULL)
844         free(sign_authdata);
845
846     return code;
847 }
848
849 static krb5_error_code
850 verify_ad_signedpath_checksum(krb5_context context,
851                               const krb5_db_entry *krbtgt,
852                               krb5_keyblock *krbtgt_key,
853                               krb5_enc_tkt_part *enc_tkt_part,
854                               krb5_principal *deleg_path,
855                               krb5_pa_data **method_data,
856                               krb5_checksum *cksum,
857                               krb5_boolean *valid)
858 {
859     krb5_error_code                 code;
860     krb5_data                      *data;
861
862     *valid = FALSE;
863
864     if (!krb5_c_is_keyed_cksum(cksum->checksum_type))
865         return KRB5KRB_AP_ERR_INAPP_CKSUM;
866
867     code = make_ad_signedpath_data(context,
868                                    enc_tkt_part->client,
869                                    enc_tkt_part->times.authtime,
870                                    deleg_path,
871                                    method_data,
872                                    enc_tkt_part->authorization_data,
873                                    &data);
874     if (code != 0)
875         return code;
876
877     code = krb5_c_verify_checksum(context,
878                                   krbtgt_key,
879                                   KRB5_KEYUSAGE_AD_SIGNEDPATH,
880                                   data,
881                                   cksum,
882                                   valid);
883
884     krb5_free_data(context, data);
885     return code;
886 }
887
888
889 static krb5_error_code
890 verify_ad_signedpath(krb5_context context,
891                      krb5_db_entry *krbtgt,
892                      krb5_keyblock *krbtgt_key,
893                      krb5_enc_tkt_part *enc_tkt_part,
894                      krb5_principal **pdelegated,
895                      krb5_boolean *path_is_signed)
896 {
897     krb5_error_code                 code;
898     krb5_ad_signedpath             *sp = NULL;
899     krb5_authdata                 **sp_authdata = NULL;
900     krb5_data                       enc_sp;
901
902     *pdelegated = NULL;
903     *path_is_signed = FALSE;
904
905     code = krb5_find_authdata(context, enc_tkt_part->authorization_data, NULL,
906                               KRB5_AUTHDATA_SIGNTICKET, &sp_authdata);
907     if (code != 0)
908         goto cleanup;
909
910     if (sp_authdata == NULL ||
911         sp_authdata[0]->ad_type != KRB5_AUTHDATA_SIGNTICKET ||
912         sp_authdata[1] != NULL)
913         goto cleanup;
914
915     enc_sp.data = (char *)sp_authdata[0]->contents;
916     enc_sp.length = sp_authdata[0]->length;
917
918     code = decode_krb5_ad_signedpath(&enc_sp, &sp);
919     if (code != 0) {
920         /* Treat an invalid signedpath authdata element as a missing one, since
921          * we believe MS is using the same number for something else. */
922         code = 0;
923         goto cleanup;
924     }
925
926     code = verify_ad_signedpath_checksum(context,
927                                          krbtgt,
928                                          krbtgt_key,
929                                          enc_tkt_part,
930                                          sp->delegated,
931                                          sp->method_data,
932                                          &sp->checksum,
933                                          path_is_signed);
934     if (code != 0)
935         goto cleanup;
936
937     if (*path_is_signed) {
938         *pdelegated = sp->delegated;
939         sp->delegated = NULL;
940     }
941
942 cleanup:
943     krb5_free_ad_signedpath(context, sp);
944     krb5_free_authdata(context, sp_authdata);
945
946     return code;
947 }
948
949 static krb5_error_code
950 make_ad_signedpath_checksum(krb5_context context,
951                             krb5_const_principal for_user_princ,
952                             const krb5_db_entry *krbtgt,
953                             krb5_keyblock *krbtgt_key,
954                             krb5_enc_tkt_part *enc_tkt_part,
955                             krb5_principal *deleg_path,
956                             krb5_pa_data **method_data,
957                             krb5_checksum *cksum)
958 {
959     krb5_error_code                 code;
960     krb5_data                      *data;
961     krb5_cksumtype                  cksumtype;
962     krb5_const_principal            client;
963
964     if (for_user_princ != NULL)
965         client = for_user_princ;
966     else
967         client = enc_tkt_part->client;
968
969     code = make_ad_signedpath_data(context,
970                                    client,
971                                    enc_tkt_part->times.authtime,
972                                    deleg_path,
973                                    method_data,
974                                    enc_tkt_part->authorization_data,
975                                    &data);
976     if (code != 0)
977         return code;
978
979     code = krb5int_c_mandatory_cksumtype(context,
980                                          krbtgt_key->enctype,
981                                          &cksumtype);
982     if (code != 0) {
983         krb5_free_data(context, data);
984         return code;
985     }
986
987     if (!krb5_c_is_keyed_cksum(cksumtype)) {
988         krb5_free_data(context, data);
989         return KRB5KRB_AP_ERR_INAPP_CKSUM;
990     }
991
992     code = krb5_c_make_checksum(context, cksumtype, krbtgt_key,
993                                 KRB5_KEYUSAGE_AD_SIGNEDPATH, data,
994                                 cksum);
995
996     krb5_free_data(context, data);
997
998     return code;
999 }
1000
1001 static krb5_error_code
1002 make_ad_signedpath(krb5_context context,
1003                    krb5_const_principal for_user_princ,
1004                    krb5_principal server,
1005                    const krb5_db_entry *krbtgt,
1006                    krb5_keyblock *krbtgt_key,
1007                    krb5_principal *deleg_path,
1008                    krb5_enc_tkt_part *enc_tkt_reply)
1009 {
1010     krb5_error_code                 code;
1011     krb5_ad_signedpath              sp;
1012     int                             i;
1013     krb5_data                      *data = NULL;
1014     krb5_authdata                   ad_datum, *ad_data[2];
1015     krb5_authdata                 **if_relevant = NULL;
1016
1017     memset(&sp, 0, sizeof(sp));
1018
1019     sp.enctype = krbtgt_key->enctype;
1020
1021     if (deleg_path != NULL) {
1022         for (i = 0; deleg_path[i] != NULL; i++)
1023             ;
1024     } else
1025         i = 0;
1026
1027     sp.delegated = k5alloc((i + (server ? 1 : 0) + 1) *
1028                            sizeof(krb5_principal), &code);
1029     if (code != 0)
1030         goto cleanup;
1031
1032     /* Combine existing and new transited services, if any */
1033     if (deleg_path != NULL)
1034         memcpy(sp.delegated, deleg_path, i * sizeof(krb5_principal));
1035     if (server != NULL)
1036         sp.delegated[i++] = server;
1037     sp.delegated[i] = NULL;
1038     sp.method_data = NULL;
1039
1040     code = make_ad_signedpath_checksum(context,
1041                                        for_user_princ,
1042                                        krbtgt,
1043                                        krbtgt_key,
1044                                        enc_tkt_reply,
1045                                        sp.delegated,
1046                                        sp.method_data,
1047                                        &sp.checksum);
1048     if (code != 0) {
1049         if (code == KRB5KRB_AP_ERR_INAPP_CKSUM) {
1050             /*
1051              * In the hopefully unlikely case the TGS key enctype
1052              * has an unkeyed mandatory checksum type, do not fail
1053              * so we do not prevent the KDC from servicing requests.
1054              */
1055             code = 0;
1056         }
1057         goto cleanup;
1058     }
1059
1060     code = encode_krb5_ad_signedpath(&sp, &data);
1061     if (code != 0)
1062         goto cleanup;
1063
1064     ad_datum.ad_type = KRB5_AUTHDATA_SIGNTICKET;
1065     ad_datum.contents = (krb5_octet *)data->data;
1066     ad_datum.length = data->length;
1067
1068     ad_data[0] = &ad_datum;
1069     ad_data[1] = NULL;
1070
1071     code = krb5_encode_authdata_container(context,
1072                                           KRB5_AUTHDATA_IF_RELEVANT,
1073                                           ad_data,
1074                                           &if_relevant);
1075     if (code != 0)
1076         goto cleanup;
1077
1078     code = merge_authdata(context,
1079                           if_relevant,
1080                           &enc_tkt_reply->authorization_data,
1081                           FALSE,        /* !copy */
1082                           FALSE);       /* !ignore_kdc_issued */
1083     if (code != 0)
1084         goto cleanup;
1085
1086     if_relevant = NULL; /* merge_authdata() freed */
1087
1088 cleanup:
1089     if (sp.delegated != NULL)
1090         free(sp.delegated);
1091     krb5_free_authdata(context, if_relevant);
1092     krb5_free_data(context, data);
1093     krb5_free_checksum_contents(context, &sp.checksum);
1094     krb5_free_pa_data(context, sp.method_data);
1095
1096     return code;
1097 }
1098
1099 static void
1100 free_deleg_path(krb5_context context, krb5_principal *deleg_path)
1101 {
1102     if (deleg_path != NULL) {
1103         int i;
1104
1105         for (i = 0; deleg_path[i] != NULL; i++)
1106             krb5_free_principal(context, deleg_path[i]);
1107         free(deleg_path);
1108     }
1109 }
1110
1111 /*
1112  * Returns TRUE if the Windows 2000 PAC is the only element in the
1113  * supplied authorization data.
1114  */
1115 static krb5_boolean
1116 only_pac_p(krb5_context context, krb5_authdata **authdata)
1117 {
1118     return has_kdc_issued_authdata(context,
1119                                    authdata, KRB5_AUTHDATA_WIN2K_PAC) &&
1120         (authdata[1] == NULL);
1121 }
1122
1123 static krb5_error_code
1124 handle_signedpath_authdata (krb5_context context,
1125                             unsigned int flags,
1126                             krb5_db_entry *client,
1127                             krb5_db_entry *server,
1128                             krb5_db_entry *krbtgt,
1129                             krb5_keyblock *client_key,
1130                             krb5_keyblock *server_key,
1131                             krb5_keyblock *krbtgt_key,
1132                             krb5_data *req_pkt,
1133                             krb5_kdc_req *request,
1134                             krb5_const_principal for_user_princ,
1135                             krb5_enc_tkt_part *enc_tkt_request,
1136                             krb5_enc_tkt_part *enc_tkt_reply)
1137 {
1138     krb5_error_code code = 0;
1139     krb5_principal *deleg_path = NULL;
1140     krb5_boolean signed_path = FALSE;
1141     krb5_boolean s4u2proxy;
1142
1143     s4u2proxy = isflagset(flags, KRB5_KDB_FLAG_CONSTRAINED_DELEGATION);
1144
1145     /*
1146      * The Windows PAC fulfils the same role as the signed path
1147      * if it is the only authorization data element.
1148      */
1149     if (request->msg_type == KRB5_TGS_REQ &&
1150         !only_pac_p(context, enc_tkt_request->authorization_data)) {
1151         code = verify_ad_signedpath(context,
1152                                     krbtgt,
1153                                     krbtgt_key,
1154                                     enc_tkt_request,
1155                                     &deleg_path,
1156                                     &signed_path);
1157         if (code != 0)
1158             goto cleanup;
1159
1160         if (s4u2proxy && signed_path == FALSE) {
1161             code = KRB5KDC_ERR_BADOPTION;
1162             goto cleanup;
1163         }
1164     }
1165
1166     /* No point in including signedpath authdata for a cross-realm TGT, since
1167      * it will be presented to a different KDC. */
1168     if (!is_cross_tgs_principal(server->princ) &&
1169         !only_pac_p(context, enc_tkt_reply->authorization_data)) {
1170         code = make_ad_signedpath(context,
1171                                   for_user_princ,
1172                                   s4u2proxy ? client->princ : NULL,
1173                                   krbtgt,
1174                                   krbtgt_key,
1175                                   deleg_path,
1176                                   enc_tkt_reply);
1177         if (code != 0)
1178             goto cleanup;
1179     }
1180
1181 cleanup:
1182     free_deleg_path(context, deleg_path);
1183
1184     return code;
1185 }