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 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 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 82 print_OM_uint32 (OM_uint32 value, FILE *f) 83 { 84 fprintf(f, "%lu", (unsigned long) value); 85 } 86 87 static inline int 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 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 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. */ 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 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 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 }; 261 OM_uint32 gssint_mecherrmap_map_errcode(OM_uint32 errcode) 262 { 263 return gssint_mecherrmap_map(errcode, &no_oid); 264 } 265 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