178527Sassar/*
2233294Sstas * Copyright (c) 2001, 2003, 2005 - 2006 Kungliga Tekniska H��gskolan
3233294Sstas * (Royal Institute of Technology, Stockholm, Sweden).
4233294Sstas * All rights reserved.
578527Sassar *
6233294Sstas * Redistribution and use in source and binary forms, with or without
7233294Sstas * modification, are permitted provided that the following conditions
8233294Sstas * are met:
978527Sassar *
10233294Sstas * 1. Redistributions of source code must retain the above copyright
11233294Sstas *    notice, this list of conditions and the following disclaimer.
1278527Sassar *
13233294Sstas * 2. Redistributions in binary form must reproduce the above copyright
14233294Sstas *    notice, this list of conditions and the following disclaimer in the
15233294Sstas *    documentation and/or other materials provided with the distribution.
1678527Sassar *
17233294Sstas * 3. Neither the name of the Institute nor the names of its contributors
18233294Sstas *    may be used to endorse or promote products derived from this software
19233294Sstas *    without specific prior written permission.
2078527Sassar *
21233294Sstas * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22233294Sstas * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23233294Sstas * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24233294Sstas * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25233294Sstas * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26233294Sstas * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27233294Sstas * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28233294Sstas * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29233294Sstas * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30233294Sstas * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31233294Sstas * SUCH DAMAGE.
3278527Sassar */
3378527Sassar
3478527Sassar#include "krb5_locl.h"
3578527Sassar
3678527Sassar#undef __attribute__
37233294Sstas#define __attribute__(x)
3878527Sassar
39233294Sstas/**
40233294Sstas * Clears the error message from the Kerberos 5 context.
41233294Sstas *
42233294Sstas * @param context The Kerberos 5 context to clear
43233294Sstas *
44233294Sstas * @ingroup krb5_error
45233294Sstas */
46233294Sstas
47233294SstasKRB5_LIB_FUNCTION void KRB5_LIB_CALL
48233294Sstaskrb5_clear_error_message(krb5_context context)
4978527Sassar{
50178825Sdfr    HEIMDAL_MUTEX_lock(context->mutex);
51233294Sstas    if (context->error_string)
52233294Sstas	free(context->error_string);
53233294Sstas    context->error_code = 0;
54233294Sstas    context->error_string = NULL;
55178825Sdfr    HEIMDAL_MUTEX_unlock(context->mutex);
5678527Sassar}
5778527Sassar
58233294Sstas/**
59233294Sstas * Set the context full error string for a specific error code.
60233294Sstas * The error that is stored should be internationalized.
61233294Sstas *
62233294Sstas * The if context is NULL, no error string is stored.
63233294Sstas *
64233294Sstas * @param context Kerberos 5 context
65233294Sstas * @param ret The error code
66233294Sstas * @param fmt Error string for the error code
67233294Sstas * @param ... printf(3) style parameters.
68233294Sstas *
69233294Sstas * @ingroup krb5_error
70233294Sstas */
71233294Sstas
72233294SstasKRB5_LIB_FUNCTION void KRB5_LIB_CALL
73233294Sstaskrb5_set_error_message(krb5_context context, krb5_error_code ret,
74233294Sstas		       const char *fmt, ...)
75233294Sstas    __attribute__ ((format (printf, 3, 4)))
7678527Sassar{
77233294Sstas    va_list ap;
78233294Sstas
79233294Sstas    va_start(ap, fmt);
80233294Sstas    krb5_vset_error_message (context, ret, fmt, ap);
81233294Sstas    va_end(ap);
82233294Sstas}
83233294Sstas
84233294Sstas/**
85233294Sstas * Set the context full error string for a specific error code.
86233294Sstas *
87233294Sstas * The if context is NULL, no error string is stored.
88233294Sstas *
89233294Sstas * @param context Kerberos 5 context
90233294Sstas * @param ret The error code
91233294Sstas * @param fmt Error string for the error code
92233294Sstas * @param args printf(3) style parameters.
93233294Sstas *
94233294Sstas * @ingroup krb5_error
95233294Sstas */
96233294Sstas
97233294Sstas
98233294SstasKRB5_LIB_FUNCTION void KRB5_LIB_CALL
99233294Sstaskrb5_vset_error_message (krb5_context context, krb5_error_code ret,
100233294Sstas			 const char *fmt, va_list args)
101233294Sstas    __attribute__ ((format (printf, 3, 0)))
102233294Sstas{
103233294Sstas    int r;
104233294Sstas
105233294Sstas    if (context == NULL)
106233294Sstas	return;
107233294Sstas
108178825Sdfr    HEIMDAL_MUTEX_lock(context->mutex);
109233294Sstas    if (context->error_string) {
11078527Sassar	free(context->error_string);
111233294Sstas	context->error_string = NULL;
112233294Sstas    }
113233294Sstas    context->error_code = ret;
114233294Sstas    r = vasprintf(&context->error_string, fmt, args);
115233294Sstas    if (r < 0)
116233294Sstas	context->error_string = NULL;
117178825Sdfr    HEIMDAL_MUTEX_unlock(context->mutex);
11878527Sassar}
11978527Sassar
120233294Sstas/**
121233294Sstas * Prepend the context full error string for a specific error code.
122233294Sstas * The error that is stored should be internationalized.
123233294Sstas *
124233294Sstas * The if context is NULL, no error string is stored.
125233294Sstas *
126233294Sstas * @param context Kerberos 5 context
127233294Sstas * @param ret The error code
128233294Sstas * @param fmt Error string for the error code
129233294Sstas * @param ... printf(3) style parameters.
130233294Sstas *
131233294Sstas * @ingroup krb5_error
132233294Sstas */
133233294Sstas
134233294SstasKRB5_LIB_FUNCTION void KRB5_LIB_CALL
135233294Sstaskrb5_prepend_error_message(krb5_context context, krb5_error_code ret,
136233294Sstas			   const char *fmt, ...)
137233294Sstas    __attribute__ ((format (printf, 3, 4)))
13878527Sassar{
13978527Sassar    va_list ap;
14078527Sassar
14178527Sassar    va_start(ap, fmt);
142233294Sstas    krb5_vprepend_error_message(context, ret, fmt, ap);
14378527Sassar    va_end(ap);
14478527Sassar}
14578527Sassar
146233294Sstas/**
147233294Sstas * Prepend the contexts's full error string for a specific error code.
148233294Sstas *
149233294Sstas * The if context is NULL, no error string is stored.
150233294Sstas *
151233294Sstas * @param context Kerberos 5 context
152233294Sstas * @param ret The error code
153233294Sstas * @param fmt Error string for the error code
154233294Sstas * @param args printf(3) style parameters.
155233294Sstas *
156233294Sstas * @ingroup krb5_error
157233294Sstas */
158233294Sstas
159233294SstasKRB5_LIB_FUNCTION void KRB5_LIB_CALL
160233294Sstaskrb5_vprepend_error_message(krb5_context context, krb5_error_code ret,
161233294Sstas			    const char *fmt, va_list args)
162233294Sstas    __attribute__ ((format (printf, 3, 0)))
16378527Sassar{
164233294Sstas    char *str = NULL, *str2 = NULL;
165233294Sstas
166233294Sstas    if (context == NULL)
167233294Sstas	return;
168233294Sstas
169178825Sdfr    HEIMDAL_MUTEX_lock(context->mutex);
170233294Sstas    if (context->error_code != ret) {
171233294Sstas	HEIMDAL_MUTEX_unlock(context->mutex);
172233294Sstas	return;
17378527Sassar    }
174233294Sstas    if (vasprintf(&str, fmt, args) < 0 || str == NULL) {
175233294Sstas	HEIMDAL_MUTEX_unlock(context->mutex);
176233294Sstas	return;
177233294Sstas    }
178233294Sstas    if (context->error_string) {
179233294Sstas	int e;
180233294Sstas
181233294Sstas	e = asprintf(&str2, "%s: %s", str, context->error_string);
182233294Sstas	free(context->error_string);
183233294Sstas	if (e < 0 || str2 == NULL)
184233294Sstas	    context->error_string = NULL;
185233294Sstas	else
186233294Sstas	    context->error_string = str2;
187233294Sstas	free(str);
188233294Sstas    } else
189233294Sstas	context->error_string = str;
190178825Sdfr    HEIMDAL_MUTEX_unlock(context->mutex);
19178527Sassar}
19278527Sassar
193233294Sstas
194178825Sdfr/**
195178825Sdfr * Return the error message in context. On error or no error string,
196178825Sdfr * the function returns NULL.
197178825Sdfr *
198178825Sdfr * @param context Kerberos 5 context
199178825Sdfr *
200178825Sdfr * @return an error string, needs to be freed with
201233294Sstas * krb5_free_error_message(). The functions return NULL on error.
202178825Sdfr *
203178825Sdfr * @ingroup krb5_error
204178825Sdfr */
205178825Sdfr
206233294SstasKRB5_LIB_FUNCTION char * KRB5_LIB_CALL
20778527Sassarkrb5_get_error_string(krb5_context context)
20878527Sassar{
209178825Sdfr    char *ret = NULL;
210178825Sdfr
211178825Sdfr    HEIMDAL_MUTEX_lock(context->mutex);
212178825Sdfr    if (context->error_string)
213178825Sdfr	ret = strdup(context->error_string);
214178825Sdfr    HEIMDAL_MUTEX_unlock(context->mutex);
21578527Sassar    return ret;
21678527Sassar}
21778527Sassar
218233294SstasKRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
21978527Sassarkrb5_have_error_string(krb5_context context)
22078527Sassar{
221178825Sdfr    char *str;
222178825Sdfr    HEIMDAL_MUTEX_lock(context->mutex);
223178825Sdfr    str = context->error_string;
224178825Sdfr    HEIMDAL_MUTEX_unlock(context->mutex);
225178825Sdfr    return str != NULL;
22678527Sassar}
227178825Sdfr
228178825Sdfr/**
229233294Sstas * Return the error message for `code' in context. On memory
230233294Sstas * allocation error the function returns NULL.
231178825Sdfr *
232178825Sdfr * @param context Kerberos 5 context
233178825Sdfr * @param code Error code related to the error
234178825Sdfr *
235178825Sdfr * @return an error string, needs to be freed with
236233294Sstas * krb5_free_error_message(). The functions return NULL on error.
237178825Sdfr *
238178825Sdfr * @ingroup krb5_error
239178825Sdfr */
240178825Sdfr
241233294SstasKRB5_LIB_FUNCTION const char * KRB5_LIB_CALL
242178825Sdfrkrb5_get_error_message(krb5_context context, krb5_error_code code)
243178825Sdfr{
244233294Sstas    char *str = NULL;
245233294Sstas    const char *cstr = NULL;
246233294Sstas    char buf[128];
247233294Sstas    int free_context = 0;
248178825Sdfr
249233294Sstas    if (code == 0)
250233294Sstas	return strdup("Success");
251178825Sdfr
252233294Sstas    /*
253233294Sstas     * The MIT version of this function ignores the krb5_context
254233294Sstas     * and several widely deployed applications call krb5_get_error_message()
255233294Sstas     * with a NULL context in order to translate an error code as a
256233294Sstas     * replacement for error_message().  Another reason a NULL context
257233294Sstas     * might be provided is if the krb5_init_context() call itself
258233294Sstas     * failed.
259233294Sstas     */
260233294Sstas    if (context)
261233294Sstas    {
262233294Sstas        HEIMDAL_MUTEX_lock(context->mutex);
263233294Sstas        if (context->error_string &&
264233294Sstas            (code == context->error_code || context->error_code == 0))
265233294Sstas        {
266233294Sstas            str = strdup(context->error_string);
267233294Sstas        }
268233294Sstas        HEIMDAL_MUTEX_unlock(context->mutex);
269233294Sstas
270233294Sstas        if (str)
271233294Sstas            return str;
272233294Sstas    }
273233294Sstas    else
274233294Sstas    {
275233294Sstas        if (krb5_init_context(&context) == 0)
276233294Sstas            free_context = 1;
277233294Sstas    }
278233294Sstas
279233294Sstas    if (context)
280233294Sstas        cstr = com_right_r(context->et_list, code, buf, sizeof(buf));
281233294Sstas
282233294Sstas    if (free_context)
283233294Sstas        krb5_free_context(context);
284233294Sstas
285178825Sdfr    if (cstr)
286233294Sstas        return strdup(cstr);
287178825Sdfr
288233294Sstas    cstr = error_message(code);
289233294Sstas    if (cstr)
290233294Sstas        return strdup(cstr);
291233294Sstas
292233294Sstas    if (asprintf(&str, "<unknown error: %d>", (int)code) == -1 || str == NULL)
293178825Sdfr	return NULL;
294178825Sdfr
295178825Sdfr    return str;
296178825Sdfr}
297178825Sdfr
298233294Sstas
299233294Sstas/**
300233294Sstas * Free the error message returned by krb5_get_error_message().
301233294Sstas *
302233294Sstas * @param context Kerberos context
303233294Sstas * @param msg error message to free, returned byg
304233294Sstas *        krb5_get_error_message().
305233294Sstas *
306233294Sstas * @ingroup krb5_error
307233294Sstas */
308233294Sstas
309233294SstasKRB5_LIB_FUNCTION void KRB5_LIB_CALL
310233294Sstaskrb5_free_error_message(krb5_context context, const char *msg)
311233294Sstas{
312233294Sstas    free(rk_UNCONST(msg));
313233294Sstas}
314233294Sstas
315233294Sstas
316233294Sstas/**
317233294Sstas * Return the error string for the error code. The caller must not
318233294Sstas * free the string.
319233294Sstas *
320233294Sstas * This function is deprecated since its not threadsafe.
321233294Sstas *
322233294Sstas * @param context Kerberos 5 context.
323233294Sstas * @param code Kerberos error code.
324233294Sstas *
325233294Sstas * @return the error message matching code
326233294Sstas *
327233294Sstas * @ingroup krb5
328233294Sstas */
329233294Sstas
330233294SstasKRB5_LIB_FUNCTION const char* KRB5_LIB_CALL
331233294Sstaskrb5_get_err_text(krb5_context context, krb5_error_code code)
332233294Sstas    KRB5_DEPRECATED_FUNCTION("Use X instead")
333233294Sstas{
334233294Sstas    const char *p = NULL;
335233294Sstas    if(context != NULL)
336233294Sstas	p = com_right(context->et_list, code);
337233294Sstas    if(p == NULL)
338233294Sstas	p = strerror(code);
339233294Sstas    if (p == NULL)
340233294Sstas	p = "Unknown error";
341233294Sstas    return p;
342233294Sstas}
343