summaryrefslogtreecommitdiff
path: root/vchat-ssl.c
diff options
context:
space:
mode:
Diffstat (limited to 'vchat-ssl.c')
-rwxr-xr-xvchat-ssl.c467
1 files changed, 467 insertions, 0 deletions
diff --git a/vchat-ssl.c b/vchat-ssl.c
new file mode 100755
index 0000000..5d68309
--- /dev/null
+++ b/vchat-ssl.c
@@ -0,0 +1,467 @@
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
22#include <openssl/err.h>
23#include <openssl/ssl.h>
24#include <openssl/bio.h>
25#include <openssl/evp.h>
26#include <openssl/x509.h>
27#include <openssl/x509v3.h>
28#include <openssl/conf.h>
29
30#include "vchat.h"
31#include "vchat-ssl.h"
32
33char *vchat_ssl_version = "$Id$";
34
35static int ignore_ssl;
36
37#define VC_CTX_ERR_EXIT(se, cx) do { \
38 snprintf(tmpstr, TMPSTRSIZE, "CREATE CTX: %s", \
39 ERR_error_string (ERR_get_error (), NULL)); \
40 writecf(FS_ERR, tmpstr); \
41 if(se) X509_STORE_free(se); \
42 if(cx) SSL_CTX_free(cx); \
43 return(0); \
44 } while(0)
45
46#define VC_SETCERT_ERR_EXIT(se, cx, err) do { \
47 snprintf(tmpstr, TMPSTRSIZE, "CREATE CTX: %s", err); \
48 writecf(FS_ERR, tmpstr); \
49 if(se) X509_STORE_free(se); \
50 if(cx) SSL_CTX_free(cx); \
51 return(NULL); \
52 } while(0)
53
54SSL_CTX * vc_create_sslctx( vc_x509store_t *vc_store )
55{
56 int i = 0;
57 int n = 0;
58 int flags = 0;
59 int r = 0;
60 SSL_CTX *ctx = NULL;
61 X509_STORE *store = NULL;
62 vc_x509verify_cb_t verify_callback = NULL;
63
64 if( !(ctx = SSL_CTX_new(SSLv3_method())) )
65 VC_CTX_ERR_EXIT(store, ctx);
66
67 if( !(store = vc_x509store_create(vc_store)) )
68 VC_CTX_ERR_EXIT(store, ctx);
69
70 SSL_CTX_set_cert_store(ctx, store);
71 store = NULL;
72 SSL_CTX_set_options(ctx, SSL_OP_ALL|SSL_OP_NO_SSLv2);
73 SSL_CTX_set_cipher_list(ctx, "ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH");
74
75 SSL_CTX_set_verify_depth (ctx, 2);
76
77 if( !(verify_callback = vc_store->callback) )
78 verify_callback = vc_verify_callback;
79
80 if( !(vc_store->flags & VC_X509S_SSL_VERIFY_MASK) ) {
81 writecf(FS_DBG, tmpstr);
82 flags = SSL_VERIFY_NONE;
83 }
84 else {
85 if(vc_store->flags & VC_X509S_SSL_VERIFY_PEER)
86 flags |= SSL_VERIFY_PEER;
87 if(vc_store->flags & VC_X509S_SSL_VERIFY_FAIL_IF_NO_PEER_CERT)
88 flags |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
89 if(vc_store->flags & VC_X509S_SSL_VERIFY_CLIENT_ONCE)
90 flags |= SSL_VERIFY_CLIENT_ONCE;
91 }
92
93 SSL_CTX_set_verify(ctx, flags, verify_callback);
94
95 if(vc_store->flags & VC_X509S_USE_CERTIFICATE) {
96 if(vc_store->use_certfile)
97 SSL_CTX_use_certificate_chain_file(ctx, vc_store->use_certfile);
98 else {
99 SSL_CTX_use_certificate(ctx,
100 sk_X509_value(vc_store->use_certs, 0));
101 for(i=0,n=sk_X509_num(vc_store->use_certs); i<n; i++)
102 SSL_CTX_add_extra_chain_cert(ctx,
103 sk_X509_value(vc_store->use_certs, i));
104 }
105
106 SSL_CTX_set_default_passwd_cb(ctx, vc_store->askpass_callback);
107
108 if(vc_store->use_keyfile) {
109 r=SSL_CTX_use_PrivateKey_file(ctx, vc_store->use_keyfile,
110 SSL_FILETYPE_PEM);
111 } else if(vc_store->use_key)
112 r=SSL_CTX_use_PrivateKey(ctx, vc_store->use_key);
113
114 if(r!=1)
115 VC_SETCERT_ERR_EXIT(store, ctx, "Load private key failed");
116
117 }
118
119 SSL_CTX_set_app_data(ctx, vc_store);
120 return(ctx);
121}
122
123#define VC_CONNSSL_ERR_EXIT(_cx, cx, cn) do { \
124 snprintf(tmpstr, TMPSTRSIZE, "[SSL ERROR] %s", \
125 ERR_error_string (ERR_get_error (), NULL)); \
126 writecf(FS_ERR, tmpstr); \
127 if(cn) BIO_free_all(cn); \
128 if(*cx) SSL_CTX_free(*cx); \
129 if(_cx) *cx = 0; \
130 return(0); \
131 } while(0)
132
133BIO * vc_connect_ssl(char *host, int port, vc_x509store_t *vc_store,
134 SSL_CTX **ctx)
135{
136 BIO *conn = NULL;
137 int _ctx = 0;
138
139 if(*ctx) {
140 CRYPTO_add( &((*ctx)->references), 1, CRYPTO_LOCK_SSL_CTX );
141 if( vc_store && vc_store != SSL_CTX_get_app_data(*ctx) ) {
142 SSL_CTX_set_cert_store(*ctx, vc_x509store_create(vc_store));
143 SSL_CTX_set_app_data(*ctx, vc_store);
144 }
145 } else {
146 *ctx = vc_create_sslctx(vc_store);
147 _ctx = 1;
148 }
149
150 if( !(conn = BIO_new_ssl_connect(*ctx)) )
151 VC_CONNSSL_ERR_EXIT(_ctx, ctx, conn);
152
153 BIO_set_conn_hostname(conn, host);
154 BIO_set_conn_int_port(conn, &port);
155
156 fflush(stdout);
157 if(BIO_do_connect(conn) <= 0)
158 VC_CONNSSL_ERR_EXIT(_ctx, ctx, conn);
159
160 if(_ctx)
161 SSL_CTX_free(*ctx);
162
163 return(conn);
164}
165
166#define VC_CONN_ERR_EXIT(cn) do { \
167 snprintf(tmpstr, TMPSTRSIZE, "[SSL ERROR] %s", \
168 ERR_error_string(ERR_get_error(), NULL)); \
169 if(ERR_get_error()) \
170 writecf(FS_ERR, tmpstr); \
171 if(cn) BIO_free_all(cn); \
172 return(NULL); \
173 } while(0)
174
175#define VC_VERIFICATION_ERR_EXIT(cn, err) do { \
176 snprintf(tmpstr, TMPSTRSIZE, \
177 "[SSL ERROR] certificate verify failed: %s", err); \
178 writecf(FS_ERR, tmpstr); \
179 if(cn && !ignore_ssl) { BIO_free_all(cn); return(NULL); } \
180 } while(0)
181
182BIO * vc_connect( char *host, int port, int use_ssl,
183 vc_x509store_t *vc_store, SSL_CTX **ctx)
184{
185 BIO *conn = NULL;
186 SSL *ssl = NULL;
187
188 if(use_ssl) {
189 if( !(conn = vc_connect_ssl(host, port, vc_store, ctx)) )
190 VC_CONN_ERR_EXIT(conn);
191
192
193 BIO_get_ssl(conn, &ssl);
194 if(!vc_verify_cert_hostname(SSL_get_peer_certificate(ssl), host))
195 VC_VERIFICATION_ERR_EXIT(conn, "Hostname does not match!");
196
197 return(conn);
198 }
199
200 *ctx = 0;
201
202 if( !(conn = BIO_new_connect(host)) )
203 VC_CONN_ERR_EXIT(conn);
204
205 BIO_set_conn_int_port(conn, &port);
206
207 if(BIO_do_connect(conn) <= 0)
208 VC_CONN_ERR_EXIT(conn);
209
210 return(conn);
211}
212
213int vc_verify_cert_hostname(X509 *cert, char *host)
214{
215
216 int i = 0;
217 int j = 0;
218 int n = 0;
219 int extcount = 0;
220 int ok = 0;
221
222 X509_NAME *subj = NULL;
223 const char *extstr = NULL;
224 CONF_VALUE *nval = NULL;
225 unsigned char *data = NULL;
226 X509_EXTENSION *ext = NULL;
227 X509V3_EXT_METHOD *meth = NULL;
228 STACK_OF(CONF_VALUE) *val = NULL;
229
230 char name[256];
231 memset(&name, 0, sizeof(name));
232
233 if((extcount = X509_get_ext_count(cert)) > 0) {
234
235 for(i=0; !ok && i < extcount; i++) {
236
237 meth = NULL;
238
239 ext = X509_get_ext(cert, i);
240 extstr = OBJ_nid2sn(OBJ_obj2nid(X509_EXTENSION_get_object(ext)));
241
242 if(!strcasecmp(extstr, "subjectAltName")) {
243
244 if( !(meth = X509V3_EXT_get(ext)) )
245 break;
246
247 if( !(meth->d2i) )
248 break;
249
250 data = ext->value->data;
251
252 val = meth->i2v(meth, meth->d2i(0, &data, ext->value->length), 0);
253 for( j=0, n=sk_CONF_VALUE_num(val); j<n; j++ ) {
254 nval = sk_CONF_VALUE_value(val, j);
255 if( !strcasecmp(nval->name, "DNS") &&
256 !strcasecmp(nval->value, host) ) {
257 ok = 1;
258 break;
259 }
260 }
261 }
262 }
263 }
264
265 if( !ok && (subj = X509_get_subject_name(cert)) &&
266 X509_NAME_get_text_by_NID(subj, NID_commonName,
267 name, sizeof(name)) > 0 ) {
268 name[sizeof(name)-1] = '\0';
269 if(!strcasecmp(name, host))
270 ok = 1;
271 }
272
273 //printf("[*] vc_verify_cert_hostname() return: %d\n", ok);
274 return(ok);
275}
276
277int vc_verify_cert(X509 *cert, vc_x509store_t *vc_store)
278{
279 int result = -1;
280 X509_STORE *store = NULL;
281 X509_STORE_CTX *ctx = NULL;
282
283 if( !(store = vc_x509store_create(vc_store)) )
284 return(result);
285
286 if( (ctx = X509_STORE_CTX_new()) != 0 ) {
287 if(X509_STORE_CTX_init(ctx, store, cert, 0) == 1)
288 result = (X509_verify_cert(ctx) == 1);
289 X509_STORE_CTX_free(ctx);
290 }
291
292 X509_STORE_free(store);
293 return(result);
294}
295
296#define VC_STORE_ERR_EXIT(s) do { \
297 fprintf(stderr, "[E] SSL_STORE: %s\n", ERR_error_string (ERR_get_error (), NULL)); \
298 if(s) X509_STORE_free(s); \
299 return(0); \
300 } while(0)
301
302X509_STORE *vc_x509store_create(vc_x509store_t *vc_store)
303{
304 int i = 0;
305 int n = 0;
306 X509_STORE *store = NULL;
307 X509_LOOKUP *lookup = NULL;
308
309 store = X509_STORE_new();
310
311 if(vc_store->callback)
312 X509_STORE_set_verify_cb_func(store, vc_store->callback);
313 else
314 X509_STORE_set_verify_cb_func(store, vc_verify_callback);
315
316 if( !(lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file())) )
317 VC_STORE_ERR_EXIT(store);
318
319 if(!vc_store->cafile) {
320 if( !(vc_store->flags & VC_X509S_NODEF_CAFILE) )
321 X509_LOOKUP_load_file(lookup, 0, X509_FILETYPE_DEFAULT);
322 } else if( !X509_LOOKUP_load_file(lookup, vc_store->cafile,
323 X509_FILETYPE_PEM) )
324 VC_STORE_ERR_EXIT(store);
325
326 if(vc_store->crlfile) {
327 if( !X509_load_crl_file(lookup, vc_store->crlfile,
328 X509_FILETYPE_PEM) )
329 VC_STORE_ERR_EXIT(store);
330
331 X509_STORE_set_flags( store,
332 X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL );
333 }
334
335 if( !(lookup = X509_STORE_add_lookup(store, X509_LOOKUP_hash_dir())) )
336 VC_STORE_ERR_EXIT(store);
337
338 if( !vc_store->capath ) {
339 if( !(vc_store->flags & VC_X509S_NODEF_CAPATH) )
340 X509_LOOKUP_add_dir(lookup, 0, X509_FILETYPE_DEFAULT);
341 } else if( !X509_LOOKUP_add_dir(lookup, vc_store->capath,
342 X509_FILETYPE_PEM) )
343 VC_STORE_ERR_EXIT(store);
344
345 for( i=0, n=sk_X509_num(vc_store->certs); i<n; i++)
346 if( !X509_STORE_add_cert(store, sk_X509_value(vc_store->certs, i)) )
347 VC_STORE_ERR_EXIT(store);
348
349 for( i=0, n=sk_X509_CRL_num(vc_store->crls); i<n; i++)
350 if( !X509_STORE_add_crl(store,
351 sk_X509_CRL_value(vc_store->crls, i)) )
352 VC_STORE_ERR_EXIT(store);
353
354 return(store);
355}
356
357int vc_verify_callback(int ok, X509_STORE_CTX *store)
358{
359 if(!ok) {
360 /* XXX handle action/abort */
361 if(!ignore_ssl)
362 snprintf(tmpstr, TMPSTRSIZE, "[SSL ERROR] %s",
363 X509_verify_cert_error_string(store->error));
364 else
365 snprintf(tmpstr, TMPSTRSIZE, "[SSL ERROR] %s (ignored)",
366 X509_verify_cert_error_string(store->error));
367
368 writecf(FS_ERR, tmpstr);
369 ok = ignore_ssl;
370 }
371 return(ok);
372}
373
374void vc_x509store_setflags(vc_x509store_t *store, int flags)
375{
376 store->flags |= flags;
377}
378
379void vc_x509store_setignssl(vc_x509store_t *store, int ignore)
380{
381 store->ignore_ssl |= ignore;
382 ignore_ssl = ignore;
383}
384
385void vc_x509store_clearflags(vc_x509store_t *store, int flags)
386{
387 store->flags &= ~flags;
388}
389
390void vc_x509store_setcb(vc_x509store_t *store,
391 vc_x509verify_cb_t callback)
392{
393 store->callback = callback;
394}
395
396void vc_x509store_set_pkeycb(vc_x509store_t *store,
397 vc_askpass_cb_t callback)
398{
399 store->askpass_callback = callback;
400}
401
402void vc_x509store_addcert(vc_x509store_t *store, X509 *cert)
403{
404 sk_X509_push(store->certs, cert);
405}
406
407void vc_x509store_setcafile(vc_x509store_t *store, char *file)
408{
409 if( store->cafile) free(store->cafile);
410 store->cafile = ( file ? strdup(file) : 0 );
411}
412
413void vc_x509store_setcapath(vc_x509store_t *store, char *path)
414{
415 if( store->capath) free(store->capath);
416 store->capath = ( path ? strdup(path) : 0 );
417}
418
419void vc_x509store_setcrlfile(vc_x509store_t *store, char *file)
420{
421 if( store->crlfile) free(store->crlfile);
422 store->crlfile = ( file ? strdup(file) : 0 );
423}
424
425void vc_x509store_setkeyfile(vc_x509store_t *store, char *file)
426{
427 if( store->use_keyfile) free(store->use_keyfile);
428 store->use_keyfile = ( file ? strdup(file) : 0 );
429}
430
431void vc_x509store_setcertfile(vc_x509store_t *store, char *file)
432{
433 if( store->use_certfile) free(store->use_certfile);
434 store->use_certfile = ( file ? strdup(file) : 0 );
435}
436
437
438void vc_init_x509store(vc_x509store_t *s)
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 s->ignore_ssl = 0;
453}
454
455void vc_cleanup_x509store(vc_x509store_t *s)
456{
457 if(s->cafile) free(s->cafile);
458 if(s->capath) free(s->capath);
459 if(s->crlfile) free(s->crlfile);
460 if(s->use_certfile) free(s->use_certfile);
461 if(s->use_keyfile) free(s->use_keyfile);
462 if(s->use_key) free(s->use_key);
463 sk_X509_free(s->certs);
464 sk_X509_free(s->crls);
465 sk_X509_free(s->use_certs);
466 s->ignore_ssl = 0;
467}