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