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