Merge users/lhoward/sasl-gs2 to trunk
authorGreg Hudson <ghudson@mit.edu>
Wed, 6 Oct 2010 18:25:04 +0000 (18:25 +0000)
committerGreg Hudson <ghudson@mit.edu>
Wed, 6 Oct 2010 18:25:04 +0000 (18:25 +0000)
git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@24436 dc483132-0cff-0310-8789-dd5450dbe970

17 files changed:
src/appl/gss-sample/gss-client.c
src/appl/gss-sample/gss-server.c
src/lib/gssapi/generic/gssapi.hin
src/lib/gssapi/generic/gssapiP_generic.h
src/lib/gssapi/generic/gssapi_generic.c
src/lib/gssapi/generic/util_buffer.c
src/lib/gssapi/krb5/gssapi_krb5.c
src/lib/gssapi/libgssapi_krb5.exports
src/lib/gssapi/mechglue/Makefile.in
src/lib/gssapi/mechglue/g_initialize.c
src/lib/gssapi/mechglue/g_mechattr.c [new file with mode: 0644]
src/lib/gssapi/mechglue/g_saslname.c [new file with mode: 0644]
src/lib/gssapi/mechglue/mglueP.h
src/lib/gssapi/spnego/gssapiP_spnego.h
src/lib/gssapi/spnego/spnego_mech.c
src/tests/gssapi/Makefile.in
src/tests/gssapi/t_saslname.c [new file with mode: 0644]

index d922cc3bd5c50eb4c2c4e0a5c6f1432601f1213c..d439f23fbcc8f226b78e1ace44fd4c5ce3099514 100644 (file)
 #include "gss-misc.h"
 
 static int verbose = 1;
