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