1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* lib/gssapi/krb5/naming_exts.c */
3 /*
4 * Copyright 2009 by the Massachusetts Institute of Technology.
5 * All Rights Reserved.
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 "gssapiP_krb5.h"
29
30 krb5_error_code
kg_init_name(krb5_context context,krb5_principal principal,char * service,char * host,krb5_authdata_context ad_context,krb5_flags flags,krb5_gss_name_t * ret_name)31 kg_init_name(krb5_context context, krb5_principal principal,
32 char *service, char *host, krb5_authdata_context ad_context,
33 krb5_flags flags, krb5_gss_name_t *ret_name)
34 {
35 krb5_error_code code;
36 krb5_gss_name_t name;
37
38 *ret_name = NULL;
39
40 assert(principal != NULL);
41
42 if (principal == NULL)
43 return EINVAL;
44
45 name = xmalloc(sizeof(krb5_gss_name_rec));
46 if (name == NULL)
47 return ENOMEM;
48
49 memset(name, 0, sizeof(krb5_gss_name_rec));
50
51 code = k5_mutex_init(&name->lock);
52 if (code != 0)
53 goto cleanup;
54
55 if ((flags & KG_INIT_NAME_NO_COPY) == 0) {
56 code = krb5_copy_principal(context, principal, &name->princ);
57 if (code != 0)
58 goto cleanup;
59
60 if (ad_context != NULL) {
61 code = krb5_authdata_context_copy(context,
62 ad_context,
63 &name->ad_context);
64 if (code != 0)
65 goto cleanup;
66 }
67
68 code = ENOMEM;
69 if (service != NULL) {
70 name->service = strdup(service);
71 if (name->service == NULL)
72 goto cleanup;
73 }
74 if (host != NULL) {
75 name->host = strdup(host);
76 if (name->host == NULL)
77 goto cleanup;
78 }
79 code = 0;
80 } else {
81 name->princ = principal;
82 name->service = service;
83 name->host = host;
84 name->ad_context = ad_context;
85 }
86
87 *ret_name = name;
88
89 cleanup:
90 if (code != 0)
91 kg_release_name(context, &name);
92
93 return code;
94 }
95
96 krb5_error_code
kg_release_name(krb5_context context,krb5_gss_name_t * name)97 kg_release_name(krb5_context context,
98 krb5_gss_name_t *name)
99 {
100 if (*name != NULL) {
101 krb5_free_principal(context, (*name)->princ);
102 free((*name)->service);
103 free((*name)->host);
104 krb5_authdata_context_free(context, (*name)->ad_context);
105 k5_mutex_destroy(&(*name)->lock);
106 free(*name);
107 *name = NULL;
108 }
109
110 return 0;
111 }
112
113 krb5_error_code
kg_duplicate_name(krb5_context context,const krb5_gss_name_t src,krb5_gss_name_t * dst)114 kg_duplicate_name(krb5_context context,
115 const krb5_gss_name_t src,
116 krb5_gss_name_t *dst)
117 {
118 krb5_error_code code;
119
120 k5_mutex_lock(&src->lock);
121 code = kg_init_name(context, src->princ, src->service, src->host,
122 src->ad_context, 0, dst);
123 k5_mutex_unlock(&src->lock);
124 return code;
125 }
126
127
128 krb5_boolean
kg_compare_name(krb5_context context,krb5_gss_name_t name1,krb5_gss_name_t name2)129 kg_compare_name(krb5_context context,
130 krb5_gss_name_t name1,
131 krb5_gss_name_t name2)
132 {
133 return krb5_principal_compare(context, name1->princ, name2->princ);
134 }
135
136 /* Determine the principal to use for an acceptor name, which is different from
137 * name->princ for host-based names. */
138 krb5_boolean
kg_acceptor_princ(krb5_context context,krb5_gss_name_t name,krb5_principal * princ_out)139 kg_acceptor_princ(krb5_context context, krb5_gss_name_t name,
140 krb5_principal *princ_out)
141 {
142 krb5_error_code code;
143 const char *host;
144 char *tmp = NULL;
145
146 *princ_out = NULL;
147 if (name == NULL)
148 return 0;
149
150 /* If it's not a host-based name, just copy name->princ. */
151 if (name->service == NULL)
152 return krb5_copy_principal(context, name->princ, princ_out);
153
154 if (name->host != NULL && name->princ->length == 2) {
155 /* If a host was given, we have to use the canonicalized form of it (as
156 * given by krb5_sname_to_principal) for backward compatibility. */
157 const krb5_data *d = &name->princ->data[1];
158 tmp = k5memdup0(d->data, d->length, &code);
159 if (tmp == NULL)
160 return ENOMEM;
161 host = tmp;
162 } else /* No host was given; use an empty string. */
163 host = "";
164
165 code = krb5_build_principal(context, princ_out, 0, "", name->service, host,
166 (char *)NULL);
167 if (*princ_out != NULL)
168 (*princ_out)->type = KRB5_NT_SRV_HST;
169 free(tmp);
170 return code;
171 }
172
173 static OM_uint32
kg_map_name_error(OM_uint32 * minor_status,krb5_error_code code)174 kg_map_name_error(OM_uint32 *minor_status, krb5_error_code code)
175 {
176 OM_uint32 major_status;
177
178 switch (code) {
179 case 0:
180 major_status = GSS_S_COMPLETE;
181 break;
182 case ENOENT:
183 case EPERM:
184 major_status = GSS_S_UNAVAILABLE;
185 break;
186 default:
187 major_status = GSS_S_FAILURE;
188 break;
189 }
190
191 *minor_status = code;
192
193 return major_status;
194 }
195
196 /* Owns data on success */
197 static krb5_error_code
data_list_to_buffer_set(krb5_context context,krb5_data * data,gss_buffer_set_t * buffer_set)198 data_list_to_buffer_set(krb5_context context,
199 krb5_data *data,
200 gss_buffer_set_t *buffer_set)
201 {
202 gss_buffer_set_t set = GSS_C_NO_BUFFER_SET;
203 OM_uint32 minor_status;
204 int i;
205 krb5_error_code code = 0;
206
207 if (data == NULL)
208 goto cleanup;
209
210 if (buffer_set == NULL)
211 goto cleanup;
212
213 if (GSS_ERROR(gss_create_empty_buffer_set(&minor_status,
214 &set))) {
215 assert(minor_status != 0);
216 code = minor_status;
217 goto cleanup;
218 }
219
220 for (i = 0; data[i].data != NULL; i++)
221 ;
222
223 set->count = i;
224 set->elements = gssalloc_calloc(i, sizeof(gss_buffer_desc));
225 if (set->elements == NULL) {
226 gss_release_buffer_set(&minor_status, &set);
227 code = ENOMEM;
228 goto cleanup;
229 }
230
231 /*
232 * Copy last element first so data remains properly
233 * NULL-terminated in case of allocation failure
234 * in data_to_gss() on windows.
235 */
236 for (i = set->count-1; i >= 0; i--) {
237 if (data_to_gss(&data[i], &set->elements[i])) {
238 gss_release_buffer_set(&minor_status, &set);
239 code = ENOMEM;
240 goto cleanup;
241 }
242 }
243 cleanup:
244 krb5int_free_data_list(context, data);
245
246 if (buffer_set != NULL)
247 *buffer_set = set;
248
249 return code;
250 }
251
252 OM_uint32 KRB5_CALLCONV
krb5_gss_inquire_name(OM_uint32 * minor_status,gss_name_t name,int * name_is_MN,gss_OID * MN_mech,gss_buffer_set_t * attrs)253 krb5_gss_inquire_name(OM_uint32 *minor_status,
254 gss_name_t name,
255 int *name_is_MN,
256 gss_OID *MN_mech,
257 gss_buffer_set_t *attrs)
258 {
259 krb5_context context;
260 krb5_error_code code;
261 krb5_gss_name_t kname;
262 krb5_data *kattrs = NULL;
263
264 *minor_status = 0;
265
266 if (attrs != NULL)
267 *attrs = GSS_C_NO_BUFFER_SET;
268
269 code = krb5_gss_init_context(&context);
270 if (code != 0) {
271 *minor_status = code;
272 return GSS_S_FAILURE;
273 }
274
275 kname = (krb5_gss_name_t)name;
276
277 k5_mutex_lock(&kname->lock);
278
279 if (kname->ad_context == NULL) {
280 code = krb5_authdata_context_init(context, &kname->ad_context);
281 if (code != 0)
282 goto cleanup;
283 }
284
285 code = krb5_authdata_get_attribute_types(context,
286 kname->ad_context,
287 &kattrs);
288 if (code != 0)
289 goto cleanup;
290
291 code = data_list_to_buffer_set(context, kattrs, attrs);
292 kattrs = NULL;
293 if (code != 0)
294 goto cleanup;
295
296 cleanup:
297 k5_mutex_unlock(&kname->lock);
298 krb5int_free_data_list(context, kattrs);
299
300 krb5_free_context(context);
301
302 return kg_map_name_error(minor_status, code);
303 }
304
305 OM_uint32 KRB5_CALLCONV
krb5_gss_get_name_attribute(OM_uint32 * minor_status,gss_name_t name,gss_buffer_t attr,int * authenticated,int * complete,gss_buffer_t value,gss_buffer_t display_value,int * more)306 krb5_gss_get_name_attribute(OM_uint32 *minor_status,
307 gss_name_t name,
308 gss_buffer_t attr,
309 int *authenticated,
310 int *complete,
311 gss_buffer_t value,
312 gss_buffer_t display_value,
313 int *more)
314 {
315 krb5_context context;
316 krb5_error_code code;
317 krb5_gss_name_t kname;
318 krb5_data kattr;
319 krb5_boolean kauthenticated;
320 krb5_boolean kcomplete;
321 krb5_data kvalue = empty_data();
322 krb5_data kdisplay_value = empty_data();
323
324 *minor_status = 0;
325
326 code = krb5_gss_init_context(&context);
327 if (code != 0) {
328 *minor_status = code;
329 return GSS_S_FAILURE;
330 }
331
332 kname = (krb5_gss_name_t)name;
333 k5_mutex_lock(&kname->lock);
334
335 if (kname->ad_context == NULL) {
336 code = krb5_authdata_context_init(context, &kname->ad_context);
337 if (code != 0) {
338 *minor_status = code;
339 k5_mutex_unlock(&kname->lock);
340 krb5_free_context(context);
341 return GSS_S_UNAVAILABLE;
342 }
343 }
344
345 kattr.data = (char *)attr->value;
346 kattr.length = attr->length;
347
348 kauthenticated = FALSE;
349 kcomplete = FALSE;
350
351 code = krb5_authdata_get_attribute(context,
352 kname->ad_context,
353 &kattr,
354 &kauthenticated,
355 &kcomplete,
356 &kvalue,
357 &kdisplay_value,
358 more);
359 if (code == 0) {
360 if (value != NULL)
361 code = data_to_gss(&kvalue, value);
362
363 if (authenticated != NULL)
364 *authenticated = kauthenticated;
365 if (complete != NULL)
366 *complete = kcomplete;
367
368 if (display_value != NULL && code == 0)
369 code = data_to_gss(&kdisplay_value, display_value);
370 }
371
372 free(kdisplay_value.data);
373 free(kvalue.data);
374
375 k5_mutex_unlock(&kname->lock);
376 krb5_free_context(context);
377
378 return kg_map_name_error(minor_status, code);
379 }
380
381 OM_uint32 KRB5_CALLCONV
krb5_gss_set_name_attribute(OM_uint32 * minor_status,gss_name_t name,int complete,gss_buffer_t attr,gss_buffer_t value)382 krb5_gss_set_name_attribute(OM_uint32 *minor_status,
383 gss_name_t name,
384 int complete,
385 gss_buffer_t attr,
386 gss_buffer_t value)
387 {
388 krb5_context context;
389 krb5_error_code code;
390 krb5_gss_name_t kname;
391 krb5_data kattr;
392 krb5_data kvalue;
393
394 *minor_status = 0;
395
396 code = krb5_gss_init_context(&context);
397 if (code != 0) {
398 *minor_status = code;
399 return GSS_S_FAILURE;
400 }
401
402 kname = (krb5_gss_name_t)name;
403 k5_mutex_lock(&kname->lock);
404
405 if (kname->ad_context == NULL) {
406 code = krb5_authdata_context_init(context, &kname->ad_context);
407 if (code != 0) {
408 *minor_status = code;
409 k5_mutex_unlock(&kname->lock);
410 krb5_free_context(context);
411 return GSS_S_UNAVAILABLE;
412 }
413 }
414
415 kattr.data = (char *)attr->value;
416 kattr.length = attr->length;
417
418 kvalue.data = (char *)value->value;
419 kvalue.length = value->length;
420
421 code = krb5_authdata_set_attribute(context,
422 kname->ad_context,
423 complete,
424 &kattr,
425 &kvalue);
426
427 k5_mutex_unlock(&kname->lock);
428 krb5_free_context(context);
429
430 return kg_map_name_error(minor_status, code);
431 }
432
433 OM_uint32 KRB5_CALLCONV
krb5_gss_delete_name_attribute(OM_uint32 * minor_status,gss_name_t name,gss_buffer_t attr)434 krb5_gss_delete_name_attribute(OM_uint32 *minor_status,
435 gss_name_t name,
436 gss_buffer_t attr)
437 {
438 krb5_context context;
439 krb5_error_code code;
440 krb5_gss_name_t kname;
441 krb5_data kattr;
442
443 *minor_status = 0;
444
445 code = krb5_gss_init_context(&context);
446 if (code != 0) {
447 *minor_status = code;
448 return GSS_S_FAILURE;
449 }
450
451 kname = (krb5_gss_name_t)name;
452 k5_mutex_lock(&kname->lock);
453
454 if (kname->ad_context == NULL) {
455 code = krb5_authdata_context_init(context, &kname->ad_context);
456 if (code != 0) {
457 *minor_status = code;
458 k5_mutex_unlock(&kname->lock);
459 krb5_free_context(context);
460 return GSS_S_UNAVAILABLE;
461 }
462 }
463
464 kattr.data = (char *)attr->value;
465 kattr.length = attr->length;
466
467 code = krb5_authdata_delete_attribute(context,
468 kname->ad_context,
469 &kattr);
470
471 k5_mutex_unlock(&kname->lock);
472 krb5_free_context(context);
473
474 return kg_map_name_error(minor_status, code);
475 }
476
477 OM_uint32 KRB5_CALLCONV
krb5_gss_map_name_to_any(OM_uint32 * minor_status,gss_name_t name,int authenticated,gss_buffer_t type_id,gss_any_t * output)478 krb5_gss_map_name_to_any(OM_uint32 *minor_status,
479 gss_name_t name,
480 int authenticated,
481 gss_buffer_t type_id,
482 gss_any_t *output)
483 {
484 krb5_context context;
485 krb5_error_code code;
486 krb5_gss_name_t kname;
487 char *kmodule;
488
489 *minor_status = 0;
490
491 code = krb5_gss_init_context(&context);
492 if (code != 0) {
493 *minor_status = code;
494 return GSS_S_FAILURE;
495 }
496
497 kname = (krb5_gss_name_t)name;
498 k5_mutex_lock(&kname->lock);
499
500 if (kname->ad_context == NULL) {
501 code = krb5_authdata_context_init(context, &kname->ad_context);
502 if (code != 0) {
503 *minor_status = code;
504 k5_mutex_unlock(&kname->lock);
505 krb5_free_context(context);
506 return GSS_S_UNAVAILABLE;
507 }
508 }
509
510 kmodule = (char *)type_id->value;
511 if (kmodule[type_id->length] != '\0') {
512 k5_mutex_unlock(&kname->lock);
513 krb5_free_context(context);
514 return GSS_S_UNAVAILABLE;
515 }
516
517 code = krb5_authdata_export_internal(context,
518 kname->ad_context,
519 authenticated,
520 kmodule,
521 (void **)output);
522
523 k5_mutex_unlock(&kname->lock);
524 krb5_free_context(context);
525
526 return kg_map_name_error(minor_status, code);
527 }
528
529 OM_uint32 KRB5_CALLCONV
krb5_gss_release_any_name_mapping(OM_uint32 * minor_status,gss_name_t name,gss_buffer_t type_id,gss_any_t * input)530 krb5_gss_release_any_name_mapping(OM_uint32 *minor_status,
531 gss_name_t name,
532 gss_buffer_t type_id,
533 gss_any_t *input)
534 {
535 krb5_context context;
536 krb5_error_code code;
537 krb5_gss_name_t kname;
538 char *kmodule;
539
540 *minor_status = 0;
541
542 code = krb5_gss_init_context(&context);
543 if (code != 0) {
544 *minor_status = code;
545 return GSS_S_FAILURE;
546 }
547
548 kname = (krb5_gss_name_t)name;
549 k5_mutex_lock(&kname->lock);
550
551 if (kname->ad_context == NULL) {
552 code = krb5_authdata_context_init(context, &kname->ad_context);
553 if (code != 0) {
554 *minor_status = code;
555 k5_mutex_unlock(&kname->lock);
556 krb5_free_context(context);
557 return GSS_S_UNAVAILABLE;
558 }
559 }
560
561 kmodule = (char *)type_id->value;
562 if (kmodule[type_id->length] != '\0') {
563 k5_mutex_unlock(&kname->lock);
564 krb5_free_context(context);
565 return GSS_S_UNAVAILABLE;
566 }
567
568 code = krb5_authdata_free_internal(context,
569 kname->ad_context,
570 kmodule,
571 *input);
572 if (code == 0)
573 *input = NULL;
574
575 k5_mutex_unlock(&kname->lock);
576 krb5_free_context(context);
577
578 return kg_map_name_error(minor_status, code);
579
580 }
581
582 OM_uint32 KRB5_CALLCONV
krb5_gss_export_name_composite(OM_uint32 * minor_status,gss_name_t name,gss_buffer_t exp_composite_name)583 krb5_gss_export_name_composite(OM_uint32 *minor_status,
584 gss_name_t name,
585 gss_buffer_t exp_composite_name)
586 {
587 krb5_context context;
588 krb5_error_code code;
589 krb5_gss_name_t kname;
590 krb5_data *attrs = NULL;
591 char *princstr = NULL;
592 unsigned char *cp;
593 size_t princlen;
594
595 *minor_status = 0;
596
597 code = krb5_gss_init_context(&context);
598 if (code != 0) {
599 *minor_status = code;
600 return GSS_S_FAILURE;
601 }
602
603 kname = (krb5_gss_name_t)name;
604 k5_mutex_lock(&kname->lock);
605
606 code = krb5_unparse_name(context, kname->princ, &princstr);
607 if (code != 0)
608 goto cleanup;
609
610 princlen = strlen(princstr);
611
612 if (kname->ad_context != NULL) {
613 code = krb5_authdata_export_attributes(context,
614 kname->ad_context,
615 AD_USAGE_MASK,
616 &attrs);
617 if (code != 0)
618 goto cleanup;
619 }
620
621 /* 04 02 OID Name AuthData */
622
623 exp_composite_name->length = 10 + gss_mech_krb5->length + princlen;
624 exp_composite_name->length += 4; /* length of encoded attributes */
625 if (attrs != NULL)
626 exp_composite_name->length += attrs->length;
627 exp_composite_name->value = gssalloc_malloc(exp_composite_name->length);
628 if (exp_composite_name->value == NULL) {
629 code = ENOMEM;
630 goto cleanup;
631 }
632
633 cp = exp_composite_name->value;
634
635 /* Note: we assume the OID will be less than 128 bytes... */
636 *cp++ = 0x04;
637 *cp++ = 0x02;
638
639 store_16_be(gss_mech_krb5->length + 2, cp);
640 cp += 2;
641 *cp++ = 0x06;
642 *cp++ = (gss_mech_krb5->length) & 0xFF;
643 memcpy(cp, gss_mech_krb5->elements, gss_mech_krb5->length);
644 cp += gss_mech_krb5->length;
645
646 store_32_be(princlen, cp);
647 cp += 4;
648 memcpy(cp, princstr, princlen);
649 cp += princlen;
650
651 store_32_be(attrs != NULL ? attrs->length : 0, cp);
652 cp += 4;
653
654 if (attrs != NULL) {
655 memcpy(cp, attrs->data, attrs->length);
656 cp += attrs->length;
657 }
658
659 cleanup:
660 krb5_free_unparsed_name(context, princstr);
661 krb5_free_data(context, attrs);
662 k5_mutex_unlock(&kname->lock);
663 krb5_free_context(context);
664
665 return kg_map_name_error(minor_status, code);
666 }
667