+static int spnego = 0;
+static gss_OID_desc gss_spnego_mechanism_oid_desc =
+        {6, (void *)"\x2b\x06\x01\x05\x05\x02"};
 
 static void
 usage()
 {
-    fprintf(stderr, "Usage: gss-client [-port port] [-mech mechanism] [-d]\n");
-    fprintf(stderr, "       [-seq] [-noreplay] [-nomutual] [-user user] [-pass pw]");
+    fprintf(stderr, "Usage: gss-client [-port port] [-mech mechanism] "
+            "[-spnego] [-d]\n");
+    fprintf(stderr, "       [-seq] [-noreplay] [-nomutual] [-user user] "
+            "[-pass pw]");
 #ifdef _WIN32
     fprintf(stderr, " [-threads num]");
 #endif
@@ -176,10 +181,17 @@ client_establish_context(int s, char *service_name, OM_uint32 gss_flags,
         gss_name_t gss_username = GSS_C_NO_NAME;
         gss_OID_set_desc mechs, *mechsp = GSS_C_NO_OID_SET;
 
-        if (oid != GSS_C_NO_OID) {
+        if (spnego) {
+            mechs.elements = &gss_spnego_mechanism_oid_desc;
+            mechs.count = 1;
+            mechsp = &mechs;
+        } else if (oid != GSS_C_NO_OID) {
             mechs.elements = oid;
             mechs.count = 1;
             mechsp = &mechs;
+        } else {
+            mechs.elements = NULL;
+            mechs.count = 0;
         }
 
         if (username != NULL) {
@@ -218,6 +230,20 @@ client_establish_context(int s, char *service_name, OM_uint32 gss_flags,
             gss_release_name(&min_stat, &gss_username);
             return -1;
         }
+        if (spnego && oid != GSS_C_NO_OID) {
+            gss_OID_set_desc neg_mechs;
+
+            neg_mechs.elements = oid;
+            neg_mechs.count = 1;
+
+            maj_stat = gss_set_neg_mechs(&min_stat, cred, &neg_mechs);
+            if (maj_stat != GSS_S_COMPLETE) {
+                display_status("setting neg mechs", maj_stat, min_stat);
+                gss_release_name(&min_stat, &gss_username);
+                gss_release_cred(&min_stat, &cred);
+                return -1;
+            }
+        }
         gss_release_name(&min_stat, &gss_username);
 
         /*
@@ -264,7 +290,8 @@ client_establish_context(int s, char *service_name, OM_uint32 gss_flags,
         do {
             maj_stat = gss_init_sec_context(&init_sec_min_stat,
                                             cred, gss_context,
-                                            target_name, oid, gss_flags, 0,
+                                            target_name, mechs.elements,
+                                            gss_flags, 0,
                                             NULL, /* channel bindings */
                                             token_ptr, NULL, /* mech type */
                                             &send_tok, ret_flags,
@@ -409,7 +436,7 @@ call_server(host, port, oid, service_name, gss_flags, auth_flag,
     char    *username;
     char    *password;
 {
-    gss_ctx_id_t context;
+    gss_ctx_id_t context = GSS_C_NO_CONTEXT;
     gss_buffer_desc in_buf, out_buf;
     int     s, state;
     OM_uint32 ret_flags;
@@ -523,7 +550,7 @@ call_server(host, port, oid, service_name, gss_flags, auth_flag,
     } else {
         /* Seal the message */
         in_buf.value = msg;
-        in_buf.length = strlen(msg);
+        in_buf.length = strlen((char *)in_buf.value);
     }
 
     for (i = 0; i < mcount; i++) {
@@ -611,6 +638,7 @@ call_server(host, port, oid, service_name, gss_flags, auth_flag,
     }
 
     (void) close(s);
+
     return 0;
 }
 
@@ -776,7 +804,7 @@ main(argc, argv)
         } else if (strcmp(*argv, "-iakerb") == 0) {
             mechanism = "{ 1 3 6 1 5 2 5 }";
         } else if (strcmp(*argv, "-spnego") == 0) {
-            mechanism = "{ 1 3 6 1 5 5 2 }";
+            spnego = 1;
         } else if (strcmp(*argv, "-krb5") == 0) {
             mechanism = "{ 1 3 5 1 5 2 }";
 #ifdef _WIN32
index 0ddfaeee87136cf3cb5ecd2ddab1937b3749604e..e83326791a076088654ec0300c04d884105083ab 100644 (file)
@@ -67,6 +67,9 @@
 #include <strings.h>
 #endif
 
+static OM_uint32
+enumerateAttributes(OM_uint32 *minor, gss_name_t name, int noisy);
+
 static void
 usage()
 {
@@ -104,6 +107,7 @@ int     verbose = 0;
  * fails, an error message is displayed and -1 is returned; otherwise,
  * 0 is returned.
  */
+
 static int
 server_acquire_creds(char *service_name, gss_cred_id_t *server_creds)
 {
@@ -121,7 +125,7 @@ server_acquire_creds(char *service_name, gss_cred_id_t *server_creds)
     }
 
     maj_stat = gss_acquire_cred(&min_stat, server_name, 0,
-                                GSS_C_NULL_OID_SET, GSS_C_ACCEPT,
+                                GSS_C_NO_OID_SET, GSS_C_ACCEPT,
                                 server_creds, NULL, NULL);
     if (maj_stat != GSS_S_COMPLETE) {
         display_status("acquiring credentials", maj_stat, min_stat);
@@ -262,6 +266,7 @@ server_establish_context(int s, gss_cred_id_t server_creds,
             display_status("displaying name", maj_stat, min_stat);
             return -1;
         }
+        enumerateAttributes(&min_stat, client, TRUE);
         maj_stat = gss_release_name(&min_stat, &client);
         if (maj_stat != GSS_S_COMPLETE) {
             display_status("releasing name", maj_stat, min_stat);
@@ -410,7 +415,8 @@ sign_server(int s, gss_cred_id_t server_creds, int export)
     gss_buffer_desc client_name, xmit_buf, msg_buf;
     gss_ctx_id_t context;
     OM_uint32 maj_stat, min_stat;
-    int     i, conf_state, ret_flags;
+    int     i, conf_state;
+    OM_uint32 ret_flags;
     char   *cp;
     int     token_flags;
 
@@ -796,3 +802,77 @@ main(int argc, char **argv)
 
     return 0;
 }
+
+static void
+dumpAttribute(OM_uint32 *minor,
+              gss_name_t name,
+              gss_buffer_t attribute,
+              int noisy)
+{
+    OM_uint32 major, tmp;
+    gss_buffer_desc value;
+    gss_buffer_desc display_value;
+    int authenticated = 0;
+    int complete = 0;
+    int more = -1;
+    unsigned int i;
+
+    while (more != 0) {
+        value.value = NULL;
+        display_value.value = NULL;
+
+        major = gss_get_name_attribute(minor, name, attribute, &authenticated,
+                                       &complete, &value, &display_value,
+                                       &more);
+        if (GSS_ERROR(major)) {
+            display_status("gss_get_name_attribute", major, *minor);
+            break;
+        }
+
+        printf("Attribute %.*s %s %s\n\n%.*s\n",
+               (int)attribute->length, (char *)attribute->value,
+               authenticated ? "Authenticated" : "",
+               complete ? "Complete" : "",
+               (int)display_value.length, (char *)display_value.value);
+
+        if (noisy) {
+            for (i = 0; i < value.length; i++) {
+                if ((i % 32) == 0)
+                    printf("\n");
+                printf("%02x", ((char *)value.value)[i] & 0xFF);
+            }
+            printf("\n\n");
+        }
+
+        gss_release_buffer(&tmp, &value);
+        gss_release_buffer(&tmp, &display_value);
+    }
+}
+
+static OM_uint32
+enumerateAttributes(OM_uint32 *minor,
+                    gss_name_t name,
+                    int noisy)
+{
+    OM_uint32 major, tmp;
+    int name_is_MN;
+    gss_OID mech = GSS_C_NO_OID;
+    gss_buffer_set_t attrs = GSS_C_NO_BUFFER_SET;
+    unsigned int i;
+
+    major = gss_inquire_name(minor, name, &name_is_MN, &mech, &attrs);
+    if (GSS_ERROR(major)) {
+        display_status("gss_inquire_name", major, *minor);
+        return major;
+    }
+
+    if (attrs != GSS_C_NO_BUFFER_SET) {
+        for (i = 0; i < attrs->count; i++)
+            dumpAttribute(minor, name, &attrs->elements[i], noisy);
+    }
+
+    gss_release_oid(&tmp, &mech);
+    gss_release_buffer_set(&tmp, &attrs);
+
+    return major;
+}
index fb82e3c4f5131e84315ab533bdc600fc3a7be184..15d685d8c08c2d88439f7695f73d15995d7d5dd3 100644 (file)
@@ -289,6 +289,8 @@ typedef int             gss_cred_usage_t;
      (((OM_uint32) 17ul) << GSS_C_ROUTINE_ERROR_OFFSET)
 #define GSS_S_NAME_NOT_MN \
      (((OM_uint32) 18ul) << GSS_C_ROUTINE_ERROR_OFFSET)
+#define GSS_S_BAD_MECH_ATTR \
+     (((OM_uint32) 19ul) << GSS_C_ROUTINE_ERROR_OFFSET)
 
 /*
  * Supplementary info bits:
@@ -831,4 +833,85 @@ gss_set_neg_mechs(
 /* XXXX This is a necessary evil until the spec is fixed */
 #define GSS_S_CRED_UNAVAIL GSS_S_FAILURE
 
+/*
+ * RFC 5587
+ */
+typedef const gss_buffer_desc *gss_const_buffer_t;
+typedef const struct gss_channel_bindings_struct *gss_const_channel_bindings_t;
+typedef const struct gss_ctx_id_struct gss_const_ctx_id_t;
+typedef const struct gss_cred_id_struct gss_const_cred_id_t;
+typedef const struct gss_name_struct gss_const_name_t;
+typedef const gss_OID_desc *gss_const_OID;
+typedef const gss_OID_set_desc *gss_const_OID_set;
+
+OM_uint32 KRB5_CALLCONV
+gss_indicate_mechs_by_attrs(
+    OM_uint32 *,        /* minor_status */
+    gss_const_OID_set,  /* desired_mech_attrs */
+    gss_const_OID_set,  /* except_mech_attrs */
+    gss_const_OID_set,  /* critical_mech_attrs */
+    gss_OID_set *);     /* mechs */
+
+OM_uint32 KRB5_CALLCONV
+gss_inquire_attrs_for_mech(
+    OM_uint32 *,        /* minor_status */
+    gss_const_OID,      /* mech */
+    gss_OID_set *,      /* mech_attrs */
+    gss_OID_set *);     /* known_mech_attrs */
+
+OM_uint32 KRB5_CALLCONV
+gss_display_mech_attr(
+    OM_uint32 *,        /* minor_status */
+    gss_const_OID,      /* mech_attr */
+    gss_buffer_t,       /* name */
+    gss_buffer_t,       /* short_desc */
+    gss_buffer_t);      /* long_desc */
+
+GSS_DLLIMP extern gss_const_OID GSS_C_MA_MECH_CONCRETE;
+GSS_DLLIMP extern gss_const_OID GSS_C_MA_MECH_PSEUDO;
+GSS_DLLIMP extern gss_const_OID GSS_C_MA_MECH_COMPOSITE;
+GSS_DLLIMP extern gss_const_OID GSS_C_MA_MECH_NEGO;
+GSS_DLLIMP extern gss_const_OID GSS_C_MA_MECH_GLUE;
+GSS_DLLIMP extern gss_const_OID GSS_C_MA_NOT_MECH;
+GSS_DLLIMP extern gss_const_OID GSS_C_MA_DEPRECATED;
+GSS_DLLIMP extern gss_const_OID GSS_C_MA_NOT_DFLT_MECH;
+GSS_DLLIMP extern gss_const_OID GSS_C_MA_ITOK_FRAMED;
+GSS_DLLIMP extern gss_const_OID GSS_C_MA_AUTH_INIT;
+GSS_DLLIMP extern gss_const_OID GSS_C_MA_AUTH_TARG;
+GSS_DLLIMP extern gss_const_OID GSS_C_MA_AUTH_INIT_INIT;
+GSS_DLLIMP extern gss_const_OID GSS_C_MA_AUTH_TARG_INIT;
+GSS_DLLIMP extern gss_const_OID GSS_C_MA_AUTH_INIT_ANON;
+GSS_DLLIMP extern gss_const_OID GSS_C_MA_AUTH_TARG_ANON;
+GSS_DLLIMP extern gss_const_OID GSS_C_MA_DELEG_CRED;
+GSS_DLLIMP extern gss_const_OID GSS_C_MA_INTEG_PROT;
+GSS_DLLIMP extern gss_const_OID GSS_C_MA_CONF_PROT;
+GSS_DLLIMP extern gss_const_OID GSS_C_MA_MIC;
+GSS_DLLIMP extern gss_const_OID GSS_C_MA_WRAP;
+GSS_DLLIMP extern gss_const_OID GSS_C_MA_PROT_READY;
+GSS_DLLIMP extern gss_const_OID GSS_C_MA_REPLAY_DET;
+GSS_DLLIMP extern gss_const_OID GSS_C_MA_OOS_DET;
+GSS_DLLIMP extern gss_const_OID GSS_C_MA_CBINDINGS;
+GSS_DLLIMP extern gss_const_OID GSS_C_MA_PFS;
+GSS_DLLIMP extern gss_const_OID GSS_C_MA_COMPRESS;
+GSS_DLLIMP extern gss_const_OID GSS_C_MA_CTX_TRANS;
+
+/*
+ * RFC 5801
+ */
+OM_uint32 KRB5_CALLCONV
+gss_inquire_saslname_for_mech(
+    OM_uint32 *,        /* minor_status */
+    const gss_OID,      /* desired_mech */
+    gss_buffer_t,       /* sasl_mech_name */
+    gss_buffer_t,       /* mech_name */
+    gss_buffer_t        /* mech_description */
+);
+
+OM_uint32 KRB5_CALLCONV
+gss_inquire_mech_for_saslname(
+    OM_uint32 *,        /* minor_status */
+    const gss_buffer_t, /* sasl_mech_name */
+    gss_OID *           /* mech_type */
+);
+
 #endif /* _GSSAPI_H_ */
index cb2340a4bc2e2f2f0eb9ed5c4a7eb905c7072044..f3af8a4d11d29a201cedc214a9dd64a1fdb7f24b 100644 (file)
@@ -294,4 +294,13 @@ OM_uint32 generic_gss_copy_oid_set
             const gss_OID_set_desc *, /* const oidset*/
             gss_OID_set * /*new_oidset*/);
 
+extern gss_OID_set gss_ma_known_attrs;
+
+OM_uint32 generic_gss_display_mech_attr(
+      OM_uint32         *minor_status,
+      gss_const_OID      mech_attr,
+      gss_buffer_t       name,
+      gss_buffer_t       short_desc,
+      gss_buffer_t       long_desc);
+
 #endif /* _GSSAPIP_GENERIC_H_ */
index 1d77d3f815ada22db2ec31bc808fd22bb139e3ad..f8d2c426c2462047967b08cd1c1b38f286f52b5a 100644 (file)
@@ -122,6 +122,35 @@ static const gss_OID_desc const_oids[] = {
 
     /* GSS_C_INQ_SSPI_SESSION_KEY 1.2.840.113554.1.2.2.5.5 */
     {11, (void *)"\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x05\x05"},
+
+    /* RFC 5587 attributes, see below */
+    {7, (void *)"\x2b\x06\x01\x05\x05\x0d\x01"},
+    {7, (void *)"\x2b\x06\x01\x05\x05\x0d\x02"},
+    {7, (void *)"\x2b\x06\x01\x05\x05\x0d\x03"},
+    {7, (void *)"\x2b\x06\x01\x05\x05\x0d\x04"},
+    {7, (void *)"\x2b\x06\x01\x05\x05\x0d\x05"},
+    {7, (void *)"\x2b\x06\x01\x05\x05\x0d\x06"},
+    {7, (void *)"\x2b\x06\x01\x05\x05\x0d\x07"},
+    {7, (void *)"\x2b\x06\x01\x05\x05\x0d\x08"},
+    {7, (void *)"\x2b\x06\x01\x05\x05\x0d\x09"},
+    {7, (void *)"\x2b\x06\x01\x05\x05\x0d\x0a"},
+    {7, (void *)"\x2b\x06\x01\x05\x05\x0d\x0b"},
+    {7, (void *)"\x2b\x06\x01\x05\x05\x0d\x0c"},
+    {7, (void *)"\x2b\x06\x01\x05\x05\x0d\x0d"},
+    {7, (void *)"\x2b\x06\x01\x05\x05\x0d\x0e"},
+    {7, (void *)"\x2b\x06\x01\x05\x05\x0d\x0f"},
+    {7, (void *)"\x2b\x06\x01\x05\x05\x0d\x10"},
+    {7, (void *)"\x2b\x06\x01\x05\x05\x0d\x11"},
+    {7, (void *)"\x2b\x06\x01\x05\x05\x0d\x12"},
+    {7, (void *)"\x2b\x06\x01\x05\x05\x0d\x13"},
+    {7, (void *)"\x2b\x06\x01\x05\x05\x0d\x14"},
+    {7, (void *)"\x2b\x06\x01\x05\x05\x0d\x15"},
+    {7, (void *)"\x2b\x06\x01\x05\x05\x0d\x16"},
+    {7, (void *)"\x2b\x06\x01\x05\x05\x0d\x17"},
+    {7, (void *)"\x2b\x06\x01\x05\x05\x0d\x18"},
+    {7, (void *)"\x2b\x06\x01\x05\x05\x0d\x19"},
+    {7, (void *)"\x2b\x06\x01\x05\x05\x0d\x1a"},
+    {7, (void *)"\x2b\x06\x01\x05\x05\x0d\x1b"},
 };
 
 /* Here are the constants which point to the static structure above.
@@ -152,3 +181,234 @@ GSS_DLLIMP gss_OID GSS_C_NT_EXPORT_NAME         = oids+6;
 gss_OID gss_nt_exported_name                    = oids+6;
 
 GSS_DLLIMP gss_OID GSS_C_INQ_SSPI_SESSION_KEY   = oids+7;
+
+GSS_DLLIMP gss_const_OID GSS_C_MA_MECH_CONCRETE     = oids+8;
+GSS_DLLIMP gss_const_OID GSS_C_MA_MECH_PSEUDO       = oids+9;
+GSS_DLLIMP gss_const_OID GSS_C_MA_MECH_COMPOSITE    = oids+10;
+GSS_DLLIMP gss_const_OID GSS_C_MA_MECH_NEGO         = oids+11;
+GSS_DLLIMP gss_const_OID GSS_C_MA_MECH_GLUE         = oids+12;
+GSS_DLLIMP gss_const_OID GSS_C_MA_NOT_MECH          = oids+13;
+GSS_DLLIMP gss_const_OID GSS_C_MA_DEPRECATED        = oids+14;
+GSS_DLLIMP gss_const_OID GSS_C_MA_NOT_DFLT_MECH     = oids+15;
+GSS_DLLIMP gss_const_OID GSS_C_MA_ITOK_FRAMED       = oids+16;
+GSS_DLLIMP gss_const_OID GSS_C_MA_AUTH_INIT         = oids+17;
+GSS_DLLIMP gss_const_OID GSS_C_MA_AUTH_TARG         = oids+18;
+GSS_DLLIMP gss_const_OID GSS_C_MA_AUTH_INIT_INIT    = oids+19;
+GSS_DLLIMP gss_const_OID GSS_C_MA_AUTH_TARG_INIT    = oids+20;
+GSS_DLLIMP gss_const_OID GSS_C_MA_AUTH_INIT_ANON    = oids+21;
+GSS_DLLIMP gss_const_OID GSS_C_MA_AUTH_TARG_ANON    = oids+22;
+GSS_DLLIMP gss_const_OID GSS_C_MA_DELEG_CRED        = oids+23;
+GSS_DLLIMP gss_const_OID GSS_C_MA_INTEG_PROT        = oids+24;
+GSS_DLLIMP gss_const_OID GSS_C_MA_CONF_PROT         = oids+25;
+GSS_DLLIMP gss_const_OID GSS_C_MA_MIC               = oids+26;
+GSS_DLLIMP gss_const_OID GSS_C_MA_WRAP              = oids+27;
+GSS_DLLIMP gss_const_OID GSS_C_MA_PROT_READY        = oids+28;
+GSS_DLLIMP gss_const_OID GSS_C_MA_REPLAY_DET        = oids+29;
+GSS_DLLIMP gss_const_OID GSS_C_MA_OOS_DET           = oids+30;
+GSS_DLLIMP gss_const_OID GSS_C_MA_CBINDINGS         = oids+31;
+GSS_DLLIMP gss_const_OID GSS_C_MA_PFS               = oids+32;
+GSS_DLLIMP gss_const_OID GSS_C_MA_COMPRESS          = oids+33;
+GSS_DLLIMP gss_const_OID GSS_C_MA_CTX_TRANS         = oids+34;
+
+static gss_OID_set_desc gss_ma_known_attrs_desc = { 27, oids+8 };
+gss_OID_set gss_ma_known_attrs = &gss_ma_known_attrs_desc;
+
+#define STRING_BUFFER(x)    { sizeof((x) - 1), (x) }
+
+static struct mech_attr_info_desc {
+    gss_OID mech_attr;
+    gss_buffer_desc name;
+    gss_buffer_desc short_desc;
+    gss_buffer_desc long_desc;
+} mech_attr_info[] = {
+    {
+        oids+8,
+        STRING_BUFFER("GSS_C_MA_MECH_CONCRETE"),
+        STRING_BUFFER("Mechanism is neither a pseudo-mechanism nor a "
+                      "composite mechanism."),
+    },
+    {
+        oids+9,
+        STRING_BUFFER("GSS_C_MA_MECH_PSEUDO"),
+        STRING_BUFFER("Mechanism is a pseudo-mechanism"),
+    },
+    {
+        oids+10,
+        STRING_BUFFER("GSS_C_MA_MECH_COMPOSITE"),
+        STRING_BUFFER("Mechanism is a composite of other mechanisms."),
+    },
+    {
+        oids+11,
+        STRING_BUFFER("GSS_C_MA_MECH_NEGO"),
+        STRING_BUFFER("Mechanism negotiates other mechanisms."),
+    },
+    {
+        oids+12,
+        STRING_BUFFER("GSS_C_MA_MECH_GLUE"),
+        STRING_BUFFER("OID is not a mechanism but the GSS-API itself."),
+    },
+    {
+        oids+13,
+        STRING_BUFFER("GSS_C_MA_NOT_MECH"),
+        STRING_BUFFER("Known OID but not a mechanism OID."),
+    },
+    {
+        oids+14,
+        STRING_BUFFER("GSS_C_MA_DEPRECATED"),
+        STRING_BUFFER("Mechanism is deprecated."),
+    },
+    {
+        oids+15,
+        STRING_BUFFER("GSS_C_MA_NOT_DFLT_MECH"),
+        STRING_BUFFER("Mechanism must not be used as a default mechanism."),
+    },
+    {
+        oids+16,
+        STRING_BUFFER("GSS_C_MA_ITOK_FRAMED"),
+        STRING_BUFFER("Mechanism's initial contexts are properly framed."),
+    },
+    {
+        oids+17,
+        STRING_BUFFER("GSS_C_MA_AUTH_INIT"),
+        STRING_BUFFER("Mechanism supports authentication of initiator to "
+                      "acceptor."),
+    },
+    {
+        oids+18,
+        STRING_BUFFER("GSS_C_MA_AUTH_TARG"),
+        STRING_BUFFER("Mechanism supports authentication of acceptor to "
+                      "initiator."),
+    },
+    {
+        oids+19,
+        STRING_BUFFER("GSS_C_MA_AUTH_INIT_INIT"),
+        STRING_BUFFER("Mechanism supports authentication of initiator using "
+                      "initial credentials."),
+    },
+    {
+        oids+20,
+        STRING_BUFFER("GSS_C_MA_AUTH_TARG_INIT"),
+        STRING_BUFFER("Mechanism supports authentication of acceptor using "
+                      "initial credentials."),
+    },
+    {
+        oids+21,
+        STRING_BUFFER("GSS_C_MA_AUTH_INIT_ANON"),
+        STRING_BUFFER("Mechanism supports GSS_C_NT_ANONYMOUS as an initiator "
+                      "name."),
+    },
+    {
+        oids+22,
+        STRING_BUFFER("GSS_C_MA_AUTH_TARG_ANON"),
+        STRING_BUFFER("Mechanism supports GSS_C_NT_ANONYMOUS as an acceptor "
+                      "name."),
+    },
+    {
+        oids+23,
+        STRING_BUFFER("GSS_C_MA_DELEG_CRED"),
+        STRING_BUFFER("Mechanism supports credential delegation."),
+    },
+    {
+        oids+24,
+        STRING_BUFFER("GSS_C_MA_INTEG_PROT"),
+        STRING_BUFFER("Mechanism supports per-message integrity protection."),
+    },
+    {
+        oids+25,
+        STRING_BUFFER("GSS_C_MA_CONF_PROT"),
+        STRING_BUFFER("Mechanism supports per-message confidentiality"
+                      "protection."),
+    },
+    {
+        oids+26,
+        STRING_BUFFER("GSS_C_MA_MIC"),
+        STRING_BUFFER("Mechanism supports Message Integrity Code (MIC) "
+                      "tokens."),
+    },
+    {
+        oids+27,
+        STRING_BUFFER("GSS_C_MA_WRAP"),
+        STRING_BUFFER("Mechanism supports wrap tokens."),
+    },
+    {
+        oids+28,
+        STRING_BUFFER("GSS_C_MA_PROT_READY"),
+        STRING_BUFFER("Mechanism supports per-message proteciton prior to "
+                      "full context establishment."),
+    },
+    {
+        oids+29,
+        STRING_BUFFER("GSS_C_MA_REPLAY_DET"),
+        STRING_BUFFER("Mechanism supports replay detection."),
+    },
+    {
+        oids+30,
+        STRING_BUFFER("GSS_C_MA_OOS_DET"),
+        STRING_BUFFER("Mechanism supports out-of-sequence detection."),
+    },
+    {
+        oids+31,
+        STRING_BUFFER("GSS_C_MA_CBINDINGS"),
+        STRING_BUFFER("Mechanism supports channel bindings."),
+    },
+    {
+        oids+32,
+        STRING_BUFFER("GSS_C_MA_PFS"),
+        STRING_BUFFER("Mechanism supports Perfect Forward Security."),
+    },
+    {
+        oids+33,
+        STRING_BUFFER("GSS_C_MA_COMPRESS"),
+        STRING_BUFFER("Mechanism supports compression of data inputs to "
+                      "gss_wrap()."),
+    },
+    {
+        oids+34,
+        STRING_BUFFER("GSS_C_MA_CTX_TRANS"),
+        STRING_BUFFER("Mechanism supports security context export/import."),
+    },
+};
+
+OM_uint32
+generic_gss_display_mech_attr(
+      OM_uint32         *minor_status,
+      gss_const_OID      mech_attr,
+      gss_buffer_t       name,
+      gss_buffer_t       short_desc,
+      gss_buffer_t       long_desc)
+{
+    size_t i;
+
+    if (name != GSS_C_NO_BUFFER) {
+        name->length = 0;
+        name->value = NULL;
+    }
+    if (short_desc != GSS_C_NO_BUFFER) {
+        short_desc->length = 0;
+        short_desc->value = NULL;
+    }
+    if (long_desc != GSS_C_NO_BUFFER) {
+        long_desc->length = 0;
+        long_desc->value = NULL;
+    }
+    for (i = 0; i < sizeof(mech_attr_info)/sizeof(mech_attr_info[0]); i++) {
+        struct mech_attr_info_desc *mai = &mech_attr_info[i];
+
+        if (g_OID_equal(mech_attr, mai->mech_attr)) {
+            if (name != GSS_C_NO_BUFFER &&
+                !g_make_string_buffer((char *)mai->name.value, name)) {
+                *minor_status = ENOMEM;
+                return GSS_S_FAILURE;
+            }
+            if (short_desc != GSS_C_NO_BUFFER &&
+                !g_make_string_buffer((char *)mai->short_desc.value,
+                                      short_desc)) {
+                *minor_status = ENOMEM;
+                return GSS_S_FAILURE;
+            }
+            return GSS_S_COMPLETE;
+        }
+    }
+
+    return GSS_S_BAD_MECH_ATTR;
+}
index cd16862f6de317e0d98d6393434ceb00af11c708..81d86fc76095ed902210e6988f46f174460b7696 100644 (file)
@@ -34,6 +34,9 @@
 
 int g_make_string_buffer(const char *str, gss_buffer_t buffer)
 {
+    if (buffer == GSS_C_NO_BUFFER)
+        return (1);
+
     buffer->length = strlen(str);
 
     if ((buffer->value = strdup(str)) == NULL) {
index 8b074d6168199f4eef7ba434a544ffb14e0682d0..25534e56b616a7ff90246a921e32fb86589468f3 100644 (file)
@@ -630,6 +630,116 @@ krb5_gssspi_mech_invoke (OM_uint32 *minor_status,
     return GSS_S_UNAVAILABLE;
 }
 
+#define GS2_KRB5_SASL_NAME        "GS2-KRB5"
+#define GS2_KRB5_SASL_NAME_LEN    (sizeof(GS2_KRB5_SASL_NAME) - 1)
+
+#define GS2_IAKERB_SASL_NAME      "GS2-IAKERB"
+#define GS2_IAKERB_SASL_NAME_LEN  (sizeof(GS2_IAKERB_SASL_NAME) - 1)
+
+static OM_uint32
+krb5_gss_inquire_mech_for_saslname(OM_uint32 *minor_status,
+                                   const gss_buffer_t sasl_mech_name,
+                                   gss_OID *mech_type)
+{
+    *minor_status = 0;
+
+    if (sasl_mech_name->length == GS2_KRB5_SASL_NAME_LEN &&
+        memcmp(sasl_mech_name->value,
+               GS2_KRB5_SASL_NAME, GS2_KRB5_SASL_NAME_LEN) == 0) {
+        if (mech_type != NULL)
+            *mech_type = (gss_OID)gss_mech_krb5;
+        return GSS_S_COMPLETE;
+    } else if (sasl_mech_name->length == GS2_IAKERB_SASL_NAME_LEN &&
+        memcmp(sasl_mech_name->value,
+               GS2_IAKERB_SASL_NAME, GS2_IAKERB_SASL_NAME_LEN) == 0) {
+        if (mech_type != NULL)
+            *mech_type = (gss_OID)gss_mech_iakerb;
+        return GSS_S_COMPLETE;
+    }
+
+    return GSS_S_BAD_MECH;
+}
+
+static OM_uint32
+krb5_gss_inquire_saslname_for_mech(OM_uint32 *minor_status,
+                                   const gss_OID desired_mech,
+                                   gss_buffer_t sasl_mech_name,
+                                   gss_buffer_t mech_name,
+                                   gss_buffer_t mech_description)
+{
+    if (g_OID_equal(desired_mech, gss_mech_iakerb)) {
+        if (!g_make_string_buffer(GS2_IAKERB_SASL_NAME, sasl_mech_name) ||
+            !g_make_string_buffer("iakerb", mech_name) ||
+            !g_make_string_buffer("Initial and Pass Through Authentication "
+                             "Kerberos Mechanism (IAKERB)", mech_description))
+            goto fail;
+    } else {
+        if (!g_make_string_buffer(GS2_KRB5_SASL_NAME, sasl_mech_name) ||
+            !g_make_string_buffer("krb5", mech_name) ||
+            !g_make_string_buffer("Kerberos 5 GSS-API Mechanism",
+                                  mech_description))
+            goto fail;
+    }
+
+    *minor_status = 0;
+    return GSS_S_COMPLETE;
+
+fail:
+    *minor_status = ENOMEM;
+    return GSS_S_FAILURE;
+}
+
+static OM_uint32
+krb5_gss_inquire_attrs_for_mech(OM_uint32 *minor_status,
+                                gss_const_OID mech,
+                                gss_OID_set *mech_attrs,
+                                gss_OID_set *known_mech_attrs)
+{
+    OM_uint32 major, tmpMinor;
+
+    if (mech_attrs == NULL) {
+        *minor_status = 0;
+        return GSS_S_COMPLETE;
+    }
+
+    major = gss_create_empty_oid_set(minor_status, mech_attrs);
+    if (GSS_ERROR(major))
+        goto cleanup;
+
+#define MA_SUPPORTED(ma)    do { \
+    major = gss_add_oid_set_member(minor_status, (gss_OID)ma, mech_attrs);  \
+    if (GSS_ERROR(major))                                                   \
+        goto cleanup;                                                       \
+    } while (0)
+
+    MA_SUPPORTED(GSS_C_MA_MECH_CONCRETE);
+    MA_SUPPORTED(GSS_C_MA_ITOK_FRAMED);
+    MA_SUPPORTED(GSS_C_MA_AUTH_INIT);
+    MA_SUPPORTED(GSS_C_MA_AUTH_TARG);
+    MA_SUPPORTED(GSS_C_MA_DELEG_CRED);
+    MA_SUPPORTED(GSS_C_MA_INTEG_PROT);
+    MA_SUPPORTED(GSS_C_MA_CONF_PROT);
+    MA_SUPPORTED(GSS_C_MA_MIC);
+    MA_SUPPORTED(GSS_C_MA_WRAP);
+    MA_SUPPORTED(GSS_C_MA_PROT_READY);
+    MA_SUPPORTED(GSS_C_MA_REPLAY_DET);
+    MA_SUPPORTED(GSS_C_MA_OOS_DET);
+    MA_SUPPORTED(GSS_C_MA_CBINDINGS);
+    MA_SUPPORTED(GSS_C_MA_CTX_TRANS);
+
+    if (g_OID_equal(mech, gss_mech_iakerb)) {
+        MA_SUPPORTED(GSS_C_MA_AUTH_INIT_INIT);
+    } else if (!g_OID_equal(mech, gss_mech_krb5)) {
+        MA_SUPPORTED(GSS_C_MA_DEPRECATED);
+    }
+
+cleanup:
+    if (GSS_ERROR(major))
+        gss_release_oid_set(&tmpMinor, mech_attrs);
+
+    return major;
+}
+
 static struct gss_config krb5_mechanism = {
     { GSS_MECH_KRB5_OID_LENGTH, GSS_MECH_KRB5_OID },
     NULL,
@@ -701,6 +811,9 @@ static struct gss_config krb5_mechanism = {
     krb5_gss_release_any_name_mapping,
     krb5_gss_pseudo_random,
     NULL,               /* set_neg_mechs */
+    krb5_gss_inquire_saslname_for_mech,
+    krb5_gss_inquire_mech_for_saslname,
+    krb5_gss_inquire_attrs_for_mech,
 };
 
 static struct gss_config_ext krb5_mechanism_ext = {
index 707fe52eeebcd490dea38bbc892e5c65f5b12103..fee99c994e66f4b5174e21bf9d5fe54896ce5fea 100644 (file)
@@ -7,6 +7,33 @@ GSS_C_NT_MACHINE_UID_NAME
 GSS_C_NT_STRING_UID_NAME
 GSS_C_NT_USER_NAME
 GSS_KRB5_NT_PRINCIPAL_NAME
+GSS_C_MA_MECH_CONCRETE
+GSS_C_MA_MECH_PSEUDO
+GSS_C_MA_MECH_COMPOSITE
+GSS_C_MA_MECH_NEGO
+GSS_C_MA_MECH_GLUE
+GSS_C_MA_NOT_MECH
+GSS_C_MA_DEPRECATED
+GSS_C_MA_NOT_DFLT_MECH
+GSS_C_MA_ITOK_FRAMED
+GSS_C_MA_AUTH_INIT
+GSS_C_MA_AUTH_TARG
+GSS_C_MA_AUTH_INIT_INIT
+GSS_C_MA_AUTH_TARG_INIT
+GSS_C_MA_AUTH_INIT_ANON
+GSS_C_MA_AUTH_TARG_ANON
+GSS_C_MA_DELEG_CRED
+GSS_C_MA_INTEG_PROT
+GSS_C_MA_CONF_PROT
+GSS_C_MA_MIC
+GSS_C_MA_WRAP
+GSS_C_MA_PROT_READY
+GSS_C_MA_REPLAY_DET
+GSS_C_MA_OOS_DET
+GSS_C_MA_CBINDINGS
+GSS_C_MA_PFS
+GSS_C_MA_COMPRESS
+GSS_C_MA_CTX_TRANS
 gss_accept_sec_context
 gss_acquire_cred
 gss_acquire_cred_with_password
@@ -23,6 +50,7 @@ gss_create_empty_buffer_set
 gss_create_empty_oid_set
 gss_delete_name_attribute
 gss_delete_sec_context
+gss_display_mech_attr
 gss_display_name
 gss_display_name_ext
 gss_display_status
@@ -36,12 +64,16 @@ gss_import_name
 gss_import_sec_context
 gss_indicate_mechs
 gss_init_sec_context
+gss_indicate_mechs_by_attrs
+gss_inquire_attrs_for_mech
 gss_inquire_context
 gss_inquire_cred
 gss_inquire_cred_by_mech
 gss_inquire_cred_by_oid
+gss_inquire_mech_for_saslname
 gss_inquire_mechs_for_name
 gss_inquire_names_for_mech
+gss_inquire_saslname_for_mech
 gss_inquire_sec_context_by_oid
 gss_krb5_ccache_name
 gss_krb5_copy_ccache
index 92cd6c99cca39fbb8e398b7730143c55f60974ef..d2dccb46be367657a86d40a3ac6f2d878b6b0278 100644 (file)
@@ -42,6 +42,7 @@ SRCS = \
        $(srcdir)/g_inq_names.c \
        $(srcdir)/g_map_name_to_any.c \
        $(srcdir)/g_mech_invoke.c \
+       $(srcdir)/g_mechattr.c \
        $(srcdir)/g_mechname.c \
        $(srcdir)/g_oid_ops.c \
        $(srcdir)/g_prf.c \
@@ -51,6 +52,7 @@ SRCS = \
        $(srcdir)/g_rel_name.c \
        $(srcdir)/g_rel_name_mapping.c \
        $(srcdir)/g_rel_oid_set.c \
+       $(srcdir)/g_saslname.c \
        $(srcdir)/g_seal.c \
        $(srcdir)/g_set_context_option.c \
        $(srcdir)/g_set_cred_option.c \
@@ -98,6 +100,7 @@ OBJS = \
        $(OUTPRE)g_inq_names.$(OBJEXT) \
        $(OUTPRE)g_map_name_to_any.$(OBJEXT) \
        $(OUTPRE)g_mech_invoke.$(OBJEXT) \
+       $(OUTPRE)g_mechattr.$(OBJEXT) \
        $(OUTPRE)g_mechname.$(OBJEXT) \
        $(OUTPRE)g_oid_ops.$(OBJEXT) \
        $(OUTPRE)g_prf.$(OBJEXT) \
@@ -107,6 +110,7 @@ OBJS = \
        $(OUTPRE)g_rel_name.$(OBJEXT) \
        $(OUTPRE)g_rel_name_mapping.$(OBJEXT) \
        $(OUTPRE)g_rel_oid_set.$(OBJEXT) \
+       $(OUTPRE)g_saslname.$(OBJEXT) \
        $(OUTPRE)g_seal.$(OBJEXT) \
        $(OUTPRE)g_set_context_option.$(OBJEXT) \
        $(OUTPRE)g_set_cred_option.$(OBJEXT) \
@@ -154,6 +158,7 @@ STLIBOBJS = \
        g_inq_names.o \
        g_map_name_to_any.o \
        g_mech_invoke.o \
+       g_mechattr.o \
        g_mechname.o \
        g_oid_ops.o \
        g_prf.o \
@@ -163,6 +168,7 @@ STLIBOBJS = \
        g_rel_name.o \
        g_rel_name_mapping.o \
        g_rel_oid_set.o \
+       g_saslname.o \
        g_seal.o \
        g_set_context_option.o \
        g_set_cred_option.o \
index a393a53095345e8453c8e85a0fd25efb0d18805b..d8b4956404dcb0df70a9a6db8f99b69c78fec76a 100644 (file)
@@ -791,6 +791,11 @@ build_dynamicMech(void *dl, const gss_OID mech_type)
        GSS_ADD_DYNAMIC_METHOD(dl, mech, gss_pseudo_random);
        /* RFC 4178 (introduced in 1.8; gss_get_neg_mechs not implemented) */
        GSS_ADD_DYNAMIC_METHOD(dl, mech, gss_set_neg_mechs);
+        /* draft-ietf-sasl-gs2 */
+        GSS_ADD_DYNAMIC_METHOD(dl, mech, gss_inquire_saslname_for_mech);
+        GSS_ADD_DYNAMIC_METHOD(dl, mech, gss_inquire_mech_for_saslname);
+        /* RFC 5587 */
+        GSS_ADD_DYNAMIC_METHOD(dl, mech, gss_inquire_attrs_for_mech);
 
        assert(mech_type != GSS_C_NO_OID);
 
diff --git a/src/lib/gssapi/mechglue/g_mechattr.c b/src/lib/gssapi/mechglue/g_mechattr.c
new file mode 100644 (file)
index 0000000..63ec357
--- /dev/null
@@ -0,0 +1,224 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * src/lib/gssapi/mechglue/g_mechattr.c
+ *
+ * Copyright (C) 2010 by the Massachusetts Institute of Technology.
+ * All rights reserved.
+ *
+ * Export of this software from the United States of America may
+ *   require a specific license from the United States Government.
+ *   It is the responsibility of any person or organization contemplating
+ *   export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission.  Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose.  It is provided "as is" without express
+ * or implied warranty.
+ *
+ *
+ * 
+ */
+#include "mglueP.h"
+
+static int
+testMechAttr(gss_const_OID attr,
+             gss_const_OID_set against)
+{
+    int present = 0;
+    OM_uint32 minor;
+
+    if (GSS_ERROR(generic_gss_test_oid_set_member(&minor, attr,
+                                                  (gss_OID_set)against,
+                                                  &present)))
+        return 0;
+
+    return present;
+}
+
+/*
+ * Return TRUE iff all the elements of desired and none of the elements
+ * of except exist in available.
+ */
+static int
+testMechAttrsOffered(gss_const_OID_set desired,
+                     gss_const_OID_set except,
+                     gss_const_OID_set available)
+{
+    size_t i;
+
+    if (desired != GSS_C_NO_OID_SET) {
+        for (i = 0; i < desired->count; i++) {
+            if (!testMechAttr(&desired->elements[i], available))
+                return 0;
+        }
+    }
+
+    if (except != GSS_C_NO_OID_SET) {
+        for (i = 0; i < except->count; i++) {
+            if (testMechAttr(&except->elements[i], available))
+                return 0;
+        }
+    }
+
+    return 1;
+}
+
+/*
+ * Return TRUE iff all the elements of critical exist in known.
+ */
+static int
+testMechAttrsKnown(gss_const_OID_set critical,
+                   gss_const_OID_set known)
+{
+    size_t i;
+
+    if (critical != GSS_C_NO_OID_SET) {
+        for (i = 0; i < critical->count; i++) {
+            if (!testMechAttr(&critical->elements[i], known))
+                return 0;
+        }
+    }
+
+    return 1;
+}
+
+OM_uint32 gss_indicate_mechs_by_attrs(
+      OM_uint32         *minor,
+      gss_const_OID_set  desired_mech_attrs,
+      gss_const_OID_set  except_mech_attrs,
+      gss_const_OID_set  critical_mech_attrs,
+      gss_OID_set       *mechs)
+{
+    OM_uint32       status, tmpMinor;
+    gss_OID_set     allMechs = GSS_C_NO_OID_SET;
+    size_t          i;
+
+    if (minor == NULL)
+        return GSS_S_CALL_INACCESSIBLE_WRITE;
+
+    *minor = 0;
+
+    if (mechs == NULL)
+        return GSS_S_CALL_INACCESSIBLE_WRITE;
+
+    *mechs = GSS_C_NO_OID_SET;
+
+    status = gss_indicate_mechs(minor, &allMechs);
+    if (GSS_ERROR(status))
+        goto cleanup;
+
+    status = generic_gss_create_empty_oid_set(minor, mechs);
+    if (GSS_ERROR(status))
+        goto cleanup;
+
+    for (i = 0; i < allMechs->count; i++) {
+        gss_OID_set supportedAttrs = GSS_C_NO_OID_SET;
+        gss_OID_set knownAttrs = GSS_C_NO_OID_SET;
+
+        status = gss_inquire_attrs_for_mech(minor, &allMechs->elements[i],
+                                            &supportedAttrs, &knownAttrs);
+        if (GSS_ERROR(status))
+            continue;
+
+        if (testMechAttrsOffered(desired_mech_attrs,
+                                 except_mech_attrs, supportedAttrs) &&
+            testMechAttrsKnown(critical_mech_attrs, knownAttrs)) {
+            status = gss_add_oid_set_member(minor, &allMechs->elements[i],
+                                            mechs);
+            if (GSS_ERROR(status)) {
+                gss_release_oid_set(&tmpMinor, &supportedAttrs);
+                gss_release_oid_set(&tmpMinor, &knownAttrs);
+                goto cleanup;
+            }
+        }
+
+        gss_release_oid_set(&tmpMinor, &supportedAttrs);
+        gss_release_oid_set(&tmpMinor, &knownAttrs);
+    }
+
+    *minor = 0;
+    status = GSS_S_COMPLETE;
+
+cleanup:
+    gss_release_oid_set(&tmpMinor, &allMechs);
+
+    return status;
+}
+
+OM_uint32 gss_inquire_attrs_for_mech(
+      OM_uint32         *minor,
+      gss_const_OID      mech_oid,
+      gss_OID_set       *mech_attrs,
+      gss_OID_set       *known_mech_attrs)
+{
+    OM_uint32       status, tmpMinor;
+    gss_mechanism   mech;
+
+    if (minor == NULL)
+        return GSS_S_CALL_INACCESSIBLE_WRITE;
+
+    *minor = 0;
+
+    if (mech_attrs != NULL)
+        *mech_attrs = GSS_C_NO_OID_SET;
+
+    if (known_mech_attrs != NULL)
+        *known_mech_attrs = GSS_C_NO_OID_SET;
+
+    mech = gssint_get_mechanism((gss_OID)mech_oid);
+    if (mech != NULL && mech->gss_inquire_attrs_for_mech != NULL) {
+        status = mech->gss_inquire_attrs_for_mech(minor,
+                                                  mech_oid,
+                                                  mech_attrs,
+                                                  known_mech_attrs);
+        if (GSS_ERROR(status))
+            return status;
+    }
+
+    if (mech_attrs != NULL && mech != gssint_get_mechanism(NULL)) {
+        if (*mech_attrs == GSS_C_NO_OID_SET) {
+            status = generic_gss_create_empty_oid_set(minor, mech_attrs);
+            if (GSS_ERROR(status))
+                return status;
+        }
+
+        status = generic_gss_add_oid_set_member(minor, GSS_C_MA_NOT_DFLT_MECH,
+                                                mech_attrs);
+        if (GSS_ERROR(status)) {
+            gss_release_oid_set(&tmpMinor, mech_attrs);
+            return status;
+        }
+    }
+
+    if (known_mech_attrs != NULL && *known_mech_attrs == GSS_C_NO_OID_SET) {
+        status = generic_gss_copy_oid_set(minor,
+                                          gss_ma_known_attrs,
+                                          known_mech_attrs);
+        if (GSS_ERROR(status)) {
+            gss_release_oid_set(&tmpMinor, mech_attrs);
+            *mech_attrs = GSS_C_NO_OID_SET;
+        }
+    }
+
+    return GSS_S_COMPLETE;
+}
+
+OM_uint32 gss_display_mech_attr(
+      OM_uint32         *minor,
+      gss_const_OID      mech_attr,
+      gss_buffer_t       name,
+      gss_buffer_t       short_desc,
+      gss_buffer_t       long_desc)
+{
+    return generic_gss_display_mech_attr(minor, mech_attr,
+                                         name, short_desc, long_desc);
+}
diff --git a/src/lib/gssapi/mechglue/g_saslname.c b/src/lib/gssapi/mechglue/g_saslname.c
new file mode 100644 (file)
index 0000000..4bffdd6
--- /dev/null
@@ -0,0 +1,210 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * src/lib/gssapi/mechglue/g_saslname.c
+ *
+ * Copyright (C) 2010 by the Massachusetts Institute of Technology.
+ * All rights reserved.
+ *
+ * Export of this software from the United States of America may
+ *   require a specific license from the United States Government.
+ *   It is the responsibility of any person or organization contemplating
+ *   export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission.  Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose.  It is provided "as is" without express
+ * or implied warranty.
+ *
+ *
+ * 
+ */
+#include "mglueP.h"
+#include <krb5/krb5.h>
+
+static char basis_32[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
+
+#define OID_SASL_NAME_LENGTH  (sizeof("GS2-XXXXXXXXXXX") - 1)
+
+static OM_uint32
+oidToSaslName(OM_uint32 *minor, const gss_OID mech,
+              char sasl_name[OID_SASL_NAME_LENGTH + 1])
+{
+    unsigned char derBuf[2];
+    krb5_crypto_iov iov[3];
+    unsigned char cksumBuf[20], *q = cksumBuf;
+    char *p = sasl_name;
+
+    if (mech->length > 127) {
+        *minor = ERANGE;
+        return GSS_S_BAD_MECH;
+    }
+
+    derBuf[0] = 0x06;
+    derBuf[1] = (unsigned char)mech->length;
+
+    iov[0].flags = KRB5_CRYPTO_TYPE_SIGN_ONLY;
+    iov[0].data.length = 2;
+    iov[0].data.data = (char *)derBuf;
+    iov[1].flags = KRB5_CRYPTO_TYPE_SIGN_ONLY;
+    iov[1].data.length = mech->length;
+    iov[1].data.data = (char *)mech->elements;
+    iov[2].flags = KRB5_CRYPTO_TYPE_CHECKSUM;
+    iov[2].data.length = sizeof(cksumBuf);
+    iov[2].data.data = (char *)cksumBuf;
+
+    *minor = krb5_k_make_checksum_iov(NULL, CKSUMTYPE_NIST_SHA,
+                                      NULL, 0, iov, 3);
+    if (*minor != 0)
+        return GSS_S_FAILURE;
+
+    memcpy(p, "GS2-", 4);
+    p += 4;
+
+    *p++ = basis_32[q[0] >> 3];
+    *p++ = basis_32[((q[0] & 7) << 2) | (q[1] >> 6)];
+    *p++ = basis_32[(q[1] & 0x3f) >> 1];
+    *p++ = basis_32[((q[1] & 1) << 4) | (q[2] >> 4)];
+    *p++ = basis_32[((q[2] & 0xf) << 1) | (q[3] >> 7)];
+    *p++ = basis_32[(q[3] & 0x7f) >> 2];
+    *p++ = basis_32[((q[3] & 3) << 3) | (q[4] >> 5)];
+    *p++ = basis_32[(q[4] & 0x1f)];
+    *p++ = basis_32[q[5] >> 3];
+    *p++ = basis_32[((q[5] & 7) << 2) | (q[6] >> 6)];
+    *p++ = basis_32[(q[6] & 0x3f) >> 1];
+
+    *p++ = '\0';
+
+    *minor = 0;
+    return GSS_S_COMPLETE;
+}
+
+static OM_uint32
+oidToSaslNameAlloc(OM_uint32 *minor, const gss_OID mech,
+                   gss_buffer_t sasl_name)
+{
+    OM_uint32 status, tmpMinor;
+
+    sasl_name->value = malloc(OID_SASL_NAME_LENGTH + 1);
+    if (sasl_name->value == NULL) {
+        *minor = ENOMEM;
+        return GSS_S_FAILURE;
+    }
+    sasl_name->length = OID_SASL_NAME_LENGTH;
+
+    status = oidToSaslName(minor, mech, (char *)sasl_name->value);
+    if (GSS_ERROR(status)) {
+        gss_release_buffer(&tmpMinor, sasl_name);
+        return status;
+    }
+
+    return GSS_S_COMPLETE;
+}
+
+OM_uint32 KRB5_CALLCONV gss_inquire_saslname_for_mech(
+    OM_uint32     *minor_status,
+    const gss_OID  desired_mech,
+    gss_buffer_t   sasl_mech_name,
+    gss_buffer_t   mech_name,
+    gss_buffer_t   mech_description)
+{
+    OM_uint32       status = GSS_S_BAD_MECH;
+    gss_mechanism   mech;
+
+    if (minor_status == NULL)
+        return GSS_S_CALL_INACCESSIBLE_WRITE;
+
+    *minor_status = 0;
+
+    if (sasl_mech_name != GSS_C_NO_BUFFER) {
+        sasl_mech_name->length = 0;
+        sasl_mech_name->value = NULL;
+    }
+
+    if (mech_name != GSS_C_NO_BUFFER) {
+        mech_name->length = 0;
+        mech_name->value = NULL;
+    }
+
+    if (mech_description != GSS_C_NO_BUFFER) {
+        mech_description->length = 0;
+        mech_description->value = NULL;
+    }
+
+    mech = gssint_get_mechanism(desired_mech);
+    if (mech != NULL && mech->gss_inquire_saslname_for_mech != NULL) {
+        status = mech->gss_inquire_saslname_for_mech(minor_status,
+                                                     desired_mech,
+                                                     sasl_mech_name,
+                                                     mech_name,
+                                                     mech_description);
+    }
+    if (status == GSS_S_BAD_MECH) {
+        if (sasl_mech_name != GSS_C_NO_BUFFER)
+            status = oidToSaslNameAlloc(minor_status, desired_mech,
+                                        sasl_mech_name);
+        else
+            status = GSS_S_COMPLETE;
+    }
+
+    return status;
+}
+
+OM_uint32 KRB5_CALLCONV gss_inquire_mech_for_saslname(
+    OM_uint32           *minor_status,
+    const gss_buffer_t   sasl_mech_name,
+    gss_OID             *mech_type)
+{
+    OM_uint32       status, tmpMinor;
+    gss_OID_set     mechSet = GSS_C_NO_OID_SET;
+    size_t          i;
+
+    if (minor_status == NULL)
+        return GSS_S_CALL_INACCESSIBLE_WRITE;
+
+    *minor_status = 0;
+
+    if (mech_type != NULL)
+        *mech_type = GSS_C_NO_OID;
+
+    status = gss_indicate_mechs(minor_status, &mechSet);
+    if (status != GSS_S_COMPLETE)
+        return status;
+
+    for (i = 0, status = GSS_S_BAD_MECH; i < mechSet->count; i++) {
+        gss_mechanism mech;
+        char mappedName[OID_SASL_NAME_LENGTH + 1];
+
+        mech = gssint_get_mechanism(&mechSet->elements[i]);
+        if (mech != NULL && mech->gss_inquire_mech_for_saslname != NULL) {
+            status = mech->gss_inquire_mech_for_saslname(minor_status,
+                                                         sasl_mech_name,
+                                                         mech_type);
+            if (status == GSS_S_COMPLETE)
+                break;
+        }
+        if (status == GSS_S_BAD_MECH &&
+            sasl_mech_name->length == OID_SASL_NAME_LENGTH &&
+            oidToSaslName(&tmpMinor, &mechSet->elements[i],
+                          mappedName) == GSS_S_COMPLETE &&
+            memcmp(sasl_mech_name->value, mappedName,
+                   OID_SASL_NAME_LENGTH) == 0) {
+            if (mech_type != NULL)
+                *mech_type = &mech->mech_type;
+            status = GSS_S_COMPLETE;
+            break;
+        }
+    }
+
+    gss_release_oid_set(&tmpMinor, &mechSet);
+
+    return status;
+}
index da427f4a67d0304aacad4e707e9f2b00bc9b21fd..63d89c771700fbbf6defed0e405be778d3dfc13f 100644 (file)
@@ -588,6 +588,31 @@ typedef struct gss_config {
            gss_cred_id_t,              /* cred_handle */
            const gss_OID_set           /* mech_set */
        /* */);
+
+       OM_uint32       (*gss_inquire_saslname_for_mech)
+       (
+           OM_uint32 *,                /* minor_status */
+           const gss_OID,              /* desired_mech */
+           gss_buffer_t,               /* sasl_mech_name */
+           gss_buffer_t,               /* mech_name */
+           gss_buffer_t                /* mech_description */
+       /* */);
+
+       OM_uint32       (*gss_inquire_mech_for_saslname)
+       (
+           OM_uint32 *,                /* minor_status */
+           const gss_buffer_t,         /* sasl_mech_name */
+           gss_OID *                   /* mech_type */
+       /* */);
+
+       OM_uint32       (*gss_inquire_attrs_for_mech)
+       (
+           OM_uint32 *,                /* minor_status */
+           gss_const_OID,              /* mech */
+           gss_OID_set *,              /* mech_attrs */
+           gss_OID_set *               /* known_mech_attrs */
+       /* */);
+
 } *gss_mechanism;
 
 /* This structure MUST NOT be used by any code outside libgss */
index e146508c5d751cc5d10ee98aa584771c4fe99e24..50f05678d62a4db74c29451276cf893d00235460 100644 (file)
@@ -565,6 +565,33 @@ spnego_gss_set_neg_mechs
        const gss_OID_set mech_list
 );
 
+OM_uint32
+spnego_gss_inquire_mech_for_saslname
+(
+       OM_uint32 *minor_status,
+       const gss_buffer_t sasl_mech_name,
+       gss_OID *mech_type
+);
+
+OM_uint32
+spnego_gss_inquire_saslname_for_mech
+(
+       OM_uint32 *minor_status,
+       const gss_OID desired_mech,
+       gss_buffer_t sasl_mech_name,
+       gss_buffer_t mech_name,
+       gss_buffer_t mech_description
+);
+
+OM_uint32
+spnego_gss_inquire_attrs_for_mech
+(
+       OM_uint32 *minor_status,
+       gss_const_OID mech,
+       gss_OID_set *mech_attrs,
+       gss_OID_set *known_mech_attrs
+);
+
 #ifdef __cplusplus
 }
 #endif
index 9c5a6140dd98edd1e39b22f79ea983f792068595..d8706af92715b09bde74042d688e412ae9592f99 100644 (file)
@@ -272,6 +272,9 @@ static struct gss_config spnego_mechanism =
        spnego_gss_release_any_name_mapping,
        spnego_gss_pseudo_random,
        spnego_gss_set_neg_mechs,
+       spnego_gss_inquire_saslname_for_mech,
+       spnego_gss_inquire_mech_for_saslname,
+       spnego_gss_inquire_attrs_for_mech,
 };
 
 static struct gss_config_ext spnego_mechanism_ext =
@@ -2257,7 +2260,6 @@ spnego_gss_set_cred_option(
        gss_cred_id_t mcred;
 
        mcred = (spcred == NULL) ? GSS_C_NO_CREDENTIAL : spcred->mcred;
-
        ret = gss_set_cred_option(minor_status,
                                  &mcred,
                                  desired_object,
@@ -2279,6 +2281,23 @@ spnego_gss_set_cred_option(
                *cred_handle = (gss_cred_id_t)spcred;
        }
 
+       if (ret == GSS_S_COMPLETE && spcred == NULL) {
+               /*
+                * If the mechanism allocated a new credential handle, then
+                * we need to wrap it up in an SPNEGO credential handle.
+                */
+
+               spcred = malloc(sizeof(spnego_gss_cred_id_rec));
+               if (spcred == NULL) {
+                       gss_release_cred(&tmp_minor_status, &mcred);
+                       *minor_status = ENOMEM;
+                       return (GSS_S_FAILURE);
+               }
+               spcred->mcred = mcred;
+               spcred->neg_mechs = GSS_C_NULL_OID_SET;
+               *cred_handle = (gss_cred_id_t)spcred;
+       }
+
        return (ret);
 }
 
@@ -2686,6 +2705,85 @@ spnego_gss_set_neg_mechs(OM_uint32 *minor_status,
        return (ret);
 }
 
+#define SPNEGO_SASL_NAME       "SPNEGO"
+#define SPNEGO_SASL_NAME_LEN   (sizeof(SPNEGO_SASL_NAME) - 1)
+
+OM_uint32
+spnego_gss_inquire_mech_for_saslname(OM_uint32 *minor_status,
+                                     const gss_buffer_t sasl_mech_name,
+                                     gss_OID *mech_type)
+{
+       if (sasl_mech_name->length == SPNEGO_SASL_NAME_LEN &&
+           memcmp(sasl_mech_name->value, SPNEGO_SASL_NAME,
+                  SPNEGO_SASL_NAME_LEN) == 0) {
+               if (mech_type != NULL)
+                       *mech_type = (gss_OID)gss_mech_spnego;
+               return (GSS_S_COMPLETE);
+       }
+
+       return (GSS_S_BAD_MECH);
+}
+
+OM_uint32
+spnego_gss_inquire_saslname_for_mech(OM_uint32 *minor_status,
+                                     const gss_OID desired_mech,
+                                     gss_buffer_t sasl_mech_name,
+                                     gss_buffer_t mech_name,
+                                     gss_buffer_t mech_description)
+{
+       *minor_status = 0;
+
+       if (!g_OID_equal(desired_mech, gss_mech_spnego))
+               return (GSS_S_BAD_MECH);
+
+       if (!g_make_string_buffer(SPNEGO_SASL_NAME, sasl_mech_name) ||
+           !g_make_string_buffer("spnego", mech_name) ||
+           !g_make_string_buffer("Simple and Protected GSS-API "
+                                 "Negotiation Mechanism", mech_description))
+               goto fail;
+
+       return (GSS_S_COMPLETE);
+
+fail:
+       *minor_status = ENOMEM;
+       return (GSS_S_FAILURE);
+}
+
+OM_uint32
+spnego_gss_inquire_attrs_for_mech(OM_uint32 *minor_status,
+                                 gss_const_OID mech,
+                                 gss_OID_set *mech_attrs,
+                                 gss_OID_set *known_mech_attrs)
+{
+       OM_uint32 major, tmpMinor;
+
+       /* known_mech_attrs is handled by mechglue */
+       *minor_status = 0;
+
+       if (mech_attrs == NULL)
+           return (GSS_S_COMPLETE);
+
+       major = gss_create_empty_oid_set(minor_status, mech_attrs);
+       if (GSS_ERROR(major))
+               goto cleanup;
+
+#define MA_SUPPORTED(ma)    do {                                       \
+               major = gss_add_oid_set_member(minor_status,            \
+                                              (gss_OID)ma, mech_attrs); \
+               if (GSS_ERROR(major))                                   \
+                       goto cleanup;                                   \
+       } while (0)
+
+       MA_SUPPORTED(GSS_C_MA_MECH_NEGO);
+       MA_SUPPORTED(GSS_C_MA_ITOK_FRAMED);
+
+cleanup:
+       if (GSS_ERROR(major))
+               gss_release_oid_set(&tmpMinor, mech_attrs);
+
+       return (major);
+}
+
 /*
  * We will release everything but the ctx_handle so that it
  * can be passed back to init/accept context. This routine should
index 2ba81de61f1657c01ba69220c221888286d44689..4002d001ef5f9f382bd98343cfb61310a3d232b0 100644 (file)
@@ -4,11 +4,11 @@ DEFINES = -DUSE_AUTOCONF_H
 PROG_LIBPATH=-L$(TOPLIBD)
 PROG_RPATH=$(KRB5_LIBDIR)
 
-SRCS= $(srcdir)/t_imp_name.c $(srcdir)/t_s4u.c $(srcdir)/t_namingexts.c $(srcdir)/t_gssexts.c
+SRCS= $(srcdir)/t_imp_name.c $(srcdir)/t_s4u.c $(srcdir)/t_namingexts.c $(srcdir)/t_gssexts.c $(srcdir)/t_saslname.c
 
-OBJS= t_imp_name.o t_s4u.o t_namingexts.o t_gssexts.o t_spnego.o
+OBJS= t_imp_name.o t_s4u.o t_namingexts.o t_gssexts.o t_spnego.o t_saslname.o
 
-all:: t_imp_name t_s4u t_namingexts t_gssexts t_spnego
+all:: t_imp_name t_s4u t_namingexts t_gssexts t_spnego t_saslname
 
 check-pytests:: t_spnego
        $(RUNPYTEST) $(srcdir)/t_gssapi.py $(PYTESTFLAGS)
@@ -23,7 +23,9 @@ t_gssexts: t_gssexts.o $(GSS_DEPLIBS) $(KRB5_BASE_DEPLIBS)
        $(CC_LINK) -o t_gssexts t_gssexts.o $(GSS_LIBS) $(KRB5_BASE_LIBS)
 t_spnego: t_spnego.o $(GSS_DEPLIBS) $(KRB5_BASE_DEPLIBS)
        $(CC_LINK) -o t_spnego t_spnego.o $(GSS_LIBS) $(KRB5_BASE_LIBS)
+t_saslname: t_saslname.o $(GSS_DEPLIBS) $(KRB5_BASE_DEPLIBS)
+       $(CC_LINK) -o t_saslname t_saslname.o $(GSS_LIBS) $(KRB5_BASE_LIBS)
 
 clean::
-       $(RM) t_imp_name t_s4u t_namingexts t_gssexts t_spnego
+       $(RM) t_imp_name t_s4u t_namingexts t_gssexts t_spnego t_saslname
 
diff --git a/src/tests/gssapi/t_saslname.c b/src/tests/gssapi/t_saslname.c
new file mode 100644 (file)
index 0000000..40384f7
--- /dev/null
@@ -0,0 +1,188 @@
+/* -*- mode: c; indent-tabs-mode: nil -*- */
+/*
+ * Copyright 2009  by the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * Export of this software from the United States of America may
+ *   require a specific license from the United States Government.
+ *   It is the responsibility of any person or organization contemplating
+ *   export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission.  Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose.  It is provided "as is" without express
+ * or implied warranty.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <gssapi/gssapi.h>
+#include <gssapi/gssapi_ext.h>
+
+static void
+displayStatus_1(char *m, OM_uint32 code, int type)
+{
+     OM_uint32 maj_stat, min_stat;
+     gss_buffer_desc msg;
+     OM_uint32 msg_ctx;
+
+     msg_ctx = 0;
+     while (1) {
+          maj_stat = gss_display_status(&min_stat, code,
+                                       type, GSS_C_NULL_OID,
+                                       &msg_ctx, &msg);
+          fprintf(stderr, "%s: %s\n", m, (char *)msg.value);
+          (void) gss_release_buffer(&min_stat, &msg);
+
+          if (!msg_ctx)
+               break;
+     }
+}
+
+static void
+displayStatus(char *msg, OM_uint32 maj_stat, OM_uint32 min_stat)
+{
+     displayStatus_1(msg, maj_stat, GSS_C_GSS_CODE);
+     displayStatus_1(msg, min_stat, GSS_C_MECH_CODE);
+}
+
+static
+OM_uint32 dumpMechAttrs(OM_uint32 *minor, gss_OID mech)
+{
+    OM_uint32 major, tmpMinor;
+    gss_OID_set mech_attrs = GSS_C_NO_OID_SET;
+    gss_OID_set known_attrs = GSS_C_NO_OID_SET;
+    size_t i;
+
+    major = gss_inquire_attrs_for_mech(minor, mech, &mech_attrs, &known_attrs);
+    if (GSS_ERROR(major)) {
+        displayStatus("gss_inquire_attrs_for_mech", major, *minor);
+        return major;
+    }
+
+    printf("Mech attrs:  ");
+
+    for (i = 0; i < mech_attrs->count; i++) {
+        gss_buffer_desc name = GSS_C_EMPTY_BUFFER;
+        gss_buffer_desc short_desc = GSS_C_EMPTY_BUFFER;
+        gss_buffer_desc long_desc = GSS_C_EMPTY_BUFFER;
+
+        major = gss_display_mech_attr(minor, &mech_attrs->elements[i],
+                                      &name, &short_desc, &long_desc);
+        if (GSS_ERROR(major)) {
+            displayStatus("gss_display_mech_attr", major, *minor);
+            continue;
+        }
+        printf("%.*s ", (int)name.length, (char *)name.value);
+        gss_release_buffer(minor, &name);
+        gss_release_buffer(minor, &short_desc);
+        gss_release_buffer(minor, &long_desc);
+    }
+    printf("\n");
+
+    printf("Known attrs: ");
+
+    for (i = 0; i < known_attrs->count; i++) {
+        gss_buffer_desc name = GSS_C_EMPTY_BUFFER;
+        gss_buffer_desc short_desc = GSS_C_EMPTY_BUFFER;
+        gss_buffer_desc long_desc = GSS_C_EMPTY_BUFFER;
+
+        major = gss_display_mech_attr(minor, &known_attrs->elements[i],
+                                      &name, &short_desc, &long_desc);
+        if (GSS_ERROR(major)) {
+            displayStatus("gss_display_mech_attr", major, *minor);
+            continue;
+        }
+        printf("%.*s ", (int)name.length, (char *)name.value);
+        gss_release_buffer(minor, &name);
+        gss_release_buffer(minor, &short_desc);
+        gss_release_buffer(minor, &long_desc);
+    }
+    printf("\n");
+
+    gss_release_oid_set(&tmpMinor, &mech_attrs);
+    gss_release_oid_set(&tmpMinor, &known_attrs);
+
+    return GSS_S_COMPLETE;
+}
+
+int main(int argc, char *argv[])
+{
+    gss_OID_set mechs;
+    OM_uint32 major, minor;
+    size_t i;
+
+    major = gss_indicate_mechs(&minor, &mechs);
+    if (GSS_ERROR(major)) {
+        displayStatus("gss_indicate_mechs", major, minor);
+        return major;
+    }
+
+    for (i = 0; i < mechs->count; i++) {
+        gss_buffer_desc oidstr = GSS_C_EMPTY_BUFFER;
+        gss_buffer_desc sasl_mech_name = GSS_C_EMPTY_BUFFER;
+        gss_buffer_desc mech_name = GSS_C_EMPTY_BUFFER;
+        gss_buffer_desc mech_description = GSS_C_EMPTY_BUFFER;
+        gss_OID oid = GSS_C_NO_OID;
+
+        major = gss_oid_to_str(&minor, &mechs->elements[i], &oidstr);
+        if (GSS_ERROR(major))
+            continue;
+
+        major = gss_inquire_saslname_for_mech(&minor, &mechs->elements[i],
+                                              &sasl_mech_name, &mech_name,
+                                              &mech_description);
+        if (GSS_ERROR(major)) {
+            gss_release_buffer(&minor, &oidstr);
+            continue;
+        }
+
+        printf("-------------------------------------------------------------"
+               "-----------------\n");
+        printf("OID        : %.*s\n", (int)oidstr.length,
+               (char *)oidstr.value);
+        printf("SASL mech  : %.*s\n", (int)sasl_mech_name.length,
+               (char *)sasl_mech_name.value);
+        printf("Mech name  : %.*s\n", (int)mech_name.length,
+               (char *)mech_name.value);
+        printf("Mech desc  : %.*s\n", (int)mech_description.length,
+               (char *)mech_description.value);
+        dumpMechAttrs(&minor, &mechs->elements[i]);
+        printf("-------------------------------------------------------------"
+               "-----------------\n");
+
+        if (GSS_ERROR(gss_inquire_mech_for_saslname(&minor, &sasl_mech_name,
+                                                    &oid))) {
+            displayStatus("gss_inquire_mech_for_saslname", major, minor);
+        } else if (oid == GSS_C_NO_OID ||
+            (oid->length != mechs->elements[i].length &&
+             memcmp(oid->elements, mechs->elements[i].elements,
+                    oid->length) != 0)) {
+            gss_release_buffer(&minor, &oidstr);
+            (void) gss_oid_to_str(&minor, oid, &oidstr);
+            fprintf(stderr, "Got different OID %.*s for mechanism %.*s\n",
+                    (int)oidstr.length, (char *)oidstr.value,
+                    (int)sasl_mech_name.length, (char *)sasl_mech_name.value);
+        }
+        gss_release_buffer(&minor, &oidstr);
+        gss_release_buffer(&minor, &sasl_mech_name);
+        gss_release_buffer(&minor, &mech_name);
+        gss_release_buffer(&minor, &mech_description);
+    }
+
+    gss_release_oid_set(&minor, &mechs);
+
+    return GSS_ERROR(major) ? 1 : 0;
+}