/ lib / krb5 / error_string.c
error_string.c
  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  
 47  KRB5_LIB_FUNCTION void KRB5_LIB_CALL
 48  krb5_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  
 72  KRB5_LIB_FUNCTION void KRB5_LIB_CALL
 73  krb5_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  
 98  KRB5_LIB_FUNCTION void KRB5_LIB_CALL
 99  krb5_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  
136  KRB5_LIB_FUNCTION void KRB5_LIB_CALL
137  krb5_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  
161  KRB5_LIB_FUNCTION void KRB5_LIB_CALL
162  krb5_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  
208  KRB5_LIB_FUNCTION const char * KRB5_LIB_CALL
209  krb5_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  
276  KRB5_LIB_FUNCTION void KRB5_LIB_CALL
277  krb5_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  
298  KRB5_LIB_FUNCTION const char* KRB5_LIB_CALL
299  krb5_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  
322  KRB5_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  
376  KRB5_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__ */