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