X-Git-Url: http://www.project-moonshot.org/gitweb/?a=blobdiff_plain;f=util_name.c;h=aa19b943c7514306adf6bdea9359c0a6c19838cc;hb=refs%2Fheads%2Fddf-name;hp=d365f9852927611a4352ff1efc7287544855c1ef;hpb=ae79fdae047f980d01b2b4e84ccea52e24d8c7a0;p=mech_eap.orig diff --git a/util_name.c b/util_name.c index d365f98..aa19b94 100644 --- a/util_name.c +++ b/util_name.c @@ -59,12 +59,12 @@ #include "gssapiP_eap.h" -static gss_OID_desc gssEapNtPrincipalName = { +static gss_OID_desc gssEapNtEapName = { /* 1.3.6.1.4.1.5322.22.2.1 */ 10, "\x2B\x06\x01\x04\x01\xA9\x4A\x16\x02\x01" }; -gss_OID GSS_EAP_NT_PRINCIPAL_NAME = &gssEapNtPrincipalName; +gss_OID GSS_EAP_NT_EAP_NAME = &gssEapNtEapName; OM_uint32 gssEapAllocName(OM_uint32 *minor, gss_name_t *pName) @@ -111,6 +111,7 @@ gssEapReleaseName(OM_uint32 *minor, gss_name_t *pName) GSSEAP_KRB_INIT(&krbContext); krb5_free_principal(krbContext, name->krbPrincipal); + gssEapReleaseOid(&tmpMinor, &name->mechanismUsed); gssEapReleaseAttrContext(&tmpMinor, name); @@ -148,15 +149,27 @@ krbPrincipalToName(OM_uint32 *minor, return GSS_S_COMPLETE; } +static char * +gssEapGetDefaultRealm(krb5_context krbContext) +{ + char *defaultRealm = NULL; + + krb5_appdefault_string(krbContext, "eap_gss", + NULL, "default_realm", "", &defaultRealm); + + return defaultRealm; +} + static OM_uint32 importServiceName(OM_uint32 *minor, const gss_buffer_t nameBuffer, gss_name_t *pName) { OM_uint32 major; + krb5_error_code code; krb5_context krbContext; krb5_principal krbPrinc; - char *service, *host; + char *service, *host, *realm = NULL; GSSEAP_KRB_INIT(&krbContext); @@ -170,83 +183,139 @@ importServiceName(OM_uint32 *minor, host++; } - /* XXX this is probably NOT what we want to be doing */ - if (krb5_sname_to_principal(krbContext, host, service, - KRB5_NT_SRV_HST, &krbPrinc) != 0) { - GSSEAP_FREE(service); - *minor = GSSEAP_BAD_SERVICE_NAME; - return GSS_S_FAILURE; - } + realm = gssEapGetDefaultRealm(krbContext); - major = krbPrincipalToName(minor, &krbPrinc, pName); - if (GSS_ERROR(major)) { - krb5_free_principal(krbContext, krbPrinc); + code = krb5_build_principal(krbContext, + &krbPrinc, + realm != NULL ? strlen(realm) : 0, + realm != NULL ? realm : "", + service, + host, + NULL); + + if (code == 0) { + KRB_PRINC_TYPE(krbPrinc) = KRB5_NT_SRV_HST; + + major = krbPrincipalToName(minor, &krbPrinc, pName); + if (GSS_ERROR(major)) + krb5_free_principal(krbContext, krbPrinc); + } else { + major = GSS_S_FAILURE; + *minor = GSSEAP_BAD_SERVICE_NAME; } + if (realm != NULL) + GSSEAP_FREE(realm); GSSEAP_FREE(service); + return major; } +#define IMPORT_FLAG_DEFAULT_REALM 0x1 + +/* + * Import an EAP name, possibly appending the default GSS EAP realm, + */ static OM_uint32 -importUserName(OM_uint32 *minor, - const gss_buffer_t nameBuffer, - gss_name_t *pName) +importEapNameFlags(OM_uint32 *minor, + const gss_buffer_t nameBuffer, + OM_uint32 importFlags, + gss_name_t *pName) { OM_uint32 major; krb5_context krbContext; - krb5_principal krbPrinc; - char *nameString; + krb5_principal krbPrinc = NULL; + krb5_error_code code; GSSEAP_KRB_INIT(&krbContext); if (nameBuffer == GSS_C_NO_BUFFER) { - *minor = krb5_copy_principal(krbContext, - krbAnonymousPrincipal(), &krbPrinc); - if (*minor != 0) + code = krb5_copy_principal(krbContext, + krbAnonymousPrincipal(), &krbPrinc); + if (code != 0) { + *minor = code; return GSS_S_FAILURE; + } } else { + char *nameString; + major = bufferToString(minor, nameBuffer, &nameString); if (GSS_ERROR(major)) return major; - *minor = krb5_parse_name(krbContext, nameString, &krbPrinc); - if (*minor != 0) { - GSSEAP_FREE(nameString); + /* + * First, attempt to parse the name on the assumption that it includes + * a qualifying realm. This allows us to avoid accidentally appending + * the default Kerberos realm to an unqualified name. (A bug in MIT + * Kerberos prevents the default realm being set to an empty value.) + */ + code = krb5_parse_name_flags(krbContext, nameString, + KRB5_PRINCIPAL_PARSE_REQUIRE_REALM, &krbPrinc); + if (code == KRB5_PARSE_MALFORMED) { + char *defaultRealm = NULL; + int parseFlags = 0; + + /* Possibly append the default EAP realm if required */ + if (importFlags & IMPORT_FLAG_DEFAULT_REALM) + defaultRealm = gssEapGetDefaultRealm(krbContext); + + /* If no default realm, leave the realm empty in the parsed name */ + if (defaultRealm == NULL || defaultRealm[0] == '\0') + parseFlags |= KRB5_PRINCIPAL_PARSE_NO_REALM; + + code = krb5_parse_name_flags(krbContext, nameString, parseFlags, &krbPrinc); + +#ifdef HAVE_HEIMDAL_VERSION + if (code == 0 && KRB_PRINC_REALM(krbPrinc) == NULL) { + KRB_PRINC_REALM(krbPrinc) = GSSEAP_CALLOC(1, sizeof(char)); + if (KRB_PRINC_REALM(krbPrinc) == NULL) + code = ENOMEM; + } +#endif + + if (defaultRealm != NULL) + GSSEAP_FREE(defaultRealm); + } + + GSSEAP_FREE(nameString); + + if (code != 0) { + *minor = code; return GSS_S_FAILURE; } } + assert(krbPrinc != NULL); + major = krbPrincipalToName(minor, &krbPrinc, pName); - if (GSS_ERROR(major)) { + if (GSS_ERROR(major)) krb5_free_principal(krbContext, krbPrinc); - } - GSSEAP_FREE(nameString); return major; } static OM_uint32 +importEapName(OM_uint32 *minor, + const gss_buffer_t nameBuffer, + gss_name_t *pName) +{ + return importEapNameFlags(minor, nameBuffer, 0, pName); +} + +static OM_uint32 +importUserName(OM_uint32 *minor, + const gss_buffer_t nameBuffer, + gss_name_t *pName) +{ + return importEapNameFlags(minor, nameBuffer, IMPORT_FLAG_DEFAULT_REALM, pName); +} + +static OM_uint32 importAnonymousName(OM_uint32 *minor, const gss_buffer_t nameBuffer GSSEAP_UNUSED, gss_name_t *pName) { - OM_uint32 major; - krb5_context krbContext; - krb5_principal krbPrinc; - - GSSEAP_KRB_INIT(&krbContext); - - *minor = krb5_copy_principal(krbContext, krbAnonymousPrincipal(), - &krbPrinc); - if (*minor != 0) - return GSS_S_FAILURE; - - major = krbPrincipalToName(minor, &krbPrinc, pName); - if (GSS_ERROR(major)) { - krb5_free_principal(krbContext, krbPrinc); - } - - return major; + return importEapNameFlags(minor, GSS_C_NO_BUFFER, 0, pName); } #define UPDATE_REMAIN(n) do { \ @@ -275,6 +344,7 @@ gssEapImportNameInternal(OM_uint32 *minor, gss_buffer_desc buf; enum gss_eap_token_type tokType; gss_name_t name = GSS_C_NO_NAME; + gss_OID mechanismUsed = GSS_C_NO_OID; GSSEAP_KRB_INIT(&krbContext); @@ -282,7 +352,10 @@ gssEapImportNameInternal(OM_uint32 *minor, remain = nameBuffer->length; if (flags & EXPORT_NAME_FLAG_OID) { - if (remain < 6 + GSS_EAP_MECHANISM->length + 4) + gss_OID_desc mech; + + /* TOK_ID || MECH_OID_LEN || MECH_OID */ + if (remain < 6) return GSS_S_BAD_NAME; if (flags & EXPORT_NAME_FLAG_COMPOSITE) @@ -297,21 +370,32 @@ gssEapImportNameInternal(OM_uint32 *minor, /* MECH_OID_LEN */ len = load_uint16_be(p); - if (len != 2 + GSS_EAP_MECHANISM->length) + if (len < 2) return GSS_S_BAD_NAME; UPDATE_REMAIN(2); /* MECH_OID */ if (p[0] != 0x06) return GSS_S_BAD_NAME; - if (p[1] != GSS_EAP_MECHANISM->length) - return GSS_S_BAD_MECH; - if (memcmp(&p[2], GSS_EAP_MECHANISM->elements, GSS_EAP_MECHANISM->length)) - return GSS_S_BAD_MECH; - UPDATE_REMAIN(2 + GSS_EAP_MECHANISM->length); + + mech.length = p[1]; + mech.elements = &p[2]; + + CHECK_REMAIN(mech.length); + + major = gssEapCanonicalizeOid(minor, + &mech, + OID_FLAG_FAMILY_MECH_VALID | + OID_FLAG_MAP_FAMILY_MECH_TO_NULL, + &mechanismUsed); + if (GSS_ERROR(major)) + goto cleanup; + + UPDATE_REMAIN(2 + mech.length); } /* NAME_LEN */ + CHECK_REMAIN(4); len = load_uint32_be(p); UPDATE_REMAIN(4); @@ -321,10 +405,13 @@ gssEapImportNameInternal(OM_uint32 *minor, buf.value = p; UPDATE_REMAIN(len); - major = importUserName(minor, &buf, &name); + major = importEapNameFlags(minor, &buf, 0, &name); if (GSS_ERROR(major)) goto cleanup; + name->mechanismUsed = mechanismUsed; + mechanismUsed = GSS_C_NO_OID; + if (flags & EXPORT_NAME_FLAG_COMPOSITE) { gss_buffer_desc buf; @@ -340,10 +427,12 @@ gssEapImportNameInternal(OM_uint32 *minor, *minor = 0; cleanup: - if (GSS_ERROR(major)) + if (GSS_ERROR(major)) { + gssEapReleaseOid(&tmpMinor, &mechanismUsed); gssEapReleaseName(&tmpMinor, &name); - else + } else { *pName = name; + } return major; } @@ -370,40 +459,56 @@ importCompositeExportName(OM_uint32 *minor, #endif struct gss_eap_name_import_provider { - gss_OID oid; + gss_const_OID oid; OM_uint32 (*import)(OM_uint32 *, const gss_buffer_t, gss_name_t *); }; OM_uint32 gssEapImportName(OM_uint32 *minor, const gss_buffer_t nameBuffer, - gss_OID nameType, - gss_name_t *name) + const gss_OID nameType, + const gss_OID mechType, + gss_name_t *pName) { struct gss_eap_name_import_provider nameTypes[] = { + { GSS_EAP_NT_EAP_NAME, importEapName }, { GSS_C_NT_USER_NAME, importUserName }, - { GSS_EAP_NT_PRINCIPAL_NAME, importUserName }, { GSS_C_NT_HOSTBASED_SERVICE, importServiceName }, { GSS_C_NT_HOSTBASED_SERVICE_X, importServiceName }, { GSS_C_NT_ANONYMOUS, importAnonymousName }, { GSS_C_NT_EXPORT_NAME, importExportName }, + { GSS_KRB5_NT_PRINCIPAL_NAME, importUserName }, #ifdef HAVE_GSS_C_NT_COMPOSITE_EXPORT { GSS_C_NT_COMPOSITE_EXPORT, importCompositeExportName }, #endif }; size_t i; + OM_uint32 major = GSS_S_BAD_NAMETYPE; + OM_uint32 tmpMinor; + gss_name_t name = GSS_C_NO_NAME; - *name = GSS_C_NO_NAME; + for (i = 0; i < sizeof(nameTypes) / sizeof(nameTypes[0]); i++) { + if (oidEqual(nameTypes[i].oid, + nameType == GSS_C_NO_OID ? GSS_EAP_NT_EAP_NAME : nameType)) { + major = nameTypes[i].import(minor, nameBuffer, &name); + break; + } + } - if (nameType == GSS_C_NO_OID) - nameType = nameTypes[0].oid; + if (major == GSS_S_COMPLETE && + mechType != GSS_C_NO_OID) { + assert(gssEapIsConcreteMechanismOid(mechType)); + assert(name->mechanismUsed == GSS_C_NO_OID); - for (i = 0; i < sizeof(nameTypes) / sizeof(nameTypes[0]); i++) { - if (oidEqual(nameTypes[i].oid, nameType)) - return nameTypes[i].import(minor, nameBuffer, name); + major = gssEapCanonicalizeOid(minor, mechType, 0, &name->mechanismUsed); } - return GSS_S_BAD_NAMETYPE; + if (GSS_ERROR(major)) + gssEapReleaseName(&tmpMinor, &name); + else + *pName = name; + + return major; } OM_uint32 @@ -422,29 +527,29 @@ gssEapExportNameInternal(OM_uint32 *minor, unsigned int flags) { OM_uint32 major = GSS_S_FAILURE, tmpMinor; - krb5_context krbContext; - char *krbName = NULL; - size_t krbNameLen, exportedNameLen; + gss_buffer_desc nameBuf = GSS_C_EMPTY_BUFFER; + size_t exportedNameLen; unsigned char *p; gss_buffer_desc attrs = GSS_C_EMPTY_BUFFER; + gss_OID mech; exportedName->length = 0; exportedName->value = NULL; - GSSEAP_KRB_INIT(&krbContext); + if (name->mechanismUsed != GSS_C_NO_OID) + mech = name->mechanismUsed; + else + mech = GSS_EAP_MECHANISM; - *minor = krb5_unparse_name(krbContext, name->krbPrincipal, &krbName); - if (*minor != 0) { - major = GSS_S_FAILURE; + major = gssEapDisplayName(minor, name, &nameBuf, NULL); + if (GSS_ERROR(major)) goto cleanup; - } - krbNameLen = strlen(krbName); exportedNameLen = 0; if (flags & EXPORT_NAME_FLAG_OID) { - exportedNameLen += 6 + GSS_EAP_MECHANISM->length; + exportedNameLen += 6 + mech->length; } - exportedNameLen += 4 + krbNameLen; + exportedNameLen += 4 + nameBuf.length; if (flags & EXPORT_NAME_FLAG_COMPOSITE) { major = gssEapExportAttrContext(minor, name, &attrs); if (GSS_ERROR(major)) @@ -469,23 +574,23 @@ gssEapExportNameInternal(OM_uint32 *minor, : TOK_TYPE_EXPORT_NAME, p); p += 2; - store_uint16_be(GSS_EAP_MECHANISM->length + 2, p); + store_uint16_be(mech->length + 2, p); p += 2; /* MECH_OID */ *p++ = 0x06; - *p++ = GSS_EAP_MECHANISM->length & 0xff; - memcpy(p, GSS_EAP_MECHANISM->elements, GSS_EAP_MECHANISM->length); - p += GSS_EAP_MECHANISM->length; + *p++ = mech->length & 0xff; + memcpy(p, mech->elements, mech->length); + p += mech->length; } /* NAME_LEN */ - store_uint32_be(krbNameLen, p); + store_uint32_be(nameBuf.length, p); p += 4; /* NAME */ - memcpy(p, krbName, krbNameLen); - p += krbNameLen; + memcpy(p, nameBuf.value, nameBuf.length); + p += nameBuf.length; if (flags & EXPORT_NAME_FLAG_COMPOSITE) { memcpy(p, attrs.value, attrs.length); @@ -499,21 +604,23 @@ gssEapExportNameInternal(OM_uint32 *minor, cleanup: gss_release_buffer(&tmpMinor, &attrs); + gss_release_buffer(&tmpMinor, &nameBuf); if (GSS_ERROR(major)) gss_release_buffer(&tmpMinor, exportedName); - krb5_free_unparsed_name(krbContext, krbName); return major; } OM_uint32 -gssEapDuplicateName(OM_uint32 *minor, - const gss_name_t input_name, - gss_name_t *dest_name) +gssEapCanonicalizeName(OM_uint32 *minor, + const gss_name_t input_name, + const gss_OID mech_type, + gss_name_t *dest_name) { OM_uint32 major, tmpMinor; krb5_context krbContext; gss_name_t name; + gss_OID mech_used; if (input_name == GSS_C_NO_NAME) { *minor = EINVAL; @@ -527,6 +634,18 @@ gssEapDuplicateName(OM_uint32 *minor, return major; } + if (mech_type != GSS_C_NO_OID) + mech_used = mech_type; + else + mech_used = input_name->mechanismUsed; + + major = gssEapCanonicalizeOid(minor, + mech_used, + OID_FLAG_NULL_VALID, + &name->mechanismUsed); + if (GSS_ERROR(major)) + goto cleanup; + name->flags = input_name->flags; *minor = krb5_copy_principal(krbContext, input_name->krbPrincipal, @@ -553,6 +672,15 @@ cleanup: } OM_uint32 +gssEapDuplicateName(OM_uint32 *minor, + const gss_name_t input_name, + gss_name_t *dest_name) +{ + return gssEapCanonicalizeName(minor, input_name, + GSS_C_NO_OID, dest_name); +} + +OM_uint32 gssEapDisplayName(OM_uint32 *minor, gss_name_t name, gss_buffer_t output_name_buffer, @@ -562,6 +690,7 @@ gssEapDisplayName(OM_uint32 *minor, krb5_context krbContext; char *krbName; gss_OID name_type; + int flags = 0; GSSEAP_KRB_INIT(&krbContext); @@ -573,7 +702,20 @@ gssEapDisplayName(OM_uint32 *minor, return GSS_S_CALL_INACCESSIBLE_READ | GSS_S_BAD_NAME; } - *minor = krb5_unparse_name(krbContext, name->krbPrincipal, &krbName); + /* + * According to draft-ietf-abfab-gss-eap-01, when the realm is + * absent the trailing '@' is not included. + */ +#ifdef HAVE_HEIMDAL_VERSION + if (KRB_PRINC_REALM(name->krbPrincipal) == NULL || + KRB_PRINC_REALM(name->krbPrincipal)[0] == '\0') +#else + if (KRB_PRINC_REALM(name->krbPrincipal)->length == 0) +#endif + flags |= KRB5_PRINCIPAL_UNPARSE_NO_REALM; + + *minor = krb5_unparse_name_flags(krbContext, name->krbPrincipal, + flags, &krbName); if (*minor != 0) { return GSS_S_FAILURE; } @@ -591,7 +733,7 @@ gssEapDisplayName(OM_uint32 *minor, name->krbPrincipal, krbAnonymousPrincipal())) { name_type = GSS_C_NT_ANONYMOUS; } else { - name_type = GSS_EAP_NT_PRINCIPAL_NAME; + name_type = GSS_EAP_NT_EAP_NAME; } if (output_name_type != NULL) @@ -599,3 +741,27 @@ gssEapDisplayName(OM_uint32 *minor, return GSS_S_COMPLETE; } + +OM_uint32 +gssEapCompareName(OM_uint32 *minor, + gss_name_t name1, + gss_name_t name2, + int *name_equal) +{ + krb5_context krbContext; + + *minor = 0; + + if (name1 == GSS_C_NO_NAME && name2 == GSS_C_NO_NAME) { + *name_equal = 1; + } else if (name1 != GSS_C_NO_NAME && name2 != GSS_C_NO_NAME) { + GSSEAP_KRB_INIT(&krbContext); + + /* krbPrincipal is immutable, so lock not required */ + *name_equal = krb5_principal_compare(krbContext, + name1->krbPrincipal, + name2->krbPrincipal); + } + + return GSS_S_COMPLETE; +}