xref: /illumos-gate/usr/src/lib/libgss/g_glue.c (revision b56bf881a9655cb27b53cba1468312f7c6dfb0a2)
1 /*
2  * Copyright (c) 1996, 2010, Oracle and/or its affiliates. All rights reserved.
3  */
4 
5 #include "mglueP.h"
6 
7 #include <stdio.h>
8 #ifdef HAVE_STDLIB_H
9 #include <stdlib.h>
10 #endif
11 #include <string.h>
12 #include <errno.h>
13 
14 #include "k5-platform-store_32.h"
15 #include "k5-platform-store_16.h"
16 /*
17  * SUNW17PACresync
18  * MIT has diff names for these GSS utilities.  Solaris needs to change
19  * them globally to get in sync w/MIT.
20  * Revisit for full 1.7 resync.
21  */
22 #define gssint_get_modOptions __gss_get_modOptions
23 #define gssint_der_length_size der_length_size
24 #define gssint_get_der_length get_der_length
25 #define gssint_put_der_length put_der_length
26 #define gssint_get_mechanism __gss_get_mechanism
27 #define gssint_get_mechanism_cred __gss_get_mechanism_cred
28 #define gssint_copy_oid_set gss_copy_oid_set
29 #define gssint_get_mech_type __gss_get_mech_type
30 #define gssint_export_internal_name __gss_export_internal_name
31 #define gssint_release_internal_name __gss_release_internal_name
32 #define gssint_convert_name_to_union_name __gss_convert_name_to_union_name
33 #define gssint_import_internal_name __gss_import_internal_name
34 #define gssint_display_internal_name __gss_display_internal_name
35 
36 
37 #define	MSO_BIT (8*(sizeof (int) - 1))  /* Most significant octet bit */
38 
39 extern gss_mechanism *gssint_mechs_array;
40 
41 /*
42  * This file contains the support routines for the glue layer.
43  */
44 
45 /*
46  * get_der_length: Givin a pointer to a buffer that contains a DER encoded
47  * length, decode the length updating the buffer to point to the character
48  * after the DER encoding. The parameter bytes will point to the number of
49  * bytes that made up the DER encoding of the length originally pointed to
50  * by the buffer. Note we return -1 on error.
51  */
52 int
53 gssint_get_der_length(unsigned char **buf, unsigned int buf_len, unsigned int *bytes)
54 {
55     /* p points to the beginning of the buffer */
56     unsigned char *p = *buf;
57     int length, new_length;
58     unsigned int octets;
59 
60     if (buf_len < 1)
61 	return (-1);
62 
63     /* We should have at least one byte */
64     *bytes = 1;
65 
66     /*
67      * If the High order bit is not set then the length is just the value
68      * of *p.
69      */
70     if (*p < 128) {
71 	*buf = p+1;	/* Advance the buffer */
72 	return (*p);		/* return the length */
73     }
74 
75     /*
76      * if the High order bit is set, then the low order bits represent
77      * the number of bytes that contain the DER encoding of the length.
78      */
79 
80     octets = *p++ & 0x7f;
81     *bytes += octets;
82 
83     /* See if the supplied buffer contains enough bytes for the length. */
84     if (octets > buf_len - 1)
85 	return (-1);
86 
87     /*
88      * Calculate a multibyte length. The length is encoded as an
89      * unsigned integer base 256.
90      */
91     for (length = 0; octets; octets--) {
92 	new_length = (length << 8) + *p++;
93 	if (new_length < length)  /* overflow */
94 	    return (-1);
95 	length = new_length;
96     }
97 
98     *buf = p; /* Advance the buffer */
99 
100     return (length);
101 }
102 
103 /*
104  * der_length_size: Return the number of bytes to encode a given length.
105  */
106 unsigned int
107 gssint_der_length_size(unsigned int len)
108 {
109     int i;
110 
111     if (len < 128)
112 	return (1);
113 
114     for (i = 0; len; i++) {
115 	len >>= 8;
116     }
117 
118     return (i+1);
119 }
120 
121 /*
122  * put_der_length: Encode the supplied length into the buffer pointed to
123  * by buf. max_length represents the maximum length of the buffer pointed
124  * to by buff. We will advance buf to point to the character after the newly
125  * DER encoded length. We return 0 on success or -l it the length cannot
126  * be encoded in max_len characters.
127  */
128 int
129 gssint_put_der_length(unsigned int length, unsigned char **buf, unsigned int max_len)
130 {
131     unsigned char *s, *p;
132     unsigned int buf_len = 0;
133     int i, first;
134 
135     /* Oops */
136     if (buf == 0 || max_len < 1)
137 	return (-1);
138 
139     s = *buf;
140 
141     /* Single byte is the length */
142     if (length < 128) {
143 	*s++ = length;
144 	*buf = s;
145 	return (0);
146     }
147 
148     /* First byte contains the number of octets */
149     p = s + 1;
150 
151     /* Running total of the DER encoding length */
152     buf_len = 0;
153 
154     /*
155      * Encode MSB first. We do the encoding by setting a shift
156      * factor to MSO_BIT (24 for 32 bit words) and then shifting the length
157      * by the factor. We then encode the resulting low order byte.
158      * We subtract 8 from the shift factor and repeat to ecnode the next
159      * byte. We stop when the shift factor is zero or we've run out of
160      * buffer to encode into.
161      */
162     first = 0;
163     for (i = MSO_BIT; i >= 0 && buf_len <= max_len; i -= 8) {
164 	unsigned int v;
165 	v = (length >> i) & 0xff;
166 	if ((v) || first) {
167 	    buf_len += 1;
168 	    *p++ = v;
169 	    first = 1;
170 	}
171     }
172     if (i >= 0)			/* buffer overflow */
173 	return (-1);
174 
175     /*
176      * We go back now and set the first byte to be the length with
177      * the high order bit set.
178      */
179     *s = buf_len | 0x80;
180     *buf = p;
181 
182     return (0);
183 }
184 
185 
186 /*
187  *  glue routine for get_mech_type
188  *
189  */
190 
191 OM_uint32 gssint_get_mech_type_oid(OID, token)
192     gss_OID		OID;
193     gss_buffer_t	token;
194 {
195     unsigned char * buffer_ptr;
196     int length;
197 
198     /*
199      * This routine reads the prefix of "token" in order to determine
200      * its mechanism type. It assumes the encoding suggested in
201      * Appendix B of RFC 1508. This format starts out as follows :
202      *
203      * tag for APPLICATION 0, Sequence[constructed, definite length]
204      * length of remainder of token
205      * tag of OBJECT IDENTIFIER
206      * length of mechanism OID
207      * encoding of mechanism OID
208      * <the rest of the token>
209      *
210      * Numerically, this looks like :
211      *
212      * 0x60
213      * <length> - could be multiple bytes
214      * 0x06
215      * <length> - assume only one byte, hence OID length < 127
216      * <mech OID bytes>
217      *
218      * The routine fills in the OID value and returns an error as necessary.
219      */
220 
221 	if (OID == NULL)
222 		return (GSS_S_CALL_INACCESSIBLE_WRITE);
223 
224 	if ((token == NULL) || (token->value == NULL))
225 	return (GSS_S_DEFECTIVE_TOKEN);
226 
227     /* Skip past the APP/Sequnce byte and the token length */
228 
229     buffer_ptr = (unsigned char *) token->value;
230 
231     if (*(buffer_ptr++) != 0x60)
232 	return (GSS_S_DEFECTIVE_TOKEN);
233     length = *buffer_ptr++;
234 
235 	/* check if token length is null */
236 	if (length == 0)
237 	    return (GSS_S_DEFECTIVE_TOKEN);
238 
239     if (length & 0x80) {
240 	if ((length & 0x7f) > 4)
241 	    return (GSS_S_DEFECTIVE_TOKEN);
242 	buffer_ptr += length & 0x7f;
243     }
244 
245     if (*(buffer_ptr++) != 0x06)
246 	return (GSS_S_DEFECTIVE_TOKEN);
247 
248     OID->length = (OM_uint32) *(buffer_ptr++);
249     OID->elements = (void *) buffer_ptr;
250     return (GSS_S_COMPLETE);
251 }
252 
253 /*
254  * The following mechanisms do not always identify themselves
255  * per the GSS-API specification, when interoperating with MS
256  * peers. We include the OIDs here so we do not have to link
257  * with the mechanism.
258  */
259 static gss_OID_desc gss_ntlm_mechanism_oid_desc =
260 	{10, (void *)"\x2b\x06\x01\x04\x01\x82\x37\x02\x02\x0a"};
261 static gss_OID_desc gss_spnego_mechanism_oid_desc =
262 	{6, (void *)"\x2b\x06\x01\x05\x05\x02"};
263 static gss_OID_desc gss_krb5_mechanism_oid_desc =
264 	{9, (void *)"\x2a\x86\x48\x86\xf7\x12\x01\x02\x02"};
265 
266 #define NTLMSSP_SIGNATURE "NTLMSSP"
267 
268 OM_uint32 gssint_get_mech_type(OID, token)
269     gss_OID		OID;
270     gss_buffer_t	token;
271 {
272     /* Check for interoperability exceptions */
273     if (token->length >= sizeof(NTLMSSP_SIGNATURE) &&
274 	memcmp(token->value, NTLMSSP_SIGNATURE,
275 	       sizeof(NTLMSSP_SIGNATURE)) == 0) {
276 	*OID = gss_ntlm_mechanism_oid_desc;
277     } else if (token->length != 0 &&
278 	       ((char *)token->value)[0] == 0x6E) {
279  	/* Could be a raw AP-REQ (check for APPLICATION tag) */
280 	*OID = gss_krb5_mechanism_oid_desc;
281     } else if (token->length == 0) {
282 	*OID = gss_spnego_mechanism_oid_desc;
283     } else {
284 	return gssint_get_mech_type_oid(OID, token);
285     }
286 
287     return (GSS_S_COMPLETE);
288 }
289 
290 
291 /*
292  *  Internal routines to get and release an internal mechanism name
293  */
294 
295 #if 0 /* SUNW17PACresync */
296 #include "mglueP.h"
297 #endif
298 
299 OM_uint32 gssint_import_internal_name (minor_status, mech_type, union_name,
300 				internal_name)
301 OM_uint32		*minor_status;
302 gss_OID			mech_type;
303 gss_union_name_t	union_name;
304 gss_name_t		*internal_name;
305 {
306     OM_uint32		status;
307     gss_mechanism	mech;
308 
309     mech = gssint_get_mechanism (mech_type);
310     if (mech) {
311 	if (mech->gss_import_name) {
312 	    status = mech->gss_import_name (
313 		    mech->context, /* SUNW17PACresync */
314 					    minor_status,
315 					    union_name->external_name,
316 					    union_name->name_type,
317 					    internal_name);
318 	    if (status != GSS_S_COMPLETE)
319 		map_error(minor_status, mech);
320 	} else
321 	    status = GSS_S_UNAVAILABLE;
322 
323 	return (status);
324     }
325 
326     return (GSS_S_BAD_MECH);
327 }
328 
329 OM_uint32 gssint_export_internal_name(minor_status, mech_type,
330 				     internal_name, name_buf)
331     OM_uint32		*minor_status;
332     const gss_OID		mech_type;
333     const gss_name_t	internal_name;
334     gss_buffer_t		name_buf;
335 {
336     OM_uint32 status;
337     gss_mechanism mech;
338     gss_buffer_desc dispName;
339     gss_OID nameOid;
340     unsigned char *buf = NULL;
341     const unsigned char tokId[] = "\x04\x01";
342     const unsigned int tokIdLen = 2;
343     const int mechOidLenLen = 2, mechOidTagLen = 1, nameLenLen = 4;
344     int mechOidDERLen = 0;
345     int mechOidLen = 0;
346 
347     mech = gssint_get_mechanism(mech_type);
348     if (!mech)
349 	return (GSS_S_BAD_MECH);
350 
351     if (mech->gss_export_name) {
352 	status = mech->gss_export_name(
353 		mech->context,  /* SUNW17PACresync */
354 		minor_status,
355 		internal_name,
356 		name_buf);
357 	if (status != GSS_S_COMPLETE)
358 	    map_error(minor_status, mech);
359 	return status;
360     }
361 
362     /*
363      * if we are here it is because the mechanism does not provide
364      * a gss_export_name so we will use our implementation.  We
365      * do required that the mechanism define a gss_display_name.
366      */
367     if (!mech->gss_display_name)
368 	return (GSS_S_UNAVAILABLE);
369 
370     /*
371      * NOTE: RFC2743 (section 3.2) governs the format of the outer
372      *	 wrapper of exported names; the mechanisms' specs govern
373      *	 the format of the inner portion of the exported name
374      *	 and, for some (e.g., RFC1964, the Kerberos V mech), a
375      *	 generic default as implemented here will do.
376      *
377      * The outer wrapper of an exported MN is: 2-octet tok Id
378      * (0x0401) + 2-octet network-byte order mech OID length + mech
379      * oid (in DER format, including DER tag and DER length) +
380      * 4-octet network-byte order length of inner portion + inner
381      * portion.
382      *
383      * For the Kerberos V mechanism the inner portion of an exported
384      * MN is the display name string and ignores the name type OID
385      * altogether.  And we hope this will be so for any future
386      * mechanisms also, so that factoring name export/import out of
387      * the mech and into libgss pays off.
388      */
389     if ((status = mech->gss_display_name(
390 		mech->context,
391 		minor_status,
392 					 internal_name,
393 					 &dispName,
394 					 &nameOid))
395 	!= GSS_S_COMPLETE) {
396 	map_error(minor_status, mech);
397 	return (status);
398     }
399 
400     /* determine the size of the buffer needed */
401     mechOidDERLen = gssint_der_length_size(mech_type->length);
402     name_buf->length = tokIdLen + mechOidLenLen +
403 	mechOidTagLen + mechOidDERLen +
404 	mech_type->length +
405 	nameLenLen + dispName.length;
406     if ((name_buf->value = (void*)malloc(name_buf->length)) ==
407 	(void*)NULL) {
408 	name_buf->length = 0;
409 	(void) gss_release_buffer(&status, &dispName);
410 	return (GSS_S_FAILURE);
411     }
412 
413     /* now create the name ..... */
414     buf = (unsigned char *)name_buf->value;
415     (void) memset(name_buf->value, 0, name_buf->length);
416     (void) memcpy(buf, tokId, tokIdLen);
417     buf += tokIdLen;
418 
419     /* spec allows only 2 bytes for the mech oid length */
420     mechOidLen = mechOidDERLen + mechOidTagLen + mech_type->length;
421     store_16_be(mechOidLen, buf);
422     buf += 2;
423 
424     /*
425      * DER Encoding of mech OID contains OID Tag (0x06), length and
426      * mech OID value
427      */
428     *buf++ = 0x06;
429     if (gssint_put_der_length(mech_type->length, &buf,
430 		       (name_buf->length - tokIdLen -2)) != 0) {
431 	name_buf->length = 0;
432 	free(name_buf->value);
433 	(void) gss_release_buffer(&status, &dispName);
434 	return (GSS_S_FAILURE);
435     }
436 
437     (void) memcpy(buf, mech_type->elements, mech_type->length);
438     buf += mech_type->length;
439 
440     /* spec designates the next 4 bytes for the name length */
441     store_32_be(dispName.length, buf);
442     buf += 4;
443 
444     /* for the final ingredient - add the name from gss_display_name */
445     (void) memcpy(buf, dispName.value, dispName.length);
446 
447     /* release the buffer obtained from gss_display_name */
448     (void) gss_release_buffer(minor_status, &dispName);
449     return (GSS_S_COMPLETE);
450 } /*  gssint_export_internal_name */
451 
452 OM_uint32 gssint_display_internal_name (minor_status, mech_type, internal_name,
453 				 external_name, name_type)
454 OM_uint32	*minor_status;
455 gss_OID		mech_type;
456 gss_name_t	internal_name;
457 gss_buffer_t	external_name;
458 gss_OID		*name_type;
459 {
460     OM_uint32		status;
461     gss_mechanism	mech;
462 
463     mech = gssint_get_mechanism (mech_type);
464     if (mech) {
465 	if (mech->gss_display_name) {
466 	    status = mech->gss_display_name (
467 		    mech->context,
468 					     minor_status,
469 					     internal_name,
470 					     external_name,
471 					     name_type);
472 	    if (status != GSS_S_COMPLETE)
473 		map_error(minor_status, mech);
474 	} else
475 	    status = GSS_S_UNAVAILABLE;
476 
477 	return (status);
478     }
479 
480     return (GSS_S_BAD_MECH);
481 }
482 
483 OM_uint32 gssint_release_internal_name (minor_status, mech_type, internal_name)
484 OM_uint32	*minor_status;
485 gss_OID		mech_type;
486 gss_name_t	*internal_name;
487 {
488     OM_uint32		status;
489     gss_mechanism	mech;
490 
491     mech = gssint_get_mechanism (mech_type);
492     if (mech) {
493 	if (mech->gss_release_name) {
494 	    status = mech->gss_release_name (
495 		    mech->context,
496 					     minor_status,
497 					     internal_name);
498 	    if (status != GSS_S_COMPLETE)
499 		map_error(minor_status, mech);
500 	} else
501 	    status = GSS_S_UNAVAILABLE;
502 
503 	return (status);
504     }
505 
506     return (GSS_S_BAD_MECH);
507 }
508 
509 OM_uint32 gssint_delete_internal_sec_context (minor_status,
510 					      mech_type,
511 					      internal_ctx,
512 					      output_token)
513 OM_uint32	*minor_status;
514 gss_OID		mech_type;
515 gss_ctx_id_t	*internal_ctx;
516 gss_buffer_t	output_token;
517 {
518     OM_uint32		status;
519     gss_mechanism	mech;
520 
521     mech = gssint_get_mechanism (mech_type);
522     if (mech) {
523 	if (mech->gss_delete_sec_context)
524 	    status = mech->gss_delete_sec_context (
525 		    mech->context,  /* SUNW17PACresync */
526 		    minor_status,
527 		    internal_ctx,
528 		    output_token);
529 	else
530 	    /* SUNW17PACresync - map error here? */
531 	    status = GSS_S_UNAVAILABLE;
532 
533 	return (status);
534     }
535 
536     return (GSS_S_BAD_MECH);
537 }
538 
539 /*
540  * This function converts an internal gssapi name to a union gssapi
541  * name.  Note that internal_name should be considered "consumed" by
542  * this call, whether or not we return an error.
543  */
544 OM_uint32 gssint_convert_name_to_union_name(minor_status, mech,
545 					   internal_name, external_name)
546     OM_uint32 *minor_status;
547     gss_mechanism	mech;
548     gss_name_t	internal_name;
549     gss_name_t	*external_name;
550 {
551     OM_uint32 major_status,tmp;
552     gss_union_name_t union_name;
553 
554     union_name = (gss_union_name_t) malloc (sizeof(gss_union_name_desc));
555     if (!union_name) {
556 	major_status = GSS_S_FAILURE;
557 	*minor_status = ENOMEM;
558 	map_errcode(minor_status);
559 	goto allocation_failure;
560     }
561     union_name->mech_type = 0;
562     union_name->mech_name = internal_name;
563     union_name->name_type = 0;
564     union_name->external_name = 0;
565 
566     major_status = generic_gss_copy_oid(minor_status, &mech->mech_type,
567 					&union_name->mech_type);
568     if (major_status != GSS_S_COMPLETE) {
569 	map_errcode(minor_status);
570 	goto allocation_failure;
571     }
572 
573     union_name->external_name =
574 	(gss_buffer_t) malloc(sizeof(gss_buffer_desc));
575     if (!union_name->external_name) {
576 	    major_status = GSS_S_FAILURE;
577 	    *minor_status = ENOMEM;
578 	    goto allocation_failure;
579     }
580 
581     union_name->external_name->length = 0;
582     union_name->external_name->value = 0;
583 
584     major_status = mech->gss_display_name(
585 	    mech->context,  /* SUNW17PACresync */
586 	    minor_status,
587 	    internal_name,
588 	    union_name->external_name,
589 	    &union_name->name_type);
590     if (major_status != GSS_S_COMPLETE) {
591 	map_error(minor_status, mech);
592 	goto allocation_failure;
593     }
594 
595     union_name->loopback = union_name;
596     *external_name = (gss_name_t) union_name;
597     return (GSS_S_COMPLETE);
598 
599 allocation_failure:
600     if (union_name) {
601 	if (union_name->external_name) {
602 	    if (union_name->external_name->value)
603 		free(union_name->external_name->value);
604 	    free(union_name->external_name);
605 	}
606 	if (union_name->name_type)
607 		(void) gss_release_oid(&tmp, &union_name->name_type);
608 	if (union_name->mech_type)
609 		(void) gss_release_oid(&tmp, &union_name->mech_type);
610 	free(union_name);
611     }
612     /*
613      * do as the top comment says - since we are now owners of
614      * internal_name, we must clean it up
615      */
616     if (internal_name)
617 	(void) gssint_release_internal_name(&tmp, &mech->mech_type,
618 					   &internal_name);
619     return (major_status);
620 }
621 
622 /*
623  * Glue routine for returning the mechanism-specific credential from a
624  * external union credential.
625  */
626 gss_cred_id_t
627 gssint_get_mechanism_cred(union_cred, mech_type)
628     gss_union_cred_t	union_cred;
629     gss_OID		mech_type;
630 {
631     int		i;
632 
633     if (union_cred == (gss_union_cred_t) GSS_C_NO_CREDENTIAL)
634 	return GSS_C_NO_CREDENTIAL;
635 
636     /*
637      * SUNW17PACresync
638      * Disable this block as it causes problems for gss_add_cred
639      * for HTTP SSO (and also probably causes STC gss.13 to fail too).
640      */
641 #if 0
642     /* SPNEGO mechanism will again call into GSSAPI */
643     if (g_OID_equal(&gss_spnego_mechanism_oid_desc, mech_type))
644 	return (gss_cred_id_t)union_cred;
645 #endif
646 
647     for (i=0; i < union_cred->count; i++) {
648 	if (g_OID_equal(mech_type, &union_cred->mechs_array[i]))
649 	    return union_cred->cred_array[i];
650 
651 	/* for SPNEGO, check the next-lower set of creds */
652 	if (g_OID_equal(&gss_spnego_mechanism_oid_desc, &union_cred->mechs_array[i])) {
653 	    gss_union_cred_t candidate_cred;
654 	    gss_cred_id_t    sub_cred;
655 
656 	    candidate_cred = (gss_union_cred_t)union_cred->cred_array[i];
657 	    sub_cred = gssint_get_mechanism_cred(candidate_cred, mech_type);
658 
659 	    if(sub_cred != GSS_C_NO_CREDENTIAL)
660 		return sub_cred;
661 	}
662     }
663 
664     return GSS_C_NO_CREDENTIAL;
665 }
666 
667 /*
668  * Routine to create and copy the gss_buffer_desc structure.
669  * Both space for the structure and the data is allocated.
670  */
671 OM_uint32
672 gssint_create_copy_buffer(srcBuf, destBuf, addNullChar)
673     const gss_buffer_t	srcBuf;
674     gss_buffer_t 		*destBuf;
675     int			addNullChar;
676 {
677     gss_buffer_t aBuf;
678     unsigned int len;
679 
680     if (destBuf == NULL)
681 	return (GSS_S_CALL_INACCESSIBLE_WRITE);
682 
683     *destBuf = 0;
684 
685     aBuf = (gss_buffer_t)malloc(sizeof (gss_buffer_desc));
686     if (!aBuf)
687 	return (GSS_S_FAILURE);
688 
689     if (addNullChar)
690 	len = srcBuf->length + 1;
691     else
692 	len = srcBuf->length;
693 
694     if (!(aBuf->value = (void*)malloc(len))) {
695 	free(aBuf);
696 	return (GSS_S_FAILURE);
697     }
698 
699 
700     (void) memcpy(aBuf->value, srcBuf->value, srcBuf->length);
701     aBuf->length = srcBuf->length;
702     *destBuf = aBuf;
703 
704     /* optionally add a NULL character */
705     if (addNullChar)
706 	((char *)aBuf->value)[aBuf->length] = '\0';
707 
708     return (GSS_S_COMPLETE);
709 } /* ****** gssint_create_copy_buffer  ****** */
710 
711