b08e8eb987c10783fcc5f71c31db49573b2af9cb
[openssl.git] / crypto / trace.c
1 /*
2  * Copyright 2019 The OpenSSL Project Authors. All Rights Reserved.
3  *
4  * Licensed under the Apache License 2.0 (the "License").  You may not use
5  * this file except in compliance with the License.  You can obtain a copy
6  * in the file LICENSE in the source distribution or at
7  * https://www.openssl.org/source/license.html
8  */
9
10 #include <stdio.h>
11 #include <string.h>
12
13 #include <openssl/bio.h>
14 #include <openssl/crypto.h>
15 #include <openssl/trace.h>
16 #include "internal/bio.h"
17 #include "internal/nelem.h"
18 #include "internal/cryptlib_int.h"
19
20 #include "e_os.h"                /* strcasecmp for Windows */
21
22 #ifndef OPENSSL_NO_TRACE
23
24 static CRYPTO_RWLOCK *trace_lock = NULL;
25
26 static const BIO  *current_channel = NULL;
27
28 /*-
29  * INTERNAL TRACE CHANNEL IMPLEMENTATION
30  *
31  * For our own flexibility, all trace categories are associated with a
32  * BIO sink object, also called the trace channel. Instead of a BIO object,
33  * the application can also provide a callback function, in which case an
34  * internal trace channel is attached, which simply calls the registered
35  * callback function.
36  */
37 static int trace_write(BIO *b, const char *buf,
38                                size_t num, size_t *written);
39 static int trace_puts(BIO *b, const char *str);
40 static long trace_ctrl(BIO *channel, int cmd, long argl, void *argp);
41 static int trace_free(BIO *b);
42
43 static const BIO_METHOD trace_method = {
44     BIO_TYPE_SOURCE_SINK,
45     "trace",
46     trace_write,
47     NULL,                        /* old write */
48     NULL,                        /* read_ex */
49     NULL,                        /* read */
50     trace_puts,
51     NULL,                        /* gets */
52     trace_ctrl,                  /* ctrl */
53     NULL,                        /* create */
54     trace_free,                  /* free */
55     NULL,                        /* callback_ctrl */
56 };
57
58 struct trace_data_st {
59     OSSL_trace_cb callback;
60     int category;
61     void *data;
62 };
63
64 static int trace_write(BIO *channel,
65                        const char *buf, size_t num, size_t *written)
66 {
67     struct trace_data_st *ctx = BIO_get_data(channel);
68     size_t cnt = ctx->callback(buf, num, ctx->category, OSSL_TRACE_CTRL_DURING,
69                                ctx->data);
70
71     *written = cnt;
72     return cnt != 0;
73 }
74
75 static int trace_puts(BIO *channel, const char *str)
76 {
77     size_t written;
78
79     if (trace_write(channel, str, strlen(str), &written))
80         return (int)written;
81
82     return EOF;
83 }
84
85 static long trace_ctrl(BIO *channel, int cmd, long argl, void *argp)
86 {
87     struct trace_data_st *ctx = BIO_get_data(channel);
88
89     switch (cmd) {
90     case OSSL_TRACE_CTRL_BEGIN:
91     case OSSL_TRACE_CTRL_END:
92         /* We know that the callback is likely to return 0 here */
93         ctx->callback("", 0, ctx->category, cmd, ctx->data);
94         return 1;
95     default:
96         break;
97     }
98     return -2;                   /* Unsupported */
99 }
100
101 static int trace_free(BIO *channel)
102 {
103     if (channel == NULL)
104         return 0;
105     OPENSSL_free(BIO_get_data(channel));
106     return 1;
107 }
108 #endif
109
110 /*-
111  * TRACE
112  */
113
114 /* Helper struct and macro to get name string to number mapping */
115 struct trace_category_st {
116     const char * const name;
117     const int num;
118 };
119 #define TRACE_CATEGORY_(name)       { #name, OSSL_TRACE_CATEGORY_##name }
120
121 static const struct trace_category_st trace_categories[] = {
122     TRACE_CATEGORY_(ANY),
123     TRACE_CATEGORY_(INIT),
124     TRACE_CATEGORY_(TLS),
125     TRACE_CATEGORY_(TLS_CIPHER),
126     TRACE_CATEGORY_(ENGINE_CONF),
127     TRACE_CATEGORY_(ENGINE_TABLE),
128     TRACE_CATEGORY_(ENGINE_REF_COUNT),
129     TRACE_CATEGORY_(PKCS5V2),
130     TRACE_CATEGORY_(PKCS12_KEYGEN),
131 };
132
133 const char *OSSL_trace_get_category_name(int num)
134 {
135     size_t i;
136
137     for (i = 0; i < OSSL_NELEM(trace_categories); i++)
138         if (trace_categories[i].num == num)
139             return trace_categories[i].name;
140     return NULL; /* not found */
141 }
142
143 int OSSL_trace_get_category_num(const char *name)
144 {
145     size_t i;
146
147     for (i = 0; i < OSSL_NELEM(trace_categories); i++)
148         if (strcasecmp(name, trace_categories[i].name) == 0)
149             return trace_categories[i].num;
150     return -1; /* not found */
151 }
152
153 #ifndef OPENSSL_NO_TRACE
154
155 /* We use one trace channel for each trace category */
156 static struct {
157     enum { t_channel, t_callback } type;
158     BIO *bio;
159     char *prefix;
160     char *suffix;
161 } trace_channels[OSSL_TRACE_CATEGORY_NUM] = {
162     { 0, NULL, NULL, NULL },
163 };
164
165 #endif
166
167 int ossl_trace_init(void)
168 {
169 #ifndef OPENSSL_NO_TRACE
170     trace_lock = CRYPTO_THREAD_lock_new();
171     if (trace_lock != NULL)
172         return 1;
173 #endif
174
175     return 0;
176 }
177
178 void ossl_trace_cleanup(void)
179 {
180 #ifndef OPENSSL_NO_TRACE
181     int category;
182
183     for (category = 0; category < OSSL_TRACE_CATEGORY_NUM; category++)
184         OSSL_trace_set_channel(category, NULL);
185     CRYPTO_THREAD_lock_free(trace_lock);
186 #endif
187 }
188
189 int OSSL_trace_set_channel(int category, BIO *channel)
190 {
191 #ifndef OPENSSL_NO_TRACE
192     BIO *prev_channel;
193
194     if (category < 0 || category >= OSSL_TRACE_CATEGORY_NUM)
195         goto err;
196
197     prev_channel = trace_channels[category].bio;
198
199     if (prev_channel != NULL) {
200         BIO_free(prev_channel);
201         trace_channels[category].bio = NULL;
202     }
203
204     if (channel == NULL)
205         return 1;                /* Done */
206
207     trace_channels[category].bio = channel;
208     trace_channels[category].type = t_channel;
209
210     return 1;
211
212  err:
213 #endif
214
215     return 0;
216 }
217
218 int OSSL_trace_set_callback(int category, OSSL_trace_cb callback, void *data)
219 {
220 #ifndef OPENSSL_NO_TRACE
221     BIO *channel = trace_channels[category].bio;
222     struct trace_data_st *trace_data = NULL;
223
224     if (channel != NULL) {
225         BIO_free(channel);
226         trace_channels[category].bio = NULL;
227     }
228
229     if (callback == NULL)
230         return 1; /* done */
231
232     channel = BIO_new(&trace_method);
233     if (channel == NULL)
234         goto err;
235
236     trace_data = OPENSSL_zalloc(sizeof(struct trace_data_st));
237     if (trace_data == NULL)
238         goto err;
239
240     trace_data->callback = callback;
241     trace_data->category = category;
242     trace_data->data = data;
243
244     BIO_set_data(channel, trace_data);
245
246     trace_channels[category].bio = channel;
247     trace_channels[category].type = t_callback;
248
249     return 1;
250
251  err:
252     BIO_free(channel);
253     OPENSSL_free(trace_data);
254 #endif
255
256     return 0;
257 }
258
259 int OSSL_trace_set_prefix(int category, const char *prefix)
260 {
261 #ifndef OPENSSL_NO_TRACE
262     char *curr_prefix = trace_channels[category].prefix;
263
264     if (curr_prefix != NULL) {
265         OPENSSL_free(curr_prefix);
266         trace_channels[category].prefix = NULL;
267     }
268
269     if (prefix == NULL)
270         return 1;                /* Done */
271
272     curr_prefix = OPENSSL_strdup(prefix);
273     if (curr_prefix == NULL)
274         goto err;
275
276     trace_channels[category].prefix = curr_prefix;
277
278     return 1;
279
280  err:
281 #endif
282
283     return 0;
284 }
285
286 int OSSL_trace_set_suffix(int category, const char *suffix)
287 {
288 #ifndef OPENSSL_NO_TRACE
289     char *curr_suffix = trace_channels[category].suffix;
290
291     if (curr_suffix != NULL) {
292         OPENSSL_free(curr_suffix);
293         trace_channels[category].suffix = NULL;
294     }
295
296     if (suffix == NULL)
297         return 1; /* done */
298
299     curr_suffix = OPENSSL_strdup(suffix);
300     if (curr_suffix == NULL)
301         goto err;
302
303     trace_channels[category].suffix = curr_suffix;
304
305     return 1;
306
307  err:
308 #endif
309
310     return 0;
311 }
312
313 #ifndef OPENSSL_NO_TRACE
314 static int ossl_trace_get_category(int category)
315 {
316     if (category < 0 || category >= OSSL_TRACE_CATEGORY_NUM)
317         return -1;
318     if (trace_channels[category].bio != NULL)
319         return category;
320     return OSSL_TRACE_CATEGORY_ANY;
321 }
322 #endif
323
324 int OSSL_trace_enabled(int category)
325 {
326     int ret = 0;
327 #ifndef OPENSSL_NO_TRACE
328     category = ossl_trace_get_category(category);
329     ret = trace_channels[category].bio != NULL;
330 #endif
331     return ret;
332 }
333
334 BIO *OSSL_trace_begin(int category)
335 {
336     BIO *channel = NULL;
337 #ifndef OPENSSL_NO_TRACE
338     char *prefix = NULL;
339
340     category = ossl_trace_get_category(category);
341     channel = trace_channels[category].bio;
342     prefix = trace_channels[category].prefix;
343
344     if (channel != NULL) {
345         CRYPTO_THREAD_write_lock(trace_lock);
346         current_channel = channel;
347         switch (trace_channels[category].type) {
348         case t_channel:
349             if (prefix != NULL) {
350                 (void)BIO_puts(channel, prefix);
351                 (void)BIO_puts(channel, "\n");
352             }
353             break;
354         case t_callback:
355             (void)BIO_ctrl(channel, OSSL_TRACE_CTRL_BEGIN,
356                            prefix == NULL ? 0 : strlen(prefix), prefix);
357             break;
358         }
359     }
360 #endif
361     return channel;
362 }
363
364 void OSSL_trace_end(int category, BIO * channel)
365 {
366 #ifndef OPENSSL_NO_TRACE
367     char *suffix = NULL;
368
369     category = ossl_trace_get_category(category);
370     suffix = trace_channels[category].suffix;
371     if (channel != NULL
372         && ossl_assert(channel == current_channel)) {
373         (void)BIO_flush(channel);
374         switch (trace_channels[category].type) {
375         case t_channel:
376             if (suffix != NULL) {
377                 (void)BIO_puts(channel, suffix);
378                 (void)BIO_puts(channel, "\n");
379             }
380             break;
381         case t_callback:
382             (void)BIO_ctrl(channel, OSSL_TRACE_CTRL_END,
383                            suffix == NULL ? 0 : strlen(suffix), suffix);
384             break;
385         }
386         current_channel = NULL;
387         CRYPTO_THREAD_unlock(trace_lock);
388     }
389 #endif
390 }