1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /*
3  * Copyright 2010 by the Massachusetts Institute of Technology.  All
4  * Rights Reserved.
5  *
6  * Export of this software from the United States of America may
7  *   require a specific license from the United States Government.
8  *   It is the responsibility of any person or organization contemplating
9  *   export to obtain such a license before exporting.
10  *
11  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
12  * distribute this software and its documentation for any purpose and
13  * without fee is hereby granted, provided that the above copyright
14  * notice appear in all copies and that both that copyright notice and
15  * this permission notice appear in supporting documentation, and that
16  * the name of M.I.T. not be used in advertising or publicity pertaining
17  * to distribution of the software without specific, written prior
18  * permission.  Furthermore if you modify this software you must label
19  * your software as modified software and not distribute it in such a
20  * fashion that it might be confused with the original M.I.T. software.
21  * M.I.T. makes no representations about the suitability of
22  * this software for any purpose.  It is provided "as is" without express
23  * or implied warranty.
24  *
25  */
26 
27 #include "k5-int.h"
28 #include "authdata.h"
29 #include "auth_con.h"
30 #include "int-proto.h"
31 
32 /*
33  * Authdata backend for processing SignedPath. Presently does not handle
34  * the equivalent information in [MS-PAC], as that would require an NDR
35  * interpreter.
36  */
37 
38 struct s4u2proxy_context {
39     int count;
40     krb5_principal *delegated;
41     krb5_boolean authenticated;
42 };
43 
44 static krb5_error_code
s4u2proxy_init(krb5_context kcontext,void ** plugin_context)45 s4u2proxy_init(krb5_context kcontext, void **plugin_context)
46 {
47     *plugin_context = NULL;
48     return 0;
49 }
50 
51 static void
s4u2proxy_flags(krb5_context kcontext,void * plugin_context,krb5_authdatatype ad_type,krb5_flags * flags)52 s4u2proxy_flags(krb5_context kcontext,
53                 void *plugin_context,
54                 krb5_authdatatype ad_type,
55                 krb5_flags *flags)
56 {
57     *flags = AD_USAGE_KDC_ISSUED;
58 }
59 
60 static void
s4u2proxy_fini(krb5_context kcontext,void * plugin_context)61 s4u2proxy_fini(krb5_context kcontext, void *plugin_context)
62 {
63     return;
64 }
65 
66 static krb5_error_code
s4u2proxy_request_init(krb5_context kcontext,krb5_authdata_context context,void * plugin_context,void ** request_context)67 s4u2proxy_request_init(krb5_context kcontext,
68                        krb5_authdata_context context,
69                        void *plugin_context,
70                        void **request_context)
71 {
72     krb5_error_code code;
73     struct s4u2proxy_context *s4uctx;
74 
75     s4uctx = k5alloc(sizeof(*s4uctx), &code);
76     if (s4uctx == NULL)
77         return code;
78 
79     s4uctx->count = 0;
80     s4uctx->delegated = NULL;
81     s4uctx->authenticated = FALSE;
82 
83     *request_context = s4uctx;
84 
85     return 0;
86 }
87 
88 static void
s4u2proxy_free_internal(krb5_context kcontext,krb5_authdata_context context,void * plugin_context,void * request_context,void * ptr)89 s4u2proxy_free_internal(krb5_context kcontext,
90                         krb5_authdata_context context,
91                         void *plugin_context,
92                         void *request_context,
93                         void *ptr)
94 {
95     krb5_principal *delegated = (krb5_principal *)ptr;
96     int i;
97 
98     if (delegated != NULL) {
99         for (i = 0; delegated[i] != NULL; i++)
100             krb5_free_principal(kcontext, delegated[i]);
101         free(delegated);
102     }
103 }
104 
105 static krb5_error_code
s4u2proxy_import_authdata(krb5_context kcontext,krb5_authdata_context context,void * plugin_context,void * request_context,krb5_authdata ** authdata,krb5_boolean kdc_issued,krb5_const_principal kdc_issuer)106 s4u2proxy_import_authdata(krb5_context kcontext,
107                           krb5_authdata_context context,
108                           void *plugin_context,
109                           void *request_context,
110                           krb5_authdata **authdata,
111                           krb5_boolean kdc_issued,
112                           krb5_const_principal kdc_issuer)
113 {
114     struct s4u2proxy_context *s4uctx = (struct s4u2proxy_context *)request_context;
115     krb5_error_code code;
116     krb5_ad_signedpath *sp;
117     krb5_data enc_sp;
118 
119     enc_sp.data = (char *)authdata[0]->contents;
120     enc_sp.length = authdata[0]->length;
121 
122     code = decode_krb5_ad_signedpath(&enc_sp, &sp);
123     if (code != 0)
124         return code;
125 
126     s4u2proxy_free_internal(kcontext, context,
127                             plugin_context, request_context,
128                             s4uctx->delegated);
129 
130     s4uctx->delegated = sp->delegated;
131     sp->delegated = NULL;
132 
133     krb5_free_ad_signedpath(kcontext, sp);
134 
135     s4uctx->count = 0;
136 
137     if (s4uctx->delegated != NULL) {
138         for (s4uctx->count = 0; s4uctx->delegated[s4uctx->count];
139              s4uctx->count++)
140             ;
141     }
142 
143     s4uctx->authenticated = FALSE;
144 
145     return 0;
146 }
147 
148 static krb5_error_code
s4u2proxy_export_authdata(krb5_context kcontext,krb5_authdata_context context,void * plugin_context,void * request_context,krb5_flags usage,krb5_authdata *** out_authdata)149 s4u2proxy_export_authdata(krb5_context kcontext,
150                           krb5_authdata_context context,
151                           void *plugin_context,
152                           void *request_context,
153                           krb5_flags usage,
154                           krb5_authdata ***out_authdata)
155 {
156     struct s4u2proxy_context *s4uctx = (struct s4u2proxy_context *)request_context;
157     krb5_error_code code;
158     krb5_ad_signedpath sp;
159     krb5_authdata **authdata;
160     krb5_data *data;
161 
162     if (s4uctx->count == 0)
163         return 0;
164 
165     memset(&sp, 0, sizeof(sp));
166     sp.delegated = s4uctx->delegated;
167 
168     authdata = k5calloc(2, sizeof(krb5_authdata *), &code);
169     if (authdata == NULL)
170         return code;
171 
172     authdata[0] = k5alloc(sizeof(krb5_authdata), &code);
173     if (authdata[0] == NULL)
174         return code;
175 
176     code = encode_krb5_ad_signedpath(&sp, &data);
177     if (code != 0) {
178         krb5_free_authdata(kcontext, authdata);
179         return code;
180     }
181 
182     authdata[0]->magic = KV5M_AUTHDATA;
183     authdata[0]->ad_type = KRB5_AUTHDATA_SIGNTICKET;
184     authdata[0]->length = data->length;
185     authdata[0]->contents = (krb5_octet *)data->data;
186 
187     authdata[1] = NULL;
188 
189     free(data);
190 
191     *out_authdata = authdata;
192 
193     return 0;
194 }
195 
196 static krb5_error_code
s4u2proxy_verify(krb5_context kcontext,krb5_authdata_context context,void * plugin_context,void * request_context,const krb5_auth_context * auth_context,const krb5_keyblock * key,const krb5_ap_req * req)197 s4u2proxy_verify(krb5_context kcontext,
198                  krb5_authdata_context context,
199                  void *plugin_context,
200                  void *request_context,
201                  const krb5_auth_context *auth_context,
202                  const krb5_keyblock *key,
203                  const krb5_ap_req *req)
204 {
205     /*
206      * XXX there is no way to verify the SignedPath without the TGS
207      * key. This means that we can never mark this as authenticated.
208      */
209 
210     return 0;
211 }
212 
213 static void
s4u2proxy_request_fini(krb5_context kcontext,krb5_authdata_context context,void * plugin_context,void * request_context)214 s4u2proxy_request_fini(krb5_context kcontext,
215                        krb5_authdata_context context,
216                        void *plugin_context,
217                        void *request_context)
218 {
219     struct s4u2proxy_context *s4uctx = (struct s4u2proxy_context *)request_context;
220 
221     if (s4uctx == NULL)
222         return;
223 
224     s4u2proxy_free_internal(kcontext, context,
225                             plugin_context, request_context,
226                             s4uctx->delegated);
227     free(s4uctx);
228 }
229 
230 /*
231  * Nomenclature defined to be similar to [MS-PAC] 2.9, for future
232  * interoperability
233  */
234 
235 static krb5_data s4u2proxy_transited_services_attr = {
236     KV5M_DATA,
237     sizeof("urn:constrained-delegation:transited-services") - 1,
238     "urn:constrained-delegation:transited-services"
239 };
240 
241 static krb5_error_code
s4u2proxy_get_attribute_types(krb5_context kcontext,krb5_authdata_context context,void * plugin_context,void * request_context,krb5_data ** out_attrs)242 s4u2proxy_get_attribute_types(krb5_context kcontext,
243                               krb5_authdata_context context,
244                               void *plugin_context,
245                               void *request_context,
246                               krb5_data **out_attrs)
247 {
248     struct s4u2proxy_context *s4uctx = (struct s4u2proxy_context *)request_context;
249     krb5_error_code code;
250     krb5_data *attrs;
251     int i = 0;
252 
253     if (s4uctx->count == 0)
254         return ENOENT;
255 
256     attrs = k5calloc(2, sizeof(krb5_data), &code);
257     if (attrs == NULL)
258         goto cleanup;
259 
260     code = krb5int_copy_data_contents(kcontext,
261                                       &s4u2proxy_transited_services_attr,
262                                       &attrs[i++]);
263     if (code != 0)
264         goto cleanup;
265 
266     attrs[i].data = NULL;
267     attrs[i].length = 0;
268 
269     *out_attrs = attrs;
270     attrs = NULL;
271 
272 cleanup:
273     if (attrs != NULL) {
274         for (i = 0; attrs[i].data; i++)
275             krb5_free_data_contents(kcontext, &attrs[i]);
276         free(attrs);
277     }
278 
279     return 0;
280 }
281 
282 static krb5_error_code
s4u2proxy_get_attribute(krb5_context kcontext,krb5_authdata_context context,void * plugin_context,void * request_context,const krb5_data * attribute,krb5_boolean * authenticated,krb5_boolean * complete,krb5_data * value,krb5_data * display_value,int * more)283 s4u2proxy_get_attribute(krb5_context kcontext,
284                         krb5_authdata_context context,
285                         void *plugin_context,
286                         void *request_context,
287                         const krb5_data *attribute,
288                         krb5_boolean *authenticated,
289                         krb5_boolean *complete,
290                         krb5_data *value,
291                         krb5_data *display_value,
292                         int *more)
293 {
294     struct s4u2proxy_context *s4uctx = (struct s4u2proxy_context *)request_context;
295     krb5_error_code code;
296     krb5_principal principal;
297     int i;
298 
299     if (display_value != NULL) {
300         display_value->data = NULL;
301         display_value->length = 0;
302     }
303 
304     if (!data_eq(*attribute, s4u2proxy_transited_services_attr))
305         return ENOENT;
306 
307     i = -(*more) - 1;
308     if (i < 0)
309         return EINVAL;
310     else if (i >= s4uctx->count)
311         return ENOENT;
312 
313     principal = s4uctx->delegated[i];
314     assert(principal != NULL);
315 
316     code = krb5_unparse_name_flags(kcontext, principal, 0, &value->data);
317     if (code != 0)
318         return code;
319 
320     value->length = strlen(value->data);
321 
322     if (display_value != NULL) {
323         code = krb5_unparse_name_flags(kcontext, principal,
324                                        KRB5_PRINCIPAL_UNPARSE_DISPLAY,
325                                        &display_value->data);
326         if (code != 0)
327             return code;
328 
329         display_value->length = strlen(display_value->data);
330     }
331 
332     i++;
333 
334     if (i == s4uctx->count)
335         *more = 0;
336     else
337         *more = -(i + 1);
338 
339     *authenticated = s4uctx->authenticated;
340     *complete = TRUE;
341 
342     return 0;
343 }
344 
345 static krb5_error_code
s4u2proxy_set_attribute(krb5_context kcontext,krb5_authdata_context context,void * plugin_context,void * request_context,krb5_boolean complete,const krb5_data * attribute,const krb5_data * value)346 s4u2proxy_set_attribute(krb5_context kcontext,
347                         krb5_authdata_context context,
348                         void *plugin_context,
349                         void *request_context,
350                         krb5_boolean complete,
351                         const krb5_data *attribute,
352                         const krb5_data *value)
353 {
354     /* Only the KDC can set this attribute. */
355     if (!data_eq(*attribute, s4u2proxy_transited_services_attr))
356         return ENOENT;
357 
358     return EPERM;
359 }
360 
361 static krb5_error_code
s4u2proxy_export_internal(krb5_context kcontext,krb5_authdata_context context,void * plugin_context,void * request_context,krb5_boolean restrict_authenticated,void ** ptr)362 s4u2proxy_export_internal(krb5_context kcontext,
363                           krb5_authdata_context context,
364                           void *plugin_context,
365                           void *request_context,
366                           krb5_boolean restrict_authenticated,
367                           void **ptr)
368 {
369     struct s4u2proxy_context *s4uctx = (struct s4u2proxy_context *)request_context;
370     krb5_error_code code;
371     int i;
372     krb5_principal *delegated;
373 
374     *ptr = NULL;
375 
376     if (s4uctx->count == 0)
377         return ENOENT;
378 
379     if (restrict_authenticated)
380         return ENOENT;
381 
382     delegated = k5calloc(s4uctx->count + 1, sizeof(krb5_principal), &code);
383     if (delegated == NULL)
384         return code;
385 
386     for (i = 0; i < s4uctx->count; i++) {
387         code = krb5_copy_principal(kcontext, s4uctx->delegated[i],
388                                    &delegated[i]);
389         if (code != 0)
390             goto cleanup;
391     }
392 
393     delegated[i] = NULL;
394 
395     *ptr = delegated;
396     delegated = NULL;
397 
398 cleanup:
399     s4u2proxy_free_internal(kcontext, context,
400                             plugin_context, request_context,
401                             delegated);
402 
403     return code;
404 }
405 
406 static krb5_error_code
s4u2proxy_size(krb5_context kcontext,krb5_authdata_context context,void * plugin_context,void * request_context,size_t * sizep)407 s4u2proxy_size(krb5_context kcontext,
408                krb5_authdata_context context,
409                void *plugin_context,
410                void *request_context,
411                size_t *sizep)
412 {
413     struct s4u2proxy_context *s4uctx = (struct s4u2proxy_context *)request_context;
414     krb5_error_code code = 0;
415     int i;
416 
417     *sizep += sizeof(krb5_int32); /* version */
418     *sizep += sizeof(krb5_int32); /* princ count */
419 
420     for (i = 0; i < s4uctx->count; i++) {
421         code = k5_size_principal(s4uctx->delegated[i], sizep);
422         if (code != 0)
423             return code;
424     }
425 
426     *sizep += sizeof(krb5_int32); /* authenticated flag */
427 
428     return code;
429 }
430 
431 static krb5_error_code
s4u2proxy_externalize(krb5_context kcontext,krb5_authdata_context context,void * plugin_context,void * request_context,krb5_octet ** buffer,size_t * lenremain)432 s4u2proxy_externalize(krb5_context kcontext,
433                       krb5_authdata_context context,
434                       void *plugin_context,
435                       void *request_context,
436                       krb5_octet **buffer,
437                       size_t *lenremain)
438 {
439     struct s4u2proxy_context *s4uctx = (struct s4u2proxy_context *)request_context;
440     krb5_error_code code = 0;
441     size_t required = 0;
442     krb5_octet *bp;
443     size_t remain;
444     int i = 0;
445 
446     bp = *buffer;
447     remain = *lenremain;
448 
449     s4u2proxy_size(kcontext, context, plugin_context,
450                    request_context, &required);
451 
452     if (required > remain)
453         return ENOMEM;
454 
455     krb5_ser_pack_int32(1, &bp, &remain); /* version */
456     krb5_ser_pack_int32(s4uctx->count, &bp, &remain); /* princ count */
457 
458     for (i = 0; i < s4uctx->count; i++) {
459         code = k5_externalize_principal(s4uctx->delegated[i], &bp, &remain);
460         if (code != 0)
461             return code;
462     }
463 
464     krb5_ser_pack_int32(s4uctx->authenticated, &bp, &remain); /* authenticated */
465 
466     *buffer = bp;
467     *lenremain = remain;
468 
469     return 0;
470 }
471 
472 static krb5_error_code
s4u2proxy_internalize(krb5_context kcontext,krb5_authdata_context context,void * plugin_context,void * request_context,krb5_octet ** buffer,size_t * lenremain)473 s4u2proxy_internalize(krb5_context kcontext,
474                       krb5_authdata_context context,
475                       void *plugin_context,
476                       void *request_context,
477                       krb5_octet **buffer,
478                       size_t *lenremain)
479 {
480     struct s4u2proxy_context *s4uctx = (struct s4u2proxy_context *)request_context;
481     krb5_error_code code;
482     krb5_int32 ibuf;
483     krb5_octet *bp;
484     size_t remain;
485     int count;
486     krb5_principal *delegated = NULL;
487 
488     bp = *buffer;
489     remain = *lenremain;
490 
491     /* version */
492     code = krb5_ser_unpack_int32(&ibuf, &bp, &remain);
493     if (code != 0)
494         goto cleanup;
495 
496     if (ibuf != 1) {
497         code = EINVAL;
498         goto cleanup;
499     }
500 
501     /* count */
502     code = krb5_ser_unpack_int32(&count, &bp, &remain);
503     if (code != 0)
504         goto cleanup;
505 
506     if (count > 65535)
507         return ERANGE; /* let's set some reasonable limits here */
508     else if (count > 0) {
509         int i;
510 
511         delegated = k5calloc(count + 1, sizeof(krb5_principal), &code);
512         if (delegated == NULL)
513             goto cleanup;
514 
515         for (i = 0; i < count; i++) {
516             code = k5_internalize_principal(&delegated[i], &bp, &remain);
517             if (code != 0)
518                 goto cleanup;
519         }
520 
521         delegated[i] = NULL;
522     }
523 
524     code = krb5_ser_unpack_int32(&ibuf, &bp, &remain);
525     if (code != 0)
526         goto cleanup;
527 
528     s4u2proxy_free_internal(kcontext, context,
529                             plugin_context, request_context,
530                             s4uctx->delegated);
531 
532     s4uctx->count = count;
533     s4uctx->delegated = delegated;
534     s4uctx->authenticated = (ibuf != 0);
535 
536     delegated = NULL;
537 
538     *buffer = bp;
539     *lenremain = remain;
540 
541 cleanup:
542     s4u2proxy_free_internal(kcontext, context,
543                             plugin_context, request_context,
544                             delegated);
545 
546     return code;
547 }
548 
549 static krb5_error_code
s4u2proxy_copy(krb5_context kcontext,krb5_authdata_context context,void * plugin_context,void * request_context,void * dst_plugin_context,void * dst_request_context)550 s4u2proxy_copy(krb5_context kcontext,
551                krb5_authdata_context context,
552                void *plugin_context,
553                void *request_context,
554                void *dst_plugin_context,
555                void *dst_request_context)
556 {
557     struct s4u2proxy_context *srcctx = (struct s4u2proxy_context *)request_context;
558     struct s4u2proxy_context *dstctx = (struct s4u2proxy_context *)dst_request_context;
559     krb5_error_code code;
560 
561     code = s4u2proxy_export_internal(kcontext, context,
562                                      plugin_context, request_context,
563                                      FALSE, (void **)&dstctx->delegated);
564     if (code != 0 && code != ENOENT)
565         return code;
566 
567     dstctx->count = srcctx->count;
568     dstctx->authenticated = srcctx->authenticated;
569 
570     return 0;
571 }
572 
573 static krb5_authdatatype s4u2proxy_ad_types[] = { KRB5_AUTHDATA_SIGNTICKET, 0 };
574 
575 krb5plugin_authdata_client_ftable_v0 k5_s4u2proxy_ad_client_ftable = {
576     "constrained-delegation",
577     s4u2proxy_ad_types,
578     s4u2proxy_init,
579     s4u2proxy_fini,
580     s4u2proxy_flags,
581     s4u2proxy_request_init,
582     s4u2proxy_request_fini,
583     s4u2proxy_get_attribute_types,
584     s4u2proxy_get_attribute,
585     s4u2proxy_set_attribute,
586     NULL, /* delete_attribute_proc */
587     s4u2proxy_export_authdata,
588     s4u2proxy_import_authdata,
589     s4u2proxy_export_internal,
590     s4u2proxy_free_internal,
591     s4u2proxy_verify,
592     s4u2proxy_size,
593     s4u2proxy_externalize,
594     s4u2proxy_internalize,
595     s4u2proxy_copy
596 };
597