1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /*
3 * Copyright 2007, 2008 by the Massachusetts Institute of Technology.
4 * All Rights Reserved.
5 *
6 * Export of this software from the United States of America may
7 * require a specific license from the United States Government.
8 * It is the responsibility of any person or organization contemplating
9 * export to obtain such a license before exporting.
10 *
11 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
12 * distribute this software and its documentation for any purpose and
13 * without fee is hereby granted, provided that the above copyright
14 * notice appear in all copies and that both that copyright notice and
15 * this permission notice appear in supporting documentation, and that
16 * the name of M.I.T. not be used in advertising or publicity pertaining
17 * to distribution of the software without specific, written prior
18 * permission. Furthermore if you modify this software you must label
19 * your software as modified software and not distribute it in such a
20 * fashion that it might be confused with the original M.I.T. software.
21 * M.I.T. makes no representations about the suitability of
22 * this software for any purpose. It is provided "as is" without express
23 * or implied warranty.
24 *
25 */
26
27 #include "gssapiP_generic.h"
28 #include <mglueP.h>
29 #include <string.h>
30 #ifndef _WIN32
31 #include <unistd.h>
32 #endif
33
34 /* The mapping table is 0-based, but let's export codes that are
35 1-based, keeping 0 for errors or unknown errors.
36
37 The elements in the mapping table currently have separate copies of
38 each OID stored. This is a bit wasteful, but we are assuming the
39 table isn't likely to grow very large. */
40
41 struct mecherror {
42 gss_OID_desc mech;
43 OM_uint32 code;
44 };
45
46 static inline int
cmp_OM_uint32(OM_uint32 m1,OM_uint32 m2)47 cmp_OM_uint32(OM_uint32 m1, OM_uint32 m2)
48 {
49 if (m1 < m2)
50 return -1;
51 else if (m1 > m2)
52 return 1;
53 else
54 return 0;
55 }
56
57 static inline int
mecherror_cmp(struct mecherror m1,struct mecherror m2)58 mecherror_cmp(struct mecherror m1, struct mecherror m2)
59 {
60 if (m1.code < m2.code)
61 return -1;
62 if (m1.code > m2.code)
63 return 1;
64 if (m1.mech.length < m2.mech.length)
65 return -1;
66 if (m1.mech.length > m2.mech.length)
67 return 1;
68 if (m1.mech.length == 0)
69 return 0;
70 return memcmp(m1.mech.elements, m2.mech.elements, m1.mech.length);
71 }
72
73 static void
print_OM_uint32(OM_uint32 value,FILE * f)74 print_OM_uint32 (OM_uint32 value, FILE *f)
75 {
76 fprintf(f, "%lu", (unsigned long) value);
77 }
78
79 static inline int
mecherror_copy(struct mecherror * dest,struct mecherror src)80 mecherror_copy(struct mecherror *dest, struct mecherror src)
81 {
82 *dest = src;
83 if (src.mech.length > 0) {
84 dest->mech.elements = malloc(src.mech.length);
85 if (dest->mech.elements == NULL)
86 return ENOMEM;
87 memcpy(dest->mech.elements, src.mech.elements, src.mech.length);
88 } else {
89 dest->mech.elements = NULL;
90 }
91 return 0;
92 }
93
94 static void
mecherror_print(struct mecherror value,FILE * f)95 mecherror_print(struct mecherror value, FILE *f)
96 {
97 OM_uint32 minor;
98 gss_buffer_desc str;
99 static const struct {
100 const char *oidstr, *name;
101 } mechnames[] = {
102 { "{ 1 2 840 113554 1 2 2 }", "krb5-new" },
103 { "{ 1 3 5 1 5 2 }", "krb5-old" },
104 { "{ 1 2 840 48018 1 2 2 }", "krb5-microsoft" },
105 { "{ 1 3 6 1 5 5 2 }", "spnego" },
106 };
107 unsigned int i;
108
109 fprintf(f, "%lu@", (unsigned long) value.code);
110
111 if (value.mech.length == 0) {
112 fprintf(f, "(com_err)");
113 return;
114 }
115 fprintf(f, "%p=", value.mech.elements);
116 if (generic_gss_oid_to_str(&minor, &value.mech, &str)) {
117 fprintf(f, "(error in conversion)");
118 return;
119 }
120 /* Note: generic_gss_oid_to_str returns a null-terminated string. */
121 for (i = 0; i < sizeof(mechnames)/sizeof(mechnames[0]); i++) {
122 if (!strcmp(str.value, mechnames[i].oidstr) && mechnames[i].name != 0) {
123 fprintf(f, "%s", mechnames[i].name);
124 break;
125 }
126 }
127 if (i == sizeof(mechnames)/sizeof(mechnames[0]))
128 fprintf(f, "%s", (char *) str.value);
129 generic_gss_release_buffer(&minor, &str);
130 }
131
132 #include "errmap.h"
133 #include "krb5.h" /* for KRB5KRB_AP_WRONG_PRINC */
134
135 static mecherrmap m;
136 static k5_mutex_t mutex = K5_MUTEX_PARTIAL_INITIALIZER;
137 static OM_uint32 next_fake = 100000;
138
gssint_mecherrmap_init(void)139 int gssint_mecherrmap_init(void)
140 {
141 int err;
142
143 err = mecherrmap_init(&m);
144 if (err)
145 return err;
146 err = k5_mutex_finish_init(&mutex);
147 if (err) {
148 mecherrmap_destroy(&m);
149 return err;
150 }
151
152 return 0;
153 }
154
155 /* Currently the enumeration template doesn't handle freeing
156 element storage when destroying the collection. */
free_one(OM_uint32 i,struct mecherror value,void * p)157 static int free_one(OM_uint32 i, struct mecherror value, void *p)
158 {
159 free(value.mech.elements);
160 return 0;
161 }
162
gssint_mecherrmap_destroy(void)163 void gssint_mecherrmap_destroy(void)
164 {
165 mecherrmap_foreach(&m, free_one, NULL);
166 mecherrmap_destroy(&m);
167 k5_mutex_destroy(&mutex);
168 }
169
gssint_mecherrmap_map(OM_uint32 minor,const gss_OID_desc * oid)170 OM_uint32 gssint_mecherrmap_map(OM_uint32 minor, const gss_OID_desc * oid)
171 {
172 const struct mecherror *mep;
173 struct mecherror me, me_copy;
174 const OM_uint32 *p;
175 int err;
176 OM_uint32 new_status;
177
178 #ifdef DEBUG
179 FILE *f;
180 f = fopen("/dev/pts/9", "w+");
181 if (f == NULL)
182 f = stderr;
183 #endif
184
185 if (gssint_mechglue_initialize_library() != 0)
186 return 0;
187
188 me.code = minor;
189 me.mech = *oid;
190 k5_mutex_lock(&mutex);
191
192 /* Is this status+oid already mapped? */
193 p = mecherrmap_findright(&m, me);
194 if (p != NULL) {
195 k5_mutex_unlock(&mutex);
196 #ifdef DEBUG
197 fprintf(f, "%s: found ", __FUNCTION__);
198 mecherror_print(me, f);
199 fprintf(f, " in map as %lu\n", (unsigned long) *p);
200 if (f != stderr) fclose(f);
201 #endif
202 return *p;
203 }
204 /* Is this status code already mapped to something else
205 mech-specific? */
206 mep = mecherrmap_findleft(&m, minor);
207 if (mep == NULL) {
208 /* Map it to itself plus this mech-oid. */
209 new_status = minor;
210 } else {
211 /* Already assigned. Pick a fake new value and map it. */
212 /* There's a theoretical infinite loop risk here, if we fill
213 in 2**32 values. Also, returning 0 has a special
214 meaning. */
215 do {
216 next_fake++;
217 new_status = next_fake;
218 if (new_status == 0)
219 /* ??? */;
220 } while (mecherrmap_findleft(&m, new_status) != NULL);
221 }
222 err = mecherror_copy(&me_copy, me);
223 if (err) {
224 k5_mutex_unlock(&mutex);
225 return err;
226 }
227 err = mecherrmap_add(&m, new_status, me_copy);
228 k5_mutex_unlock(&mutex);
229 if (err)
230 free(me_copy.mech.elements);
231 #ifdef DEBUG
232 fprintf(f, "%s: mapping ", __FUNCTION__);
233 mecherror_print(me, f);
234 fprintf(f, " to %lu: err=%d\nnew map: ", (unsigned long) new_status, err);
235 mecherrmap_printmap(&m, f);
236 fprintf(f, "\n");
237 if (f != stderr) fclose(f);
238 #endif
239 if (err)
240 return 0;
241 else
242 return new_status;
243 }
244
245 static gss_OID_desc no_oid = { 0, 0 };
gssint_mecherrmap_map_errcode(OM_uint32 errcode)246 OM_uint32 gssint_mecherrmap_map_errcode(OM_uint32 errcode)
247 {
248 return gssint_mecherrmap_map(errcode, &no_oid);
249 }
250
gssint_mecherrmap_get(OM_uint32 minor,gss_OID mech_oid,OM_uint32 * mech_minor)251 int gssint_mecherrmap_get(OM_uint32 minor, gss_OID mech_oid,
252 OM_uint32 *mech_minor)
253 {
254 const struct mecherror *p;
255
256 if (minor == 0 || gssint_mechglue_initialize_library() != 0) {
257 return EINVAL;
258 }
259 k5_mutex_lock(&mutex);
260 p = mecherrmap_findleft(&m, minor);
261 k5_mutex_unlock(&mutex);
262 if (!p) {
263 return EINVAL;
264 }
265 *mech_oid = p->mech;
266 *mech_minor = p->code;
267 return 0;
268 }
269