9bceb3b87aa283a7a3c8170988449d718fa8fcdc
[mech_eap.orig] / util_name.c
1 /*
2  * Copyright (c) 2010, JANET(UK)
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  *
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * 3. Neither the name of JANET(UK) nor the names of its contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  */
32 /*
33  * Portions Copyright 2009 by the Massachusetts Institute of Technology.
34  * All Rights Reserved.
35  *
36  * Export of this software from the United States of America may
37  *   require a specific license from the United States Government.
38  *   It is the responsibility of any person or organization contemplating
39  *   export to obtain such a license before exporting.
40  *
41  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
42  * distribute this software and its documentation for any purpose and
43  * without fee is hereby granted, provided that the above copyright
44  * notice appear in all copies and that both that copyright notice and
45  * this permission notice appear in supporting documentation, and that
46  * the name of M.I.T. not be used in advertising or publicity pertaining
47  * to distribution of the software without specific, written prior
48  * permission.  Furthermore if you modify this software you must label
49  * your software as modified software and not distribute it in such a
50  * fashion that it might be confused with the original M.I.T. software.
51  * M.I.T. makes no representations about the suitability of
52  * this software for any purpose.  It is provided "as is" without express
53  * or implied warranty.
54  */
55
56 /*
57  * Name utility routines.
58  */
59
60 #include "gssapiP_eap.h"
61
62 static gss_OID_desc gssEapNtPrincipalName = {
63     /* 1.3.6.1.4.1.5322.22.2.1  */
64     10, "\x2B\x06\x01\x04\x01\xA9\x4A\x16\x02\x01"
65 };
66
67 gss_OID GSS_EAP_NT_PRINCIPAL_NAME = &gssEapNtPrincipalName;
68
69 OM_uint32
70 gssEapAllocName(OM_uint32 *minor, gss_name_t *pName)
71 {
72     OM_uint32 tmpMinor;
73     gss_name_t name;
74
75     *pName = GSS_C_NO_NAME;
76
77     name = (gss_name_t)GSSEAP_CALLOC(1, sizeof(*name));
78     if (name == NULL) {
79         *minor = ENOMEM;
80         return GSS_S_FAILURE;
81     }
82
83     if (GSSEAP_MUTEX_INIT(&name->mutex) != 0) {
84         *minor = errno;
85         gssEapReleaseName(&tmpMinor, &name);
86         return GSS_S_FAILURE;
87     }
88
89     *pName = name;
90
91     return GSS_S_COMPLETE;
92 }
93
94 OM_uint32
95 gssEapReleaseName(OM_uint32 *minor, gss_name_t *pName)
96 {
97     gss_name_t name;
98     krb5_context krbContext = NULL;
99     OM_uint32 tmpMinor;
100
101     *minor = 0;
102
103     if (pName == NULL) {
104         return GSS_S_COMPLETE;
105     }
106
107     name = *pName;
108     if (name == GSS_C_NO_NAME) {
109         return GSS_S_COMPLETE;
110     }
111
112     GSSEAP_KRB_INIT(&krbContext);
113     krb5_free_principal(krbContext, name->krbPrincipal);
114
115     gssEapReleaseAttrContext(&tmpMinor, name);
116
117     GSSEAP_MUTEX_DESTROY(&name->mutex);
118     GSSEAP_FREE(name);
119     *pName = NULL;
120
121     return GSS_S_COMPLETE;
122 }
123
124 static OM_uint32
125 krbPrincipalToName(OM_uint32 *minor,
126                    krb5_principal *principal,
127                    gss_name_t *pName)
128 {
129     OM_uint32 major;
130     gss_name_t name;
131
132     major = gssEapAllocName(minor, &name);
133     if (GSS_ERROR(major))
134         return major;
135
136     name->krbPrincipal = *principal;
137     *principal = NULL;
138
139     if (name->krbPrincipal->length > 1) {
140         name->flags |= NAME_FLAG_SERVICE;
141     } else {
142         name->flags |= NAME_FLAG_NAI;
143     }
144
145     *pName = name;
146     *minor = 0;
147
148     return GSS_S_COMPLETE;
149 }
150
151 static OM_uint32
152 importServiceName(OM_uint32 *minor,
153                   const gss_buffer_t nameBuffer,
154                   gss_name_t *pName)
155 {
156     OM_uint32 major;
157     krb5_context krbContext;
158     krb5_principal krbPrinc;
159     char *service, *host;
160
161     GSSEAP_KRB_INIT(&krbContext);
162
163     major = bufferToString(minor, nameBuffer, &service);
164     if (GSS_ERROR(major))
165         return major;
166
167     host = strchr(service, '@');
168     if (host != NULL) {
169         *host = '\0';
170         host++;
171     }
172
173     /* XXX this is probably NOT what we want to be doing */
174     if (krb5_sname_to_principal(krbContext, host, service,
175                                 KRB5_NT_SRV_HST, &krbPrinc) != 0) {
176         GSSEAP_FREE(service);
177         *minor = GSSEAP_BAD_SERVICE_NAME;
178         return GSS_S_FAILURE;
179     }
180
181     major = krbPrincipalToName(minor, &krbPrinc, pName);
182     if (GSS_ERROR(major)) {
183         krb5_free_principal(krbContext, krbPrinc);
184     }
185
186     GSSEAP_FREE(service);
187     return major;
188 }
189
190 static OM_uint32
191 importUserName(OM_uint32 *minor,
192                const gss_buffer_t nameBuffer,
193                gss_name_t *pName)
194 {
195     OM_uint32 major;
196     krb5_context krbContext;
197     krb5_principal krbPrinc;
198     char *nameString;
199
200     GSSEAP_KRB_INIT(&krbContext);
201
202     if (nameBuffer == GSS_C_NO_BUFFER) {
203         *minor = krb5_copy_principal(krbContext,
204                                      krb5_anonymous_principal(), &krbPrinc);
205         if (*minor != 0)
206             return GSS_S_FAILURE;
207     } else {
208         major = bufferToString(minor, nameBuffer, &nameString);
209         if (GSS_ERROR(major))
210             return major;
211
212         *minor = krb5_parse_name(krbContext, nameString, &krbPrinc);
213         if (*minor != 0) {
214             GSSEAP_FREE(nameString);
215             return GSS_S_FAILURE;
216         }
217     }
218
219     major = krbPrincipalToName(minor, &krbPrinc, pName);
220     if (GSS_ERROR(major)) {
221         krb5_free_principal(krbContext, krbPrinc);
222     }
223
224     GSSEAP_FREE(nameString);
225     return major;
226 }
227
228 #define UPDATE_REMAIN(n)    do {            \
229         p += (n);                           \
230         remain -= (n);                      \
231     } while (0)
232
233 #define CHECK_REMAIN(n)     do {        \
234         if (remain < (n)) {             \
235             major = GSS_S_BAD_NAME;     \
236             *minor = GSSEAP_TOK_TRUNC;  \
237             goto cleanup;               \
238         }                               \
239     } while (0)
240
241 OM_uint32
242 gssEapImportNameInternal(OM_uint32 *minor,
243                          const gss_buffer_t nameBuffer,
244                          gss_name_t *pName,
245                          unsigned int flags)
246 {
247     OM_uint32 major, tmpMinor;
248     krb5_context krbContext;
249     unsigned char *p;
250     size_t len, remain;
251     gss_buffer_desc buf;
252     enum gss_eap_token_type tokType;
253     gss_name_t name = GSS_C_NO_NAME;
254
255     GSSEAP_KRB_INIT(&krbContext);
256
257     p = (unsigned char *)nameBuffer->value;
258     remain = nameBuffer->length;
259
260     if (flags & EXPORT_NAME_FLAG_OID) {
261         if (remain < 6 + GSS_EAP_MECHANISM->length + 4)
262             return GSS_S_BAD_NAME;
263
264         if (flags & EXPORT_NAME_FLAG_COMPOSITE)
265             tokType = TOK_TYPE_EXPORT_NAME_COMPOSITE;
266         else
267             tokType = TOK_TYPE_EXPORT_NAME;
268
269         /* TOK_ID */
270         if (load_uint16_be(p) != tokType)
271             return GSS_S_BAD_NAME;
272         UPDATE_REMAIN(2);
273
274         /* MECH_OID_LEN */
275         len = load_uint16_be(p);
276         if (len != 2 + GSS_EAP_MECHANISM->length)
277             return GSS_S_BAD_NAME;
278         UPDATE_REMAIN(2);
279
280         /* MECH_OID */
281         if (p[0] != 0x06)
282             return GSS_S_BAD_NAME;
283         if (p[1] != GSS_EAP_MECHANISM->length)
284             return GSS_S_BAD_MECH;
285         if (memcmp(&p[2], GSS_EAP_MECHANISM->elements, GSS_EAP_MECHANISM->length))
286             return GSS_S_BAD_MECH;
287         UPDATE_REMAIN(2 + GSS_EAP_MECHANISM->length);
288     }
289
290     /* NAME_LEN */
291     len = load_uint32_be(p);
292     UPDATE_REMAIN(4);
293
294     /* NAME */
295     CHECK_REMAIN(len);
296     buf.length = len;
297     buf.value = p;
298     UPDATE_REMAIN(len);
299
300     major = importUserName(minor, &buf, &name);
301     if (GSS_ERROR(major))
302         goto cleanup;
303
304     if (flags & EXPORT_NAME_FLAG_COMPOSITE) {
305         gss_buffer_desc buf;
306
307         buf.length = remain;
308         buf.value = p;
309
310         major = gssEapImportAttrContext(minor, &buf, name);
311         if (GSS_ERROR(major))
312             goto cleanup;
313     }
314
315     major = GSS_S_COMPLETE;
316     *minor = 0;
317
318 cleanup:
319     if (GSS_ERROR(major))
320         gssEapReleaseName(&tmpMinor, &name);
321     else
322         *pName = name;
323
324     return major;
325 }
326
327 static OM_uint32
328 importExportName(OM_uint32 *minor,
329                  const gss_buffer_t nameBuffer,
330                  gss_name_t *name)
331 {
332     return gssEapImportNameInternal(minor, nameBuffer, name,
333                                     EXPORT_NAME_FLAG_OID);
334 }
335
336 #ifdef HAVE_GSS_C_NT_COMPOSITE_EXPORT
337 static OM_uint32
338 importCompositeExportName(OM_uint32 *minor,
339                           const gss_buffer_t nameBuffer,
340                           gss_name_t *name)
341 {
342     return gssEapImportNameInternal(minor, nameBuffer, name,
343                                     EXPORT_NAME_FLAG_OID |
344                                     EXPORT_NAME_FLAG_COMPOSITE);
345 }
346 #endif
347
348 struct gss_eap_name_import_provider {
349     gss_OID oid;
350     OM_uint32 (*import)(OM_uint32 *, const gss_buffer_t, gss_name_t *);
351 };
352
353 OM_uint32
354 gssEapImportName(OM_uint32 *minor,
355                  const gss_buffer_t nameBuffer,
356                  gss_OID nameType,
357                  gss_name_t *name)
358 {
359     struct gss_eap_name_import_provider nameTypes[] = {
360         { GSS_C_NT_USER_NAME,               importUserName              },
361         { GSS_EAP_NT_PRINCIPAL_NAME,        importUserName              },
362         { GSS_C_NT_HOSTBASED_SERVICE,       importServiceName           },
363         { GSS_C_NT_HOSTBASED_SERVICE_X,     importServiceName           },
364         { GSS_C_NT_EXPORT_NAME,             importExportName            },
365 #ifdef HAVE_GSS_C_NT_COMPOSITE_EXPORT
366         { GSS_C_NT_COMPOSITE_EXPORT,        importCompositeExportName   },
367 #endif
368     };
369     size_t i;
370
371     *name = GSS_C_NO_NAME;
372
373     if (nameType == GSS_C_NO_OID)
374         nameType = nameTypes[0].oid;
375
376     for (i = 0; i < sizeof(nameTypes) / sizeof(nameTypes[0]); i++) {
377         if (oidEqual(nameTypes[i].oid, nameType))
378             return nameTypes[i].import(minor, nameBuffer, name);
379     }
380
381     return GSS_S_BAD_NAMETYPE;
382 }
383
384 OM_uint32
385 gssEapExportName(OM_uint32 *minor,
386                  const gss_name_t name,
387                  gss_buffer_t exportedName)
388 {
389     return gssEapExportNameInternal(minor, name, exportedName,
390                                     EXPORT_NAME_FLAG_OID);
391 }
392
393 OM_uint32
394 gssEapExportNameInternal(OM_uint32 *minor,
395                          const gss_name_t name,
396                          gss_buffer_t exportedName,
397                          unsigned int flags)
398 {
399     OM_uint32 major = GSS_S_FAILURE, tmpMinor;
400     krb5_context krbContext;
401     char *krbName = NULL;
402     size_t krbNameLen, exportedNameLen;
403     unsigned char *p;
404     gss_buffer_desc attrs = GSS_C_EMPTY_BUFFER;
405
406     exportedName->length = 0;
407     exportedName->value = NULL;
408
409     GSSEAP_KRB_INIT(&krbContext);
410
411     *minor = krb5_unparse_name(krbContext, name->krbPrincipal, &krbName);
412     if (*minor != 0) {
413         major = GSS_S_FAILURE;
414         goto cleanup;
415     }
416     krbNameLen = strlen(krbName);
417
418     exportedNameLen = 0;
419     if (flags & EXPORT_NAME_FLAG_OID) {
420         exportedNameLen += 6 + GSS_EAP_MECHANISM->length;
421     }
422     exportedNameLen += 4 + krbNameLen;
423     if (flags & EXPORT_NAME_FLAG_COMPOSITE) {
424         major = gssEapExportAttrContext(minor, name, &attrs);
425         if (GSS_ERROR(major))
426             goto cleanup;
427         exportedNameLen += attrs.length;
428     }
429
430     exportedName->value = GSSEAP_MALLOC(exportedNameLen);
431     if (exportedName->value == NULL) {
432         major = GSS_S_FAILURE;
433         *minor = ENOMEM;
434         goto cleanup;
435     }
436     exportedName->length = exportedNameLen;
437
438     p = (unsigned char *)exportedName->value;
439
440     if (flags & EXPORT_NAME_FLAG_OID) {
441         /* TOK | MECH_OID_LEN */
442         store_uint16_be((flags & EXPORT_NAME_FLAG_COMPOSITE)
443                         ? TOK_TYPE_EXPORT_NAME_COMPOSITE
444                         : TOK_TYPE_EXPORT_NAME,
445                         p);
446         p += 2;
447         store_uint16_be(GSS_EAP_MECHANISM->length + 2, p);
448         p += 2;
449
450         /* MECH_OID */
451         *p++ = 0x06;
452         *p++ = GSS_EAP_MECHANISM->length & 0xff;
453         memcpy(p, GSS_EAP_MECHANISM->elements, GSS_EAP_MECHANISM->length);
454         p += GSS_EAP_MECHANISM->length;
455     }
456
457     /* NAME_LEN */
458     store_uint32_be(krbNameLen, p);
459     p += 4;
460
461     /* NAME */
462     memcpy(p, krbName, krbNameLen);
463     p += krbNameLen;
464
465     if (flags & EXPORT_NAME_FLAG_COMPOSITE) {
466         memcpy(p, attrs.value, attrs.length);
467         p += attrs.length;
468     }
469
470     assert(p == (unsigned char *)exportedName->value + exportedNameLen);
471
472     major = GSS_S_COMPLETE;
473     *minor = 0;
474
475 cleanup:
476     gss_release_buffer(&tmpMinor, &attrs);
477     if (GSS_ERROR(major))
478         gss_release_buffer(&tmpMinor, exportedName);
479     krb5_free_unparsed_name(krbContext, krbName);
480
481     return major;
482 }
483
484 OM_uint32
485 gssEapDuplicateName(OM_uint32 *minor,
486                     const gss_name_t input_name,
487                     gss_name_t *dest_name)
488 {
489     OM_uint32 major, tmpMinor;
490     krb5_context krbContext;
491     gss_name_t name;
492
493     if (input_name == GSS_C_NO_NAME) {
494         *minor = EINVAL;
495         return GSS_S_CALL_INACCESSIBLE_READ | GSS_S_BAD_NAME;
496     }
497
498     GSSEAP_KRB_INIT(&krbContext);
499
500     major = gssEapAllocName(minor, &name);
501     if (GSS_ERROR(major)) {
502         return major;
503     }
504
505     name->flags = input_name->flags;
506
507     *minor = krb5_copy_principal(krbContext, input_name->krbPrincipal,
508                                  &name->krbPrincipal);
509     if (*minor != 0) {
510         major = GSS_S_FAILURE;
511         goto cleanup;
512     }
513
514     if (input_name->attrCtx != NULL) {
515         major = gssEapDuplicateAttrContext(minor, input_name, name);
516         if (GSS_ERROR(major))
517             goto cleanup;
518     }
519
520     *dest_name = name;
521
522 cleanup:
523     if (GSS_ERROR(major)) {
524         gssEapReleaseName(&tmpMinor, &name);
525     }
526
527     return major;
528 }
529
530 OM_uint32
531 gssEapDisplayName(OM_uint32 *minor,
532                   gss_name_t name,
533                   gss_buffer_t output_name_buffer,
534                   gss_OID *output_name_type)
535 {
536     OM_uint32 major;
537     krb5_context krbContext;
538     char *krbName;
539
540     GSSEAP_KRB_INIT(&krbContext);
541
542     output_name_buffer->length = 0;
543     output_name_buffer->value = NULL;
544
545     if (name == GSS_C_NO_NAME) {
546         *minor = EINVAL;
547         return GSS_S_CALL_INACCESSIBLE_READ | GSS_S_BAD_NAME;
548     }
549
550     *minor = krb5_unparse_name(krbContext, name->krbPrincipal, &krbName);
551     if (*minor != 0) {
552         return GSS_S_FAILURE;
553     }
554
555     major = makeStringBuffer(minor, krbName, output_name_buffer);
556     if (GSS_ERROR(major)) {
557         krb5_free_unparsed_name(krbContext, krbName);
558         return major;
559     }
560
561     krb5_free_unparsed_name(krbContext, krbName);
562
563     if (output_name_type != NULL)
564         *output_name_type = GSS_EAP_NT_PRINCIPAL_NAME;
565
566     return GSS_S_COMPLETE;
567 }