1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* lib/krb5/os/trace.c - krb5int_trace implementation */
4 * Copyright 2009 by the Massachusetts Institute of Technology.
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.
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.
28 * krb5int_trace is defined in k5-trace.h as a macro or static inline
29 * function, and is called like so:
31 * void krb5int_trace(krb5_context context, const char *fmt, ...)
33 * Arguments may or may not be evaluated, so don't pass argument
34 * expressions with side effects. Tracing support and calls can be
35 * explicitly compiled out with DISABLE_TRACING, but compile-time
36 * support is enabled by default. Tracing calls use a custom
37 * formatter supporting the following format specifications:
43 #ifndef DISABLE_TRACING
45 static void subfmt(krb5_context context, struct k5buf *buf,
46 const char *fmt, ...);
48 /* Return a four-byte hex string from the first two bytes of a SHA-1 hash of a
49 * byte array. Return NULL on failure. */
51 hash_bytes(krb5_context context, const void *ptr, size_t len)
54 krb5_data d = make_data((void *) ptr, len);
57 if (krb5_k_make_checksum(context, CKSUMTYPE_NIST_SHA, NULL, 0, &d,
60 if (cksum.length >= 2)
61 (void) asprintf(&s, "%02X%02X", cksum.contents[0], cksum.contents[1]);
62 krb5_free_checksum_contents(context, &cksum);
67 principal_type_string(krb5_int32 type)
70 case KRB5_NT_UNKNOWN: return "unknown";
71 case KRB5_NT_PRINCIPAL: return "principal";
72 case KRB5_NT_SRV_INST: return "service instance";
73 case KRB5_NT_SRV_HST: return "service with host as instance";
74 case KRB5_NT_SRV_XHST: return "service with host as components";
75 case KRB5_NT_UID: return "unique ID";
76 case KRB5_NT_X500_PRINCIPAL: return "X.509";
77 case KRB5_NT_SMTP_NAME: return "SMTP email";
78 case KRB5_NT_ENTERPRISE_PRINCIPAL: return "Windows 2000 UPN";
79 case KRB5_NT_WELLKNOWN: return "well-known";
80 case KRB5_NT_MS_PRINCIPAL: return "Windows 2000 UPN and SID";
81 case KRB5_NT_MS_PRINCIPAL_AND_ID: return "NT 4 style name";
82 case KRB5_NT_ENT_PRINCIPAL_AND_ID: return "NT 4 style name and SID";
88 trace_format(krb5_context context, const char *fmt, va_list ap)
94 struct conn_state *cs;
97 char addrbuf[NI_MAXHOST], portbuf[NI_MAXSERV], tmpbuf[200], *str;
99 krb5_const_principal princ;
100 const krb5_keyblock *keyblock;
102 const krb5_checksum *cksum;
103 krb5_pa_data **padata;
107 krb5_enctype *etypes, etype;
109 krb5int_buf_init_dynamic(&buf);
111 /* Advance to the next word in braces. */
112 len = strcspn(fmt, "{");
113 krb5int_buf_add_len(&buf, fmt, len);
114 if (fmt[len] == '\0')
117 len = strcspn(fmt, "}");
118 if (fmt[len] == '\0' || len > sizeof(tmpbuf) - 1)
120 memcpy(tmpbuf, fmt, len);
124 /* Process the format word. */
125 if (strcmp(tmpbuf, "int") == 0) {
126 krb5int_buf_add_fmt(&buf, "%d", va_arg(ap, int));
127 } else if (strcmp(tmpbuf, "long") == 0) {
128 krb5int_buf_add_fmt(&buf, "%ld", va_arg(ap, long));
129 } else if (strcmp(tmpbuf, "str") == 0) {
130 p = va_arg(ap, const char *);
131 krb5int_buf_add(&buf, (p == NULL) ? "(null)" : p);
132 } else if (strcmp(tmpbuf, "lenstr") == 0) {
133 len = va_arg(ap, size_t);
134 p = va_arg(ap, const char *);
135 if (p == NULL && len != 0)
136 krb5int_buf_add(&buf, "(null)");
138 krb5int_buf_add_len(&buf, p, len);
139 } else if (strcmp(tmpbuf, "hexlenstr") == 0) {
140 len = va_arg(ap, size_t);
141 p = va_arg(ap, const char *);
142 if (p == NULL && len != 0)
143 krb5int_buf_add(&buf, "(null)");
145 for (i = 0; i < len; i++)
146 krb5int_buf_add_fmt(&buf, "%02X", (unsigned char) p[i]);
148 } else if (strcmp(tmpbuf, "hashlenstr") == 0) {
149 len = va_arg(ap, size_t);
150 p = va_arg(ap, const char *);
151 if (p == NULL && len != 0)
152 krb5int_buf_add(&buf, "(null)");
154 str = hash_bytes(context, p, len);
156 krb5int_buf_add(&buf, str);
159 } else if (strcmp(tmpbuf, "connstate") == 0) {
160 cs = va_arg(ap, struct conn_state *);
161 if (cs->socktype == SOCK_DGRAM)
162 krb5int_buf_add(&buf, "dgram");
163 else if (cs->socktype == SOCK_STREAM)
164 krb5int_buf_add(&buf, "stream");
166 krb5int_buf_add_fmt(&buf, "socktype%d", cs->socktype);
168 if (getnameinfo((struct sockaddr *)&cs->addr, cs->addrlen,
169 addrbuf, sizeof(addrbuf), portbuf, sizeof(portbuf),
170 NI_NUMERICHOST|NI_NUMERICSERV) != 0) {
171 if (cs->family == AF_UNSPEC)
172 krb5int_buf_add(&buf, " AF_UNSPEC");
174 krb5int_buf_add_fmt(&buf, " af%d", cs->family);
176 krb5int_buf_add_fmt(&buf, " %s:%s", addrbuf, portbuf);
177 } else if (strcmp(tmpbuf, "data") == 0) {
178 d = va_arg(ap, krb5_data *);
179 if (d == NULL || (d->length != 0 && d->data == NULL))
180 krb5int_buf_add(&buf, "(null)");
182 krb5int_buf_add_len(&buf, d->data, d->length);
183 } else if (strcmp(tmpbuf, "hexdata") == 0) {
184 d = va_arg(ap, krb5_data *);
186 krb5int_buf_add(&buf, "(null)");
188 subfmt(context, &buf, "{hexlenstr}", d->length, d->data);
189 } else if (strcmp(tmpbuf, "errno") == 0) {
190 err = va_arg(ap, int);
192 #ifdef HAVE_STRERROR_R
193 if (strerror_r(err, tmpbuf, sizeof(tmpbuf)) == 0)
198 krb5int_buf_add_fmt(&buf, "%d/%s", err, p);
199 } else if (strcmp(tmpbuf, "kerr") == 0) {
200 kerr = va_arg(ap, krb5_error_code);
201 p = krb5_get_error_message(context, kerr);
202 krb5int_buf_add_fmt(&buf, "%ld/%s", (long) kerr,
203 kerr ? p : "Success");
204 krb5_free_error_message(context, p);
205 } else if (strcmp(tmpbuf, "keyblock") == 0) {
206 keyblock = va_arg(ap, const krb5_keyblock *);
207 if (keyblock == NULL)
208 krb5int_buf_add(&buf, "(null)");
210 subfmt(context, &buf, "{etype}/{hashlenstr}",
211 keyblock->enctype, keyblock->length,
214 } else if (strcmp(tmpbuf, "key") == 0) {
215 key = va_arg(ap, krb5_key);
217 krb5int_buf_add(&buf, "(null)");
219 subfmt(context, &buf, "{keyblock}", &key->keyblock);
220 } else if (strcmp(tmpbuf, "cksum") == 0) {
221 cksum = va_arg(ap, const krb5_checksum *);
222 data = make_data(cksum->contents, cksum->length);
223 subfmt(context, &buf, "{int}/{hexdata}",
224 (int) cksum->checksum_type, &data);
225 } else if (strcmp(tmpbuf, "princ") == 0) {
226 princ = va_arg(ap, krb5_principal);
227 if (krb5_unparse_name(context, princ, &str) == 0) {
228 krb5int_buf_add(&buf, str);
229 krb5_free_unparsed_name(context, str);
231 } else if (strcmp(tmpbuf, "ptype") == 0) {
232 p = principal_type_string(va_arg(ap, krb5_int32));
233 krb5int_buf_add(&buf, p);
234 } else if (strcmp(tmpbuf, "patypes") == 0) {
235 padata = va_arg(ap, krb5_pa_data **);
236 if (padata == NULL || *padata == NULL)
237 krb5int_buf_add(&buf, "(empty)");
238 for (; padata != NULL && *padata != NULL; padata++) {
239 krb5int_buf_add_fmt(&buf, "%d", (int) (*padata)->pa_type);
240 if (*(padata + 1) != NULL)
241 krb5int_buf_add(&buf, ", ");
243 } else if (strcmp(tmpbuf, "etype") == 0) {
244 etype = va_arg(ap, krb5_enctype);
245 if (krb5_enctype_to_name(etype, TRUE, tmpbuf, sizeof(tmpbuf)) == 0)
246 krb5int_buf_add(&buf, tmpbuf);
248 krb5int_buf_add_fmt(&buf, "%d", (int) etype);
249 } else if (strcmp(tmpbuf, "etypes") == 0) {
250 etypes = va_arg(ap, krb5_enctype *);
251 if (etypes == NULL || *etypes == 0)
252 krb5int_buf_add(&buf, "(empty)");
253 for (; etypes != NULL && *etypes != 0; etypes++) {
254 subfmt(context, &buf, "{etype}", *etypes);
255 if (*(etypes + 1) != 0)
256 krb5int_buf_add(&buf, ", ");
258 } else if (strcmp(tmpbuf, "ccache") == 0) {
259 ccache = va_arg(ap, krb5_ccache);
260 krb5int_buf_add(&buf, krb5_cc_get_type(context, ccache));
261 krb5int_buf_add(&buf, ":");
262 krb5int_buf_add(&buf, krb5_cc_get_name(context, ccache));
263 } else if (strcmp(tmpbuf, "keytab") == 0) {
264 keytab = va_arg(ap, krb5_keytab);
265 if (krb5_kt_get_name(context, keytab, tmpbuf, sizeof(tmpbuf)) == 0)
266 krb5int_buf_add(&buf, tmpbuf);
267 } else if (strcmp(tmpbuf, "creds") == 0) {
268 creds = va_arg(ap, krb5_creds *);
269 subfmt(context, &buf, "{princ} -> {princ}",
270 creds->client, creds->server);
273 return krb5int_buf_data(&buf);
276 /* Allows trace_format formatters to be represented in terms of other
279 subfmt(krb5_context context, struct k5buf *buf, const char *fmt, ...)
285 str = trace_format(context, fmt, ap);
287 krb5int_buf_add(buf, str);
293 krb5int_init_trace(krb5_context context)
295 const char *filename;
297 filename = getenv("KRB5_TRACE");
299 (void) krb5_set_trace_filename(context, filename);
303 krb5int_trace(krb5_context context, const char *fmt, ...)
306 struct krb5_trace_info info;
307 char *str = NULL, *msg = NULL;
308 krb5_int32 sec, usec;
310 if (context == NULL || context->trace_callback == NULL)
313 str = trace_format(context, fmt, ap);
316 if (krb5_crypto_us_timeofday(&sec, &usec) != 0)
318 if (asprintf(&msg, "[%d] %d.%d: %s\n", (int) getpid(), (int) sec,
319 (int) usec, str) < 0)
322 context->trace_callback(context, &info, context->trace_callback_data);
329 krb5_error_code KRB5_CALLCONV
330 krb5_set_trace_callback(krb5_context context, krb5_trace_callback fn,
333 /* Allow the old callback to destroy its data if necessary. */
334 if (context->trace_callback != NULL)
335 context->trace_callback(context, NULL, context->trace_callback_data);
336 context->trace_callback = fn;
337 context->trace_callback_data = cb_data;
341 static void KRB5_CALLCONV
342 file_trace_cb(krb5_context context, const struct krb5_trace_info *info, void *data)
347 /* Null info means destroy the callback data. */
353 (void) write(*fd, info->message, strlen(info->message));
356 krb5_error_code KRB5_CALLCONV
357 krb5_set_trace_filename(krb5_context context, const char *filename)
361 /* Create callback data containing a file descriptor. */
362 fd = malloc(sizeof(*fd));
365 *fd = open(filename, O_WRONLY|O_CREAT|O_APPEND, 0600);
371 return krb5_set_trace_callback(context, file_trace_cb, fd);
374 #else /* DISABLE_TRACING */
376 krb5_error_code KRB5_CALLCONV
377 krb5_set_trace_callback(krb5_context context, krb5_trace_callback fn,
382 return KRB5_TRACE_NOSUPP;
385 krb5_error_code KRB5_CALLCONV
386 krb5_set_trace_filename(krb5_context context, const char *filename)
388 return KRB5_TRACE_NOSUPP;
391 #endif /* DISABLE_TRACING */