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