remap mechanism-specific status codes in mechglue/spnego
authorKen Raeburn <raeburn@mit.edu>
Thu, 16 Aug 2007 22:55:06 +0000 (22:55 +0000)
committerKen Raeburn <raeburn@mit.edu>
Thu, 16 Aug 2007 22:55:06 +0000 (22:55 +0000)
This patch creates a mapping in the mechglue/spnego code to modify
mechanism status codes when passing them back to the application, so
that mechglue's display_status dispatcher can determine the correct
mechanism to dispatch to.

This is part of the "get enhanced error messages from gssapi
applications" project; ticket 5590 has updates to the Kerberos 5
mechanism to extract enhanced error messages (when there are any) from
the Kerberos library.

util/gen.pl, util/t_*.pm: New code generation script and templates.

lib/gssapi/generic: Add a new, global mapping that enumerates the
{mechOID,status} pairs as they're seen, allowing a magic mechOID value
to indicate com_err error codes from mechglue and spnego, and
reserving status code 0 for unknown errors.  Preload the Kerberos
"wrong principal" error code once for each mechanism OID used for
Kerberos, so the entries get fixed positions (1-3) in the table.

lib/gssapi/gss_libinit.c: Call the initializer and destructor
functions.

lib/gssapi/mechglue, lib/gssapi/spnego: Enter all mechanism-generated
or locally-generated status codes into the mapping table, and return
the table index to the application.  Do the reverse in display_status,
to get the messages from the mechanism..

lib/rpc: Define new function gssrpcint_printf to use for debugging
instead of printf, to redirect output away from dejagnu; add a couple
more debugging calls.  Check for minor status codes 1-3 now instead of
KRB5KRB_AP_WRONG_PRINC.

tests/dejagnu/krb-standalone/gssftp.exp: Test getting more detailed
error messages back, by having the ftp client attempt to authenticate
to a non-existent service, and examining the error message for the
service principal name.

ticket: new

git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@19831 dc483132-0cff-0310-8789-dd5450dbe970

43 files changed:
src/lib/gssapi/generic/Makefile.in
src/lib/gssapi/generic/gssapiP_generic.h
src/lib/gssapi/generic/maptest.c [new file with mode: 0644]
src/lib/gssapi/generic/util_errmap.c [new file with mode: 0644]
src/lib/gssapi/gss_libinit.c
src/lib/gssapi/mechglue/g_accept_sec_context.c
src/lib/gssapi/mechglue/g_acquire_cred.c
src/lib/gssapi/mechglue/g_canon_name.c
src/lib/gssapi/mechglue/g_compare_name.c
src/lib/gssapi/mechglue/g_context_time.c
src/lib/gssapi/mechglue/g_delete_sec_context.c
src/lib/gssapi/mechglue/g_dsp_name.c
src/lib/gssapi/mechglue/g_dsp_status.c
src/lib/gssapi/mechglue/g_dup_name.c
src/lib/gssapi/mechglue/g_exp_sec_context.c
src/lib/gssapi/mechglue/g_glue.c
src/lib/gssapi/mechglue/g_imp_name.c
src/lib/gssapi/mechglue/g_imp_sec_context.c
src/lib/gssapi/mechglue/g_init_sec_context.c
src/lib/gssapi/mechglue/g_initialize.c
src/lib/gssapi/mechglue/g_inq_context.c
src/lib/gssapi/mechglue/g_inq_cred.c
src/lib/gssapi/mechglue/g_inq_names.c
src/lib/gssapi/mechglue/g_mechname.c
src/lib/gssapi/mechglue/g_oid_ops.c
src/lib/gssapi/mechglue/g_process_context.c
src/lib/gssapi/mechglue/g_rel_cred.c
src/lib/gssapi/mechglue/g_seal.c
src/lib/gssapi/mechglue/g_sign.c
src/lib/gssapi/mechglue/g_store_cred.c
src/lib/gssapi/mechglue/g_unseal.c
src/lib/gssapi/mechglue/g_verify.c
src/lib/gssapi/mechglue/mglueP.h
src/lib/gssapi/mechglue/oid_ops.c
src/lib/gssapi/spnego/spnego_mech.c
src/lib/rpc/auth_gssapi_misc.c
src/lib/rpc/svc_auth_gssapi.c
src/tests/dejagnu/krb-standalone/gssftp.exp
src/util/gen.pl [new file with mode: 0644]
src/util/t_array.pm [new file with mode: 0644]
src/util/t_enum.pm [new file with mode: 0644]
src/util/t_template.pm [new file with mode: 0644]
src/util/t_tsenum.pm [new file with mode: 0644]

index e63be6b7870ff985e21e040134b7863bf942e7f9..6d66396be5c1046800de3aaa620f971ca1f332c4 100644 (file)
@@ -70,6 +70,7 @@ SRCS = \
        $(srcdir)/rel_buffer.c \
        $(srcdir)/rel_oid_set.c \
        $(srcdir)/util_buffer.c \
+       $(srcdir)/util_errmap.c \
        $(srcdir)/util_ordering.c \
        $(srcdir)/util_set.c \
        $(srcdir)/util_token.c \
@@ -83,6 +84,7 @@ OBJS = \
        $(OUTPRE)rel_buffer.$(OBJEXT) \
        $(OUTPRE)rel_oid_set.$(OBJEXT) \
        $(OUTPRE)util_buffer.$(OBJEXT) \
+       $(OUTPRE)util_errmap.$(OBJEXT) \
        $(OUTPRE)util_ordering.$(OBJEXT) \
        $(OUTPRE)util_set.$(OBJEXT) \
        $(OUTPRE)util_token.$(OBJEXT) \
@@ -96,6 +98,7 @@ STLIBOBJS = \
        rel_buffer.o \
        rel_oid_set.o \
        util_buffer.o \
+       util_errmap.o \
        util_ordering.o \
        util_set.o \
        util_token.o \
@@ -110,6 +113,19 @@ $(OBJS): $(EXPORTED_HEADERS) $(ETHDRS)
 all-unix:: $(EXPORTED_HEADERS) $(ETHDRS) $(HDRS)
 all-unix:: all-libobjs
 
+errmap.h: $(SRCTOP)/util/gen.pl $(SRCTOP)/util/t_array.pm \
+               $(SRCTOP)/util/t_enum.pm $(SRCTOP)/util/t_tsenum.pm
+       $(PERL) -w -I$(SRCTOP)/util $(SRCTOP)/util/gen.pl tsenum errmap.h \
+               NAME=mecherrmap TYPE="struct mecherror" COMPARE=mecherror_cmp \
+               COPY=mecherror_copy PRINT=mecherror_print
+
+maptest.h: $(SRCTOP)/util/gen.pl $(SRCTOP)/util/t_array.pm \
+               $(SRCTOP)/util/t_enum.pm $(SRCTOP)/util/t_tsenum.pm
+       $(PERL) -w -I$(SRCTOP)/util $(SRCTOP)/util/gen.pl tsenum maptest.h \
+               NAME=foo TYPE=elt COMPARE=eltcmp COPY=eltcp PRINT=eltprt
+maptest.o: maptest.c maptest.h
+maptest: maptest.o
+       $(CC_LINK) -o maptest maptest.o $(SUPPORT_LIB)
 
 ##DOS##LIBOBJS = $(OBJS)
 
@@ -180,6 +196,11 @@ util_buffer.so util_buffer.po $(OUTPRE)util_buffer.$(OBJEXT): \
   $(COM_ERR_DEPS) $(SRCTOP)/include/k5-platform.h $(SRCTOP)/include/k5-thread.h \
   gssapiP_generic.h gssapi_err_generic.h gssapi_generic.h \
   util_buffer.c
+util_errmap.so util_errmap.po $(OUTPRE)util_errmap.$(OBJEXT): \
+  $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \
+  $(COM_ERR_DEPS) $(SRCTOP)/include/k5-platform.h $(SRCTOP)/include/k5-thread.h \
+  errmap.h gssapiP_generic.h gssapi_err_generic.h gssapi_generic.h \
+  util_errmap.c
 util_ordering.so util_ordering.po $(OUTPRE)util_ordering.$(OBJEXT): \
   $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \
   $(COM_ERR_DEPS) $(SRCTOP)/include/k5-platform.h $(SRCTOP)/include/k5-thread.h \
index e34055b6ce7e1971462a0d6dd5dca3f387e54587..1ec5417ba8b7c705ad7ec0013ec42c005cefb6cd 100644 (file)
@@ -255,4 +255,9 @@ OM_uint32 generic_gss_str_to_oid
            gss_OID *           /* oid */
           );
 
+OM_uint32 gssint_mecherrmap_map(OM_uint32 minor, const gss_OID_desc *oid);
+int gssint_mecherrmap_get(OM_uint32 minor, gss_OID mech_oid,
+                         OM_uint32 *mech_minor);
+OM_uint32 gssint_mecherrmap_map_errcode(OM_uint32 errcode);
+
 #endif /* _GSSAPIP_GENERIC_H_ */
