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