1/* 2 * Copyright (c) 2001, 2003, 2005 - 2006 Kungliga Tekniska Högskolan 3 * (Royal Institute of Technology, Stockholm, Sweden). 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * 3. Neither the name of the Institute nor the names of its contributors 18 * may be used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 */ 33 34#include "krb5_locl.h" 35 36#undef HEIMDAL_PRINTF_ATTRIBUTE 37#define HEIMDAL_PRINTF_ATTRIBUTE(x) 38 39/** 40 * Clears the error message from the Kerberos 5 context. 41 * 42 * @param context The Kerberos 5 context to clear 43 * 44 * @ingroup krb5_error 45 */ 46 47KRB5_LIB_FUNCTION void KRB5_LIB_CALL 48krb5_clear_error_message(krb5_context context) 49{ 50 HEIMDAL_MUTEX_lock(context->mutex); 51 if (context->error_string) 52 free(context->error_string); 53 context->error_code = 0; 54 context->error_string = NULL; 55 HEIMDAL_MUTEX_unlock(context->mutex); 56} 57 58/** 59 * Set the context full error string for a specific error code. 60 * The error that is stored should be internationalized. 61 * 62 * The if context is NULL, no error string is stored. 63 * 64 * @param context Kerberos 5 context 65 * @param ret The error code 66 * @param fmt Error string for the error code 67 * @param ... printf(3) style parameters. 68 * 69 * @ingroup krb5_error 70 */ 71 72KRB5_LIB_FUNCTION void KRB5_LIB_CALL 73krb5_set_error_message(krb5_context context, krb5_error_code ret, 74 const char *fmt, ...) 75 HEIMDAL_PRINTF_ATTRIBUTE((printf, 3, 4)) 76{ 77 va_list ap; 78 79 va_start(ap, fmt); 80 krb5_vset_error_message (context, ret, fmt, ap); 81 va_end(ap); 82} 83 84/** 85 * Set the context full error string for a specific error code. 86 * 87 * The if context is NULL, no error string is stored. 88 * 89 * @param context Kerberos 5 context 90 * @param ret The error code 91 * @param fmt Error string for the error code 92 * @param args printf(3) style parameters. 93 * 94 * @ingroup krb5_error 95 */ 96 97 98KRB5_LIB_FUNCTION void KRB5_LIB_CALL 99krb5_vset_error_message (krb5_context context, krb5_error_code ret, 100 const char *fmt, va_list args) 101 HEIMDAL_PRINTF_ATTRIBUTE((printf, 3, 0)) 102{ 103 int r; 104 105 if (context == NULL) 106 return; 107 108 HEIMDAL_MUTEX_lock(context->mutex); 109 if (context->error_string) { 110 free(context->error_string); 111 context->error_string = NULL; 112 } 113 context->error_code = ret; 114 r = vasprintf(&context->error_string, fmt, args); 115 if (r < 0) 116 context->error_string = NULL; 117 HEIMDAL_MUTEX_unlock(context->mutex); 118 if (context->error_string) 119 _krb5_debugx(context, 100, "set-error: %d: %s", (int)ret, context->error_string); 120} 121 122/** 123 * Prepend the context full error string for a specific error code. 124 * The error that is stored should be internationalized. 125 * 126 * The if context is NULL, no error string is stored. 127 * 128 * @param context Kerberos 5 context 129 * @param ret The error code 130 * @param fmt Error string for the error code 131 * @param ... printf(3) style parameters. 132 * 133 * @ingroup krb5_error 134 */ 135 136KRB5_LIB_FUNCTION void KRB5_LIB_CALL 137krb5_prepend_error_message(krb5_context context, krb5_error_code ret, 138 const char *fmt, ...) 139 HEIMDAL_PRINTF_ATTRIBUTE((printf, 3, 4)) 140{ 141 va_list ap; 142 143 va_start(ap, fmt); 144 krb5_vprepend_error_message(context, ret, fmt, ap); 145 va_end(ap); 146} 147 148/** 149 * Prepend the contexts's full error string for a specific error code. 150 * 151 * The if context is NULL, no error string is stored. 152 * 153 * @param context Kerberos 5 context 154 * @param ret The error code 155 * @param fmt Error string for the error code 156 * @param args printf(3) style parameters. 157 * 158 * @ingroup krb5_error 159 */ 160 161KRB5_LIB_FUNCTION void KRB5_LIB_CALL 162krb5_vprepend_error_message(krb5_context context, krb5_error_code ret, 163 const char *fmt, va_list args) 164 HEIMDAL_PRINTF_ATTRIBUTE((printf, 3, 0)) 165{ 166 char *str = NULL, *str2 = NULL; 167 168 if (context == NULL) 169 return; 170 171 HEIMDAL_MUTEX_lock(context->mutex); 172 if (context->error_code != ret) { 173 HEIMDAL_MUTEX_unlock(context->mutex); 174 return; 175 } 176 if (vasprintf(&str, fmt, args) < 0 || str == NULL) { 177 HEIMDAL_MUTEX_unlock(context->mutex); 178 return; 179 } 180 if (context->error_string) { 181 int e; 182 183 e = asprintf(&str2, "%s: %s", str, context->error_string); 184 free(context->error_string); 185 if (e < 0 || str2 == NULL) 186 context->error_string = NULL; 187 else 188 context->error_string = str2; 189 free(str); 190 } else 191 context->error_string = str; 192 HEIMDAL_MUTEX_unlock(context->mutex); 193} 194 195/** 196 * Return the error message for `code' in context. On memory 197 * allocation error the function returns NULL. 198 * 199 * @param context Kerberos 5 context 200 * @param code Error code related to the error 201 * 202 * @return an error string, needs to be freed with 203 * krb5_free_error_message(). The functions return NULL on error. 204 * 205 * @ingroup krb5_error 206 */ 207 208KRB5_LIB_FUNCTION const char * KRB5_LIB_CALL 209krb5_get_error_message(krb5_context context, krb5_error_code code) 210{ 211 char *str = NULL; 212 const char *cstr = NULL; 213 char buf[128]; 214 int free_context = 0; 215 216 if (code == 0) 217 return strdup("Success"); 218 219 /* 220 * The MIT version of this function ignores the krb5_context 221 * and several widely deployed applications call krb5_get_error_message() 222 * with a NULL context in order to translate an error code as a 223 * replacement for error_message(). Another reason a NULL context 224 * might be provided is if the krb5_init_context() call itself 225 * failed. 226 */ 227 if (context) 228 { 229 HEIMDAL_MUTEX_lock(context->mutex); 230 if (context->error_string && 231 (code == context->error_code || context->error_code == 0)) 232 { 233 str = strdup(context->error_string); 234 } 235 HEIMDAL_MUTEX_unlock(context->mutex); 236 237 if (str) 238 return str; 239 } 240 else 241 { 242 if (krb5_init_context(&context) == 0) 243 free_context = 1; 244 } 245 246 if (context) 247 cstr = com_right_r(context->et_list, code, buf, sizeof(buf)); 248 249 if (free_context) 250 krb5_free_context(context); 251 252 if (cstr) 253 return strdup(cstr); 254 255 cstr = error_message(code); 256 if (cstr) 257 return strdup(cstr); 258 259 if (asprintf(&str, "<unknown error: %d>", (int)code) == -1 || str == NULL) 260 return NULL; 261 262 return str; 263} 264 265 266/** 267 * Free the error message returned by krb5_get_error_message(). 268 * 269 * @param context Kerberos context 270 * @param msg error message to free, returned by 271 * krb5_get_error_message(). NULL is ok (nothing freed). 272 * 273 * @ingroup krb5_error 274 */ 275 276KRB5_LIB_FUNCTION void KRB5_LIB_CALL 277krb5_free_error_message(krb5_context context, const char *msg) 278{ 279 if (msg) 280 free(rk_UNCONST(msg)); 281} 282 283 284/** 285 * Return the error string for the error code. The caller must not 286 * free the string. 287 * 288 * This function is deprecated since its not threadsafe. 289 * 290 * @param context Kerberos 5 context. 291 * @param code Kerberos error code. 292 * 293 * @return the error message matching code 294 * 295 * @ingroup krb5 296 */ 297 298KRB5_LIB_FUNCTION const char* KRB5_LIB_CALL 299krb5_get_err_text(krb5_context context, krb5_error_code code) 300 KRB5_DEPRECATED_FUNCTION("Use krb5_get_error_message instead") 301{ 302 const char *p = NULL; 303 if(context != NULL) 304 p = com_right(context->et_list, code); 305 if(p == NULL) 306 p = strerror(code); 307 if (p == NULL) 308 p = "Unknown error"; 309 return p; 310} 311 312#ifdef __APPLE__ 313 314/** 315 * Extracts out the error string from the CFError if there is one and 316 * returns the new error code if the error is a CommonErrorCode error. 317 * 318 * If the error is not a CommonErrorCode error, the error code passed 319 * in is used instead. 320 */ 321 322KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 323_krb5_vset_cf_error_message (krb5_context context, 324 krb5_error_code ret, 325 CFErrorRef error, 326 const char *fmt, 327 va_list args) 328 HEIMDAL_PRINTF_ATTRIBUTE((printf, 4, 0)) 329{ 330 char *str = NULL, *error_string = NULL; 331 CFStringRef description = NULL; 332 CFDictionaryRef userInfo = NULL; 333 CFBooleanRef commonError = NULL; 334 int r; 335 336 if (error) { 337 description = CFErrorCopyDescription(error); 338 if (description) { 339 str = rk_cfstring2cstring(description); 340 CFRelease(description); 341 } 342 userInfo = CFErrorCopyUserInfo(error); 343 if (userInfo) { 344 commonError = CFDictionaryGetValue(userInfo, CFSTR("CommonErrorCode")); 345 if (commonError && CFGetTypeID(commonError) == CFBooleanGetTypeID() && CFBooleanGetValue(commonError)) 346 ret = (krb5_error_code)CFErrorGetCode(error); 347 CFRelease(userInfo); 348 } 349 } 350 351 r = vasprintf(&error_string, fmt, args); 352 if (r < 0 || error_string == NULL) { 353 free(str); 354 return ret; 355 } 356 357 if (str) { 358 krb5_set_error_message(context, ret, "%s: %s", error_string, str); 359 free(str); 360 } else { 361 krb5_set_error_message(context, ret, "%s", error_string); 362 } 363 free(error_string); 364 365 return ret; 366} 367 368/** 369 * Extracts out the error string from the CFError if there is one and 370 * returns the new error code if the error is a CommonErrorCode error. 371 * 372 * If the error is not a CommonErrorCode error, the error code passed 373 * in is used instead. 374 */ 375 376KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 377_krb5_set_cf_error_message(krb5_context context, 378 krb5_error_code ret, 379 CFErrorRef error, 380 const char *fmt, ...) 381 HEIMDAL_PRINTF_ATTRIBUTE((printf, 4, 5)) 382{ 383 krb5_error_code ret2; 384 va_list ap; 385 386 va_start(ap, fmt); 387 ret2 = _krb5_vset_cf_error_message(context, ret, error, fmt, ap); 388 va_end(ap); 389 390 return ret2; 391} 392 393#endif /* __APPLE__ */ 394