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