1 /* Can't include krb5.h here, or k5-int.h which includes it, because
2    krb5.h needs to be generated with error tables, after util/et,
3    which builds after this directory.  */
4 #include <stdarg.h>
5 #include <string.h>
6 #include <stdlib.h>
7 #include <stdio.h>
8 #include <k5-err.h>
9 
10 #include "k5-thread.h"
11 #include <k5-platform.h>
12 #include "supp-int.h"
13 
14 #ifdef _WIN32
15 #ifndef vsnprintf
16 #define vsnprintf _vsnprintf
17 #endif
18 #endif
19 
20 /* It would be nice to just use error_message() always.  Pity that
21    it's defined in a library that depends on this one, and we're not
22    allowed to make circular dependencies.  */
23 /* We really want a rwlock here, since we should hold it while calling
24    the function and copying out its results.  But I haven't
25    implemented shims for rwlock yet.  */
26 static k5_mutex_t krb5int_error_info_support_mutex =
27     K5_MUTEX_PARTIAL_INITIALIZER;
28 static const char *(KRB5_CALLCONV *fptr)(long); /* = &error_message */
29 
30 int
krb5int_err_init(void)31 krb5int_err_init (void)
32 {
33     return k5_mutex_finish_init (&krb5int_error_info_support_mutex);
34 }
35 #define initialize()	krb5int_call_thread_support_init()
36 #define lock()		k5_mutex_lock(&krb5int_error_info_support_mutex)
37 #define unlock()	k5_mutex_unlock(&krb5int_error_info_support_mutex)
38 
39 void
krb5int_set_error(struct errinfo * ep,long code,const char * fmt,...)40 krb5int_set_error (struct errinfo *ep, long code, const char *fmt, ...)
41 {
42     va_list args;
43     va_start (args, fmt);
44     krb5int_vset_error (ep, code, fmt, args);
45     va_end (args);
46 }
47 
48 void
krb5int_vset_error(struct errinfo * ep,long code,const char * fmt,va_list args)49 krb5int_vset_error (struct errinfo *ep, long code,
50 		    const char *fmt, va_list args)
51 {
52     char *p;
53 
54     if (ep->msg && ep->msg != ep->scratch_buf) {
55 	free ((void *)ep->msg);
56 	ep->msg = NULL;
57     }
58     ep->code = code;
59 #ifdef HAVE_VASPRINTF
60     {
61 	char *str = NULL;
62 	if (vasprintf(&str, fmt, args) >= 0 && str != NULL) {
63 	    ep->msg = str;
64 	    return;
65 	}
66     }
67 #endif
68     vsnprintf(ep->scratch_buf, sizeof(ep->scratch_buf), fmt, args);
69     p = strdup(ep->scratch_buf);
70     ep->msg = p ? p : ep->scratch_buf;
71 }
72 
73 const char *
krb5int_get_error(struct errinfo * ep,long code)74 krb5int_get_error (struct errinfo *ep, long code)
75 {
76     char *r, *r2;
77     if (code == ep->code && ep->msg) {
78 	r = strdup(ep->msg);
79 	if (r == NULL) {
80 	    strcpy(ep->scratch_buf, _("Out of memory"));
81 	    r = ep->scratch_buf;
82 	}
83 	return r;
84     }
85     if (initialize() != 0) {
86 	strncpy(ep->scratch_buf, _("Kerberos library initialization failure"),
87 		sizeof(ep->scratch_buf));
88 	ep->scratch_buf[sizeof(ep->scratch_buf)-1] = 0;
89 	ep->msg = NULL;
90 	return ep->scratch_buf;
91     }
92     lock();
93     if (fptr == NULL) {
94 	unlock();
95 #ifdef HAVE_STRERROR_R
96 	if (strerror_r (code, ep->scratch_buf, sizeof(ep->scratch_buf)) == 0) {
97 	    char *p = strdup(ep->scratch_buf);
98 	    if (p)
99 		return p;
100 	    return ep->scratch_buf;
101 	}
102 	/* If strerror_r didn't work with the 1K buffer, we can try a
103 	   really big one.  This seems kind of gratuitous though.  */
104 #define BIG_ERR_BUFSIZ 8192
105 	r = malloc(BIG_ERR_BUFSIZ);
106 	if (r) {
107 	    if (strerror_r (code, r, BIG_ERR_BUFSIZ) == 0) {
108 		r2 = realloc (r, 1 + strlen(r));
109 		if (r2)
110 		    return r2;
111 		return r;
112 	    }
113 	    free (r);
114 	}
115 #endif
116 	r = strerror (code);
117 	if (r) {
118 	    if (strlen (r) < sizeof (ep->scratch_buf)
119 		|| (r2 = strdup (r)) == NULL) {
120 		strncpy (ep->scratch_buf, r, sizeof(ep->scratch_buf));
121 		return ep->scratch_buf;
122 	    } else
123 		return r2;
124 	}
125     format_number:
126 	sprintf (ep->scratch_buf, _("error %ld"), code);
127 	return ep->scratch_buf;
128     }
129     r = (char *) fptr(code);
130     if (r == NULL) {
131 	unlock();
132 	goto format_number;
133     }
134     r2 = strdup (r);
135     if (r2 == NULL) {
136 	strncpy(ep->scratch_buf, r, sizeof(ep->scratch_buf));
137 	unlock();
138 	return ep->scratch_buf;
139     } else {
140 	unlock();
141 	return r2;
142     }
143 }
144 
145 void
krb5int_free_error(struct errinfo * ep,const char * msg)146 krb5int_free_error (struct errinfo *ep, const char *msg)
147 {
148     if (msg != ep->scratch_buf)
149 	free ((char *) msg);
150 }
151 
152 void
krb5int_clear_error(struct errinfo * ep)153 krb5int_clear_error (struct errinfo *ep)
154 {
155     krb5int_free_error (ep, ep->msg);
156     ep->msg = NULL;
157 }
158 
159 void
krb5int_set_error_info_callout_fn(const char * (KRB5_CALLCONV * f)(long))160 krb5int_set_error_info_callout_fn (const char *(KRB5_CALLCONV *f)(long))
161 {
162     initialize();
163     lock();
164     fptr = f;
165     unlock();
166 }
167