1 /*   Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. */
2 /* -*- mode: c; indent-tabs-mode: nil -*- */
3 /*
4  * Copyright 2007, 2008 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 
28 #include "gssapiP_generic.h"
29 #include "mechglueP.h"
30 #include <string.h>
31 #include <stdio.h>
32 #ifndef _WIN32
33 #include <unistd.h>
34 #endif
35 
36 /* Solaris Kerberos */
37 #define inline
38 #ifdef DEBUG
39 #undef DEBUG
40 #endif
41 
42 /* The mapping table is 0-based, but let's export codes that are
43    1-based, keeping 0 for errors or unknown errors.
44 
45    The elements in the mapping table currently have separate copies of
46    each OID stored.  This is a bit wasteful, but we are assuming the
47    table isn't likely to grow very large.  */
48 
49 struct mecherror {
50     gss_OID_desc mech;
51     OM_uint32 code;
52 };
53 
54 static inline int
cmp_OM_uint32(OM_uint32 m1,OM_uint32 m2)55 cmp_OM_uint32(OM_uint32 m1, OM_uint32 m2)
56 {
57     if (m1 < m2)
58         return -1;
59     else if (m1 > m2)
60         return 1;
61     else
62         return 0;
63 }
64 
65 static inline int
mecherror_cmp(struct mecherror m1,struct mecherror m2)66 mecherror_cmp(struct mecherror m1, struct mecherror m2)
67 {
68     if (m1.code < m2.code)
69         return -1;
70     if (m1.code > m2.code)
71         return 1;
72     if (m1.mech.length < m2.mech.length)
73         return -1;
74     if (m1.mech.length > m2.mech.length)
75         return 1;
76     if (m1.mech.length == 0)
77         return 0;
78     return memcmp(m1.mech.elements, m2.mech.elements, m1.mech.length);
79 }
80 
81 static void
print_OM_uint32(OM_uint32 value,FILE * f)82 print_OM_uint32 (OM_uint32 value, FILE *f)
83 {
84     fprintf(f, "%lu", (unsigned long) value);
85 }
86 
87 static inline int
mecherror_copy(struct mecherror * dest,struct mecherror src)88 mecherror_copy(struct mecherror *dest, struct mecherror src)
89 {
90     *dest = src;
91     dest->mech.elements = malloc(src.mech.length);
92     if (dest->mech.elements == NULL) {
93         if (src.mech.length)
94             return ENOMEM;
95         else
96             return 0;
97     }
98     memcpy(dest->mech.elements, src.mech.elements, src.mech.length);
99     return 0;
100 }
101 
102 static void
mecherror_print(struct mecherror value,FILE * f)103 mecherror_print(struct mecherror value, FILE *f)
104 {
105     OM_uint32 minor;
106     gss_buffer_desc str;
107     static const struct {
108         const char *oidstr, *name;
109     } mechnames[] = {
110         { "{ 1 2 840 113554 1 2 2 }", "krb5-new" },
111         { "{ 1 3 5 1 5 2 }", "krb5-old" },
112         { "{ 1 2 840 48018 1 2 2 }", "krb5-microsoft" },
113         { "{ 1 3 6 1 5 5 2 }", "spnego" },
114     };
115     unsigned int i;
116 
117     fprintf(f, "%lu@", (unsigned long) value.code);
118 
119     if (value.mech.length == 0) {
120         fprintf(f, "(com_err)");
121         return;
122     }
123     fprintf(f, "%p=", value.mech.elements);
124     if (generic_gss_oid_to_str(&minor, &value.mech, &str)) {
125         fprintf(f, "(error in conversion)");
126         return;
127     }
128     /* Note: generic_gss_oid_to_str returns a null-terminated string.  */
129     for (i = 0; i < sizeof(mechnames)/sizeof(mechnames[0]); i++) {
130         if (!strcmp(str.value, mechnames[i].oidstr) && mechnames[i].name != 0) {
131             fprintf(f, "%s", mechnames[i].name);
132             break;
133         }
134     }
135     if (i == sizeof(mechnames)/sizeof(mechnames[0]))
136         fprintf(f, "%s", (char *) str.value);
137     generic_gss_release_buffer(&minor, &str);
138 }
139 
140 #include "errmap.h"
141 #include "krb5.h"               /* for KRB5KRB_AP_WRONG_PRINC */
142 
143 static mecherrmap m;
144 static k5_mutex_t mutex = K5_MUTEX_PARTIAL_INITIALIZER;
145 static OM_uint32 next_fake = 100000;
146 
gssint_mecherrmap_init(void)147 int gssint_mecherrmap_init(void)
148 {
149     int err;
150 
151     err = mecherrmap_init(&m);
152     if (err)
153         return err;
154     err = k5_mutex_finish_init(&mutex);
155     if (err) {
156         mecherrmap_destroy(&m);
157         return err;
158     }
159 
160     return 0;
161 }
162 
163 /* Currently the enumeration template doesn't handle freeing
164    element storage when destroying the collection.  */
free_one(OM_uint32 i,struct mecherror value,void * p)165 static int free_one(OM_uint32 i, struct mecherror value, void *p)
166 {
167     if (value.mech.length && value.mech.elements)
168         free(value.mech.elements);
169     return 0;
170 }
171 
gssint_mecherrmap_destroy(void)172 void gssint_mecherrmap_destroy(void)
173 {
174     mecherrmap_foreach(&m, free_one, NULL);
175     mecherrmap_destroy(&m);
176     k5_mutex_destroy(&mutex);
177 }
178 
gssint_mecherrmap_map(OM_uint32 minor,const gss_OID_desc * oid)179 OM_uint32 gssint_mecherrmap_map(OM_uint32 minor, const gss_OID_desc * oid)
180 {
181     const struct mecherror *mep;
182     struct mecherror me, me_copy;
183     const OM_uint32 *p;
184     int err;
185     OM_uint32 new_status;
186 
187 #ifdef DEBUG
188     FILE *f;
189     f = fopen("/dev/pts/9", "w+");
190     if (f == NULL)
191         f = stderr;
192 #endif
193 
194     me.code = minor;
195     me.mech = *oid;
196     err = k5_mutex_lock(&mutex);
197     if (err) {
198 #ifdef DEBUG
199         if (f != stderr) fclose(f);
200 #endif
201         return 0;
202     }
203 
204     /* Is this status+oid already mapped?  */
205     p = mecherrmap_findright(&m, me);
206     if (p != NULL) {
207         k5_mutex_unlock(&mutex);
208 #ifdef DEBUG
209         fprintf(f, "%s: found ", __func__);
210         mecherror_print(me, f);
211         fprintf(f, " in map as %lu\n", (unsigned long) *p);
212         if (f != stderr) fclose(f);
213 #endif
214         return *p;
215     }
216     /* Is this status code already mapped to something else
217        mech-specific?  */
218     mep = mecherrmap_findleft(&m, minor);
219     if (mep == NULL) {
220         /* Map it to itself plus this mech-oid.  */
221         new_status = minor;
222     } else {
223         /* Already assigned.  Pick a fake new value and map it.  */
224         /* There's a theoretical infinite loop risk here, if we fill
225            in 2**32 values.  Also, returning 0 has a special
226            meaning.  */
227         do {
228             next_fake++;
229             new_status = next_fake;
230             if (new_status == 0)
231                 /* ??? */;
232         } while (mecherrmap_findleft(&m, new_status) != NULL);
233     }
234     err = mecherror_copy(&me_copy, me);
235     if (err) {
236         k5_mutex_unlock(&mutex);
237         return err;
238     }
239     err = mecherrmap_add(&m, new_status, me_copy);
240     k5_mutex_unlock(&mutex);
241     if (err) {
242         if (me_copy.mech.length)
243             free(me_copy.mech.elements);
244     }
245 #ifdef DEBUG
246     fprintf(f, "%s: mapping ", __func__);
247     mecherror_print(me, f);
248     fprintf(f, " to %lu: err=%d\nnew map: ", (unsigned long) new_status, err);
249     mecherrmap_printmap(&m, f);
250     fprintf(f, "\n");
251     if (f != stderr) fclose(f);
252 #endif
253 
254     if (err)
255         return 0;
256     else
257         return new_status;
258 }
259 
260 static gss_OID_desc no_oid = { 0, 0 };
gssint_mecherrmap_map_errcode(OM_uint32 errcode)261 OM_uint32 gssint_mecherrmap_map_errcode(OM_uint32 errcode)
262 {
263     return gssint_mecherrmap_map(errcode, &no_oid);
264 }
265 
gssint_mecherrmap_get(OM_uint32 minor,gss_OID mech_oid,OM_uint32 * mech_minor)266 int gssint_mecherrmap_get(OM_uint32 minor, gss_OID mech_oid,
267                           OM_uint32 *mech_minor)
268 {
269     const struct mecherror *p;
270     int err;
271 
272     if (minor == 0) {
273         return EINVAL;
274     }
275     err = k5_mutex_lock(&mutex);
276     if (err)
277         return err;
278     p = mecherrmap_findleft(&m, minor);
279     k5_mutex_unlock(&mutex);
280     if (!p) {
281         return EINVAL;
282     }
283     *mech_oid = p->mech;
284     *mech_minor = p->code;
285     return 0;
286 }
287