summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorKen Raeburn <raeburn@mit.edu>2007-08-16 22:55:06 +0000
committerKen Raeburn <raeburn@mit.edu>2007-08-16 22:55:06 +0000
commitc15ec7751a7d7c1d97dbeb1dd88dda2a328515e0 (patch)
tree824bd8c158b1c5b72913515953c7e8576399d912 /src
parent9db2f5eb745287654117e70032d05dd9f5a91a3f (diff)
downloadkrb5-c15ec7751a7d7c1d97dbeb1dd88dda2a328515e0.tar.gz
krb5-c15ec7751a7d7c1d97dbeb1dd88dda2a328515e0.tar.xz
krb5-c15ec7751a7d7c1d97dbeb1dd88dda2a328515e0.zip
remap mechanism-specific status codes in mechglue/spnego
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
Diffstat (limited to 'src')
-rw-r--r--src/lib/gssapi/generic/Makefile.in21
-rw-r--r--src/lib/gssapi/generic/gssapiP_generic.h5
-rw-r--r--src/lib/gssapi/generic/maptest.c54
-rw-r--r--src/lib/gssapi/generic/util_errmap.c190
-rw-r--r--src/lib/gssapi/gss_libinit.c4
-rw-r--r--src/lib/gssapi/mechglue/g_accept_sec_context.c8
-rw-r--r--src/lib/gssapi/mechglue/g_acquire_cred.c4
-rw-r--r--src/lib/gssapi/mechglue/g_canon_name.c14
-rw-r--r--src/lib/gssapi/mechglue/g_compare_name.c13
-rw-r--r--src/lib/gssapi/mechglue/g_context_time.c6
-rw-r--r--src/lib/gssapi/mechglue/g_delete_sec_context.c6
-rw-r--r--src/lib/gssapi/mechglue/g_dsp_name.c4
-rw-r--r--src/lib/gssapi/mechglue/g_dsp_status.c51
-rw-r--r--src/lib/gssapi/mechglue/g_dup_name.c12
-rw-r--r--src/lib/gssapi/mechglue/g_exp_sec_context.c4
-rw-r--r--src/lib/gssapi/mechglue/g_glue.c49
-rw-r--r--src/lib/gssapi/mechglue/g_imp_name.c31
-rw-r--r--src/lib/gssapi/mechglue/g_imp_sec_context.c1
-rw-r--r--src/lib/gssapi/mechglue/g_init_sec_context.c1
-rw-r--r--src/lib/gssapi/mechglue/g_initialize.c1
-rw-r--r--src/lib/gssapi/mechglue/g_inq_context.c1
-rw-r--r--src/lib/gssapi/mechglue/g_inq_cred.c10
-rw-r--r--src/lib/gssapi/mechglue/g_inq_names.c6
-rw-r--r--src/lib/gssapi/mechglue/g_mechname.c9
-rw-r--r--src/lib/gssapi/mechglue/g_oid_ops.c24
-rw-r--r--src/lib/gssapi/mechglue/g_process_context.c6
-rw-r--r--src/lib/gssapi/mechglue/g_rel_cred.c6
-rw-r--r--src/lib/gssapi/mechglue/g_seal.c17
-rw-r--r--src/lib/gssapi/mechglue/g_sign.c6
-rw-r--r--src/lib/gssapi/mechglue/g_store_cred.c27
-rw-r--r--src/lib/gssapi/mechglue/g_unseal.c6
-rw-r--r--src/lib/gssapi/mechglue/g_verify.c6
-rw-r--r--src/lib/gssapi/mechglue/mglueP.h21
-rw-r--r--src/lib/gssapi/mechglue/oid_ops.c4
-rw-r--r--src/lib/gssapi/spnego/spnego_mech.c30
-rw-r--r--src/lib/rpc/auth_gssapi_misc.c6
-rw-r--r--src/lib/rpc/svc_auth_gssapi.c27
-rw-r--r--src/tests/dejagnu/krb-standalone/gssftp.exp49
-rw-r--r--src/util/gen.pl61
-rw-r--r--src/util/t_array.pm129
-rw-r--r--src/util/t_enum.pm133
-rw-r--r--src/util/t_template.pm61
-rw-r--r--src/util/t_tsenum.pm163
43 files changed, 1191 insertions, 96 deletions
diff --git a/src/lib/gssapi/generic/Makefile.in b/src/lib/gssapi/generic/Makefile.in
index e63be6b787..6d66396be5 100644
--- a/src/lib/gssapi/generic/Makefile.in
+++ b/src/lib/gssapi/generic/Makefile.in
@@ -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 \
diff --git a/src/lib/gssapi/generic/gssapiP_generic.h b/src/lib/gssapi/generic/gssapiP_generic.h
index e34055b6ce..1ec5417ba8 100644
--- a/src/lib/gssapi/generic/gssapiP_generic.h
+++ b/src/lib/gssapi/generic/gssapiP_generic.h
@@ -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
index 0000000000..b884eed8b4
--- /dev/null
+++ b/src/lib/gssapi/generic/maptest.c
@@ -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
index 0000000000..24975cc455
--- /dev/null
+++ b/src/lib/gssapi/generic/util_errmap.c
@@ -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;
+}
diff --git a/src/lib/gssapi/gss_libinit.c b/src/lib/gssapi/gss_libinit.c
index b96eb9e7aa..bb90857132 100644
--- a/src/lib/gssapi/gss_libinit.c
+++ b/src/lib/gssapi/gss_libinit.c
@@ -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();
}
diff --git a/src/lib/gssapi/mechglue/g_accept_sec_context.c b/src/lib/gssapi/mechglue/g_accept_sec_context.c
index 0e8506a49d..9b55629674 100644
--- a/src/lib/gssapi/mechglue/g_accept_sec_context.c
+++ b/src/lib/gssapi/mechglue/g_accept_sec_context.c
@@ -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,
diff --git a/src/lib/gssapi/mechglue/g_acquire_cred.c b/src/lib/gssapi/mechglue/g_acquire_cred.c
index bd5bef8ea8..6d63e5b8f2 100644
--- a/src/lib/gssapi/mechglue/g_acquire_cred.c
+++ b/src/lib/gssapi/mechglue/g_acquire_cred.c
@@ -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) {
diff --git a/src/lib/gssapi/mechglue/g_canon_name.c b/src/lib/gssapi/mechglue/g_canon_name.c
index 9f72055b38..3d371c0e04 100644
--- a/src/lib/gssapi/mechglue/g_canon_name.c
+++ b/src/lib/gssapi/mechglue/g_canon_name.c
@@ -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,
diff --git a/src/lib/gssapi/mechglue/g_compare_name.c b/src/lib/gssapi/mechglue/g_compare_name.c
index 7fb5cc9cfc..40f4648efd 100644
--- a/src/lib/gssapi/mechglue/g_compare_name.c
+++ b/src/lib/gssapi/mechglue/g_compare_name.c
@@ -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);
diff --git a/src/lib/gssapi/mechglue/g_context_time.c b/src/lib/gssapi/mechglue/g_context_time.c
index 5ce6b56d86..866405729b 100644
--- a/src/lib/gssapi/mechglue/g_context_time.c
+++ b/src/lib/gssapi/mechglue/g_context_time.c
@@ -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);
diff --git a/src/lib/gssapi/mechglue/g_delete_sec_context.c b/src/lib/gssapi/mechglue/g_delete_sec_context.c
index bf2e9010fd..fdaf2c310d 100644
--- a/src/lib/gssapi/mechglue/g_delete_sec_context.c
+++ b/src/lib/gssapi/mechglue/g_delete_sec_context.c
@@ -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 */
diff --git a/src/lib/gssapi/mechglue/g_dsp_name.c b/src/lib/gssapi/mechglue/g_dsp_name.c
index 7db90584d8..7efd583f8f 100644
--- a/src/lib/gssapi/mechglue/g_dsp_name.c
+++ b/src/lib/gssapi/mechglue/g_dsp_name.c
@@ -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 =
diff --git a/src/lib/gssapi/mechglue/g_dsp_status.c b/src/lib/gssapi/mechglue/g_dsp_status.c
index 519d4a4a15..881bd76697 100644
--- a/src/lib/gssapi/mechglue/g_dsp_status.c
+++ b/src/lib/gssapi/mechglue/g_dsp_status.c
@@ -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)
diff --git a/src/lib/gssapi/mechglue/g_dup_name.c b/src/lib/gssapi/mechglue/g_dup_name.c
index 1d37be9e17..9312de761f 100644
--- a/src/lib/gssapi/mechglue/g_dup_name.c
+++ b/src/lib/gssapi/mechglue/g_dup_name.c
@@ -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,
diff --git a/src/lib/gssapi/mechglue/g_exp_sec_context.c b/src/lib/gssapi/mechglue/g_exp_sec_context.c
index 1490a2b373..28e25b325a 100644
--- a/src/lib/gssapi/mechglue/g_exp_sec_context.c
+++ b/src/lib/gssapi/mechglue/g_exp_sec_context.c
@@ -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;
diff --git a/src/lib/gssapi/mechglue/g_glue.c b/src/lib/gssapi/mechglue/g_glue.c
index 1331862dc9..41759468f4 100644
--- a/src/lib/gssapi/mechglue/g_glue.c
+++ b/src/lib/gssapi/mechglue/g_glue.c
@@ -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;
diff --git a/src/lib/gssapi/mechglue/g_imp_name.c b/src/lib/gssapi/mechglue/g_imp_name.c
index fa7aa8d13b..c786445428 100644
--- a/src/lib/gssapi/mechglue/g_imp_name.c
+++ b/src/lib/gssapi/mechglue/g_imp_name.c
@@ -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 */
diff --git a/src/lib/gssapi/mechglue/g_imp_sec_context.c b/src/lib/gssapi/mechglue/g_imp_sec_context.c
index 1dd9542073..f83d861707 100644
--- a/src/lib/gssapi/mechglue/g_imp_sec_context.c
+++ b/src/lib/gssapi/mechglue/g_imp_sec_context.c
@@ -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) {
diff --git a/src/lib/gssapi/mechglue/g_init_sec_context.c b/src/lib/gssapi/mechglue/g_init_sec_context.c
index 52f93f124c..b51fb8951d 100644
--- a/src/lib/gssapi/mechglue/g_init_sec_context.c
+++ b/src/lib/gssapi/mechglue/g_init_sec_context.c
@@ -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);
diff --git a/src/lib/gssapi/mechglue/g_initialize.c b/src/lib/gssapi/mechglue/g_initialize.c
index d948b98d12..e25b1faf08 100644
--- a/src/lib/gssapi/mechglue/g_initialize.c
+++ b/src/lib/gssapi/mechglue/g_initialize.c
@@ -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 */
diff --git a/src/lib/gssapi/mechglue/g_inq_context.c b/src/lib/gssapi/mechglue/g_inq_context.c
index aeab57d60b..a473834d51 100644
--- a/src/lib/gssapi/mechglue/g_inq_context.c
+++ b/src/lib/gssapi/mechglue/g_inq_context.c
@@ -135,6 +135,7 @@ int * open;
open);
if (status != GSS_S_COMPLETE) {
+ map_error(minor_status, mech);
return status;
}
diff --git a/src/lib/gssapi/mechglue/g_inq_cred.c b/src/lib/gssapi/mechglue/g_inq_cred.c
index 805579e78a..2413abca23 100644
--- a/src/lib/gssapi/mechglue/g_inq_cred.c
+++ b/src/lib/gssapi/mechglue/g_inq_cred.c
@@ -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);
}
}
diff --git a/src/lib/gssapi/mechglue/g_inq_names.c b/src/lib/gssapi/mechglue/g_inq_names.c
index d1ed23152c..6142d86bac 100644
--- a/src/lib/gssapi/mechglue/g_inq_names.c
+++ b/src/lib/gssapi/mechglue/g_inq_names.c
@@ -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);
diff --git a/src/lib/gssapi/mechglue/g_mechname.c b/src/lib/gssapi/mechglue/g_mechname.c
index 0607c38f10..9ade234567 100644
--- a/src/lib/gssapi/mechglue/g_mechname.c
+++ b/src/lib/gssapi/mechglue/g_mechname.c
@@ -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;
diff --git a/src/lib/gssapi/mechglue/g_oid_ops.c b/src/lib/gssapi/mechglue/g_oid_ops.c
index 86e57972d2..261d699f8f 100644
--- a/src/lib/gssapi/mechglue/g_oid_ops.c
+++ b/src/lib/gssapi/mechglue/g_oid_ops.c
@@ -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;
}
diff --git a/src/lib/gssapi/mechglue/g_process_context.c b/src/lib/gssapi/mechglue/g_process_context.c
index 18b12050c4..5172c4cb57 100644
--- a/src/lib/gssapi/mechglue/g_process_context.c
+++ b/src/lib/gssapi/mechglue/g_process_context.c
@@ -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);
diff --git a/src/lib/gssapi/mechglue/g_rel_cred.c b/src/lib/gssapi/mechglue/g_rel_cred.c
index 6f58d6592a..6f546a6e24 100644
--- a/src/lib/gssapi/mechglue/g_rel_cred.c
+++ b/src/lib/gssapi/mechglue/g_rel_cred.c
@@ -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;
diff --git a/src/lib/gssapi/mechglue/g_seal.c b/src/lib/gssapi/mechglue/g_seal.c
index f784be1abd..95c9b45a01 100644
--- a/src/lib/gssapi/mechglue/g_seal.c
+++ b/src/lib/gssapi/mechglue/g_seal.c
@@ -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;
}
diff --git a/src/lib/gssapi/mechglue/g_sign.c b/src/lib/gssapi/mechglue/g_sign.c
index c0510afe2f..d297ee1ca3 100644
--- a/src/lib/gssapi/mechglue/g_sign.c
+++ b/src/lib/gssapi/mechglue/g_sign.c
@@ -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);
diff --git a/src/lib/gssapi/mechglue/g_store_cred.c b/src/lib/gssapi/mechglue/g_store_cred.c
index 5663c28ea6..b02f7069ad 100644
--- a/src/lib/gssapi/mechglue/g_store_cred.c
+++ b/src/lib/gssapi/mechglue/g_store_cred.c
@@ -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 */
diff --git a/src/lib/gssapi/mechglue/g_unseal.c b/src/lib/gssapi/mechglue/g_unseal.c
index 56f5673594..be7a8de90b 100644
--- a/src/lib/gssapi/mechglue/g_unseal.c
+++ b/src/lib/gssapi/mechglue/g_unseal.c
@@ -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);
diff --git a/src/lib/gssapi/mechglue/g_verify.c b/src/lib/gssapi/mechglue/g_verify.c
index 5f0374643a..a6ca923a40 100644
--- a/src/lib/gssapi/mechglue/g_verify.c
+++ b/src/lib/gssapi/mechglue/g_verify.c
@@ -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);
diff --git a/src/lib/gssapi/mechglue/mglueP.h b/src/lib/gssapi/mechglue/mglueP.h
index 85ae002c35..a2470fb9eb 100644
--- a/src/lib/gssapi/mechglue/mglueP.h
+++ b/src/lib/gssapi/mechglue/mglueP.h
@@ -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 */
diff --git a/src/lib/gssapi/mechglue/oid_ops.c b/src/lib/gssapi/mechglue/oid_ops.c
index 2dfbfeae76..4a79028e07 100644
--- a/src/lib/gssapi/mechglue/oid_ops.c
+++ b/src/lib/gssapi/mechglue/oid_ops.c
@@ -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);
diff --git a/src/lib/gssapi/spnego/spnego_mech.c b/src/lib/gssapi/spnego/spnego_mech.c
index 832abe6ec4..9f68a6ccdf 100644
--- a/src/lib/gssapi/spnego/spnego_mech.c
+++ b/src/lib/gssapi/spnego/spnego_mech.c
@@ -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);
diff --git a/src/lib/rpc/auth_gssapi_misc.c b/src/lib/rpc/auth_gssapi_misc.c
index 3e878206b4..89569f0a62 100644
--- a/src/lib/rpc/auth_gssapi_misc.c
+++ b/src/lib/rpc/auth_gssapi_misc.c
@@ -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)
diff --git a/src/lib/rpc/svc_auth_gssapi.c b/src/lib/rpc/svc_auth_gssapi.c
index a18ab6815f..cb1e8f90f8 100644
--- a/src/lib/rpc/svc_auth_gssapi.c
+++ b/src/lib/rpc/svc_auth_gssapi.c
@@ -45,7 +45,26 @@
#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;
diff --git a/src/tests/dejagnu/krb-standalone/gssftp.exp b/src/tests/dejagnu/krb-standalone/gssftp.exp
index ac1126e1bd..90fe9034f2 100644
--- a/src/tests/dejagnu/krb-standalone/gssftp.exp
+++ b/src/tests/dejagnu/krb-standalone/gssftp.exp
@@ -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
index 0000000000..9d2a3a1804
--- /dev/null
+++ b/src/util/gen.pl
@@ -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
index 0000000000..d4b2173938
--- /dev/null
+++ b/src/util/t_array.pm
@@ -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__
+
+/*
+ * 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;
+}
+
+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
index 0000000000..e62bfce5a8
--- /dev/null
+++ b/src/util/t_enum.pm
@@ -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__
+
+/*
+ * 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;
+}
+
+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
index 0000000000..9722a87308
--- /dev/null
+++ b/src/util/t_template.pm
@@ -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
index 0000000000..00efb51425
--- /dev/null
+++ b/src/util/t_tsenum.pm
@@ -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__
+
+/*
+ */
+#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;
+}
+
+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, " }");
+ }
+}