├── .gitignore ├── ld_preload └── Makefile ├── test.py ├── frida └── README.md ├── README.md ├── gdb └── sslkeylog.py └── sslkeylog.c /.gitignore: -------------------------------------------------------------------------------- 1 | out 2 | *.swp 3 | __pycache__ 4 | premaster.txt 5 | *.so 6 | -------------------------------------------------------------------------------- /ld_preload/Makefile: -------------------------------------------------------------------------------- 1 | all: 2 | cc sslkeylog.c -shared -o libsslkeylog.so -fPIC -ldl 3 | clean: 4 | rm libsslkeylog.so 5 | -------------------------------------------------------------------------------- /test.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import requests 3 | 4 | print(requests.get("https://heise.de", allow_redirects=False).text) 5 | -------------------------------------------------------------------------------- /frida/README.md: -------------------------------------------------------------------------------- 1 | # the frida way 2 | 3 | - read this: https://www.nccgroup.com/us/about-us/newsroom-and-events/blog/2019/august/datajack-proxy-intercepting-tls-in-native-applications/ 4 | - then use this: https://github.com/nccgroup/DatajackProxy 5 | - or this: https://github.com/rgov/ssl_logger 6 | - but think about this: https://github.com/nccgroup/DatajackProxy/issues/2 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # generic way to look into ssl traffic (hooking libssl library calls and dumping the key) 2 | 3 | credits: https://security.stackexchange.com/questions/80158/extract-pre-master-keys-from-an-openssl-application 4 | 5 | ## the LD_PRELOAD way 6 | ### How to 7 | 1. cc sslkeylog.c -shared -o libsslkeylog.so -fPIC -ldl 8 | 2. sudo tcpdump -i any port 443 -w out 9 | 3. SSLKEYLOGFILE=premaster.txt LD_PRELOAD=./libsslkeylog.so curl https://heise.de 10 | 4. wireshark -o ssl.keylog_file:premaster.txt out 11 | 12 | -------------------------------------------------------------------------------- /gdb/sslkeylog.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | r''' 3 | Extract SSL/DTLS keys from programs that use OpenSSL. 4 | 5 | Example usage, attach to an existing process, put keys in premaster.txt: 6 | 7 | PYTHONPATH=. \ 8 | gdb -q -ex 'py import sslkeylog as skl; skl.start("premaster.txt")' \ 9 | -p `pidof curl` 10 | 11 | Run a new program while outputting keys to the default stderr 12 | (you can set envvar SSLKEYLOGFILE to override this): 13 | 14 | PYTHONPATH=. gdb -q -ex 'py import sslkeylog as skl; skl.start()' \ 15 | -ex r --args curl https://example.com 16 | 17 | 18 | Recommended configuration: copy this file to ~/.gdb/sslkeylog.py and put 19 | the following in your ~/.gdbinit: 20 | 21 | python 22 | import sys, os.path 23 | #sys.dont_write_bytecode = True # avoid *.pyc clutter 24 | sys.path.insert(0, os.path.expanduser('~/.gdb')) 25 | import sslkeylog as skl 26 | # Override default keylog (SSLKEYLOGFILE env or stderr) 27 | #skl.keylog_filename = '/tmp/premaster.txt' 28 | end 29 | 30 | define skl-batch 31 | dont-repeat 32 | handle all noprint pass 33 | handle SIGINT noprint pass 34 | py skl.start() 35 | end 36 | 37 | Then you can simply execute: 38 | 39 | gdb -q -ex 'py skl.start()' -p `pidof curl` 40 | 41 | To stop capturing keys, detach GDB or invoke 'skl.stop()' 42 | 43 | If you are not interested in debugging the program, and only want to 44 | extract keys, use the skl-batch command defined in gdbinit: 45 | 46 | SSLKEYLOGFILE=premaster.txt gdb -batch -ex skl-batch -p `pidof curl` 47 | 48 | To stop capturing keys early, send SIGTERM to gdb. (Note that SIGTRAP is 49 | used internally for breakpoints and should not be ignored.) 50 | ''' 51 | 52 | import gdb 53 | import errno 54 | from os import getenv 55 | 56 | # Default filename for new Keylog instances. 57 | #keylog_filename = getenv('SSLKEYLOGFILE', '/dev/stderr') 58 | keylog_filename = getenv('SSLKEYLOGFILE', 'keys.log') 59 | _SSL_KEYLOG_HEADER = '# Automatically generated by sslkeylog.py\n' 60 | 61 | def _read_as_hex(value, size): 62 | addr = value.address 63 | data = gdb.selected_inferior().read_memory(addr, size) 64 | return ''.join('%02X' % ord(x) for x in data) 65 | 66 | def _ssl_get_master_key(ssl_ptr): 67 | session = ssl_ptr['session'] 68 | if session != 0 and session['master_key_length'] > 0: 69 | return _read_as_hex(session['master_key'], 48) 70 | return None 71 | 72 | def get_keylog_line(ssl_ptr): 73 | ''' 74 | Returns (client_random, master_key) for the current SSL session. 75 | ''' 76 | mk = _ssl_get_master_key(ssl_ptr) 77 | s3 = ssl_ptr['s3'] 78 | if s3 == 0 or mk is None: 79 | return 80 | 81 | cr = _read_as_hex(s3['client_random'], 32) 82 | # Maybe optimize storage by using Session ID if available? 83 | #sid = _read_as_hex(self.ssl_ptr['session']['session_id'], 32) 84 | return (cr, mk) 85 | 86 | class SKLFinishBreakpoint(gdb.FinishBreakpoint): 87 | '''Breaks on points where new key material is possibly available.''' 88 | def __init__(self, ssl_ptr, key_listener): 89 | # Mark as internal, it is expected to be gone as soon as this quits. 90 | gdb.FinishBreakpoint.__init__(self, internal=True) 91 | self.ssl_ptr = ssl_ptr 92 | self.key_listener = key_listener 93 | 94 | def stop(self): 95 | # Attempt to recover key material. 96 | info = get_keylog_line(self.ssl_ptr) 97 | if info: 98 | # Line consists of a cache key and actual key log line 99 | self.key_listener.notify(*info) 100 | return False # Continue execution 101 | 102 | class SKLBreakpoint(gdb.Breakpoint): 103 | '''Breaks at function entrance and registers a finish breakpoint.''' 104 | def __init__(self, spec, key_listener): 105 | gdb.Breakpoint.__init__(self, spec) 106 | self.key_listener = key_listener 107 | 108 | def stop(self): 109 | # Retrieve SSL* parameter. 110 | ssl_ptr = gdb.selected_frame().read_var('s') 111 | #ssl_ptr = gdb.selected_frame().read_var('ssl') 112 | 113 | # Proceed with handshakes (finish function) before checking for keys. 114 | SKLFinishBreakpoint(ssl_ptr, self.key_listener) 115 | 116 | # Increase hit count for debugging (info breakpoints) 117 | # This number will be decremented when execution continues. 118 | self.ignore_count += 1 119 | 120 | return False # Continue execution 121 | 122 | class Keylog(object): 123 | '''Listens for new key material and writes them to a file.''' 124 | def __init__(self, keylog_file): 125 | self.keylog_file = keylog_file 126 | # Remember written lines to avoid printing duplicates. 127 | self.written_items = set() 128 | 129 | def notify(self, client_random, master_key): 130 | '''Puts a new entry in the key log if not already known.''' 131 | if client_random not in self.written_items: 132 | line = 'CLIENT_RANDOM %s %s\n' % (client_random, master_key) 133 | self.keylog_file.write(line.encode('ascii')) 134 | 135 | # Assume client random is random enough as cache key. 136 | cache_key = client_random 137 | self.written_items.add(cache_key) 138 | 139 | def close(): 140 | self.keylog_file.close() 141 | 142 | @classmethod 143 | def create(cls, filename): 144 | def needs_header(f): 145 | try: 146 | # Might fail for pipes (such as stdin). 147 | return f.tell() == 0 148 | except: 149 | return False 150 | 151 | # Byte output is needed for unbuffered I/O 152 | try: 153 | f = open(filename, 'ab', 0) 154 | except OSError as e: 155 | # Older gdb try to seek when append is requested. If seeking is not 156 | # possible (for stderr or pipes), use plain write mode. 157 | if e.errno == errno.ESPIPE: 158 | f = open(filename, 'wb', 0) 159 | else: 160 | raise 161 | if needs_header(f): 162 | f.write(_SSL_KEYLOG_HEADER.encode('ascii')) 163 | return cls(f) 164 | 165 | # A shared Keylog instance. 166 | _keylog_file = None 167 | 168 | def start(sslkeylogfile=None, cont=True): 169 | ''' 170 | :param sslkeylogfile: optional SSL keylog file name (overrides 171 | SSLKEYLOGFILE environment variable and its fallback value). 172 | :param cont: True to continue this process when paused. 173 | ''' 174 | global keylog_filename 175 | if sslkeylogfile: 176 | keylog_filename = sslkeylogfile 177 | enable() 178 | 179 | # Continue the process when it was already started before. 180 | if cont and gdb.selected_thread(): 181 | gdb.execute('continue') 182 | 183 | def stop(): 184 | '''Remove all breakpoints and close the key logfile.''' 185 | global _keylog_file 186 | if not _keylog_file: 187 | print('No active keylog session') 188 | return 189 | disable() 190 | _keylog_file.close() 191 | print('Logged %d entries in total' % _keylog_file.written_items) 192 | _keylog_file = None 193 | 194 | 195 | # Remember enabled breakpoints 196 | _locations = { name: None for name in ( 197 | 'SSL_connect', 198 | 'SSL_do_handshake', 199 | 'SSL_accept', 200 | 'SSL_read', 201 | 'SSL_write', 202 | )} 203 | 204 | def enable(): 205 | '''Enable all SSL-related breakpoints.''' 206 | global _keylog_file 207 | if not _keylog_file: 208 | _keylog_file = Keylog.create(keylog_filename) 209 | print('Started logging SSL keys to %s' % keylog_filename) 210 | for name, breakpoint in _locations.items(): 211 | if breakpoint: 212 | print('Breakpoint for %s is already active, ignoring' % name) 213 | continue 214 | _locations[name] = SKLBreakpoint(name, _keylog_file) 215 | 216 | def disable(): 217 | '''Disable all SSL-related breakpoints.''' 218 | for name, breakpoint in _locations.items(): 219 | if breakpoint: 220 | msg = 'Deleting breakpoint %d' % breakpoint.number 221 | msg += ' (%s)' % breakpoint.location 222 | if breakpoint.hit_count > 0: 223 | msg += ' (called %d times)' % breakpoint.hit_count 224 | print(msg) 225 | breakpoint.delete() 226 | _locations[name] = None 227 | -------------------------------------------------------------------------------- /sslkeylog.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Dumps master keys for OpenSSL clients to file. The format is documented at 3 | * https://developer.mozilla.org/en-US/docs/Mozilla/Projects/NSS/Key_Log_Format 4 | * Supports TLS 1.3 when used with OpenSSL 1.1.1. 5 | * 6 | * Copyright (C) 2014 Peter Wu 7 | * Licensed under the terms of GPLv3 (or any later version) at your choice. 8 | * 9 | * Usage: 10 | * cc sslkeylog.c -shared -o libsslkeylog.so -fPIC -ldl 11 | * SSLKEYLOGFILE=premaster.txt LD_PRELOAD=./libsslkeylog.so openssl ... 12 | */ 13 | 14 | /* 15 | * A single libsslkeylog.so supports multiple OpenSSL runtime versions. If you 16 | * would like to build this library without OpenSSL development headers and do 17 | * not require support for older OpenSSL versions, then disable it by defining 18 | * the NO_OPENSSL_102_SUPPORT or NO_OPENSSL_110_SUPPORT macros. 19 | */ 20 | /* Define to drop OpenSSL <= 1.0.2 support and require OpenSSL >= 1.1.0. */ 21 | //#define NO_OPENSSL_102_SUPPORT 22 | /* Define to drop OpenSSL <= 1.1.0 support and require OpenSSL >= 1.1.1. */ 23 | //#define NO_OPENSSL_110_SUPPORT 24 | 25 | /* No OpenSSL 1.1.0 support implies no OpenSSL 1.0.2 support. */ 26 | #ifdef NO_OPENSSL_110_SUPPORT 27 | # define NO_OPENSSL_102_SUPPORT 28 | #endif 29 | 30 | #define _GNU_SOURCE /* for RTLD_NEXT */ 31 | #include 32 | #ifndef NO_OPENSSL_102_SUPPORT 33 | # include 34 | #endif /* ! NO_OPENSSL_102_SUPPORT */ 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | 41 | #ifndef OPENSSL_SONAME 42 | /* fallback library if OpenSSL is not already loaded. Other values to try: 43 | * libssl.so.0.9.8 libssl.so.1.0.0 libssl.so.1.1 */ 44 | # define OPENSSL_SONAME "libssl.so" 45 | #endif 46 | 47 | #define FIRSTLINE "# SSL key logfile generated by sslkeylog.c\n" 48 | #define FIRSTLINE_LEN (sizeof(FIRSTLINE) - 1) 49 | 50 | /* When building for OpenSSL 1.1.0 or newer, no headers are required. */ 51 | #ifdef NO_OPENSSL_102_SUPPORT 52 | typedef struct ssl_st SSL; 53 | typedef struct ssl_ctx_st SSL_CTX; 54 | /* Extra definitions for OpenSSL 1.1.0 support when headers are unavailable. */ 55 | # ifndef NO_OPENSSL_110_SUPPORT 56 | typedef struct ssl_session_st SSL_SESSION; 57 | # define SSL3_RANDOM_SIZE 32 58 | # define SSL_MAX_MASTER_KEY_LENGTH 48 59 | # define OPENSSL_VERSION_NUMBER 0x10100000L 60 | # endif /* ! NO_OPENSSL_110_SUPPORT */ 61 | #endif /* ! NO_OPENSSL_102_SUPPORT */ 62 | 63 | static int keylog_file_fd = -1; 64 | 65 | /* Legacy routines for dumping TLS <= 1.2 secrets on older OpenSSL versions. */ 66 | #ifndef NO_OPENSSL_110_SUPPORT 67 | #define PREFIX "CLIENT_RANDOM " 68 | #define PREFIX_LEN (sizeof(PREFIX) - 1) 69 | 70 | static inline void put_hex(char *buffer, int pos, char c) 71 | { 72 | unsigned char c1 = ((unsigned char) c) >> 4; 73 | unsigned char c2 = c & 0xF; 74 | buffer[pos] = c1 < 10 ? '0' + c1 : 'A' + c1 - 10; 75 | buffer[pos+1] = c2 < 10 ? '0' + c2 : 'A' + c2 - 10; 76 | } 77 | 78 | static void dump_to_fd(int fd, unsigned char *client_random, 79 | unsigned char *master_key, int master_key_length) 80 | { 81 | int pos, i; 82 | char line[PREFIX_LEN + 2 * SSL3_RANDOM_SIZE + 1 + 83 | 2 * SSL_MAX_MASTER_KEY_LENGTH + 1]; 84 | 85 | memcpy(line, PREFIX, PREFIX_LEN); 86 | pos = PREFIX_LEN; 87 | /* Client Random for SSLv3/TLS */ 88 | for (i = 0; i < SSL3_RANDOM_SIZE; i++) { 89 | put_hex(line, pos, client_random[i]); 90 | pos += 2; 91 | } 92 | line[pos++] = ' '; 93 | /* Master Secret (size is at most SSL_MAX_MASTER_KEY_LENGTH) */ 94 | for (i = 0; i < master_key_length; i++) { 95 | put_hex(line, pos, master_key[i]); 96 | pos += 2; 97 | } 98 | line[pos++] = '\n'; 99 | /* Write at once rather than using buffered I/O. Perhaps there is concurrent 100 | * write access so do not write hex values one by one. */ 101 | write(fd, line, pos); 102 | } 103 | #endif /* ! NO_OPENSSL_110_SUPPORT */ 104 | 105 | static void init_keylog_file(void) 106 | { 107 | if (keylog_file_fd >= 0) 108 | return; 109 | 110 | const char *filename = getenv("SSLKEYLOGFILE"); 111 | if (filename) { 112 | keylog_file_fd = open(filename, O_WRONLY | O_APPEND | O_CREAT, 0644); 113 | if (keylog_file_fd >= 0 && lseek(keylog_file_fd, 0, SEEK_END) == 0) { 114 | /* file is opened successfully and there is no data (pos == 0) */ 115 | write(keylog_file_fd, FIRSTLINE, FIRSTLINE_LEN); 116 | } 117 | } 118 | } 119 | 120 | static inline void *try_lookup_symbol(const char *sym, int optional) 121 | { 122 | void *func = dlsym(RTLD_NEXT, sym); 123 | if (!func && optional && dlsym(RTLD_NEXT, "SSL_new")) { 124 | /* Symbol not found, but an old OpenSSL version was actually loaded. */ 125 | return NULL; 126 | } 127 | /* Symbol not found, OpenSSL is not loaded (linked) so try to load it 128 | * manually. This is error-prone as it depends on a fixed library name. 129 | * Perhaps it should be an env name? */ 130 | if (!func) { 131 | void *handle = dlopen(OPENSSL_SONAME, RTLD_LAZY); 132 | if (!handle) { 133 | fprintf(stderr, "Lookup error for %s: %s\n", sym, dlerror()); 134 | abort(); 135 | } 136 | func = dlsym(handle, sym); 137 | if (!func && !optional) { 138 | fprintf(stderr, "Cannot lookup %s\n", sym); 139 | abort(); 140 | } 141 | dlclose(handle); 142 | } 143 | return func; 144 | } 145 | 146 | static inline void *lookup_symbol(const char *sym) 147 | { 148 | return try_lookup_symbol(sym, 0); 149 | } 150 | 151 | #ifndef NO_OPENSSL_110_SUPPORT 152 | typedef struct ssl_tap_state { 153 | int master_key_length; 154 | unsigned char master_key[SSL_MAX_MASTER_KEY_LENGTH]; 155 | 156 | } ssl_tap_state_t; 157 | 158 | static inline SSL_SESSION *ssl_get_session(const SSL *ssl) 159 | { 160 | #if OPENSSL_VERSION_NUMBER >= 0x10100000L 161 | static SSL_SESSION *(*func)(); 162 | if (!func) { 163 | func = lookup_symbol("SSL_get_session"); 164 | } 165 | return func(ssl); 166 | #else 167 | return ssl->session; 168 | #endif 169 | } 170 | 171 | static void copy_master_secret(const SSL_SESSION *session, 172 | unsigned char *master_key_out, int *keylen_out) 173 | { 174 | #if OPENSSL_VERSION_NUMBER >= 0x10100000L 175 | static size_t (*func)(); 176 | if (!func) { 177 | func = lookup_symbol("SSL_SESSION_get_master_key"); 178 | } 179 | *keylen_out = func(session, master_key_out, SSL_MAX_MASTER_KEY_LENGTH); 180 | #else 181 | if (session->master_key_length > 0) { 182 | *keylen_out = session->master_key_length; 183 | memcpy(master_key_out, session->master_key, 184 | session->master_key_length); 185 | } 186 | #endif 187 | } 188 | 189 | static void copy_client_random(const SSL *ssl, unsigned char *client_random) 190 | { 191 | #if OPENSSL_VERSION_NUMBER >= 0x10100000L 192 | static size_t (*func)(); 193 | if (!func) { 194 | func = lookup_symbol("SSL_get_client_random"); 195 | } 196 | /* ssl->s3 is not checked in openssl 1.1.0-pre6, but let's assume that 197 | * we have a valid SSL context if we have a non-NULL session. */ 198 | func(ssl, client_random, SSL3_RANDOM_SIZE); 199 | #else 200 | if (ssl->s3) { 201 | memcpy(client_random, ssl->s3->client_random, SSL3_RANDOM_SIZE); 202 | } 203 | #endif 204 | } 205 | 206 | /* non-NULL if the new OpenSSL 1.1.1 keylog API is supported. */ 207 | static int supports_keylog_api(void) 208 | { 209 | static int supported = -1; 210 | if (supported == -1) { 211 | supported = try_lookup_symbol("SSL_CTX_set_keylog_callback", 1) != NULL; 212 | } 213 | return supported; 214 | } 215 | 216 | /* Copies SSL state for later comparison in tap_ssl_key. */ 217 | static void ssl_tap_state_init(ssl_tap_state_t *state, const SSL *ssl) 218 | { 219 | if (supports_keylog_api()) { 220 | /* Favor using the callbacks API to extract secrets. */ 221 | return; 222 | } 223 | 224 | const SSL_SESSION *session = ssl_get_session(ssl); 225 | 226 | memset(state, 0, sizeof(ssl_tap_state_t)); 227 | if (session) { 228 | copy_master_secret(session, state->master_key, &state->master_key_length); 229 | } 230 | } 231 | 232 | #define SSL_TAP_STATE(state, ssl) \ 233 | ssl_tap_state_t state; \ 234 | ssl_tap_state_init(&state, ssl) 235 | 236 | static void tap_ssl_key(const SSL *ssl, ssl_tap_state_t *state) 237 | { 238 | if (supports_keylog_api()) { 239 | /* Favor using the callbacks API to extract secrets. */ 240 | return; 241 | } 242 | 243 | const SSL_SESSION *session = ssl_get_session(ssl); 244 | unsigned char client_random[SSL3_RANDOM_SIZE]; 245 | unsigned char master_key[SSL_MAX_MASTER_KEY_LENGTH]; 246 | int master_key_length = 0; 247 | 248 | if (session) { 249 | copy_master_secret(session, master_key, &master_key_length); 250 | /* Assume we have a client random if the master key is set. */ 251 | if (master_key_length > 0) { 252 | copy_client_random(ssl, client_random); 253 | } 254 | } 255 | 256 | /* Write the logfile when the master key is available for SSLv3/TLSv1. */ 257 | if (master_key_length > 0) { 258 | /* Skip writing keys if it did not change. */ 259 | if (state->master_key_length == master_key_length && 260 | memcmp(state->master_key, master_key, master_key_length) == 0) { 261 | return; 262 | } 263 | 264 | init_keylog_file(); 265 | if (keylog_file_fd >= 0) { 266 | dump_to_fd(keylog_file_fd, client_random, master_key, 267 | master_key_length); 268 | } 269 | } 270 | } 271 | 272 | int SSL_connect(SSL *ssl) 273 | { 274 | static int (*func)(); 275 | if (!func) { 276 | func = lookup_symbol(__func__); 277 | } 278 | SSL_TAP_STATE(state, ssl); 279 | int ret = func(ssl); 280 | tap_ssl_key(ssl, &state); 281 | return ret; 282 | } 283 | 284 | int SSL_do_handshake(SSL *ssl) 285 | { 286 | static int (*func)(); 287 | if (!func) { 288 | func = lookup_symbol(__func__); 289 | } 290 | SSL_TAP_STATE(state, ssl); 291 | int ret = func(ssl); 292 | tap_ssl_key(ssl, &state); 293 | return ret; 294 | } 295 | 296 | int SSL_accept(SSL *ssl) 297 | { 298 | static int (*func)(); 299 | if (!func) { 300 | func = lookup_symbol(__func__); 301 | } 302 | SSL_TAP_STATE(state, ssl); 303 | int ret = func(ssl); 304 | tap_ssl_key(ssl, &state); 305 | return ret; 306 | } 307 | 308 | int SSL_read(SSL *ssl, void *buf, int num) 309 | { 310 | static int (*func)(); 311 | if (!func) { 312 | func = lookup_symbol(__func__); 313 | } 314 | SSL_TAP_STATE(state, ssl); 315 | int ret = func(ssl, buf, num); 316 | tap_ssl_key(ssl, &state); 317 | return ret; 318 | } 319 | 320 | int SSL_write(SSL *ssl, const void *buf, int num) 321 | { 322 | static int (*func)(); 323 | if (!func) { 324 | func = lookup_symbol(__func__); 325 | } 326 | SSL_TAP_STATE(state, ssl); 327 | int ret = func(ssl, buf, num); 328 | tap_ssl_key(ssl, &state); 329 | return ret; 330 | } 331 | #endif /* ! NO_OPENSSL_110_SUPPORT */ 332 | 333 | /* Key extraction via the new OpenSSL 1.1.1 API. */ 334 | static void keylog_callback(const SSL *ssl, const char *line) 335 | { 336 | init_keylog_file(); 337 | if (keylog_file_fd >= 0) { 338 | write(keylog_file_fd, line, strlen(line)); 339 | write(keylog_file_fd, "\n", 1); 340 | } 341 | } 342 | 343 | SSL *SSL_new(SSL_CTX *ctx) 344 | { 345 | static SSL *(*func)(); 346 | static void (*set_keylog_cb)(); 347 | if (!func) { 348 | func = lookup_symbol(__func__); 349 | #ifdef NO_OPENSSL_110_SUPPORT 350 | /* The new API MUST be available since OpenSSL 1.1.1. */ 351 | set_keylog_cb = lookup_symbol("SSL_CTX_set_keylog_callback"); 352 | #else /* ! NO_OPENSSL_110_SUPPORT */ 353 | /* May be NULL if used with an older OpenSSL runtime library. */ 354 | set_keylog_cb = try_lookup_symbol("SSL_CTX_set_keylog_callback", 1); 355 | #endif /* ! NO_OPENSSL_110_SUPPORT */ 356 | } 357 | if (set_keylog_cb) { 358 | /* Override any previous key log callback. */ 359 | set_keylog_cb(ctx, keylog_callback); 360 | } 361 | return func(ctx); 362 | } 363 | --------------------------------------------------------------------------------