1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* lib/krb5/krb/kerrs.c - Error message functions */
3 /*
4 * Copyright 2006 Massachusetts Institute of Technology.
5 * All Rights Reserved.
6 *
7 * Export of this software from the United States of America may
8 * require a specific license from the United States Government.
9 * It is the responsibility of any person or organization contemplating
10 * export to obtain such a license before exporting.
11 *
12 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
13 * distribute this software and its documentation for any purpose and
14 * without fee is hereby granted, provided that the above copyright
15 * notice appear in all copies and that both that copyright notice and
16 * this permission notice appear in supporting documentation, and that
17 * the name of M.I.T. not be used in advertising or publicity pertaining
18 * to distribution of the software without specific, written prior
19 * permission. Furthermore if you modify this software you must label
20 * your software as modified software and not distribute it in such a
21 * fashion that it might be confused with the original M.I.T. software.
22 * M.I.T. makes no representations about the suitability of
23 * this software for any purpose. It is provided "as is" without express
24 * or implied warranty.
25 */
26
27 #include "k5-int.h"
28 #include "int-proto.h"
29
30 #ifdef DEBUG
31 static int error_message_debug = 0;
32 #ifndef ERROR_MESSAGE_DEBUG
33 #define ERROR_MESSAGE_DEBUG() (error_message_debug != 0)
34 #endif
35 #endif
36
37 void KRB5_CALLCONV_C
krb5_set_error_message(krb5_context ctx,krb5_error_code code,const char * fmt,...)38 krb5_set_error_message(krb5_context ctx, krb5_error_code code,
39 const char *fmt, ...)
40 {
41 va_list args;
42
43 if (ctx == NULL)
44 return;
45 va_start(args, fmt);
46 #ifdef DEBUG
47 if (ERROR_MESSAGE_DEBUG()) {
48 fprintf(stderr,
49 "krb5_set_error_message(ctx=%p/err=%p, code=%ld, ...)\n",
50 ctx, &ctx->err, (long)code);
51 }
52 #endif
53 k5_vset_error(&ctx->err, code, fmt, args);
54 #ifdef DEBUG
55 if (ERROR_MESSAGE_DEBUG())
56 fprintf(stderr, "->%s\n", ctx->err.msg);
57 #endif
58 va_end(args);
59 }
60
61 void KRB5_CALLCONV
krb5_vset_error_message(krb5_context ctx,krb5_error_code code,const char * fmt,va_list args)62 krb5_vset_error_message(krb5_context ctx, krb5_error_code code,
63 const char *fmt, va_list args)
64 {
65 #ifdef DEBUG
66 if (ERROR_MESSAGE_DEBUG()) {
67 fprintf(stderr, "krb5_vset_error_message(ctx=%p, code=%ld, ...)\n",
68 ctx, (long)code);
69 }
70 #endif
71 if (ctx == NULL)
72 return;
73 k5_vset_error(&ctx->err, code, fmt, args);
74 #ifdef DEBUG
75 if (ERROR_MESSAGE_DEBUG())
76 fprintf(stderr, "->%s\n", ctx->err.msg);
77 #endif
78 }
79
80 void KRB5_CALLCONV
krb5_prepend_error_message(krb5_context ctx,krb5_error_code code,const char * fmt,...)81 krb5_prepend_error_message(krb5_context ctx, krb5_error_code code,
82 const char *fmt, ...)
83 {
84 va_list ap;
85
86 va_start(ap, fmt);
87 krb5_vwrap_error_message(ctx, code, code, fmt, ap);
88 va_end(ap);
89 }
90
91 void KRB5_CALLCONV
krb5_vprepend_error_message(krb5_context ctx,krb5_error_code code,const char * fmt,va_list ap)92 krb5_vprepend_error_message(krb5_context ctx, krb5_error_code code,
93 const char *fmt, va_list ap)
94 {
95 krb5_wrap_error_message(ctx, code, code, fmt, ap);
96 }
97
98 void KRB5_CALLCONV
krb5_wrap_error_message(krb5_context ctx,krb5_error_code old_code,krb5_error_code code,const char * fmt,...)99 krb5_wrap_error_message(krb5_context ctx, krb5_error_code old_code,
100 krb5_error_code code, const char *fmt, ...)
101 {
102 va_list ap;
103
104 va_start(ap, fmt);
105 krb5_vwrap_error_message(ctx, old_code, code, fmt, ap);
106 va_end(ap);
107 }
108
109 void KRB5_CALLCONV
krb5_vwrap_error_message(krb5_context ctx,krb5_error_code old_code,krb5_error_code code,const char * fmt,va_list ap)110 krb5_vwrap_error_message(krb5_context ctx, krb5_error_code old_code,
111 krb5_error_code code, const char *fmt, va_list ap)
112 {
113 const char *prev_msg;
114 char *prepend;
115
116 if (ctx == NULL || vasprintf(&prepend, fmt, ap) < 0)
117 return;
118 prev_msg = k5_get_error(&ctx->err, old_code);
119 k5_set_error(&ctx->err, code, "%s: %s", prepend, prev_msg);
120 k5_free_error(&ctx->err, prev_msg);
121 free(prepend);
122 }
123
124 /* Set the error message state of dest_ctx to that of src_ctx. */
125 void KRB5_CALLCONV
krb5_copy_error_message(krb5_context dest_ctx,krb5_context src_ctx)126 krb5_copy_error_message(krb5_context dest_ctx, krb5_context src_ctx)
127 {
128 if (dest_ctx == src_ctx)
129 return;
130 if (src_ctx->err.msg != NULL) {
131 k5_set_error(&dest_ctx->err, src_ctx->err.code, "%s",
132 src_ctx->err.msg);
133 } else {
134 k5_clear_error(&dest_ctx->err);
135 }
136 }
137
138 /* Re-format msg using the format string err_fmt. Return an allocated result,
139 * or NULL if err_fmt is NULL or on allocation failure. */
140 static char *
err_fmt_fmt(const char * err_fmt,long code,const char * msg)141 err_fmt_fmt(const char *err_fmt, long code, const char *msg)
142 {
143 struct k5buf buf;
144 const char *p, *s;
145
146 if (err_fmt == NULL)
147 return NULL;
148
149 k5_buf_init_dynamic(&buf);
150
151 s = err_fmt;
152 while ((p = strchr(s, '%')) != NULL) {
153 k5_buf_add_len(&buf, s, p - s);
154 s = p;
155 if (p[1] == '\0')
156 break;
157 else if (p[1] == 'M')
158 k5_buf_add(&buf, msg);
159 else if (p[1] == 'C')
160 k5_buf_add_fmt(&buf, "%ld", code);
161 else if (p[1] == '%')
162 k5_buf_add(&buf, "%");
163 else
164 k5_buf_add_fmt(&buf, "%%%c", p[1]);
165 s += 2;
166 }
167 k5_buf_add(&buf, s); /* Remainder after last token */
168 return k5_buf_cstring(&buf);
169 }
170
171 const char * KRB5_CALLCONV
krb5_get_error_message(krb5_context ctx,krb5_error_code code)172 krb5_get_error_message(krb5_context ctx, krb5_error_code code)
173 {
174 const char *std, *custom;
175
176 #ifdef DEBUG
177 if (ERROR_MESSAGE_DEBUG())
178 fprintf(stderr, "krb5_get_error_message(%p, %ld)\n", ctx, (long)code);
179 #endif
180 if (ctx == NULL)
181 return error_message(code);
182
183 std = k5_get_error(&ctx->err, code);
184 custom = err_fmt_fmt(ctx->err_fmt, code, std);
185 if (custom != NULL) {
186 free((char *)std);
187 return custom;
188 }
189 return std;
190 }
191
192 void KRB5_CALLCONV
krb5_free_error_message(krb5_context ctx,const char * msg)193 krb5_free_error_message(krb5_context ctx, const char *msg)
194 {
195 #ifdef DEBUG
196 if (ERROR_MESSAGE_DEBUG())
197 fprintf(stderr, "krb5_free_error_message(%p, %p)\n", ctx, msg);
198 #endif
199 if (ctx == NULL)
200 return;
201 k5_free_error(&ctx->err, msg);
202 }
203
204 void KRB5_CALLCONV
krb5_clear_error_message(krb5_context ctx)205 krb5_clear_error_message(krb5_context ctx)
206 {
207 #ifdef DEBUG
208 if (ERROR_MESSAGE_DEBUG())
209 fprintf(stderr, "krb5_clear_error_message(%p)\n", ctx);
210 #endif
211 if (ctx == NULL)
212 return;
213 k5_clear_error(&ctx->err);
214 }
215
216 void
k5_save_ctx_error(krb5_context ctx,krb5_error_code code,struct errinfo * out)217 k5_save_ctx_error(krb5_context ctx, krb5_error_code code, struct errinfo *out)
218 {
219 out->code = code;
220 out->msg = NULL;
221 if (ctx != NULL && ctx->err.code == code) {
222 out->msg = ctx->err.msg;
223 ctx->err.code = 0;
224 ctx->err.msg = NULL;
225 }
226 }
227
228 krb5_error_code
k5_restore_ctx_error(krb5_context ctx,struct errinfo * in)229 k5_restore_ctx_error(krb5_context ctx, struct errinfo *in)
230 {
231 krb5_error_code code = in->code;
232
233 if (ctx != NULL) {
234 k5_clear_error(&ctx->err);
235 ctx->err.code = in->code;
236 ctx->err.msg = in->msg;
237 in->msg = NULL;
238 } else {
239 k5_clear_error(in);
240 }
241 return code;
242 }
243
244 /* If ctx contains an extended error message for oldcode, change it to be an
245 * extended error message for newcode. */
246 void
k5_change_error_message_code(krb5_context ctx,krb5_error_code oldcode,krb5_error_code newcode)247 k5_change_error_message_code(krb5_context ctx, krb5_error_code oldcode,
248 krb5_error_code newcode)
249 {
250 if (ctx != NULL && ctx->err.msg != NULL && ctx->err.code == oldcode)
251 ctx->err.code = newcode;
252 }
253