xref: /freebsd/crypto/krb5/src/lib/gssapi/generic/oid_ops.c (revision f1c4c3daccbaf3820f0e2224de53df12fc952fcc)
1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* lib/gssapi/generic/oid_ops.c */
3 /*
4  * Copyright 1995 by the Massachusetts Institute of Technology.
5  * All Rights Reserved.
6  *
7  * Export of this software from the United States of America may
8  *   require a specific license from the United States Government.
9  *   It is the responsibility of any person or organization contemplating
10  *   export to obtain such a license before exporting.
11  *
12  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
13  * distribute this software and its documentation for any purpose and
14  * without fee is hereby granted, provided that the above copyright
15  * notice appear in all copies and that both that copyright notice and
16  * this permission notice appear in supporting documentation, and that
17  * the name of M.I.T. not be used in advertising or publicity pertaining
18  * to distribution of the software without specific, written prior
19  * permission.  Furthermore if you modify this software you must label
20  * your software as modified software and not distribute it in such a
21  * fashion that it might be confused with the original M.I.T. software.
22  * M.I.T. makes no representations about the suitability of
23  * this software for any purpose.  It is provided "as is" without express
24  * or implied warranty.
25  */
26 /*
27  * Copyright 1993 by OpenVision Technologies, Inc.
28  *
29  * Permission to use, copy, modify, distribute, and sell this software
30  * and its documentation for any purpose is hereby granted without fee,
31  * provided that the above copyright notice appears in all copies and
32  * that both that copyright notice and this permission notice appear in
33  * supporting documentation, and that the name of OpenVision not be used
34  * in advertising or publicity pertaining to distribution of the software
35  * without specific, written prior permission. OpenVision makes no
36  * representations about the suitability of this software for any
37  * purpose.  It is provided "as is" without express or implied warranty.
38  *
39  * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
40  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
41  * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR
42  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
43  * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
44  * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
45  * PERFORMANCE OF THIS SOFTWARE.
46  */
47 
48 /* GSS-API V2 interfaces to manipulate OIDs */
49 
50 #include "gssapiP_generic.h"
51 #ifdef HAVE_UNISTD_H
52 #include <unistd.h>
53 #endif
54 #include <stdlib.h>
55 #include <string.h>
56 #include <stdio.h>
57 #include <gssapi/gssapi_generic.h>
58 #include <errno.h>
59 #include <ctype.h>
60 
61 /*
62  * The functions for allocating and releasing individual OIDs use malloc and
63  * free instead of the gssalloc wrappers, because the mechglue currently mixes
64  * generic_gss_copy_oid() with hand-freeing of OIDs.  We do not need to free
65  * free OIDs allocated by mechanisms, so this should not be a problem.
66  */
67 
68 OM_uint32
generic_gss_release_oid(OM_uint32 * minor_status,gss_OID * oid)69 generic_gss_release_oid(OM_uint32 *minor_status, gss_OID *oid)
70 {
71     *minor_status = 0;
72 
73     if (oid == NULL || *oid == GSS_C_NO_OID)
74         return(GSS_S_COMPLETE);
75 
76     /*
77      * The V2 API says the following!
78      *
79      * gss_release_oid[()] will recognize any of the GSSAPI's own OID values,
80      * and will silently ignore attempts to free these OIDs; for other OIDs
81      * it will call the C free() routine for both the OID data and the
82      * descriptor.  This allows applications to freely mix their own heap-
83      * allocated OID values with OIDs returned by GSS-API.
84      */
85 
86     /*
87      * We use the official OID definitions instead of the unofficial OID
88      * definitions. But we continue to support the unofficial OID
89      * gss_nt_service_name just in case if some gss applications use
90      * the old OID.
91      */
92 
93     if ((*oid != GSS_C_NT_USER_NAME) &&
94         (*oid != GSS_C_NT_MACHINE_UID_NAME) &&
95         (*oid != GSS_C_NT_STRING_UID_NAME) &&
96         (*oid != GSS_C_NT_HOSTBASED_SERVICE) &&
97         (*oid != GSS_C_NT_ANONYMOUS) &&
98         (*oid != GSS_C_NT_EXPORT_NAME) &&
99         (*oid != GSS_C_NT_COMPOSITE_EXPORT) &&
100         (*oid != gss_nt_service_name)) {
101         free((*oid)->elements);
102         free(*oid);
103     }
104     *oid = GSS_C_NO_OID;
105     return(GSS_S_COMPLETE);
106 }
107 
108 OM_uint32
generic_gss_copy_oid(OM_uint32 * minor_status,const gss_OID_desc * const oid,gss_OID * new_oid)109 generic_gss_copy_oid(OM_uint32 *minor_status,
110                      const gss_OID_desc * const oid,
111                      gss_OID *new_oid)
112 {
113     gss_OID         p;
114 
115     *minor_status = 0;
116 
117     p = (gss_OID) malloc(sizeof(gss_OID_desc));
118     if (!p) {
119         *minor_status = ENOMEM;
120         return GSS_S_FAILURE;
121     }
122     p->length = oid->length;
123     p->elements = malloc(p->length);
124     if (!p->elements) {
125         free(p);
126         return GSS_S_FAILURE;
127     }
128     memcpy(p->elements, oid->elements, p->length);
129     *new_oid = p;
130     return(GSS_S_COMPLETE);
131 }
132 
133 
134 OM_uint32
generic_gss_create_empty_oid_set(OM_uint32 * minor_status,gss_OID_set * oid_set)135 generic_gss_create_empty_oid_set(OM_uint32 *minor_status, gss_OID_set *oid_set)
136 {
137     *minor_status = 0;
138 
139     if (oid_set == NULL)
140         return GSS_S_CALL_INACCESSIBLE_WRITE;
141 
142     if ((*oid_set = (gss_OID_set) gssalloc_malloc(sizeof(gss_OID_set_desc)))) {
143         memset(*oid_set, 0, sizeof(gss_OID_set_desc));
144         return(GSS_S_COMPLETE);
145     }
146     else {
147         *minor_status = ENOMEM;
148         return(GSS_S_FAILURE);
149     }
150 }
151 
152 OM_uint32
generic_gss_add_oid_set_member(OM_uint32 * minor_status,const gss_OID_desc * const member_oid,gss_OID_set * oid_set)153 generic_gss_add_oid_set_member(OM_uint32 *minor_status,
154                                const gss_OID_desc * const member_oid,
155                                gss_OID_set *oid_set)
156 {
157     gss_OID     elist;
158     gss_OID     lastel;
159 
160     *minor_status = 0;
161 
162     if (member_oid == NULL || member_oid->length == 0 ||
163         member_oid->elements == NULL)
164         return (GSS_S_CALL_INACCESSIBLE_READ);
165 
166     if (oid_set == NULL)
167         return GSS_S_CALL_INACCESSIBLE_WRITE;
168 
169     elist = (*oid_set)->elements;
170     /* Get an enlarged copy of the array */
171     if (((*oid_set)->elements = (gss_OID) gssalloc_malloc(((*oid_set)->count+1) *
172                                                           sizeof(gss_OID_desc)))) {
173         /* Copy in the old junk */
174         if (elist)
175             memcpy((*oid_set)->elements,
176                    elist,
177                    ((*oid_set)->count * sizeof(gss_OID_desc)));
178 
179         /* Duplicate the input element */
180         lastel = &(*oid_set)->elements[(*oid_set)->count];
181         if ((lastel->elements =
182              (void *) gssalloc_malloc((size_t) member_oid->length))) {
183             /* Success - copy elements */
184             memcpy(lastel->elements, member_oid->elements,
185                    (size_t) member_oid->length);
186             /* Set length */
187             lastel->length = member_oid->length;
188 
189             /* Update count */
190             (*oid_set)->count++;
191             if (elist)
192                 gssalloc_free(elist);
193             *minor_status = 0;
194             return(GSS_S_COMPLETE);
195         }
196         else
197             gssalloc_free((*oid_set)->elements);
198     }
199     /* Failure - restore old contents of list */
200     (*oid_set)->elements = elist;
201     *minor_status = ENOMEM;
202     return(GSS_S_FAILURE);
203 }
204 
205 OM_uint32
generic_gss_test_oid_set_member(OM_uint32 * minor_status,const gss_OID_desc * const member,gss_OID_set set,int * present)206 generic_gss_test_oid_set_member(OM_uint32 *minor_status,
207                                 const gss_OID_desc * const member,
208                                 gss_OID_set set,
209                                 int * present)
210 {
211     OM_uint32   i;
212     int         result;
213 
214     *minor_status = 0;
215 
216     if (member == NULL || set == NULL)
217         return (GSS_S_CALL_INACCESSIBLE_READ);
218 
219     if (present == NULL)
220         return (GSS_S_CALL_INACCESSIBLE_WRITE);
221 
222     result = 0;
223     for (i=0; i<set->count; i++) {
224         if ((set->elements[i].length == member->length) &&
225             !memcmp(set->elements[i].elements,
226                     member->elements,
227                     (size_t) member->length)) {
228             result = 1;
229             break;
230         }
231     }
232     *present = result;
233     return(GSS_S_COMPLETE);
234 }
235 
236 OM_uint32
generic_gss_oid_to_str(OM_uint32 * minor_status,const gss_OID_desc * const oid,gss_buffer_t oid_str)237 generic_gss_oid_to_str(OM_uint32 *minor_status,
238                        const gss_OID_desc * const oid,
239                        gss_buffer_t oid_str)
240 {
241     unsigned long       number, n;
242     OM_uint32           i;
243     int                 first;
244     unsigned char       *cp;
245     struct k5buf        buf;
246 
247     *minor_status = 0;
248 
249     if (oid_str != GSS_C_NO_BUFFER) {
250         oid_str->length = 0;
251         oid_str->value = NULL;
252     }
253 
254     if (oid == NULL || oid->length == 0 || oid->elements == NULL)
255         return (GSS_S_CALL_INACCESSIBLE_READ);
256 
257     if (oid_str == GSS_C_NO_BUFFER)
258         return (GSS_S_CALL_INACCESSIBLE_WRITE);
259 
260     /* Decoded according to krb5/gssapi_krb5.c */
261 
262     cp = (unsigned char *) oid->elements;
263     number = (unsigned long) cp[0];
264     k5_buf_init_dynamic(&buf);
265     k5_buf_add(&buf, "{ ");
266     number = 0;
267     cp = (unsigned char *) oid->elements;
268     first = 1;
269     for (i = 0; i < oid->length; i++) {
270         number = (number << 7) | (cp[i] & 0x7f);
271         if ((cp[i] & 0x80) == 0) {
272             if (first) {
273                 n = (number < 40) ? 0 : (number < 80) ? 1 : 2;
274                 k5_buf_add_fmt(&buf, "%lu %lu ", n, number - (n * 40));
275                 first = 0;
276             } else {
277                 k5_buf_add_fmt(&buf, "%lu ", number);
278             }
279             number = 0;
280         }
281     }
282     k5_buf_add_len(&buf, "}\0", 2);
283     return k5buf_to_gss(minor_status, &buf, oid_str);
284 }
285 
286 /* Return the length of a DER OID subidentifier encoding. */
287 static size_t
arc_encoded_length(unsigned long arc)288 arc_encoded_length(unsigned long arc)
289 {
290     size_t len = 1;
291 
292     for (arc >>= 7; arc; arc >>= 7)
293         len++;
294     return len;
295 }
296 
297 /* Encode a subidentifier into *bufp and advance it to the encoding's end. */
298 static void
arc_encode(unsigned long arc,unsigned char ** bufp)299 arc_encode(unsigned long arc, unsigned char **bufp)
300 {
301     unsigned char *p;
302 
303     /* Advance to the end and encode backwards. */
304     p = *bufp = *bufp + arc_encoded_length(arc);
305     *--p = arc & 0x7f;
306     for (arc >>= 7; arc; arc >>= 7)
307         *--p = (arc & 0x7f) | 0x80;
308 }
309 
310 /* Fetch an arc value from *bufp and advance past it and any following spaces
311  * or periods.  Return 1 on success, 0 if *bufp is not at a valid arc value. */
312 static int
get_arc(const unsigned char ** bufp,const unsigned char * end,unsigned long * arc_out)313 get_arc(const unsigned char **bufp, const unsigned char *end,
314         unsigned long *arc_out)
315 {
316     const unsigned char *p = *bufp;
317     unsigned long arc = 0, newval;
318 
319     if (p == end || !isdigit(*p))
320         return 0;
321     for (; p < end && isdigit(*p); p++) {
322         newval = arc * 10 + (*p - '0');
323         if (newval < arc)
324             return 0;
325         arc = newval;
326     }
327     while (p < end && (isspace(*p) || *p == '.'))
328         p++;
329     *bufp = p;
330     *arc_out = arc;
331     return 1;
332 }
333 
334 /*
335  * Convert a sequence of two or more decimal arc values into a DER-encoded OID.
336  * The values may be separated by any combination of whitespace and period
337  * characters, and may be optionally surrounded with braces.  Leading
338  * whitespace and trailing garbage is allowed.  The first arc value must be 0,
339  * 1, or 2, and the second value must be less than 40 if the first value is not
340  * 2.
341  */
342 OM_uint32
generic_gss_str_to_oid(OM_uint32 * minor_status,gss_buffer_t oid_str,gss_OID * oid_out)343 generic_gss_str_to_oid(OM_uint32 *minor_status,
344                        gss_buffer_t oid_str,
345                        gss_OID *oid_out)
346 {
347     const unsigned char *p, *end, *arc3_start;
348     unsigned char *out;
349     unsigned long arc, arc1, arc2;
350     size_t nbytes;
351     int brace = 0;
352     gss_OID oid;
353 
354     *minor_status = 0;
355 
356     if (oid_out != NULL)
357         *oid_out = GSS_C_NO_OID;
358 
359     if (GSS_EMPTY_BUFFER(oid_str))
360         return (GSS_S_CALL_INACCESSIBLE_READ);
361 
362     if (oid_out == NULL)
363         return (GSS_S_CALL_INACCESSIBLE_WRITE);
364 
365     /* Skip past initial spaces and, optionally, an open brace. */
366     brace = 0;
367     p = oid_str->value;
368     end = p + oid_str->length;
369     while (p < end && isspace(*p))
370         p++;
371     if (p < end && *p == '{') {
372         brace = 1;
373         p++;
374     }
375     while (p < end && isspace(*p))
376         p++;
377 
378     /* Get the first two arc values, to be encoded as one subidentifier. */
379     if (!get_arc(&p, end, &arc1) || !get_arc(&p, end, &arc2))
380         return (GSS_S_FAILURE);
381     if (arc1 > 2 || (arc1 < 2 && arc2 > 39) || arc2 > ULONG_MAX - 80)
382         return (GSS_S_FAILURE);
383     arc3_start = p;
384 
385     /* Compute the total length of the encoding while checking syntax. */
386     nbytes = arc_encoded_length(arc1 * 40 + arc2);
387     while (get_arc(&p, end, &arc))
388         nbytes += arc_encoded_length(arc);
389     if (brace && (p == end || *p != '}'))
390         return (GSS_S_FAILURE);
391 
392     /* Allocate an oid structure. */
393     oid = malloc(sizeof(*oid));
394     if (oid == NULL)
395         return (GSS_S_FAILURE);
396     oid->elements = malloc(nbytes);
397     if (oid->elements == NULL) {
398         free(oid);
399         return (GSS_S_FAILURE);
400     }
401     oid->length = nbytes;
402 
403     out = oid->elements;
404     arc_encode(arc1 * 40 + arc2, &out);
405     p = arc3_start;
406     while (get_arc(&p, end, &arc))
407         arc_encode(arc, &out);
408     assert(out - nbytes == oid->elements);
409     *oid_out = oid;
410     return(GSS_S_COMPLETE);
411 }
412 
413 /* Compose an OID of a prefix and an integer suffix */
414 OM_uint32
generic_gss_oid_compose(OM_uint32 * minor_status,const char * prefix,size_t prefix_len,int suffix,gss_OID_desc * oid)415 generic_gss_oid_compose(OM_uint32 *minor_status,
416                         const char *prefix,
417                         size_t prefix_len,
418                         int suffix,
419                         gss_OID_desc *oid)
420 {
421     int osuffix, i;
422     size_t nbytes;
423     unsigned char *op;
424 
425     if (oid == GSS_C_NO_OID) {
426         *minor_status = EINVAL;
427         return GSS_S_FAILURE;
428     }
429     if (oid->length < prefix_len) {
430         *minor_status = ERANGE;
431         return GSS_S_FAILURE;
432     }
433 
434     memcpy(oid->elements, prefix, prefix_len);
435 
436     nbytes = 0;
437     osuffix = suffix;
438     while (suffix) {
439         nbytes++;
440         suffix >>= 7;
441     }
442     suffix = osuffix;
443 
444     if (oid->length < prefix_len + nbytes) {
445         *minor_status = ERANGE;
446         return GSS_S_FAILURE;
447     }
448 
449     op = (unsigned char *) oid->elements + prefix_len + nbytes;
450     i = -1;
451     while (suffix) {
452         op[i] = (unsigned char)suffix & 0x7f;
453         if (i != -1)
454             op[i] |= 0x80;
455         i--;
456         suffix >>= 7;
457     }
458 
459     oid->length = prefix_len + nbytes;
460 
461     *minor_status = 0;
462     return GSS_S_COMPLETE;
463 }
464 
465 OM_uint32
generic_gss_oid_decompose(OM_uint32 * minor_status,const char * prefix,size_t prefix_len,gss_OID_desc * oid,int * suffix)466 generic_gss_oid_decompose(OM_uint32 *minor_status,
467                           const char *prefix,
468                           size_t prefix_len,
469                           gss_OID_desc *oid,
470                           int *suffix)
471 {
472     size_t i, slen;
473     unsigned char *op;
474 
475     if (oid->length < prefix_len ||
476         memcmp(oid->elements, prefix, prefix_len) != 0) {
477         return GSS_S_BAD_MECH;
478     }
479 
480     op = (unsigned char *) oid->elements + prefix_len;
481 
482     *suffix = 0;
483 
484     slen = oid->length - prefix_len;
485 
486     for (i = 0; i < slen; i++) {
487         *suffix = (*suffix << 7) | (op[i] & 0x7f);
488         if (i + 1 != slen && (op[i] & 0x80) == 0) {
489             *minor_status = EINVAL;
490             return GSS_S_FAILURE;
491         }
492     }
493 
494     return GSS_S_COMPLETE;
495 }
496 
497 OM_uint32
generic_gss_copy_oid_set(OM_uint32 * minor_status,const gss_OID_set_desc * const oidset,gss_OID_set * new_oidset)498 generic_gss_copy_oid_set(OM_uint32 *minor_status,
499                          const gss_OID_set_desc * const oidset,
500                          gss_OID_set *new_oidset)
501 {
502     gss_OID_set_desc *copy;
503     OM_uint32 minor = 0;
504     OM_uint32 major = GSS_S_COMPLETE;
505     OM_uint32 i;
506 
507     if (minor_status != NULL)
508         *minor_status = 0;
509 
510     if (new_oidset != NULL)
511         *new_oidset = GSS_C_NO_OID_SET;
512 
513     if (oidset == GSS_C_NO_OID_SET)
514         return (GSS_S_CALL_INACCESSIBLE_READ);
515 
516     if (new_oidset == NULL)
517         return (GSS_S_CALL_INACCESSIBLE_WRITE);
518 
519     if ((copy = (gss_OID_set_desc *) gssalloc_calloc(1, sizeof (*copy))) == NULL) {
520         major = GSS_S_FAILURE;
521         goto done;
522     }
523 
524     if ((copy->elements = (gss_OID_desc *)
525          gssalloc_calloc(oidset->count, sizeof (*copy->elements))) == NULL) {
526         major = GSS_S_FAILURE;
527         goto done;
528     }
529     copy->count = oidset->count;
530 
531     for (i = 0; i < copy->count; i++) {
532         gss_OID_desc *out = &copy->elements[i];
533         gss_OID_desc *in = &oidset->elements[i];
534 
535         if ((out->elements = (void *) gssalloc_malloc(in->length)) == NULL) {
536             major = GSS_S_FAILURE;
537             goto done;
538         }
539         (void) memcpy(out->elements, in->elements, in->length);
540         out->length = in->length;
541     }
542 
543     *new_oidset = copy;
544 done:
545     if (major != GSS_S_COMPLETE) {
546         (void) generic_gss_release_oid_set(&minor, &copy);
547     }
548 
549     return (major);
550 }
551