gss: port NegoEx implementation from MIT
An implementation of draft-zhu-negoex-04 for MIT Kerberos was developed in 2011. This has been recently integrated, with many fixes from Greg Hudson. This commit ports it to Heimdal. The implementation has been interoperability tested with MIT Kerberos and Windows, using the GSS EAP mechanism developed as part of the Moonshot project. The SPNEGO code was also updated to import the state machine from Apple which improves mechListMIC processing and avoids discarding initial context tokens generated during mechanism probing, that can be used for optimistic tokens. Finally, to aid in testing, the GSS-API mechanism glue configuration file can be changed using the environment variable GSS_MECH_CONFIG. This environment variable name, along with the format of the configuration file, is compatible with MIT (although it would be difficult for a single mechanism binary to support both implementations).
This commit is contained in:
@@ -63,37 +63,60 @@ send_reject (OM_uint32 *minor_status,
|
||||
}
|
||||
|
||||
static OM_uint32
|
||||
acceptor_approved(gss_const_cred_id_t input_cred,
|
||||
gss_name_t target_name,
|
||||
acceptor_approved(OM_uint32 *minor_status,
|
||||
void *userptr,
|
||||
gss_const_name_t target_name,
|
||||
gss_const_cred_id_t cred_handle,
|
||||
gss_OID mech)
|
||||
{
|
||||
gss_cred_id_t cred = GSS_C_NO_CREDENTIAL;
|
||||
gss_OID_set oidset;
|
||||
gss_OID_set oidset = GSS_C_NO_OID_SET;
|
||||
OM_uint32 junk, ret;
|
||||
|
||||
if (target_name == GSS_C_NO_NAME)
|
||||
return GSS_S_COMPLETE;
|
||||
|
||||
if (input_cred != GSS_C_NO_CREDENTIAL) {
|
||||
return gss_inquire_cred_by_mech(&junk, input_cred, mech,
|
||||
NULL, NULL, NULL, NULL);
|
||||
if (gss_oid_equal(mech, GSS_NEGOEX_MECHANISM)) {
|
||||
size_t i;
|
||||
|
||||
ret = _gss_spnego_indicate_mechs(minor_status, &oidset);
|
||||
if (ret != GSS_S_COMPLETE)
|
||||
return ret;
|
||||
|
||||
/* before committing to NegoEx, check we can negotiate a mech */
|
||||
for (i = 0; i < oidset->count; i++) {
|
||||
gss_OID inner_mech = &oidset->elements[i];
|
||||
|
||||
if (_gss_negoex_mech_p(inner_mech)) {
|
||||
ret = acceptor_approved(minor_status, userptr,
|
||||
target_name, cred_handle,
|
||||
inner_mech);
|
||||
if (ret == GSS_S_COMPLETE)
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (cred_handle != GSS_C_NO_CREDENTIAL) {
|
||||
ret = gss_inquire_cred_by_mech(minor_status, cred_handle, mech,
|
||||
NULL, NULL, NULL, NULL);
|
||||
} else {
|
||||
ret = gss_create_empty_oid_set(minor_status, &oidset);
|
||||
if (ret == GSS_S_COMPLETE)
|
||||
ret = gss_add_oid_set_member(minor_status, mech, &oidset);
|
||||
if (ret == GSS_S_COMPLETE)
|
||||
ret = gss_acquire_cred(minor_status, target_name,
|
||||
GSS_C_INDEFINITE, oidset,
|
||||
GSS_C_ACCEPT, &cred, NULL, NULL);
|
||||
}
|
||||
|
||||
gss_create_empty_oid_set(&junk, &oidset);
|
||||
gss_add_oid_set_member(&junk, mech, &oidset);
|
||||
|
||||
ret = gss_acquire_cred(&junk, target_name, GSS_C_INDEFINITE, oidset,
|
||||
GSS_C_ACCEPT, &cred, NULL, NULL);
|
||||
gss_release_oid_set(&junk, &oidset);
|
||||
if (ret != GSS_S_COMPLETE)
|
||||
return ret;
|
||||
gss_release_cred(&junk, &cred);
|
||||
|
||||
return GSS_S_COMPLETE;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static OM_uint32
|
||||
send_supported_mechs (OM_uint32 *minor_status,
|
||||
gssspnego_ctx ctx,
|
||||
gss_const_cred_id_t acceptor_cred,
|
||||
gss_buffer_t output_token)
|
||||
{
|
||||
@@ -109,8 +132,8 @@ send_supported_mechs (OM_uint32 *minor_status,
|
||||
nt.u.negTokenInit.mechToken = NULL;
|
||||
nt.u.negTokenInit.negHints = NULL;
|
||||
|
||||
ret = _gss_spnego_indicate_mechtypelist(minor_status, GSS_C_NO_NAME,
|
||||
acceptor_approved, 1, acceptor_cred,
|
||||
ret = _gss_spnego_indicate_mechtypelist(minor_status, NULL,
|
||||
acceptor_approved, ctx, 1, acceptor_cred,
|
||||
&nt.u.negTokenInit.mechTypes, NULL);
|
||||
if (ret != GSS_S_COMPLETE) {
|
||||
return ret;
|
||||
@@ -160,13 +183,15 @@ send_supported_mechs (OM_uint32 *minor_status,
|
||||
static OM_uint32
|
||||
send_accept (OM_uint32 *minor_status,
|
||||
gssspnego_ctx context_handle,
|
||||
int optimistic_mech_ok,
|
||||
gss_buffer_t mech_token,
|
||||
int initial_response,
|
||||
gss_const_OID selected_mech, /* valid on initial response only */
|
||||
gss_buffer_t mech_buf,
|
||||
gss_buffer_t output_token)
|
||||
{
|
||||
int initial_response = (selected_mech != GSS_C_NO_OID);
|
||||
NegotiationToken nt;
|
||||
OM_uint32 ret;
|
||||
OM_uint32 ret, minor;
|
||||
gss_buffer_desc mech_mic_buf;
|
||||
size_t size;
|
||||
|
||||
@@ -180,7 +205,7 @@ send_accept (OM_uint32 *minor_status,
|
||||
return GSS_S_FAILURE;
|
||||
}
|
||||
|
||||
if (context_handle->open) {
|
||||
if (context_handle->flags.open) {
|
||||
if (mech_token != GSS_C_NO_BUFFER
|
||||
&& mech_token->length != 0
|
||||
&& mech_buf != GSS_C_NO_BUFFER)
|
||||
@@ -188,7 +213,7 @@ send_accept (OM_uint32 *minor_status,
|
||||
else
|
||||
*(nt.u.negTokenResp.negResult) = accept_completed;
|
||||
} else {
|
||||
if (initial_response && context_handle->require_mic)
|
||||
if (initial_response && !optimistic_mech_ok)
|
||||
*(nt.u.negTokenResp.negResult) = request_mic;
|
||||
else
|
||||
*(nt.u.negTokenResp.negResult) = accept_incomplete;
|
||||
@@ -197,20 +222,22 @@ send_accept (OM_uint32 *minor_status,
|
||||
if (initial_response) {
|
||||
ALLOC(nt.u.negTokenResp.supportedMech, 1);
|
||||
if (nt.u.negTokenResp.supportedMech == NULL) {
|
||||
free_NegotiationToken(&nt);
|
||||
*minor_status = ENOMEM;
|
||||
return GSS_S_FAILURE;
|
||||
ret = GSS_S_FAILURE;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = der_get_oid(context_handle->preferred_mech_type->elements,
|
||||
context_handle->preferred_mech_type->length,
|
||||
ret = der_get_oid(selected_mech->elements,
|
||||
selected_mech->length,
|
||||
nt.u.negTokenResp.supportedMech,
|
||||
NULL);
|
||||
if (ret) {
|
||||
free_NegotiationToken(&nt);
|
||||
*minor_status = ENOMEM;
|
||||
return GSS_S_FAILURE;
|
||||
ret = GSS_S_FAILURE;
|
||||
goto out;
|
||||
}
|
||||
|
||||
_gss_spnego_log_mech("acceptor sending selected mech", selected_mech);
|
||||
} else {
|
||||
nt.u.negTokenResp.supportedMech = NULL;
|
||||
}
|
||||
@@ -218,9 +245,9 @@ send_accept (OM_uint32 *minor_status,
|
||||
if (mech_token != GSS_C_NO_BUFFER && mech_token->length != 0) {
|
||||
ALLOC(nt.u.negTokenResp.responseToken, 1);
|
||||
if (nt.u.negTokenResp.responseToken == NULL) {
|
||||
free_NegotiationToken(&nt);
|
||||
*minor_status = ENOMEM;
|
||||
return GSS_S_FAILURE;
|
||||
ret = GSS_S_FAILURE;
|
||||
goto out;
|
||||
}
|
||||
nt.u.negTokenResp.responseToken->length = mech_token->length;
|
||||
nt.u.negTokenResp.responseToken->data = mech_token->value;
|
||||
@@ -236,25 +263,22 @@ send_accept (OM_uint32 *minor_status,
|
||||
0,
|
||||
mech_buf,
|
||||
&mech_mic_buf);
|
||||
if (ret == GSS_S_COMPLETE &&
|
||||
gss_oid_equal(context_handle->negotiated_mech_type,
|
||||
GSS_NTLM_MECHANISM))
|
||||
_gss_spnego_ntlm_reset_crypto(minor_status, context_handle, 0);
|
||||
if (ret == GSS_S_COMPLETE) {
|
||||
_gss_spnego_ntlm_reset_crypto(&minor, context_handle, FALSE);
|
||||
|
||||
ALLOC(nt.u.negTokenResp.mechListMIC, 1);
|
||||
if (nt.u.negTokenResp.mechListMIC == NULL) {
|
||||
gss_release_buffer(minor_status, &mech_mic_buf);
|
||||
free_NegotiationToken(&nt);
|
||||
*minor_status = ENOMEM;
|
||||
return GSS_S_FAILURE;
|
||||
ret = GSS_S_FAILURE;
|
||||
goto out;
|
||||
}
|
||||
nt.u.negTokenResp.mechListMIC->length = mech_mic_buf.length;
|
||||
nt.u.negTokenResp.mechListMIC->data = mech_mic_buf.value;
|
||||
} else if (ret == GSS_S_UNAVAILABLE) {
|
||||
nt.u.negTokenResp.mechListMIC = NULL;
|
||||
} else {
|
||||
free_NegotiationToken(&nt);
|
||||
return ret;
|
||||
goto out;
|
||||
}
|
||||
|
||||
} else
|
||||
@@ -264,9 +288,9 @@ send_accept (OM_uint32 *minor_status,
|
||||
output_token->value, output_token->length,
|
||||
&nt, &size, ret);
|
||||
if (ret) {
|
||||
free_NegotiationToken(&nt);
|
||||
*minor_status = ret;
|
||||
return GSS_S_FAILURE;
|
||||
*minor_status = ENOMEM;
|
||||
ret = GSS_S_FAILURE;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -279,136 +303,165 @@ send_accept (OM_uint32 *minor_status,
|
||||
ret = GSS_S_COMPLETE;
|
||||
else
|
||||
ret = GSS_S_CONTINUE_NEEDED;
|
||||
|
||||
out:
|
||||
free_NegotiationToken(&nt);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the default acceptor identity based on the local hostname
|
||||
* or the GSSAPI_SPNEGO_NAME environment variable.
|
||||
*/
|
||||
|
||||
static OM_uint32
|
||||
verify_mechlist_mic
|
||||
(OM_uint32 *minor_status,
|
||||
gssspnego_ctx context_handle,
|
||||
gss_buffer_t mech_buf,
|
||||
heim_octet_string *mechListMIC
|
||||
)
|
||||
default_acceptor_name(OM_uint32 *minor_status,
|
||||
gss_name_t *namep)
|
||||
{
|
||||
OM_uint32 ret;
|
||||
gss_buffer_desc mic_buf;
|
||||
OM_uint32 major_status;
|
||||
gss_buffer_desc namebuf;
|
||||
char *str = NULL, *host, hostname[MAXHOSTNAMELEN];
|
||||
|
||||
if (context_handle->verified_mic) {
|
||||
/* This doesn't make sense, we've already verified it? */
|
||||
*minor_status = 0;
|
||||
return GSS_S_DUPLICATE_TOKEN;
|
||||
*namep = GSS_C_NO_NAME;
|
||||
|
||||
host = secure_getenv("GSSAPI_SPNEGO_NAME");
|
||||
if (host == NULL) {
|
||||
int rv;
|
||||
|
||||
if (gethostname(hostname, sizeof(hostname)) != 0) {
|
||||
*minor_status = errno;
|
||||
return GSS_S_FAILURE;
|
||||
}
|
||||
|
||||
rv = asprintf(&str, "host@%s", hostname);
|
||||
if (rv < 0 || str == NULL) {
|
||||
*minor_status = ENOMEM;
|
||||
return GSS_S_FAILURE;
|
||||
}
|
||||
host = str;
|
||||
}
|
||||
|
||||
if (mechListMIC == NULL) {
|
||||
*minor_status = 0;
|
||||
return GSS_S_DEFECTIVE_TOKEN;
|
||||
}
|
||||
namebuf.length = strlen(host);
|
||||
namebuf.value = host;
|
||||
|
||||
mic_buf.length = mechListMIC->length;
|
||||
mic_buf.value = mechListMIC->data;
|
||||
major_status = gss_import_name(minor_status, &namebuf,
|
||||
GSS_C_NT_HOSTBASED_SERVICE, namep);
|
||||
|
||||
ret = gss_verify_mic(minor_status,
|
||||
context_handle->negotiated_ctx_id,
|
||||
mech_buf,
|
||||
&mic_buf,
|
||||
NULL);
|
||||
free(str);
|
||||
|
||||
if (ret != GSS_S_COMPLETE)
|
||||
ret = GSS_S_DEFECTIVE_TOKEN;
|
||||
|
||||
return ret;
|
||||
return major_status;
|
||||
}
|
||||
|
||||
/*
|
||||
* Determine whether the mech in mechType can be negotiated. If the
|
||||
* mech is NegoEx, make NegoEx mechanisms available for negotiation.
|
||||
*/
|
||||
|
||||
static OM_uint32
|
||||
select_mech(OM_uint32 *minor_status, MechType *mechType, int verify_p,
|
||||
gss_OID *mech_p)
|
||||
select_mech(OM_uint32 *minor_status,
|
||||
gssspnego_ctx ctx,
|
||||
gss_const_cred_id_t cred,
|
||||
gss_const_OID_set supported_mechs,
|
||||
MechType *mechType,
|
||||
int verify_p, /* set on non-optimistic tokens */
|
||||
gss_const_OID *advertised_mech_p)
|
||||
{
|
||||
char mechbuf[64];
|
||||
size_t mech_len;
|
||||
gss_OID_desc oid;
|
||||
gss_OID oidp;
|
||||
gss_OID_set mechs;
|
||||
size_t i;
|
||||
gss_OID selected_mech = GSS_C_NO_OID;
|
||||
OM_uint32 ret, junk;
|
||||
int negoex_proposed = FALSE, negoex_selected = FALSE;
|
||||
int includeMSCompatOID = FALSE;
|
||||
size_t i;
|
||||
|
||||
*minor_status = 0;
|
||||
*advertised_mech_p = GSS_C_NO_OID; /* deals with broken MS OID */
|
||||
|
||||
ctx->selected_mech_type = GSS_C_NO_OID;
|
||||
|
||||
ret = der_put_oid ((unsigned char *)mechbuf + sizeof(mechbuf) - 1,
|
||||
sizeof(mechbuf),
|
||||
mechType,
|
||||
&mech_len);
|
||||
if (ret) {
|
||||
if (ret)
|
||||
return GSS_S_DEFECTIVE_TOKEN;
|
||||
}
|
||||
|
||||
oid.length = mech_len;
|
||||
oid.length = (OM_uint32)mech_len;
|
||||
oid.elements = mechbuf + sizeof(mechbuf) - mech_len;
|
||||
|
||||
if (gss_oid_equal(&oid, GSS_SPNEGO_MECHANISM)) {
|
||||
return GSS_S_BAD_MECH;
|
||||
if (gss_oid_equal(&oid, GSS_NEGOEX_MECHANISM))
|
||||
negoex_proposed = TRUE;
|
||||
else if (gss_oid_equal(&oid, &_gss_spnego_mskrb_mechanism_oid_desc))
|
||||
includeMSCompatOID = TRUE;
|
||||
|
||||
for (i = 0; i < supported_mechs->count; i++) {
|
||||
gss_OID iter = &supported_mechs->elements[i];
|
||||
auth_scheme scheme;
|
||||
int is_negoex_mech = /* mechanism is negotiable under NegoEx */
|
||||
gssspi_query_mechanism_info(&junk, iter, scheme) == GSS_S_COMPLETE;
|
||||
|
||||
if (is_negoex_mech && negoex_proposed) {
|
||||
ret = _gss_negoex_add_auth_mech(minor_status, ctx, iter, scheme);
|
||||
if (ret != GSS_S_COMPLETE)
|
||||
break;
|
||||
|
||||
negoex_selected = TRUE;
|
||||
}
|
||||
|
||||
if (gss_oid_equal(includeMSCompatOID ? GSS_KRB5_MECHANISM : &oid, iter)) {
|
||||
ret = _gss_intern_oid(minor_status, iter, &selected_mech);
|
||||
if (ret != GSS_S_COMPLETE)
|
||||
return ret;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
*minor_status = 0;
|
||||
/* always prefer NegoEx if a mechanism supported both */
|
||||
if (negoex_selected)
|
||||
selected_mech = GSS_NEGOEX_MECHANISM;
|
||||
if (selected_mech == GSS_C_NO_OID)
|
||||
ret = GSS_S_BAD_MECH;
|
||||
if (ret != GSS_S_COMPLETE)
|
||||
return ret;
|
||||
|
||||
/* Translate broken MS Kebreros OID */
|
||||
if (gss_oid_equal(&oid, &_gss_spnego_mskrb_mechanism_oid_desc))
|
||||
oidp = &_gss_spnego_krb5_mechanism_oid_desc;
|
||||
else
|
||||
oidp = &oid;
|
||||
|
||||
|
||||
ret = gss_indicate_mechs(&junk, &mechs);
|
||||
if (ret)
|
||||
return (ret);
|
||||
|
||||
for (i = 0; i < mechs->count; i++)
|
||||
if (gss_oid_equal(&mechs->elements[i], oidp))
|
||||
break;
|
||||
|
||||
if (i == mechs->count) {
|
||||
gss_release_oid_set(&junk, &mechs);
|
||||
return GSS_S_BAD_MECH;
|
||||
}
|
||||
gss_release_oid_set(&junk, &mechs);
|
||||
|
||||
ret = gss_duplicate_oid(minor_status,
|
||||
&oid, /* possibly this should be oidp */
|
||||
mech_p);
|
||||
heim_assert(!gss_oid_equal(selected_mech, GSS_SPNEGO_MECHANISM),
|
||||
"SPNEGO should not be able to negotiate itself");
|
||||
|
||||
if (verify_p) {
|
||||
gss_name_t name = GSS_C_NO_NAME;
|
||||
gss_buffer_desc namebuf;
|
||||
char *str = NULL, *host, hostname[MAXHOSTNAMELEN];
|
||||
|
||||
host = secure_getenv("GSSAPI_SPNEGO_NAME");
|
||||
if (host == NULL) {
|
||||
int rv;
|
||||
if (gethostname(hostname, sizeof(hostname)) != 0) {
|
||||
*minor_status = errno;
|
||||
return GSS_S_FAILURE;
|
||||
}
|
||||
rv = asprintf(&str, "host@%s", hostname);
|
||||
if (rv < 0 || str == NULL) {
|
||||
*minor_status = ENOMEM;
|
||||
return GSS_S_FAILURE;
|
||||
}
|
||||
host = str;
|
||||
/*
|
||||
* If we do not have a credential, acquire a default name as a hint
|
||||
* to acceptor_approved() so it can attempt to acquire a default
|
||||
* credential.
|
||||
*/
|
||||
if (cred == GSS_C_NO_CREDENTIAL) {
|
||||
ret = default_acceptor_name(minor_status, &name);
|
||||
if (ret != GSS_S_COMPLETE)
|
||||
return ret;
|
||||
}
|
||||
|
||||
namebuf.length = strlen(host);
|
||||
namebuf.value = host;
|
||||
ret = acceptor_approved(minor_status, ctx, name, cred, selected_mech);
|
||||
|
||||
ret = gss_import_name(minor_status, &namebuf,
|
||||
GSS_C_NT_HOSTBASED_SERVICE, &name);
|
||||
if (str)
|
||||
free(str);
|
||||
if (ret != GSS_S_COMPLETE)
|
||||
return ret;
|
||||
|
||||
ret = acceptor_approved(GSS_C_NO_CREDENTIAL, name, *mech_p);
|
||||
gss_release_name(&junk, &name);
|
||||
}
|
||||
|
||||
/* Stash optimistic mech for use by _gss_spnego_require_mechlist_mic() */
|
||||
if (ret == GSS_S_COMPLETE && !verify_p)
|
||||
ret = gss_duplicate_oid(minor_status, &oid, &ctx->preferred_mech_type);
|
||||
|
||||
if (ret == GSS_S_COMPLETE) {
|
||||
*minor_status = 0;
|
||||
|
||||
*advertised_mech_p = ctx->selected_mech_type = selected_mech;
|
||||
|
||||
/* if the initiator used the broken MS OID, send that instead */
|
||||
if (includeMSCompatOID && gss_oid_equal(selected_mech, GSS_KRB5_MECHANISM))
|
||||
*advertised_mech_p = &_gss_spnego_mskrb_mechanism_oid_desc;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -417,25 +470,19 @@ static OM_uint32
|
||||
acceptor_complete(OM_uint32 * minor_status,
|
||||
gssspnego_ctx ctx,
|
||||
int *get_mic,
|
||||
gss_buffer_t mech_buf,
|
||||
gss_buffer_t mech_input_token,
|
||||
gss_buffer_t mech_output_token,
|
||||
heim_octet_string *mic,
|
||||
gss_buffer_t output_token)
|
||||
{
|
||||
gss_buffer_desc buf = GSS_C_EMPTY_BUFFER;
|
||||
OM_uint32 ret;
|
||||
int require_mic, verify_mic;
|
||||
int verify_mic;
|
||||
|
||||
ret = _gss_spnego_require_mechlist_mic(minor_status, ctx, &require_mic);
|
||||
if (ret)
|
||||
return ret;
|
||||
ctx->flags.require_mic = 1;
|
||||
ctx->flags.safe_omit = _gss_spnego_safe_omit_mechlist_mic(ctx);
|
||||
|
||||
ctx->require_mic = require_mic;
|
||||
|
||||
if (mic != NULL)
|
||||
require_mic = 1;
|
||||
|
||||
if (ctx->open && require_mic) {
|
||||
if (ctx->flags.open) {
|
||||
if (mech_input_token == GSS_C_NO_BUFFER) { /* Even/One */
|
||||
verify_mic = 1;
|
||||
*get_mic = 0;
|
||||
@@ -447,29 +494,20 @@ acceptor_complete(OM_uint32 * minor_status,
|
||||
*get_mic = 1;
|
||||
}
|
||||
|
||||
if (verify_mic || *get_mic) {
|
||||
int eret;
|
||||
size_t buf_len = 0;
|
||||
|
||||
ASN1_MALLOC_ENCODE(MechTypeList,
|
||||
mech_buf->value, mech_buf->length,
|
||||
&ctx->initiator_mech_types, &buf_len, eret);
|
||||
if (eret) {
|
||||
*minor_status = eret;
|
||||
return GSS_S_FAILURE;
|
||||
}
|
||||
heim_assert(mech_buf->length == buf_len, "Internal ASN.1 error");
|
||||
UNREACHABLE(return GSS_S_FAILURE);
|
||||
}
|
||||
|
||||
if (verify_mic) {
|
||||
ret = verify_mechlist_mic(minor_status, ctx, mech_buf, mic);
|
||||
if (verify_mic && mic == NULL && ctx->flags.safe_omit) {
|
||||
/*
|
||||
* Peer is old and didn't send a mic while we expected
|
||||
* one, but since it safe to omit, let do that
|
||||
*/
|
||||
} else if (verify_mic) {
|
||||
ret = _gss_spnego_verify_mechtypes_mic(minor_status, ctx, mic);
|
||||
if (ret) {
|
||||
if (*get_mic)
|
||||
send_reject (minor_status, output_token);
|
||||
send_reject(minor_status, output_token);
|
||||
if (buf.value)
|
||||
free(buf.value);
|
||||
return ret;
|
||||
}
|
||||
ctx->verified_mic = 1;
|
||||
}
|
||||
} else
|
||||
*get_mic = 0;
|
||||
@@ -477,6 +515,57 @@ acceptor_complete(OM_uint32 * minor_status,
|
||||
return GSS_S_COMPLETE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Call gss_accept_sec_context() via mechglue or NegoEx, depending on
|
||||
* whether mech_oid is NegoEx.
|
||||
*/
|
||||
|
||||
static OM_uint32
|
||||
mech_accept(OM_uint32 *minor_status,
|
||||
gssspnego_ctx ctx,
|
||||
gss_const_cred_id_t acceptor_cred_handle,
|
||||
gss_const_buffer_t input_token_buffer,
|
||||
const gss_channel_bindings_t input_chan_bindings,
|
||||
gss_buffer_t output_token,
|
||||
gss_cred_id_t *delegated_cred_handle)
|
||||
{
|
||||
OM_uint32 ret, junk;
|
||||
|
||||
heim_assert(ctx->selected_mech_type != GSS_C_NO_OID,
|
||||
"mech_accept called with no selected mech");
|
||||
|
||||
if (gss_oid_equal(ctx->selected_mech_type, GSS_NEGOEX_MECHANISM)) {
|
||||
ret = _gss_negoex_accept(minor_status,
|
||||
ctx,
|
||||
(gss_cred_id_t)acceptor_cred_handle,
|
||||
input_token_buffer,
|
||||
input_chan_bindings,
|
||||
output_token,
|
||||
delegated_cred_handle);
|
||||
} else {
|
||||
if (ctx->mech_src_name != GSS_C_NO_NAME)
|
||||
gss_release_name(&junk, &ctx->mech_src_name);
|
||||
|
||||
ret = gss_accept_sec_context(minor_status,
|
||||
&ctx->negotiated_ctx_id,
|
||||
acceptor_cred_handle,
|
||||
(gss_buffer_t)input_token_buffer,
|
||||
input_chan_bindings,
|
||||
&ctx->mech_src_name,
|
||||
&ctx->negotiated_mech_type,
|
||||
output_token,
|
||||
&ctx->mech_flags,
|
||||
&ctx->mech_time_rec,
|
||||
delegated_cred_handle);
|
||||
if (GSS_ERROR(ret))
|
||||
gss_mg_collect_error(ctx->negotiated_mech_type, ret, *minor_status);
|
||||
else if (ctx->negotiated_mech_type != GSS_C_NO_OID &&
|
||||
!gss_oid_equal(ctx->negotiated_mech_type, ctx->selected_mech_type))
|
||||
_gss_mg_log(1, "spnego client didn't send the mech they said they would");
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static OM_uint32 GSSAPI_CALLCONV
|
||||
acceptor_start
|
||||
@@ -495,23 +584,24 @@ acceptor_start
|
||||
{
|
||||
OM_uint32 ret, junk;
|
||||
NegotiationToken nt;
|
||||
size_t nt_len;
|
||||
gss_OID_set supported_mechs = GSS_C_NO_OID_SET;
|
||||
size_t size;
|
||||
NegTokenInit *ni;
|
||||
gss_buffer_desc data;
|
||||
gss_buffer_t mech_input_token = GSS_C_NO_BUFFER;
|
||||
gss_buffer_desc mech_output_token;
|
||||
gss_buffer_desc mech_buf;
|
||||
gss_OID preferred_mech_type = GSS_C_NO_OID;
|
||||
gssspnego_ctx ctx;
|
||||
int get_mic = 0;
|
||||
int first_ok = 0;
|
||||
gss_const_OID advertised_mech = GSS_C_NO_OID;
|
||||
|
||||
memset(&nt, 0, sizeof(nt));
|
||||
|
||||
mech_output_token.value = NULL;
|
||||
mech_output_token.length = 0;
|
||||
mech_buf.value = NULL;
|
||||
|
||||
if (input_token_buffer->length == 0)
|
||||
return send_supported_mechs (minor_status,
|
||||
return send_supported_mechs (minor_status, NULL,
|
||||
acceptor_cred_handle, output_token);
|
||||
|
||||
ret = _gss_spnego_alloc_sec_context(minor_status, context_handle);
|
||||
@@ -520,6 +610,8 @@ acceptor_start
|
||||
|
||||
ctx = (gssspnego_ctx)*context_handle;
|
||||
|
||||
HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex);
|
||||
|
||||
/*
|
||||
* The GSS-API encapsulation is only present on the initial
|
||||
* context token (negTokenInit).
|
||||
@@ -528,36 +620,58 @@ acceptor_start
|
||||
GSS_SPNEGO_MECHANISM,
|
||||
&data);
|
||||
if (ret)
|
||||
return ret;
|
||||
goto out;
|
||||
|
||||
ret = decode_NegotiationToken(data.value, data.length, &nt, &nt_len);
|
||||
ret = decode_NegotiationToken(data.value, data.length, &nt, &size);
|
||||
gss_release_buffer(minor_status, &data);
|
||||
if (ret) {
|
||||
*minor_status = ret;
|
||||
return GSS_S_DEFECTIVE_TOKEN;
|
||||
ret = GSS_S_DEFECTIVE_TOKEN;
|
||||
goto out;
|
||||
}
|
||||
if (nt.element != choice_NegotiationToken_negTokenInit) {
|
||||
*minor_status = 0;
|
||||
return GSS_S_DEFECTIVE_TOKEN;
|
||||
ret = GSS_S_DEFECTIVE_TOKEN;
|
||||
goto out;
|
||||
}
|
||||
ni = &nt.u.negTokenInit;
|
||||
|
||||
if (ni->mechTypes.len < 1) {
|
||||
free_NegotiationToken(&nt);
|
||||
*minor_status = 0;
|
||||
return GSS_S_DEFECTIVE_TOKEN;
|
||||
ret = GSS_S_DEFECTIVE_TOKEN;
|
||||
goto out;
|
||||
}
|
||||
|
||||
HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex);
|
||||
_gss_spnego_log_mechTypes(&ni->mechTypes);
|
||||
|
||||
ret = copy_MechTypeList(&ni->mechTypes, &ctx->initiator_mech_types);
|
||||
if (ret) {
|
||||
HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
|
||||
free_NegotiationToken(&nt);
|
||||
*minor_status = ret;
|
||||
return GSS_S_FAILURE;
|
||||
{
|
||||
MechTypeList mt;
|
||||
int kret;
|
||||
|
||||
mt.len = ni->mechTypes.len;
|
||||
mt.val = ni->mechTypes.val;
|
||||
|
||||
ASN1_MALLOC_ENCODE(MechTypeList,
|
||||
ctx->NegTokenInit_mech_types.value,
|
||||
ctx->NegTokenInit_mech_types.length,
|
||||
&mt, &size, kret);
|
||||
if (kret) {
|
||||
*minor_status = kret;
|
||||
ret = GSS_S_FAILURE;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
if (acceptor_cred_handle != GSS_C_NO_CREDENTIAL)
|
||||
ret = _gss_spnego_inquire_cred_mechs(minor_status,
|
||||
acceptor_cred_handle,
|
||||
&supported_mechs);
|
||||
else
|
||||
ret = _gss_spnego_indicate_mechs(minor_status, &supported_mechs);
|
||||
if (ret != GSS_S_COMPLETE)
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* First we try the opportunistic token if we have support for it,
|
||||
* don't try to verify we have credential for the token,
|
||||
@@ -566,41 +680,39 @@ acceptor_start
|
||||
*/
|
||||
|
||||
ret = select_mech(minor_status,
|
||||
ctx,
|
||||
acceptor_cred_handle,
|
||||
supported_mechs,
|
||||
&ni->mechTypes.val[0],
|
||||
0,
|
||||
&preferred_mech_type);
|
||||
0, /* optimistic token */
|
||||
&advertised_mech);
|
||||
|
||||
if (ret == 0 && ni->mechToken != NULL) {
|
||||
if (ret == GSS_S_COMPLETE && ni->mechToken != NULL) {
|
||||
gss_buffer_desc ibuf;
|
||||
|
||||
ibuf.length = ni->mechToken->length;
|
||||
ibuf.value = ni->mechToken->data;
|
||||
mech_input_token = &ibuf;
|
||||
|
||||
if (ctx->mech_src_name != GSS_C_NO_NAME)
|
||||
gss_release_name(&junk, &ctx->mech_src_name);
|
||||
|
||||
ret = gss_accept_sec_context(minor_status,
|
||||
&ctx->negotiated_ctx_id,
|
||||
acceptor_cred_handle,
|
||||
mech_input_token,
|
||||
input_chan_bindings,
|
||||
&ctx->mech_src_name,
|
||||
&ctx->negotiated_mech_type,
|
||||
&mech_output_token,
|
||||
&ctx->mech_flags,
|
||||
&ctx->mech_time_rec,
|
||||
delegated_cred_handle);
|
||||
_gss_spnego_log_mech("acceptor selected opportunistic mech", ctx->selected_mech_type);
|
||||
|
||||
ret = mech_accept(&junk,
|
||||
ctx,
|
||||
acceptor_cred_handle,
|
||||
mech_input_token,
|
||||
input_chan_bindings,
|
||||
&mech_output_token,
|
||||
delegated_cred_handle);
|
||||
if (ret == GSS_S_COMPLETE || ret == GSS_S_CONTINUE_NEEDED) {
|
||||
ctx->preferred_mech_type = preferred_mech_type;
|
||||
if (ret == GSS_S_COMPLETE)
|
||||
ctx->open = 1;
|
||||
first_ok = 1;
|
||||
} else {
|
||||
ctx->selected_mech_type = GSS_C_NO_OID;
|
||||
}
|
||||
|
||||
if (ret == GSS_S_COMPLETE) {
|
||||
ret = acceptor_complete(minor_status,
|
||||
ctx,
|
||||
&get_mic,
|
||||
&mech_buf,
|
||||
mech_input_token,
|
||||
&mech_output_token,
|
||||
ni->mechListMIC,
|
||||
@@ -608,10 +720,14 @@ acceptor_start
|
||||
if (ret != GSS_S_COMPLETE)
|
||||
goto out;
|
||||
|
||||
first_ok = 1;
|
||||
} else {
|
||||
gss_mg_collect_error(preferred_mech_type, ret, *minor_status);
|
||||
ctx->flags.open = 1;
|
||||
}
|
||||
} else {
|
||||
*minor_status = 0;
|
||||
HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
|
||||
return gss_mg_set_error_string(GSS_C_NO_OID, GSS_S_NO_CONTEXT,
|
||||
*minor_status,
|
||||
"SPNEGO acceptor didn't find a prefered mechanism");
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -621,24 +737,25 @@ acceptor_start
|
||||
if (!first_ok && ni->mechToken != NULL) {
|
||||
size_t j;
|
||||
|
||||
preferred_mech_type = GSS_C_NO_OID;
|
||||
|
||||
/* Call glue layer to find first mech we support */
|
||||
for (j = 1; j < ni->mechTypes.len; ++j) {
|
||||
ret = select_mech(minor_status,
|
||||
ret = select_mech(&junk,
|
||||
ctx,
|
||||
acceptor_cred_handle,
|
||||
supported_mechs,
|
||||
&ni->mechTypes.val[j],
|
||||
1,
|
||||
&preferred_mech_type);
|
||||
if (ret == 0)
|
||||
1, /* not optimistic token */
|
||||
&advertised_mech);
|
||||
if (ret == GSS_S_COMPLETE) {
|
||||
_gss_spnego_log_mech("acceptor selected non-opportunistic mech", ctx->selected_mech_type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (preferred_mech_type == GSS_C_NO_OID) {
|
||||
HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
|
||||
free_NegotiationToken(&nt);
|
||||
return ret;
|
||||
if (ctx->selected_mech_type == GSS_C_NO_OID) {
|
||||
heim_assert(ret != GSS_S_COMPLETE, "no oid and no error code?");
|
||||
*minor_status = junk;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ctx->preferred_mech_type = preferred_mech_type;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -647,20 +764,18 @@ acceptor_start
|
||||
|
||||
ret = send_accept (minor_status,
|
||||
ctx,
|
||||
first_ok,
|
||||
&mech_output_token,
|
||||
1,
|
||||
get_mic ? &mech_buf : NULL,
|
||||
advertised_mech,
|
||||
get_mic ? &ctx->NegTokenInit_mech_types : NULL,
|
||||
output_token);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
out:
|
||||
gss_release_oid_set(&junk, &supported_mechs);
|
||||
if (mech_output_token.value != NULL)
|
||||
gss_release_buffer(&junk, &mech_output_token);
|
||||
if (mech_buf.value != NULL) {
|
||||
free(mech_buf.value);
|
||||
mech_buf.value = NULL;
|
||||
}
|
||||
free_NegotiationToken(&nt);
|
||||
|
||||
|
||||
@@ -705,18 +820,15 @@ acceptor_continue
|
||||
gss_cred_id_t *delegated_cred_handle
|
||||
)
|
||||
{
|
||||
OM_uint32 ret, ret2, minor;
|
||||
OM_uint32 ret, ret2, minor, junk;
|
||||
NegotiationToken nt;
|
||||
size_t nt_len;
|
||||
NegTokenResp *na;
|
||||
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;
|
||||
gssspnego_ctx ctx;
|
||||
|
||||
mech_buf.value = NULL;
|
||||
|
||||
ctx = (gssspnego_ctx)*context_handle;
|
||||
|
||||
/*
|
||||
@@ -745,9 +857,8 @@ acceptor_continue
|
||||
|
||||
{
|
||||
gss_buffer_desc ibuf, obuf;
|
||||
int require_mic, get_mic = 0;
|
||||
int get_mic = 0;
|
||||
int require_response;
|
||||
heim_octet_string *mic;
|
||||
|
||||
if (na->responseToken != NULL) {
|
||||
ibuf.length = na->responseToken->length;
|
||||
@@ -760,53 +871,31 @@ acceptor_continue
|
||||
|
||||
if (mech_input_token != GSS_C_NO_BUFFER) {
|
||||
|
||||
if (ctx->mech_src_name != GSS_C_NO_NAME)
|
||||
gss_release_name(&minor, &ctx->mech_src_name);
|
||||
|
||||
ret = gss_accept_sec_context(&minor,
|
||||
&ctx->negotiated_ctx_id,
|
||||
acceptor_cred_handle,
|
||||
mech_input_token,
|
||||
input_chan_bindings,
|
||||
&ctx->mech_src_name,
|
||||
&ctx->negotiated_mech_type,
|
||||
&obuf,
|
||||
&ctx->mech_flags,
|
||||
&ctx->mech_time_rec,
|
||||
delegated_cred_handle);
|
||||
|
||||
ret = mech_accept(minor_status,
|
||||
ctx,
|
||||
acceptor_cred_handle,
|
||||
mech_input_token,
|
||||
input_chan_bindings,
|
||||
&obuf,
|
||||
delegated_cred_handle);
|
||||
if (ret == GSS_S_COMPLETE || ret == GSS_S_CONTINUE_NEEDED) {
|
||||
mech_output_token = &obuf;
|
||||
}
|
||||
if (ret != GSS_S_COMPLETE && ret != GSS_S_CONTINUE_NEEDED) {
|
||||
free_NegotiationToken(&nt);
|
||||
gss_mg_collect_error(ctx->negotiated_mech_type, ret, minor);
|
||||
send_reject (minor_status, output_token);
|
||||
send_reject(&junk, output_token);
|
||||
HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
|
||||
return ret;
|
||||
}
|
||||
if (ret == GSS_S_COMPLETE)
|
||||
ctx->open = 1;
|
||||
ctx->flags.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 = na->mechListMIC;
|
||||
if (mic != NULL)
|
||||
require_mic = 1;
|
||||
|
||||
if (ret == GSS_S_COMPLETE)
|
||||
ret = acceptor_complete(minor_status,
|
||||
ctx,
|
||||
&get_mic,
|
||||
&mech_buf,
|
||||
mech_input_token,
|
||||
mech_output_token,
|
||||
na->mechListMIC,
|
||||
@@ -823,26 +912,26 @@ acceptor_continue
|
||||
*/
|
||||
if ((mech_output_token != GSS_C_NO_BUFFER &&
|
||||
mech_output_token->length != 0)
|
||||
|| (ctx->open && negResult == accept_incomplete)
|
||||
|| (ctx->flags.open && negResult == accept_incomplete)
|
||||
|| require_response
|
||||
|| get_mic) {
|
||||
ret2 = send_accept (minor_status,
|
||||
ctx,
|
||||
0, /* ignored on subsequent tokens */
|
||||
mech_output_token,
|
||||
0,
|
||||
get_mic ? &mech_buf : NULL,
|
||||
GSS_C_NO_OID,
|
||||
get_mic ? &ctx->NegTokenInit_mech_types : NULL,
|
||||
output_token);
|
||||
if (ret2)
|
||||
goto out;
|
||||
}
|
||||
} else
|
||||
ret2 = GSS_S_COMPLETE;
|
||||
|
||||
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);
|
||||
free_NegotiationToken(&nt);
|
||||
}
|
||||
|
||||
|
@@ -2,6 +2,8 @@
|
||||
* Copyright (c) 2004, PADL Software Pty Ltd.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Portions Copyright (c) 2009 Apple Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
@@ -43,9 +45,6 @@
|
||||
gss_OID_desc _gss_spnego_mskrb_mechanism_oid_desc =
|
||||
{9, rk_UNCONST("\x2a\x86\x48\x82\xf7\x12\x01\x02\x02")};
|
||||
|
||||
gss_OID_desc _gss_spnego_krb5_mechanism_oid_desc =
|
||||
{9, rk_UNCONST("\x2a\x86\x48\x86\xf7\x12\x01\x02\x02")};
|
||||
|
||||
/*
|
||||
* Allocate a SPNEGO context handle
|
||||
*/
|
||||
@@ -61,28 +60,33 @@ _gss_spnego_alloc_sec_context (OM_uint32 * minor_status,
|
||||
return GSS_S_FAILURE;
|
||||
}
|
||||
|
||||
ctx->initiator_mech_types.len = 0;
|
||||
ctx->initiator_mech_types.val = NULL;
|
||||
ctx->NegTokenInit_mech_types.value = NULL;
|
||||
ctx->NegTokenInit_mech_types.length = 0;
|
||||
|
||||
ctx->preferred_mech_type = GSS_C_NO_OID;
|
||||
ctx->selected_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->open = 0;
|
||||
ctx->local = 0;
|
||||
ctx->require_mic = 0;
|
||||
ctx->verified_mic = 0;
|
||||
ctx->flags.open = 0;
|
||||
ctx->flags.local = 0;
|
||||
ctx->flags.peer_require_mic = 0;
|
||||
ctx->flags.require_mic = 0;
|
||||
ctx->flags.verified_mic = 0;
|
||||
|
||||
HEIMDAL_MUTEX_init(&ctx->ctx_id_mutex);
|
||||
|
||||
ctx->negoex_step = 0;
|
||||
ctx->negoex_transcript = NULL;
|
||||
ctx->negoex_seqnum = 0;
|
||||
HEIM_TAILQ_INIT(&ctx->negoex_mechs);
|
||||
memset(ctx->negoex_conv_id, 0, GUID_LENGTH);
|
||||
|
||||
*context_handle = (gss_ctx_id_t)ctx;
|
||||
|
||||
return GSS_S_COMPLETE;
|
||||
@@ -119,11 +123,12 @@ OM_uint32 GSSAPI_CALLCONV _gss_spnego_internal_delete_sec_context
|
||||
return GSS_S_NO_CONTEXT;
|
||||
}
|
||||
|
||||
if (ctx->initiator_mech_types.val != NULL)
|
||||
free_MechTypeList(&ctx->initiator_mech_types);
|
||||
if (ctx->NegTokenInit_mech_types.value)
|
||||
free(ctx->NegTokenInit_mech_types.value);
|
||||
|
||||
gss_release_oid(&minor, &ctx->preferred_mech_type);
|
||||
ctx->preferred_mech_type = GSS_C_NO_OID;
|
||||
ctx->negotiated_mech_type = GSS_C_NO_OID;
|
||||
ctx->selected_mech_type = GSS_C_NO_OID;
|
||||
|
||||
gss_release_name(&minor, &ctx->target_name);
|
||||
gss_release_name(&minor, &ctx->mech_src_name);
|
||||
@@ -137,6 +142,8 @@ OM_uint32 GSSAPI_CALLCONV _gss_spnego_internal_delete_sec_context
|
||||
ret = GSS_S_COMPLETE;
|
||||
}
|
||||
|
||||
_gss_negoex_release_context(ctx);
|
||||
|
||||
HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
|
||||
HEIMDAL_MUTEX_destroy(&ctx->ctx_id_mutex);
|
||||
|
||||
@@ -146,93 +153,121 @@ OM_uint32 GSSAPI_CALLCONV _gss_spnego_internal_delete_sec_context
|
||||
}
|
||||
|
||||
/*
|
||||
* 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
|
||||
* Returns TRUE if it is safe to omit mechListMIC because the preferred
|
||||
* mechanism was selected, and the peer did not require it.
|
||||
*/
|
||||
|
||||
OM_uint32 GSSAPI_CALLCONV
|
||||
_gss_spnego_require_mechlist_mic(OM_uint32 *minor_status,
|
||||
gssspnego_ctx ctx,
|
||||
int *require_mic)
|
||||
int
|
||||
_gss_spnego_safe_omit_mechlist_mic(gssspnego_ctx ctx)
|
||||
{
|
||||
gss_buffer_set_t buffer_set = GSS_C_NO_BUFFER_SET;
|
||||
OM_uint32 minor;
|
||||
int safe_omit = 0;
|
||||
|
||||
*minor_status = 0;
|
||||
*require_mic = 0;
|
||||
if (ctx->flags.peer_require_mic == FALSE)
|
||||
safe_omit = gss_oid_equal(ctx->selected_mech_type, ctx->preferred_mech_type);
|
||||
|
||||
if (ctx == NULL) {
|
||||
return GSS_S_COMPLETE;
|
||||
if (safe_omit)
|
||||
_gss_mg_log(10, "spnego: safe to omit mechListMIC, as preferred mechanism selected");
|
||||
else
|
||||
_gss_mg_log(10, "spnego: mechListMIC required");
|
||||
|
||||
return safe_omit;
|
||||
}
|
||||
|
||||
|
||||
static OM_uint32
|
||||
add_mech_type(OM_uint32 *minor_status,
|
||||
gss_OID mech_type,
|
||||
MechTypeList *mechtypelist)
|
||||
{
|
||||
MechType mech;
|
||||
int ret;
|
||||
|
||||
heim_assert(!gss_oid_equal(mech_type, GSS_SPNEGO_MECHANISM),
|
||||
"SPNEGO mechanism not filtered");
|
||||
|
||||
ret = der_get_oid(mech_type->elements, mech_type->length, &mech, NULL);
|
||||
if (ret == 0) {
|
||||
ret = add_MechTypeList(mechtypelist, &mech);
|
||||
free_MechType(&mech);
|
||||
}
|
||||
|
||||
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_spnego_krb5_mechanism_oid_desc) &&
|
||||
gss_oid_equal(ctx->preferred_mech_type, &_gss_spnego_mskrb_mechanism_oid_desc)) {
|
||||
*require_mic = 0;
|
||||
}
|
||||
if (ret) {
|
||||
*minor_status = ret;
|
||||
return GSS_S_FAILURE;
|
||||
}
|
||||
|
||||
return GSS_S_COMPLETE;
|
||||
}
|
||||
|
||||
static int
|
||||
add_mech_type(gss_OID mech_type,
|
||||
int includeMSCompatOID,
|
||||
MechTypeList *mechtypelist)
|
||||
add_mech_if_approved(OM_uint32 *minor_status,
|
||||
gss_const_name_t target_name,
|
||||
OM_uint32 (*func)(OM_uint32 *, void *, gss_const_name_t, gss_const_cred_id_t, gss_OID),
|
||||
void *userptr,
|
||||
int includeMSCompatOID,
|
||||
gss_const_cred_id_t cred_handle,
|
||||
MechTypeList *mechtypelist,
|
||||
gss_OID mech_oid,
|
||||
gss_OID *first_mech,
|
||||
OM_uint32 *first_major,
|
||||
OM_uint32 *first_minor,
|
||||
int *added_negoex)
|
||||
{
|
||||
MechType mech;
|
||||
int ret;
|
||||
OM_uint32 major, minor;
|
||||
|
||||
if (gss_oid_equal(mech_type, GSS_SPNEGO_MECHANISM))
|
||||
return 0;
|
||||
|
||||
if (includeMSCompatOID &&
|
||||
gss_oid_equal(mech_type, &_gss_spnego_krb5_mechanism_oid_desc)) {
|
||||
ret = der_get_oid(_gss_spnego_mskrb_mechanism_oid_desc.elements,
|
||||
_gss_spnego_mskrb_mechanism_oid_desc.length,
|
||||
&mech,
|
||||
NULL);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = add_MechTypeList(mechtypelist, &mech);
|
||||
free_MechType(&mech);
|
||||
if (ret)
|
||||
return ret;
|
||||
/*
|
||||
* Unapproved mechanisms are ignored, but we capture their result
|
||||
* code in case we didn't find any other mechanisms, in which case
|
||||
* we return that to the caller of _gss_spnego_indicate_mechtypelist().
|
||||
*/
|
||||
major = (*func)(&minor, userptr, target_name, cred_handle, mech_oid);
|
||||
if (major != GSS_S_COMPLETE) {
|
||||
if (*first_mech == GSS_C_NO_OID) {
|
||||
*first_major = major;
|
||||
*first_minor = minor;
|
||||
}
|
||||
return GSS_S_COMPLETE;
|
||||
}
|
||||
ret = der_get_oid(mech_type->elements, mech_type->length, &mech, NULL);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = add_MechTypeList(mechtypelist, &mech);
|
||||
free_MechType(&mech);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (_gss_negoex_mech_p(mech_oid)) {
|
||||
if (*added_negoex == FALSE) {
|
||||
major = add_mech_type(minor_status, GSS_NEGOEX_MECHANISM, mechtypelist);
|
||||
if (major != GSS_S_COMPLETE)
|
||||
return major;
|
||||
*added_negoex = TRUE;
|
||||
}
|
||||
|
||||
if (*first_mech == GSS_C_NO_OID)
|
||||
*first_mech = GSS_NEGOEX_MECHANISM;
|
||||
|
||||
/* if NegoEx-only mech, we are done */
|
||||
if (!_gss_negoex_and_spnego_mech_p(mech_oid))
|
||||
return GSS_S_COMPLETE;
|
||||
}
|
||||
|
||||
if (includeMSCompatOID && gss_oid_equal(mech_oid, GSS_KRB5_MECHANISM)) {
|
||||
major = add_mech_type(minor_status,
|
||||
&_gss_spnego_mskrb_mechanism_oid_desc,
|
||||
mechtypelist);
|
||||
if (major != GSS_S_COMPLETE)
|
||||
return major;
|
||||
}
|
||||
|
||||
major = add_mech_type(minor_status, mech_oid, mechtypelist);
|
||||
if (major != GSS_S_COMPLETE)
|
||||
return major;
|
||||
|
||||
if (*first_mech == GSS_C_NO_OID)
|
||||
*first_mech = mech_oid;
|
||||
|
||||
return GSS_S_COMPLETE;
|
||||
}
|
||||
|
||||
OM_uint32 GSSAPI_CALLCONV
|
||||
_gss_spnego_indicate_mechtypelist (OM_uint32 *minor_status,
|
||||
gss_name_t target_name,
|
||||
OM_uint32 (*func)(gss_const_cred_id_t, gss_name_t, gss_OID),
|
||||
gss_const_name_t target_name,
|
||||
OM_uint32 (*func)(OM_uint32 *, void *, gss_const_name_t, gss_const_cred_id_t, gss_OID),
|
||||
void *userptr,
|
||||
int includeMSCompatOID,
|
||||
gss_const_cred_id_t cred_handle,
|
||||
MechTypeList *mechtypelist,
|
||||
@@ -240,94 +275,297 @@ _gss_spnego_indicate_mechtypelist (OM_uint32 *minor_status,
|
||||
{
|
||||
gss_OID_set supported_mechs = GSS_C_NO_OID_SET;
|
||||
gss_OID first_mech = GSS_C_NO_OID;
|
||||
OM_uint32 ret;
|
||||
OM_uint32 ret, minor;
|
||||
OM_uint32 first_major = GSS_S_BAD_MECH, first_minor = 0;
|
||||
size_t i;
|
||||
int present = FALSE;
|
||||
int added_negoex = FALSE;
|
||||
|
||||
mechtypelist->len = 0;
|
||||
mechtypelist->val = NULL;
|
||||
|
||||
if (cred_handle) {
|
||||
ret = gss_inquire_cred(minor_status,
|
||||
cred_handle,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
&supported_mechs);
|
||||
} else {
|
||||
ret = gss_indicate_mechs(minor_status, &supported_mechs);
|
||||
}
|
||||
|
||||
if (ret != GSS_S_COMPLETE) {
|
||||
if (cred_handle != GSS_C_NO_CREDENTIAL)
|
||||
ret = _gss_spnego_inquire_cred_mechs(minor_status,
|
||||
cred_handle, &supported_mechs);
|
||||
else
|
||||
ret = _gss_spnego_indicate_mechs(minor_status, &supported_mechs);
|
||||
if (ret != GSS_S_COMPLETE)
|
||||
return ret;
|
||||
|
||||
heim_assert(supported_mechs != GSS_C_NO_OID_SET,
|
||||
"NULL mech set returned by SPNEGO inquire/indicate mechs");
|
||||
|
||||
/*
|
||||
* Propose Kerberos mech first if we have Kerberos credentials/supported mechs
|
||||
*/
|
||||
|
||||
ret = gss_test_oid_set_member(minor_status, GSS_KRB5_MECHANISM,
|
||||
supported_mechs, &present);
|
||||
if (ret == GSS_S_COMPLETE && present) {
|
||||
ret = add_mech_if_approved(minor_status, target_name,
|
||||
func, userptr, includeMSCompatOID,
|
||||
cred_handle, mechtypelist,
|
||||
GSS_KRB5_MECHANISM, &first_mech,
|
||||
&first_major, &first_minor,
|
||||
&added_negoex);
|
||||
}
|
||||
|
||||
if (supported_mechs->count == 0) {
|
||||
*minor_status = ENOENT;
|
||||
gss_release_oid_set(minor_status, &supported_mechs);
|
||||
return GSS_S_FAILURE;
|
||||
}
|
||||
|
||||
ret = (*func)(cred_handle, target_name, GSS_KRB5_MECHANISM);
|
||||
if (ret == GSS_S_COMPLETE) {
|
||||
ret = add_mech_type(GSS_KRB5_MECHANISM,
|
||||
includeMSCompatOID,
|
||||
mechtypelist);
|
||||
if (!GSS_ERROR(ret))
|
||||
first_mech = GSS_KRB5_MECHANISM;
|
||||
}
|
||||
ret = GSS_S_COMPLETE;
|
||||
/*
|
||||
* Now let's check all other mechs
|
||||
*/
|
||||
|
||||
for (i = 0; i < supported_mechs->count; i++) {
|
||||
OM_uint32 subret;
|
||||
if (gss_oid_equal(&supported_mechs->elements[i], GSS_SPNEGO_MECHANISM))
|
||||
continue;
|
||||
if (gss_oid_equal(&supported_mechs->elements[i], GSS_KRB5_MECHANISM))
|
||||
continue;
|
||||
|
||||
subret = (*func)(cred_handle, target_name, &supported_mechs->elements[i]);
|
||||
if (subret != GSS_S_COMPLETE)
|
||||
continue;
|
||||
|
||||
ret = add_mech_type(&supported_mechs->elements[i],
|
||||
includeMSCompatOID,
|
||||
mechtypelist);
|
||||
if (ret != 0) {
|
||||
*minor_status = ret;
|
||||
ret = GSS_S_FAILURE;
|
||||
break;
|
||||
ret = add_mech_if_approved(minor_status, target_name,
|
||||
func, userptr, FALSE,
|
||||
cred_handle, mechtypelist,
|
||||
&supported_mechs->elements[i],
|
||||
&first_mech,
|
||||
&first_major, &first_minor,
|
||||
&added_negoex);
|
||||
if (ret != GSS_S_COMPLETE) {
|
||||
gss_release_oid_set(&minor, &supported_mechs);
|
||||
return ret;
|
||||
}
|
||||
if (first_mech == GSS_C_NO_OID)
|
||||
first_mech = &supported_mechs->elements[i];
|
||||
}
|
||||
|
||||
if (mechtypelist->len == 0) {
|
||||
gss_release_oid_set(minor_status, &supported_mechs);
|
||||
*minor_status = 0;
|
||||
return GSS_S_BAD_MECH;
|
||||
}
|
||||
heim_assert(mechtypelist->len == 0 || first_mech != GSS_C_NO_OID,
|
||||
"mechtypelist non-empty but no mech selected");
|
||||
|
||||
if (preferred_mech != NULL) {
|
||||
ret = gss_duplicate_oid(minor_status, first_mech, preferred_mech);
|
||||
if (ret != GSS_S_COMPLETE)
|
||||
free_MechTypeList(mechtypelist);
|
||||
}
|
||||
gss_release_oid_set(minor_status, &supported_mechs);
|
||||
if (first_mech != GSS_C_NO_OID)
|
||||
ret = _gss_intern_oid(minor_status, first_mech, &first_mech);
|
||||
else if (GSS_ERROR(first_major)) {
|
||||
ret = first_major;
|
||||
*minor_status = first_minor;
|
||||
} else
|
||||
ret = GSS_S_BAD_MECH;
|
||||
|
||||
if (preferred_mech != NULL)
|
||||
*preferred_mech = first_mech;
|
||||
|
||||
gss_release_oid_set(&minor, &supported_mechs);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
|
||||
OM_uint32
|
||||
_gss_spnego_verify_mechtypes_mic(OM_uint32 *minor_status,
|
||||
gssspnego_ctx ctx,
|
||||
heim_octet_string *mic)
|
||||
{
|
||||
gss_buffer_desc mic_buf;
|
||||
OM_uint32 major_status;
|
||||
|
||||
if (mic == NULL) {
|
||||
*minor_status = 0;
|
||||
return gss_mg_set_error_string(GSS_SPNEGO_MECHANISM,
|
||||
GSS_S_DEFECTIVE_TOKEN, 0,
|
||||
"SPNEGO peer failed to send mechListMIC");
|
||||
}
|
||||
|
||||
if (ctx->flags.verified_mic) {
|
||||
/* This doesn't make sense, we've already verified it? */
|
||||
*minor_status = 0;
|
||||
return GSS_S_DUPLICATE_TOKEN;
|
||||
}
|
||||
|
||||
mic_buf.length = mic->length;
|
||||
mic_buf.value = mic->data;
|
||||
|
||||
major_status = gss_verify_mic(minor_status,
|
||||
ctx->negotiated_ctx_id,
|
||||
&ctx->NegTokenInit_mech_types,
|
||||
&mic_buf,
|
||||
NULL);
|
||||
if (major_status == GSS_S_COMPLETE) {
|
||||
_gss_spnego_ntlm_reset_crypto(minor_status, ctx, TRUE);
|
||||
} else if (major_status == GSS_S_UNAVAILABLE) {
|
||||
_gss_mg_log(10, "mech doesn't support MIC, allowing anyway");
|
||||
} else if (major_status) {
|
||||
return gss_mg_set_error_string(GSS_SPNEGO_MECHANISM,
|
||||
GSS_S_DEFECTIVE_TOKEN, *minor_status,
|
||||
"SPNEGO peer sent invalid mechListMIC");
|
||||
}
|
||||
ctx->flags.verified_mic = 1;
|
||||
|
||||
*minor_status = 0;
|
||||
|
||||
return GSS_S_COMPLETE;
|
||||
}
|
||||
|
||||
/*
|
||||
* According to [MS-SPNG] 3.3.5.1 the crypto state for NTLM is reset
|
||||
* before the completed context is returned to the application.
|
||||
*/
|
||||
|
||||
OM_uint32
|
||||
_gss_spnego_ntlm_reset_crypto(OM_uint32 *minor_status,
|
||||
gssspnego_ctx ctx,
|
||||
OM_uint32 verify)
|
||||
{
|
||||
gss_buffer_desc value;
|
||||
if (gss_oid_equal(ctx->negotiated_mech_type, GSS_NTLM_MECHANISM)) {
|
||||
gss_buffer_desc value;
|
||||
|
||||
value.length = sizeof(verify);
|
||||
value.value = &verify;
|
||||
value.length = sizeof(verify);
|
||||
value.value = &verify;
|
||||
|
||||
return gss_set_sec_context_option(minor_status,
|
||||
&ctx->negotiated_ctx_id,
|
||||
GSS_C_NTLM_RESET_CRYPTO,
|
||||
&value);
|
||||
return gss_set_sec_context_option(minor_status,
|
||||
&ctx->negotiated_ctx_id,
|
||||
GSS_C_NTLM_RESET_CRYPTO,
|
||||
&value);
|
||||
}
|
||||
|
||||
return GSS_S_COMPLETE;
|
||||
}
|
||||
|
||||
void
|
||||
_gss_spnego_log_mech(const char *prefix, gss_const_OID oid)
|
||||
{
|
||||
gss_buffer_desc oidbuf = GSS_C_EMPTY_BUFFER;
|
||||
OM_uint32 junk;
|
||||
const char *name = NULL;
|
||||
|
||||
if (!_gss_mg_log_level(10))
|
||||
return;
|
||||
|
||||
if (oid == GSS_C_NO_OID ||
|
||||
gss_oid_to_str(&junk, (gss_OID)oid, &oidbuf) != GSS_S_COMPLETE) {
|
||||
_gss_mg_log(10, "spnego: %s (null)", prefix);
|
||||
return;
|
||||
}
|
||||
|
||||
if (gss_oid_equal(oid, GSS_NEGOEX_MECHANISM))
|
||||
name = "negoex"; /* not a real mech */
|
||||
else if (gss_oid_equal(oid, &_gss_spnego_mskrb_mechanism_oid_desc))
|
||||
name = "mskrb";
|
||||
else {
|
||||
gssapi_mech_interface m = __gss_get_mechanism(oid);
|
||||
if (m)
|
||||
name = m->gm_name;
|
||||
}
|
||||
|
||||
_gss_mg_log(10, "spnego: %s %s { %.*s }",
|
||||
prefix,
|
||||
name ? name : "unknown",
|
||||
(int)oidbuf.length, (char *)oidbuf.value);
|
||||
gss_release_buffer(&junk, &oidbuf);
|
||||
}
|
||||
|
||||
void
|
||||
_gss_spnego_log_mechTypes(MechTypeList *mechTypes)
|
||||
{
|
||||
size_t i;
|
||||
char mechbuf[64];
|
||||
size_t mech_len;
|
||||
gss_OID_desc oid;
|
||||
int ret;
|
||||
|
||||
if (!_gss_mg_log_level(10))
|
||||
return;
|
||||
|
||||
for (i = 0; i < mechTypes->len; i++) {
|
||||
ret = der_put_oid ((unsigned char *)mechbuf + sizeof(mechbuf) - 1,
|
||||
sizeof(mechbuf),
|
||||
&mechTypes->val[i],
|
||||
&mech_len);
|
||||
if (ret)
|
||||
continue;
|
||||
|
||||
oid.length = (OM_uint32)mech_len;
|
||||
oid.elements = mechbuf + sizeof(mechbuf) - mech_len;
|
||||
|
||||
_gss_spnego_log_mech("initiator proposed mech", &oid);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Indicate mechs negotiable by SPNEGO
|
||||
*/
|
||||
|
||||
OM_uint32
|
||||
_gss_spnego_indicate_mechs(OM_uint32 *minor_status,
|
||||
gss_OID_set *mechs_p)
|
||||
{
|
||||
gss_OID_desc oids[3];
|
||||
gss_OID_set_desc except;
|
||||
|
||||
*mechs_p = GSS_C_NO_OID_SET;
|
||||
|
||||
oids[0] = *GSS_C_MA_DEPRECATED;
|
||||
oids[1] = *GSS_C_MA_NOT_DFLT_MECH;
|
||||
oids[2] = *GSS_C_MA_MECH_NEGO;
|
||||
|
||||
except.count = sizeof(oids) / sizeof(oids[0]);
|
||||
except.elements = oids;
|
||||
|
||||
return gss_indicate_mechs_by_attrs(minor_status,
|
||||
GSS_C_NO_OID_SET,
|
||||
&except,
|
||||
GSS_C_NO_OID_SET,
|
||||
mechs_p);
|
||||
}
|
||||
|
||||
/*
|
||||
* Indicate mechs in cred negotiatble by SPNEGO
|
||||
*/
|
||||
|
||||
OM_uint32
|
||||
_gss_spnego_inquire_cred_mechs(OM_uint32 *minor_status,
|
||||
gss_const_cred_id_t cred,
|
||||
gss_OID_set *mechs_p)
|
||||
{
|
||||
OM_uint32 ret, junk;
|
||||
gss_OID_set cred_mechs = GSS_C_NO_OID_SET;
|
||||
gss_OID_set mechs = GSS_C_NO_OID_SET;
|
||||
size_t i;
|
||||
|
||||
*mechs_p = GSS_C_NO_OID_SET;
|
||||
|
||||
heim_assert(cred != GSS_C_NO_CREDENTIAL, "Invalid null credential handle");
|
||||
|
||||
ret = gss_inquire_cred(minor_status, cred, NULL, NULL, NULL, &cred_mechs);
|
||||
if (ret != GSS_S_COMPLETE)
|
||||
goto out;
|
||||
|
||||
heim_assert(cred_mechs != GSS_C_NO_OID_SET,
|
||||
"gss_inquire_cred succeeded but returned null OID set");
|
||||
|
||||
ret = _gss_spnego_indicate_mechs(minor_status, &mechs);
|
||||
if (ret != GSS_S_COMPLETE)
|
||||
goto out;
|
||||
|
||||
heim_assert(mechs != GSS_C_NO_OID_SET,
|
||||
"_gss_spnego_indicate_mechs succeeded but returned null OID set");
|
||||
|
||||
ret = gss_create_empty_oid_set(minor_status, mechs_p);
|
||||
if (ret != GSS_S_COMPLETE)
|
||||
goto out;
|
||||
|
||||
for (i = 0; i < cred_mechs->count; i++) {
|
||||
gss_OID cred_mech = &cred_mechs->elements[i];
|
||||
int present = 0;
|
||||
|
||||
gss_test_oid_set_member(&junk, cred_mech, mechs, &present);
|
||||
if (!present)
|
||||
continue;
|
||||
|
||||
ret = gss_add_oid_set_member(minor_status, cred_mech, mechs_p);
|
||||
if (ret != GSS_S_COMPLETE)
|
||||
break;
|
||||
}
|
||||
|
||||
out:
|
||||
if (ret != GSS_S_COMPLETE)
|
||||
gss_release_oid_set(&junk, mechs_p);
|
||||
gss_release_oid_set(&junk, &cred_mechs);
|
||||
gss_release_oid_set(&junk, &mechs);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@@ -32,40 +32,6 @@
|
||||
|
||||
#include "spnego_locl.h"
|
||||
|
||||
static OM_uint32
|
||||
spnego_supported_mechs(OM_uint32 *minor_status, gss_OID_set *mechs)
|
||||
{
|
||||
OM_uint32 ret, junk;
|
||||
gss_OID_set m;
|
||||
size_t 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;
|
||||
}
|
||||
}
|
||||
gss_release_oid_set(&junk, &m);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
|
||||
OM_uint32 GSSAPI_CALLCONV _gss_spnego_process_context_token
|
||||
(OM_uint32 *minor_status,
|
||||
gss_const_ctx_id_t context_handle,
|
||||
@@ -450,7 +416,7 @@ OM_uint32 GSSAPI_CALLCONV _gss_spnego_import_sec_context (
|
||||
return ret;
|
||||
}
|
||||
|
||||
ctx->open = 1;
|
||||
ctx->flags.open = 1;
|
||||
/* don't bother filling in the rest of the fields */
|
||||
|
||||
HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
|
||||
@@ -472,7 +438,7 @@ OM_uint32 GSSAPI_CALLCONV _gss_spnego_inquire_names_for_mech (
|
||||
|
||||
*name_types = NULL;
|
||||
|
||||
ret = spnego_supported_mechs(minor_status, &mechs);
|
||||
ret = _gss_spnego_indicate_mechs(minor_status, &mechs);
|
||||
if (ret != GSS_S_COMPLETE)
|
||||
return ret;
|
||||
|
||||
|
@@ -68,48 +68,20 @@ OM_uint32 GSSAPI_CALLCONV _gss_spnego_acquire_cred_from
|
||||
)
|
||||
{
|
||||
OM_uint32 ret, tmp;
|
||||
gss_OID_set_desc actual_desired_mechs;
|
||||
gss_OID_set mechs;
|
||||
size_t i, j;
|
||||
|
||||
*output_cred_handle = GSS_C_NO_CREDENTIAL;
|
||||
|
||||
ret = gss_indicate_mechs(minor_status, &mechs);
|
||||
ret = _gss_spnego_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_acquire_cred_from(minor_status, desired_name,
|
||||
time_req, &actual_desired_mechs,
|
||||
time_req, mechs,
|
||||
cred_usage, cred_store,
|
||||
output_cred_handle,
|
||||
actual_mechs, time_rec);
|
||||
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, output_cred_handle);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -235,9 +207,6 @@ _gss_spnego_set_neg_mechs (OM_uint32 *minor_status,
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* for inner negotiation mechs, such as NegoEx */
|
||||
(void) gss_set_neg_mechs(&minor, cred_handle, mech_list);
|
||||
} else {
|
||||
/*
|
||||
* RFC 4178 says that GSS_Set_neg_mechs() on NULL credential sets
|
||||
|
@@ -151,6 +151,9 @@ static gssapi_mech_interface_desc spnego_mech = {
|
||||
NULL, /* gm_store_cred_into */
|
||||
_gss_spnego_set_neg_mechs,
|
||||
_gss_spnego_get_neg_mechs,
|
||||
NULL, /* gm_query_mechanism_info */
|
||||
NULL, /* gm_query_meta_data */
|
||||
NULL, /* gm_exchange_meta_data */
|
||||
NULL /* gm_compat */
|
||||
};
|
||||
|
||||
@@ -159,3 +162,4 @@ __gss_spnego_initialize(void)
|
||||
{
|
||||
return &spnego_mech;
|
||||
}
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
1018
lib/gssapi/spnego/negoex_ctx.c
Normal file
1018
lib/gssapi/spnego/negoex_ctx.c
Normal file
File diff suppressed because it is too large
Load Diff
25
lib/gssapi/spnego/negoex_err.et
Normal file
25
lib/gssapi/spnego/negoex_err.et
Normal file
@@ -0,0 +1,25 @@
|
||||
#
|
||||
# NegoEx error messages
|
||||
#
|
||||
|
||||
id "$Id$"
|
||||
|
||||
error_table ngex
|
||||
|
||||
prefix NEGOEX
|
||||
|
||||
error_code INVALID_MESSAGE_SIGNATURE, "Invalid NegoEx signature"
|
||||
error_code INVALID_MESSAGE_TYPE, "Invalid NegoEx message type"
|
||||
error_code INVALID_MESSAGE_SIZE, "Invalid NegoEx message size"
|
||||
error_code INVALID_CONVERSATION_ID, "Invalid NegoEx conversation ID"
|
||||
error_code AUTH_SCHEME_NOT_FOUND, "NegoEx authentication scheme not found"
|
||||
error_code MISSING_NEGO_MESSAGE, "Missing NegoEx negotiate message"
|
||||
error_code MISSING_AP_REQUEST_MESSAGE, "Missing NegoEx authentication protocol request message"
|
||||
error_code NO_AVAILABLE_MECHS, "No mutually supported NegoEx authentication schemes"
|
||||
error_code NO_VERIFY_KEY, "No NegoEx verify key"
|
||||
error_code UNKNOWN_CHECKSUM_SCHEME, "Unknown NegoEx checksum scheme"
|
||||
error_code INVALID_CHECKSUM, "Invalid NegoEx checksum"
|
||||
error_code UNSUPPORTED_CRITICAL_EXTENSION, "Unsupported critical NegoEx extension"
|
||||
error_code UNSUPPORTED_VERSION, "Unsupported NegoEx version"
|
||||
error_code MESSAGE_OUT_OF_SEQUENCE, "NegoEx message out of sequence"
|
||||
|
127
lib/gssapi/spnego/negoex_locl.h
Normal file
127
lib/gssapi/spnego/negoex_locl.h
Normal file
@@ -0,0 +1,127 @@
|
||||
/*
|
||||
* Copyright (C) 2011-2019 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:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * 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.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
|
||||
* COPYRIGHT HOLDER 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.
|
||||
*/
|
||||
|
||||
#ifndef NEGOEX_LOCL_H
|
||||
#define NEGOEX_LOCL_H
|
||||
|
||||
#include <negoex_err.h>
|
||||
|
||||
struct gssspnego_ctx_desc;
|
||||
|
||||
#define MESSAGE_SIGNATURE 0x535458454F47454EULL
|
||||
|
||||
#define EXTENSION_LENGTH 12
|
||||
|
||||
#define EXTENSION_FLAG_CRITICAL 0x80000000
|
||||
|
||||
#define CHECKSUM_SCHEME_RFC3961 1
|
||||
|
||||
#define NEGOEX_KEYUSAGE_INITIATOR_CHECKSUM 23
|
||||
#define NEGOEX_KEYUSAGE_ACCEPTOR_CHECKSUM 25
|
||||
|
||||
#define CHECKSUM_HEADER_LENGTH 20
|
||||
|
||||
#define GUID_LENGTH 16
|
||||
|
||||
typedef uint8_t auth_scheme[GUID_LENGTH];
|
||||
typedef uint8_t conversation_id[GUID_LENGTH];
|
||||
#define GUID_EQ(a, b) (memcmp(a, b, GUID_LENGTH) == 0)
|
||||
|
||||
#define NEGO_MESSAGE_HEADER_LENGTH 96
|
||||
#define EXCHANGE_MESSAGE_HEADER_LENGTH 64
|
||||
#define VERIFY_MESSAGE_HEADER_LENGTH 80
|
||||
#define ALERT_MESSAGE_HEADER_LENGTH 72
|
||||
#define ALERT_LENGTH 12
|
||||
#define ALERT_PULSE_LENGTH 8
|
||||
|
||||
#define ALERT_TYPE_PULSE 1
|
||||
#define ALERT_VERIFY_NO_KEY 1
|
||||
|
||||
enum message_type {
|
||||
INITIATOR_NEGO = 0, /* NEGO_MESSAGE */
|
||||
ACCEPTOR_NEGO, /* NEGO_MESSAGE */
|
||||
INITIATOR_META_DATA, /* EXCHANGE_MESSAGE */
|
||||
ACCEPTOR_META_DATA, /* EXCHANGE_MESSAGE */
|
||||
CHALLENGE, /* EXCHANGE_MESSAGE */
|
||||
AP_REQUEST, /* EXCHANGE_MESSAGE */
|
||||
VERIFY, /* VERIFY_MESSAGE */
|
||||
ALERT, /* ALERT */
|
||||
};
|
||||
|
||||
struct nego_message {
|
||||
uint8_t random[32];
|
||||
const uint8_t *schemes;
|
||||
uint16_t nschemes;
|
||||
};
|
||||
|
||||
struct exchange_message {
|
||||
auth_scheme scheme;
|
||||
gss_buffer_desc token;
|
||||
};
|
||||
|
||||
struct verify_message {
|
||||
auth_scheme scheme;
|
||||
uint32_t cksum_type;
|
||||
const uint8_t *cksum;
|
||||
size_t cksum_len;
|
||||
size_t offset_in_token;
|
||||
};
|
||||
|
||||
struct alert_message {
|
||||
auth_scheme scheme;
|
||||
int verify_no_key;
|
||||
};
|
||||
|
||||
struct negoex_message {
|
||||
uint32_t type;
|
||||
union {
|
||||
struct nego_message n;
|
||||
struct exchange_message e;
|
||||
struct verify_message v;
|
||||
struct alert_message a;
|
||||
} u;
|
||||
};
|
||||
|
||||
struct negoex_auth_mech {
|
||||
HEIM_TAILQ_ENTRY(negoex_auth_mech) links;
|
||||
gss_OID oid;
|
||||
auth_scheme scheme;
|
||||
gss_ctx_id_t mech_context;
|
||||
gss_buffer_desc metadata;
|
||||
krb5_crypto crypto;
|
||||
krb5_crypto verify_crypto;
|
||||
int complete;
|
||||
int sent_checksum;
|
||||
int verified_checksum;
|
||||
};
|
||||
|
||||
#define NEGOEX_LOG_LEVEL 10
|
||||
|
||||
#endif /* NEGOEX_LOCL_H */
|
1059
lib/gssapi/spnego/negoex_util.c
Normal file
1059
lib/gssapi/spnego/negoex_util.c
Normal file
File diff suppressed because it is too large
Load Diff
@@ -37,14 +37,17 @@ NegTokenInit ::= SEQUENCE {
|
||||
...
|
||||
}
|
||||
|
||||
NegResultEnum ::= ENUMERATED {
|
||||
accept_completed(0),
|
||||
accept_incomplete(1),
|
||||
reject(2),
|
||||
request-mic(3)
|
||||
}
|
||||
|
||||
-- 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,
|
||||
negResult [0] NegResultEnum OPTIONAL,
|
||||
supportedMech [1] MechType OPTIONAL,
|
||||
responseToken [2] OCTET STRING OPTIONAL,
|
||||
mechListMIC [3] OCTET STRING OPTIONAL,
|
||||
|
@@ -50,6 +50,7 @@
|
||||
#include <pthread.h>
|
||||
#endif
|
||||
|
||||
#include <krb5.h>
|
||||
#include <gssapi.h>
|
||||
#include <gssapi_krb5.h>
|
||||
#include <gssapi_spnego.h>
|
||||
@@ -63,11 +64,13 @@
|
||||
#endif
|
||||
|
||||
#include <heim_threads.h>
|
||||
#include <heimqueue.h>
|
||||
#include <asn1_err.h>
|
||||
|
||||
#include <gssapi_mech.h>
|
||||
|
||||
#include "spnego_asn1.h"
|
||||
#include "negoex_locl.h"
|
||||
#include "utils.h"
|
||||
#include <der.h>
|
||||
|
||||
@@ -75,30 +78,72 @@
|
||||
|
||||
#define ALLOC(X, N) (X) = calloc((N), sizeof(*(X)))
|
||||
|
||||
typedef struct {
|
||||
MechTypeList initiator_mech_types;
|
||||
struct gssspnego_ctx_desc;
|
||||
typedef struct gssspnego_ctx_desc *gssspnego_ctx;
|
||||
|
||||
typedef OM_uint32
|
||||
(*gssspnego_initiator_state)(OM_uint32 * minor_status,
|
||||
gss_const_cred_id_t cred,
|
||||
gssspnego_ctx ctx,
|
||||
gss_const_name_t name,
|
||||
gss_const_OID mech_type,
|
||||
OM_uint32 req_flags,
|
||||
OM_uint32 time_req,
|
||||
const gss_channel_bindings_t input_chan_bindings,
|
||||
gss_const_buffer_t input_token,
|
||||
gss_buffer_t output_token,
|
||||
OM_uint32 * ret_flags,
|
||||
OM_uint32 * time_rec);
|
||||
|
||||
struct gssspnego_ctx_desc {
|
||||
gss_buffer_desc NegTokenInit_mech_types;
|
||||
gss_OID preferred_mech_type;
|
||||
gss_OID selected_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;
|
||||
unsigned int open : 1;
|
||||
unsigned int local : 1;
|
||||
unsigned int require_mic : 1;
|
||||
unsigned int verified_mic : 1;
|
||||
unsigned int maybe_open : 1;
|
||||
struct spnego_flags {
|
||||
unsigned int open : 1;
|
||||
unsigned int local : 1;
|
||||
unsigned int require_mic : 1;
|
||||
unsigned int peer_require_mic : 1;
|
||||
unsigned int sent_mic : 1;
|
||||
unsigned int verified_mic : 1;
|
||||
unsigned int safe_omit : 1;
|
||||
unsigned int maybe_open : 1;
|
||||
unsigned int seen_supported_mech : 1;
|
||||
} flags;
|
||||
HEIMDAL_MUTEX ctx_id_mutex;
|
||||
|
||||
gss_name_t target_name;
|
||||
gssspnego_initiator_state initiator_state;
|
||||
|
||||
u_char oidbuf[17];
|
||||
size_t oidlen;
|
||||
|
||||
} *gssspnego_ctx;
|
||||
int negoex_step;
|
||||
krb5_storage *negoex_transcript;
|
||||
uint32_t negoex_seqnum;
|
||||
conversation_id negoex_conv_id;
|
||||
HEIM_TAILQ_HEAD(negoex_mech_list, negoex_auth_mech) negoex_mechs;
|
||||
};
|
||||
|
||||
extern gss_OID_desc _gss_spnego_mskrb_mechanism_oid_desc;
|
||||
extern gss_OID_desc _gss_spnego_krb5_mechanism_oid_desc;
|
||||
|
||||
struct gssspnego_optimistic_ctx {
|
||||
gssspnego_ctx spnegoctx;
|
||||
OM_uint32 req_flags;
|
||||
gss_name_t target_name;
|
||||
OM_uint32 time_req;
|
||||
gss_channel_bindings_t input_chan_bindings;
|
||||
/* out */
|
||||
gss_OID preferred_mech_type;
|
||||
gss_OID negotiated_mech_type;
|
||||
gss_buffer_desc optimistic_token;
|
||||
OM_uint32 optimistic_flags, optimistic_time_rec;
|
||||
gss_ctx_id_t gssctx;
|
||||
int complete;
|
||||
auth_scheme scheme;
|
||||
};
|
||||
|
||||
#include "spnego-private.h"
|
||||
|
||||
|
Reference in New Issue
Block a user