Add gss_import_name skeleton
[moonshot.git] / mech_eap / 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 #include "gssapiP_eap.h"
57
58 static const gss_OID_desc gssEapNtPrincipalName = {
59     /* 1.3.6.1.4.1.5322.21.2.1  */
60     12, "\x06\x0A\x2B\x06\x01\x04\x01\xA9\x4A\x15\x02\x01"
61 };
62
63 const gss_OID_desc *const GSS_EAP_NT_PRINCIPAL_NAME =
64     &gssEapNtPrincipalName;
65
66 OM_uint32
67 gssEapAllocName(OM_uint32 *minor, gss_name_t *pName)
68 {
69     OM_uint32 tmpMinor;
70     gss_name_t name;
71
72     assert(*pName == GSS_C_NO_NAME);
73
74     name = (gss_name_t)GSSEAP_CALLOC(1, sizeof(*name));
75     if (name == NULL) {
76         *minor = ENOMEM;
77         return GSS_S_FAILURE;
78     }
79
80     if (GSSEAP_MUTEX_INIT(&name->mutex) != 0) {
81         *minor = errno;
82         gssEapReleaseName(&tmpMinor, &name);
83         return GSS_S_FAILURE;
84     }
85
86     *pName = name;
87
88     return GSS_S_COMPLETE;
89 }
90
91 OM_uint32
92 gssEapReleaseName(OM_uint32 *minor, gss_name_t *pName)
93 {
94     gss_name_t name;
95     krb5_context krbContext = NULL;
96     OM_uint32 tmpMinor;
97
98     if (pName == NULL) {
99         return GSS_S_COMPLETE;
100     }
101
102     name = *pName;
103     if (name == GSS_C_NO_NAME) {
104         return GSS_S_COMPLETE;
105     }
106
107     GSSEAP_KRB_INIT(&krbContext);
108     krb5_free_principal(krbContext, name->krbPrincipal);
109
110     radiusFreeAVPs(&tmpMinor, name->avps);
111     samlFreeAssertion(&tmpMinor, name->assertion);
112
113     GSSEAP_MUTEX_DESTROY(&name->mutex);
114     GSSEAP_FREE(name);
115     *pName = NULL;
116
117     *minor = 0;
118     return GSS_S_COMPLETE;
119 }
120
121 static OM_uint32
122 krbPrincipalToName(OM_uint32 *minor,
123                    krb5_principal *principal,
124                    gss_name_t *pName)
125 {
126     OM_uint32 major;
127     gss_name_t name;
128
129     major = gssEapAllocName(minor, &name);
130     if (GSS_ERROR(major))
131         return major;
132
133     name->krbPrincipal = *principal;
134     *principal = NULL;
135
136     *minor = 0;
137     return GSS_S_COMPLETE;
138 }
139
140 static OM_uint32
141 importServiceName(OM_uint32 *minor,
142                   const gss_buffer_t nameBuffer,
143                   gss_name_t *pName)
144 {
145     OM_uint32 major, tmpMinor;
146     krb5_context krbContext;
147     krb5_principal krbPrinc;
148     char *service, *host;
149
150     GSSEAP_KRB_INIT(&krbContext);
151
152     major = bufferToString(minor, nameBuffer, &service);
153     if (GSS_ERROR(major))
154         return major;
155
156     host = strchr(service, '@');
157     if (host != NULL) {
158         *host = '\0';
159         host++;
160     }    
161
162     /* XXX this is probably NOT what we want to be doing */
163     *minor = krb5_sname_to_principal(krbContext, host, service,
164                                      KRB5_NT_SRV_HST, &krbPrinc);
165     if (*minor != 0) {
166         GSSEAP_FREE(service);
167         return GSS_S_FAILURE;
168     }
169
170     major = krbPrincipalToName(minor, &krbPrinc, pName);
171     if (GSS_ERROR(major)) {
172         krb5_free_principal(krbContext, krbPrinc);
173     }
174
175     GSSEAP_FREE(service);
176     return major;
177 }
178
179 static OM_uint32
180 importUserName(OM_uint32 *minor,
181                const gss_buffer_t nameBuffer,
182                gss_name_t *pName)
183 {
184     OM_uint32 major, tmpMinor;
185     krb5_context krbContext;
186     krb5_principal krbPrinc;
187     char *nameString;
188
189     GSSEAP_KRB_INIT(&krbContext);
190
191     major = bufferToString(minor, nameBuffer, &nameString);
192     if (GSS_ERROR(major))
193         return major;
194
195     *minor = krb5_parse_name(krbContext, nameString, &krbPrinc);
196     if (*minor != 0) {
197         GSSEAP_FREE(nameString);
198         return GSS_S_FAILURE;
199     }
200
201     major = krbPrincipalToName(minor, &krbPrinc, pName);
202     if (GSS_ERROR(major)) {
203         krb5_free_principal(krbContext, krbPrinc);
204     }
205
206     GSSEAP_FREE(nameString);
207     return major;
208 }
209
210 static OM_uint32
211 importExportedName(OM_uint32 *minor,
212                    const gss_buffer_t nameBuffer,
213                    gss_name_t *pName)
214 {
215     OM_uint32 major, tmpMinor;
216     krb5_context krbContext;
217     unsigned char *p;
218     int composite = 0;
219     size_t len, remain;
220     gss_buffer_desc buf;
221
222     GSSEAP_KRB_INIT(&krbContext);
223
224     p = (unsigned char *)nameBuffer->value;
225     remain = nameBuffer->length;
226
227     if (remain < 6 + GSS_EAP_MECHANISM->length + 4)
228         return GSS_S_BAD_NAME;
229
230     if (*p++ != 0x04)
231         return GSS_S_BAD_NAME;
232
233     switch (*p++) {
234     case 0x02:
235         composite = 1;
236         break;
237     case 0x01:
238         break;
239     default:
240         return GSS_S_BAD_NAME;
241         break;
242     }
243     remain -= 2;
244
245     len = load_uint16_be(p);
246     if (len != 2 + GSS_EAP_MECHANISM->length)
247         return GSS_S_BAD_NAME;
248     p += 2;
249     remain -= 2;
250
251     if (*p++ != 0x06)
252         return GSS_S_BAD_NAME;
253     if (*p++ != GSS_EAP_MECHANISM->length)
254         return GSS_S_BAD_MECH;
255     remain -= 2;
256
257     if (memcmp(p, GSS_EAP_MECHANISM->elements, GSS_EAP_MECHANISM->length))
258         return GSS_S_BAD_MECH;
259     p += GSS_EAP_MECHANISM->length;
260     remain -= GSS_EAP_MECHANISM->length;
261
262     len = load_uint32_be(p);
263     p += 4;
264
265     if (remain < len)
266         return GSS_S_BAD_NAME;
267
268     buf.length = len;
269     buf.value = p;
270
271     p += len;
272     remain -= len;
273
274     if (composite == 0 && remain != 0)
275         return GSS_S_BAD_NAME;
276
277     major = importUserName(minor, &buf, pName);
278     if (GSS_ERROR(major))
279         return major;
280
281     /* XXX TODO composite handling */
282
283     return GSS_S_COMPLETE;
284 }
285
286 OM_uint32 gssEapImportName(OM_uint32 *minor,
287                            const gss_buffer_t nameBuffer,
288                            gss_OID nameType,
289                            gss_name_t *name)
290 {
291     OM_uint32 major, tmpMinor;
292
293     *name = GSS_C_NO_NAME;
294
295     if (nameType == GSS_C_NULL_OID ||
296         oidEqual(nameType, GSS_C_NT_USER_NAME) ||
297         oidEqual(nameType, GSS_EAP_NT_PRINCIPAL_NAME))
298         major = importUserName(minor, nameBuffer, name);
299     else if (oidEqual(nameType, GSS_C_NT_HOSTBASED_SERVICE) ||
300                oidEqual(nameType, GSS_C_NT_HOSTBASED_SERVICE_X))
301         major = importServiceName(minor, nameBuffer, name);
302     else if (oidEqual(nameType, GSS_C_NT_EXPORT_NAME))
303         major = importExportedName(minor, nameBuffer, name);
304     else
305         major = GSS_S_BAD_NAMETYPE;
306
307     if (GSS_ERROR(major))
308         gssEapReleaseName(&tmpMinor, name);
309
310     return major;
311 }
312
313 OM_uint32 gssEapExportName(OM_uint32 *minor,
314                            const gss_name_t name,
315                            gss_buffer_t exportedName,
316                            int composite)
317 {
318     OM_uint32 major, tmpMinor;
319     krb5_context krbContext;
320     char *krbName = NULL;
321     size_t krbNameLen;
322     unsigned char *p;
323
324     exportedName->length = 0;
325     exportedName->value = NULL;
326
327     GSSEAP_KRB_INIT(&krbContext);
328
329     if (name == GSS_C_NO_NAME) {
330         *minor = EINVAL;
331         return GSS_S_CALL_INACCESSIBLE_READ | GSS_S_BAD_NAME;
332     }
333
334     GSSEAP_MUTEX_LOCK(&name->mutex);
335
336     /*
337      * Don't export a composite name if we don't have any attributes.
338      */
339     if (composite &&
340         (name->flags & (NAME_FLAG_SAML | NAME_FLAG_RADIUS)) == 0) {
341         composite = 0;
342     }
343
344     *minor = krb5_unparse_name(krbContext, name->krbPrincipal, &krbName);
345     if (*minor != 0) {
346         major = GSS_S_FAILURE;
347         goto cleanup;
348     }
349     krbNameLen = strlen(krbName);
350
351     exportedName->length = 6 + GSS_EAP_MECHANISM->length + 4 + krbNameLen;
352     if (composite) {
353         /* TODO: export SAML/AVP, this is pending specification */
354         GSSEAP_NOT_IMPLEMENTED;
355     }
356
357     exportedName->value = GSSEAP_MALLOC(exportedName->length);
358     if (exportedName->value == NULL) {
359         *minor = ENOMEM;
360         major = GSS_S_FAILURE;
361         goto cleanup;
362     }
363
364     p = (unsigned char *)exportedName->value;
365     *p++ = 0x04;
366     if (composite) {
367         *p++ = 0x02;
368     } else {
369         *p++ = 0x01;
370     }
371     store_uint16_be(GSS_EAP_MECHANISM->length + 2, p);
372     p += 2;
373     *p++ = 0x06;
374     *p++ = GSS_EAP_MECHANISM->length & 0xff;
375     memcpy(p, GSS_EAP_MECHANISM->elements, GSS_EAP_MECHANISM->length);
376     p += GSS_EAP_MECHANISM->length;
377
378     store_uint32_be(krbNameLen, p);
379     p += 4;
380     memcpy(p, krbName, krbNameLen);
381     p += krbNameLen;
382
383     *minor = 0;
384     major = GSS_S_COMPLETE;
385
386 cleanup:
387     GSSEAP_MUTEX_UNLOCK(&name->mutex);
388     if (GSS_ERROR(major))
389         gss_release_buffer(&tmpMinor, exportedName);
390     krb5_free_unparsed_name(krbContext, krbName);
391
392     return major;
393 }