diff options
author | Dirk Engling <erdgeist@erdgeist.org> | 2022-05-16 15:53:39 +0200 |
---|---|---|
committer | Dirk Engling <erdgeist@erdgeist.org> | 2022-05-16 15:53:39 +0200 |
commit | d1ac67f6d73f24a165ccc008440bb8b208ae140f (patch) | |
tree | 0da0184ec2273ffa6f642ab06e776fcdda9f4ac3 /vchat-ssl.c | |
parent | 43b74147212ddc5e619ac17dd1b5967b6b293d8a (diff) |
Decouple IO openssl's BIO abstraction and split connection and tls handling to allow for other TLS libs
Diffstat (limited to 'vchat-ssl.c')
-rwxr-xr-x | vchat-ssl.c | 475 |
1 files changed, 0 insertions, 475 deletions
diff --git a/vchat-ssl.c b/vchat-ssl.c deleted file mode 100755 index adef70d..0000000 --- a/vchat-ssl.c +++ /dev/null | |||
@@ -1,475 +0,0 @@ | |||
1 | /* | ||
2 | * vchat-client - alpha version | ||
3 | * vchat-ssl.c - handling of SSL connection and X509 certificate | ||
4 | * verification | ||
5 | * | ||
6 | * Copyright (C) 2007 Thorsten Schroeder <ths@berlin.ccc.de> | ||
7 | * | ||
8 | * This program is free software. It can be redistributed and/or modified, | ||
9 | * provided that this copyright notice is kept intact. This program is | ||
10 | * distributed in the hope that it will be useful, but without any warranty; | ||
11 | * without even the implied warranty of merchantability or fitness for a | ||
12 | * particular purpose. In no event shall the copyright holder be liable for | ||
13 | * any direct, indirect, incidental or special damages arising in any way out | ||
14 | * of the use of this software. | ||
15 | * | ||
16 | */ | ||
17 | |||
18 | #include <stdio.h> | ||
19 | #include <stdlib.h> | ||
20 | #include <string.h> | ||
21 | #include <assert.h> | ||
22 | |||
23 | #include <openssl/err.h> | ||
24 | #include <openssl/ssl.h> | ||
25 | #include <openssl/bio.h> | ||
26 | #include <openssl/evp.h> | ||
27 | #include <openssl/x509.h> | ||
28 | #include <openssl/x509v3.h> | ||
29 | #include <openssl/conf.h> | ||
30 | |||
31 | #include <readline/readline.h> | ||
32 | |||
33 | #include "vchat.h" | ||
34 | #include "vchat-ssl.h" | ||
35 | |||
36 | const char *vchat_ssl_version = "vchat-ssl.c $Id$"; | ||
37 | |||
38 | typedef int (*vc_x509verify_cb_t)(int, X509_STORE_CTX *); | ||
39 | struct vc_x509store_t { | ||
40 | char *cafile; | ||
41 | char *capath; | ||
42 | char *crlfile; | ||
43 | vc_x509verify_cb_t callback; | ||
44 | vc_askpass_cb_t askpass_callback; | ||
45 | STACK_OF(X509) *certs; | ||
46 | STACK_OF(X509_CRL) *crls; | ||
47 | char *use_certfile; | ||
48 | STACK_OF(X509) *use_certs; | ||
49 | char *use_keyfile; | ||
50 | EVP_PKEY *use_key; | ||
51 | int flags; | ||
52 | }; | ||
53 | |||
54 | static void vc_cleanup_x509store(vc_x509store_t *); // Should not be static but is unused | ||
55 | static SSL_CTX * vc_create_sslctx( vc_x509store_t *vc_store ); | ||
56 | static int vc_verify_callback(int, X509_STORE_CTX *); | ||
57 | static X509_STORE * vc_x509store_create(vc_x509store_t *); | ||
58 | static void vc_x509store_clearflags(vc_x509store_t *, int); | ||
59 | static void vc_x509store_setcafile(vc_x509store_t *, char *); | ||
60 | static void vc_x509store_setcapath(vc_x509store_t *, char *); | ||
61 | static void vc_x509store_setcrlfile(vc_x509store_t *, char *); | ||
62 | static void vc_x509store_addcert(vc_x509store_t *, X509 *); | ||
63 | static void vc_x509store_setcb(vc_x509store_t *, vc_x509verify_cb_t); | ||
64 | |||
65 | #define VC_CTX_ERR_EXIT(se, cx) do { \ | ||
66 | snprintf(tmpstr, TMPSTRSIZE, "CREATE CTX: %s", \ | ||
67 | ERR_error_string (ERR_get_error (), NULL)); \ | ||
68 | writecf(FS_ERR, tmpstr); \ | ||
69 | if(se) X509_STORE_free(se); \ | ||
70 | if(cx) SSL_CTX_free(cx); \ | ||
71 | return(0); \ | ||
72 | } while(0) | ||
73 | |||
74 | #define VC_SETCERT_ERR_EXIT(se, cx, err) do { \ | ||
75 | snprintf(tmpstr, TMPSTRSIZE, "CREATE CTX: %s", err); \ | ||
76 | writecf(FS_ERR, tmpstr); \ | ||
77 | if(se) X509_STORE_free(se); \ | ||
78 | if(cx) SSL_CTX_free(cx); \ | ||
79 | return(NULL); \ | ||
80 | } while(0) | ||
81 | |||
82 | static SSL_CTX * vc_create_sslctx( vc_x509store_t *vc_store ) | ||
83 | { | ||
84 | int i = 0; | ||
85 | int n = 0; | ||
86 | int flags = 0; | ||
87 | int r = 0; | ||
88 | SSL_CTX *ctx = NULL; | ||
89 | X509_STORE *store = NULL; | ||
90 | vc_x509verify_cb_t verify_callback = NULL; | ||
91 | |||
92 | /* Explicitly use TLSv1 (or maybe later) */ | ||
93 | if( !(ctx = SSL_CTX_new(SSLv23_client_method())) ) | ||
94 | VC_CTX_ERR_EXIT(store, ctx); | ||
95 | |||
96 | if( !(store = vc_x509store_create(vc_store)) ) | ||
97 | VC_CTX_ERR_EXIT(store, ctx); | ||
98 | |||
99 | SSL_CTX_set_cert_store(ctx, store); | ||
100 | store = NULL; | ||
101 | /* Disable some insecure protocols explicitly */ | ||
102 | SSL_CTX_set_options(ctx, SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3); | ||
103 | if (getstroption(CF_CIPHERSUITE)) | ||
104 | SSL_CTX_set_cipher_list(ctx, getstroption(CF_CIPHERSUITE)); | ||
105 | else | ||
106 | SSL_CTX_set_cipher_list(ctx, "ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-SHA"); | ||
107 | |||
108 | SSL_CTX_set_verify_depth (ctx, getintoption(CF_VERIFYSSL)); | ||
109 | |||
110 | if( !(verify_callback = vc_store->callback) ) | ||
111 | verify_callback = vc_verify_callback; | ||
112 | |||
113 | if( !(vc_store->flags & VC_X509S_SSL_VERIFY_MASK) ) { | ||
114 | writecf(FS_DBG, tmpstr); | ||
115 | flags = SSL_VERIFY_NONE; | ||
116 | } | ||
117 | else { | ||
118 | if(vc_store->flags & VC_X509S_SSL_VERIFY_PEER) | ||
119 | flags |= SSL_VERIFY_PEER; | ||
120 | if(vc_store->flags & VC_X509S_SSL_VERIFY_FAIL_IF_NO_PEER_CERT) | ||
121 | flags |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT; | ||
122 | if(vc_store->flags & VC_X509S_SSL_VERIFY_CLIENT_ONCE) | ||
123 | flags |= SSL_VERIFY_CLIENT_ONCE; | ||
124 | } | ||
125 | |||
126 | SSL_CTX_set_verify(ctx, flags, verify_callback); | ||
127 | |||
128 | if(vc_store->flags & VC_X509S_USE_CERTIFICATE) { | ||
129 | if(vc_store->use_certfile) | ||
130 | SSL_CTX_use_certificate_chain_file(ctx, vc_store->use_certfile); | ||
131 | else { | ||
132 | SSL_CTX_use_certificate(ctx, | ||
133 | sk_X509_value(vc_store->use_certs, 0)); | ||
134 | for(i=0,n=sk_X509_num(vc_store->use_certs); i<n; i++) | ||
135 | SSL_CTX_add_extra_chain_cert(ctx, | ||
136 | sk_X509_value(vc_store->use_certs, i)); | ||
137 | } | ||
138 | |||
139 | SSL_CTX_set_default_passwd_cb(ctx, vc_store->askpass_callback); | ||
140 | |||
141 | if(vc_store->use_keyfile) { | ||
142 | r=SSL_CTX_use_PrivateKey_file(ctx, vc_store->use_keyfile, | ||
143 | SSL_FILETYPE_PEM); | ||
144 | } else if(vc_store->use_key) | ||
145 | r=SSL_CTX_use_PrivateKey(ctx, vc_store->use_key); | ||
146 | |||
147 | if( r!=1 || !SSL_CTX_check_private_key(ctx)) | ||
148 | VC_SETCERT_ERR_EXIT(store, ctx, "Load private key failed"); | ||
149 | } | ||
150 | |||
151 | SSL_CTX_set_app_data(ctx, vc_store); | ||
152 | return(ctx); | ||
153 | } | ||
154 | |||
155 | int vc_connect_ssl( BIO **conn, vc_x509store_t *vc_store ) | ||
156 | { | ||
157 | SSL_CTX * ctx = vc_create_sslctx(vc_store); | ||
158 | X509 *peercert = NULL; | ||
159 | BIO *ssl_conn = NULL; | ||
160 | const SSL *sslp = NULL; | ||
161 | const SSL_CIPHER * cipher = NULL; | ||
162 | |||
163 | /* To display and check server fingerprint */ | ||
164 | char fingerprint[EVP_MAX_MD_SIZE*4]; | ||
165 | unsigned char fingerprint_bin[EVP_MAX_MD_SIZE]; | ||
166 | unsigned int fingerprint_len; | ||
167 | |||
168 | FILE *fingerprint_file = NULL; | ||
169 | char * fp = fingerprint; | ||
170 | |||
171 | long result, j; | ||
172 | |||
173 | if( !ctx ) | ||
174 | return 1; | ||
175 | |||
176 | ssl_conn = BIO_new_ssl(ctx, 1); | ||
177 | SSL_CTX_free(ctx); | ||
178 | |||
179 | if( !ssl_conn ) | ||
180 | goto ssl_error; | ||
181 | |||
182 | BIO_push( ssl_conn, *conn ); | ||
183 | *conn = ssl_conn; | ||
184 | fflush(stdout); | ||
185 | |||
186 | if( BIO_do_handshake( *conn ) <= 0 ) | ||
187 | goto ssl_error; | ||
188 | |||
189 | /* Show information about cipher used */ | ||
190 | /* Get cipher object */ | ||
191 | BIO_get_ssl(ssl_conn, &sslp); | ||
192 | if (sslp) | ||
193 | cipher = SSL_get_current_cipher(sslp); | ||
194 | if (cipher) { | ||
195 | char cipher_desc[TMPSTRSIZE]; | ||
196 | snprintf(tmpstr, TMPSTRSIZE, "[SSL CIPHER ] %s", SSL_CIPHER_description(cipher, cipher_desc, TMPSTRSIZE)); | ||
197 | writecf(FS_SERV, tmpstr); | ||
198 | } else { | ||
199 | snprintf(tmpstr, TMPSTRSIZE, "[SSL ERROR ] Cipher not known / SSL object can't be queried!"); | ||
200 | writecf(FS_ERR, tmpstr); | ||
201 | } | ||
202 | |||
203 | /* Accept being connected, _if_ verification passed */ | ||
204 | if (!sslp) | ||
205 | goto ssl_error; | ||
206 | |||
207 | peercert = SSL_get_peer_certificate(sslp); | ||
208 | if (!peercert) | ||
209 | goto ssl_error; | ||
210 | |||
211 | /* show basic information about peer cert */ | ||
212 | snprintf(tmpstr, TMPSTRSIZE, "[SSL SUBJECT ] %s", X509_NAME_oneline(X509_get_subject_name(peercert),0,0)); | ||
213 | writecf(FS_SERV, tmpstr); | ||
214 | snprintf(tmpstr, TMPSTRSIZE, "[SSL ISSUER ] %s", X509_NAME_oneline(X509_get_issuer_name(peercert),0,0)); | ||
215 | writecf(FS_SERV, tmpstr); | ||
216 | |||
217 | /* calculate fingerprint */ | ||
218 | if (!X509_digest(peercert,EVP_sha1(),fingerprint_bin,&fingerprint_len)) | ||
219 | goto ssl_error; | ||
220 | |||
221 | assert ( ( fingerprint_len > 1 ) && (fingerprint_len <= EVP_MAX_MD_SIZE )); | ||
222 | for (j=0; j<(int)fingerprint_len; j++) | ||
223 | fp += sprintf(fp, "%02X:", fingerprint_bin[j]); | ||
224 | assert ( fp > fingerprint ); | ||
225 | fp[-1] = 0; | ||
226 | snprintf(tmpstr, TMPSTRSIZE, "[SSL FINGERPRINT ] %s (from server)", fingerprint); | ||
227 | writecf(FS_SERV, tmpstr); | ||
228 | |||
229 | /* we don't need the peercert anymore */ | ||
230 | X509_free(peercert); | ||
231 | |||
232 | /* verify fingerprint */ | ||
233 | if (getintoption(CF_PINFINGER)) { | ||
234 | |||
235 | fingerprint_file = fopen(tilde_expand(getstroption(CF_FINGERPRINT)), "r"); | ||
236 | if (fingerprint_file) { | ||
237 | |||
238 | /* Read fingerprint from file */ | ||
239 | char old_fingerprint[EVP_MAX_MD_SIZE*4]; | ||
240 | char * r = fgets(old_fingerprint, sizeof(old_fingerprint), fingerprint_file); | ||
241 | fclose(fingerprint_file); | ||
242 | |||
243 | if (r) { | ||
244 | /* chomp */ | ||
245 | char *nl = strchr(r, '\n'); | ||
246 | if (nl) *nl = 0; | ||
247 | |||
248 | /* verify fingerprint matches stored version */ | ||
249 | if (!strcmp(fingerprint, old_fingerprint)) | ||
250 | return 0; | ||
251 | } | ||
252 | |||
253 | snprintf(tmpstr, TMPSTRSIZE, "[SSL FINGERPRINT ] %s (from %s)", r ? old_fingerprint : "<FILE READ ERROR>", getstroption(CF_FINGERPRINT)); | ||
254 | writecf(FS_ERR, tmpstr); | ||
255 | writecf(FS_ERR, "[SSL CONNECT ERROR] Fingerprint mismatch! Server cert updated?"); | ||
256 | return 1; | ||
257 | } | ||
258 | |||
259 | fingerprint_file = fopen(tilde_expand(getstroption(CF_FINGERPRINT)), "w"); | ||
260 | if (!fingerprint_file) { | ||
261 | snprintf (tmpstr, TMPSTRSIZE, "[WARNING] Can't write fingerprint file, %s.", strerror(errno)); | ||
262 | writecf(FS_ERR, tmpstr); | ||
263 | } else { | ||
264 | fputs(fingerprint, fingerprint_file); | ||
265 | fclose(fingerprint_file); | ||
266 | writecf(FS_SERV, "Stored fingerprint."); | ||
267 | } | ||
268 | return 0; | ||
269 | } | ||
270 | |||
271 | /* If verify of x509 chain was requested, do the check here */ | ||
272 | result = SSL_get_verify_result(sslp); | ||
273 | |||
274 | if (result == X509_V_OK) | ||
275 | return 0; | ||
276 | |||
277 | if (getintoption(CF_IGNSSL)) { | ||
278 | writecf(FS_ERR, "[SSL VERIFY ERROR ] FAILURE IGNORED!!!"); | ||
279 | return 0; | ||
280 | } | ||
281 | |||
282 | ssl_error: | ||
283 | snprintf(tmpstr, TMPSTRSIZE, "[SSL CONNECT ERROR] %s", ERR_error_string (ERR_get_error (), NULL)); | ||
284 | writecf(FS_ERR, tmpstr); | ||
285 | |||
286 | return 1; | ||
287 | } | ||
288 | |||
289 | #define VC_STORE_ERR_EXIT(s) do { \ | ||
290 | fprintf(stderr, "[E] SSL_STORE: %s\n", ERR_error_string (ERR_get_error (), NULL)); \ | ||
291 | if(s) X509_STORE_free(s); \ | ||
292 | return(0); \ | ||
293 | } while(0) | ||
294 | |||
295 | X509_STORE *vc_x509store_create(vc_x509store_t *vc_store) | ||
296 | { | ||
297 | int i = 0; | ||
298 | int n = 0; | ||
299 | X509_STORE *store = NULL; | ||
300 | X509_LOOKUP *lookup = NULL; | ||
301 | |||
302 | store = X509_STORE_new(); | ||
303 | |||
304 | if(vc_store->callback) | ||
305 | X509_STORE_set_verify_cb_func(store, vc_store->callback); | ||
306 | else | ||
307 | X509_STORE_set_verify_cb_func(store, vc_verify_callback); | ||
308 | |||
309 | if( !(lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file())) ) | ||
310 | VC_STORE_ERR_EXIT(store); | ||
311 | |||
312 | if(!vc_store->cafile) { | ||
313 | if( !(vc_store->flags & VC_X509S_NODEF_CAFILE) ) | ||
314 | X509_LOOKUP_load_file(lookup, 0, X509_FILETYPE_DEFAULT); | ||
315 | } else if( !X509_LOOKUP_load_file(lookup, vc_store->cafile, | ||
316 | X509_FILETYPE_PEM) ) | ||
317 | VC_STORE_ERR_EXIT(store); | ||
318 | |||
319 | if(vc_store->crlfile) { | ||
320 | if( !X509_load_crl_file(lookup, vc_store->crlfile, | ||
321 | X509_FILETYPE_PEM) ) | ||
322 | VC_STORE_ERR_EXIT(store); | ||
323 | |||
324 | X509_STORE_set_flags( store, | ||
325 | X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL ); | ||
326 | } | ||
327 | |||
328 | if( !(lookup = X509_STORE_add_lookup(store, X509_LOOKUP_hash_dir())) ) | ||
329 | VC_STORE_ERR_EXIT(store); | ||
330 | |||
331 | if( !vc_store->capath ) { | ||
332 | if( !(vc_store->flags & VC_X509S_NODEF_CAPATH) ) | ||
333 | X509_LOOKUP_add_dir(lookup, 0, X509_FILETYPE_DEFAULT); | ||
334 | } else if( !X509_LOOKUP_add_dir(lookup, vc_store->capath, | ||
335 | X509_FILETYPE_PEM) ) | ||
336 | VC_STORE_ERR_EXIT(store); | ||
337 | |||
338 | for( i=0, n=sk_X509_num(vc_store->certs); i<n; i++) | ||
339 | if( !X509_STORE_add_cert(store, sk_X509_value(vc_store->certs, i)) ) | ||
340 | VC_STORE_ERR_EXIT(store); | ||
341 | |||
342 | for( i=0, n=sk_X509_CRL_num(vc_store->crls); i<n; i++) | ||
343 | if( !X509_STORE_add_crl(store, | ||
344 | sk_X509_CRL_value(vc_store->crls, i)) ) | ||
345 | VC_STORE_ERR_EXIT(store); | ||
346 | |||
347 | return(store); | ||
348 | } | ||
349 | |||
350 | int vc_verify_callback(int ok, X509_STORE_CTX *store) | ||
351 | { | ||
352 | if(!ok) { | ||
353 | snprintf(tmpstr, TMPSTRSIZE, "[SSL VERIFY ERROR ] %s", | ||
354 | X509_verify_cert_error_string(X509_STORE_CTX_get_error(store))); | ||
355 | writecf(FS_ERR, tmpstr); | ||
356 | } | ||
357 | return (ok | getintoption(CF_IGNSSL)); | ||
358 | } | ||
359 | |||
360 | void vc_x509store_setflags(vc_x509store_t *store, int flags) | ||
361 | { | ||
362 | store->flags |= flags; | ||
363 | } | ||
364 | |||
365 | void vc_x509store_clearflags(vc_x509store_t *store, int flags) | ||
366 | { | ||
367 | store->flags &= ~flags; | ||
368 | } | ||
369 | |||
370 | void vc_x509store_setcb(vc_x509store_t *store, | ||
371 | vc_x509verify_cb_t callback) | ||
372 | { | ||
373 | store->callback = callback; | ||
374 | } | ||
375 | |||
376 | void vc_x509store_set_pkeycb(vc_x509store_t *store, | ||
377 | vc_askpass_cb_t callback) | ||
378 | { | ||
379 | store->askpass_callback = callback; | ||
380 | } | ||
381 | |||
382 | void vc_x509store_addcert(vc_x509store_t *store, X509 *cert) | ||
383 | { | ||
384 | sk_X509_push(store->certs, cert); | ||
385 | } | ||
386 | |||
387 | void vc_x509store_setcafile(vc_x509store_t *store, char *file) | ||
388 | { | ||
389 | free(store->cafile); | ||
390 | store->cafile = ( file ? strdup(file) : 0 ); | ||
391 | } | ||
392 | |||
393 | void vc_x509store_setcapath(vc_x509store_t *store, char *path) | ||
394 | { | ||
395 | free(store->capath); | ||
396 | store->capath = ( path ? strdup(path) : 0 ); | ||
397 | } | ||
398 | |||
399 | void vc_x509store_setcrlfile(vc_x509store_t *store, char *file) | ||
400 | { | ||
401 | free(store->crlfile); | ||
402 | store->crlfile = ( file ? strdup(file) : 0 ); | ||
403 | } | ||
404 | |||
405 | void vc_x509store_setkeyfile(vc_x509store_t *store, char *file) | ||
406 | { | ||
407 | free(store->use_keyfile); | ||
408 | store->use_keyfile = ( file ? strdup(file) : 0 ); | ||
409 | } | ||
410 | |||
411 | void vc_x509store_setcertfile(vc_x509store_t *store, char *file) | ||
412 | { | ||
413 | free(store->use_certfile); | ||
414 | store->use_certfile = ( file ? strdup(file) : 0 ); | ||
415 | } | ||
416 | |||
417 | #if 0 | ||
418 | int vc_tls_read() | ||
419 | { | ||
420 | |||
421 | } | ||
422 | |||
423 | int vc_tls_read() | ||
424 | { | ||
425 | |||
426 | } | ||
427 | #endif | ||
428 | |||
429 | vc_x509store_t *vc_init_x509store() | ||
430 | { | ||
431 | vc_x509store_t *s = malloc(sizeof(vc_x509store_t)); | ||
432 | if (s) { | ||
433 | |||
434 | static int sslinit; | ||
435 | if( !sslinit++ ) { | ||
436 | SSL_library_init (); | ||
437 | SSL_load_error_strings(); | ||
438 | } | ||
439 | |||
440 | s->cafile = NULL; | ||
441 | s->capath = NULL; | ||
442 | s->crlfile = NULL; | ||
443 | s->callback = NULL; | ||
444 | s->askpass_callback = NULL; | ||
445 | s->certs = sk_X509_new_null(); | ||
446 | s->crls = sk_X509_CRL_new_null(); | ||
447 | s->use_certfile = NULL; | ||
448 | s->use_certs = sk_X509_new_null(); | ||
449 | s->use_keyfile = NULL; | ||
450 | s->use_key = NULL; | ||
451 | s->flags = 0; | ||
452 | } | ||
453 | return s; | ||
454 | } | ||
455 | |||
456 | void vc_cleanup_x509store(vc_x509store_t *s) | ||
457 | { | ||
458 | free(s->cafile); | ||
459 | free(s->capath); | ||
460 | free(s->crlfile); | ||
461 | free(s->use_certfile); | ||
462 | free(s->use_keyfile); | ||
463 | free(s->use_key); | ||
464 | sk_X509_free(s->certs); | ||
465 | sk_X509_CRL_free(s->crls); | ||
466 | sk_X509_free(s->use_certs); | ||
467 | } | ||
468 | |||
469 | const char *vchat_ssl_version_external = "OpenSSL implementation; version unknown"; | ||
470 | void vchat_ssl_get_version_external() | ||
471 | { | ||
472 | char tmpstr[TMPSTRSIZE]; | ||
473 | snprintf(tmpstr, TMPSTRSIZE, "%s with %s", SSLeay_version(SSLEAY_VERSION), SSLeay_version(SSLEAY_CFLAGS)); | ||
474 | vchat_ssl_version_external = strdup(tmpstr); | ||
475 | } | ||