/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ /* lib/gssapi/generic/oid_ops.c */ /* * Copyright 1995 by the Massachusetts Institute of Technology. * All Rights Reserved. * * Export of this software from the United States of America may * require a specific license from the United States Government. * It is the responsibility of any person or organization contemplating * export to obtain such a license before exporting. * * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and * distribute this software and its documentation for any purpose and * without fee is hereby granted, provided that the above copyright * notice appear in all copies and that both that copyright notice and * this permission notice appear in supporting documentation, and that * the name of M.I.T. not be used in advertising or publicity pertaining * to distribution of the software without specific, written prior * permission. Furthermore if you modify this software you must label * your software as modified software and not distribute it in such a * fashion that it might be confused with the original M.I.T. software. * M.I.T. makes no representations about the suitability of * this software for any purpose. It is provided "as is" without express * or implied warranty. */ /* * Copyright 1993 by OpenVision Technologies, Inc. * * Permission to use, copy, modify, distribute, and sell this software * and its documentation for any purpose is hereby granted without fee, * provided that the above copyright notice appears in all copies and * that both that copyright notice and this permission notice appear in * supporting documentation, and that the name of OpenVision not be used * in advertising or publicity pertaining to distribution of the software * without specific, written prior permission. OpenVision makes no * representations about the suitability of this software for any * purpose. It is provided "as is" without express or implied warranty. * * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ /* GSS-API V2 interfaces to manipulate OIDs */ #include "gssapiP_generic.h" #ifdef HAVE_UNISTD_H #include #endif #include #include #include #include #include #include /* * The functions for allocating and releasing individual OIDs use malloc and * free instead of the gssalloc wrappers, because the mechglue currently mixes * generic_gss_copy_oid() with hand-freeing of OIDs. We do not need to free * free OIDs allocated by mechanisms, so this should not be a problem. */ OM_uint32 generic_gss_release_oid(OM_uint32 *minor_status, gss_OID *oid) { if (minor_status) *minor_status = 0; if (oid == NULL || *oid == GSS_C_NO_OID) return(GSS_S_COMPLETE); /* * The V2 API says the following! * * gss_release_oid[()] will recognize any of the GSSAPI's own OID values, * and will silently ignore attempts to free these OIDs; for other OIDs * it will call the C free() routine for both the OID data and the * descriptor. This allows applications to freely mix their own heap- * allocated OID values with OIDs returned by GSS-API. */ /* * We use the official OID definitions instead of the unofficial OID * definitions. But we continue to support the unofficial OID * gss_nt_service_name just in case if some gss applications use * the old OID. */ if ((*oid != GSS_C_NT_USER_NAME) && (*oid != GSS_C_NT_MACHINE_UID_NAME) && (*oid != GSS_C_NT_STRING_UID_NAME) && (*oid != GSS_C_NT_HOSTBASED_SERVICE) && (*oid != GSS_C_NT_ANONYMOUS) && (*oid != GSS_C_NT_EXPORT_NAME) && (*oid != GSS_C_NT_COMPOSITE_EXPORT) && (*oid != gss_nt_service_name)) { free((*oid)->elements); free(*oid); } *oid = GSS_C_NO_OID; return(GSS_S_COMPLETE); } OM_uint32 generic_gss_copy_oid(OM_uint32 *minor_status, const gss_OID_desc * const oid, gss_OID *new_oid) { gss_OID p; *minor_status = 0; p = (gss_OID) malloc(sizeof(gss_OID_desc)); if (!p) { *minor_status = ENOMEM; return GSS_S_FAILURE; } p->length = oid->length; p->elements = malloc(p->length); if (!p->elements) { free(p); return GSS_S_FAILURE; } memcpy(p->elements, oid->elements, p->length); *new_oid = p; return(GSS_S_COMPLETE); } OM_uint32 generic_gss_create_empty_oid_set(OM_uint32 *minor_status, gss_OID_set *oid_set) { *minor_status = 0; if (oid_set == NULL) return GSS_S_CALL_INACCESSIBLE_WRITE; if ((*oid_set = (gss_OID_set) gssalloc_malloc(sizeof(gss_OID_set_desc)))) { memset(*oid_set, 0, sizeof(gss_OID_set_desc)); return(GSS_S_COMPLETE); } else { *minor_status = ENOMEM; return(GSS_S_FAILURE); } } OM_uint32 generic_gss_add_oid_set_member(OM_uint32 *minor_status, const gss_OID_desc * const member_oid, gss_OID_set *oid_set) { gss_OID elist; gss_OID lastel; *minor_status = 0; if (member_oid == NULL || member_oid->length == 0 || member_oid->elements == NULL) return (GSS_S_CALL_INACCESSIBLE_READ); if (oid_set == NULL) return GSS_S_CALL_INACCESSIBLE_WRITE; elist = (*oid_set)->elements; /* Get an enlarged copy of the array */ if (((*oid_set)->elements = (gss_OID) gssalloc_malloc(((*oid_set)->count+1) * sizeof(gss_OID_desc)))) { /* Copy in the old junk */ if (elist) memcpy((*oid_set)->elements, elist, ((*oid_set)->count * sizeof(gss_OID_desc))); /* Duplicate the input element */ lastel = &(*oid_set)->elements[(*oid_set)->count]; if ((lastel->elements = (void *) gssalloc_malloc((size_t) member_oid->length))) { /* Success - copy elements */ memcpy(lastel->elements, member_oid->elements, (size_t) member_oid->length); /* Set length */ lastel->length = member_oid->length; /* Update count */ (*oid_set)->count++; if (elist) gssalloc_free(elist); *minor_status = 0; return(GSS_S_COMPLETE); } else gssalloc_free((*oid_set)->elements); } /* Failure - restore old contents of list */ (*oid_set)->elements = elist; *minor_status = ENOMEM; return(GSS_S_FAILURE); } OM_uint32 generic_gss_test_oid_set_member(OM_uint32 *minor_status, const gss_OID_desc * const member, gss_OID_set set, int * present) { OM_uint32 i; int result; *minor_status = 0; if (member == NULL || set == NULL) return (GSS_S_CALL_INACCESSIBLE_READ); if (present == NULL) return (GSS_S_CALL_INACCESSIBLE_WRITE); result = 0; for (i=0; icount; i++) { if ((set->elements[i].length == member->length) && !memcmp(set->elements[i].elements, member->elements, (size_t) member->length)) { result = 1; break; } } *present = result; return(GSS_S_COMPLETE); } OM_uint32 generic_gss_oid_to_str(OM_uint32 *minor_status, const gss_OID_desc * const oid, gss_buffer_t oid_str) { unsigned long number, n; OM_uint32 i; int first; unsigned char *cp; struct k5buf buf; if (minor_status != NULL) *minor_status = 0; if (oid_str != GSS_C_NO_BUFFER) { oid_str->length = 0; oid_str->value = NULL; } if (oid == NULL || oid->length == 0 || oid->elements == NULL) return (GSS_S_CALL_INACCESSIBLE_READ); if (oid_str == GSS_C_NO_BUFFER) return (GSS_S_CALL_INACCESSIBLE_WRITE); /* Decoded according to krb5/gssapi_krb5.c */ cp = (unsigned char *) oid->elements; number = (unsigned long) cp[0]; k5_buf_init_dynamic(&buf); k5_buf_add(&buf, "{ "); number = 0; cp = (unsigned char *) oid->elements; first = 1; for (i = 0; i < oid->length; i++) { number = (number << 7) | (cp[i] & 0x7f); if ((cp[i] & 0x80) == 0) { if (first) { n = (number < 40) ? 0 : (number < 80) ? 1 : 2; k5_buf_add_fmt(&buf, "%lu %lu ", n, number - (n * 40)); first = 0; } else { k5_buf_add_fmt(&buf, "%lu ", number); } number = 0; } } k5_buf_add_len(&buf, "}\0", 2); return k5buf_to_gss(minor_status, &buf, oid_str); } /* Return the length of a DER OID subidentifier encoding. */ static size_t arc_encoded_length(unsigned long arc) { size_t len = 1; for (arc >>= 7; arc; arc >>= 7) len++; return len; } /* Encode a subidentifier into *bufp and advance it to the encoding's end. */ static void arc_encode(unsigned long arc, unsigned char **bufp) { unsigned char *p; /* Advance to the end and encode backwards. */ p = *bufp = *bufp + arc_encoded_length(arc); *--p = arc & 0x7f; for (arc >>= 7; arc; arc >>= 7) *--p = (arc & 0x7f) | 0x80; } /* Fetch an arc value from *bufp and advance past it and any following spaces * or periods. Return 1 on success, 0 if *bufp is not at a valid arc value. */ static int get_arc(const unsigned char **bufp, const unsigned char *end, unsigned long *arc_out) { const unsigned char *p = *bufp; unsigned long arc = 0, newval; if (p == end || !isdigit(*p)) return 0; for (; p < end && isdigit(*p); p++) { newval = arc * 10 + (*p - '0'); if (newval < arc) return 0; arc = newval; } while (p < end && (isspace(*p) || *p == '.')) p++; *bufp = p; *arc_out = arc; return 1; } /* * Convert a sequence of two or more decimal arc values into a DER-encoded OID. * The values may be separated by any combination of whitespace and period * characters, and may be optionally surrounded with braces. Leading * whitespace and trailing garbage is allowed. The first arc value must be 0, * 1, or 2, and the second value must be less than 40 if the first value is not * 2. */ OM_uint32 generic_gss_str_to_oid(OM_uint32 *minor_status, gss_buffer_t oid_str, gss_OID *oid_out) { const unsigned char *p, *end, *arc3_start; unsigned char *out; unsigned long arc, arc1, arc2; size_t nbytes; int brace = 0; gss_OID oid; if (minor_status != NULL) *minor_status = 0; if (oid_out != NULL) *oid_out = GSS_C_NO_OID; if (GSS_EMPTY_BUFFER(oid_str)) return (GSS_S_CALL_INACCESSIBLE_READ); if (oid_out == NULL) return (GSS_S_CALL_INACCESSIBLE_WRITE); /* Skip past initial spaces and, optionally, an open brace. */ brace = 0; p = oid_str->value; end = p + oid_str->length; while (p < end && isspace(*p)) p++; if (p < end && *p == '{') { brace = 1; p++; } while (p < end && isspace(*p)) p++; /* Get the first two arc values, to be encoded as one subidentifier. */ if (!get_arc(&p, end, &arc1) || !get_arc(&p, end, &arc2)) return (GSS_S_FAILURE); if (arc1 > 2 || (arc1 < 2 && arc2 > 39) || arc2 > ULONG_MAX - 80) return (GSS_S_FAILURE); arc3_start = p; /* Compute the total length of the encoding while checking syntax. */ nbytes = arc_encoded_length(arc1 * 40 + arc2); while (get_arc(&p, end, &arc)) nbytes += arc_encoded_length(arc); if (brace && (p == end || *p != '}')) return (GSS_S_FAILURE); /* Allocate an oid structure. */ oid = malloc(sizeof(*oid)); if (oid == NULL) return (GSS_S_FAILURE); oid->elements = malloc(nbytes); if (oid->elements == NULL) { free(oid); return (GSS_S_FAILURE); } oid->length = nbytes; out = oid->elements; arc_encode(arc1 * 40 + arc2, &out); p = arc3_start; while (get_arc(&p, end, &arc)) arc_encode(arc, &out); assert(out - nbytes == oid->elements); *oid_out = oid; return(GSS_S_COMPLETE); } /* Compose an OID of a prefix and an integer suffix */ OM_uint32 generic_gss_oid_compose(OM_uint32 *minor_status, const char *prefix, size_t prefix_len, int suffix, gss_OID_desc *oid) { int osuffix, i; size_t nbytes; unsigned char *op; if (oid == GSS_C_NO_OID) { *minor_status = EINVAL; return GSS_S_FAILURE; } if (oid->length < prefix_len) { *minor_status = ERANGE; return GSS_S_FAILURE; } memcpy(oid->elements, prefix, prefix_len); nbytes = 0; osuffix = suffix; while (suffix) { nbytes++; suffix >>= 7; } suffix = osuffix; if (oid->length < prefix_len + nbytes) { *minor_status = ERANGE; return GSS_S_FAILURE; } op = (unsigned char *) oid->elements + prefix_len + nbytes; i = -1; while (suffix) { op[i] = (unsigned char)suffix & 0x7f; if (i != -1) op[i] |= 0x80; i--; suffix >>= 7; } oid->length = prefix_len + nbytes; *minor_status = 0; return GSS_S_COMPLETE; } OM_uint32 generic_gss_oid_decompose(OM_uint32 *minor_status, const char *prefix, size_t prefix_len, gss_OID_desc *oid, int *suffix) { size_t i, slen; unsigned char *op; if (oid->length < prefix_len || memcmp(oid->elements, prefix, prefix_len) != 0) { return GSS_S_BAD_MECH; } op = (unsigned char *) oid->elements + prefix_len; *suffix = 0; slen = oid->length - prefix_len; for (i = 0; i < slen; i++) { *suffix = (*suffix << 7) | (op[i] & 0x7f); if (i + 1 != slen && (op[i] & 0x80) == 0) { *minor_status = EINVAL; return GSS_S_FAILURE; } } return GSS_S_COMPLETE; } OM_uint32 generic_gss_copy_oid_set(OM_uint32 *minor_status, const gss_OID_set_desc * const oidset, gss_OID_set *new_oidset) { gss_OID_set_desc *copy; OM_uint32 minor = 0; OM_uint32 major = GSS_S_COMPLETE; OM_uint32 i; if (minor_status != NULL) *minor_status = 0; if (new_oidset != NULL) *new_oidset = GSS_C_NO_OID_SET; if (oidset == GSS_C_NO_OID_SET) return (GSS_S_CALL_INACCESSIBLE_READ); if (new_oidset == NULL) return (GSS_S_CALL_INACCESSIBLE_WRITE); if ((copy = (gss_OID_set_desc *) gssalloc_calloc(1, sizeof (*copy))) == NULL) { major = GSS_S_FAILURE; goto done; } if ((copy->elements = (gss_OID_desc *) gssalloc_calloc(oidset->count, sizeof (*copy->elements))) == NULL) { major = GSS_S_FAILURE; goto done; } copy->count = oidset->count; for (i = 0; i < copy->count; i++) { gss_OID_desc *out = ©->elements[i]; gss_OID_desc *in = &oidset->elements[i]; if ((out->elements = (void *) gssalloc_malloc(in->length)) == NULL) { major = GSS_S_FAILURE; goto done; } (void) memcpy(out->elements, in->elements, in->length); out->length = in->length; } *new_oidset = copy; done: if (major != GSS_S_COMPLETE) { (void) generic_gss_release_oid_set(&minor, ©); } return (major); }