xref: /freebsd/crypto/krb5/src/lib/gssapi/mechglue/g_glue.c (revision 7f2fe78b9dd5f51c821d771b63d2e096f6fd49e9)
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 /* Retrieve the mechanism OID from an RFC 2743 InitialContextToken.  Place
43  * the result into *oid_out, aliasing memory from token. */
gssint_get_mech_type_oid(gss_OID oid_out,gss_buffer_t token)44 OM_uint32 gssint_get_mech_type_oid(gss_OID oid_out, gss_buffer_t token)
45 {
46     struct k5input in;
47 
48     if (oid_out == NULL)
49 	return (GSS_S_CALL_INACCESSIBLE_WRITE);
50     if (token == NULL || token->value == NULL)
51 	return (GSS_S_DEFECTIVE_TOKEN);
52 
53     k5_input_init(&in, token->value, token->length);
54     if (!k5_der_get_value(&in, 0x60, &in))
55 	return (GSS_S_DEFECTIVE_TOKEN);
56     if (!k5_der_get_value(&in, 0x06, &in))
57 	return (GSS_S_DEFECTIVE_TOKEN);
58     oid_out->length = in.len;
59     oid_out->elements = (uint8_t *)in.ptr;
60     return (GSS_S_COMPLETE);
61 }
62 
63 /*
64  * The following mechanisms do not always identify themselves
65  * per the GSS-API specification, when interoperating with MS
66  * peers. We include the OIDs here so we do not have to ilnk
67  * with the mechanism.
68  */
69 static gss_OID_desc gss_ntlm_mechanism_oid_desc =
70 	{10, (void *)"\x2b\x06\x01\x04\x01\x82\x37\x02\x02\x0a"};
71 static gss_OID_desc gss_spnego_mechanism_oid_desc =
72 	{6, (void *)"\x2b\x06\x01\x05\x05\x02"};
73 static gss_OID_desc gss_krb5_mechanism_oid_desc =
74 	{9, (void *)"\x2a\x86\x48\x86\xf7\x12\x01\x02\x02"};
75 
76 #define NTLMSSP_SIGNATURE "NTLMSSP"
77 
gssint_get_mech_type(OID,token)78 OM_uint32 gssint_get_mech_type(OID, token)
79     gss_OID		OID;
80     gss_buffer_t	token;
81 {
82     /* Check for interoperability exceptions */
83     if (token->length >= sizeof(NTLMSSP_SIGNATURE) &&
84 	memcmp(token->value, NTLMSSP_SIGNATURE,
85 	       sizeof(NTLMSSP_SIGNATURE)) == 0) {
86 	*OID = gss_ntlm_mechanism_oid_desc;
87     } else if (token->length != 0 &&
88 	       ((char *)token->value)[0] == 0x6E) {
89  	/* Could be a raw AP-REQ (check for APPLICATION tag) */
90 	*OID = gss_krb5_mechanism_oid_desc;
91     } else if (token->length == 0) {
92 	*OID = gss_spnego_mechanism_oid_desc;
93     } else {
94 	return gssint_get_mech_type_oid(OID, token);
95     }
96 
97     return (GSS_S_COMPLETE);
98 }
99 
100 static OM_uint32
import_internal_attributes(OM_uint32 * minor,gss_mechanism dmech,gss_union_name_t sname,gss_name_t dname)101 import_internal_attributes(OM_uint32 *minor,
102 			   gss_mechanism dmech,
103 			   gss_union_name_t sname,
104 			   gss_name_t dname)
105 {
106     OM_uint32 major, tmpMinor;
107     gss_mechanism smech;
108     gss_buffer_set_t attrs = GSS_C_NO_BUFFER_SET;
109     size_t i;
110 
111     if (sname->mech_name == GSS_C_NO_NAME)
112 	return (GSS_S_UNAVAILABLE);
113 
114     smech = gssint_get_mechanism (sname->mech_type);
115     if (smech == NULL)
116 	return (GSS_S_BAD_MECH);
117 
118     if (smech->gss_inquire_name == NULL ||
119 	smech->gss_get_name_attribute == NULL)
120 	return (GSS_S_UNAVAILABLE);
121 
122     if (dmech->gss_set_name_attribute == NULL)
123 	return (GSS_S_UNAVAILABLE);
124 
125     major = smech->gss_inquire_name(minor, sname->mech_name,
126 				    NULL, NULL, &attrs);
127     if (GSS_ERROR(major) || attrs == GSS_C_NO_BUFFER_SET) {
128 	gss_release_buffer_set(&tmpMinor, &attrs);
129 	return (major);
130     }
131 
132     for (i = 0; i < attrs->count; i++) {
133 	int more = -1;
134 
135 	while (more != 0) {
136 	    gss_buffer_desc value, display_value;
137 	    int authenticated, complete;
138 
139 	    major = smech->gss_get_name_attribute(minor, sname->mech_name,
140 						  &attrs->elements[i],
141 						  &authenticated, &complete,
142 						  &value, &display_value,
143 						  &more);
144 	    if (GSS_ERROR(major))
145 		continue;
146 
147 	    if (authenticated) {
148 		dmech->gss_set_name_attribute(minor, dname, complete,
149 					      &attrs->elements[i], &value);
150 	    }
151 
152 	    gss_release_buffer(&tmpMinor, &value);
153 	    gss_release_buffer(&tmpMinor, &display_value);
154 	}
155     }
156 
157     gss_release_buffer_set(&tmpMinor, &attrs);
158 
159     return (GSS_S_COMPLETE);
160 }
161 
162 /*
163  *  Internal routines to get and release an internal mechanism name
164  */
165 
gssint_import_internal_name(minor_status,mech_type,union_name,internal_name)166 OM_uint32 gssint_import_internal_name (minor_status, mech_type, union_name,
167 				internal_name)
168 OM_uint32	*minor_status;
169 gss_OID		mech_type;
170 gss_union_name_t	union_name;
171 gss_name_t	*internal_name;
172 {
173     OM_uint32		status, tmpMinor;
174     gss_mechanism	mech;
175     gss_OID		public_mech;
176 
177     mech = gssint_get_mechanism (mech_type);
178     if (mech == NULL)
179 	return (GSS_S_BAD_MECH);
180 
181     /*
182      * If we are importing a name for the same mechanism, and the
183      * mechanism implements gss_duplicate_name, then use that.
184      */
185     if (union_name->mech_type != GSS_C_NO_OID &&
186 	union_name->mech_name != GSS_C_NO_NAME &&
187 	g_OID_equal(union_name->mech_type, mech_type) &&
188 	mech->gss_duplicate_name != NULL) {
189 	status = mech->gss_duplicate_name(minor_status,
190 					  union_name->mech_name,
191 					  internal_name);
192 	if (status != GSS_S_UNAVAILABLE) {
193 	    if (status != GSS_S_COMPLETE)
194 		map_error(minor_status, mech);
195 	    return (status);
196 	}
197     }
198 
199     if (mech->gssspi_import_name_by_mech) {
200 	public_mech = gssint_get_public_oid(mech_type);
201 	status = mech->gssspi_import_name_by_mech(minor_status, public_mech,
202 						  union_name->external_name,
203 						  union_name->name_type,
204 						  internal_name);
205     } else if (mech->gss_import_name) {
206 	status = mech->gss_import_name(minor_status, union_name->external_name,
207 				       union_name->name_type, internal_name);
208     } else {
209 	return (GSS_S_UNAVAILABLE);
210     }
211 
212     if (status == GSS_S_COMPLETE) {
213         /* Attempt to round-trip attributes */
214 	(void) import_internal_attributes(&tmpMinor, mech,
215 				          union_name, *internal_name);
216     } else {
217 	map_error(minor_status, mech);
218     }
219 
220     return (status);
221 }
222 
gssint_export_internal_name(minor_status,mech_type,internal_name,name_buf)223 OM_uint32 gssint_export_internal_name(minor_status, mech_type,
224 				     internal_name, name_buf)
225     OM_uint32		*minor_status;
226     const gss_OID		mech_type;
227     const gss_name_t	internal_name;
228     gss_buffer_t		name_buf;
229 {
230     OM_uint32 status;
231     gss_mechanism mech;
232     gss_buffer_desc dispName;
233     gss_OID nameOid;
234     int mech_der_len = 0;
235     struct k5buf buf;
236 
237     mech = gssint_get_mechanism(mech_type);
238     if (!mech)
239 	return (GSS_S_BAD_MECH);
240 
241     if (mech->gss_export_name) {
242 	status = mech->gss_export_name(minor_status,
243 				       internal_name,
244 				       name_buf);
245 	if (status != GSS_S_COMPLETE)
246 	    map_error(minor_status, mech);
247 	return status;
248     }
249 
250     /*
251      * if we are here it is because the mechanism does not provide
252      * a gss_export_name so we will use our implementation.  We
253      * do required that the mechanism define a gss_display_name.
254      */
255     if (!mech->gss_display_name)
256 	return (GSS_S_UNAVAILABLE);
257 
258     /*
259      * NOTE: RFC2743 (section 3.2) governs the format of the outer
260      *	 wrapper of exported names; the mechanisms' specs govern
261      *	 the format of the inner portion of the exported name
262      *	 and, for some (e.g., RFC1964, the Kerberos V mech), a
263      *	 generic default as implemented here will do.
264      *
265      * The outer wrapper of an exported MN is: 2-octet tok Id
266      * (0x0401) + 2-octet network-byte order mech OID length + mech
267      * oid (in DER format, including DER tag and DER length) +
268      * 4-octet network-byte order length of inner portion + inner
269      * portion.
270      *
271      * For the Kerberos V mechanism the inner portion of an exported
272      * MN is the display name string and ignores the name type OID
273      * altogether.  And we hope this will be so for any future
274      * mechanisms also, so that factoring name export/import out of
275      * the mech and into libgss pays off.
276      */
277     if ((status = mech->gss_display_name(minor_status,
278 					 internal_name,
279 					 &dispName,
280 					 &nameOid))
281 	!= GSS_S_COMPLETE) {
282 	map_error(minor_status, mech);
283 	return (status);
284     }
285 
286     /* Allocate space and prepare a buffer. */
287     mech_der_len = k5_der_value_len(mech_type->length);
288     name_buf->length = 2 + 2 + mech_der_len + 4 + dispName.length;
289     name_buf->value = gssalloc_malloc(name_buf->length);
290     if (name_buf->value == NULL) {
291 	name_buf->length = 0;
292 	(void) gss_release_buffer(&status, &dispName);
293 	return (GSS_S_FAILURE);
294     }
295     k5_buf_init_fixed(&buf, name_buf->value, name_buf->length);
296 
297     /* Assemble the name. */
298     k5_buf_add_len(&buf, "\x04\x01", 2);
299     k5_buf_add_uint16_be(&buf, mech_der_len);
300     k5_der_add_value(&buf, 0x06, mech_type->elements, mech_type->length);
301     k5_buf_add_uint32_be(&buf, dispName.length);
302     k5_buf_add_len(&buf, dispName.value, dispName.length);
303     assert(buf.len == name_buf->length);
304 
305     /* release the buffer obtained from gss_display_name */
306     (void) gss_release_buffer(minor_status, &dispName);
307     return (GSS_S_COMPLETE);
308 } /*  gssint_export_internal_name */
309 
gssint_display_internal_name(minor_status,mech_type,internal_name,external_name,name_type)310 OM_uint32 gssint_display_internal_name (minor_status, mech_type, internal_name,
311 				 external_name, name_type)
312 OM_uint32	*minor_status;
313 gss_OID		mech_type;
314 gss_name_t	internal_name;
315 gss_buffer_t	external_name;
316 gss_OID		*name_type;
317 {
318     OM_uint32		status;
319     gss_mechanism	mech;
320 
321     mech = gssint_get_mechanism (mech_type);
322     if (mech) {
323 	if (mech->gss_display_name) {
324 	    status = mech->gss_display_name (
325 					     minor_status,
326 					     internal_name,
327 					     external_name,
328 					     name_type);
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 
gssint_release_internal_name(minor_status,mech_type,internal_name)340 OM_uint32 gssint_release_internal_name (minor_status, mech_type, internal_name)
341 OM_uint32	*minor_status;
342 gss_OID		mech_type;
343 gss_name_t	*internal_name;
344 {
345     OM_uint32		status;
346     gss_mechanism	mech;
347 
348     mech = gssint_get_mechanism (mech_type);
349     if (mech) {
350 	if (mech->gss_release_name) {
351 	    status = mech->gss_release_name (
352 					     minor_status,
353 					     internal_name);
354 	    if (status != GSS_S_COMPLETE)
355 		map_error(minor_status, mech);
356 	} else
357 	    status = GSS_S_UNAVAILABLE;
358 
359 	return (status);
360     }
361 
362     return (GSS_S_BAD_MECH);
363 }
364 
gssint_delete_internal_sec_context(minor_status,mech_type,internal_ctx,output_token)365 OM_uint32 gssint_delete_internal_sec_context (minor_status,
366 					      mech_type,
367 					      internal_ctx,
368 					      output_token)
369 OM_uint32	*minor_status;
370 gss_OID		mech_type;
371 gss_ctx_id_t	*internal_ctx;
372 gss_buffer_t	output_token;
373 {
374     OM_uint32		status;
375     gss_mechanism	mech;
376 
377     mech = gssint_get_mechanism (mech_type);
378     if (mech) {
379 	if (mech->gss_delete_sec_context)
380 	    status = mech->gss_delete_sec_context (minor_status,
381 						   internal_ctx,
382 						   output_token);
383 	else
384 	    status = GSS_S_UNAVAILABLE;
385 
386 	return (status);
387     }
388 
389     return (GSS_S_BAD_MECH);
390 }
391 
392 /*
393  * This function converts an internal gssapi name to a union gssapi
394  * name.  Note that internal_name should be considered "consumed" by
395  * this call, whether or not we return an error.
396  */
gssint_convert_name_to_union_name(minor_status,mech,internal_name,external_name)397 OM_uint32 gssint_convert_name_to_union_name(minor_status, mech,
398 					   internal_name, external_name)
399     OM_uint32 *minor_status;
400     gss_mechanism	mech;
401     gss_name_t	internal_name;
402     gss_name_t	*external_name;
403 {
404     OM_uint32 major_status,tmp;
405     gss_union_name_t union_name;
406 
407     union_name = (gss_union_name_t) malloc (sizeof(gss_union_name_desc));
408     if (!union_name) {
409 	major_status = GSS_S_FAILURE;
410 	*minor_status = ENOMEM;
411 	map_errcode(minor_status);
412 	goto allocation_failure;
413     }
414     union_name->mech_type = 0;
415     union_name->mech_name = internal_name;
416     union_name->name_type = 0;
417     union_name->external_name = 0;
418 
419     major_status = generic_gss_copy_oid(minor_status, &mech->mech_type,
420 					&union_name->mech_type);
421     if (major_status != GSS_S_COMPLETE) {
422 	map_errcode(minor_status);
423 	goto allocation_failure;
424     }
425 
426     union_name->external_name =
427 	(gss_buffer_t) malloc(sizeof(gss_buffer_desc));
428     if (!union_name->external_name) {
429 	    major_status = GSS_S_FAILURE;
430 	    goto allocation_failure;
431     }
432     union_name->external_name->length = 0;
433     union_name->external_name->value = NULL;
434 
435     major_status = mech->gss_display_name(minor_status,
436 					  internal_name,
437 					  union_name->external_name,
438 					  &union_name->name_type);
439     if (major_status != GSS_S_COMPLETE) {
440 	map_error(minor_status, mech);
441 	goto allocation_failure;
442     }
443 
444     union_name->loopback = union_name;
445     *external_name = /*(gss_name_t) CHECK */union_name;
446     return (GSS_S_COMPLETE);
447 
448 allocation_failure:
449     if (union_name) {
450 	if (union_name->external_name) {
451 	    if (union_name->external_name->value)
452 		free(union_name->external_name->value);
453 	    free(union_name->external_name);
454 	}
455 	if (union_name->name_type)
456 	    gss_release_oid(&tmp, &union_name->name_type);
457 	if (union_name->mech_type)
458 	    gss_release_oid(&tmp, &union_name->mech_type);
459 	free(union_name);
460     }
461     /*
462      * do as the top comment says - since we are now owners of
463      * internal_name, we must clean it up
464      */
465     if (internal_name)
466 	(void) gssint_release_internal_name(&tmp, &mech->mech_type,
467 					   &internal_name);
468     return (major_status);
469 }
470 
471 /*
472  * Glue routine for returning the mechanism-specific credential from a
473  * external union credential.
474  */
475 gss_cred_id_t
gssint_get_mechanism_cred(union_cred,mech_type)476 gssint_get_mechanism_cred(union_cred, mech_type)
477     gss_union_cred_t	union_cred;
478     gss_OID		mech_type;
479 {
480     int		i;
481 
482     if (union_cred == GSS_C_NO_CREDENTIAL)
483 	return GSS_C_NO_CREDENTIAL;
484 
485     for (i=0; i < union_cred->count; i++) {
486 	if (g_OID_equal(mech_type, &union_cred->mechs_array[i]))
487 	    return union_cred->cred_array[i];
488     }
489     return GSS_C_NO_CREDENTIAL;
490 }
491 
492 /*
493  * Routine to create and copy the gss_buffer_desc structure.
494  * Both space for the structure and the data is allocated.
495  */
496 OM_uint32
gssint_create_copy_buffer(srcBuf,destBuf,addNullChar)497 gssint_create_copy_buffer(srcBuf, destBuf, addNullChar)
498     const gss_buffer_t	srcBuf;
499     gss_buffer_t 		*destBuf;
500     int			addNullChar;
501 {
502     gss_buffer_t aBuf;
503     unsigned int len;
504 
505     if (destBuf == NULL)
506 	return (GSS_S_CALL_INACCESSIBLE_WRITE);
507 
508     *destBuf = 0;
509 
510     aBuf = (gss_buffer_t)malloc(sizeof (gss_buffer_desc));
511     if (!aBuf)
512 	return (GSS_S_FAILURE);
513 
514     if (addNullChar)
515 	len = srcBuf->length + 1;
516     else
517 	len = srcBuf->length;
518 
519     if (!(aBuf->value = (void*)gssalloc_malloc(len))) {
520 	free(aBuf);
521 	return (GSS_S_FAILURE);
522     }
523 
524 
525     (void) memcpy(aBuf->value, srcBuf->value, srcBuf->length);
526     aBuf->length = srcBuf->length;
527     *destBuf = aBuf;
528 
529     /* optionally add a NULL character */
530     if (addNullChar)
531 	((char *)aBuf->value)[aBuf->length] = '\0';
532 
533     return (GSS_S_COMPLETE);
534 } /* ****** gssint_create_copy_buffer  ****** */
535 
536 OM_uint32
gssint_create_union_context(OM_uint32 * minor,gss_const_OID mech_oid,gss_union_ctx_id_t * ctx_out)537 gssint_create_union_context(OM_uint32 *minor, gss_const_OID mech_oid,
538 			    gss_union_ctx_id_t *ctx_out)
539 {
540     OM_uint32 status;
541     gss_union_ctx_id_t ctx;
542 
543     *ctx_out = NULL;
544 
545     ctx = calloc(1, sizeof(*ctx));
546     if (ctx == NULL) {
547 	*minor = ENOMEM;
548 	return GSS_S_FAILURE;
549     }
550 
551     status = generic_gss_copy_oid(minor, mech_oid, &ctx->mech_type);
552     if (status != GSS_S_COMPLETE) {
553 	free(ctx);
554 	return status;
555     }
556 
557     ctx->loopback = ctx;
558     ctx->internal_ctx_id = GSS_C_NO_CONTEXT;
559 
560     *ctx_out = ctx;
561     return GSS_S_COMPLETE;
562 }
563