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