diff --git a/src/lib/gssapi/generic/maptest.c b/src/lib/gssapi/generic/maptest.c
new file mode 100644 (file)
index 0000000..b884eed
--- /dev/null
@@ -0,0 +1,54 @@
+#include <stdio.h>
+#include <stdarg.h>
+#include <assert.h>
+
+typedef struct { int a, b; } elt;
+static int eltcp(elt *dest, elt src)
+{
+    *dest = src;
+    return 0;
+}
+static int eltcmp(elt left, elt right)
+{
+    if (left.a < right.a)
+       return -1;
+    if (left.a > right.a)
+       return 1;
+    if (left.b < right.b)
+       return -1;
+    if (left.b > right.b)
+       return 1;
+    return 0;
+}
+static void eltprt(elt v, FILE *f)
+{
+    fprintf(f, "{%d,%d}", v.a, v.b);
+}
+
+#include "maptest.h"
+
+foo foo1;
+
+int main ()
+{
+    int err;
+    elt v1 = { 1, 2 }, v2 = { 3, 4 };
+    long idx;
+    int added;
+
+    err = foo_init(&foo1);
+    assert(err == 0);
+    err = foo_find_or_append(&foo1, v1, &idx, &added);
+    assert(err == 0);
+    printf("v1: idx=%ld added=%d\n", idx, added);
+    err = foo_find_or_append(&foo1, v2, &idx, &added);
+    assert(err == 0);
+    printf("v2: idx=%ld added=%d\n", idx, added);
+    err = foo_find_or_append(&foo1, v2, &idx, &added);
+    assert(err == 0);
+    printf("v2: idx=%ld added=%d\n", idx, added);
+    err = foo_find_or_append(&foo1, v1, &idx, &added);
+    assert(err == 0);
+    printf("v1: idx=%ld added=%d\n", idx, added);
+    return 0;
+}
diff --git a/src/lib/gssapi/generic/util_errmap.c b/src/lib/gssapi/generic/util_errmap.c
new file mode 100644 (file)
index 0000000..24975cc
--- /dev/null
@@ -0,0 +1,190 @@
+#include "gssapiP_generic.h"
+#include <string.h>
+#include <unistd.h>
+
+/* The mapping table is 0-based, but let's export codes that are
+   1-based, keeping 0 for errors or unknown errors.
+
+   The elements in the mapping table currently have separate copies of
+   each OID stored.  This is a bit wasteful, but we are assuming the
+   table isn't likely to grow very large.  */
+
+struct mecherror {
+    gss_OID_desc mech;
+    OM_uint32 code;
+};
+
+static inline int
+mecherror_cmp(struct mecherror m1, struct mecherror m2)
+{
+    if (m1.code < m2.code)
+       return -1;
+    if (m1.code > m2.code)
+       return 1;
+    if (m1.mech.length < m2.mech.length)
+       return -1;
+    if (m1.mech.length > m2.mech.length)
+       return 1;
+    if (m1.mech.length == 0)
+       return 0;
+    return memcmp(m1.mech.elements, m2.mech.elements, m1.mech.length);
+}
+
+static inline int
+mecherror_copy(struct mecherror *dest, struct mecherror src)
+{
+    *dest = src;
+    if (src.mech.length) {
+       dest->mech.elements = malloc(src.mech.length);
+       if (dest->mech.elements == NULL)
+           return ENOMEM;
+    }
+    memcpy(dest->mech.elements, src.mech.elements, src.mech.length);
+    return 0;
+}
+
+static void
+mecherror_print(struct mecherror value, FILE *f)
+{
+    OM_uint32 minor;
+    gss_buffer_desc str;
+    static const struct {
+       const char *oidstr, *name;
+    } mechnames[] = {
+       { "{ 1 2 840 113554 1 2 2 }", "krb5-new" },
+       { "{ 1 3 5 1 5 2 }", "krb5-old" },
+       { "{ 1 2 840 48018 1 2 2 }", "krb5-microsoft" },
+       { "{ 1 3 6 1 5 5 2 }", "spnego" },
+    };
+    int i;
+
+    fprintf(f, "%lu@", (unsigned long) value.code);
+
+    if (value.mech.length == 0) {
+       fprintf(f, "(com_err)");
+       return;
+    }
+    if (generic_gss_oid_to_str(&minor, &value.mech, &str)) {
+       fprintf(f, "(error in conversion)");
+       return;
+    }
+    /* Note: generic_gss_oid_to_str returns a null-terminated string.  */
+    for (i = 0; i < sizeof(mechnames)/sizeof(mechnames[0]); i++) {
+       if (!strcmp(str.value, mechnames[i].oidstr) && mechnames[i].name != 0) {
+           fprintf(f, "%s", mechnames[i].name);
+           break;
+       }
+    }
+    if (i == sizeof(mechnames)/sizeof(mechnames[0]))
+       fprintf(f, "%s", (char *) str.value);
+    generic_gss_release_buffer(&minor, &str);
+}
+
+#include "errmap.h"
+#include "krb5.h"              /* for KRB5KRB_AP_WRONG_PRINC */
+
+static mecherrmap m;
+
+int gssint_mecherrmap_init(void)
+{
+    int err;
+    OM_uint32 n;
+
+    err = mecherrmap_init(&m);
+    if (err)
+       return err;
+
+    /* This is *so* gross.
+
+       The RPC code depends on being able to recognize the "wrong
+       principal" minor status return from the Kerberos mechanism.
+       But a totally generic enumeration of status codes as they come
+       up makes that impossible.  So "register" that status code
+       early, and always with the same value.
+
+       Of course, to make things worse, we're treating each mechanism
+       OID separately, and there are three for Kerberos.  */
+    {
+       /* Declare here to avoid including header files not generated
+          yet.  */
+       extern const gss_OID_desc *const gss_mech_krb5;
+       extern const gss_OID_desc *const gss_mech_krb5_old;
+       extern const gss_OID_desc *const gss_mech_krb5_wrong;
+
+       const OM_uint32 wrong_princ = (OM_uint32) KRB5KRB_AP_WRONG_PRINC;
+
+       n = gssint_mecherrmap_map(wrong_princ, gss_mech_krb5);
+       if (n <= 0)
+           return ENOMEM;
+       n = gssint_mecherrmap_map(wrong_princ, gss_mech_krb5_old);
+       if (n <= 0)
+           return ENOMEM;
+       n = gssint_mecherrmap_map(wrong_princ, gss_mech_krb5_wrong);
+       if (n <= 0)
+           return ENOMEM;
+    }
+
+    return 0;
+}
+
+/* Currently the enumeration template doesn't handle freeing
+   element storage when destroying the collection.  */
+static int free_one(size_t i, struct mecherror value, void *p)
+{
+    if (value.mech.length && value.mech.elements)
+       free(value.mech.elements);
+    return 0;
+}
+
+void gssint_mecherrmap_destroy(void)
+{
+    mecherrmap_foreach(&m, free_one, NULL);
+    mecherrmap_destroy(&m);
+}
+
+OM_uint32 gssint_mecherrmap_map(OM_uint32 minor, const gss_OID_desc * oid)
+{
+    struct mecherror me;
+    int err, added;
+    long idx;
+
+    me.code = minor;
+    me.mech = *oid;
+    err = mecherrmap_find_or_append(&m, me, &idx, &added);
+    if (err) {
+       return 0;
+    }
+    return idx+1;
+}
+
+static gss_OID_desc no_oid = { 0, 0 };
+OM_uint32 gssint_mecherrmap_map_errcode(OM_uint32 errcode)
+{
+    return gssint_mecherrmap_map(errcode, &no_oid);
+}
+
+int gssint_mecherrmap_get(OM_uint32 minor, gss_OID mech_oid,
+                         OM_uint32 *mech_minor)
+{
+    struct mecherror me;
+    int err;
+    long size;
+
+    if (minor == 0) {
+       return EINVAL;
+    }
+    err = mecherrmap_size(&m, &size);
+    if (err) {
+       return err;
+    }
+    if (minor > size) {
+       return EINVAL;
+    }
+    err = mecherrmap_get(&m, minor-1, &me);
+    if (err) {
+       return err;
+    }
+    *mech_oid = me.mech;
+    *mech_minor = me.code;
+    return 0;
+}
index b96eb9e7aa99b244b4ff2515d35f6cfffe8896d5..bb90857132358f4f331f300f349b3e70a40313bc 100644 (file)
@@ -44,6 +44,9 @@ int gssint_lib_init(void)
                          krb5_gss_delete_error_info);
     if (err)
        return err;
+    err = gssint_mecherrmap_init();
+    if (err)
+       return err;
 #ifndef _WIN32
     err = k5_mutex_finish_init(&kg_kdc_flag_mutex);
     if (err)
@@ -74,6 +77,7 @@ void gssint_lib_fini(void)
     k5_mutex_destroy(&kg_kdc_flag_mutex);
 #endif
     k5_mutex_destroy(&gssint_krb5_keytab_lock);
+    gssint_mecherrmap_destroy();
     gssint_mechglue_fini();
 }
 
index 0e8506a49de3aef8f1bf05a93d2f0bd5f0cdeb16..9b5562967460f65a98ac6265c3c64f60e3535aad 100644 (file)
@@ -211,8 +211,10 @@ gss_cred_id_t *            d_cred;
                return GSS_S_CONTINUE_NEEDED;
            
            /* if the call failed, return with failure */
-           if (status != GSS_S_COMPLETE)
+           if (status != GSS_S_COMPLETE) {
+               map_error(minor_status, mech);
                goto error_out;
+           }
 
            /*
             * if src_name is non-NULL,
@@ -227,6 +229,7 @@ gss_cred_id_t *             d_cred;
                       internal_name, &tmp_src_name);
                if (temp_status != GSS_S_COMPLETE) {
                    *minor_status = temp_minor_status;
+                   map_error(minor_status, mech);
                    if (output_token->length)
                        (void) gss_release_buffer(&temp_minor_status,
                                                  output_token);
@@ -297,6 +300,8 @@ gss_cred_id_t *             d_cred;
                                                    &d_u_cred->auxinfo.time_rec,
                                                    &d_u_cred->auxinfo.cred_usage,
                                                    NULL);
+                   if (status != GSS_S_COMPLETE)
+                       map_error(minor_status, mech);
                }
 
                if (internal_name != NULL) {
@@ -305,6 +310,7 @@ gss_cred_id_t *             d_cred;
                        internal_name, &tmp_src_name);
                    if (temp_status != GSS_S_COMPLETE) {
                        *minor_status = temp_minor_status;
+                       map_error(minor_status, mech);
                        if (output_token->length)
                            (void) gss_release_buffer(
                                &temp_minor_status,
index bd5bef8ea84cb2e17af11a1fcbdcf1ff9c8837c2..6d63e5b8f2253372aaf48a539c597ad320bafcd8 100644 (file)
@@ -384,8 +384,10 @@ gss_add_cred(minor_status, input_cred_handle,
                                    GSS_C_NULL_OID_SET, cred_usage,
                                    &cred, NULL, &time_rec);
 
-    if (status != GSS_S_COMPLETE)
+    if (status != GSS_S_COMPLETE) {
+       map_error(minor_status, mech);
        goto errout;
+    }
 
     /* may need to set credential auxinfo strucutre */
     if (union_cred->auxinfo.creation_time == 0) {
index 9f72055b3850ae708add86b32b46992ce8861e91..3d371c0e0422155f2f6efab33bb2e4b6fdf12bde 100644 (file)
@@ -104,9 +104,13 @@ gss_name_t *output_name;
                        goto allocation_failure;
 
                if (in_union->name_type != GSS_C_NULL_OID) {
-                       if ((major_status = generic_gss_copy_oid(minor_status,
-                               in_union->name_type, &out_union->name_type)))
+                   major_status = generic_gss_copy_oid(minor_status,
+                                                       in_union->name_type,
+                                                       &out_union->name_type);
+                   if (major_status) {
+                       map_errcode(minor_status);
                        goto allocation_failure;
+                   }
                }
 
        }
