diff options
author | Andrew Bartlett <abartlet@samba.org> | 2006-11-07 06:59:56 +0000 |
---|---|---|
committer | Gerald (Jerry) Carter <jerry@samba.org> | 2007-10-10 14:25:03 -0500 |
commit | 3c1e780ec7e16dc6667402bbc65708bf9a5c062f (patch) | |
tree | 2102bb577ea9f00751b8c869b0a5c756fc2ae8e5 /source4/heimdal/lib/gssapi/spnego | |
parent | 8b91594e0936bbaedf5430406fcf8df3ea406c10 (diff) | |
download | samba-3c1e780ec7e16dc6667402bbc65708bf9a5c062f.tar.gz samba-3c1e780ec7e16dc6667402bbc65708bf9a5c062f.tar.bz2 samba-3c1e780ec7e16dc6667402bbc65708bf9a5c062f.zip |
r19604: This is a massive commit, and I appologise in advance for it's size.
This merges Samba4 with lorikeet-heimdal, which itself has been
tracking Heimdal CVS for the past couple of weeks.
This is such a big change because Heimdal reorganised it's internal
structures, with the mechglue merge, and because many of our 'wishes' have been granted: we now have DCE_STYLE GSSAPI, send_to_kdc hooks and many other features merged into the mainline code. We have adapted to upstream's choice of API in these cases.
In gensec_gssapi and gensec_krb5, we either expect a valid PAC, or NO
PAC. This matches windows behavour. We also have an option to
require the PAC to be present (which allows us to automate the testing
of this code).
This also includes a restructure of how the kerberos dependencies are
handled, due to the fallout of the merge.
Andrew Bartlett
(This used to be commit 4826f1735197c2a471d771495e6d4c1051b4c471)
Diffstat (limited to 'source4/heimdal/lib/gssapi/spnego')
-rw-r--r-- | source4/heimdal/lib/gssapi/spnego/accept_sec_context.c | 873 | ||||
-rw-r--r-- | source4/heimdal/lib/gssapi/spnego/compat.c | 285 | ||||
-rw-r--r-- | source4/heimdal/lib/gssapi/spnego/context_stubs.c | 835 | ||||
-rw-r--r-- | source4/heimdal/lib/gssapi/spnego/cred_stubs.c | 291 | ||||
-rw-r--r-- | source4/heimdal/lib/gssapi/spnego/external.c | 89 | ||||
-rw-r--r-- | source4/heimdal/lib/gssapi/spnego/init_sec_context.c | 578 | ||||
-rw-r--r-- | source4/heimdal/lib/gssapi/spnego/spnego-private.h | 347 | ||||
-rw-r--r-- | source4/heimdal/lib/gssapi/spnego/spnego.asn1 | 51 | ||||
-rw-r--r-- | source4/heimdal/lib/gssapi/spnego/spnego_locl.h | 96 |
9 files changed, 3445 insertions, 0 deletions
diff --git a/source4/heimdal/lib/gssapi/spnego/accept_sec_context.c b/source4/heimdal/lib/gssapi/spnego/accept_sec_context.c new file mode 100644 index 0000000000..8a885a3e2f --- /dev/null +++ b/source4/heimdal/lib/gssapi/spnego/accept_sec_context.c @@ -0,0 +1,873 @@ +/* + * Copyright (c) 1997 - 2004 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * Portions Copyright (c) 2004 PADL Software Pty Ltd. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "spnego/spnego_locl.h" + +RCSID("$Id: accept_sec_context.c,v 1.6 2006/10/07 22:26:57 lha Exp $"); + +OM_uint32 +_gss_spnego_encode_response(OM_uint32 *minor_status, + const NegTokenResp *resp, + gss_buffer_t data, + u_char **ret_buf) +{ + OM_uint32 ret; + u_char *buf; + size_t buf_size, buf_len; + + buf_size = 1024; + buf = malloc(buf_size); + if (buf == NULL) { + *minor_status = ENOMEM; + return GSS_S_FAILURE; + } + + do { + ret = encode_NegTokenResp(buf + buf_size - 1, + buf_size, + resp, &buf_len); + if (ret == 0) { + size_t tmp; + + ret = der_put_length_and_tag(buf + buf_size - buf_len - 1, + buf_size - buf_len, + buf_len, + ASN1_C_CONTEXT, + CONS, + 1, + &tmp); + if (ret == 0) + buf_len += tmp; + } + if (ret) { + if (ret == ASN1_OVERFLOW) { + u_char *tmp; + + buf_size *= 2; + tmp = realloc (buf, buf_size); + if (tmp == NULL) { + *minor_status = ENOMEM; + free(buf); + return GSS_S_FAILURE; + } + buf = tmp; + } else { + *minor_status = ret; + free(buf); + return GSS_S_FAILURE; + } + } + } while (ret == ASN1_OVERFLOW); + + data->value = buf + buf_size - buf_len; + data->length = buf_len; + *ret_buf = buf; + + return GSS_S_COMPLETE; +} + +static OM_uint32 +send_reject (OM_uint32 *minor_status, + gss_buffer_t output_token) +{ + NegTokenResp resp; + gss_buffer_desc data; + u_char *buf; + OM_uint32 ret; + + ALLOC(resp.negResult, 1); + if (resp.negResult == NULL) { + *minor_status = ENOMEM; + return GSS_S_FAILURE; + } + *(resp.negResult) = reject; + resp.supportedMech = NULL; + resp.responseToken = NULL; + resp.mechListMIC = NULL; + + ret = _gss_spnego_encode_response (minor_status, &resp, &data, &buf); + free_NegTokenResp(&resp); + if (ret != GSS_S_COMPLETE) + return ret; + + output_token->value = malloc(data.length); + if (output_token->value == NULL) { + *minor_status = ENOMEM; + ret = GSS_S_FAILURE; + } else { + output_token->length = data.length; + memcpy(output_token->value, data.value, output_token->length); + } + free(buf); + if (ret != GSS_S_COMPLETE) + return ret; + return GSS_S_BAD_MECH; +} + +OM_uint32 +_gss_spnego_indicate_mechtypelist (OM_uint32 *minor_status, + int includeMSCompatOID, + const gssspnego_cred cred_handle, + MechTypeList *mechtypelist, + gss_OID *preferred_mech) +{ + OM_uint32 ret; + gss_OID_set supported_mechs = GSS_C_NO_OID_SET; + int i, count; + + if (cred_handle != NULL) { + ret = gss_inquire_cred(minor_status, + cred_handle->negotiated_cred_id, + NULL, + NULL, + NULL, + &supported_mechs); + } else { + ret = gss_indicate_mechs(minor_status, &supported_mechs); + } + + if (ret != GSS_S_COMPLETE) { + return ret; + } + + if (supported_mechs->count == 0) { + *minor_status = ENOENT; + gss_release_oid_set(minor_status, &supported_mechs); + return GSS_S_FAILURE; + } + + count = supported_mechs->count; + if (includeMSCompatOID) + count++; + + mechtypelist->len = 0; + mechtypelist->val = calloc(count, sizeof(MechType)); + if (mechtypelist->val == NULL) { + *minor_status = ENOMEM; + gss_release_oid_set(minor_status, &supported_mechs); + return GSS_S_FAILURE; + } + + for (i = 0; i < supported_mechs->count; i++) { + ret = _gss_spnego_add_mech_type(&supported_mechs->elements[i], + includeMSCompatOID, + mechtypelist); + if (ret != 0) { + *minor_status = ENOMEM; + ret = GSS_S_FAILURE; + break; + } + } + + if (ret == GSS_S_COMPLETE && preferred_mech != NULL) { + ret = gss_duplicate_oid(minor_status, + &supported_mechs->elements[0], + preferred_mech); + } + + if (ret != GSS_S_COMPLETE) { + free_MechTypeList(mechtypelist); + mechtypelist->len = 0; + mechtypelist->val = NULL; + } + gss_release_oid_set(minor_status, &supported_mechs); + + return ret; +} + +static OM_uint32 +send_supported_mechs (OM_uint32 *minor_status, + gss_buffer_t output_token) +{ + NegTokenInit ni; + char hostname[MAXHOSTNAMELEN], *p; + gss_buffer_desc name_buf; + gss_OID name_type; + gss_name_t target_princ; + gss_name_t canon_princ; + OM_uint32 ret, minor; + u_char *buf; + size_t buf_size, buf_len; + gss_buffer_desc data; + + memset(&ni, 0, sizeof(ni)); + + ni.reqFlags = NULL; + ni.mechToken = NULL; + ni.negHints = NULL; + ni.mechListMIC = NULL; + + ret = _gss_spnego_indicate_mechtypelist(minor_status, 1, + NULL, + &ni.mechTypes, NULL); + if (ret != GSS_S_COMPLETE) { + return ret; + } + + memset(&target_princ, 0, sizeof(target_princ)); + if (gethostname(hostname, sizeof(hostname) - 1) != 0) { + *minor_status = errno; + free_NegTokenInit(&ni); + return GSS_S_FAILURE; + } + + /* Send the constructed SAM name for this host */ + for (p = hostname; *p != '\0' && *p != '.'; p++) { + *p = toupper((unsigned char)*p); + } + *p++ = '$'; + *p = '\0'; + + name_buf.length = strlen(hostname); + name_buf.value = hostname; + + ret = gss_import_name(minor_status, &name_buf, + GSS_C_NO_OID, + &target_princ); + if (ret != GSS_S_COMPLETE) { + return ret; + } + + name_buf.length = 0; + name_buf.value = NULL; + + /* Canonicalize the name using the preferred mechanism */ + ret = gss_canonicalize_name(minor_status, + target_princ, + GSS_C_NO_OID, + &canon_princ); + if (ret != GSS_S_COMPLETE) { + gss_release_name(&minor, &target_princ); + return ret; + } + + ret = gss_display_name(minor_status, canon_princ, + &name_buf, &name_type); + if (ret != GSS_S_COMPLETE) { + gss_release_name(&minor, &canon_princ); + gss_release_name(&minor, &target_princ); + return ret; + } + + gss_release_name(&minor, &canon_princ); + gss_release_name(&minor, &target_princ); + + ALLOC(ni.negHints, 1); + if (ni.negHints == NULL) { + *minor_status = ENOMEM; + gss_release_buffer(&minor, &name_buf); + free_NegTokenInit(&ni); + return GSS_S_FAILURE; + } + + ALLOC(ni.negHints->hintName, 1); + if (ni.negHints->hintName == NULL) { + *minor_status = ENOMEM; + gss_release_buffer(&minor, &name_buf); + free_NegTokenInit(&ni); + return GSS_S_FAILURE; + } + + *(ni.negHints->hintName) = name_buf.value; + name_buf.value = NULL; + ni.negHints->hintAddress = NULL; + + buf_size = 1024; + buf = malloc(buf_size); + if (buf == NULL) { + free_NegTokenInit(&ni); + *minor_status = ENOMEM; + return GSS_S_FAILURE; + } + + do { + ret = encode_NegTokenInit(buf + buf_size - 1, + buf_size, + &ni, &buf_len); + if (ret == 0) { + size_t tmp; + + ret = der_put_length_and_tag(buf + buf_size - buf_len - 1, + buf_size - buf_len, + buf_len, + ASN1_C_CONTEXT, + CONS, + 0, + &tmp); + if (ret == 0) + buf_len += tmp; + } + if (ret) { + if (ret == ASN1_OVERFLOW) { + u_char *tmp; + + buf_size *= 2; + tmp = realloc (buf, buf_size); + if (tmp == NULL) { + *minor_status = ENOMEM; + free(buf); + free_NegTokenInit(&ni); + return GSS_S_FAILURE; + } + buf = tmp; + } else { + *minor_status = ret; + free(buf); + free_NegTokenInit(&ni); + return GSS_S_FAILURE; + } + } + } while (ret == ASN1_OVERFLOW); + + data.value = buf + buf_size - buf_len; + data.length = buf_len; + + ret = gss_encapsulate_token(&data, + GSS_SPNEGO_MECHANISM, + output_token); + free (buf); + free_NegTokenInit (&ni); + + if (ret != GSS_S_COMPLETE) + return ret; + + *minor_status = 0; + + return GSS_S_CONTINUE_NEEDED; +} + +static OM_uint32 +send_accept (OM_uint32 *minor_status, + gssspnego_ctx context_handle, + gss_buffer_t mech_token, + int initial_response, + gss_buffer_t mech_buf, + gss_buffer_t output_token) +{ + NegTokenResp resp; + gss_buffer_desc data; + u_char *buf; + OM_uint32 ret; + gss_buffer_desc mech_mic_buf; + + memset(&resp, 0, sizeof(resp)); + + ALLOC(resp.negResult, 1); + if (resp.negResult == NULL) { + *minor_status = ENOMEM; + return GSS_S_FAILURE; + } + + if (context_handle->open) { + if (mech_token != GSS_C_NO_BUFFER + && mech_token->length != 0 + && mech_buf != GSS_C_NO_BUFFER) + *(resp.negResult) = accept_incomplete; + else + *(resp.negResult) = accept_completed; + } else { + if (initial_response && context_handle->require_mic) + *(resp.negResult) = request_mic; + else + *(resp.negResult) = accept_incomplete; + } + + if (initial_response) { + ALLOC(resp.supportedMech, 1); + if (resp.supportedMech == NULL) { + free_NegTokenResp(&resp); + *minor_status = ENOMEM; + return GSS_S_FAILURE; + } + + ret = der_get_oid(context_handle->preferred_mech_type->elements, + context_handle->preferred_mech_type->length, + resp.supportedMech, + NULL); + if (ret) { + free_NegTokenResp(&resp); + *minor_status = ENOMEM; + return GSS_S_FAILURE; + } + } else { + resp.supportedMech = NULL; + } + + if (mech_token != GSS_C_NO_BUFFER && mech_token->length != 0) { + ALLOC(resp.responseToken, 1); + if (resp.responseToken == NULL) { + free_NegTokenResp(&resp); + *minor_status = ENOMEM; + return GSS_S_FAILURE; + } + resp.responseToken->length = mech_token->length; + resp.responseToken->data = mech_token->value; + mech_token->length = 0; + mech_token->value = NULL; + } else { + resp.responseToken = NULL; + } + + if (mech_buf != GSS_C_NO_BUFFER) { + ALLOC(resp.mechListMIC, 1); + if (resp.mechListMIC == NULL) { + free_NegTokenResp(&resp); + *minor_status = ENOMEM; + return GSS_S_FAILURE; + } + + ret = gss_get_mic(minor_status, + context_handle->negotiated_ctx_id, + 0, + mech_buf, + &mech_mic_buf); + if (ret != GSS_S_COMPLETE) { + free_NegTokenResp(&resp); + return ret; + } + + resp.mechListMIC->length = mech_mic_buf.length; + resp.mechListMIC->data = mech_mic_buf.value; + } else + resp.mechListMIC = NULL; + + ret = _gss_spnego_encode_response (minor_status, &resp, &data, &buf); + if (ret != GSS_S_COMPLETE) { + free_NegTokenResp(&resp); + return ret; + } + + /* + * The response should not be encapsulated, because + * it is a SubsequentContextToken (note though RFC 1964 + * specifies encapsulation for all _Kerberos_ tokens). + */ + output_token->value = malloc(data.length); + if (output_token->value == NULL) { + *minor_status = ENOMEM; + ret = GSS_S_FAILURE; + } else { + output_token->length = data.length; + memcpy(output_token->value, data.value, output_token->length); + } + free(buf); + if (ret != GSS_S_COMPLETE) { + free_NegTokenResp(&resp); + return ret; + } + + ret = (*(resp.negResult) == accept_completed) ? GSS_S_COMPLETE : + GSS_S_CONTINUE_NEEDED; + free_NegTokenResp(&resp); + return ret; +} + + +static OM_uint32 +verify_mechlist_mic + (OM_uint32 *minor_status, + gssspnego_ctx context_handle, + gss_buffer_t mech_buf, + heim_octet_string *mechListMIC + ) +{ + OM_uint32 ret; + gss_buffer_desc mic_buf; + + if (context_handle->verified_mic) { + /* This doesn't make sense, we've already verified it? */ + *minor_status = 0; + return GSS_S_DUPLICATE_TOKEN; + } + + if (mechListMIC == NULL) { + *minor_status = 0; + return GSS_S_DEFECTIVE_TOKEN; + } + + mic_buf.length = mechListMIC->length; + mic_buf.value = mechListMIC->data; + + ret = gss_verify_mic(minor_status, + context_handle->negotiated_ctx_id, + mech_buf, + &mic_buf, + NULL); + + if (ret != GSS_S_COMPLETE) + ret = GSS_S_DEFECTIVE_TOKEN; + + return ret; +} + +OM_uint32 +_gss_spnego_accept_sec_context + (OM_uint32 * minor_status, + gss_ctx_id_t * context_handle, + const gss_cred_id_t acceptor_cred_handle, + const gss_buffer_t input_token_buffer, + const gss_channel_bindings_t input_chan_bindings, + gss_name_t * src_name, + gss_OID * mech_type, + gss_buffer_t output_token, + OM_uint32 * ret_flags, + OM_uint32 * time_rec, + gss_cred_id_t *delegated_cred_handle + ) +{ + OM_uint32 ret, ret2, minor; + NegTokenInit ni; + NegTokenResp na; + size_t ni_len, na_len; + int i; + gss_buffer_desc data; + size_t len, taglen; + int initialToken; + unsigned int negResult = accept_incomplete; + gss_buffer_t mech_input_token = GSS_C_NO_BUFFER; + gss_buffer_t mech_output_token = GSS_C_NO_BUFFER; + gss_buffer_desc mech_buf; + gss_OID preferred_mech_type = GSS_C_NO_OID; + gssspnego_ctx ctx; + gssspnego_cred acceptor_cred = (gssspnego_cred)acceptor_cred_handle; + + *minor_status = 0; + + output_token->length = 0; + output_token->value = NULL; + + if (src_name != NULL) + *src_name = GSS_C_NO_NAME; + + if (mech_type != NULL) + *mech_type = GSS_C_NO_OID; + + if (ret_flags != NULL) + *ret_flags = 0; + + if (time_rec != NULL) + *time_rec = 0; + + if (delegated_cred_handle != NULL) + *delegated_cred_handle = GSS_C_NO_CREDENTIAL; + + mech_buf.value = NULL; + + if (*context_handle == GSS_C_NO_CONTEXT) { + ret = _gss_spnego_alloc_sec_context(minor_status, + context_handle); + if (ret != GSS_S_COMPLETE) + return ret; + + if (input_token_buffer->length == 0) { + return send_supported_mechs (minor_status, + output_token); + } + } + + ctx = (gssspnego_ctx)*context_handle; + + /* + * The GSS-API encapsulation is only present on the initial + * context token (negTokenInit). + */ + ret = gss_decapsulate_token (input_token_buffer, + GSS_SPNEGO_MECHANISM, + &data); + initialToken = (ret == GSS_S_COMPLETE); + + if (!initialToken) { + data.value = input_token_buffer->value; + data.length = input_token_buffer->length; + } + + ret = der_match_tag_and_length(data.value, data.length, + ASN1_C_CONTEXT, CONS, + initialToken ? 0 : 1, + &len, &taglen); + if (ret) { + *minor_status = ret; + return GSS_S_FAILURE; + } + + if (len > data.length - taglen) { + *minor_status = ASN1_OVERRUN; + return GSS_S_FAILURE; + } + + if (initialToken) { + ret = decode_NegTokenInit((const unsigned char *)data.value + taglen, + len, &ni, &ni_len); + } else { + ret = decode_NegTokenResp((const unsigned char *)data.value + taglen, + len, &na, &na_len); + } + if (ret) { + *minor_status = ret; + return GSS_S_DEFECTIVE_TOKEN; + } + + if (!initialToken && na.negResult != NULL) { + negResult = *(na.negResult); + } + + if (negResult == reject || negResult == request_mic) { + /* request_mic should only be sent by acceptor */ + free_NegTokenResp(&na); + return GSS_S_DEFECTIVE_TOKEN; + } + + if (initialToken) { + for (i = 0; i < ni.mechTypes.len; ++i) { + /* Call glue layer to find first mech we support */ + ret = _gss_spnego_select_mech(minor_status, &ni.mechTypes.val[i], + &preferred_mech_type); + if (ret == 0) + break; + } + if (preferred_mech_type == GSS_C_NO_OID) { + free_NegTokenInit(&ni); + return GSS_S_BAD_MECH; + } + } + + HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex); + + if (initialToken) { + ctx->preferred_mech_type = preferred_mech_type; + ctx->initiator_mech_types.len = ni.mechTypes.len; + ctx->initiator_mech_types.val = ni.mechTypes.val; + ni.mechTypes.len = 0; + ni.mechTypes.val = NULL; + } + + { + gss_buffer_desc ibuf, obuf; + int require_mic, verify_mic, get_mic; + int require_response; + heim_octet_string *mic; + + if (initialToken) { + if (ni.mechToken != NULL) { + ibuf.length = ni.mechToken->length; + ibuf.value = ni.mechToken->data; + mech_input_token = &ibuf; + } + } else { + if (na.responseToken != NULL) { + ibuf.length = na.responseToken->length; + ibuf.value = na.responseToken->data; + mech_input_token = &ibuf; + } + } + + if (mech_input_token != GSS_C_NO_BUFFER) { + gss_cred_id_t mech_cred; + gss_cred_id_t mech_delegated_cred; + gss_cred_id_t *mech_delegated_cred_p; + + if (acceptor_cred != NULL) + mech_cred = acceptor_cred->negotiated_cred_id; + else + mech_cred = GSS_C_NO_CREDENTIAL; + + if (delegated_cred_handle != NULL) { + mech_delegated_cred = GSS_C_NO_CREDENTIAL; + mech_delegated_cred_p = &mech_delegated_cred; + } else { + mech_delegated_cred_p = NULL; + } + + if (ctx->mech_src_name != GSS_C_NO_NAME) + gss_release_name(&minor, &ctx->mech_src_name); + + if (ctx->delegated_cred_id != GSS_C_NO_CREDENTIAL) + _gss_spnego_release_cred(&minor, &ctx->delegated_cred_id); + + ret = gss_accept_sec_context(&minor, + &ctx->negotiated_ctx_id, + mech_cred, + mech_input_token, + input_chan_bindings, + &ctx->mech_src_name, + &ctx->negotiated_mech_type, + &obuf, + &ctx->mech_flags, + &ctx->mech_time_rec, + mech_delegated_cred_p); + if (ret == GSS_S_COMPLETE || ret == GSS_S_CONTINUE_NEEDED) { + if (mech_delegated_cred_p != NULL && + mech_delegated_cred != GSS_C_NO_CREDENTIAL) { + ret2 = _gss_spnego_alloc_cred(minor_status, + mech_delegated_cred, + &ctx->delegated_cred_id); + if (ret2 != GSS_S_COMPLETE) + ret = ret2; + } + mech_output_token = &obuf; + } + if (ret != GSS_S_COMPLETE && ret != GSS_S_CONTINUE_NEEDED) { + if (initialToken) + free_NegTokenInit(&ni); + else + free_NegTokenResp(&na); + send_reject (minor_status, output_token); + HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); + return ret; + } + if (ret == GSS_S_COMPLETE) + ctx->open = 1; + } else + ret = GSS_S_COMPLETE; + + ret2 = _gss_spnego_require_mechlist_mic(minor_status, + ctx, + &require_mic); + if (ret2) + goto out; + + ctx->require_mic = require_mic; + + mic = initialToken ? ni.mechListMIC : na.mechListMIC; + if (mic != NULL) + require_mic = 1; + + if (ctx->open && require_mic) { + if (mech_input_token == GSS_C_NO_BUFFER) { /* Even/One */ + verify_mic = 1; + get_mic = 0; + } else if (mech_output_token != GSS_C_NO_BUFFER && + mech_output_token->length == 0) { /* Odd */ + get_mic = verify_mic = 1; + } else { /* Even/One */ + verify_mic = 0; + get_mic = 1; + } + + if (verify_mic || get_mic) { + int eret; + size_t buf_len; + + ASN1_MALLOC_ENCODE(MechTypeList, + mech_buf.value, mech_buf.length, + &ctx->initiator_mech_types, &buf_len, eret); + if (eret) { + ret2 = GSS_S_FAILURE; + *minor_status = eret; + goto out; + } + if (mech_buf.length != buf_len) + abort(); + } + + if (verify_mic) { + ret2 = verify_mechlist_mic(minor_status, ctx, &mech_buf, mic); + if (ret2) { + if (get_mic) + send_reject (minor_status, output_token); + goto out; + } + + ctx->verified_mic = 1; + } + } else + verify_mic = get_mic = 0; + + if (ctx->mech_flags & GSS_C_DCE_STYLE) + require_response = (negResult != accept_completed); + else + require_response = 0; + + /* + * Check whether we need to send a result: there should be only + * one accept_completed response sent in the entire negotiation + */ + if ((mech_output_token != GSS_C_NO_BUFFER && + mech_output_token->length != 0) + || require_response + || get_mic) { + ret2 = send_accept (minor_status, + ctx, + mech_output_token, + initialToken, + get_mic ? &mech_buf : NULL, + output_token); + if (ret2) + goto out; + } + + out: + if (ret2 != GSS_S_COMPLETE) + ret = ret2; + if (mech_output_token != NULL) + gss_release_buffer(&minor, mech_output_token); + if (mech_buf.value != NULL) + free(mech_buf.value); + if (initialToken) + free_NegTokenInit(&ni); + else + free_NegTokenResp(&na); + } + + if (ret == GSS_S_COMPLETE) { + if (src_name != NULL) { + ret2 = gss_duplicate_name(minor_status, + ctx->mech_src_name, + src_name); + if (ret2 != GSS_S_COMPLETE) + ret = ret2; + } + if (delegated_cred_handle != NULL) { + *delegated_cred_handle = ctx->delegated_cred_id; + ctx->delegated_cred_id = GSS_C_NO_CREDENTIAL; + } + } + + if (mech_type != NULL) + *mech_type = ctx->negotiated_mech_type; + if (ret_flags != NULL) + *ret_flags = ctx->mech_flags; + if (time_rec != NULL) + *time_rec = ctx->mech_time_rec; + + if (ret == GSS_S_COMPLETE || ret == GSS_S_CONTINUE_NEEDED) { + HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); + return ret; + } + + _gss_spnego_internal_delete_sec_context(&minor, context_handle, + GSS_C_NO_BUFFER); + + return ret; +} + diff --git a/source4/heimdal/lib/gssapi/spnego/compat.c b/source4/heimdal/lib/gssapi/spnego/compat.c new file mode 100644 index 0000000000..aeae088258 --- /dev/null +++ b/source4/heimdal/lib/gssapi/spnego/compat.c @@ -0,0 +1,285 @@ +/* + * Copyright (c) 2004, PADL Software Pty Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of PADL Software nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY PADL SOFTWARE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PADL SOFTWARE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "spnego/spnego_locl.h" + +RCSID("$Id: compat.c,v 1.6 2006/10/07 22:26:59 lha Exp $"); + +/* + * Apparently Microsoft got the OID wrong, and used + * 1.2.840.48018.1.2.2 instead. We need both this and + * the correct Kerberos OID here in order to deal with + * this. Because this is manifest in SPNEGO only I'd + * prefer to deal with this here rather than inside the + * Kerberos mechanism. + */ +static gss_OID_desc gss_mskrb_mechanism_oid_desc = + {9, (void *)"\x2a\x86\x48\x82\xf7\x12\x01\x02\x02"}; + +static gss_OID_desc gss_krb5_mechanism_oid_desc = + {9, (void *)"\x2a\x86\x48\x86\xf7\x12\x01\x02\x02"}; + +/* + * Allocate a SPNEGO context handle + */ +OM_uint32 _gss_spnego_alloc_sec_context (OM_uint32 * minor_status, + gss_ctx_id_t *context_handle) +{ + gssspnego_ctx ctx; + + ctx = calloc(1, sizeof(*ctx)); + if (ctx == NULL) { + *minor_status = ENOMEM; + return GSS_S_FAILURE; + } + + ctx->initiator_mech_types.len = 0; + ctx->initiator_mech_types.val = NULL; + ctx->preferred_mech_type = GSS_C_NO_OID; + ctx->negotiated_mech_type = GSS_C_NO_OID; + ctx->negotiated_ctx_id = GSS_C_NO_CONTEXT; + + /* + * Cache these so we can return them before returning + * GSS_S_COMPLETE, even if the mechanism has itself + * completed earlier + */ + ctx->mech_flags = 0; + ctx->mech_time_rec = 0; + ctx->mech_src_name = GSS_C_NO_NAME; + ctx->delegated_cred_id = GSS_C_NO_CREDENTIAL; + + ctx->open = 0; + ctx->local = 0; + ctx->require_mic = 0; + ctx->verified_mic = 0; + + HEIMDAL_MUTEX_init(&ctx->ctx_id_mutex); + + *context_handle = (gss_ctx_id_t)ctx; + + return GSS_S_COMPLETE; +} + +/* + * Free a SPNEGO context handle. The caller must have acquired + * the lock before this is called. + */ +OM_uint32 _gss_spnego_internal_delete_sec_context + (OM_uint32 *minor_status, + gss_ctx_id_t *context_handle, + gss_buffer_t output_token + ) +{ + gssspnego_ctx ctx; + OM_uint32 ret, minor; + + *minor_status = 0; + + if (context_handle == NULL) { + return GSS_S_NO_CONTEXT; + } + + if (output_token != GSS_C_NO_BUFFER) { + output_token->length = 0; + output_token->value = NULL; + } + + ctx = (gssspnego_ctx)*context_handle; + *context_handle = GSS_C_NO_CONTEXT; + + if (ctx == NULL) { + return GSS_S_NO_CONTEXT; + } + + if (ctx->initiator_mech_types.val != NULL) + free_MechTypeList(&ctx->initiator_mech_types); + + _gss_spnego_release_cred(&minor, &ctx->delegated_cred_id); + + gss_release_oid(&minor, &ctx->preferred_mech_type); + ctx->negotiated_mech_type = GSS_C_NO_OID; + + gss_release_name(&minor, &ctx->mech_src_name); + + if (ctx->negotiated_ctx_id != GSS_C_NO_CONTEXT) { + ret = gss_delete_sec_context(minor_status, + &ctx->negotiated_ctx_id, + output_token); + ctx->negotiated_ctx_id = GSS_C_NO_CONTEXT; + } else { + ret = GSS_S_COMPLETE; + } + + HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); + HEIMDAL_MUTEX_destroy(&ctx->ctx_id_mutex); + + free(ctx); + *context_handle = NULL; + + return ret; +} + +/* + * For compatability with the Windows SPNEGO implementation, the + * default is to ignore the mechListMIC unless CFX is used and + * a non-preferred mechanism was negotiated + */ + +OM_uint32 +_gss_spnego_require_mechlist_mic(OM_uint32 *minor_status, + gssspnego_ctx ctx, + int *require_mic) +{ + gss_buffer_set_t buffer_set = GSS_C_NO_BUFFER_SET; + OM_uint32 minor; + + *minor_status = 0; + *require_mic = 0; + + if (ctx == NULL) { + return GSS_S_COMPLETE; + } + + if (ctx->require_mic) { + /* Acceptor requested it: mandatory to honour */ + *require_mic = 1; + return GSS_S_COMPLETE; + } + + /* + * Check whether peer indicated implicit support for updated SPNEGO + * (eg. in the Kerberos case by using CFX) + */ + if (gss_inquire_sec_context_by_oid(&minor, ctx->negotiated_ctx_id, + GSS_C_PEER_HAS_UPDATED_SPNEGO, + &buffer_set) == GSS_S_COMPLETE) { + *require_mic = 1; + gss_release_buffer_set(&minor, &buffer_set); + } + + /* Safe-to-omit MIC rules follow */ + if (*require_mic) { + if (gss_oid_equal(ctx->negotiated_mech_type, ctx->preferred_mech_type)) { + *require_mic = 0; + } else if (gss_oid_equal(ctx->negotiated_mech_type, &gss_krb5_mechanism_oid_desc) && + gss_oid_equal(ctx->preferred_mech_type, &gss_mskrb_mechanism_oid_desc)) { + *require_mic = 0; + } + } + + return GSS_S_COMPLETE; +} + +int _gss_spnego_add_mech_type(gss_OID mech_type, + int includeMSCompatOID, + MechTypeList *mechtypelist) +{ + int ret; + + if (gss_oid_equal(mech_type, GSS_SPNEGO_MECHANISM)) + return 0; + + if (includeMSCompatOID && + gss_oid_equal(mech_type, &gss_krb5_mechanism_oid_desc)) { + ret = der_get_oid(gss_mskrb_mechanism_oid_desc.elements, + gss_mskrb_mechanism_oid_desc.length, + &mechtypelist->val[mechtypelist->len], + NULL); + if (ret) + return ret; + mechtypelist->len++; + } + ret = der_get_oid(mech_type->elements, + mech_type->length, + &mechtypelist->val[mechtypelist->len], + NULL); + if (ret) + return ret; + mechtypelist->len++; + + return 0; +} + +OM_uint32 +_gss_spnego_select_mech(OM_uint32 *minor_status, + MechType *mechType, + gss_OID *mech_p) +{ + char mechbuf[64]; + size_t mech_len; + gss_OID_desc oid; + OM_uint32 ret; + + ret = der_put_oid ((unsigned char *)mechbuf + sizeof(mechbuf) - 1, + sizeof(mechbuf), + mechType, + &mech_len); + if (ret) { + return GSS_S_DEFECTIVE_TOKEN; + } + + oid.length = mech_len; + oid.elements = mechbuf + sizeof(mechbuf) - mech_len; + + if (gss_oid_equal(&oid, GSS_SPNEGO_MECHANISM)) { + return GSS_S_BAD_MECH; + } + + *minor_status = 0; + + /* Translate broken MS Kebreros OID */ + if (gss_oid_equal(&oid, &gss_mskrb_mechanism_oid_desc)) { + gssapi_mech_interface mech; + + mech = __gss_get_mechanism(&gss_krb5_mechanism_oid_desc); + if (mech == NULL) + return GSS_S_BAD_MECH; + + ret = gss_duplicate_oid(minor_status, + &gss_mskrb_mechanism_oid_desc, + mech_p); + } else { + gssapi_mech_interface mech; + + mech = __gss_get_mechanism(&oid); + if (mech == NULL) + return GSS_S_BAD_MECH; + + ret = gss_duplicate_oid(minor_status, + &mech->gm_mech_oid, + mech_p); + } + + return ret; +} + diff --git a/source4/heimdal/lib/gssapi/spnego/context_stubs.c b/source4/heimdal/lib/gssapi/spnego/context_stubs.c new file mode 100644 index 0000000000..902ddbbdf9 --- /dev/null +++ b/source4/heimdal/lib/gssapi/spnego/context_stubs.c @@ -0,0 +1,835 @@ +/* + * Copyright (c) 2004, PADL Software Pty Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of PADL Software nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY PADL SOFTWARE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PADL SOFTWARE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "spnego/spnego_locl.h" + +RCSID("$Id: context_stubs.c,v 1.8 2006/10/07 22:27:01 lha Exp $"); + +static OM_uint32 +spnego_supported_mechs(OM_uint32 *minor_status, gss_OID_set *mechs) +{ + OM_uint32 ret, junk; + gss_OID_set m; + int i; + + ret = gss_indicate_mechs(minor_status, &m); + if (ret != GSS_S_COMPLETE) + return ret; + + ret = gss_create_empty_oid_set(minor_status, mechs); + if (ret != GSS_S_COMPLETE) { + gss_release_oid_set(&junk, &m); + return ret; + } + + for (i = 0; i < m->count; i++) { + if (gss_oid_equal(&m->elements[i], GSS_SPNEGO_MECHANISM)) + continue; + + ret = gss_add_oid_set_member(minor_status, &m->elements[i], mechs); + if (ret) { + gss_release_oid_set(&junk, &m); + gss_release_oid_set(&junk, mechs); + return ret; + } + } + return ret; +} + + + +OM_uint32 _gss_spnego_process_context_token + (OM_uint32 *minor_status, + const gss_ctx_id_t context_handle, + const gss_buffer_t token_buffer + ) +{ + gss_ctx_id_t context ; + gssspnego_ctx ctx; + OM_uint32 ret; + + if (context_handle == GSS_C_NO_CONTEXT) + return GSS_S_NO_CONTEXT; + + context = context_handle; + ctx = (gssspnego_ctx)context_handle; + + HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex); + + ret = gss_process_context_token(minor_status, + ctx->negotiated_ctx_id, + token_buffer); + if (ret != GSS_S_COMPLETE) { + HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); + return ret; + } + + ctx->negotiated_ctx_id = GSS_C_NO_CONTEXT; + + return _gss_spnego_internal_delete_sec_context(minor_status, + &context, + GSS_C_NO_BUFFER); +} + +OM_uint32 _gss_spnego_delete_sec_context + (OM_uint32 *minor_status, + gss_ctx_id_t *context_handle, + gss_buffer_t output_token + ) +{ + gssspnego_ctx ctx; + + if (context_handle == NULL || *context_handle == GSS_C_NO_CONTEXT) + return GSS_S_NO_CONTEXT; + + ctx = (gssspnego_ctx)*context_handle; + + HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex); + + return _gss_spnego_internal_delete_sec_context(minor_status, + context_handle, + output_token); +} + +OM_uint32 _gss_spnego_context_time + (OM_uint32 *minor_status, + const gss_ctx_id_t context_handle, + OM_uint32 *time_rec + ) +{ + gssspnego_ctx ctx; + *minor_status = 0; + + if (context_handle == GSS_C_NO_CONTEXT) { + return GSS_S_NO_CONTEXT; + } + + ctx = (gssspnego_ctx)context_handle; + + if (ctx->negotiated_ctx_id == GSS_C_NO_CONTEXT) { + return GSS_S_NO_CONTEXT; + } + + return gss_context_time(minor_status, + ctx->negotiated_ctx_id, + time_rec); +} + +OM_uint32 _gss_spnego_get_mic + (OM_uint32 *minor_status, + const gss_ctx_id_t context_handle, + gss_qop_t qop_req, + const gss_buffer_t message_buffer, + gss_buffer_t message_token + ) +{ + gssspnego_ctx ctx; + + *minor_status = 0; + + if (context_handle == GSS_C_NO_CONTEXT) { + return GSS_S_NO_CONTEXT; + } + + ctx = (gssspnego_ctx)context_handle; + + if (ctx->negotiated_ctx_id == GSS_C_NO_CONTEXT) { + return GSS_S_NO_CONTEXT; + } + + return gss_get_mic(minor_status, ctx->negotiated_ctx_id, + qop_req, message_buffer, message_token); +} + +OM_uint32 _gss_spnego_verify_mic + (OM_uint32 * minor_status, + const gss_ctx_id_t context_handle, + const gss_buffer_t message_buffer, + const gss_buffer_t token_buffer, + gss_qop_t * qop_state + ) +{ + gssspnego_ctx ctx; + + *minor_status = 0; + + if (context_handle == GSS_C_NO_CONTEXT) { + return GSS_S_NO_CONTEXT; + } + + ctx = (gssspnego_ctx)context_handle; + + if (ctx->negotiated_ctx_id == GSS_C_NO_CONTEXT) { + return GSS_S_NO_CONTEXT; + } + + return gss_verify_mic(minor_status, + ctx->negotiated_ctx_id, + message_buffer, + token_buffer, + qop_state); +} + +OM_uint32 _gss_spnego_wrap + (OM_uint32 * minor_status, + const gss_ctx_id_t context_handle, + int conf_req_flag, + gss_qop_t qop_req, + const gss_buffer_t input_message_buffer, + int * conf_state, + gss_buffer_t output_message_buffer + ) +{ + gssspnego_ctx ctx; + + *minor_status = 0; + + if (context_handle == GSS_C_NO_CONTEXT) { + return GSS_S_NO_CONTEXT; + } + + ctx = (gssspnego_ctx)context_handle; + + if (ctx->negotiated_ctx_id == GSS_C_NO_CONTEXT) { + return GSS_S_NO_CONTEXT; + } + + return gss_wrap(minor_status, + ctx->negotiated_ctx_id, + conf_req_flag, + qop_req, + input_message_buffer, + conf_state, + output_message_buffer); +} + +OM_uint32 _gss_spnego_unwrap + (OM_uint32 * minor_status, + const gss_ctx_id_t context_handle, + const gss_buffer_t input_message_buffer, + gss_buffer_t output_message_buffer, + int * conf_state, + gss_qop_t * qop_state + ) +{ + gssspnego_ctx ctx; + + *minor_status = 0; + + if (context_handle == GSS_C_NO_CONTEXT) { + return GSS_S_NO_CONTEXT; + } + + ctx = (gssspnego_ctx)context_handle; + + if (ctx->negotiated_ctx_id == GSS_C_NO_CONTEXT) { + return GSS_S_NO_CONTEXT; + } + + return gss_unwrap(minor_status, + ctx->negotiated_ctx_id, + input_message_buffer, + output_message_buffer, + conf_state, + qop_state); +} + +OM_uint32 _gss_spnego_display_status + (OM_uint32 * minor_status, + OM_uint32 status_value, + int status_type, + const gss_OID mech_type, + OM_uint32 * message_context, + gss_buffer_t status_string + ) +{ + return GSS_S_FAILURE; +} + +OM_uint32 _gss_spnego_compare_name + (OM_uint32 *minor_status, + const gss_name_t name1, + const gss_name_t name2, + int * name_equal + ) +{ + return gss_compare_name(minor_status, name1, name2, name_equal); +} + +OM_uint32 _gss_spnego_display_name + (OM_uint32 * minor_status, + const gss_name_t input_name, + gss_buffer_t output_name_buffer, + gss_OID * output_name_type + ) +{ + return gss_display_name(minor_status, input_name, + output_name_buffer, output_name_type); +} + +OM_uint32 _gss_spnego_import_name + (OM_uint32 * minor_status, + const gss_buffer_t input_name_buffer, + const gss_OID input_name_type, + gss_name_t * output_name + ) +{ + return gss_import_name(minor_status, input_name_buffer, + input_name_type, output_name); +} + +OM_uint32 _gss_spnego_export_name + (OM_uint32 * minor_status, + const gss_name_t input_name, + gss_buffer_t exported_name + ) +{ + return gss_export_name(minor_status, input_name, + exported_name); +} + +OM_uint32 _gss_spnego_release_name + (OM_uint32 * minor_status, + gss_name_t * input_name + ) +{ + return gss_release_name(minor_status, input_name); +} + +OM_uint32 _gss_spnego_inquire_context ( + OM_uint32 * minor_status, + const gss_ctx_id_t context_handle, + gss_name_t * src_name, + gss_name_t * targ_name, + OM_uint32 * lifetime_rec, + gss_OID * mech_type, + OM_uint32 * ctx_flags, + int * locally_initiated, + int * open_context + ) +{ + gssspnego_ctx ctx; + + *minor_status = 0; + + if (context_handle == GSS_C_NO_CONTEXT) { + return GSS_S_NO_CONTEXT; + } + + ctx = (gssspnego_ctx)context_handle; + + if (ctx->negotiated_ctx_id == GSS_C_NO_CONTEXT) { + return GSS_S_NO_CONTEXT; + } + + return gss_inquire_context(minor_status, + ctx->negotiated_ctx_id, + src_name, + targ_name, + lifetime_rec, + mech_type, + ctx_flags, + locally_initiated, + open_context); +} + +OM_uint32 _gss_spnego_wrap_size_limit ( + OM_uint32 * minor_status, + const gss_ctx_id_t context_handle, + int conf_req_flag, + gss_qop_t qop_req, + OM_uint32 req_output_size, + OM_uint32 * max_input_size + ) +{ + gssspnego_ctx ctx; + + *minor_status = 0; + + if (context_handle == GSS_C_NO_CONTEXT) { + return GSS_S_NO_CONTEXT; + } + + ctx = (gssspnego_ctx)context_handle; + + if (ctx->negotiated_ctx_id == GSS_C_NO_CONTEXT) { + return GSS_S_NO_CONTEXT; + } + + return gss_wrap_size_limit(minor_status, + ctx->negotiated_ctx_id, + conf_req_flag, + qop_req, + req_output_size, + max_input_size); +} + +OM_uint32 _gss_spnego_export_sec_context ( + OM_uint32 * minor_status, + gss_ctx_id_t * context_handle, + gss_buffer_t interprocess_token + ) +{ + gssspnego_ctx ctx; + OM_uint32 ret; + + *minor_status = 0; + + if (context_handle == NULL) { + return GSS_S_NO_CONTEXT; + } + + ctx = (gssspnego_ctx)*context_handle; + + if (ctx == NULL) + return GSS_S_NO_CONTEXT; + + HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex); + + if (ctx->negotiated_ctx_id == GSS_C_NO_CONTEXT) { + HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); + return GSS_S_NO_CONTEXT; + } + + ret = gss_export_sec_context(minor_status, + &ctx->negotiated_ctx_id, + interprocess_token); + if (ret == GSS_S_COMPLETE) { + ret = _gss_spnego_internal_delete_sec_context(minor_status, + context_handle, + GSS_C_NO_BUFFER); + if (ret == GSS_S_COMPLETE) + return GSS_S_COMPLETE; + } + + HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); + + return ret; +} + +OM_uint32 _gss_spnego_import_sec_context ( + OM_uint32 * minor_status, + const gss_buffer_t interprocess_token, + gss_ctx_id_t *context_handle + ) +{ + OM_uint32 ret, minor; + gss_ctx_id_t context; + gssspnego_ctx ctx; + + ret = _gss_spnego_alloc_sec_context(minor_status, &context); + if (ret != GSS_S_COMPLETE) { + return ret; + } + ctx = (gssspnego_ctx)context; + + HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex); + + ret = gss_import_sec_context(minor_status, + interprocess_token, + &ctx->negotiated_ctx_id); + if (ret != GSS_S_COMPLETE) { + _gss_spnego_internal_delete_sec_context(&minor, context_handle, GSS_C_NO_BUFFER); + return ret; + } + + ctx->open = 1; + /* don't bother filling in the rest of the fields */ + + HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); + + *context_handle = (gss_ctx_id_t)ctx; + + return GSS_S_COMPLETE; +} + +OM_uint32 _gss_spnego_inquire_names_for_mech ( + OM_uint32 * minor_status, + const gss_OID mechanism, + gss_OID_set * name_types + ) +{ + gss_OID_set mechs, names, n; + OM_uint32 ret, junk; + int i, j; + + *name_types = NULL; + + ret = spnego_supported_mechs(minor_status, &mechs); + if (ret != GSS_S_COMPLETE) + return ret; + + ret = gss_create_empty_oid_set(minor_status, &names); + if (ret != GSS_S_COMPLETE) + goto out; + + for (i = 0; i < mechs->count; i++) { + ret = gss_inquire_names_for_mech(minor_status, + &mechs->elements[i], + &n); + if (ret) + continue; + + for (j = 0; j < n->count; j++) + gss_add_oid_set_member(minor_status, + &n->elements[j], + &names); + gss_release_oid_set(&junk, &n); + } + + ret = GSS_S_COMPLETE; + *name_types = names; +out: + + gss_release_oid_set(&junk, &mechs); + + return GSS_S_COMPLETE; +} + +OM_uint32 _gss_spnego_inquire_mechs_for_name ( + OM_uint32 * minor_status, + const gss_name_t input_name, + gss_OID_set * mech_types + ) +{ + OM_uint32 ret, junk; + + ret = gss_create_empty_oid_set(minor_status, mech_types); + if (ret) + return ret; + + ret = gss_add_oid_set_member(minor_status, + GSS_SPNEGO_MECHANISM, + mech_types); + if (ret) + gss_release_oid_set(&junk, mech_types); + + return ret; +} + +OM_uint32 _gss_spnego_canonicalize_name ( + OM_uint32 * minor_status, + const gss_name_t input_name, + const gss_OID mech_type, + gss_name_t * output_name + ) +{ + /* XXX */ + return gss_duplicate_name(minor_status, input_name, output_name); +} + +OM_uint32 _gss_spnego_duplicate_name ( + OM_uint32 * minor_status, + const gss_name_t src_name, + gss_name_t * dest_name + ) +{ + return gss_duplicate_name(minor_status, src_name, dest_name); +} + +OM_uint32 _gss_spnego_sign + (OM_uint32 * minor_status, + gss_ctx_id_t context_handle, + int qop_req, + gss_buffer_t message_buffer, + gss_buffer_t message_token + ) +{ + gssspnego_ctx ctx; + + *minor_status = 0; + + if (context_handle == GSS_C_NO_CONTEXT) { + return GSS_S_NO_CONTEXT; + } + + ctx = (gssspnego_ctx)context_handle; + + if (ctx->negotiated_ctx_id == GSS_C_NO_CONTEXT) { + return GSS_S_NO_CONTEXT; + } + + return gss_sign(minor_status, + ctx->negotiated_ctx_id, + qop_req, + message_buffer, + message_token); +} + +OM_uint32 _gss_spnego_verify + (OM_uint32 * minor_status, + gss_ctx_id_t context_handle, + gss_buffer_t message_buffer, + gss_buffer_t token_buffer, + int * qop_state + ) +{ + gssspnego_ctx ctx; + + *minor_status = 0; + + if (context_handle == GSS_C_NO_CONTEXT) { + return GSS_S_NO_CONTEXT; + } + + ctx = (gssspnego_ctx)context_handle; + + if (ctx->negotiated_ctx_id == GSS_C_NO_CONTEXT) { + return GSS_S_NO_CONTEXT; + } + + return gss_verify(minor_status, + ctx->negotiated_ctx_id, + message_buffer, + token_buffer, + qop_state); +} + +OM_uint32 _gss_spnego_seal + (OM_uint32 * minor_status, + gss_ctx_id_t context_handle, + int conf_req_flag, + int qop_req, + gss_buffer_t input_message_buffer, + int * conf_state, + gss_buffer_t output_message_buffer + ) +{ + gssspnego_ctx ctx; + + *minor_status = 0; + + if (context_handle == GSS_C_NO_CONTEXT) { + return GSS_S_NO_CONTEXT; + } + + ctx = (gssspnego_ctx)context_handle; + + if (ctx->negotiated_ctx_id == GSS_C_NO_CONTEXT) { + return GSS_S_NO_CONTEXT; + } + + return gss_seal(minor_status, + ctx->negotiated_ctx_id, + conf_req_flag, + qop_req, + input_message_buffer, + conf_state, + output_message_buffer); +} + +OM_uint32 _gss_spnego_unseal + (OM_uint32 * minor_status, + gss_ctx_id_t context_handle, + gss_buffer_t input_message_buffer, + gss_buffer_t output_message_buffer, + int * conf_state, + int * qop_state + ) +{ + gssspnego_ctx ctx; + + *minor_status = 0; + + if (context_handle == GSS_C_NO_CONTEXT) { + return GSS_S_NO_CONTEXT; + } + + ctx = (gssspnego_ctx)context_handle; + + if (ctx->negotiated_ctx_id == GSS_C_NO_CONTEXT) { + return GSS_S_NO_CONTEXT; + } + + return gss_unseal(minor_status, + ctx->negotiated_ctx_id, + input_message_buffer, + output_message_buffer, + conf_state, + qop_state); +} + +#if 0 +OM_uint32 _gss_spnego_unwrap_ex + (OM_uint32 * minor_status, + const gss_ctx_id_t context_handle, + const gss_buffer_t token_header_buffer, + const gss_buffer_t associated_data_buffer, + const gss_buffer_t input_message_buffer, + gss_buffer_t output_message_buffer, + int * conf_state, + gss_qop_t * qop_state) +{ + gssspnego_ctx ctx; + + *minor_status = 0; + + if (context_handle == GSS_C_NO_CONTEXT) { + return GSS_S_NO_CONTEXT; + } + + ctx = (gssspnego_ctx)context_handle; + + if (ctx->negotiated_ctx_id == GSS_C_NO_CONTEXT) { + return GSS_S_NO_CONTEXT; + } + + return gss_unwrap_ex(minor_status, + ctx->negotiated_ctx_id, + token_header_buffer, + associated_data_buffer, + input_message_buffer, + output_message_buffer, + conf_state, + qop_state); +} + +OM_uint32 _gss_spnego_wrap_ex + (OM_uint32 * minor_status, + const gss_ctx_id_t context_handle, + int conf_req_flag, + gss_qop_t qop_req, + const gss_buffer_t associated_data_buffer, + const gss_buffer_t input_message_buffer, + int * conf_state, + gss_buffer_t output_token_buffer, + gss_buffer_t output_message_buffer + ) +{ + gssspnego_ctx ctx; + + *minor_status = 0; + + if (context_handle == GSS_C_NO_CONTEXT) { + return GSS_S_NO_CONTEXT; + } + + ctx = (gssspnego_ctx)context_handle; + + if (ctx->negotiated_ctx_id == GSS_C_NO_CONTEXT) { + return GSS_S_NO_CONTEXT; + } + + if ((ctx->mech_flags & GSS_C_DCE_STYLE) == 0 && + associated_data_buffer->length != input_message_buffer->length) { + *minor_status = EINVAL; + return GSS_S_BAD_QOP; + } + + return gss_wrap_ex(minor_status, + ctx->negotiated_ctx_id, + conf_req_flag, + qop_req, + associated_data_buffer, + input_message_buffer, + conf_state, + output_token_buffer, + output_message_buffer); +} + +OM_uint32 _gss_spnego_complete_auth_token + (OM_uint32 * minor_status, + const gss_ctx_id_t context_handle, + gss_buffer_t input_message_buffer) +{ + gssspnego_ctx ctx; + + *minor_status = 0; + + if (context_handle == GSS_C_NO_CONTEXT) { + return GSS_S_NO_CONTEXT; + } + + ctx = (gssspnego_ctx)context_handle; + + if (ctx->negotiated_ctx_id == GSS_C_NO_CONTEXT) { + return GSS_S_NO_CONTEXT; + } + + return gss_complete_auth_token(minor_status, + ctx->negotiated_ctx_id, + input_message_buffer); +} +#endif + +OM_uint32 _gss_spnego_inquire_sec_context_by_oid + (OM_uint32 * minor_status, + const gss_ctx_id_t context_handle, + const gss_OID desired_object, + gss_buffer_set_t *data_set) +{ + gssspnego_ctx ctx; + + *minor_status = 0; + + if (context_handle == GSS_C_NO_CONTEXT) { + return GSS_S_NO_CONTEXT; + } + + ctx = (gssspnego_ctx)context_handle; + + if (ctx->negotiated_ctx_id == GSS_C_NO_CONTEXT) { + return GSS_S_NO_CONTEXT; + } + + return gss_inquire_sec_context_by_oid(minor_status, + ctx->negotiated_ctx_id, + desired_object, + data_set); +} + +OM_uint32 _gss_spnego_set_sec_context_option + (OM_uint32 * minor_status, + gss_ctx_id_t * context_handle, + const gss_OID desired_object, + const gss_buffer_t value) +{ + gssspnego_ctx ctx; + + *minor_status = 0; + + if (context_handle == NULL || *context_handle == GSS_C_NO_CONTEXT) { + return GSS_S_NO_CONTEXT; + } + + ctx = (gssspnego_ctx)context_handle; + + if (ctx->negotiated_ctx_id == GSS_C_NO_CONTEXT) { + return GSS_S_NO_CONTEXT; + } + + return gss_set_sec_context_option(minor_status, + &ctx->negotiated_ctx_id, + desired_object, + value); +} + diff --git a/source4/heimdal/lib/gssapi/spnego/cred_stubs.c b/source4/heimdal/lib/gssapi/spnego/cred_stubs.c new file mode 100644 index 0000000000..8f8edab15e --- /dev/null +++ b/source4/heimdal/lib/gssapi/spnego/cred_stubs.c @@ -0,0 +1,291 @@ +/* + * Copyright (c) 2004, PADL Software Pty Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of PADL Software nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY PADL SOFTWARE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PADL SOFTWARE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "spnego/spnego_locl.h" + +RCSID("$Id: cred_stubs.c,v 1.5 2006/10/07 22:27:04 lha Exp $"); + +OM_uint32 +_gss_spnego_release_cred(OM_uint32 *minor_status, gss_cred_id_t *cred_handle) +{ + gssspnego_cred cred; + OM_uint32 ret; + + *minor_status = 0; + + if (*cred_handle == GSS_C_NO_CREDENTIAL) { + return GSS_S_COMPLETE; + } + cred = (gssspnego_cred)*cred_handle; + + ret = gss_release_cred(minor_status, &cred->negotiated_cred_id); + + free(cred); + *cred_handle = GSS_C_NO_CREDENTIAL; + + return ret; +} + +OM_uint32 +_gss_spnego_alloc_cred(OM_uint32 *minor_status, + gss_cred_id_t mech_cred_handle, + gss_cred_id_t *cred_handle) +{ + gssspnego_cred cred; + + if (*cred_handle != GSS_C_NO_CREDENTIAL) { + *minor_status = EINVAL; + return GSS_S_FAILURE; + } + + cred = calloc(1, sizeof(*cred)); + if (cred == NULL) { + *cred_handle = GSS_C_NO_CREDENTIAL; + *minor_status = ENOMEM; + return GSS_S_FAILURE; + } + + cred->negotiated_cred_id = mech_cred_handle; + + *cred_handle = (gss_cred_id_t)cred; + + return GSS_S_COMPLETE; +} + +/* + * For now, just a simple wrapper that avoids recursion. When + * we support gss_{get,set}_neg_mechs() we will need to expose + * more functionality. + */ +OM_uint32 _gss_spnego_acquire_cred +(OM_uint32 *minor_status, + const gss_name_t desired_name, + OM_uint32 time_req, + const gss_OID_set desired_mechs, + gss_cred_usage_t cred_usage, + gss_cred_id_t * output_cred_handle, + gss_OID_set * actual_mechs, + OM_uint32 * time_rec + ) +{ + OM_uint32 ret, tmp; + gss_OID_set_desc actual_desired_mechs; + gss_OID_set mechs; + int i, j; + gss_cred_id_t cred_handle = GSS_C_NO_CREDENTIAL; + gssspnego_cred cred; + + *output_cred_handle = GSS_C_NO_CREDENTIAL; + + ret = gss_indicate_mechs(minor_status, &mechs); + if (ret != GSS_S_COMPLETE) + return ret; + + /* Remove ourselves from this list */ + actual_desired_mechs.count = mechs->count; + actual_desired_mechs.elements = malloc(actual_desired_mechs.count * + sizeof(gss_OID_desc)); + if (actual_desired_mechs.elements == NULL) { + *minor_status = ENOMEM; + ret = GSS_S_FAILURE; + goto out; + } + + for (i = 0, j = 0; i < mechs->count; i++) { + if (gss_oid_equal(&mechs->elements[i], GSS_SPNEGO_MECHANISM)) + continue; + + actual_desired_mechs.elements[j] = mechs->elements[i]; + j++; + } + actual_desired_mechs.count = j; + + ret = _gss_spnego_alloc_cred(minor_status, GSS_C_NO_CREDENTIAL, + &cred_handle); + if (ret != GSS_S_COMPLETE) + goto out; + + cred = (gssspnego_cred)cred_handle; + ret = gss_acquire_cred(minor_status, desired_name, + time_req, &actual_desired_mechs, + cred_usage, + &cred->negotiated_cred_id, + actual_mechs, time_rec); + if (ret != GSS_S_COMPLETE) + goto out; + + *output_cred_handle = cred_handle; + +out: + gss_release_oid_set(&tmp, &mechs); + if (actual_desired_mechs.elements != NULL) { + free(actual_desired_mechs.elements); + } + if (ret != GSS_S_COMPLETE) { + _gss_spnego_release_cred(&tmp, &cred_handle); + } + + return ret; +} + +OM_uint32 _gss_spnego_inquire_cred + (OM_uint32 * minor_status, + const gss_cred_id_t cred_handle, + gss_name_t * name, + OM_uint32 * lifetime, + gss_cred_usage_t * cred_usage, + gss_OID_set * mechanisms + ) +{ + gssspnego_cred cred; + OM_uint32 ret; + + if (cred_handle == GSS_C_NO_CREDENTIAL) { + *minor_status = 0; + return GSS_S_NO_CRED; + } + + cred = (gssspnego_cred)cred_handle; + + ret = gss_inquire_cred(minor_status, + cred->negotiated_cred_id, + name, + lifetime, + cred_usage, + mechanisms); + + return ret; +} + +OM_uint32 _gss_spnego_add_cred ( + OM_uint32 * minor_status, + const gss_cred_id_t input_cred_handle, + const gss_name_t desired_name, + const gss_OID desired_mech, + gss_cred_usage_t cred_usage, + OM_uint32 initiator_time_req, + OM_uint32 acceptor_time_req, + gss_cred_id_t * output_cred_handle, + gss_OID_set * actual_mechs, + OM_uint32 * initiator_time_rec, + OM_uint32 * acceptor_time_rec + ) +{ + gss_cred_id_t spnego_output_cred_handle = GSS_C_NO_CREDENTIAL; + OM_uint32 ret, tmp; + gssspnego_cred input_cred, output_cred; + + *output_cred_handle = GSS_C_NO_CREDENTIAL; + + ret = _gss_spnego_alloc_cred(minor_status, GSS_C_NO_CREDENTIAL, + &spnego_output_cred_handle); + if (ret) + return ret; + + input_cred = (gssspnego_cred)input_cred_handle; + output_cred = (gssspnego_cred)spnego_output_cred_handle; + + ret = gss_add_cred(minor_status, + input_cred->negotiated_cred_id, + desired_name, + desired_mech, + cred_usage, + initiator_time_req, + acceptor_time_req, + &output_cred->negotiated_cred_id, + actual_mechs, + initiator_time_rec, + acceptor_time_rec); + if (ret) { + _gss_spnego_release_cred(&tmp, &spnego_output_cred_handle); + return ret; + } + + *output_cred_handle = spnego_output_cred_handle; + + return GSS_S_COMPLETE; +} + +OM_uint32 _gss_spnego_inquire_cred_by_mech ( + OM_uint32 * minor_status, + const gss_cred_id_t cred_handle, + const gss_OID mech_type, + gss_name_t * name, + OM_uint32 * initiator_lifetime, + OM_uint32 * acceptor_lifetime, + gss_cred_usage_t * cred_usage + ) +{ + gssspnego_cred cred; + OM_uint32 ret; + + if (cred_handle == GSS_C_NO_CREDENTIAL) { + *minor_status = 0; + return GSS_S_NO_CRED; + } + + cred = (gssspnego_cred)cred_handle; + + ret = gss_inquire_cred_by_mech(minor_status, + cred->negotiated_cred_id, + mech_type, + name, + initiator_lifetime, + acceptor_lifetime, + cred_usage); + + return ret; +} + +OM_uint32 _gss_spnego_inquire_cred_by_oid + (OM_uint32 * minor_status, + const gss_cred_id_t cred_handle, + const gss_OID desired_object, + gss_buffer_set_t *data_set) +{ + gssspnego_cred cred; + OM_uint32 ret; + + if (cred_handle == GSS_C_NO_CREDENTIAL) { + *minor_status = 0; + return GSS_S_NO_CRED; + } + cred = (gssspnego_cred)cred_handle; + + ret = gss_inquire_cred_by_oid(minor_status, + cred->negotiated_cred_id, + desired_object, + data_set); + + return ret; +} + diff --git a/source4/heimdal/lib/gssapi/spnego/external.c b/source4/heimdal/lib/gssapi/spnego/external.c new file mode 100644 index 0000000000..b7e02a55e1 --- /dev/null +++ b/source4/heimdal/lib/gssapi/spnego/external.c @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2004, PADL Software Pty Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of PADL Software nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY PADL SOFTWARE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PADL SOFTWARE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "spnego/spnego_locl.h" +#include <gssapi_mech.h> + +RCSID("$Id: external.c,v 1.7 2006/10/07 22:27:06 lha Exp $"); + +/* + * RFC2478, SPNEGO: + * The security mechanism of the initial + * negotiation token is identified by the Object Identifier + * iso.org.dod.internet.security.mechanism.snego (1.3.6.1.5.5.2). + */ + +static gssapi_mech_interface_desc spnego_mech = { + GMI_VERSION, + "spnego", + {6, (void *)"\x2b\x06\x01\x05\x05\x02"}, + _gss_spnego_acquire_cred, + _gss_spnego_release_cred, + _gss_spnego_init_sec_context, + _gss_spnego_accept_sec_context, + _gss_spnego_process_context_token, + _gss_spnego_internal_delete_sec_context, + _gss_spnego_context_time, + _gss_spnego_get_mic, + _gss_spnego_verify_mic, + _gss_spnego_wrap, + _gss_spnego_unwrap, + _gss_spnego_display_status, + NULL, + _gss_spnego_compare_name, + _gss_spnego_display_name, + _gss_spnego_import_name, + _gss_spnego_export_name, + _gss_spnego_release_name, + _gss_spnego_inquire_cred, + _gss_spnego_inquire_context, + _gss_spnego_wrap_size_limit, + _gss_spnego_add_cred, + _gss_spnego_inquire_cred_by_mech, + _gss_spnego_export_sec_context, + _gss_spnego_import_sec_context, + _gss_spnego_inquire_names_for_mech, + _gss_spnego_inquire_mechs_for_name, + _gss_spnego_canonicalize_name, + _gss_spnego_duplicate_name +}; + +gssapi_mech_interface +__gss_spnego_initialize(void) +{ + return &spnego_mech; +} + +static gss_OID_desc _gss_spnego_mechanism_desc = + {6, (void *)"\x2b\x06\x01\x05\x05\x02"}; + +gss_OID GSS_SPNEGO_MECHANISM = &_gss_spnego_mechanism_desc; diff --git a/source4/heimdal/lib/gssapi/spnego/init_sec_context.c b/source4/heimdal/lib/gssapi/spnego/init_sec_context.c new file mode 100644 index 0000000000..5a652fdb2e --- /dev/null +++ b/source4/heimdal/lib/gssapi/spnego/init_sec_context.c @@ -0,0 +1,578 @@ +/* + * Copyright (c) 1997 - 2004 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * Portions Copyright (c) 2004 PADL Software Pty Ltd. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "spnego/spnego_locl.h" + +RCSID("$Id: init_sec_context.c,v 1.6 2006/10/14 10:09:15 lha Exp $"); + +/* + * Send a reply. Note that we only need to send a reply if we + * need to send a MIC or a mechanism token. Otherwise, we can + * return an empty buffer. + * + * The return value of this will be returned to the API, so it + * must return GSS_S_CONTINUE_NEEDED if a token was generated. + */ +static OM_uint32 +spnego_reply_internal(OM_uint32 *minor_status, + gssspnego_ctx context_handle, + const gss_buffer_t mech_buf, + gss_buffer_t mech_token, + gss_buffer_t output_token) +{ + NegTokenResp resp; + gss_buffer_desc mic_buf; + OM_uint32 ret; + gss_buffer_desc data; + u_char *buf; + + if (mech_buf == GSS_C_NO_BUFFER && mech_token->length == 0) { + output_token->length = 0; + output_token->value = NULL; + + return context_handle->open ? GSS_S_COMPLETE : GSS_S_FAILURE; + } + + memset(&resp, 0, sizeof(resp)); + + ALLOC(resp.negResult, 1); + if (resp.negResult == NULL) { + *minor_status = ENOMEM; + return GSS_S_FAILURE; + } + + resp.supportedMech = NULL; + + output_token->length = 0; + output_token->value = NULL; + + if (mech_token->length == 0) { + resp.responseToken = NULL; + *(resp.negResult) = accept_completed; + } else { + ALLOC(resp.responseToken, 1); + if (resp.responseToken == NULL) { + free_NegTokenResp(&resp); + *minor_status = ENOMEM; + return GSS_S_FAILURE; + } + resp.responseToken->length = mech_token->length; + resp.responseToken->data = mech_token->value; + mech_token->length = 0; + mech_token->value = NULL; + + *(resp.negResult) = accept_incomplete; + } + + if (mech_buf != GSS_C_NO_BUFFER) { + ALLOC(resp.mechListMIC, 1); + if (resp.mechListMIC == NULL) { + free_NegTokenResp(&resp); + *minor_status = ENOMEM; + return GSS_S_FAILURE; + } + + ret = gss_get_mic(minor_status, + context_handle->negotiated_ctx_id, + 0, + mech_buf, + &mic_buf); + if (ret) { + free_NegTokenResp(&resp); + *minor_status = ENOMEM; + return GSS_S_FAILURE; + } + + resp.mechListMIC->length = mic_buf.length; + resp.mechListMIC->data = mic_buf.value; + } else { + resp.mechListMIC = NULL; + } + + ret = _gss_spnego_encode_response (minor_status, &resp, + &data, &buf); + if (ret) { + free_NegTokenResp(&resp); + return ret; + } + + output_token->value = malloc(data.length); + if (output_token->value == NULL) { + *minor_status = ENOMEM; + ret = GSS_S_FAILURE; + } else { + output_token->length = data.length; + memcpy(output_token->value, data.value, output_token->length); + } + free(buf); + + if (*(resp.negResult) == accept_completed) + ret = GSS_S_COMPLETE; + else + ret = GSS_S_CONTINUE_NEEDED; + + free_NegTokenResp(&resp); + return ret; +} + +static OM_uint32 +spnego_initial + (OM_uint32 * minor_status, + gssspnego_cred cred, + gss_ctx_id_t * context_handle, + const gss_name_t target_name, + const gss_OID mech_type, + OM_uint32 req_flags, + OM_uint32 time_req, + const gss_channel_bindings_t input_chan_bindings, + const gss_buffer_t input_token, + gss_OID * actual_mech_type, + gss_buffer_t output_token, + OM_uint32 * ret_flags, + OM_uint32 * time_rec + ) +{ + NegTokenInit ni; + int ret; + OM_uint32 sub, minor; + gss_buffer_desc mech_token; + u_char *buf; + size_t buf_size, buf_len; + gss_buffer_desc data; + size_t ni_len; + gss_ctx_id_t context; + gssspnego_ctx ctx; + + memset (&ni, 0, sizeof(ni)); + + *context_handle = GSS_C_NO_CONTEXT; + + *minor_status = 0; + + sub = _gss_spnego_alloc_sec_context(&minor, &context); + if (GSS_ERROR(sub)) { + *minor_status = minor; + return sub; + } + ctx = (gssspnego_ctx)context; + + HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex); + + ctx->local = 1; + + sub = _gss_spnego_indicate_mechtypelist(&minor, 0, + cred, + &ni.mechTypes, + &ctx->preferred_mech_type); + if (GSS_ERROR(sub)) { + *minor_status = minor; + _gss_spnego_internal_delete_sec_context(&minor, &context, GSS_C_NO_BUFFER); + return sub; + } + + ni.reqFlags = NULL; + + /* + * If we have a credential handle, use it to select the mechanism + * that we will use + */ + + /* generate optimistic token */ + sub = gss_init_sec_context(&minor, + (cred != NULL) ? cred->negotiated_cred_id : + GSS_C_NO_CREDENTIAL, + &ctx->negotiated_ctx_id, + target_name, + GSS_C_NO_OID, + req_flags, + time_req, + input_chan_bindings, + input_token, + &ctx->negotiated_mech_type, + &mech_token, + &ctx->mech_flags, + &ctx->mech_time_rec); + if (GSS_ERROR(sub)) { + free_NegTokenInit(&ni); + *minor_status = minor; + _gss_spnego_internal_delete_sec_context(&minor, &context, GSS_C_NO_BUFFER); + return sub; + } + + if (mech_token.length != 0) { + ALLOC(ni.mechToken, 1); + if (ni.mechToken == NULL) { + free_NegTokenInit(&ni); + gss_release_buffer(&minor, &mech_token); + _gss_spnego_internal_delete_sec_context(&minor, &context, GSS_C_NO_BUFFER); + *minor_status = ENOMEM; + return GSS_S_FAILURE; + } + ni.mechToken->length = mech_token.length; + ni.mechToken->data = malloc(mech_token.length); + if (ni.mechToken->data == NULL && mech_token.length != 0) { + free_NegTokenInit(&ni); + gss_release_buffer(&minor, &mech_token); + *minor_status = ENOMEM; + _gss_spnego_internal_delete_sec_context(&minor, &context, GSS_C_NO_BUFFER); + return GSS_S_FAILURE; + } + memcpy(ni.mechToken->data, mech_token.value, mech_token.length); + gss_release_buffer(&minor, &mech_token); + } else + ni.mechToken = NULL; + + ni.mechListMIC = NULL; + + ni_len = length_NegTokenInit(&ni); + buf_size = 1 + der_length_len(ni_len) + ni_len; + + buf = malloc(buf_size); + if (buf == NULL) { + free_NegTokenInit(&ni); + *minor_status = ENOMEM; + _gss_spnego_internal_delete_sec_context(&minor, &context, GSS_C_NO_BUFFER); + return GSS_S_FAILURE; + } + + ret = encode_NegTokenInit(buf + buf_size - 1, + ni_len, + &ni, &buf_len); + if (ret == 0 && ni_len != buf_len) + abort(); + + if (ret == 0) { + size_t tmp; + + ret = der_put_length_and_tag(buf + buf_size - buf_len - 1, + buf_size - buf_len, + buf_len, + ASN1_C_CONTEXT, + CONS, + 0, + &tmp); + if (ret == 0 && tmp + buf_len != buf_size) + abort(); + } + if (ret) { + *minor_status = ret; + free(buf); + free_NegTokenInit(&ni); + _gss_spnego_internal_delete_sec_context(&minor, &context, GSS_C_NO_BUFFER); + return GSS_S_FAILURE; + } + + data.value = buf; + data.length = buf_size; + + ctx->initiator_mech_types.len = ni.mechTypes.len; + ctx->initiator_mech_types.val = ni.mechTypes.val; + ni.mechTypes.len = 0; + ni.mechTypes.val = NULL; + + free_NegTokenInit(&ni); + + sub = gss_encapsulate_token(&data, + GSS_SPNEGO_MECHANISM, + output_token); + free (buf); + + if (sub) { + _gss_spnego_internal_delete_sec_context(&minor, &context, GSS_C_NO_BUFFER); + return sub; + } + + if (actual_mech_type) + *actual_mech_type = ctx->negotiated_mech_type; + if (ret_flags) + *ret_flags = ctx->mech_flags; + if (time_rec) + *time_rec = ctx->mech_time_rec; + + HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); + + *context_handle = context; + + return GSS_S_CONTINUE_NEEDED; +} + +static OM_uint32 +spnego_reply + (OM_uint32 * minor_status, + const gssspnego_cred cred, + gss_ctx_id_t * context_handle, + const gss_name_t target_name, + const gss_OID mech_type, + OM_uint32 req_flags, + OM_uint32 time_req, + const gss_channel_bindings_t input_chan_bindings, + const gss_buffer_t input_token, + gss_OID * actual_mech_type, + gss_buffer_t output_token, + OM_uint32 * ret_flags, + OM_uint32 * time_rec + ) +{ + OM_uint32 ret, minor; + NegTokenResp resp; + u_char oidbuf[17]; + size_t oidlen; + size_t len, taglen; + gss_OID_desc mech; + int require_mic; + size_t buf_len; + gss_buffer_desc mic_buf, mech_buf; + gss_buffer_desc mech_output_token; + gssspnego_ctx ctx; + + *minor_status = 0; + + ctx = (gssspnego_ctx)*context_handle; + + output_token->length = 0; + output_token->value = NULL; + + mech_output_token.length = 0; + mech_output_token.value = NULL; + + mech_buf.value = NULL; + mech_buf.length = 0; + + ret = der_match_tag_and_length(input_token->value, input_token->length, + ASN1_C_CONTEXT, CONS, 1, &len, &taglen); + if (ret) + return ret; + + if (len > input_token->length - taglen) + return ASN1_OVERRUN; + + ret = decode_NegTokenResp((const unsigned char *)input_token->value+taglen, + len, &resp, NULL); + if (ret) { + *minor_status = ENOMEM; + return GSS_S_FAILURE; + } + + if (resp.negResult == NULL + || *(resp.negResult) == reject + || resp.supportedMech == NULL) { + free_NegTokenResp(&resp); + return GSS_S_BAD_MECH; + } + + ret = der_put_oid(oidbuf + sizeof(oidbuf) - 1, + sizeof(oidbuf), + resp.supportedMech, + &oidlen); + if (ret || (oidlen == GSS_SPNEGO_MECHANISM->length && + memcmp(oidbuf + sizeof(oidbuf) - oidlen, + GSS_SPNEGO_MECHANISM->elements, + oidlen) == 0)) { + /* Avoid recursively embedded SPNEGO */ + free_NegTokenResp(&resp); + return GSS_S_BAD_MECH; + } + + HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex); + + if (resp.responseToken != NULL) { + gss_buffer_desc mech_input_token; + + mech_input_token.length = resp.responseToken->length; + mech_input_token.value = resp.responseToken->data; + + mech.length = oidlen; + mech.elements = oidbuf + sizeof(oidbuf) - oidlen; + + /* Fall through as if the negotiated mechanism + was requested explicitly */ + ret = gss_init_sec_context(&minor, + (cred != NULL) ? cred->negotiated_cred_id : + GSS_C_NO_CREDENTIAL, + &ctx->negotiated_ctx_id, + target_name, + &mech, + req_flags, + time_req, + input_chan_bindings, + &mech_input_token, + &ctx->negotiated_mech_type, + &mech_output_token, + &ctx->mech_flags, + &ctx->mech_time_rec); + if (GSS_ERROR(ret)) { + HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); + free_NegTokenResp(&resp); + *minor_status = minor; + return ret; + } + if (ret == GSS_S_COMPLETE) { + ctx->open = 1; + } + } + + if (*(resp.negResult) == request_mic) { + ctx->require_mic = 1; + } + + if (ctx->open) { + /* + * Verify the mechListMIC if one was provided or CFX was + * used and a non-preferred mechanism was selected + */ + if (resp.mechListMIC != NULL) { + require_mic = 1; + } else { + ret = _gss_spnego_require_mechlist_mic(minor_status, ctx, + &require_mic); + if (ret) { + HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); + free_NegTokenResp(&resp); + gss_release_buffer(&minor, &mech_output_token); + return ret; + } + } + } else { + require_mic = 0; + } + + if (require_mic) { + ASN1_MALLOC_ENCODE(MechTypeList, mech_buf.value, mech_buf.length, + &ctx->initiator_mech_types, &buf_len, ret); + if (ret) { + HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); + free_NegTokenResp(&resp); + gss_release_buffer(&minor, &mech_output_token); + *minor_status = ret; + return GSS_S_FAILURE; + } + if (mech_buf.length != buf_len) + abort(); + + if (resp.mechListMIC == NULL) { + HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); + free(mech_buf.value); + free_NegTokenResp(&resp); + *minor_status = 0; + return GSS_S_DEFECTIVE_TOKEN; + } + mic_buf.length = resp.mechListMIC->length; + mic_buf.value = resp.mechListMIC->data; + + if (mech_output_token.length == 0) { + ret = gss_verify_mic(minor_status, + ctx->negotiated_ctx_id, + &mech_buf, + &mic_buf, + NULL); + if (ret) { + HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); + free(mech_buf.value); + gss_release_buffer(&minor, &mech_output_token); + free_NegTokenResp(&resp); + return GSS_S_DEFECTIVE_TOKEN; + } + ctx->verified_mic = 1; + } + } + + ret = spnego_reply_internal(minor_status, ctx, + require_mic ? &mech_buf : NULL, + &mech_output_token, + output_token); + + if (mech_buf.value != NULL) + free(mech_buf.value); + + free_NegTokenResp(&resp); + gss_release_buffer(&minor, &mech_output_token); + + if (actual_mech_type) + *actual_mech_type = ctx->negotiated_mech_type; + if (ret_flags) + *ret_flags = ctx->mech_flags; + if (time_rec) + *time_rec = ctx->mech_time_rec; + + HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); + return ret; +} + +OM_uint32 _gss_spnego_init_sec_context + (OM_uint32 * minor_status, + const gss_cred_id_t initiator_cred_handle, + gss_ctx_id_t * context_handle, + const gss_name_t target_name, + const gss_OID mech_type, + OM_uint32 req_flags, + OM_uint32 time_req, + const gss_channel_bindings_t input_chan_bindings, + const gss_buffer_t input_token, + gss_OID * actual_mech_type, + gss_buffer_t output_token, + OM_uint32 * ret_flags, + OM_uint32 * time_rec + ) +{ + gssspnego_cred cred = (gssspnego_cred)initiator_cred_handle; + + if (*context_handle == GSS_C_NO_CONTEXT) + return spnego_initial (minor_status, + cred, + context_handle, + target_name, + mech_type, + req_flags, + time_req, + input_chan_bindings, + input_token, + actual_mech_type, + output_token, + ret_flags, + time_rec); + else + return spnego_reply (minor_status, + cred, + context_handle, + target_name, + mech_type, + req_flags, + time_req, + input_chan_bindings, + input_token, + actual_mech_type, + output_token, + ret_flags, + time_rec); +} + diff --git a/source4/heimdal/lib/gssapi/spnego/spnego-private.h b/source4/heimdal/lib/gssapi/spnego/spnego-private.h new file mode 100644 index 0000000000..df50f65580 --- /dev/null +++ b/source4/heimdal/lib/gssapi/spnego/spnego-private.h @@ -0,0 +1,347 @@ +/* This is a generated file */ +#ifndef __spnego_private_h__ +#define __spnego_private_h__ + +#include <stdarg.h> + +gssapi_mech_interface +__gss_spnego_initialize (void); + +OM_uint32 +_gss_spnego_accept_sec_context ( + OM_uint32 * /*minor_status*/, + gss_ctx_id_t * /*context_handle*/, + const gss_cred_id_t /*acceptor_cred_handle*/, + const gss_buffer_t /*input_token_buffer*/, + const gss_channel_bindings_t /*input_chan_bindings*/, + gss_name_t * /*src_name*/, + gss_OID * /*mech_type*/, + gss_buffer_t /*output_token*/, + OM_uint32 * /*ret_flags*/, + OM_uint32 * /*time_rec*/, + gss_cred_id_t *delegated_cred_handle ); + +OM_uint32 +_gss_spnego_acquire_cred ( + OM_uint32 */*minor_status*/, + const gss_name_t /*desired_name*/, + OM_uint32 /*time_req*/, + const gss_OID_set /*desired_mechs*/, + gss_cred_usage_t /*cred_usage*/, + gss_cred_id_t * /*output_cred_handle*/, + gss_OID_set * /*actual_mechs*/, + OM_uint32 * time_rec ); + +OM_uint32 +_gss_spnego_add_cred ( + OM_uint32 * /*minor_status*/, + const gss_cred_id_t /*input_cred_handle*/, + const gss_name_t /*desired_name*/, + const gss_OID /*desired_mech*/, + gss_cred_usage_t /*cred_usage*/, + OM_uint32 /*initiator_time_req*/, + OM_uint32 /*acceptor_time_req*/, + gss_cred_id_t * /*output_cred_handle*/, + gss_OID_set * /*actual_mechs*/, + OM_uint32 * /*initiator_time_rec*/, + OM_uint32 * acceptor_time_rec ); + +int +_gss_spnego_add_mech_type ( + gss_OID /*mech_type*/, + int /*includeMSCompatOID*/, + MechTypeList */*mechtypelist*/); + +OM_uint32 +_gss_spnego_alloc_cred ( + OM_uint32 */*minor_status*/, + gss_cred_id_t /*mech_cred_handle*/, + gss_cred_id_t */*cred_handle*/); + +OM_uint32 +_gss_spnego_alloc_sec_context ( + OM_uint32 * /*minor_status*/, + gss_ctx_id_t */*context_handle*/); + +OM_uint32 +_gss_spnego_canonicalize_name ( + OM_uint32 * /*minor_status*/, + const gss_name_t /*input_name*/, + const gss_OID /*mech_type*/, + gss_name_t * output_name ); + +OM_uint32 +_gss_spnego_compare_name ( + OM_uint32 */*minor_status*/, + const gss_name_t /*name1*/, + const gss_name_t /*name2*/, + int * name_equal ); + +OM_uint32 +_gss_spnego_context_time ( + OM_uint32 */*minor_status*/, + const gss_ctx_id_t /*context_handle*/, + OM_uint32 *time_rec ); + +OM_uint32 +_gss_spnego_delete_sec_context ( + OM_uint32 */*minor_status*/, + gss_ctx_id_t */*context_handle*/, + gss_buffer_t output_token ); + +OM_uint32 +_gss_spnego_display_name ( + OM_uint32 * /*minor_status*/, + const gss_name_t /*input_name*/, + gss_buffer_t /*output_name_buffer*/, + gss_OID * output_name_type ); + +OM_uint32 +_gss_spnego_display_status ( + OM_uint32 * /*minor_status*/, + OM_uint32 /*status_value*/, + int /*status_type*/, + const gss_OID /*mech_type*/, + OM_uint32 * /*message_context*/, + gss_buffer_t status_string ); + +OM_uint32 +_gss_spnego_duplicate_name ( + OM_uint32 * /*minor_status*/, + const gss_name_t /*src_name*/, + gss_name_t * dest_name ); + +OM_uint32 +_gss_spnego_encode_response ( + OM_uint32 */*minor_status*/, + const NegTokenResp */*resp*/, + gss_buffer_t /*data*/, + u_char **/*ret_buf*/); + +OM_uint32 +_gss_spnego_export_name ( + OM_uint32 * /*minor_status*/, + const gss_name_t /*input_name*/, + gss_buffer_t exported_name ); + +OM_uint32 +_gss_spnego_export_sec_context ( + OM_uint32 * /*minor_status*/, + gss_ctx_id_t * /*context_handle*/, + gss_buffer_t interprocess_token ); + +OM_uint32 +_gss_spnego_get_mic ( + OM_uint32 */*minor_status*/, + const gss_ctx_id_t /*context_handle*/, + gss_qop_t /*qop_req*/, + const gss_buffer_t /*message_buffer*/, + gss_buffer_t message_token ); + +OM_uint32 +_gss_spnego_import_name ( + OM_uint32 * /*minor_status*/, + const gss_buffer_t /*input_name_buffer*/, + const gss_OID /*input_name_type*/, + gss_name_t * output_name ); + +OM_uint32 +_gss_spnego_import_sec_context ( + OM_uint32 * /*minor_status*/, + const gss_buffer_t /*interprocess_token*/, + gss_ctx_id_t *context_handle ); + +OM_uint32 +_gss_spnego_indicate_mechtypelist ( + OM_uint32 */*minor_status*/, + int /*includeMSCompatOID*/, + const gssspnego_cred /*cred_handle*/, + MechTypeList */*mechtypelist*/, + gss_OID */*preferred_mech*/); + +OM_uint32 +_gss_spnego_init_sec_context ( + OM_uint32 * /*minor_status*/, + const gss_cred_id_t /*initiator_cred_handle*/, + gss_ctx_id_t * /*context_handle*/, + const gss_name_t /*target_name*/, + const gss_OID /*mech_type*/, + OM_uint32 /*req_flags*/, + OM_uint32 /*time_req*/, + const gss_channel_bindings_t /*input_chan_bindings*/, + const gss_buffer_t /*input_token*/, + gss_OID * /*actual_mech_type*/, + gss_buffer_t /*output_token*/, + OM_uint32 * /*ret_flags*/, + OM_uint32 * time_rec ); + +OM_uint32 +_gss_spnego_inquire_context ( + OM_uint32 * /*minor_status*/, + const gss_ctx_id_t /*context_handle*/, + gss_name_t * /*src_name*/, + gss_name_t * /*targ_name*/, + OM_uint32 * /*lifetime_rec*/, + gss_OID * /*mech_type*/, + OM_uint32 * /*ctx_flags*/, + int * /*locally_initiated*/, + int * open_context ); + +OM_uint32 +_gss_spnego_inquire_cred ( + OM_uint32 * /*minor_status*/, + const gss_cred_id_t /*cred_handle*/, + gss_name_t * /*name*/, + OM_uint32 * /*lifetime*/, + gss_cred_usage_t * /*cred_usage*/, + gss_OID_set * mechanisms ); + +OM_uint32 +_gss_spnego_inquire_cred_by_mech ( + OM_uint32 * /*minor_status*/, + const gss_cred_id_t /*cred_handle*/, + const gss_OID /*mech_type*/, + gss_name_t * /*name*/, + OM_uint32 * /*initiator_lifetime*/, + OM_uint32 * /*acceptor_lifetime*/, + gss_cred_usage_t * cred_usage ); + +OM_uint32 +_gss_spnego_inquire_cred_by_oid ( + OM_uint32 * /*minor_status*/, + const gss_cred_id_t /*cred_handle*/, + const gss_OID /*desired_object*/, + gss_buffer_set_t */*data_set*/); + +OM_uint32 +_gss_spnego_inquire_mechs_for_name ( + OM_uint32 * /*minor_status*/, + const gss_name_t /*input_name*/, + gss_OID_set * mech_types ); + +OM_uint32 +_gss_spnego_inquire_names_for_mech ( + OM_uint32 * /*minor_status*/, + const gss_OID /*mechanism*/, + gss_OID_set * name_types ); + +OM_uint32 +_gss_spnego_inquire_sec_context_by_oid ( + OM_uint32 * /*minor_status*/, + const gss_ctx_id_t /*context_handle*/, + const gss_OID /*desired_object*/, + gss_buffer_set_t */*data_set*/); + +OM_uint32 +_gss_spnego_internal_delete_sec_context ( + OM_uint32 */*minor_status*/, + gss_ctx_id_t */*context_handle*/, + gss_buffer_t output_token ); + +OM_uint32 +_gss_spnego_process_context_token ( + OM_uint32 */*minor_status*/, + const gss_ctx_id_t /*context_handle*/, + const gss_buffer_t token_buffer ); + +OM_uint32 +_gss_spnego_release_cred ( + OM_uint32 */*minor_status*/, + gss_cred_id_t */*cred_handle*/); + +OM_uint32 +_gss_spnego_release_name ( + OM_uint32 * /*minor_status*/, + gss_name_t * input_name ); + +OM_uint32 +_gss_spnego_require_mechlist_mic ( + OM_uint32 */*minor_status*/, + gssspnego_ctx /*ctx*/, + int */*require_mic*/); + +OM_uint32 +_gss_spnego_seal ( + OM_uint32 * /*minor_status*/, + gss_ctx_id_t /*context_handle*/, + int /*conf_req_flag*/, + int /*qop_req*/, + gss_buffer_t /*input_message_buffer*/, + int * /*conf_state*/, + gss_buffer_t output_message_buffer ); + +OM_uint32 +_gss_spnego_select_mech ( + OM_uint32 */*minor_status*/, + MechType */*mechType*/, + gss_OID */*mech_p*/); + +OM_uint32 +_gss_spnego_set_sec_context_option ( + OM_uint32 * /*minor_status*/, + gss_ctx_id_t * /*context_handle*/, + const gss_OID /*desired_object*/, + const gss_buffer_t /*value*/); + +OM_uint32 +_gss_spnego_sign ( + OM_uint32 * /*minor_status*/, + gss_ctx_id_t /*context_handle*/, + int /*qop_req*/, + gss_buffer_t /*message_buffer*/, + gss_buffer_t message_token ); + +OM_uint32 +_gss_spnego_unseal ( + OM_uint32 * /*minor_status*/, + gss_ctx_id_t /*context_handle*/, + gss_buffer_t /*input_message_buffer*/, + gss_buffer_t /*output_message_buffer*/, + int * /*conf_state*/, + int * qop_state ); + +OM_uint32 +_gss_spnego_unwrap ( + OM_uint32 * /*minor_status*/, + const gss_ctx_id_t /*context_handle*/, + const gss_buffer_t /*input_message_buffer*/, + gss_buffer_t /*output_message_buffer*/, + int * /*conf_state*/, + gss_qop_t * qop_state ); + +OM_uint32 +_gss_spnego_verify ( + OM_uint32 * /*minor_status*/, + gss_ctx_id_t /*context_handle*/, + gss_buffer_t /*message_buffer*/, + gss_buffer_t /*token_buffer*/, + int * qop_state ); + +OM_uint32 +_gss_spnego_verify_mic ( + OM_uint32 * /*minor_status*/, + const gss_ctx_id_t /*context_handle*/, + const gss_buffer_t /*message_buffer*/, + const gss_buffer_t /*token_buffer*/, + gss_qop_t * qop_state ); + +OM_uint32 +_gss_spnego_wrap ( + OM_uint32 * /*minor_status*/, + const gss_ctx_id_t /*context_handle*/, + int /*conf_req_flag*/, + gss_qop_t /*qop_req*/, + const gss_buffer_t /*input_message_buffer*/, + int * /*conf_state*/, + gss_buffer_t output_message_buffer ); + +OM_uint32 +_gss_spnego_wrap_size_limit ( + OM_uint32 * /*minor_status*/, + const gss_ctx_id_t /*context_handle*/, + int /*conf_req_flag*/, + gss_qop_t /*qop_req*/, + OM_uint32 /*req_output_size*/, + OM_uint32 * max_input_size ); + +#endif /* __spnego_private_h__ */ diff --git a/source4/heimdal/lib/gssapi/spnego/spnego.asn1 b/source4/heimdal/lib/gssapi/spnego/spnego.asn1 new file mode 100644 index 0000000000..187ce0a0a6 --- /dev/null +++ b/source4/heimdal/lib/gssapi/spnego/spnego.asn1 @@ -0,0 +1,51 @@ +-- $Id: spnego.asn1,v 1.1.1.1 2006/06/28 08:34:45 lha Exp $ + +SPNEGO DEFINITIONS ::= +BEGIN + +MechType::= OBJECT IDENTIFIER + +MechTypeList ::= SEQUENCE OF MechType + +ContextFlags ::= BIT STRING { + delegFlag (0), + mutualFlag (1), + replayFlag (2), + sequenceFlag (3), + anonFlag (4), + confFlag (5), + integFlag (6) +} + +NegHints ::= SEQUENCE { + hintName [0] GeneralString OPTIONAL, + hintAddress [1] OCTET STRING OPTIONAL +} + +NegTokenInit ::= SEQUENCE { + mechTypes [0] MechTypeList, + reqFlags [1] ContextFlags OPTIONAL, + mechToken [2] OCTET STRING OPTIONAL, + negHints [3] NegHints OPTIONAL, + mechListMIC [4] OCTET STRING OPTIONAL + } + +-- NB: negResult is not OPTIONAL in the new SPNEGO spec but +-- Windows clients do not always send it +NegTokenResp ::= SEQUENCE { + negResult [0] ENUMERATED { + accept_completed (0), + accept_incomplete (1), + reject (2), + request-mic (3) } OPTIONAL, + supportedMech [1] MechType OPTIONAL, + responseToken [2] OCTET STRING OPTIONAL, + mechListMIC [3] OCTET STRING OPTIONAL +} + +NegotiationToken ::= CHOICE { + negTokenInit[0] NegTokenInit, + negTokenResp[1] NegTokenResp +} + +END diff --git a/source4/heimdal/lib/gssapi/spnego/spnego_locl.h b/source4/heimdal/lib/gssapi/spnego/spnego_locl.h new file mode 100644 index 0000000000..571bce5569 --- /dev/null +++ b/source4/heimdal/lib/gssapi/spnego/spnego_locl.h @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2004, PADL Software Pty Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of PADL Software nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY PADL SOFTWARE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PADL SOFTWARE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* $Id: spnego_locl.h,v 1.11 2006/10/12 06:28:06 lha Exp $ */ + +#ifndef SPNEGO_LOCL_H +#define SPNEGO_LOCL_H + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#ifdef HAVE_SYS_TYPES_H +#include <sys/types.h> +#endif +#ifdef HAVE_SYS_PARAM_H +#include <sys/param.h> +#endif + +#ifdef HAVE_PTHREAD_H +#include <pthread.h> +#endif + +#include <gssapi/gssapi_spnego.h> +#include <gssapi.h> +#include <assert.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <ctype.h> +#ifdef HAVE_NETDB_H +#include <netdb.h> +#endif + +#include <heim_threads.h> +#include <asn1_err.h> + +#include <gssapi_mech.h> + +#include "spnego_asn1.h" +#include <der.h> + +#define ALLOC(X, N) (X) = calloc((N), sizeof(*(X))) + +typedef struct { + gss_cred_id_t negotiated_cred_id; +} *gssspnego_cred; + +typedef struct { + MechTypeList initiator_mech_types; + gss_OID preferred_mech_type; + gss_OID negotiated_mech_type; + gss_ctx_id_t negotiated_ctx_id; + OM_uint32 mech_flags; + OM_uint32 mech_time_rec; + gss_name_t mech_src_name; + gss_cred_id_t delegated_cred_id; + int open : 1; + int local : 1; + int require_mic : 1; + int verified_mic : 1; + HEIMDAL_MUTEX ctx_id_mutex; +} *gssspnego_ctx; + +#include <spnego/spnego-private.h> + +#endif /* SPNEGO_LOCL_H */ |