xref: /freebsd/crypto/krb5/src/lib/gssapi/krb5/naming_exts.c (revision 7f2fe78b9dd5f51c821d771b63d2e096f6fd49e9)
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