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 trace_format(krb5_context context, const char *fmt, va_list ap)
73 struct conn_state *cs;
76 char addrbuf[NI_MAXHOST], portbuf[NI_MAXSERV], tmpbuf[200], *str;
78 krb5_const_principal princ;
79 const krb5_keyblock *keyblock;
81 const krb5_checksum *cksum;
82 krb5_pa_data **padata;
86 krb5_enctype *etypes, etype;
88 krb5int_buf_init_dynamic(&buf);
90 /* Advance to the next word in braces. */
91 len = strcspn(fmt, "{");
92 krb5int_buf_add_len(&buf, fmt, len);
96 len = strcspn(fmt, "}");
97 if (fmt[len] == '\0' || len > sizeof(tmpbuf) - 1)
99 memcpy(tmpbuf, fmt, len);
103 /* Process the format word. */
104 if (strcmp(tmpbuf, "int") == 0) {
105 krb5int_buf_add_fmt(&buf, "%d", va_arg(ap, int));
106 } else if (strcmp(tmpbuf, "long") == 0) {
107 krb5int_buf_add_fmt(&buf, "%ld", va_arg(ap, long));
108 } else if (strcmp(tmpbuf, "str") == 0) {
109 p = va_arg(ap, const char *);
110 krb5int_buf_add(&buf, (p == NULL) ? "(null)" : p);
111 } else if (strcmp(tmpbuf, "lenstr") == 0) {
112 len = va_arg(ap, size_t);
113 p = va_arg(ap, const char *);
114 if (p == NULL && len != 0)
115 krb5int_buf_add(&buf, "(null)");
117 krb5int_buf_add_len(&buf, p, len);
118 } else if (strcmp(tmpbuf, "hexlenstr") == 0) {
119 len = va_arg(ap, size_t);
120 p = va_arg(ap, const char *);
121 if (p == NULL && len != 0)
122 krb5int_buf_add(&buf, "(null)");
124 for (i = 0; i < len; i++)
125 krb5int_buf_add_fmt(&buf, "%02X", (unsigned char) p[i]);
127 } else if (strcmp(tmpbuf, "hashlenstr") == 0) {
128 len = va_arg(ap, size_t);
129 p = va_arg(ap, const char *);
130 if (p == NULL && len != 0)
131 krb5int_buf_add(&buf, "(null)");
133 str = hash_bytes(context, p, len);
135 krb5int_buf_add(&buf, str);
138 } else if (strcmp(tmpbuf, "connstate") == 0) {
139 cs = va_arg(ap, struct conn_state *);
140 if (cs->socktype == SOCK_DGRAM)
141 krb5int_buf_add(&buf, "dgram");
142 else if (cs->socktype == SOCK_STREAM)
143 krb5int_buf_add(&buf, "stream");
145 krb5int_buf_add_fmt(&buf, "socktype%d", cs->socktype);
147 if (getnameinfo((struct sockaddr *)&cs->addr, cs->addrlen,
148 addrbuf, sizeof(addrbuf), portbuf, sizeof(portbuf),
149 NI_NUMERICHOST|NI_NUMERICSERV) != 0) {
150 if (cs->family == AF_UNSPEC)
151 krb5int_buf_add(&buf, " AF_UNSPEC");
153 krb5int_buf_add_fmt(&buf, " af%d", cs->family);
155 krb5int_buf_add_fmt(&buf, " %s:%s", addrbuf, portbuf);
156 } else if (strcmp(tmpbuf, "data") == 0) {
157 d = va_arg(ap, krb5_data *);
158 if (d == NULL || (d->length != 0 && d->data == NULL))
159 krb5int_buf_add(&buf, "(null)");
161 krb5int_buf_add_len(&buf, d->data, d->length);
162 } else if (strcmp(tmpbuf, "hexdata") == 0) {
163 d = va_arg(ap, krb5_data *);
165 krb5int_buf_add(&buf, "(null)");
167 subfmt(context, &buf, "{hexlenstr}", d->length, d->data);
168 } else if (strcmp(tmpbuf, "errno") == 0) {
169 err = va_arg(ap, int);
171 #ifdef HAVE_STRERROR_R
172 if (strerror_r(err, tmpbuf, sizeof(tmpbuf)) == 0)
177 krb5int_buf_add_fmt(&buf, "%d/%s", err, p);
178 } else if (strcmp(tmpbuf, "kerr") == 0) {
179 kerr = va_arg(ap, krb5_error_code);
180 p = krb5_get_error_message(context, kerr);
181 krb5int_buf_add_fmt(&buf, "%ld/%s", (long) kerr,
182 kerr ? p : "Success");
183 krb5_free_error_message(context, p);
184 } else if (strcmp(tmpbuf, "keyblock") == 0) {
185 keyblock = va_arg(ap, const krb5_keyblock *);
186 if (keyblock == NULL)
187 krb5int_buf_add(&buf, "(null)");
189 subfmt(context, &buf, "{etype}/{hashlenstr}",
190 keyblock->enctype, keyblock->length,
193 } else if (strcmp(tmpbuf, "key") == 0) {
194 key = va_arg(ap, krb5_key);
196 krb5int_buf_add(&buf, "(null");
198 subfmt(context, &buf, "{keyblock}", &key->keyblock);
199 } else if (strcmp(tmpbuf, "cksum") == 0) {
200 cksum = va_arg(ap, const krb5_checksum *);
201 data = make_data(cksum->contents, cksum->length);
202 subfmt(context, &buf, "{int}/{hexdata}",
203 (int) cksum->checksum_type, &data);
204 } else if (strcmp(tmpbuf, "princ") == 0) {
205 princ = va_arg(ap, krb5_principal);
206 if (krb5_unparse_name(context, princ, &str) == 0) {
207 krb5int_buf_add(&buf, str);
208 krb5_free_unparsed_name(context, str);
210 } else if (strcmp(tmpbuf, "patypes") == 0) {
211 padata = va_arg(ap, krb5_pa_data **);
212 if (padata == NULL || *padata == NULL)
213 krb5int_buf_add(&buf, "(empty)");
214 for (; padata != NULL && *padata != NULL; padata++) {
215 krb5int_buf_add_fmt(&buf, "%d", (int) (*padata)->pa_type);
216 if (*(padata + 1) != NULL)
217 krb5int_buf_add(&buf, ", ");
219 } else if (strcmp(tmpbuf, "etype") == 0) {
220 etype = va_arg(ap, krb5_enctype);
221 if (krb5_enctype_to_name(etype, TRUE, tmpbuf, sizeof(tmpbuf)) == 0)
222 krb5int_buf_add(&buf, tmpbuf);
224 krb5int_buf_add_fmt(&buf, "%d", (int) etype);
225 } else if (strcmp(tmpbuf, "etypes") == 0) {
226 etypes = va_arg(ap, krb5_enctype *);
227 if (etypes == NULL || *etypes == 0)
228 krb5int_buf_add(&buf, "(empty");
229 for (; etypes != NULL && *etypes != 0; etypes++) {
230 subfmt(context, &buf, "{etype}", *etypes);
231 if (*(etypes + 1) != 0)
232 krb5int_buf_add(&buf, ", ");
234 } else if (strcmp(tmpbuf, "ccache") == 0) {
235 ccache = va_arg(ap, krb5_ccache);
236 krb5int_buf_add(&buf, krb5_cc_get_type(context, ccache));
237 krb5int_buf_add(&buf, ":");
238 krb5int_buf_add(&buf, krb5_cc_get_name(context, ccache));
239 } else if (strcmp(tmpbuf, "keytab") == 0) {
240 keytab = va_arg(ap, krb5_keytab);
241 if (krb5_kt_get_name(context, keytab, tmpbuf, sizeof(tmpbuf)) == 0)
242 krb5int_buf_add(&buf, tmpbuf);
243 } else if (strcmp(tmpbuf, "creds") == 0) {
244 creds = va_arg(ap, krb5_creds *);
245 subfmt(context, &buf, "{princ} -> {princ}",
246 creds->client, creds->server);
249 return krb5int_buf_data(&buf);
252 /* Allows trace_format formatters to be represented in terms of other
255 subfmt(krb5_context context, struct k5buf *buf, const char *fmt, ...)
261 str = trace_format(context, fmt, ap);
263 krb5int_buf_add(buf, str);
269 krb5int_init_trace(krb5_context context)
271 const char *filename;
273 filename = getenv("KRB5_TRACE");
275 (void) krb5_set_trace_filename(context, filename);
279 krb5int_trace(krb5_context context, const char *fmt, ...)
282 struct krb5_trace_info info;
283 char *str = NULL, *msg = NULL;
284 krb5_int32 sec, usec;
286 if (context == NULL || context->trace_callback == NULL)
289 str = trace_format(context, fmt, ap);
292 if (krb5_crypto_us_timeofday(&sec, &usec) != 0)
294 if (asprintf(&msg, "[%d] %d.%d: %s\n", (int) getpid(), (int) sec,
295 (int) usec, str) < 0)
298 context->trace_callback(context, &info, context->trace_callback_data);
305 krb5_error_code KRB5_CALLCONV
306 krb5_set_trace_callback(krb5_context context, krb5_trace_callback fn,
309 /* Allow the old callback to destroy its data if necessary. */
310 if (context->trace_callback != NULL)
311 context->trace_callback(context, NULL, context->trace_callback_data);
312 context->trace_callback = fn;
313 context->trace_callback_data = cb_data;
317 static void KRB5_CALLCONV
318 file_trace_cb(krb5_context context, const struct krb5_trace_info *info, void *data)
323 /* Null info means destroy the callback data. */
329 (void) write(*fd, info->message, strlen(info->message));
332 krb5_error_code KRB5_CALLCONV
333 krb5_set_trace_filename(krb5_context context, const char *filename)
337 /* Create callback data containing a file descriptor. */
338 fd = malloc(sizeof(*fd));
341 *fd = open(filename, O_WRONLY|O_CREAT|O_APPEND, 0600);
347 return krb5_set_trace_callback(context, file_trace_cb, fd);
350 #else /* DISABLE_TRACING */
352 krb5_error_code KRB5_CALLCONV
353 krb5_set_trace_callback(krb5_context context, krb5_trace_callback fn,
358 return KRB5_TRACE_NOSUPP;
361 krb5_error_code KRB5_CALLCONV
362 krb5_set_trace_filename(krb5_context context, const char *filename)
364 return KRB5_TRACE_NOSUPP;
367 #endif /* DISABLE_TRACING */