spnego: Restrict when SANON gets negotiated
There were cases where we weren't negotiating SANON where we should have. But we really don't want to overdo it. In particular we really never ever want a user with expired or absent Kerberos credentials (say) to accidentally negotiate SANON as that will then lead to authorization errors down the line, and those would be hard to diagnose as they would be masking the real issue (expired or absent credentials). So basically either the user passes GSS_C_ANON_FLAG or (and/or) they call gss_set_neg_mechs() to explicitly request SANON. Partly authored by me, partly authored by Claude with heavy human guidance, and reviewed by me.
This commit is contained in:
@@ -89,7 +89,8 @@ acceptor_approved(OM_uint32 *minor_status,
|
||||
if (gss_oid_equal(mech, GSS_NEGOEX_MECHANISM)) {
|
||||
size_t i;
|
||||
|
||||
ret = _gss_spnego_indicate_mechs(minor_status, &oidset);
|
||||
ret = _gss_spnego_indicate_mechs(minor_status, GSS_C_ANON_FLAG,
|
||||
&oidset);
|
||||
if (ret != GSS_S_COMPLETE)
|
||||
return ret;
|
||||
|
||||
@@ -681,13 +682,43 @@ acceptor_start
|
||||
}
|
||||
}
|
||||
|
||||
if (acceptor_cred_handle != GSS_C_NO_CREDENTIAL)
|
||||
ret = _gss_spnego_inquire_cred_mechs(minor_status,
|
||||
acceptor_cred_handle,
|
||||
&supported_mechs,
|
||||
&canonical_order);
|
||||
else
|
||||
ret = _gss_spnego_indicate_mechs(minor_status, &supported_mechs);
|
||||
/*
|
||||
* If the initiator proposed NegoEx, include anonymous mechanisms in
|
||||
* supported_mechs since NegoEx mechanisms like sanon-x25519 may be used.
|
||||
*/
|
||||
{
|
||||
OM_uint32 indicate_flags = 0;
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < ni->mechTypes.len; i++) {
|
||||
size_t oidlen = der_length_oid(&ni->mechTypes.val[i]);
|
||||
gss_OID_desc oid;
|
||||
char oidbuf[64];
|
||||
|
||||
if (oidlen > sizeof(oidbuf))
|
||||
continue;
|
||||
|
||||
oid.length = oidlen;
|
||||
oid.elements = oidbuf;
|
||||
|
||||
if (der_put_oid((unsigned char *)oidbuf + oidlen - 1,
|
||||
oidlen, &ni->mechTypes.val[i], &oidlen) == 0 &&
|
||||
gss_oid_equal(&oid, GSS_NEGOEX_MECHANISM)) {
|
||||
indicate_flags |= GSS_C_ANON_FLAG;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (acceptor_cred_handle != GSS_C_NO_CREDENTIAL)
|
||||
ret = _gss_spnego_inquire_cred_mechs(minor_status,
|
||||
indicate_flags,
|
||||
acceptor_cred_handle,
|
||||
&supported_mechs,
|
||||
&canonical_order);
|
||||
else
|
||||
ret = _gss_spnego_indicate_mechs(minor_status, indicate_flags,
|
||||
&supported_mechs);
|
||||
}
|
||||
if (ret != GSS_S_COMPLETE)
|
||||
goto out;
|
||||
|
||||
|
||||
@@ -403,10 +403,11 @@ _gss_spnego_indicate_mechtypelist (OM_uint32 *minor_status,
|
||||
mechtypelist->val = NULL;
|
||||
|
||||
if (cred_handle != GSS_C_NO_CREDENTIAL)
|
||||
ret = _gss_spnego_inquire_cred_mechs(minor_status, cred_handle,
|
||||
ret = _gss_spnego_inquire_cred_mechs(minor_status, req_flags, cred_handle,
|
||||
&supported_mechs, &canonical_order);
|
||||
else
|
||||
ret = _gss_spnego_indicate_mechs(minor_status, &supported_mechs);
|
||||
ret = _gss_spnego_indicate_mechs(minor_status, req_flags,
|
||||
&supported_mechs);
|
||||
if (ret != GSS_S_COMPLETE)
|
||||
return ret;
|
||||
|
||||
@@ -595,19 +596,24 @@ _gss_spnego_log_mechTypes(MechTypeList *mechTypes)
|
||||
|
||||
OM_uint32
|
||||
_gss_spnego_indicate_mechs(OM_uint32 *minor_status,
|
||||
OM_uint32 req_flags,
|
||||
gss_OID_set *mechs_p)
|
||||
{
|
||||
gss_OID_desc oids[3];
|
||||
gss_OID_desc oids[5];
|
||||
gss_OID_set_desc except;
|
||||
except.elements = oids;
|
||||
|
||||
*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;
|
||||
except.count = sizeof(oids) / sizeof(oids[0]) - 2;
|
||||
if ((req_flags & GSS_C_ANON_FLAG) == 0) {
|
||||
oids[3] = *GSS_C_MA_AUTH_INIT_ANON;
|
||||
oids[4] = *GSS_C_MA_AUTH_TARG_ANON;
|
||||
except.count = sizeof(oids) / sizeof(oids[0]);
|
||||
}
|
||||
|
||||
return gss_indicate_mechs_by_attrs(minor_status,
|
||||
GSS_C_NO_OID_SET,
|
||||
@@ -622,6 +628,7 @@ _gss_spnego_indicate_mechs(OM_uint32 *minor_status,
|
||||
|
||||
OM_uint32
|
||||
_gss_spnego_inquire_cred_mechs(OM_uint32 *minor_status,
|
||||
OM_uint32 req_flags,
|
||||
gss_const_cred_id_t cred,
|
||||
gss_OID_set *mechs_p,
|
||||
int *canonical_order)
|
||||
@@ -648,7 +655,32 @@ _gss_spnego_inquire_cred_mechs(OM_uint32 *minor_status,
|
||||
heim_assert(cred_mechs != GSS_C_NO_OID_SET && cred_mechs->count > 0,
|
||||
"gss_inquire_cred succeeded but returned no mechanisms");
|
||||
|
||||
ret = _gss_spnego_indicate_mechs(minor_status, &negotiable_mechs);
|
||||
/*
|
||||
* If gss_set_neg_mechs() was called and included an anonymous mechanism,
|
||||
* add GSS_C_ANON_FLAG so it won't be filtered out by _gss_spnego_indicate_mechs().
|
||||
*/
|
||||
if (*canonical_order && (req_flags & GSS_C_ANON_FLAG) == 0) {
|
||||
for (i = 0; i < cred_mechs->count; i++) {
|
||||
gss_OID_set mech_attrs = GSS_C_NO_OID_SET;
|
||||
int present = 0;
|
||||
|
||||
ret = gss_inquire_attrs_for_mech(minor_status,
|
||||
&cred_mechs->elements[i],
|
||||
&mech_attrs, NULL);
|
||||
if (ret == GSS_S_COMPLETE) {
|
||||
gss_test_oid_set_member(&junk, GSS_C_MA_AUTH_INIT_ANON,
|
||||
mech_attrs, &present);
|
||||
gss_release_oid_set(&junk, &mech_attrs);
|
||||
}
|
||||
if (present) {
|
||||
req_flags |= GSS_C_ANON_FLAG;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ret = _gss_spnego_indicate_mechs(minor_status, req_flags,
|
||||
&negotiable_mechs);
|
||||
if (ret != GSS_S_COMPLETE)
|
||||
goto out;
|
||||
|
||||
|
||||
@@ -370,7 +370,7 @@ OM_uint32 GSSAPI_CALLCONV _gss_spnego_inquire_names_for_mech (
|
||||
|
||||
*name_types = NULL;
|
||||
|
||||
ret = _gss_spnego_indicate_mechs(minor_status, &mechs);
|
||||
ret = _gss_spnego_indicate_mechs(minor_status, 0, &mechs);
|
||||
if (ret != GSS_S_COMPLETE)
|
||||
return ret;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user