$(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 \
$(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) \
rel_buffer.o \
rel_oid_set.o \
util_buffer.o \
+ util_errmap.o \
util_ordering.o \
util_set.o \
util_token.o \
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)
$(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 \
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_ */
--- /dev/null
+#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;
+}
--- /dev/null
+#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;
+}
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)
k5_mutex_destroy(&kg_kdc_flag_mutex);
#endif
k5_mutex_destroy(&gssint_krb5_keytab_lock);
+ gssint_mecherrmap_destroy();
gssint_mechglue_fini();
}
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,
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);
&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) {
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,
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) {
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;
+ }
}
}
/* 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,
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;
}
/*
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);
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);
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 */
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 =
* 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)
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;
+ }
}
/*
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,
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;
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);
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
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);
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);
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);
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;
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));
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;
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;
+ }
}
/*
* 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);
}
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 */
*context_handle = ctx;
return (GSS_S_COMPLETE);
}
+ map_error(minor_status, mech);
error_out:
if (ctx) {
* 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);
k5_mutex_unlock(&g_mechListLock);
return (GSS_S_COMPLETE);
}
+ map_error(minor_status, aMech->mech);
}
aMech = aMech->next;
} /* while */
open);
if (status != GSS_S_COMPLETE) {
+ map_error(minor_status, mech);
return status;
}
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) {
/*
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,
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) {
/*
internal_name, name);
if (status != GSS_S_COMPLETE) {
*minor_status = temp_minor_status;
+ map_error(minor_status, mech);
return (status);
}
}
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);
p = malloc(sizeof(gss_mech_spec_name_desc));
if (!p) {
*minor_status = ENOMEM;
+ map_errcode(minor_status);
goto allocation_failure;
}
p->name_type = 0;
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;
/*
* 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
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
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
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
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;
}
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);
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;
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,
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);
{
gss_union_ctx_id_t ctx;
gss_mechanism mech;
+ OM_uint32 major_status;
if (minor_status == NULL)
return (GSS_S_CALL_INACCESSIBLE_WRITE);
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;
}
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,
qop_req,
message_buffer,
msg_token);
- else
+ if (status != GSS_S_COMPLETE)
+ map_error(minor_status, mech);
+ } else
status = GSS_S_UNAVAILABLE;
return(status);
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 */
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 */
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,
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);
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,
message_buffer,
token_buffer,
qop_state);
- else
+ if (status != GSS_S_COMPLETE)
+ map_error(minor_status, mech);
+ } else
status = GSS_S_UNAVAILABLE;
return(status);
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 */
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);
/*
- * 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
*/
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);
}
if (acc_negState == REJECT) {
*minor_status = ERR_SPNEGO_NEGOTIATION_FAILED;
+ map_errcode(minor_status);
*tokflag = NO_TOKEN_SEND;
ret = GSS_S_FAILURE;
goto cleanup;
*/
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)) {
* mech selected.
*/
*minor_status = ERR_SPNEGO_NO_TOKEN_FROM_ACCEPTOR;
+ map_errcode(minor_status);
ret = GSS_S_DEFECTIVE_TOKEN;
}
} else if (sc->mech_complete) {
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;
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;
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,
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;
(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;
}
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);
}
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);
}
}
}
&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;
&returned_mech);
if (status != GSS_S_COMPLETE) {
*negResult = REJECT;
+ map_errcode(minor_status);
return (NULL);
}
return (returned_mech);
#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
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)
#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
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! */
* 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;
}
#
- # 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)"
--- /dev/null
+# -*- 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);
--- /dev/null
+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;
+}
--- /dev/null
+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;
+ }
+}
--- /dev/null
+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;
--- /dev/null
+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, " }");
+ }
+}