xref: /illumos-gate/usr/src/lib/libgss/g_glue.c (revision 3db86aab)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <mechglueP.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <strings.h>
33 #include <errno.h>
34 
35 #define	MSO_BIT (8*(sizeof (int) - 1))  /* Most significant octet bit */
36 
37 /*
38  * This file contains the support routines for the glue layer.
39  */
40 
41 /*
42  * get_der_length: Givin a pointer to a buffer that contains a DER encoded
43  * length, decode the length updating the buffer to point to the character
44  * after the DER encoding. The parameter bytes will point to the number of
45  * bytes that made up the DER encoding of the length originally pointed to
46  * by the buffer. Note we return -1 on error.
47  */
48 int
49 get_der_length(unsigned char **buf, unsigned int buf_len, unsigned int *bytes)
50 {
51 	/* p points to the beginning of the buffer */
52 	unsigned char *p = *buf;
53 	int length, new_length;
54 	int octets;
55 
56 	if (buf_len < 1)
57 		return (-1);
58 
59 	/* We should have at least one byte */
60 	*bytes = 1;
61 
62 	/*
63 	 * If the High order bit is not set then the length is just the value
64 	 * of *p.
65 	 */
66 	if (*p < 128) {
67 		*buf = p+1;	/* Advance the buffer */
68 	return (*p);		/* return the length */
69 	}
70 
71 	/*
72 	 * if the High order bit is set, then the low order bits represent
73 	 * the number of bytes that contain the DER encoding of the length.
74 	 */
75 
76 	octets = *p++ & 0x7f;
77 	*bytes += octets;
78 
79 	/* See if the supplied buffer contains enough bytes for the length. */
80 	if (octets > buf_len - 1)
81 		return (-1);
82 
83 	/*
84 	 * Calculate a multibyte length. The length is encoded as an
85 	 * unsigned integer base 256.
86 	 */
87 	for (length = 0; octets; octets--) {
88 		new_length = (length << 8) + *p++;
89 		if (new_length < length)  /* overflow */
90 			return (-1);
91 		length = new_length;
92 	}
93 
94 	*buf = p; /* Advance the buffer */
95 
96 	return (length);
97 }
98 
99 /*
100  * der_length_size: Return the number of bytes to encode a given length.
101  */
102 unsigned int
103 der_length_size(unsigned int len)
104 {
105 	int i;
106 
107 	if (len < 128)
108 		return (1);
109 
110 	for (i = 0; len; i++) {
111 		len >>= 8;
112 	}
113 
114 	return (i+1);
115 }
116 
117 /*
118  * put_der_length: Encode the supplied length into the buffer pointed to
119  * by buf. max_length represents the maximum length of the buffer pointed
120  * to by buff. We will advance buf to point to the character after the newly
121  * DER encoded length. We return 0 on success or -l it the length cannot
122  * be encoded in max_len characters.
123  */
124 int
125 put_der_length(unsigned length, unsigned char **buf, unsigned int max_len)
126 {
127 	unsigned char *s = *buf, *p;
128 	unsigned int buf_len = 0;
129 	int i, first;
130 
131 	/* Oops */
132 	if (buf == 0 || max_len < 1)
133 		return (-1);
134 
135 	/* Single byte is the length */
136 	if (length < 128) {
137 		*s++ = length;
138 		*buf = s;
139 		return (0);
140 	}
141 
142 	/* First byte contains the number of octets */
143 	p = s + 1;
144 
145 	/* Running total of the DER encoding length */
146 	buf_len = 0;
147 
148 	/*
149 	 * Encode MSB first. We do the encoding by setting a shift
150 	 * factor to MSO_BIT (24 for 32 bit words) and then shifting the length
151 	 * by the factor. We then encode the resulting low order byte.
152 	 * We subtract 8 from the shift factor and repeat to ecnode the next
153 	 * byte. We stop when the shift factor is zero or we've run out of
154 	 * buffer to encode into.
155 	 */
156 	first = 0;
157 	for (i = MSO_BIT; i >= 0 && buf_len <= max_len; i -= 8) {
158 		unsigned int v;
159 		v = (length >> i) & 0xff;
160 		if ((v) || first) {
161 			buf_len += 1;
162 			*p++ = v;
163 			first = 1;
164 		}
165 	}
166 	if (i >= 0)			/* buffer overflow */
167 		return (-1);
168 
169 	/*
170 	 * We go back now and set the first byte to be the length with
171 	 * the high order bit set.
172 	 */
173 	*s = buf_len | 0x80;
174 	*buf = p;
175 
176 	return (0);
177 }
178 
179 
180 /*
181  *  glue routine for get_mech_type
182  *
183  */
184 OM_uint32
185 __gss_get_mech_type(OID, token)
186 	gss_OID			OID;
187 	const gss_buffer_t	token;
188 {
189 	unsigned char *buffer_ptr;
190 	int length;
191 
192 	/*
193 	 * This routine reads the prefix of "token" in order to determine
194 	 * its mechanism type. It assumes the encoding suggested in
195 	 * Appendix B of RFC 1508. This format starts out as follows :
196 	 *
197 	 * tag for APPLICATION 0, Sequence[constructed, definite length]
198 	 * length of remainder of token
199 	 * tag of OBJECT IDENTIFIER
200 	 * length of mechanism OID
201 	 * encoding of mechanism OID
202 	 * <the rest of the token>
203 	 *
204 	 * Numerically, this looks like :
205 	 *
206 	 * 0x60
207 	 * <length> - could be multiple bytes
208 	 * 0x06
209 	 * <length> - assume only one byte, hence OID length < 127
210 	 * <mech OID bytes>
211 	 *
212 	 * The routine fills in the OID value and returns an error as necessary.
213 	 */
214 
215 	if (OID == NULL)
216 		return (GSS_S_CALL_INACCESSIBLE_WRITE);
217 
218 	if ((token == NULL) || (token->value == NULL))
219 		return (GSS_S_DEFECTIVE_TOKEN);
220 
221 	/* Skip past the APP/Sequnce byte and the token length */
222 
223 	buffer_ptr = (unsigned char *) token->value;
224 
225 	if (*(buffer_ptr++) != 0x60)
226 		return (GSS_S_DEFECTIVE_TOKEN);
227 	length = *buffer_ptr++;
228 
229 	/* check if token length is null */
230 	if (length == 0)
231 	    return (GSS_S_DEFECTIVE_TOKEN);
232 
233 	if (length & 0x80) {
234 		if ((length & 0x7f) > 4)
235 			return (GSS_S_DEFECTIVE_TOKEN);
236 		buffer_ptr += length & 0x7f;
237 	}
238 
239 	if (*(buffer_ptr++) != 0x06)
240 		return (GSS_S_DEFECTIVE_TOKEN);
241 
242 	OID->length = (OM_uint32) *(buffer_ptr++);
243 	OID->elements = (void *) buffer_ptr;
244 	return (GSS_S_COMPLETE);
245 }
246 
247 
248 /*
249  *  Internal routines to get and release an internal mechanism name
250  */
251 OM_uint32 __gss_import_internal_name(minor_status, mech_type, union_name,
252 					internal_name)
253 OM_uint32		*minor_status;
254 const gss_OID		mech_type;
255 gss_union_name_t	union_name;
256 gss_name_t		*internal_name;
257 {
258 	OM_uint32			status;
259 	gss_mechanism		mech;
260 
261 	mech = __gss_get_mechanism(mech_type);
262 	if (mech) {
263 		if (mech->gss_import_name)
264 			status = mech->gss_import_name(
265 						mech->context,
266 						minor_status,
267 						union_name->external_name,
268 						union_name->name_type,
269 						internal_name);
270 		else
271 			status = GSS_S_UNAVAILABLE;
272 
273 		return (status);
274 	}
275 
276 	return (GSS_S_BAD_MECH);
277 }
278 
279 
280 OM_uint32 __gss_export_internal_name(minor_status, mech_type,
281 		internal_name, name_buf)
282 OM_uint32		*minor_status;
283 const gss_OID		mech_type;
284 const gss_name_t	internal_name;
285 gss_buffer_t		name_buf;
286 {
287 	OM_uint32 status;
288 	gss_mechanism mech;
289 	gss_buffer_desc dispName;
290 	gss_OID nameOid;
291 	unsigned char *buf = NULL;
292 	const unsigned char tokId[] = "\x04\x01";
293 	const int tokIdLen = 2;
294 	const int mechOidLenLen = 2, mechOidTagLen = 1, nameLenLen = 4;
295 	int mechOidDERLen = 0;
296 	int mechOidLen = 0;
297 
298 	mech = __gss_get_mechanism(mech_type);
299 	if (!mech)
300 		return (GSS_S_BAD_MECH);
301 
302 	if (mech->gss_export_name)
303 		return (mech->gss_export_name(mech->context,
304 						minor_status,
305 						internal_name,
306 						name_buf));
307 
308 	/*
309 	 * if we are here it is because the mechanism does not provide
310 	 * a gss_export_name so we will use our implementation.  We
311 	 * do required that the mechanism define a gss_display_name.
312 	 */
313 	if (!mech->gss_display_name)
314 		return (GSS_S_UNAVAILABLE);
315 
316 	/*
317 	 * NOTE: RFC2743 (section 3.2) governs the format of the outer
318 	 *	 wrapper of exported names; the mechanisms' specs govern
319 	 *	 the format of the inner portion of the exported name
320 	 *	 and, for some (e.g., RFC1964, the Kerberos V mech), a
321 	 *	 generic default as implemented here will do.
322 	 *
323 	 * The outer wrapper of an exported MN is: 2-octet tok Id
324 	 * (0x0401) + 2-octet network-byte order mech OID length + mech
325 	 * oid (in DER format, including DER tag and DER length) +
326 	 * 4-octet network-byte order length of inner portion + inner
327 	 * portion.
328 	 *
329 	 * For the Kerberos V mechanism the inner portion of an exported
330 	 * MN is the display name string and ignores the name type OID
331 	 * altogether.  And we hope this will be so for any future
332 	 * mechanisms also, so that factoring name export/import out of
333 	 * the mech and into libgss pays off.
334 	 */
335 	if ((status = mech->gss_display_name(mech->context,
336 						minor_status,
337 						internal_name,
338 						&dispName,
339 						&nameOid))
340 						!= GSS_S_COMPLETE)
341 		return (status);
342 
343 	/* determine the size of the buffer needed */
344 	mechOidDERLen = der_length_size(mech_type->length);
345 	name_buf->length = tokIdLen + mechOidLenLen +
346 				mechOidTagLen + mechOidDERLen +
347 				mech_type->length +
348 				nameLenLen + dispName.length;
349 	if ((name_buf->value = (void*)malloc(name_buf->length)) ==
350 		(void*)NULL) {
351 			name_buf->length = 0;
352 			(void) gss_release_buffer(&status, &dispName);
353 			return (GSS_S_FAILURE);
354 	}
355 
356 	/* now create the name ..... */
357 	buf = (unsigned char *)name_buf->value;
358 	(void) memset(name_buf->value, 0, name_buf->length);
359 	(void) memcpy(buf, tokId, tokIdLen);
360 	buf += tokIdLen;
361 
362 	/* spec allows only 2 bytes for the mech oid length */
363 	mechOidLen = mechOidDERLen + mechOidTagLen + mech_type->length;
364 	*buf++ = (mechOidLen & 0xFF00) >> 8;
365 	*buf++ = (mechOidLen & 0x00FF);
366 
367 	/*
368 	 * DER Encoding of mech OID contains OID Tag (0x06), length and
369 	 * mech OID value
370 	 */
371 	*buf++ = 0x06;
372 	if (put_der_length(mech_type->length, &buf,
373 		(name_buf->length - tokIdLen -2)) != 0) {
374 		name_buf->length = 0;
375 		free(name_buf->value);
376 		(void) gss_release_buffer(&status, &dispName);
377 		return (GSS_S_FAILURE);
378 	}
379 
380 	(void) memcpy(buf, mech_type->elements, mech_type->length);
381 	buf += mech_type->length;
382 
383 	/* spec designates the next 4 bytes for the name length */
384 	*buf++ = (dispName.length & 0xFF000000) >> 24;
385 	*buf++ = (dispName.length & 0x00FF0000) >> 16;
386 	*buf++ = (dispName.length & 0x0000FF00) >> 8;
387 	*buf++ = (dispName.length & 0X000000FF);
388 
389 	/* for the final ingredient - add the name from gss_display_name */
390 	(void) memcpy(buf, dispName.value, dispName.length);
391 
392 	/* release the buffer obtained from gss_display_name */
393 	(void) gss_release_buffer(minor_status, &dispName);
394 	return (GSS_S_COMPLETE);
395 } /*  __gss_export_internal_name */
396 
397 
398 OM_uint32 __gss_display_internal_name(minor_status, mech_type, internal_name,
399 						external_name, name_type)
400 OM_uint32		*minor_status;
401 const gss_OID		mech_type;
402 const gss_name_t	internal_name;
403 gss_buffer_t		external_name;
404 gss_OID			*name_type;
405 {
406 	OM_uint32			status;
407 	gss_mechanism		mech;
408 
409 	mech = __gss_get_mechanism(mech_type);
410 	if (mech) {
411 		if (mech->gss_display_name)
412 			status = mech->gss_display_name(
413 							mech->context,
414 							minor_status,
415 							internal_name,
416 							external_name,
417 							name_type);
418 		else
419 			status = GSS_S_UNAVAILABLE;
420 
421 		return (status);
422 	}
423 
424 	return (GSS_S_BAD_MECH);
425 }
426 
427 OM_uint32
428 __gss_release_internal_name(minor_status, mech_type, internal_name)
429 OM_uint32		*minor_status;
430 const gss_OID		mech_type;
431 gss_name_t		*internal_name;
432 {
433 	OM_uint32			status;
434 	gss_mechanism		mech;
435 
436 	mech = __gss_get_mechanism(mech_type);
437 	if (mech) {
438 		if (mech->gss_release_name)
439 			status = mech->gss_release_name(
440 							mech->context,
441 							minor_status,
442 							internal_name);
443 		else
444 			status = GSS_S_UNAVAILABLE;
445 
446 		return (status);
447 	}
448 
449 	return (GSS_S_BAD_MECH);
450 }
451 
452 
453 /*
454  * This function converts an internal gssapi name to a union gssapi
455  * name.  Note that internal_name should be considered "consumed" by
456  * this call, whether or not we return an error.
457  */
458 OM_uint32 __gss_convert_name_to_union_name(minor_status, mech,
459 						internal_name, external_name)
460 	OM_uint32 *minor_status;
461 	gss_mechanism		mech;
462 	gss_name_t		internal_name;
463 	gss_name_t		*external_name;
464 {
465 	OM_uint32 major_status, tmp;
466 	gss_union_name_t union_name;
467 
468 	union_name = (gss_union_name_t)malloc(sizeof (gss_union_name_desc));
469 	if (!union_name) {
470 			goto allocation_failure;
471 	}
472 	union_name->mech_type = 0;
473 	union_name->mech_name = internal_name;
474 	union_name->name_type = 0;
475 	union_name->external_name = 0;
476 
477 	major_status = generic_gss_copy_oid(minor_status, &mech->mech_type,
478 						&union_name->mech_type);
479 	if (major_status != GSS_S_COMPLETE)
480 		goto allocation_failure;
481 
482 	union_name->external_name =
483 		(gss_buffer_t)malloc(sizeof (gss_buffer_desc));
484 	if (!union_name->external_name) {
485 			goto allocation_failure;
486 	}
487 
488 	major_status = mech->gss_display_name(mech->context, minor_status,
489 						internal_name,
490 						union_name->external_name,
491 						&union_name->name_type);
492 	if (major_status != GSS_S_COMPLETE)
493 		goto allocation_failure;
494 
495 	*external_name =  (gss_name_t)union_name;
496 	return (GSS_S_COMPLETE);
497 
498 allocation_failure:
499 	if (union_name) {
500 		if (union_name->external_name) {
501 			if (union_name->external_name->value)
502 				free(union_name->external_name->value);
503 			free(union_name->external_name);
504 		}
505 		if (union_name->name_type)
506 			(void) gss_release_oid(&tmp, &union_name->name_type);
507 		if (union_name->mech_type)
508 			(void) gss_release_oid(&tmp, &union_name->mech_type);
509 		free(union_name);
510 	}
511 	/*
512 	 * do as the top comment says - since we are now owners of
513 	 * internal_name, we must clean it up
514 	 */
515 	if (internal_name)
516 		(void) __gss_release_internal_name(&tmp, &mech->mech_type,
517 						&internal_name);
518 
519 	return (major_status);
520 }
521 
522 /*
523  * Glue routine for returning the mechanism-specific credential from a
524  * external union credential.
525  */
526 gss_cred_id_t
527 __gss_get_mechanism_cred(union_cred, mech_type)
528 	const gss_union_cred_t	union_cred;
529 	const gss_OID		mech_type;
530 {
531 	int			i;
532 
533 	if (union_cred == (gss_union_cred_t)GSS_C_NO_CREDENTIAL)
534 		return (GSS_C_NO_CREDENTIAL);
535 
536 	for (i = 0; i < union_cred->count; i++) {
537 		if (g_OID_equal(mech_type, &union_cred->mechs_array[i]))
538 			return (union_cred->cred_array[i]);
539 	}
540 	return (GSS_C_NO_CREDENTIAL);
541 }
542 
543 
544 /*
545  * Routine to create and copy the gss_buffer_desc structure.
546  * Both space for the structure and the data is allocated.
547  */
548 OM_uint32
549 __gss_create_copy_buffer(srcBuf, destBuf, addNullChar)
550 	const gss_buffer_t	srcBuf;
551 	gss_buffer_t 		*destBuf;
552 	int			addNullChar;
553 {
554 	gss_buffer_t aBuf;
555 	int len;
556 
557 	if (destBuf == NULL)
558 		return (GSS_S_CALL_INACCESSIBLE_WRITE);
559 
560 	*destBuf = 0;
561 
562 	aBuf = (gss_buffer_t)malloc(sizeof (gss_buffer_desc));
563 	if (!aBuf)
564 		return (GSS_S_FAILURE);
565 
566 	if (addNullChar)
567 		len = srcBuf->length + 1;
568 	else
569 		len = srcBuf->length;
570 
571 	if (!(aBuf->value = (void*)malloc(len))) {
572 		free(aBuf);
573 		return (GSS_S_FAILURE);
574 	}
575 
576 
577 	(void) memcpy(aBuf->value, srcBuf->value, srcBuf->length);
578 	aBuf->length = srcBuf->length;
579 	*destBuf = aBuf;
580 
581 	/* optionally add a NULL character */
582 	if (addNullChar)
583 		((char *)aBuf->value)[aBuf->length] = '\0';
584 
585 	return (GSS_S_COMPLETE);
586 } /* ****** __gss_create_copy_buffer  ****** */
587