Don't include @ symbol in realmless names, to conform with draft-ietf-abfab-gss-eap
[mech_eap.orig] / util_name.c
index fa5b108..c11c5bf 100644 (file)
 
 #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)
@@ -149,15 +149,32 @@ krbPrincipalToName(OM_uint32 *minor,
     return GSS_S_COMPLETE;
 }
 
+static krb5_error_code
+gssEapGetDefaultRealm(krb5_context krbContext, char **defaultRealm)
+{
+    *defaultRealm = NULL;
+
+    krb5_appdefault_string(krbContext, "eap_gss",
+                           NULL, "default_realm", "", defaultRealm);
+
+    if (*defaultRealm != NULL && (*defaultRealm)[0] == '\0') {
+        GSSEAP_FREE(*defaultRealm);
+        *defaultRealm = NULL;
+    }
+
+    return (*defaultRealm != NULL) ? 0 : KRB5_CONFIG_NODEFREALM;
+}
+
 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);
 
@@ -171,20 +188,31 @@ 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;
-    }
+    gssEapGetDefaultRealm(krbContext, &realm);
 
-    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;
 }
 
@@ -195,36 +223,67 @@ importUserName(OM_uint32 *minor,
 {
     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_flags(krbContext, nameString,
-                                       KRB5_PRINCIPAL_PARSE_REQUIRE_REALM,
-                                       &krbPrinc);
-        if (*minor != 0) {
-            GSSEAP_FREE(nameString);
+        /*
+         * First, attempt to parse the name on the assumption that it includes
+         * a qualifying realm.
+         */
+        code = krb5_parse_name_flags(krbContext, nameString,
+                                     KRB5_PRINCIPAL_PARSE_REQUIRE_REALM, &krbPrinc);
+        if (code == KRB5_PARSE_MALFORMED) {
+            char *defaultRealm = NULL;
+            int flags = 0;
+
+            /*
+             * We need an explicit appdefaults check because, at least with MIT
+             * Kerberos, setting the context realm to NULL will reset it to the
+             * default Kerberos realm after the second call to get_default_realm.
+             * We want to make sure that the default Kerberos realm does not end
+             * up accidentally appended to an unqualified name.
+             */
+            gssEapGetDefaultRealm(krbContext, &defaultRealm);
+
+            if (defaultRealm == NULL)
+                flags |= KRB5_PRINCIPAL_PARSE_NO_REALM;
+
+            code = krb5_parse_name_flags(krbContext, nameString, flags, &krbPrinc);
+
+            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;
 }
 
@@ -393,24 +452,25 @@ 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_OID mechType,
+                 const gss_OID nameType,
+                 const gss_OID mechType,
                  gss_name_t *pName)
 {
     struct gss_eap_name_import_provider nameTypes[] = {
+        { GSS_EAP_NT_EAP_NAME,              importUserName              },
         { 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
@@ -420,11 +480,9 @@ gssEapImportName(OM_uint32 *minor,
     OM_uint32 tmpMinor;
     gss_name_t name = GSS_C_NO_NAME;
 
-    if (nameType == GSS_C_NO_OID)
-        nameType = nameTypes[0].oid;
-
     for (i = 0; i < sizeof(nameTypes) / sizeof(nameTypes[0]); i++) {
-        if (oidEqual(nameTypes[i].oid, nameType)) {
+        if (oidEqual(nameTypes[i].oid,
+                     nameType == GSS_C_NO_OID ? GSS_EAP_NT_EAP_NAME : nameType)) {
             major = nameTypes[i].import(minor, nameBuffer, &name);
             break;
         }
@@ -631,6 +689,7 @@ gssEapDisplayName(OM_uint32 *minor,
     krb5_context krbContext;
     char *krbName;
     gss_OID name_type;
+    int flags = 0;
 
     GSSEAP_KRB_INIT(&krbContext);
 
@@ -642,7 +701,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;
     }
@@ -660,7 +732,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)