@@ -130,8 +134,10 @@ gss_name_t *output_name;
 
        /* now let's create the new mech name */
        if ((major_status = generic_gss_copy_oid(minor_status, mech_type,
-                                               &dest_union->mech_type)))
-               goto allocation_failure;
+                                                &dest_union->mech_type))) {
+           map_errcode(minor_status);
+           goto allocation_failure;
+       }
 
        if ((major_status =
                gssint_import_internal_name(minor_status, mech_type,
index 7fb5cc9cfca4565f6be65828895c866422b362f2..40f4648efdd2c81a6a8aca47463709133886c39d 100644 (file)
@@ -114,10 +114,13 @@ int *                     name_equal;
        if ((union_name1->mech_name == 0) || (union_name2->mech_name == 0))
            /* should never happen */
            return (GSS_S_BAD_NAME);
-       return (mech->gss_compare_name(mech->context, minor_status,
-                                      union_name1->mech_name,
-                                      union_name2->mech_name, name_equal));
-       
+       major_status = mech->gss_compare_name(mech->context, minor_status,
+                                             union_name1->mech_name,
+                                             union_name2->mech_name,
+                                             name_equal);
+       if (major_status != GSS_S_COMPLETE)
+           map_error(minor_status, mech);
+       return major_status;
     }
 
     /*
@@ -190,6 +193,8 @@ int *                       name_equal;
     major_status = mech->gss_compare_name(mech->context, minor_status,
                                          union_name1->mech_name,
                                          internal_name, name_equal);
+    if (major_status != GSS_S_COMPLETE)
+       map_error(minor_status, mech);
     gssint_release_internal_name(&temp_minor, union_name1->mech_type,
                                &internal_name);
     return (major_status);
index 5ce6b56d86bf1c0741fcc0b12674640caacb0b04..866405729b681c4b96d47a42603586f681ae7de5 100644 (file)
@@ -62,13 +62,15 @@ OM_uint32 *         time_rec;
     
     if (mech) {
 
-       if (mech->gss_context_time)
+       if (mech->gss_context_time) {
            status = mech->gss_context_time(
                                            mech->context,
                                            minor_status,
                                            ctx->internal_ctx_id,
                                            time_rec);
-       else
+           if (status != GSS_S_COMPLETE)
+               map_error(minor_status, mech);
+       } else
            status = GSS_S_UNAVAILABLE;
 
        return(status);
index bf2e9010fdee0ee62f3d6a0bfcb5a241f83eb633..fdaf2c310de953f7aa0d782ae0a0b2d35ee182cc 100644 (file)
@@ -91,13 +91,15 @@ gss_buffer_t                output_token;
     
     if (mech) {
 
-       if (mech->gss_delete_sec_context)
+       if (mech->gss_delete_sec_context) {
            status = mech->gss_delete_sec_context(
                                                  mech->context,
                                                  minor_status,
                                                  &ctx->internal_ctx_id,
                                                  output_token);
-       else
+           if (status != GSS_S_COMPLETE)
+               map_error(minor_status, mech);
+       } else
            status = GSS_S_UNAVAILABLE;
 
        /* now free up the space for the union context structure */
index 7db90584d83ca4f324ad8be3a7537588d07739a1..7efd583f8fc0435adf21742a704806a1e3c5679c 100644 (file)
@@ -113,8 +113,10 @@ gss_OID *          output_name_type;
        major_status = generic_gss_copy_oid(minor_status,
                                            union_name->name_type,
                                            output_name_type);
-       if (major_status != GSS_S_COMPLETE)
+       if (major_status != GSS_S_COMPLETE) {
+           map_errcode(minor_status);
            return (major_status);
+       }
     }
 
     if ((output_name_buffer->value =
index 519d4a4a15a1eee4206b5a2022f097a3ea43bbc6..881bd7669774f121c70bf5c9e455c5b6009a26a5 100644 (file)
@@ -79,15 +79,60 @@ gss_buffer_t                status_string;
      * call it.
      */
 
+    /* In this version, we only handle status codes that have been
+       mapped to a flat numbering space.  Look up the value we got
+       passed.  If it's not found, complain.  */
+    if (status_value == 0) {
+       status_string->value = strdup("Unknown error");
+       if (status_string->value == NULL) {
+           *minor_status = ENOMEM;
+           map_errcode(minor_status);
+           return GSS_S_FAILURE;
+       }
+       status_string->length = strlen(status_string->value);
+       *message_context = 0;
+       *minor_status = 0;
+       return GSS_S_COMPLETE;
+    }
+    {
+       int err;
+       gss_OID_desc m_oid = { 0, 0 };
+       OM_uint32 m_status = 0, status;
+
+       err = gssint_mecherrmap_get(status_value, &m_oid, &m_status);
+       if (err) {
+           *minor_status = err;
+           map_errcode(minor_status);
+           return GSS_S_FAILURE;
+       }
+       if (m_oid.length == 0) {
+           /* Magic flag for com_err values.  */
+           status = g_display_com_err_status(minor_status, m_status, status_string);
+           if (status != GSS_S_COMPLETE)
+               map_errcode(minor_status);
+           return status;
+       }
+       mech_type = &m_oid;
+       status_value = m_status;
+    }
+
     mech = gssint_get_mechanism (mech_type);
 
     if (mech && mech->gss_display_status) {
+       OM_uint32 r;
+
        if (mech_type == GSS_C_NULL_OID)
            mech_type = &mech->mech_type;
 
-       return (mech->gss_display_status(mech->context, minor_status,
-                                        status_value, status_type, mech_type,
-                                        message_context, status_string));
+       r = mech->gss_display_status(mech->context, minor_status,
+                                    status_value, status_type, mech_type,
+                                    message_context, status_string);
+       /* How's this for weird?  If we get an error returning the
+          mechanism-specific error code, we save away the
+          mechanism-specific error code describing the error.  */
+       if (r != GSS_S_COMPLETE)
+           map_error(minor_status, mech);
+       return r;
     }
 
     if (!mech)
index 1d37be9e17c2ea172e1d329ec8c121540566f90b..9312de761ff0e27ef38ed966c480771d8d9e216e 100644 (file)
@@ -90,8 +90,10 @@ gss_name_t *dest_name;
                major_status = generic_gss_copy_oid(minor_status,
                                                src_union->name_type,
                                                &dest_union->name_type);
-               if (major_status != GSS_S_COMPLETE)
-                       goto allocation_failure;
+               if (major_status != GSS_S_COMPLETE) {
+                   map_errcode(minor_status);
+                   goto allocation_failure;
+               }
        }
 
        /*
@@ -101,8 +103,10 @@ gss_name_t *dest_name;
                major_status = generic_gss_copy_oid(minor_status,
                                                        src_union->mech_type,
                                                        &dest_union->mech_type);
-               if (major_status != GSS_S_COMPLETE)
-                       goto allocation_failure;
+               if (major_status != GSS_S_COMPLETE) {
+                   map_errcode(minor_status);
+                   goto allocation_failure;
+               }
 
                major_status = gssint_import_internal_name(minor_status,
                                                        dest_union->mech_type,
index 1490a2b373081d047f30ccac38cd96d0641e86f1..28e25b325a37cbb5525e1fcdcf5e6c943843c802 100644 (file)
@@ -102,8 +102,10 @@ gss_buffer_t               interprocess_token;
     
     status = mech->gss_export_sec_context(mech->context, minor_status,
                                          &ctx->internal_ctx_id, &token);
-    if (status != GSS_S_COMPLETE)
+    if (status != GSS_S_COMPLETE) {
+       map_error(minor_status, mech);
        return (status);
+    }
 
     length = token.length + 4 + ctx->mech_type->length;
     interprocess_token->length = length;
index 1331862dc9613cf81fc3f0f7a4ecf1aabd566f84..41759468f431a7e0d314a4d1092524ceecbfe7de 100644 (file)
@@ -263,14 +263,16 @@ gss_name_t        *internal_name;
 
     mech = gssint_get_mechanism (mech_type);
     if (mech) {
-       if (mech->gss_import_name)
+       if (mech->gss_import_name) {
            status = mech->gss_import_name (
                                            mech->context,
                                            minor_status,
                                            union_name->external_name,
                                            union_name->name_type,
                                            internal_name);
-       else
+           if (status != GSS_S_COMPLETE)
+               map_error(minor_status, mech);
+       } else
            status = GSS_S_UNAVAILABLE;
 
        return (status);
@@ -301,11 +303,15 @@ OM_uint32 gssint_export_internal_name(minor_status, mech_type,
     if (!mech)
        return (GSS_S_BAD_MECH);
 
-    if (mech->gss_export_name)
-       return (mech->gss_export_name(mech->context,
-                                     minor_status,
-                                     internal_name,
-                                     name_buf));
+    if (mech->gss_export_name) {
+       status = mech->gss_export_name(mech->context,
+                                      minor_status,
+                                      internal_name,
+                                      name_buf);
+       if (status != GSS_S_COMPLETE)
+           map_error(minor_status, mech);
+       return status;
+    }
 
     /*
      * if we are here it is because the mechanism does not provide
@@ -339,8 +345,10 @@ OM_uint32 gssint_export_internal_name(minor_status, mech_type,
                                         internal_name,
                                         &dispName,
                                         &nameOid))
-       != GSS_S_COMPLETE)
+       != GSS_S_COMPLETE) {
+       map_error(minor_status, mech);
        return (status);
+    }
 
     /* determine the size of the buffer needed */
     mechOidDERLen = gssint_der_length_size(mech_type->length);
@@ -409,14 +417,16 @@ gss_OID           *name_type;
 
     mech = gssint_get_mechanism (mech_type);
     if (mech) {
-       if (mech->gss_display_name)
+       if (mech->gss_display_name) {
            status = mech->gss_display_name (
                                             mech->context,
                                             minor_status,
                                             internal_name,
                                             external_name,
                                             name_type);
-       else
+           if (status != GSS_S_COMPLETE)
+               map_error(minor_status, mech);
+       } else
            status = GSS_S_UNAVAILABLE;
 
        return (status);
@@ -435,12 +445,14 @@ gss_name_t        *internal_name;
 
     mech = gssint_get_mechanism (mech_type);
     if (mech) {
-       if (mech->gss_release_name)
+       if (mech->gss_release_name) {
            status = mech->gss_release_name (
                                             mech->context,
                                             minor_status,
                                             internal_name);
-       else
+           if (status != GSS_S_COMPLETE)
+               map_error(minor_status, mech);
+       } else
            status = GSS_S_UNAVAILABLE;
 
        return (status);
@@ -467,7 +479,10 @@ OM_uint32 gssint_convert_name_to_union_name(minor_status, mech,
 
     union_name = (gss_union_name_t) malloc (sizeof(gss_union_name_desc));
     if (!union_name) {
-           goto allocation_failure;
+       major_status = GSS_S_FAILURE;
+       *minor_status = ENOMEM;
+       map_errcode(minor_status);
+       goto allocation_failure;
     }
     union_name->mech_type = 0;
     union_name->mech_name = internal_name;
@@ -476,8 +491,10 @@ OM_uint32 gssint_convert_name_to_union_name(minor_status, mech,
 
     major_status = generic_gss_copy_oid(minor_status, &mech->mech_type,
                                        &union_name->mech_type);
-    if (major_status != GSS_S_COMPLETE)
+    if (major_status != GSS_S_COMPLETE) {
+       map_errcode(minor_status);
        goto allocation_failure;
+    }
 
     union_name->external_name =
        (gss_buffer_t) malloc(sizeof(gss_buffer_desc));
@@ -489,8 +506,10 @@ OM_uint32 gssint_convert_name_to_union_name(minor_status, mech,
                                          internal_name,
                                          union_name->external_name,
                                          &union_name->name_type);
-    if (major_status != GSS_S_COMPLETE)
+    if (major_status != GSS_S_COMPLETE) {
+       map_error(minor_status, mech);
        goto allocation_failure;
+    }
 
     union_name->loopback = union_name;
     *external_name = /*(gss_name_t) CHECK */union_name;
index fa7aa8d13b490761cca4f7e4119f364cd87cbd0f..c7864454285822caf574bb2380ad2dbc8ea494d8 100644 (file)
@@ -128,8 +128,10 @@ gss_name_t *               output_name;
        major_status = generic_gss_copy_oid(minor_status,
                                            input_name_type,
                                            &union_name->name_type);
-       if (major_status != GSS_S_COMPLETE)
+       if (major_status != GSS_S_COMPLETE) {
+           map_errcode(minor_status);
            goto allocation_failure;
+       }
     }
 
     /*
@@ -250,13 +252,16 @@ importExportName(minor, unionName)
      * have created it.
      */
     if (mech->gss_export_name) {
-       if ((major = mech->gss_import_name(mech->context, minor,
-                                          &expName, (gss_OID)GSS_C_NT_EXPORT_NAME,
-                                          &unionName->mech_name)) != GSS_S_COMPLETE ||
-           (major = generic_gss_copy_oid(minor, &mechOid,
-                                         &unionName->mech_type)) !=
-           GSS_S_COMPLETE) {
-           return (major);
+       major = mech->gss_import_name(mech->context, minor,
+                                     &expName, (gss_OID)GSS_C_NT_EXPORT_NAME,
+                                     &unionName->mech_name);
+       if (major != GSS_S_COMPLETE)
+           map_error(minor, mech);
+       else {
+           major = generic_gss_copy_oid(minor, &mechOid,
+                                        &unionName->mech_type);
+           if (major != GSS_S_COMPLETE)
+               map_errcode(minor);
        }
        return (major);
     }
@@ -349,8 +354,14 @@ importExportName(minor, unionName)
     expName.value = nameLen ? (void *)buf : NULL;
     major = mech->gss_import_name(mech->context, minor, &expName,
                                  GSS_C_NULL_OID, &unionName->mech_name);
-    if (major != GSS_S_COMPLETE)
+    if (major != GSS_S_COMPLETE) {
+       map_error(minor, mech);
        return (major);
+    }
 
-    return (generic_gss_copy_oid(minor, &mechOid, &unionName->mech_type));
+    major = generic_gss_copy_oid(minor, &mechOid, &unionName->mech_type);
+    if (major != GSS_S_COMPLETE) {
+       map_errcode(minor);
+    }
+    return major;
 } /* importExportName */
index 1dd9542073dcd1a4903fd85e767e864fca752577..f83d86170742360720a073d33283a2ac9cfa6e49 100644 (file)
@@ -149,6 +149,7 @@ gss_ctx_id_t *              context_handle;
        *context_handle = ctx;
        return (GSS_S_COMPLETE);
     }
+    map_error(minor_status, mech);
     
 error_out:
     if (ctx) {
index 52f93f124c2f5ad2c78e6663767fe48cb231d96c..b51fb8951d6725b11668b6a9c761a642df1b8e53 100644 (file)
@@ -231,6 +231,7 @@ OM_uint32 *         time_rec;
         * subsequent calls make the caller responsible for
         * calling gss_delete_sec_context
         */
+       map_error(minor_status, mech);
        if (*context_handle == GSS_C_NO_CONTEXT) {
            free(union_ctx_id->mech_type->elements);
            free(union_ctx_id->mech_type);
index d948b98d125de22dca40d5cf40a9c93dc917e59e..e25b1faf08e2646b1bde75f4430cc3da58e7b626 100644 (file)
@@ -120,6 +120,7 @@ gss_OID *oid;
                                k5_mutex_unlock(&g_mechListLock);
                                return (GSS_S_COMPLETE);
                        }
+                       map_error(minor_status, aMech->mech);
                }
                aMech = aMech->next;
        } /* while */
index aeab57d60b0a16c26850e56c5de6c6091fd6d999..a473834d51ffeccaf9bdba898c9fbbf8e3fa7087 100644 (file)
@@ -135,6 +135,7 @@ int *               open;
                        open);
 
     if (status != GSS_S_COMPLETE) {
+       map_error(minor_status, mech);
        return status;
     }
 
index 805579e78a0d0b99d5bd97b624505eb9be259f13..2413abca235635995379a351645095bfb0a07188 100644 (file)
@@ -91,8 +91,10 @@ gss_OID_set *                mechanisms;
                                        name ? &internal_name : NULL,
                                        lifetime, cred_usage, mechanisms);
 
-       if (status != GSS_S_COMPLETE)
+       if (status != GSS_S_COMPLETE) {
+           map_error(minor_status, mech);
            return(status);
+       }
 
        if (name) {
            /*
@@ -103,6 +105,7 @@ gss_OID_set *               mechanisms;
                                                      name);
            if (status != GSS_S_COMPLETE) {
                *minor_status = temp_minor_status;
+               map_error(minor_status, mech);
                if (mechanisms && *mechanisms) {
                    (void) gss_release_oid_set(
                        &temp_minor_status,
@@ -249,8 +252,10 @@ gss_inquire_cred_by_mech(minor_status, cred_handle, mech_type, name,
                                            initiator_lifetime,
                                            acceptor_lifetime, cred_usage);
        
-    if (status != GSS_S_COMPLETE)
+    if (status != GSS_S_COMPLETE) {
+       map_error(minor_status, mech);
        return (status);
+    }
 
     if (name) {
        /*
@@ -261,6 +266,7 @@ gss_inquire_cred_by_mech(minor_status, cred_handle, mech_type, name,
            internal_name, name);
        if (status != GSS_S_COMPLETE) {
            *minor_status = temp_minor_status;
+           map_error(minor_status, mech);
            return (status);
        }
     }
index d1ed23152cf9b342cc38944a8b77c6988566d34e..6142d86bacc47d78b0dd4e9c855da5ef252dc26c 100644 (file)
@@ -67,13 +67,15 @@ gss_OID_set *       name_types;
     
     if (mech) {
 
-       if (mech->gss_inquire_names_for_mech)
+       if (mech->gss_inquire_names_for_mech) {
            status = mech->gss_inquire_names_for_mech(
                                mech->context,
                                minor_status,
                                mechanism,
                                name_types);
-       else
+           if (status != GSS_S_COMPLETE)
+               map_error(minor_status, mech);
+       } else
            status = GSS_S_UNAVAILABLE;
 
        return(status);
index 0607c38f10b062156237cb5ff3d4455a07900ff2..9ade23456702755f8ea52b33d04ffec729e8d9dc 100644 (file)
@@ -79,6 +79,7 @@ gss_add_mech_name_type(minor_status, name_type, mech)
     p = malloc(sizeof(gss_mech_spec_name_desc));
     if (!p) {
        *minor_status = ENOMEM;
+       map_errcode(minor_status);
        goto allocation_failure;
     }
     p->name_type = 0;
@@ -86,12 +87,16 @@ gss_add_mech_name_type(minor_status, name_type, mech)
     
     major_status = generic_gss_copy_oid(minor_status, name_type,
                                        &p->name_type);
-    if (major_status)
+    if (major_status) {
+       map_errcode(minor_status);
        goto allocation_failure;
+    }
     major_status = generic_gss_copy_oid(minor_status, mech,
                                        &p->mech);
-    if (major_status)
+    if (major_status) {
+       map_errcode(minor_status);
        goto allocation_failure;
+    }
 
     p->next = name_list;
     p->prev = 0;
index 86e57972d2a6f810265df9d5e86c41ae9c42c29a..261d699f8fec09e6aa54300082ee00956911a3fc 100644 (file)
@@ -2,7 +2,7 @@
 /*
  * lib/gssapi/mechglue/g_oid_ops.c
  *
- * Copyright 1995 by the Massachusetts Institute of Technology.
+ * Copyright 1995, 2007 by the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * Export of this software from the United States of America may
@@ -46,7 +46,11 @@ gss_create_empty_oid_set(minor_status, oid_set)
     OM_uint32  *minor_status;
     gss_OID_set        *oid_set;
 {
-       return generic_gss_create_empty_oid_set(minor_status, oid_set);
+    OM_uint32 status;
+    status = generic_gss_create_empty_oid_set(minor_status, oid_set);
+    if (status != GSS_S_COMPLETE)
+       map_errcode(minor_status);
+    return status;
 }
 
 OM_uint32 KRB5_CALLCONV
@@ -55,7 +59,11 @@ gss_add_oid_set_member(minor_status, member_oid, oid_set)
     gss_OID    member_oid;
     gss_OID_set        *oid_set;
 {
-     return generic_gss_add_oid_set_member(minor_status, member_oid, oid_set);
+    OM_uint32 status;
+    status = generic_gss_add_oid_set_member(minor_status, member_oid, oid_set);
+    if (status != GSS_S_COMPLETE)
+       map_errcode(minor_status);
+    return status;
 }
 
 OM_uint32 KRB5_CALLCONV
@@ -74,7 +82,10 @@ gss_oid_to_str(minor_status, oid, oid_str)
     gss_OID            oid;
     gss_buffer_t       oid_str;
 {
-    return generic_gss_oid_to_str(minor_status, oid, oid_str);
+    OM_uint32 status = generic_gss_oid_to_str(minor_status, oid, oid_str);
+    if (status != GSS_S_COMPLETE)
+       map_errcode(minor_status);
+    return status;
 }
 
 OM_uint32 KRB5_CALLCONV
@@ -83,6 +94,9 @@ gss_str_to_oid(minor_status, oid_str, oid)
     gss_buffer_t       oid_str;
     gss_OID            *oid;
 {
-    return generic_gss_str_to_oid(minor_status, oid_str, oid);
+    OM_uint32 status = generic_gss_str_to_oid(minor_status, oid_str, oid);
+    if (status != GSS_S_COMPLETE)
+       map_errcode(minor_status);
+    return status;
 }
 
index 18b12050c433002cbdf22bbd226abc17da1f47ee..5172c4cb570760d88f283a8a2fed24e698ffa9e8 100644 (file)
@@ -65,13 +65,15 @@ gss_buffer_t                token_buffer;
 
     if (mech) {
 
-       if (mech->gss_process_context_token)
+       if (mech->gss_process_context_token) {
            status = mech->gss_process_context_token(
                                                    mech->context,
                                                    minor_status,
                                                    ctx->internal_ctx_id,
                                                    token_buffer);
-       else
+           if (status != GSS_S_COMPLETE)
+               map_error(minor_status, mech);
+       } else
            status = GSS_S_UNAVAILABLE;
 
        return(status);
index 6f58d6592af2a1926840c7a1085d46cbc37d9150..6f546a6e241a6e1903c1437d041887287ff5da21 100644 (file)
@@ -82,8 +82,10 @@ gss_cred_id_t *              cred_handle;
                     minor_status,
                     &union_cred->cred_array[j]);
 
-           if (temp_status != GSS_S_COMPLETE)
-               status = GSS_S_NO_CRED;
+               if (temp_status != GSS_S_COMPLETE) {
+                   map_error(minor_status, mech);
+                   status = GSS_S_NO_CRED;
+               }
 
            } else
                status = GSS_S_UNAVAILABLE;
index f784be1abda55fc4f4ce43b52b06d406d7fe3a1e..95c9b45a01d036d353bb85655cf2d871e65b9635 100644 (file)
@@ -106,7 +106,7 @@ gss_buffer_t                output_message_buffer;
     mech = gssint_get_mechanism (ctx->mech_type);
     
     if (mech) {
-       if (mech->gss_seal)
+       if (mech->gss_seal) {
            status = mech->gss_seal(
                                    mech->context,
                                    minor_status,
@@ -116,7 +116,9 @@ gss_buffer_t                output_message_buffer;
                                    input_message_buffer,
                                    conf_state,
                                    output_message_buffer);
-       else
+           if (status != GSS_S_COMPLETE)
+               map_error(minor_status, mech);
+       } else
            status = GSS_S_UNAVAILABLE;
        
        return(status);
@@ -165,6 +167,7 @@ gss_wrap_size_limit(minor_status, context_handle, conf_req_flag,
 {
     gss_union_ctx_id_t ctx;
     gss_mechanism      mech;
+    OM_uint32          major_status;
 
     if (minor_status == NULL)
        return (GSS_S_CALL_INACCESSIBLE_WRITE);
@@ -190,7 +193,11 @@ gss_wrap_size_limit(minor_status, context_handle, conf_req_flag,
     if (!mech->gss_wrap_size_limit)
        return (GSS_S_UNAVAILABLE);
     
-    return (mech->gss_wrap_size_limit(mech->context, minor_status,
-                                     ctx->internal_ctx_id, conf_req_flag, qop_req,
-                                     req_output_size, max_input_size));
+    major_status = mech->gss_wrap_size_limit(mech->context, minor_status,
+                                            ctx->internal_ctx_id,
+                                            conf_req_flag, qop_req,
+                                            req_output_size, max_input_size);
+    if (major_status != GSS_S_COMPLETE)
+       map_error(minor_status, mech);
+    return major_status;
 }
index c0510afe2fe1adf7ecf1dfd2cade1c2fbf99e5b7..d297ee1ca34c8e670b74a52aceb591aa24d50126 100644 (file)
@@ -97,7 +97,7 @@ gss_buffer_t          msg_token;
     mech = gssint_get_mechanism (ctx->mech_type);
 
     if (mech) {
-       if (mech->gss_sign)
+       if (mech->gss_sign) {
            status = mech->gss_sign(
                                    mech->context,
                                    minor_status,
@@ -105,7 +105,9 @@ gss_buffer_t                msg_token;
                                    qop_req,
                                    message_buffer,
                                    msg_token);
-       else
+           if (status != GSS_S_COMPLETE)
+               map_error(minor_status, mech);
+       } else
            status = GSS_S_UNAVAILABLE;
 
        return(status);
index 5663c28ea6b8985971f91b0257879bfaccb231df..b02f7069ad990a0248619005b633438dd0ab3925 100644 (file)
@@ -101,15 +101,18 @@ gss_cred_usage_t  *cred_usage_stored;
                if (mech_cred == GSS_C_NO_CREDENTIAL)
                        return (GSS_S_NO_CRED);
 
-               return (mech->gss_store_cred(mech->context,
-                                               minor_status,
-                                               (gss_cred_id_t)mech_cred,
-                                               cred_usage,
-                                               desired_mech,
-                                               overwrite_cred,
-                                               default_cred,
-                                               elements_stored,
-                                               cred_usage_stored));
+               major_status = mech->gss_store_cred(mech->context,
+                                                   minor_status,
+                                                   (gss_cred_id_t)mech_cred,
+                                                   cred_usage,
+                                                   desired_mech,
+                                                   overwrite_cred,
+                                                   default_cred,
+                                                   elements_stored,
+                                                   cred_usage_stored);
+               if (major_status != GSS_S_COMPLETE)
+                   map_error(minor_status, mech);
+               return major_status;
        }
 
        /* desired_mech == GSS_C_NULL_OID -> store all elements */
@@ -139,8 +142,10 @@ gss_cred_usage_t   *cred_usage_stored;
                                                default_cred,
                                                NULL,
                                                cred_usage_stored);
-               if (major_status != GSS_S_COMPLETE)
-                       continue;
+               if (major_status != GSS_S_COMPLETE) {
+                   map_error(minor_status, mech);
+                   continue;
+               }
 
                /* Succeeded for at least one mech */
 
index 56f5673594a263aa498d69d52613add35f95d418..be7a8de90b13dac3b7ea625331c688b1ce84434b 100644 (file)
@@ -80,7 +80,7 @@ int *                 qop_state;
     mech = gssint_get_mechanism (ctx->mech_type);
 
     if (mech) {
-       if (mech->gss_unseal) 
+       if (mech->gss_unseal) {
            status = mech->gss_unseal(
                                      mech->context,
                                      minor_status,
@@ -89,7 +89,9 @@ int *                 qop_state;
                                      output_message_buffer,
                                      conf_state,
                                      qop_state);
-       else
+           if (status != GSS_S_COMPLETE)
+               map_error(minor_status, mech);
+       } else
            status = GSS_S_UNAVAILABLE;
 
        return(status);
index 5f0374643a9c48dbb706a7026a6b8f705afd9451..a6ca923a40e2e9e6a271b397d4ae8319d0852d57 100644 (file)
@@ -68,7 +68,7 @@ int *                 qop_state;
     mech = gssint_get_mechanism (ctx->mech_type);
 
     if (mech) {
-       if (mech->gss_verify)
+       if (mech->gss_verify) {
            status = mech->gss_verify(
                                      mech->context,
                                      minor_status,
@@ -76,7 +76,9 @@ int *                 qop_state;
                                      message_buffer,
                                      token_buffer,
                                      qop_state);
-       else
+           if (status != GSS_S_COMPLETE)
+               map_error(minor_status, mech);
+       } else
            status = GSS_S_UNAVAILABLE;
 
        return(status);
index 85ae002c35a532cc5bd5cd48f7b7949d9aa14700..a2470fb9eb12a5f5f54436247f3a6693cd6eadc1 100644 (file)
@@ -489,4 +489,25 @@ gssint_put_der_length(
        unsigned int            /* max_len */
 );
 
+/* Use this to map an error code that was returned from a mech
+   operation; the mech will be asked to produce the associated error
+   messages.
+
+   Remember that if the minor status code cannot be returned to the
+   caller (e.g., if it's stuffed in an automatic variable and then
+   ignored), then we don't care about producing a mapping.  */
+#define map_error(MINORP, MECH) \
+    (*(MINORP) = gssint_mecherrmap_map(*(MINORP), &(MECH)->mech_type))
+#define map_error_oid(MINORP, MECHOID) \
+    (*(MINORP) = gssint_mecherrmap_map(*(MINORP), (MECHOID)))
+
+/* Use this to map an errno value or com_err error code being
+   generated within the mechglue code (e.g., by calling generic oid
+   ops).  Any errno or com_err values produced by mech operations
+   should be processed with map_error.  This means they'll be stored
+   separately even if the mech uses com_err, because we can't assume
+   that it will use com_err.  */
+#define map_errcode(MINORP) \
+    (*(MINORP) = gssint_mecherrmap_map_errcode(*(MINORP)))
+
 #endif /* _GSS_MECHGLUEP_H */
index 2dfbfeae769a47c7befc4f0e739bd30eaf91d90e..4a79028e0749cfee57f935615f1f07aac236c9a6 100644 (file)
@@ -95,8 +95,8 @@ generic_gss_copy_oid(minor_status, oid, new_oid)
 
        p = (gss_OID) malloc(sizeof(gss_OID_desc));
        if (!p) {
-               *minor_status = ENOMEM;
-               return GSS_S_FAILURE;
+           *minor_status = ENOMEM;
+           return GSS_S_FAILURE;
        }
        p->length = oid->length;
        p->elements = malloc(p->length);
index 832abe6ec4a283a4622439ea0738ea2e9b186491..9f68a6ccdf865bba653047a5956f7aa5c012d3e3 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2006 by the Massachusetts Institute of Technology.
+ * Copyright (C) 2006,2007 by the Massachusetts Institute of Technology.
  * All rights reserved.
  *
  * Export of this software from the United States of America may
@@ -472,8 +472,10 @@ init_ctx_new(OM_uint32 *minor_status,
         */
        ret = generic_gss_copy_oid(minor_status, (*mechSet)->elements,
                                   &sc->internal_mech);
-       if (ret != GSS_S_COMPLETE)
-               goto cleanup;
+       if (ret != GSS_S_COMPLETE) {
+           map_errcode(minor_status);
+           goto cleanup;
+       }
 
        if (put_mech_set(*mechSet, &sc->DER_mechTypes) < 0) {
                generic_gss_release_oid(&tmpmin, &sc->internal_mech);
@@ -527,6 +529,7 @@ init_ctx_cont(OM_uint32 *minor_status, gss_ctx_id_t *ctx, gss_buffer_t buf,
        }
        if (acc_negState == REJECT) {
                *minor_status = ERR_SPNEGO_NEGOTIATION_FAILED;
+               map_errcode(minor_status);
                *tokflag = NO_TOKEN_SEND;
                ret = GSS_S_FAILURE;
                goto cleanup;
@@ -579,10 +582,12 @@ init_ctx_nego(OM_uint32 *minor_status, spnego_gss_ctx_id_t sc,
         */
        if (supportedMech == GSS_C_NO_OID) {
                *minor_status = ERR_SPNEGO_NO_MECH_FROM_ACCEPTOR;
+               map_errcode(minor_status);
                return GSS_S_DEFECTIVE_TOKEN;
        }
        if (acc_negState == ACCEPT_DEFECTIVE_TOKEN) {
                *minor_status = ERR_SPNEGO_NEGOTIATION_FAILED;
+               map_errcode(minor_status);
                return GSS_S_DEFECTIVE_TOKEN;
        }
        if (!g_OID_equal(supportedMech, sc->internal_mech)) {
@@ -607,6 +612,7 @@ init_ctx_nego(OM_uint32 *minor_status, spnego_gss_ctx_id_t sc,
                         * mech selected.
                         */
                        *minor_status = ERR_SPNEGO_NO_TOKEN_FROM_ACCEPTOR;
+                       map_errcode(minor_status);
                        ret = GSS_S_DEFECTIVE_TOKEN;
                }
        } else if (sc->mech_complete) {
@@ -639,6 +645,7 @@ init_ctx_reselect(OM_uint32 *minor_status, spnego_gss_ctx_id_t sc,
        ret = generic_gss_copy_oid(minor_status, supportedMech,
                                   &sc->internal_mech);
        if (ret != GSS_S_COMPLETE) {
+               map_errcode(minor_status);
                sc->internal_mech = GSS_C_NO_OID;
                *tokflag = NO_TOKEN_SEND;
                return ret;
@@ -1058,6 +1065,7 @@ acc_ctx_vfy_oid(OM_uint32 *minor_status,
        mech = gssint_get_mechanism(sc->internal_mech);
        if (mech == NULL || mech->gss_indicate_mechs == NULL) {
                *minor_status = ERR_SPNEGO_NEGOTIATION_FAILED;
+               map_errcode(minor_status);
                *negState = REJECT;
                *tokflag = ERROR_TOKEN_SEND;
                return GSS_S_BAD_MECH;
@@ -1065,6 +1073,7 @@ acc_ctx_vfy_oid(OM_uint32 *minor_status,
        ret = mech->gss_indicate_mechs(NULL, minor_status, &mech_set);
        if (ret != GSS_S_COMPLETE) {
                *tokflag = NO_TOKEN_SEND;
+               map_error(minor_status, mech);
                goto cleanup;
        }
        ret = gss_test_oid_set_member(minor_status, mechoid,
@@ -1073,6 +1082,7 @@ acc_ctx_vfy_oid(OM_uint32 *minor_status,
                goto cleanup;
        if (!present) {
                *minor_status = ERR_SPNEGO_NEGOTIATION_FAILED;
+               map_errcode(minor_status);
                *negState = REJECT;
                *tokflag = ERROR_TOKEN_SEND;
                ret = GSS_S_BAD_MECH;
@@ -1730,6 +1740,7 @@ get_available_mechs(OM_uint32 *minor_status,
        (void) gss_release_oid_set(&tmpmin, &mechs);
        if (found == 0 || stat != GSS_S_COMPLETE) {
                *minor_status = ERR_SPNEGO_NO_MECHS_AVAILABLE;
+               map_errcode(minor_status);
                if (stat == GSS_S_COMPLETE)
                        stat = GSS_S_FAILURE;
        }
@@ -1769,8 +1780,10 @@ get_mech_oid(OM_uint32 *minor_status, unsigned char **buff_in, size_t length)
 
        status = generic_gss_copy_oid(minor_status, &toid, &mech_out);
 
-       if (status != GSS_S_COMPLETE)
+       if (status != GSS_S_COMPLETE) {
+               map_errcode(minor_status);
                mech_out = NULL;
+       }
 
        return (mech_out);
 }
@@ -1896,7 +1909,8 @@ get_mech_set(OM_uint32 *minor_status, unsigned char **buff_in,
                                        temp, &returned_mechSet);
                    if (major_status == GSS_S_COMPLETE) {
                        set_length += returned_mechSet->elements[i].length +2;
-                       generic_gss_release_oid(minor_status, &temp);
+                       if (generic_gss_release_oid(minor_status, &temp))
+                           map_errcode(minor_status);
                    }
                }
        }
@@ -2032,11 +2046,14 @@ get_negTokenInit(OM_uint32 *minor_status,
                                    &len, &ptr, 0, REMAIN);
        if (err) {
                *minor_status = err;
+               map_errcode(minor_status);
                return GSS_S_FAILURE;
        }
        *minor_status = g_verify_neg_token_init(&ptr, REMAIN);
-       if (*minor_status)
+       if (*minor_status) {
+               map_errcode(minor_status);
                return GSS_S_FAILURE;
+       }
 
        /* alias into input_token */
        tmpbuf.value = ptr;
@@ -2223,6 +2240,7 @@ negotiate_mech_type(OM_uint32 *minor_status,
                                              &returned_mech);
                if (status != GSS_S_COMPLETE) {
                        *negResult = REJECT;
+                       map_errcode(minor_status);
                        return (NULL);
                }
                return (returned_mech);
index 3e878206b4474c1ba8c664e151788cd1be7d46f3..89569f0a62ba18687026c9520d449687c89a767b 100644 (file)
@@ -15,7 +15,8 @@
 
 #ifdef DEBUG_GSSAPI
 int misc_debug_gssapi = DEBUG_GSSAPI;
-#define L_PRINTF(l,args) if (misc_debug_gssapi >= l) printf args
+extern void gssrpcint_printf(const char *, ...);
+#define L_PRINTF(l,args) if (misc_debug_gssapi >= l) gssrpcint_printf args
 #define PRINTF(args) L_PRINTF(99, args)
 #define AUTH_GSSAPI_DISPLAY_STATUS(args) \
        if (misc_debug_gssapi) auth_gssapi_display_status args
@@ -178,6 +179,9 @@ static void auth_gssapi_display_status_1(
          fprintf (stderr, "GSS-API authentication error %s: ", m);
          fwrite (msg.value, msg.length, 1, stderr);
          putc ('\n', stderr);
+         if (misc_debug_gssapi)
+             gssrpcint_printf("GSS-API authentication error %s: %*s\n",
+                              m, msg.length, msg.value);
          (void) gss_release_buffer(&minor_stat, &msg);
          
          if (!msg_ctx)
index a18ab6815f3db51f7c76cdcde89878f5d2646722..cb1e8f90f80e2a372a2dd2f14b210814ffb52ddc 100644 (file)
 
 #ifdef DEBUG_GSSAPI
 int svc_debug_gssapi = DEBUG_GSSAPI;
-#define L_PRINTF(l,args) if (svc_debug_gssapi >= l) printf args
+void gssrpcint_printf(const char *format, ...)
+{
+    va_list ap;
+    va_start(ap, format);
+#if 1
+    vprintf(format, ap);
+#else
+    {
+       static FILE *f;
+       if (f == NULL)
+           f = fopen("/dev/pts/4", "a");
+       if (f) {
+           vfprintf(f, format, ap);
+           fflush(f);
+       }
+    }
+#endif
+    va_end(ap);
+}
+#define L_PRINTF(l,args) if (svc_debug_gssapi >= l) gssrpcint_printf args
 #define PRINTF(args) L_PRINTF(99, args)
 #define AUTH_GSSAPI_DISPLAY_STATUS(args) \
        if (svc_debug_gssapi) auth_gssapi_display_status args
@@ -383,6 +402,8 @@ enum auth_stat gssrpc__svcauth_gssapi(
               if (server_creds == client_data->server_creds)
                    break;
 
+              gssrpcint_printf("accept_sec_context returned 0x%x 0x%x\n",
+                               call_res.gss_major, call_res.gss_minor);
               if (call_res.gss_major == GSS_S_COMPLETE ||
                   call_res.gss_major == GSS_S_CONTINUE_NEEDED) {
                    /* server_creds was right, set it! */
@@ -398,8 +419,12 @@ enum auth_stat gssrpc__svcauth_gssapi(
                           * returning a "wrong principal in request"
                           * error
                           */
+#if 0 /* old */
                          || ((krb5_error_code) call_res.gss_minor !=
                              (krb5_error_code) KRB5KRB_AP_WRONG_PRINC)
+#else
+                         || (call_res.gss_minor <= 0 || call_res.gss_minor > 3)
+#endif
 #endif
                          ) {
                    break;
index ac1126e1bdca1de58cf15b24a2ad10bad5d6f98b..90fe9034f2074dc9008a1d8e7104ff77086e57fe 100644 (file)
@@ -189,7 +189,54 @@ proc ftp_test { } {
     }
 
     #
-    # set KRB5_KTNAME
+    # set KRB5_KTNAME *incorrectly*
+    #
+    set env(KRB5_KTNAME) FILE:$tmppwd/srvtabxx
+    verbose "KRB5_KTNAME=$env(KRB5_KTNAME)"
+
+    # Force some auth errors.
+    set testname "ftp auth errors"
+
+    # Start the ftp daemon.
+    start_ftp_daemon
+
+    # Try connecting.
+    spawn $FTP -d -v $hostname [expr 8 + $portbase]
+    expect_after {
+       -re "--->\[^\r\n\]*\r\n" { exp_continue }
+       -re "encoding \[0-9\]* bytes MIC \[a-zA-Z/+\]*" { exp_continue }
+       -re "sealed \[A-Z()\]*" { exp_continue }
+       -re "secure_command\[A-Z()\]*" { exp_continue }
+       timeout {
+           fail "$testname (timeout)"
+           catch "expect_after"
+           return
+       }
+       eof {
+           fail "$testname (eof)"
+           catch "expect_after"
+           return
+       }
+    }
+    expect -nocase "connected to $hostname"
+    expect -nocase -re "$localhostname.*ftp server .version \[0-9.\]*. ready."
+    expect -re "Using authentication type GSSAPI; ADAT must follow"
+    expect "GSSAPI accepted as authentication type"
+    expect -re "Trying to authenticate to <ftp@.*>"
+    # The ftp client doesn't print the gssapi error except on the last attempt.
+#    expect "GSSAPI error major: Unspecified GSS failure."
+#    expect -re "GSSAPI error minor: Key table file '.*' not found"
+    expect -re "Trying to authenticate to <host@.*>"
+    expect "GSSAPI error major: Unspecified GSS failure."
+    expect -re "GSSAPI error minor: Server host/.* not found in Kerberos database"
+    expect -re "Name (.*): "
+    close -i $spawn_id
+    wait -i $spawn_id
+    wait -i $ftpd_spawn_id
+    catch "close -i $ftpd_spawn_id"
+
+    #
+    # set KRB5_KTNAME correctly now
     #
     set env(KRB5_KTNAME) FILE:$tmppwd/srvtab
     verbose "KRB5_KTNAME=$env(KRB5_KTNAME)"
diff --git a/src/util/gen.pl b/src/util/gen.pl
new file mode 100644 (file)
index 0000000..9d2a3a1
--- /dev/null
@@ -0,0 +1,61 @@
+# -*- perl -*-
+
+# Crude template instantiation hack.
+#
+# The template named on the command line maps to a perl module t_$foo
+# which defines certain methods including variable processing and
+# output generation.  It can also suck in additional template modules
+# for internal use.  One output file is generated, which typically
+# contains structures and inline functions, and should be included by
+# other files which will define, for example, the typedefname
+# parameters supplied to this script.
+
+# To do:
+# Find a way to make dependency generation automatic.
+# Make it less gross.
+
+sub usage {
+    print STDERR "usage: $0 TemplateName [-oOutputFile] PARM=value ...\n";
+    print STDERR "  where acceptable PARM values depend on the template\n";
+    exit(1);
+}
+
+my $orig_args = join(" ", @ARGV);
+my $templatename = shift @ARGV || &usage;
+my $outfile = shift @ARGV || &usage;
+my $x;
+
+eval "require t_$templatename;" || die;
+eval "\$x = new t_$templatename;" || die;
+
+sub getparms {
+    my $arg;
+    my $outfile;
+    my %allowed_parms = ();
+
+    foreach $arg (@ARGV) {
+       my @words = split '=', $arg;
+       if ($#words != 1) {
+           print STDERR "$0: $arg : #words = $#words\n";
+           &usage;
+       }
+       $x->setparm($words[0], $words[1]);
+    }
+}
+
+sub generate {
+    open OUTFILE, ">$outfile" || die;
+    print OUTFILE "/*\n";
+    print OUTFILE " * This file is generated, please don't edit it.\n";
+    print OUTFILE " * script: $0\n";
+    print OUTFILE " * args:   $orig_args\n";
+    print OUTFILE " * The rest of this file is copied from a template, with\n";
+    print OUTFILE " * substitutions.  See the template for copyright info.\n";
+    print OUTFILE " */\n";
+    $x->output(\*OUTFILE);
+    close OUTFILE;
+}
+
+&getparms;
+&generate;
+exit (0);
diff --git a/src/util/t_array.pm b/src/util/t_array.pm
new file mode 100644 (file)
index 0000000..d4b2173
--- /dev/null
@@ -0,0 +1,129 @@
+package t_array;
+
+use strict;
+use vars qw(@ISA);
+
+#require ktemplate;
+require t_template;
+
+@ISA=qw(t_template);
+
+my @parms = qw(NAME TYPE);
+my %defaults = ( );
+my @templatelines = <DATA>;
+
+sub new { # no args
+    my $self = {};
+    bless $self;
+    $self->init(\@parms, \%defaults, \@templatelines);
+    return $self;
+}
+
+__DATA__
+\f
+/*
+ * array type, derived from template
+ *
+ * parameters:
+ * NAME: <NAME>
+ * TYPE: <TYPE>
+ *
+ * methods:
+ * int init() -> nonzero if fail initial allocation
+ * unsigned long size() -> nonnegative number of values stored
+ * int grow(newsize) -> negative if fail allocation, memset(,0,) new space
+ * <TYPE> *getaddr(idx) -> aborts if out of range
+ * void set(idx, value) -> aborts if out of range
+ * <TYPE> get(idx) -> value, or aborts if out of range
+ */
+
+#include <stdlib.h>
+#include <errno.h>
+#include <limits.h>
+#include <string.h>
+
+struct <NAME>__header {
+    size_t allocated;
+    <TYPE> *elts;
+};
+typedef struct <NAME>__header <NAME>;
+
+static inline int
+<NAME>_init(<NAME> *arr)
+{
+    arr->elts = calloc(10, sizeof(<TYPE>));
+    if (arr->elts == NULL)
+       return ENOMEM;
+    arr->allocated = 10;
+    return 0;
+}
+
+static inline long
+<NAME>_size(<NAME> *arr)
+{
+    return arr->allocated;
+}
+
+static inline long
+<NAME>_max_size(<NAME> *arr)
+{
+    size_t upper_bound;
+
+    upper_bound = SIZE_MAX / sizeof(*arr->elts);
+    if (upper_bound > LONG_MAX)
+       upper_bound = LONG_MAX;
+    return (long) upper_bound;
+}
+\f
+static inline int
+<NAME>_grow(<NAME> *arr, unsigned long newcount)
+{
+    size_t oldsize = sizeof(*arr->elts) * arr->allocated;
+    size_t newsize;
+    void *ptr;
+
+    if (newcount > LONG_MAX)
+       return -1;
+    if (newcount < arr->allocated)
+       return 0;
+    if (newcount > <NAME>_max_size(arr))
+       return -1;
+
+    newsize = sizeof(*arr->elts) * newcount;
+    ptr = realloc(arr->elts, newsize);
+    if (ptr == NULL)
+       return -1;
+    memset((char *)arr->elts + oldsize, 0, newsize - oldsize);
+    arr->elts = ptr;
+    arr->allocated = newcount;
+    return 0;
+}
+
+static inline <TYPE> *
+<NAME>_getaddr (<NAME> *arr, long idx)
+{
+    if (idx < 0 || idx >= arr->allocated)
+       abort();
+    return arr->elts + idx;
+}
+
+static inline void
+<NAME>_set (<NAME> *arr, long idx, <TYPE> value)
+{
+    <TYPE> *newvalp;
+    newvalp = <NAME>_getaddr(arr, idx);
+    *newvalp = value;
+}
+
+static inline <TYPE>
+<NAME>_get (<NAME> *arr, long idx)
+{
+    return *<NAME>_getaddr(arr, idx);
+}
+
+static inline void
+<NAME>_destroy (<NAME> *arr)
+{
+    free(arr->elts);
+    arr->elts = 0;
+}
diff --git a/src/util/t_enum.pm b/src/util/t_enum.pm
new file mode 100644 (file)
index 0000000..e62bfce
--- /dev/null
@@ -0,0 +1,133 @@
+package t_enum;
+
+use strict;
+use vars qw(@ISA);
+
+#require ktemplate;
+require t_template;
+require t_array;
+
+@ISA=qw(t_template);
+
+my @parms = qw(NAME TYPE COMPARE);
+my %defaults = ( );
+my @templatelines = <DATA>;
+
+sub new { # no args
+    my $self = {};
+    bless $self;
+    $self->init(\@parms, \%defaults, \@templatelines);
+    return $self;
+}
+
+sub output {
+    my ($self, $fh) = @_;
+    my $a = new t_array;
+    $a->setparm("NAME", $self->{values}{"NAME"} . "__enumerator_array");
+    $a->setparm("TYPE", $self->{values}{"TYPE"});
+    $a->output($fh);
+    $self->SUPER::output($fh);
+}
+
+1;
+
+__DATA__
+\f
+/*
+ * an enumerated collection type, generated from template
+ *
+ * Methods:
+ * int init() -> returns nonzero on alloc failure
+ * long size()
+ * long find(match) -> -1 or index of any match
+ * long append(value) -> -1 or new index
+ * <TYPE> get(index) -> aborts if out of range
+ * void destroy() -> frees array data
+ *
+ * Errors adding elements don't distinguish between "out of memory"
+ * and "too big for size_t".
+ *
+ * Initial implementation: A flat array, reallocated as needed.  Our
+ * uses probably aren't going to get very large.
+ */
+
+struct <NAME>__enumerator {
+    <NAME>__enumerator_array a;
+    size_t used;       /* number of entries used, idx used-1 is last */
+};
+typedef struct <NAME>__enumerator <NAME>;
+
+static inline int
+<NAME>_init(<NAME> *en)
+{
+    en->used = 0;
+    return <NAME>__enumerator_array_init(&en->a);
+}
+
+static inline long
+<NAME>_size(<NAME> *en)
+{
+    return en->used;
+}
+
+static inline long
+<NAME>__s2l(size_t idx)
+{
+    long l;
+    if (idx > LONG_MAX)
+       abort();
+    l = idx;
+    if (l != idx)
+       abort();
+    return l;
+}
+
+static inline long
+<NAME>_find(<NAME> *en, <TYPE> value)
+{
+    size_t i;
+    for (i = 0; i < en->used; i++) {
+       if (<COMPARE> (value, <NAME>__enumerator_array_get(&en->a, <NAME>__s2l(i))) == 0)
+           return i;
+    }
+    return -1;
+}
+
+static inline long
+<NAME>_append(<NAME> *en, <TYPE> value)
+{
+    if (en->used >= LONG_MAX - 1)
+       return -1;
+    if (en->used >= SIZE_MAX - 1)
+       return -1;
+    if (<NAME>__enumerator_array_size(&en->a) == en->used) {
+       if (<NAME>__enumerator_array_grow(&en->a, en->used + 1) < 0)
+           return -1;
+    }
+    <NAME>__enumerator_array_set(&en->a, <NAME>__s2l(en->used), value);
+    en->used++;
+    return en->used-1;
+}
+\f
+static inline <TYPE>
+<NAME>_get(<NAME> *en, size_t idx)
+{
+    return <NAME>__enumerator_array_get(&en->a, <NAME>__s2l(idx));
+}
+
+static inline void
+<NAME>_destroy(<NAME> *en)
+{
+    <NAME>__enumerator_array_destroy(&en->a);
+    en->used = 0;
+}
+
+static inline void
+<NAME>_foreach(<NAME> *en, int (*fn)(size_t i, <TYPE> t, void *p), void *p)
+{
+    size_t i;
+    for (i = 0; i < en->used; i++) {
+       if (fn (i, <NAME>__enumerator_array_get(&en->a, <NAME>__s2l(i)), p) != 0)
+           break;
+    }
+}
diff --git a/src/util/t_template.pm b/src/util/t_template.pm
new file mode 100644 (file)
index 0000000..9722a87
--- /dev/null
@@ -0,0 +1,61 @@
+package t_template;
+
+use strict;
+use vars qw(@ISA @EXPORT_OK);
+
+@ISA=();
+@EXPORT_OK= qw(init setparm output);
+
+sub init { # (\@parms, \%defaults, \@template)
+    my ($self, $parms, $defs, $templatelines) = @_;
+    $self->{parms} = { };
+    $self->{values} = { };
+    my $key;
+    foreach $key (@$parms) {
+       $self->{parms}{$key} = 1;
+    }
+    foreach $key (keys %$defs) {
+       $self->{values}{$key} = ${$defs}{$key};
+    }
+    if (defined($templatelines)) {
+       $self->{template} = join "", @$templatelines;
+    }
+}
+
+sub validateparm { # (parmname)
+    no strict 'refs';
+    my ($self, $parmname) = @_;
+    if (!defined($self->{parms}{$parmname})) {
+       die "unknown parameter $parmname";
+    }
+}
+
+sub setparm { # (parm, value)
+    my ($self, $parm, $value) = @_;
+    $self->validateparm($parm);
+    $self->{values}{$parm} = $value;
+}
+
+sub substitute { # (text)
+    my ($self, $text) = @_;
+    my ($p);
+
+    # Do substitutions.
+    foreach $p (keys %{$self->{parms}}) {
+       if (!defined $self->{values}{$p}) {
+           die "$0: No value supplied for parameter $p\n";
+       }
+       # XXX More careful quoting of supplied value!
+       $text =~ s|<$p>|$self->{values}{$p}|g;
+    }
+    return $text;
+}
+
+sub output { # (fh)
+    my ($self, $fh) = @_;
+    print $fh "/* start of ", ref($self), " template */\n";
+    print $fh $self->substitute($self->{template});
+    print $fh "/* end of ", ref($self), " template */\n";
+}
+
+1;
diff --git a/src/util/t_tsenum.pm b/src/util/t_tsenum.pm
new file mode 100644 (file)
index 0000000..00efb51
--- /dev/null
@@ -0,0 +1,163 @@
+package t_tsenum;
+
+use strict;
+use vars qw(@ISA);
+
+require t_template;
+require t_enum;
+
+@ISA=qw(t_template);
+
+my @parms = qw(NAME TYPE COMPARE COPY PRINT);
+my %defaults = ( "COPY", "0", "PRINT", "0" );
+my @templatelines = <DATA>;
+
+sub new { # no args
+    my $self = {};
+    bless $self;
+    $self->init(\@parms, \%defaults, \@templatelines);
+    return $self;
+}
+
+sub output {
+    my ($self, $fh) = @_;
+    my $a = new t_enum;
+    $a->setparm("NAME", $self->{values}{"NAME"} . "__unsafe_enumerator");
+    $a->setparm("TYPE", $self->{values}{"TYPE"});
+    $a->setparm("COMPARE", $self->{values}{"COMPARE"});
+    $a->output($fh);
+    $self->SUPER::output($fh);
+}
+
+1;
+
+__DATA__
+\f
+/*
+ */
+#include "k5-thread.h"
+struct <NAME>__ts_enumerator {
+    <NAME>__unsafe_enumerator e;
+    k5_mutex_t m;
+};
+typedef struct <NAME>__ts_enumerator <NAME>;
+
+static inline int
+<NAME>_init(<NAME> *en)
+{
+    int err = k5_mutex_init(&en->m);
+    if (err)
+       return err;
+    err = <NAME>__unsafe_enumerator_init(&en->e);
+    if (err) {
+       k5_mutex_destroy(&en->m);
+       return err;
+    }
+    return 0;
+}
+
+static inline int
+<NAME>_size(<NAME> *en, long *size)
+{
+    int err = k5_mutex_lock(&en->m);
+    if (err) {
+       *size = -48;
+       return err;
+    }
+    *size = <NAME>__unsafe_enumerator_size(&en->e);
+    k5_mutex_unlock(&en->m);
+    return 0;
+}
+
+static inline int
+<NAME>__do_copy (<TYPE> *dest, <TYPE> src)
+{
+    int (*copyfn)(<TYPE>*, <TYPE>) = <COPY>;
+    if (copyfn)
+       return copyfn(dest, src);
+    *dest = src;
+    return 0;
+}
+
+static inline int
+<NAME>_find_or_append(<NAME> *en, <TYPE> value, long *idxp, int *added)
+{
+    int err;
+    long idx;
+
+    err = k5_mutex_lock(&en->m);
+    if (err)
+       return err;
+    idx = <NAME>__unsafe_enumerator_find(&en->e, value);
+    if (idx < 0) {
+       <TYPE> newvalue;
+       err = <NAME>__do_copy(&newvalue, value);
+       if (err == 0)
+           idx = <NAME>__unsafe_enumerator_append(&en->e, newvalue);
+       k5_mutex_unlock(&en->m);
+       if (err != 0)
+           return err;
+       if (idx < 0)
+           return ENOMEM;
+       *idxp = idx;
+       *added = 1;
+       return 0;
+    }
+    k5_mutex_unlock(&en->m);
+    *idxp = idx;
+    *added = 0;
+    return 0;
+}
+\f
+static inline int
+<NAME>_get(<NAME> *en, size_t idx, <TYPE> *value)
+{
+    int err;
+    err = k5_mutex_lock(&en->m);
+    if (err)
+       return err;
+    *value = <NAME>__unsafe_enumerator_get(&en->e, idx);
+    k5_mutex_unlock(&en->m);
+    return 0;
+}
+
+static inline void
+<NAME>_destroy(<NAME> *en)
+{
+    k5_mutex_destroy(&en->m);
+    <NAME>__unsafe_enumerator_destroy(&en->e);
+}
+
+static inline int
+<NAME>_foreach(<NAME> *en, int (*fn)(size_t i, <TYPE> t, void *p), void *p)
+{
+    int err = k5_mutex_lock(&en->m);
+    if (err)
+       return err;
+    <NAME>__unsafe_enumerator_foreach(&en->e, fn, p);
+    k5_mutex_unlock(&en->m);
+    return 0;
+}
+
+static inline int
+<NAME>__print_map_elt(size_t idx, <TYPE> val, void *p)
+{
+    void (*printfn)(<TYPE>, FILE *) = <PRINT>;
+    FILE *f = (FILE *) p;
+    if (printfn) {
+       fprintf(f, " %lu=", (unsigned long) idx);
+       printfn(val, f);
+    }
+    return 0;
+}
+
+static inline void
+<NAME>_print(<NAME> *en, FILE *f)
+{
+    void (*printfn)(<TYPE>, FILE *) = <PRINT>;
+    if (printfn) {
+       fprintf(f, "{");
+       <NAME>_foreach (en, <NAME>__print_map_elt, f);
+       fprintf(f, " }");
+    }
+}