2 * Copyright (c) 1996, 2010, Oracle and/or its affiliates. All rights reserved.
3 * Copyright 2011 Nexenta Systems, Inc. All rights reserved.
7 #include "gssapiP_generic.h"
15 #include "k5-platform-store_32.h"
16 #include "k5-platform-store_16.h"
19 * MIT has diff names for these GSS utilities. Solaris needs to change
20 * them globally to get in sync w/MIT.
21 * Revisit for full 1.7 resync.
23 #define gssint_get_modOptions __gss_get_modOptions
24 #define gssint_der_length_size der_length_size
25 #define gssint_get_der_length get_der_length
26 #define gssint_put_der_length put_der_length
27 #define gssint_get_mechanism __gss_get_mechanism
28 #define gssint_get_mechanism_cred __gss_get_mechanism_cred
29 #define gssint_copy_oid_set gss_copy_oid_set
30 #define gssint_get_mech_type __gss_get_mech_type
31 #define gssint_export_internal_name __gss_export_internal_name
32 #define gssint_release_internal_name __gss_release_internal_name
33 #define gssint_convert_name_to_union_name __gss_convert_name_to_union_name
34 #define gssint_import_internal_name __gss_import_internal_name
35 #define gssint_display_internal_name __gss_display_internal_name
38 #define MSO_BIT (8*(sizeof (int) - 1)) /* Most significant octet bit */
40 extern gss_mechanism
*gssint_mechs_array
;
43 * This file contains the support routines for the glue layer.
47 * get_der_length: Givin a pointer to a buffer that contains a DER encoded
48 * length, decode the length updating the buffer to point to the character
49 * after the DER encoding. The parameter bytes will point to the number of
50 * bytes that made up the DER encoding of the length originally pointed to
51 * by the buffer. Note we return -1 on error.
54 gssint_get_der_length(unsigned char **buf
, unsigned int buf_len
, unsigned int *bytes
)
56 /* p points to the beginning of the buffer */
57 unsigned char *p
= *buf
;
58 int length
, new_length
;
64 /* We should have at least one byte */
68 * If the High order bit is not set then the length is just the value
72 *buf
= p
+1; /* Advance the buffer */
73 return (*p
); /* return the length */
77 * if the High order bit is set, then the low order bits represent
78 * the number of bytes that contain the DER encoding of the length.
84 /* See if the supplied buffer contains enough bytes for the length. */
85 if (octets
> buf_len
- 1)
89 * Calculate a multibyte length. The length is encoded as an
90 * unsigned integer base 256.
92 for (length
= 0; octets
; octets
--) {
93 new_length
= (length
<< 8) + *p
++;
94 if (new_length
< length
) /* overflow */
99 *buf
= p
; /* Advance the buffer */
105 * der_length_size: Return the number of bytes to encode a given length.
108 gssint_der_length_size(unsigned int len
)
115 for (i
= 0; len
; i
++) {
123 * put_der_length: Encode the supplied length into the buffer pointed to
124 * by buf. max_length represents the maximum length of the buffer pointed
125 * to by buff. We will advance buf to point to the character after the newly
126 * DER encoded length. We return 0 on success or -l it the length cannot
127 * be encoded in max_len characters.
130 gssint_put_der_length(unsigned int length
, unsigned char **buf
, unsigned int max_len
)
132 unsigned char *s
, *p
;
133 unsigned int buf_len
= 0;
137 if (buf
== 0 || max_len
< 1)
142 /* Single byte is the length */
149 /* First byte contains the number of octets */
152 /* Running total of the DER encoding length */
156 * Encode MSB first. We do the encoding by setting a shift
157 * factor to MSO_BIT (24 for 32 bit words) and then shifting the length
158 * by the factor. We then encode the resulting low order byte.
159 * We subtract 8 from the shift factor and repeat to ecnode the next
160 * byte. We stop when the shift factor is zero or we've run out of
161 * buffer to encode into.
164 for (i
= MSO_BIT
; i
>= 0 && buf_len
<= max_len
; i
-= 8) {
166 v
= (length
>> i
) & 0xff;
173 if (i
>= 0) /* buffer overflow */
177 * We go back now and set the first byte to be the length with
178 * the high order bit set.
188 * glue routine for get_mech_type
192 OM_uint32
gssint_get_mech_type_oid(OID
, token
)
196 unsigned char * buffer_ptr
;
200 * This routine reads the prefix of "token" in order to determine
201 * its mechanism type. It assumes the encoding suggested in
202 * Appendix B of RFC 1508. This format starts out as follows :
204 * tag for APPLICATION 0, Sequence[constructed, definite length]
205 * length of remainder of token
206 * tag of OBJECT IDENTIFIER
207 * length of mechanism OID
208 * encoding of mechanism OID
209 * <the rest of the token>
211 * Numerically, this looks like :
214 * <length> - could be multiple bytes
216 * <length> - assume only one byte, hence OID length < 127
219 * The routine fills in the OID value and returns an error as necessary.
223 return (GSS_S_CALL_INACCESSIBLE_WRITE
);
225 if ((token
== NULL
) || (token
->value
== NULL
))
226 return (GSS_S_DEFECTIVE_TOKEN
);
228 /* Skip past the APP/Sequnce byte and the token length */
230 buffer_ptr
= (unsigned char *) token
->value
;
232 if (*(buffer_ptr
++) != 0x60)
233 return (GSS_S_DEFECTIVE_TOKEN
);
234 length
= *buffer_ptr
++;
236 /* check if token length is null */
238 return (GSS_S_DEFECTIVE_TOKEN
);
241 if ((length
& 0x7f) > 4)
242 return (GSS_S_DEFECTIVE_TOKEN
);
243 buffer_ptr
+= length
& 0x7f;
246 if (*(buffer_ptr
++) != 0x06)
247 return (GSS_S_DEFECTIVE_TOKEN
);
249 OID
->length
= (OM_uint32
) *(buffer_ptr
++);
250 OID
->elements
= (void *) buffer_ptr
;
251 return (GSS_S_COMPLETE
);
255 * The following mechanisms do not always identify themselves
256 * per the GSS-API specification, when interoperating with MS
257 * peers. We include the OIDs here so we do not have to link
258 * with the mechanism.
260 static const gss_OID_desc gss_ntlm_mechanism_oid_desc
=
261 {10, "\x2b\x06\x01\x04\x01\x82\x37\x02\x02\x0a"};
262 static const gss_OID_desc gss_spnego_mechanism_oid_desc
=
263 {6, "\x2b\x06\x01\x05\x05\x02"};
264 static const gss_OID_desc gss_krb5_mechanism_oid_desc
=
265 {9, "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02"};
266 const gss_OID_desc
* const gss_mech_krb5
=
267 &gss_krb5_mechanism_oid_desc
;
269 #define NTLMSSP_SIGNATURE "NTLMSSP"
271 OM_uint32
gssint_get_mech_type(OID
, token
)
275 /* Check for interoperability exceptions */
276 if (token
->length
>= sizeof(NTLMSSP_SIGNATURE
) &&
277 memcmp(token
->value
, NTLMSSP_SIGNATURE
,
278 sizeof(NTLMSSP_SIGNATURE
)) == 0) {
279 *OID
= gss_ntlm_mechanism_oid_desc
;
280 } else if (token
->length
!= 0 &&
281 ((char *)token
->value
)[0] == 0x6E) {
282 /* Could be a raw AP-REQ (check for APPLICATION tag) */
283 *OID
= gss_krb5_mechanism_oid_desc
;
284 } else if (token
->length
== 0) {
285 *OID
= gss_spnego_mechanism_oid_desc
;
287 return gssint_get_mech_type_oid(OID
, token
);
290 return (GSS_S_COMPLETE
);
295 * Internal routines to get and release an internal mechanism name
298 #if 0 /* SUNW17PACresync */
302 OM_uint32
gssint_import_internal_name (minor_status
, mech_type
, union_name
,
304 OM_uint32
*minor_status
;
306 gss_union_name_t union_name
;
307 gss_name_t
*internal_name
;
312 mech
= gssint_get_mechanism (mech_type
);
314 if (mech
->gss_import_name
) {
315 status
= mech
->gss_import_name (
316 mech
->context
, /* SUNW17PACresync */
318 union_name
->external_name
,
319 union_name
->name_type
,
321 if (status
!= GSS_S_COMPLETE
)
322 map_error(minor_status
, mech
);
324 status
= GSS_S_UNAVAILABLE
;
329 return (GSS_S_BAD_MECH
);
332 OM_uint32
gssint_export_internal_name(minor_status
, mech_type
,
333 internal_name
, name_buf
)
334 OM_uint32
*minor_status
;
335 const gss_OID mech_type
;
336 const gss_name_t internal_name
;
337 gss_buffer_t name_buf
;
341 gss_buffer_desc dispName
;
343 unsigned char *buf
= NULL
;
344 const unsigned char tokId
[] = "\x04\x01";
345 const unsigned int tokIdLen
= 2;
346 const int mechOidLenLen
= 2, mechOidTagLen
= 1, nameLenLen
= 4;
347 int mechOidDERLen
= 0;
350 mech
= gssint_get_mechanism(mech_type
);
352 return (GSS_S_BAD_MECH
);
354 if (mech
->gss_export_name
) {
355 status
= mech
->gss_export_name(
356 mech
->context
, /* SUNW17PACresync */
360 if (status
!= GSS_S_COMPLETE
)
361 map_error(minor_status
, mech
);
366 * if we are here it is because the mechanism does not provide
367 * a gss_export_name so we will use our implementation. We
368 * do required that the mechanism define a gss_display_name.
370 if (!mech
->gss_display_name
)
371 return (GSS_S_UNAVAILABLE
);
374 * NOTE: RFC2743 (section 3.2) governs the format of the outer
375 * wrapper of exported names; the mechanisms' specs govern
376 * the format of the inner portion of the exported name
377 * and, for some (e.g., RFC1964, the Kerberos V mech), a
378 * generic default as implemented here will do.
380 * The outer wrapper of an exported MN is: 2-octet tok Id
381 * (0x0401) + 2-octet network-byte order mech OID length + mech
382 * oid (in DER format, including DER tag and DER length) +
383 * 4-octet network-byte order length of inner portion + inner
386 * For the Kerberos V mechanism the inner portion of an exported
387 * MN is the display name string and ignores the name type OID
388 * altogether. And we hope this will be so for any future
389 * mechanisms also, so that factoring name export/import out of
390 * the mech and into libgss pays off.
392 if ((status
= mech
->gss_display_name(
399 map_error(minor_status
, mech
);
403 /* determine the size of the buffer needed */
404 mechOidDERLen
= gssint_der_length_size(mech_type
->length
);
405 name_buf
->length
= tokIdLen
+ mechOidLenLen
+
406 mechOidTagLen
+ mechOidDERLen
+
408 nameLenLen
+ dispName
.length
;
409 if ((name_buf
->value
= malloc(name_buf
->length
)) == NULL
) {
410 name_buf
->length
= 0;
411 (void) gss_release_buffer(&status
, &dispName
);
412 return (GSS_S_FAILURE
);
415 /* now create the name ..... */
416 buf
= (unsigned char *)name_buf
->value
;
417 (void) memset(name_buf
->value
, 0, name_buf
->length
);
418 (void) memcpy(buf
, tokId
, tokIdLen
);
421 /* spec allows only 2 bytes for the mech oid length */
422 mechOidLen
= mechOidDERLen
+ mechOidTagLen
+ mech_type
->length
;
423 store_16_be(mechOidLen
, buf
);
427 * DER Encoding of mech OID contains OID Tag (0x06), length and
431 if (gssint_put_der_length(mech_type
->length
, &buf
,
432 (name_buf
->length
- tokIdLen
-2)) != 0) {
433 name_buf
->length
= 0;
434 free(name_buf
->value
);
435 (void) gss_release_buffer(&status
, &dispName
);
436 return (GSS_S_FAILURE
);
439 (void) memcpy(buf
, mech_type
->elements
, mech_type
->length
);
440 buf
+= mech_type
->length
;
442 /* spec designates the next 4 bytes for the name length */
443 store_32_be(dispName
.length
, buf
);
446 /* for the final ingredient - add the name from gss_display_name */
447 (void) memcpy(buf
, dispName
.value
, dispName
.length
);
449 /* release the buffer obtained from gss_display_name */
450 (void) gss_release_buffer(minor_status
, &dispName
);
451 return (GSS_S_COMPLETE
);
452 } /* gssint_export_internal_name */
454 OM_uint32
gssint_display_internal_name (minor_status
, mech_type
, internal_name
,
455 external_name
, name_type
)
456 OM_uint32
*minor_status
;
458 gss_name_t internal_name
;
459 gss_buffer_t external_name
;
465 mech
= gssint_get_mechanism (mech_type
);
467 if (mech
->gss_display_name
) {
468 status
= mech
->gss_display_name (
474 if (status
!= GSS_S_COMPLETE
)
475 map_error(minor_status
, mech
);
477 status
= GSS_S_UNAVAILABLE
;
482 return (GSS_S_BAD_MECH
);
485 OM_uint32
gssint_release_internal_name (minor_status
, mech_type
, internal_name
)
486 OM_uint32
*minor_status
;
488 gss_name_t
*internal_name
;
493 mech
= gssint_get_mechanism (mech_type
);
495 if (mech
->gss_release_name
) {
496 status
= mech
->gss_release_name (
500 if (status
!= GSS_S_COMPLETE
)
501 map_error(minor_status
, mech
);
503 status
= GSS_S_UNAVAILABLE
;
508 return (GSS_S_BAD_MECH
);
511 OM_uint32
gssint_delete_internal_sec_context (minor_status
,
515 OM_uint32
*minor_status
;
517 gss_ctx_id_t
*internal_ctx
;
518 gss_buffer_t output_token
;
523 mech
= gssint_get_mechanism (mech_type
);
525 if (mech
->gss_delete_sec_context
)
526 status
= mech
->gss_delete_sec_context (
527 mech
->context
, /* SUNW17PACresync */
532 /* SUNW17PACresync - map error here? */
533 status
= GSS_S_UNAVAILABLE
;
538 return (GSS_S_BAD_MECH
);
542 * This function converts an internal gssapi name to a union gssapi
543 * name. Note that internal_name should be considered "consumed" by
544 * this call, whether or not we return an error.
546 OM_uint32
gssint_convert_name_to_union_name(minor_status
, mech
,
547 internal_name
, external_name
)
548 OM_uint32
*minor_status
;
550 gss_name_t internal_name
;
551 gss_name_t
*external_name
;
553 OM_uint32 major_status
,tmp
;
554 gss_union_name_t union_name
;
556 union_name
= (gss_union_name_t
) malloc (sizeof(gss_union_name_desc
));
558 major_status
= GSS_S_FAILURE
;
559 *minor_status
= ENOMEM
;
560 map_errcode(minor_status
);
561 goto allocation_failure
;
563 union_name
->mech_type
= 0;
564 union_name
->mech_name
= internal_name
;
565 union_name
->name_type
= 0;
566 union_name
->external_name
= 0;
568 major_status
= generic_gss_copy_oid(minor_status
, &mech
->mech_type
,
569 &union_name
->mech_type
);
570 if (major_status
!= GSS_S_COMPLETE
) {
571 map_errcode(minor_status
);
572 goto allocation_failure
;
575 union_name
->external_name
=
576 (gss_buffer_t
) malloc(sizeof(gss_buffer_desc
));
577 if (!union_name
->external_name
) {
578 major_status
= GSS_S_FAILURE
;
579 *minor_status
= ENOMEM
;
580 goto allocation_failure
;
583 union_name
->external_name
->length
= 0;
584 union_name
->external_name
->value
= 0;
586 major_status
= mech
->gss_display_name(
587 mech
->context
, /* SUNW17PACresync */
590 union_name
->external_name
,
591 &union_name
->name_type
);
592 if (major_status
!= GSS_S_COMPLETE
) {
593 map_error(minor_status
, mech
);
594 goto allocation_failure
;
597 union_name
->loopback
= union_name
;
598 *external_name
= (gss_name_t
) union_name
;
599 return (GSS_S_COMPLETE
);
603 if (union_name
->external_name
) {
604 free(union_name
->external_name
->value
);
605 free(union_name
->external_name
);
607 if (union_name
->name_type
)
608 (void) gss_release_oid(&tmp
, &union_name
->name_type
);
609 if (union_name
->mech_type
)
610 (void) gss_release_oid(&tmp
, &union_name
->mech_type
);
614 * do as the top comment says - since we are now owners of
615 * internal_name, we must clean it up
618 (void) gssint_release_internal_name(&tmp
, &mech
->mech_type
,
620 return (major_status
);
624 * Glue routine for returning the mechanism-specific credential from a
625 * external union credential.
628 gssint_get_mechanism_cred(union_cred
, mech_type
)
629 gss_union_cred_t union_cred
;
634 if (union_cred
== (gss_union_cred_t
) GSS_C_NO_CREDENTIAL
)
635 return GSS_C_NO_CREDENTIAL
;
639 * Disable this block as it causes problems for gss_add_cred
640 * for HTTP SSO (and also probably causes STC gss.13 to fail too).
643 /* SPNEGO mechanism will again call into GSSAPI */
644 if (g_OID_equal(&gss_spnego_mechanism_oid_desc
, mech_type
))
645 return (gss_cred_id_t
)union_cred
;
648 for (i
=0; i
< union_cred
->count
; i
++) {
649 if (g_OID_equal(mech_type
, &union_cred
->mechs_array
[i
]))
650 return union_cred
->cred_array
[i
];
652 /* for SPNEGO, check the next-lower set of creds */
653 if (g_OID_equal(&gss_spnego_mechanism_oid_desc
, &union_cred
->mechs_array
[i
])) {
654 gss_union_cred_t candidate_cred
;
655 gss_cred_id_t sub_cred
;
657 candidate_cred
= (gss_union_cred_t
)union_cred
->cred_array
[i
];
658 sub_cred
= gssint_get_mechanism_cred(candidate_cred
, mech_type
);
660 if(sub_cred
!= GSS_C_NO_CREDENTIAL
)
665 return GSS_C_NO_CREDENTIAL
;
669 * Routine to create and copy the gss_buffer_desc structure.
670 * Both space for the structure and the data is allocated.
673 gssint_create_copy_buffer(srcBuf
, destBuf
, addNullChar
)
674 const gss_buffer_t srcBuf
;
675 gss_buffer_t
*destBuf
;
682 return (GSS_S_CALL_INACCESSIBLE_WRITE
);
686 aBuf
= (gss_buffer_t
)malloc(sizeof (gss_buffer_desc
));
688 return (GSS_S_FAILURE
);
691 len
= srcBuf
->length
+ 1;
693 len
= srcBuf
->length
;
695 if (!(aBuf
->value
= (void*)malloc(len
))) {
697 return (GSS_S_FAILURE
);
701 (void) memcpy(aBuf
->value
, srcBuf
->value
, srcBuf
->length
);
702 aBuf
->length
= srcBuf
->length
;
705 /* optionally add a NULL character */
707 ((char *)aBuf
->value
)[aBuf
->length
] = '\0';
709 return (GSS_S_COMPLETE
);
710 } /* ****** gssint_create_copy_buffer ****** */