xref: /curl/lib/vtls/keylog.c (revision bcec0840)
1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
9  *
10  * This software is licensed as described in the file COPYING, which
11  * you should have received as part of this distribution. The terms
12  * are also available at https://curl.se/docs/copyright.html.
13  *
14  * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15  * copies of the Software, and permit persons to whom the Software is
16  * furnished to do so, under the terms of the COPYING file.
17  *
18  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19  * KIND, either express or implied.
20  *
21  * SPDX-License-Identifier: curl
22  *
23  ***************************************************************************/
24 #include "curl_setup.h"
25 
26 #if defined(USE_OPENSSL) || \
27   defined(USE_GNUTLS) || \
28   defined(USE_WOLFSSL) || \
29   (defined(USE_NGTCP2) && defined(USE_NGHTTP3)) || \
30   defined(USE_QUICHE)
31 
32 #include "keylog.h"
33 #include <curl/curl.h>
34 
35 /* The last #include files should be: */
36 #include "curl_memory.h"
37 #include "memdebug.h"
38 
39 #define KEYLOG_LABEL_MAXLEN (sizeof("CLIENT_HANDSHAKE_TRAFFIC_SECRET") - 1)
40 
41 #define CLIENT_RANDOM_SIZE  32
42 
43 /*
44  * The master secret in TLS 1.2 and before is always 48 bytes. In TLS 1.3, the
45  * secret size depends on the cipher suite's hash function which is 32 bytes
46  * for SHA-256 and 48 bytes for SHA-384.
47  */
48 #define SECRET_MAXLEN       48
49 
50 
51 /* The fp for the open SSLKEYLOGFILE, or NULL if not open */
52 static FILE *keylog_file_fp;
53 
54 void
Curl_tls_keylog_open(void)55 Curl_tls_keylog_open(void)
56 {
57   char *keylog_file_name;
58 
59   if(!keylog_file_fp) {
60     keylog_file_name = curl_getenv("SSLKEYLOGFILE");
61     if(keylog_file_name) {
62       keylog_file_fp = fopen(keylog_file_name, FOPEN_APPENDTEXT);
63       if(keylog_file_fp) {
64 #ifdef _WIN32
65         if(setvbuf(keylog_file_fp, NULL, _IONBF, 0))
66 #else
67         if(setvbuf(keylog_file_fp, NULL, _IOLBF, 4096))
68 #endif
69         {
70           fclose(keylog_file_fp);
71           keylog_file_fp = NULL;
72         }
73       }
74       Curl_safefree(keylog_file_name);
75     }
76   }
77 }
78 
79 void
Curl_tls_keylog_close(void)80 Curl_tls_keylog_close(void)
81 {
82   if(keylog_file_fp) {
83     fclose(keylog_file_fp);
84     keylog_file_fp = NULL;
85   }
86 }
87 
88 bool
Curl_tls_keylog_enabled(void)89 Curl_tls_keylog_enabled(void)
90 {
91   return keylog_file_fp != NULL;
92 }
93 
94 bool
Curl_tls_keylog_write_line(const char * line)95 Curl_tls_keylog_write_line(const char *line)
96 {
97   /* The current maximum valid keylog line length LF and NUL is 195. */
98   size_t linelen;
99   char buf[256];
100 
101   if(!keylog_file_fp || !line) {
102     return FALSE;
103   }
104 
105   linelen = strlen(line);
106   if(linelen == 0 || linelen > sizeof(buf) - 2) {
107     /* Empty line or too big to fit in a LF and NUL. */
108     return FALSE;
109   }
110 
111   memcpy(buf, line, linelen);
112   if(line[linelen - 1] != '\n') {
113     buf[linelen++] = '\n';
114   }
115   buf[linelen] = '\0';
116 
117   /* Using fputs here instead of fprintf since libcurl's fprintf replacement
118      may not be thread-safe. */
119   fputs(buf, keylog_file_fp);
120   return TRUE;
121 }
122 
123 bool
Curl_tls_keylog_write(const char * label,const unsigned char client_random[CLIENT_RANDOM_SIZE],const unsigned char * secret,size_t secretlen)124 Curl_tls_keylog_write(const char *label,
125                       const unsigned char client_random[CLIENT_RANDOM_SIZE],
126                       const unsigned char *secret, size_t secretlen)
127 {
128   const char *hex = "0123456789ABCDEF";
129   size_t pos, i;
130   char line[KEYLOG_LABEL_MAXLEN + 1 + 2 * CLIENT_RANDOM_SIZE + 1 +
131             2 * SECRET_MAXLEN + 1 + 1];
132 
133   if(!keylog_file_fp) {
134     return FALSE;
135   }
136 
137   pos = strlen(label);
138   if(pos > KEYLOG_LABEL_MAXLEN || !secretlen || secretlen > SECRET_MAXLEN) {
139     /* Should never happen - sanity check anyway. */
140     return FALSE;
141   }
142 
143   memcpy(line, label, pos);
144   line[pos++] = ' ';
145 
146   /* Client Random */
147   for(i = 0; i < CLIENT_RANDOM_SIZE; i++) {
148     line[pos++] = hex[client_random[i] >> 4];
149     line[pos++] = hex[client_random[i] & 0xF];
150   }
151   line[pos++] = ' ';
152 
153   /* Secret */
154   for(i = 0; i < secretlen; i++) {
155     line[pos++] = hex[secret[i] >> 4];
156     line[pos++] = hex[secret[i] & 0xF];
157   }
158   line[pos++] = '\n';
159   line[pos] = '\0';
160 
161   /* Using fputs here instead of fprintf since libcurl's fprintf replacement
162      may not be thread-safe. */
163   fputs(line, keylog_file_fp);
164   return TRUE;
165 }
166 
167 #endif  /* TLS or QUIC backend */
168