/src/openssl/crypto/trace.c
Line  | Count  | Source (jump to first uncovered line)  | 
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 "internal/thread_once.h"  | 
14  |  | #include <openssl/bio.h>  | 
15  |  | #include <openssl/crypto.h>  | 
16  |  | #include <openssl/trace.h>  | 
17  |  | #include "internal/bio.h"  | 
18  |  | #include "internal/nelem.h"  | 
19  |  | #include "crypto/cryptlib.h"  | 
20  |  |  | 
21  |  | #include "e_os.h"                /* strcasecmp for Windows */  | 
22  |  |  | 
23  |  | #ifndef OPENSSL_NO_TRACE  | 
24  |  |  | 
25  |  | static CRYPTO_RWLOCK *trace_lock = NULL;  | 
26  |  |  | 
27  |  | static const BIO  *current_channel = NULL;  | 
28  |  |  | 
29  |  | /*-  | 
30  |  |  * INTERNAL TRACE CHANNEL IMPLEMENTATION  | 
31  |  |  *  | 
32  |  |  * For our own flexibility, all trace categories are associated with a  | 
33  |  |  * BIO sink object, also called the trace channel. Instead of a BIO object,  | 
34  |  |  * the application can also provide a callback function, in which case an  | 
35  |  |  * internal trace channel is attached, which simply calls the registered  | 
36  |  |  * callback function.  | 
37  |  |  */  | 
38  |  | static int trace_write(BIO *b, const char *buf,  | 
39  |  |                                size_t num, size_t *written);  | 
40  |  | static int trace_puts(BIO *b, const char *str);  | 
41  |  | static long trace_ctrl(BIO *channel, int cmd, long argl, void *argp);  | 
42  |  | static int trace_free(BIO *b);  | 
43  |  |  | 
44  |  | static const BIO_METHOD trace_method = { | 
45  |  |     BIO_TYPE_SOURCE_SINK,  | 
46  |  |     "trace",  | 
47  |  |     trace_write,  | 
48  |  |     NULL,                        /* old write */  | 
49  |  |     NULL,                        /* read_ex */  | 
50  |  |     NULL,                        /* read */  | 
51  |  |     trace_puts,  | 
52  |  |     NULL,                        /* gets */  | 
53  |  |     trace_ctrl,                  /* ctrl */  | 
54  |  |     NULL,                        /* create */  | 
55  |  |     trace_free,                  /* free */  | 
56  |  |     NULL,                        /* callback_ctrl */  | 
57  |  | };  | 
58  |  |  | 
59  |  | struct trace_data_st { | 
60  |  |     OSSL_trace_cb callback;  | 
61  |  |     int category;  | 
62  |  |     void *data;  | 
63  |  | };  | 
64  |  |  | 
65  |  | static int trace_write(BIO *channel,  | 
66  |  |                        const char *buf, size_t num, size_t *written)  | 
67  |  | { | 
68  |  |     struct trace_data_st *ctx = BIO_get_data(channel);  | 
69  |  |     size_t cnt = ctx->callback(buf, num, ctx->category, OSSL_TRACE_CTRL_WRITE,  | 
70  |  |                                ctx->data);  | 
71  |  |  | 
72  |  |     *written = cnt;  | 
73  |  |     return cnt != 0;  | 
74  |  | }  | 
75  |  |  | 
76  |  | static int trace_puts(BIO *channel, const char *str)  | 
77  |  | { | 
78  |  |     size_t written;  | 
79  |  |  | 
80  |  |     if (trace_write(channel, str, strlen(str), &written))  | 
81  |  |         return (int)written;  | 
82  |  |  | 
83  |  |     return EOF;  | 
84  |  | }  | 
85  |  |  | 
86  |  | static long trace_ctrl(BIO *channel, int cmd, long argl, void *argp)  | 
87  |  | { | 
88  |  |     struct trace_data_st *ctx = BIO_get_data(channel);  | 
89  |  |  | 
90  |  |     switch (cmd) { | 
91  |  |     case OSSL_TRACE_CTRL_BEGIN:  | 
92  |  |     case OSSL_TRACE_CTRL_END:  | 
93  |  |         /* We know that the callback is likely to return 0 here */  | 
94  |  |         ctx->callback("", 0, ctx->category, cmd, ctx->data); | 
95  |  |         return 1;  | 
96  |  |     default:  | 
97  |  |         break;  | 
98  |  |     }  | 
99  |  |     return -2;                   /* Unsupported */  | 
100  |  | }  | 
101  |  |  | 
102  |  | static int trace_free(BIO *channel)  | 
103  |  | { | 
104  |  |     if (channel == NULL)  | 
105  |  |         return 0;  | 
106  |  |     OPENSSL_free(BIO_get_data(channel));  | 
107  |  |     return 1;  | 
108  |  | }  | 
109  |  | #endif  | 
110  |  |  | 
111  |  | /*-  | 
112  |  |  * TRACE  | 
113  |  |  */  | 
114  |  |  | 
115  |  | /* Helper struct and macro to get name string to number mapping */  | 
116  |  | struct trace_category_st { | 
117  |  |     const char * const name;  | 
118  |  |     const int num;  | 
119  |  | };  | 
120  |  | #define TRACE_CATEGORY_(name)       { #name, OSSL_TRACE_CATEGORY_##name } | 
121  |  |  | 
122  |  | static const struct trace_category_st trace_categories[] = { | 
123  |  |     TRACE_CATEGORY_(ALL),  | 
124  |  |     TRACE_CATEGORY_(TRACE),  | 
125  |  |     TRACE_CATEGORY_(INIT),  | 
126  |  |     TRACE_CATEGORY_(TLS),  | 
127  |  |     TRACE_CATEGORY_(TLS_CIPHER),  | 
128  |  |     TRACE_CATEGORY_(CONF),  | 
129  |  |     TRACE_CATEGORY_(ENGINE_TABLE),  | 
130  |  |     TRACE_CATEGORY_(ENGINE_REF_COUNT),  | 
131  |  |     TRACE_CATEGORY_(PKCS5V2),  | 
132  |  |     TRACE_CATEGORY_(PKCS12_KEYGEN),  | 
133  |  |     TRACE_CATEGORY_(PKCS12_DECRYPT),  | 
134  |  |     TRACE_CATEGORY_(X509V3_POLICY),  | 
135  |  |     TRACE_CATEGORY_(BN_CTX),  | 
136  |  |     TRACE_CATEGORY_(STORE),  | 
137  |  | };  | 
138  |  |  | 
139  |  | const char *OSSL_trace_get_category_name(int num)  | 
140  | 0  | { | 
141  | 0  |     size_t i;  | 
142  |  | 
  | 
143  | 0  |     for (i = 0; i < OSSL_NELEM(trace_categories); i++)  | 
144  | 0  |         if (trace_categories[i].num == num)  | 
145  | 0  |             return trace_categories[i].name;  | 
146  | 0  |     return NULL; /* not found */  | 
147  | 0  | }  | 
148  |  |  | 
149  |  | int OSSL_trace_get_category_num(const char *name)  | 
150  | 0  | { | 
151  | 0  |     size_t i;  | 
152  |  | 
  | 
153  | 0  |     for (i = 0; i < OSSL_NELEM(trace_categories); i++)  | 
154  | 0  |         if (strcasecmp(name, trace_categories[i].name) == 0)  | 
155  | 0  |             return trace_categories[i].num;  | 
156  | 0  |     return -1; /* not found */  | 
157  | 0  | }  | 
158  |  |  | 
159  |  | #ifndef OPENSSL_NO_TRACE  | 
160  |  |  | 
161  |  | /* We use one trace channel for each trace category */  | 
162  |  | static struct { | 
163  |  |     enum { SIMPLE_CHANNEL, CALLBACK_CHANNEL } type; | 
164  |  |     BIO *bio;  | 
165  |  |     char *prefix;  | 
166  |  |     char *suffix;  | 
167  |  | } trace_channels[OSSL_TRACE_CATEGORY_NUM] = { | 
168  |  |     { 0, NULL, NULL, NULL }, | 
169  |  | };  | 
170  |  |  | 
171  |  | #endif  | 
172  |  |  | 
173  |  | #ifndef OPENSSL_NO_TRACE  | 
174  |  |  | 
175  |  | enum { | 
176  |  |     CHANNEL,  | 
177  |  |     PREFIX,  | 
178  |  |     SUFFIX  | 
179  |  | };  | 
180  |  |  | 
181  |  | static int trace_attach_cb(int category, int type, const void *data)  | 
182  |  | { | 
183  |  |     switch (type) { | 
184  |  |     case CHANNEL:  | 
185  |  |         OSSL_TRACE2(TRACE, "Attach channel %p to category '%s'\n",  | 
186  |  |                     data, trace_categories[category].name);  | 
187  |  |         break;  | 
188  |  |     case PREFIX:  | 
189  |  |         OSSL_TRACE2(TRACE, "Attach prefix \"%s\" to category '%s'\n",  | 
190  |  |                     (const char *)data, trace_categories[category].name);  | 
191  |  |         break;  | 
192  |  |     case SUFFIX:  | 
193  |  |         OSSL_TRACE2(TRACE, "Attach suffix \"%s\" to category '%s'\n",  | 
194  |  |                     (const char *)data, trace_categories[category].name);  | 
195  |  |         break;  | 
196  |  |     default:                     /* No clue */  | 
197  |  |         break;  | 
198  |  |     }  | 
199  |  |     return 1;  | 
200  |  | }  | 
201  |  |  | 
202  |  | static int trace_detach_cb(int category, int type, const void *data)  | 
203  |  | { | 
204  |  |     switch (type) { | 
205  |  |     case CHANNEL:  | 
206  |  |         OSSL_TRACE2(TRACE, "Detach channel %p from category '%s'\n",  | 
207  |  |                     data, trace_categories[category].name);  | 
208  |  |         break;  | 
209  |  |     case PREFIX:  | 
210  |  |         OSSL_TRACE2(TRACE, "Detach prefix \"%s\" from category '%s'\n",  | 
211  |  |                     (const char *)data, trace_categories[category].name);  | 
212  |  |         break;  | 
213  |  |     case SUFFIX:  | 
214  |  |         OSSL_TRACE2(TRACE, "Detach suffix \"%s\" from category '%s'\n",  | 
215  |  |                     (const char *)data, trace_categories[category].name);  | 
216  |  |         break;  | 
217  |  |     default:                     /* No clue */  | 
218  |  |         break;  | 
219  |  |     }  | 
220  |  |     return 1;  | 
221  |  | }  | 
222  |  |  | 
223  |  | static int do_ossl_trace_init(void);  | 
224  |  | static CRYPTO_ONCE trace_inited = CRYPTO_ONCE_STATIC_INIT;  | 
225  |  | DEFINE_RUN_ONCE_STATIC(ossl_trace_init)  | 
226  |  | { | 
227  |  |     return do_ossl_trace_init();  | 
228  |  | }  | 
229  |  |  | 
230  |  | static int set_trace_data(int category, int type, BIO **channel,  | 
231  |  |                           const char **prefix, const char **suffix,  | 
232  |  |                           int (*attach_cb)(int, int, const void *),  | 
233  |  |                           int (*detach_cb)(int, int, const void *))  | 
234  |  | { | 
235  |  |     BIO *curr_channel = NULL;  | 
236  |  |     char *curr_prefix = NULL;  | 
237  |  |     char *curr_suffix = NULL;  | 
238  |  |  | 
239  |  |     /* Ensure do_ossl_trace_init() is called once */  | 
240  |  |     if (!RUN_ONCE(&trace_inited, ossl_trace_init))  | 
241  |  |         return 0;  | 
242  |  |  | 
243  |  |     curr_channel = trace_channels[category].bio;  | 
244  |  |     curr_prefix = trace_channels[category].prefix;  | 
245  |  |     curr_suffix = trace_channels[category].suffix;  | 
246  |  |  | 
247  |  |     /* Make sure to run the detach callback first on all data */  | 
248  |  |     if (prefix != NULL && curr_prefix != NULL) { | 
249  |  |         detach_cb(category, PREFIX, curr_prefix);  | 
250  |  |     }  | 
251  |  |  | 
252  |  |     if (suffix != NULL && curr_suffix != NULL) { | 
253  |  |         detach_cb(category, SUFFIX, curr_suffix);  | 
254  |  |     }  | 
255  |  |  | 
256  |  |     if (channel != NULL && curr_channel != NULL) { | 
257  |  |         detach_cb(category, CHANNEL, curr_channel);  | 
258  |  |     }  | 
259  |  |  | 
260  |  |     /* After detach callbacks are done, clear data where appropriate */  | 
261  |  |     if (prefix != NULL && curr_prefix != NULL) { | 
262  |  |         OPENSSL_free(curr_prefix);  | 
263  |  |         trace_channels[category].prefix = NULL;  | 
264  |  |     }  | 
265  |  |  | 
266  |  |     if (suffix != NULL && curr_suffix != NULL) { | 
267  |  |         OPENSSL_free(curr_suffix);  | 
268  |  |         trace_channels[category].suffix = NULL;  | 
269  |  |     }  | 
270  |  |  | 
271  |  |     if (channel != NULL && curr_channel != NULL) { | 
272  |  |         BIO_free(curr_channel);  | 
273  |  |         trace_channels[category].type = 0;  | 
274  |  |         trace_channels[category].bio = NULL;  | 
275  |  |     }  | 
276  |  |  | 
277  |  |     /* Before running callbacks are done, set new data where appropriate */  | 
278  |  |     if (channel != NULL && *channel != NULL) { | 
279  |  |         trace_channels[category].type = type;  | 
280  |  |         trace_channels[category].bio = *channel;  | 
281  |  |     }  | 
282  |  |  | 
283  |  |     if (prefix != NULL && *prefix != NULL) { | 
284  |  |         if ((curr_prefix = OPENSSL_strdup(*prefix)) == NULL)  | 
285  |  |             return 0;  | 
286  |  |         trace_channels[category].prefix = curr_prefix;  | 
287  |  |     }  | 
288  |  |  | 
289  |  |     if (suffix != NULL && *suffix != NULL) { | 
290  |  |         if ((curr_suffix = OPENSSL_strdup(*suffix)) == NULL)  | 
291  |  |             return 0;  | 
292  |  |         trace_channels[category].suffix = curr_suffix;  | 
293  |  |     }  | 
294  |  |  | 
295  |  |     /* Finally, run the attach callback on the new data */  | 
296  |  |     if (channel != NULL && *channel != NULL) { | 
297  |  |         attach_cb(category, CHANNEL, *channel);  | 
298  |  |     }  | 
299  |  |  | 
300  |  |     if (prefix != NULL && *prefix != NULL) { | 
301  |  |         attach_cb(category, PREFIX, *prefix);  | 
302  |  |     }  | 
303  |  |  | 
304  |  |     if (suffix != NULL && *suffix != NULL) { | 
305  |  |         attach_cb(category, SUFFIX, *suffix);  | 
306  |  |     }  | 
307  |  |  | 
308  |  |     return 1;  | 
309  |  | }  | 
310  |  |  | 
311  |  | static int do_ossl_trace_init(void)  | 
312  |  | { | 
313  |  |     trace_lock = CRYPTO_THREAD_lock_new();  | 
314  |  |     return trace_lock != NULL;  | 
315  |  | }  | 
316  |  |  | 
317  |  | #endif  | 
318  |  |  | 
319  |  | void ossl_trace_cleanup(void)  | 
320  | 16  | { | 
321  |  | #ifndef OPENSSL_NO_TRACE  | 
322  |  |     int category;  | 
323  |  |     BIO *channel = NULL;  | 
324  |  |     const char *prefix = NULL;  | 
325  |  |     const char *suffix = NULL;  | 
326  |  |  | 
327  |  |     for (category = 0; category < OSSL_TRACE_CATEGORY_NUM; category++) { | 
328  |  |         /* We force the TRACE category to be treated last */  | 
329  |  |         if (category == OSSL_TRACE_CATEGORY_TRACE)  | 
330  |  |             continue;  | 
331  |  |         set_trace_data(category, 0, &channel, &prefix, &suffix,  | 
332  |  |                        trace_attach_cb, trace_detach_cb);  | 
333  |  |     }  | 
334  |  |     set_trace_data(OSSL_TRACE_CATEGORY_TRACE, 0, &channel,  | 
335  |  |                    &prefix, &suffix,  | 
336  |  |                    trace_attach_cb, trace_detach_cb);  | 
337  |  |     CRYPTO_THREAD_lock_free(trace_lock);  | 
338  |  | #endif  | 
339  | 16  | }  | 
340  |  |  | 
341  |  | int OSSL_trace_set_channel(int category, BIO *channel)  | 
342  | 16  | { | 
343  |  | #ifndef OPENSSL_NO_TRACE  | 
344  |  |     if (category >= 0 && category < OSSL_TRACE_CATEGORY_NUM)  | 
345  |  |         return set_trace_data(category, SIMPLE_CHANNEL, &channel, NULL, NULL,  | 
346  |  |                               trace_attach_cb, trace_detach_cb);  | 
347  |  | #endif  | 
348  | 16  |     return 0;  | 
349  | 16  | }  | 
350  |  |  | 
351  |  | #ifndef OPENSSL_NO_TRACE  | 
352  |  | static int trace_attach_w_callback_cb(int category, int type, const void *data)  | 
353  |  | { | 
354  |  |     switch (type) { | 
355  |  |     case CHANNEL:  | 
356  |  |         OSSL_TRACE2(TRACE,  | 
357  |  |                     "Attach channel %p to category '%s' (with callback)\n",  | 
358  |  |                     data, trace_categories[category].name);  | 
359  |  |         break;  | 
360  |  |     case PREFIX:  | 
361  |  |         OSSL_TRACE2(TRACE, "Attach prefix \"%s\" to category '%s'\n",  | 
362  |  |                     (const char *)data, trace_categories[category].name);  | 
363  |  |         break;  | 
364  |  |     case SUFFIX:  | 
365  |  |         OSSL_TRACE2(TRACE, "Attach suffix \"%s\" to category '%s'\n",  | 
366  |  |                     (const char *)data, trace_categories[category].name);  | 
367  |  |         break;  | 
368  |  |     default:                     /* No clue */  | 
369  |  |         break;  | 
370  |  |     }  | 
371  |  |     return 1;  | 
372  |  | }  | 
373  |  | #endif  | 
374  |  |  | 
375  |  | int OSSL_trace_set_callback(int category, OSSL_trace_cb callback, void *data)  | 
376  | 0  | { | 
377  |  | #ifndef OPENSSL_NO_TRACE  | 
378  |  |     BIO *channel = NULL;  | 
379  |  |     struct trace_data_st *trace_data = NULL;  | 
380  |  |  | 
381  |  |     if (category < 0 || category >= OSSL_TRACE_CATEGORY_NUM)  | 
382  |  |         return 0;  | 
383  |  |  | 
384  |  |     if (callback != NULL) { | 
385  |  |         if ((channel = BIO_new(&trace_method)) == NULL  | 
386  |  |             || (trace_data =  | 
387  |  |                 OPENSSL_zalloc(sizeof(struct trace_data_st))) == NULL)  | 
388  |  |             goto err;  | 
389  |  |  | 
390  |  |         trace_data->callback = callback;  | 
391  |  |         trace_data->category = category;  | 
392  |  |         trace_data->data = data;  | 
393  |  |  | 
394  |  |         BIO_set_data(channel, trace_data);  | 
395  |  |     }  | 
396  |  |  | 
397  |  |     if (!set_trace_data(category, CALLBACK_CHANNEL, &channel, NULL, NULL,  | 
398  |  |                         trace_attach_w_callback_cb, trace_detach_cb))  | 
399  |  |         goto err;  | 
400  |  |  | 
401  |  |     return 1;  | 
402  |  |  | 
403  |  |  err:  | 
404  |  |     BIO_free(channel);  | 
405  |  |     OPENSSL_free(trace_data);  | 
406  |  | #endif  | 
407  |  | 
  | 
408  | 0  |     return 0;  | 
409  | 0  | }  | 
410  |  |  | 
411  |  | int OSSL_trace_set_prefix(int category, const char *prefix)  | 
412  | 0  | { | 
413  |  | #ifndef OPENSSL_NO_TRACE  | 
414  |  |     if (category >= 0 && category < OSSL_TRACE_CATEGORY_NUM)  | 
415  |  |         return set_trace_data(category, 0, NULL, &prefix, NULL,  | 
416  |  |                               trace_attach_cb, trace_detach_cb);  | 
417  |  | #endif  | 
418  | 0  |     return 0;  | 
419  | 0  | }  | 
420  |  |  | 
421  |  | int OSSL_trace_set_suffix(int category, const char *suffix)  | 
422  | 0  | { | 
423  |  | #ifndef OPENSSL_NO_TRACE  | 
424  |  |     if (category >= 0 && category < OSSL_TRACE_CATEGORY_NUM)  | 
425  |  |         return set_trace_data(category, 0, NULL, NULL, &suffix,  | 
426  |  |                               trace_attach_cb, trace_detach_cb);  | 
427  |  | #endif  | 
428  | 0  |     return 0;  | 
429  | 0  | }  | 
430  |  |  | 
431  |  | #ifndef OPENSSL_NO_TRACE  | 
432  |  | static int ossl_trace_get_category(int category)  | 
433  |  | { | 
434  |  |     if (category < 0 || category >= OSSL_TRACE_CATEGORY_NUM)  | 
435  |  |         return -1;  | 
436  |  |     if (trace_channels[category].bio != NULL)  | 
437  |  |         return category;  | 
438  |  |     return OSSL_TRACE_CATEGORY_ALL;  | 
439  |  | }  | 
440  |  | #endif  | 
441  |  |  | 
442  |  | int OSSL_trace_enabled(int category)  | 
443  | 0  | { | 
444  | 0  |     int ret = 0;  | 
445  |  | #ifndef OPENSSL_NO_TRACE  | 
446  |  |     category = ossl_trace_get_category(category);  | 
447  |  |     if (category >= 0)  | 
448  |  |         ret = trace_channels[category].bio != NULL;  | 
449  |  | #endif  | 
450  | 0  |     return ret;  | 
451  | 0  | }  | 
452  |  |  | 
453  |  | BIO *OSSL_trace_begin(int category)  | 
454  | 0  | { | 
455  | 0  |     BIO *channel = NULL;  | 
456  |  | #ifndef OPENSSL_NO_TRACE  | 
457  |  |     char *prefix = NULL;  | 
458  |  |  | 
459  |  |     category = ossl_trace_get_category(category);  | 
460  |  |     if (category < 0)  | 
461  |  |         return NULL;  | 
462  |  |  | 
463  |  |     channel = trace_channels[category].bio;  | 
464  |  |     prefix = trace_channels[category].prefix;  | 
465  |  |  | 
466  |  |     if (channel != NULL) { | 
467  |  |         CRYPTO_THREAD_write_lock(trace_lock);  | 
468  |  |         current_channel = channel;  | 
469  |  |         switch (trace_channels[category].type) { | 
470  |  |         case SIMPLE_CHANNEL:  | 
471  |  |             if (prefix != NULL) { | 
472  |  |                 (void)BIO_puts(channel, prefix);  | 
473  |  |                 (void)BIO_puts(channel, "\n");  | 
474  |  |             }  | 
475  |  |             break;  | 
476  |  |         case CALLBACK_CHANNEL:  | 
477  |  |             (void)BIO_ctrl(channel, OSSL_TRACE_CTRL_BEGIN,  | 
478  |  |                            prefix == NULL ? 0 : strlen(prefix), prefix);  | 
479  |  |             break;  | 
480  |  |         }  | 
481  |  |     }  | 
482  |  | #endif  | 
483  | 0  |     return channel;  | 
484  | 0  | }  | 
485  |  |  | 
486  |  | void OSSL_trace_end(int category, BIO * channel)  | 
487  | 0  | { | 
488  |  | #ifndef OPENSSL_NO_TRACE  | 
489  |  |     char *suffix = NULL;  | 
490  |  |  | 
491  |  |     category = ossl_trace_get_category(category);  | 
492  |  |     suffix = trace_channels[category].suffix;  | 
493  |  |     if (channel != NULL  | 
494  |  |         && ossl_assert(channel == current_channel)) { | 
495  |  |         (void)BIO_flush(channel);  | 
496  |  |         switch (trace_channels[category].type) { | 
497  |  |         case SIMPLE_CHANNEL:  | 
498  |  |             if (suffix != NULL) { | 
499  |  |                 (void)BIO_puts(channel, suffix);  | 
500  |  |                 (void)BIO_puts(channel, "\n");  | 
501  |  |             }  | 
502  |  |             break;  | 
503  |  |         case CALLBACK_CHANNEL:  | 
504  |  |             (void)BIO_ctrl(channel, OSSL_TRACE_CTRL_END,  | 
505  |  |                            suffix == NULL ? 0 : strlen(suffix), suffix);  | 
506  |  |             break;  | 
507  |  |         }  | 
508  |  |         current_channel = NULL;  | 
509  |  |         CRYPTO_THREAD_unlock(trace_lock);  | 
510  |  |     }  | 
511  |  | #endif  | 
512  | 0  | }  |