xref: /freebsd/crypto/krb5/src/lib/gssapi/mechglue/g_glue.c (revision f1c4c3daccbaf3820f0e2224de53df12fc952fcc)
1 /* #pragma ident	"@(#)g_glue.c	1.25	04/02/23 SMI" */
2 
3 /*
4  * Copyright 1996 by Sun Microsystems, Inc.
5  *
6  * Permission to use, copy, modify, distribute, and sell this software
7  * and its documentation for any purpose is hereby granted without fee,
8  * provided that the above copyright notice appears in all copies and
9  * that both that copyright notice and this permission notice appear in
10  * supporting documentation, and that the name of Sun Microsystems not be used
11  * in advertising or publicity pertaining to distribution of the software
12  * without specific, written prior permission. Sun Microsystems makes no
13  * representations about the suitability of this software for any
14  * purpose.  It is provided "as is" without express or implied warranty.
15  *
16  * SUN MICROSYSTEMS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
17  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
18  * EVENT SHALL SUN MICROSYSTEMS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
19  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
20  * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
21  * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
22  * PERFORMANCE OF THIS SOFTWARE.
23  */
24 
25 #include "mglueP.h"
26 #include "k5-der.h"
27 #include <stdio.h>
28 #ifdef HAVE_STDLIB_H
29 #include <stdlib.h>
30 #endif
31 #include <string.h>
32 #include <errno.h>
33 
34 #define	MSO_BIT (8*(sizeof (int) - 1))  /* Most significant octet bit */
35 
36 extern gss_mechanism *gssint_mechs_array;
37 
38 /*
39  * This file contains the support routines for the glue layer.
40  */
41 
42 /*
43  * The following mechanisms do not always identify themselves
44  * per the GSS-API specification, when interoperating with MS
45  * peers. We include the OIDs here so we do not have to ilnk
46  * with the mechanism.
47  */
48 static gss_OID_desc gss_ntlm_mechanism_oid_desc =
49 	{10, (void *)"\x2b\x06\x01\x04\x01\x82\x37\x02\x02\x0a"};
50 static gss_OID_desc gss_spnego_mechanism_oid_desc =
51 	{6, (void *)"\x2b\x06\x01\x05\x05\x02"};
52 static gss_OID_desc gss_krb5_mechanism_oid_desc =
53 	{9, (void *)"\x2a\x86\x48\x86\xf7\x12\x01\x02\x02"};
54 
55 #define NTLMSSP_SIGNATURE "NTLMSSP"
56 
57 OM_uint32
gssint_get_mech_type(gss_OID OID,gss_buffer_t token)58 gssint_get_mech_type(gss_OID OID, gss_buffer_t token)
59 {
60     struct k5input in;
61     size_t tlen;
62 
63     /* Check for interoperability exceptions */
64     if (token->length >= sizeof(NTLMSSP_SIGNATURE) &&
65 	memcmp(token->value, NTLMSSP_SIGNATURE,
66 	       sizeof(NTLMSSP_SIGNATURE)) == 0) {
67 	*OID = gss_ntlm_mechanism_oid_desc;
68     } else if (token->length != 0 &&
69 	       ((char *)token->value)[0] == 0x6E) {
70  	/* Could be a raw AP-REQ (check for APPLICATION tag) */
71 	*OID = gss_krb5_mechanism_oid_desc;
72     } else if (token->length == 0) {
73 	*OID = gss_spnego_mechanism_oid_desc;
74     } else {
75 	k5_input_init(&in, token->value, token->length);
76 	return (g_get_token_header(&in, OID, &tlen) ? GSS_S_COMPLETE :
77 		GSS_S_DEFECTIVE_TOKEN);
78     }
79 
80     return (GSS_S_COMPLETE);
81 }
82 
83 static OM_uint32
import_internal_attributes(OM_uint32 * minor,gss_mechanism dmech,gss_union_name_t sname,gss_name_t dname)84 import_internal_attributes(OM_uint32 *minor,
85 			   gss_mechanism dmech,
86 			   gss_union_name_t sname,
87 			   gss_name_t dname)
88 {
89     OM_uint32 major, tmpMinor;
90     gss_mechanism smech;
91     gss_buffer_set_t attrs = GSS_C_NO_BUFFER_SET;
92     size_t i;
93 
94     if (sname->mech_name == GSS_C_NO_NAME)
95 	return (GSS_S_UNAVAILABLE);
96 
97     smech = gssint_get_mechanism (sname->mech_type);
98     if (smech == NULL)
99 	return (GSS_S_BAD_MECH);
100 
101     if (smech->gss_inquire_name == NULL ||
102 	smech->gss_get_name_attribute == NULL)
103 	return (GSS_S_UNAVAILABLE);
104 
105     if (dmech->gss_set_name_attribute == NULL)
106 	return (GSS_S_UNAVAILABLE);
107 
108     major = smech->gss_inquire_name(minor, sname->mech_name,
109 				    NULL, NULL, &attrs);
110     if (GSS_ERROR(major) || attrs == GSS_C_NO_BUFFER_SET) {
111 	gss_release_buffer_set(&tmpMinor, &attrs);
112 	return (major);
113     }
114 
115     for (i = 0; i < attrs->count; i++) {
116 	int more = -1;
117 
118 	while (more != 0) {
119 	    gss_buffer_desc value, display_value;
120 	    int authenticated, complete;
121 
122 	    major = smech->gss_get_name_attribute(minor, sname->mech_name,
123 						  &attrs->elements[i],
124 						  &authenticated, &complete,
125 						  &value, &display_value,
126 						  &more);
127 	    if (GSS_ERROR(major))
128 		continue;
129 
130 	    if (authenticated) {
131 		dmech->gss_set_name_attribute(minor, dname, complete,
132 					      &attrs->elements[i], &value);
133 	    }
134 
135 	    gss_release_buffer(&tmpMinor, &value);
136 	    gss_release_buffer(&tmpMinor, &display_value);
137 	}
138     }
139 
140     gss_release_buffer_set(&tmpMinor, &attrs);
141 
142     return (GSS_S_COMPLETE);
143 }
144 
145 /*
146  *  Internal routines to get and release an internal mechanism name
147  */
148 
149 OM_uint32
gssint_import_internal_name(OM_uint32 * minor_status,gss_OID mech_type,gss_union_name_t union_name,gss_name_t * internal_name)150 gssint_import_internal_name(OM_uint32 *minor_status, gss_OID mech_type,
151 			    gss_union_name_t union_name,
152 			    gss_name_t *internal_name)
153 {
154     OM_uint32		status, tmpMinor;
155     gss_mechanism	mech;
156     gss_OID		public_mech;
157 
158     mech = gssint_get_mechanism (mech_type);
159     if (mech == NULL)
160 	return (GSS_S_BAD_MECH);
161 
162     /*
163      * If we are importing a name for the same mechanism, and the
164      * mechanism implements gss_duplicate_name, then use that.
165      */
166     if (union_name->mech_type != GSS_C_NO_OID &&
167 	union_name->mech_name != GSS_C_NO_NAME &&
168 	g_OID_equal(union_name->mech_type, mech_type) &&
169 	mech->gss_duplicate_name != NULL) {
170 	status = mech->gss_duplicate_name(minor_status,
171 					  union_name->mech_name,
172 					  internal_name);
173 	if (status != GSS_S_UNAVAILABLE) {
174 	    if (status != GSS_S_COMPLETE)
175 		map_error(minor_status, mech);
176 	    return (status);
177 	}
178     }
179 
180     if (mech->gssspi_import_name_by_mech) {
181 	public_mech = gssint_get_public_oid(mech_type);
182 	status = mech->gssspi_import_name_by_mech(minor_status, public_mech,
183 						  union_name->external_name,
184 						  union_name->name_type,
185 						  internal_name);
186     } else if (mech->gss_import_name) {
187 	status = mech->gss_import_name(minor_status, union_name->external_name,
188 				       union_name->name_type, internal_name);
189     } else {
190 	return (GSS_S_UNAVAILABLE);
191     }
192 
193     if (status == GSS_S_COMPLETE) {
194         /* Attempt to round-trip attributes */
195 	(void) import_internal_attributes(&tmpMinor, mech,
196 				          union_name, *internal_name);
197     } else {
198 	map_error(minor_status, mech);
199     }
200 
201     return (status);
202 }
203 
204 OM_uint32
gssint_export_internal_name(OM_uint32 * minor_status,const gss_OID mech_type,const gss_name_t internal_name,gss_buffer_t name_buf)205 gssint_export_internal_name(OM_uint32 *minor_status, const gss_OID mech_type,
206 			    const gss_name_t internal_name,
207 			    gss_buffer_t name_buf)
208 {
209     OM_uint32 status;
210     gss_mechanism mech;
211     gss_buffer_desc dispName;
212     gss_OID nameOid;
213     int mech_der_len = 0;
214     struct k5buf buf;
215 
216     mech = gssint_get_mechanism(mech_type);
217     if (!mech)
218 	return (GSS_S_BAD_MECH);
219 
220     if (mech->gss_export_name) {
221 	status = mech->gss_export_name(minor_status,
222 				       internal_name,
223 				       name_buf);
224 	if (status != GSS_S_COMPLETE)
225 	    map_error(minor_status, mech);
226 	return status;
227     }
228 
229     /*
230      * if we are here it is because the mechanism does not provide
231      * a gss_export_name so we will use our implementation.  We
232      * do required that the mechanism define a gss_display_name.
233      */
234     if (!mech->gss_display_name)
235 	return (GSS_S_UNAVAILABLE);
236 
237     /*
238      * NOTE: RFC2743 (section 3.2) governs the format of the outer
239      *	 wrapper of exported names; the mechanisms' specs govern
240      *	 the format of the inner portion of the exported name
241      *	 and, for some (e.g., RFC1964, the Kerberos V mech), a
242      *	 generic default as implemented here will do.
243      *
244      * The outer wrapper of an exported MN is: 2-octet tok Id
245      * (0x0401) + 2-octet network-byte order mech OID length + mech
246      * oid (in DER format, including DER tag and DER length) +
247      * 4-octet network-byte order length of inner portion + inner
248      * portion.
249      *
250      * For the Kerberos V mechanism the inner portion of an exported
251      * MN is the display name string and ignores the name type OID
252      * altogether.  And we hope this will be so for any future
253      * mechanisms also, so that factoring name export/import out of
254      * the mech and into libgss pays off.
255      */
256     if ((status = mech->gss_display_name(minor_status,
257 					 internal_name,
258 					 &dispName,
259 					 &nameOid))
260 	!= GSS_S_COMPLETE) {
261 	map_error(minor_status, mech);
262 	return (status);
263     }
264 
265     /* Allocate space and prepare a buffer. */
266     mech_der_len = k5_der_value_len(mech_type->length);
267     name_buf->length = 2 + 2 + mech_der_len + 4 + dispName.length;
268     name_buf->value = gssalloc_malloc(name_buf->length);
269     if (name_buf->value == NULL) {
270 	name_buf->length = 0;
271 	(void) gss_release_buffer(&status, &dispName);
272 	return (GSS_S_FAILURE);
273     }
274     k5_buf_init_fixed(&buf, name_buf->value, name_buf->length);
275 
276     /* Assemble the name. */
277     k5_buf_add_len(&buf, "\x04\x01", 2);
278     k5_buf_add_uint16_be(&buf, mech_der_len);
279     k5_der_add_value(&buf, 0x06, mech_type->elements, mech_type->length);
280     k5_buf_add_uint32_be(&buf, dispName.length);
281     k5_buf_add_len(&buf, dispName.value, dispName.length);
282     assert(buf.len == name_buf->length);
283 
284     /* release the buffer obtained from gss_display_name */
285     (void) gss_release_buffer(minor_status, &dispName);
286     return (GSS_S_COMPLETE);
287 } /*  gssint_export_internal_name */
288 
289 OM_uint32
gssint_display_internal_name(OM_uint32 * minor_status,gss_OID mech_type,gss_name_t internal_name,gss_buffer_t external_name,gss_OID * name_type)290 gssint_display_internal_name(OM_uint32 *minor_status, gss_OID mech_type,
291 			     gss_name_t internal_name,
292 			     gss_buffer_t external_name, gss_OID *name_type)
293 {
294     OM_uint32		status;
295     gss_mechanism	mech;
296 
297     mech = gssint_get_mechanism (mech_type);
298     if (mech) {
299 	if (mech->gss_display_name) {
300 	    status = mech->gss_display_name (
301 					     minor_status,
302 					     internal_name,
303 					     external_name,
304 					     name_type);
305 	    if (status != GSS_S_COMPLETE)
306 		map_error(minor_status, mech);
307 	} else
308 	    status = GSS_S_UNAVAILABLE;
309 
310 	return (status);
311     }
312 
313     return (GSS_S_BAD_MECH);
314 }
315 
316 OM_uint32
gssint_release_internal_name(OM_uint32 * minor_status,gss_OID mech_type,gss_name_t * internal_name)317 gssint_release_internal_name(OM_uint32 *minor_status, gss_OID mech_type,
318 			     gss_name_t *internal_name)
319 {
320     OM_uint32		status;
321     gss_mechanism	mech;
322 
323     mech = gssint_get_mechanism (mech_type);
324     if (mech) {
325 	if (mech->gss_release_name) {
326 	    status = mech->gss_release_name (
327 					     minor_status,
328 					     internal_name);
329 	    if (status != GSS_S_COMPLETE)
330 		map_error(minor_status, mech);
331 	} else
332 	    status = GSS_S_UNAVAILABLE;
333 
334 	return (status);
335     }
336 
337     return (GSS_S_BAD_MECH);
338 }
339 
340 OM_uint32
gssint_delete_internal_sec_context(OM_uint32 * minor_status,gss_OID mech_type,gss_ctx_id_t * internal_ctx,gss_buffer_t output_token)341 gssint_delete_internal_sec_context(OM_uint32 *minor_status, gss_OID mech_type,
342 				   gss_ctx_id_t *internal_ctx,
343 				   gss_buffer_t output_token)
344 {
345     OM_uint32		status;
346     gss_mechanism	mech;
347 
348     mech = gssint_get_mechanism (mech_type);
349     if (mech) {
350 	if (mech->gss_delete_sec_context)
351 	    status = mech->gss_delete_sec_context (minor_status,
352 						   internal_ctx,
353 						   output_token);
354 	else
355 	    status = GSS_S_UNAVAILABLE;
356 
357 	return (status);
358     }
359 
360     return (GSS_S_BAD_MECH);
361 }
362 
363 /*
364  * This function converts an internal gssapi name to a union gssapi
365  * name.  Note that internal_name should be considered "consumed" by
366  * this call, whether or not we return an error.
367  */
368 OM_uint32
gssint_convert_name_to_union_name(OM_uint32 * minor_status,gss_mechanism mech,gss_name_t internal_name,gss_name_t * external_name)369 gssint_convert_name_to_union_name(OM_uint32 *minor_status, gss_mechanism mech,
370 				  gss_name_t internal_name,
371 				  gss_name_t *external_name)
372 {
373     OM_uint32 major_status,tmp;
374     gss_union_name_t union_name;
375 
376     union_name = (gss_union_name_t) malloc (sizeof(gss_union_name_desc));
377     if (!union_name) {
378 	major_status = GSS_S_FAILURE;
379 	*minor_status = ENOMEM;
380 	map_errcode(minor_status);
381 	goto allocation_failure;
382     }
383     union_name->mech_type = 0;
384     union_name->mech_name = internal_name;
385     union_name->name_type = 0;
386     union_name->external_name = 0;
387 
388     major_status = generic_gss_copy_oid(minor_status, &mech->mech_type,
389 					&union_name->mech_type);
390     if (major_status != GSS_S_COMPLETE) {
391 	map_errcode(minor_status);
392 	goto allocation_failure;
393     }
394 
395     union_name->external_name =
396 	(gss_buffer_t) malloc(sizeof(gss_buffer_desc));
397     if (!union_name->external_name) {
398 	    major_status = GSS_S_FAILURE;
399 	    goto allocation_failure;
400     }
401     union_name->external_name->length = 0;
402     union_name->external_name->value = NULL;
403 
404     major_status = mech->gss_display_name(minor_status,
405 					  internal_name,
406 					  union_name->external_name,
407 					  &union_name->name_type);
408     if (major_status != GSS_S_COMPLETE) {
409 	map_error(minor_status, mech);
410 	goto allocation_failure;
411     }
412 
413     union_name->loopback = union_name;
414     *external_name = /*(gss_name_t) CHECK */union_name;
415     return (GSS_S_COMPLETE);
416 
417 allocation_failure:
418     if (union_name) {
419 	if (union_name->external_name) {
420 	    if (union_name->external_name->value)
421 		free(union_name->external_name->value);
422 	    free(union_name->external_name);
423 	}
424 	if (union_name->name_type)
425 	    gss_release_oid(&tmp, &union_name->name_type);
426 	if (union_name->mech_type)
427 	    gss_release_oid(&tmp, &union_name->mech_type);
428 	free(union_name);
429     }
430     /*
431      * do as the top comment says - since we are now owners of
432      * internal_name, we must clean it up
433      */
434     if (internal_name)
435 	(void) gssint_release_internal_name(&tmp, &mech->mech_type,
436 					   &internal_name);
437     return (major_status);
438 }
439 
440 /*
441  * Glue routine for returning the mechanism-specific credential from a
442  * external union credential.
443  */
444 gss_cred_id_t
gssint_get_mechanism_cred(gss_union_cred_t union_cred,gss_OID mech_type)445 gssint_get_mechanism_cred(gss_union_cred_t union_cred, gss_OID mech_type)
446 {
447     int		i;
448 
449     if (union_cred == GSS_C_NO_CREDENTIAL)
450 	return GSS_C_NO_CREDENTIAL;
451 
452     for (i=0; i < union_cred->count; i++) {
453 	if (g_OID_equal(mech_type, &union_cred->mechs_array[i]))
454 	    return union_cred->cred_array[i];
455     }
456     return GSS_C_NO_CREDENTIAL;
457 }
458 
459 /*
460  * Routine to create and copy the gss_buffer_desc structure.
461  * Both space for the structure and the data is allocated.
462  */
463 OM_uint32
gssint_create_copy_buffer(const gss_buffer_t srcBuf,gss_buffer_t * destBuf,int addNullChar)464 gssint_create_copy_buffer(const gss_buffer_t srcBuf, gss_buffer_t *destBuf,
465 			  int addNullChar)
466 {
467     gss_buffer_t aBuf;
468     unsigned int len;
469 
470     if (destBuf == NULL)
471 	return (GSS_S_CALL_INACCESSIBLE_WRITE);
472 
473     *destBuf = 0;
474 
475     aBuf = (gss_buffer_t)malloc(sizeof (gss_buffer_desc));
476     if (!aBuf)
477 	return (GSS_S_FAILURE);
478 
479     if (addNullChar)
480 	len = srcBuf->length + 1;
481     else
482 	len = srcBuf->length;
483 
484     if (!(aBuf->value = (void*)gssalloc_malloc(len))) {
485 	free(aBuf);
486 	return (GSS_S_FAILURE);
487     }
488 
489 
490     (void) memcpy(aBuf->value, srcBuf->value, srcBuf->length);
491     aBuf->length = srcBuf->length;
492     *destBuf = aBuf;
493 
494     /* optionally add a NULL character */
495     if (addNullChar)
496 	((char *)aBuf->value)[aBuf->length] = '\0';
497 
498     return (GSS_S_COMPLETE);
499 } /* ****** gssint_create_copy_buffer  ****** */
500 
501 OM_uint32
gssint_create_union_context(OM_uint32 * minor,gss_const_OID mech_oid,gss_union_ctx_id_t * ctx_out)502 gssint_create_union_context(OM_uint32 *minor, gss_const_OID mech_oid,
503 			    gss_union_ctx_id_t *ctx_out)
504 {
505     OM_uint32 status;
506     gss_union_ctx_id_t ctx;
507 
508     *ctx_out = NULL;
509 
510     ctx = calloc(1, sizeof(*ctx));
511     if (ctx == NULL) {
512 	*minor = ENOMEM;
513 	return GSS_S_FAILURE;
514     }
515 
516     status = generic_gss_copy_oid(minor, mech_oid, &ctx->mech_type);
517     if (status != GSS_S_COMPLETE) {
518 	free(ctx);
519 	return status;
520     }
521 
522     ctx->loopback = ctx;
523     ctx->internal_ctx_id = GSS_C_NO_CONTEXT;
524 
525     *ctx_out = ctx;
526     return GSS_S_COMPLETE;
527 }
528