From 3c11bea99886b006ca499e1be6a3a17d225cedc7 Mon Sep 17 00:00:00 2001 From: erdgeist <> Date: Wed, 27 Jun 2007 21:59:32 +0000 Subject: Introducing new ssl code --- Makefile | 5 +- TODO | 4 + vchat-client.c | 13 +- vchat-config.h | 6 +- vchat-howto | 23 +++ vchat-protocol.c | 372 ++++++++++---------------------------------- vchat-ssl.c | 467 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ vchat-ssl.h | 53 +++++++ vchat-ui.c | 3 +- vchat.h | 2 +- 10 files changed, 642 insertions(+), 306 deletions(-) create mode 100755 vchat-ssl.c create mode 100755 vchat-ssl.h diff --git a/Makefile b/Makefile index 216f161..81c4fbe 100755 --- a/Makefile +++ b/Makefile @@ -30,7 +30,7 @@ CFLAGS += $(OLDREADLINE) PREFIX=/usr/local LIBS = -lreadline -lncurses -lssl -lcrypto -OBJS = vchat-client.o vchat-ui.o vchat-protocol.o vchat-user.o vchat-commands.o +OBJS = vchat-client.o vchat-ui.o vchat-protocol.o vchat-user.o vchat-commands.o vchat-ssl.o ############################################## @@ -83,5 +83,8 @@ vchat-user.o: vchat-user.c vchat.h vchat-commands.o: vchat-commands.c vchat.h vchat-config.h $(CC) $(CFLAGS) -o vchat-commands.o -c vchat-commands.c +vchat-ssl.o: vchat-ssl.c vchat-ssl.h + $(CC) $(CFLAGS) -o vchat-ssl.o -c vchat-ssl.c + #vchat-client.1: vchat-client.sgml # docbook2man vchat-client.sgml diff --git a/TODO b/TODO index ef91851..6e9f4df 100755 --- a/TODO +++ b/TODO @@ -1,3 +1,7 @@ + +- SSL: print SSL fingerprint and let the user decide wether it's authentic + and store it to a fingerprint cache or not. + - more documentation - .i with nickchanges diff --git a/vchat-client.c b/vchat-client.c index e0fc214..56cc721 100755 --- a/vchat-client.c +++ b/vchat-client.c @@ -25,7 +25,6 @@ #include #include #include -#include #ifndef NO_LOCALE #include #endif @@ -509,15 +508,11 @@ main (int argc, char **argv) loadformats(GLOBAL_FORMAT_FILE); loadformats(getstroption (CF_FORMFILE)); - if (!getintoption(CF_USESSL)) { - setstroption(CF_SERVERHOST,"localhost"); - setstroption(CF_SERVERPORT,"2323"); - } else { - SSL_library_init (); - SSL_load_error_strings (); - } + if (!getintoption(CF_USESSL)) + setstroption(CF_SERVERPORT,"2323"); + else + setstroption(CF_SERVERPORT,"2325"); - /* install signal handler */ signal (SIGINT, cleanup); signal (SIGHUP, cleanup); diff --git a/vchat-config.h b/vchat-config.h index e2bbc40..eb0afc1 100755 --- a/vchat-config.h +++ b/vchat-config.h @@ -24,14 +24,15 @@ /* configuration array with structure as defined in vchat.h */ extern unsigned int usessl; +extern unsigned int ignssl; extern unsigned int usetime; extern unsigned int hscroll; static volatile configoption configoptions[] = { /* config-option type name in file default value value localvar */ {CF_NICK, CO_STR, "nick", NULL, NULL, { .pstr = &nick } }, - {CF_FROM, CO_STR, "from", "vc-alpha-0.16", NULL, { NULL } }, - {CF_SERVERHOST, CO_STR, "host", "pulse.flatline.de", NULL, { NULL } }, + {CF_FROM, CO_STR, "from", "vc-alpha-0.17", NULL, { NULL } }, + {CF_SERVERHOST, CO_STR, "host", "localhost", NULL, { NULL } }, {CF_SERVERPORT, CO_STR, "port", "2325", NULL, { NULL } }, {CF_CIPHERSUITE, CO_STR, "ciphers", "HIGH:MEDIUM", NULL, { NULL } }, {CF_CONFIGFILE, CO_STR, "conffile", "~/.vchat/config", NULL, { NULL } }, @@ -41,6 +42,7 @@ static volatile configoption configoptions[] = { {CF_LOGINSCRIPT, CO_STR, "loginscript","~/.vchat/loginscript", NULL, { NULL } }, {CF_ENCODING, CO_STR, "encoding", NULL, NULL, { .pstr = &encoding }}, {CF_USESSL, CO_INT, "usessl", (char *) 1, (char *)-1, { .pint = &usessl } }, + {CF_IGNSSL, CO_INT, "ignssl", (char *) 0, (char *)-1, { .pint = &ignssl } }, {CF_USECERT, CO_INT, "usecert", (char *) 1, (char *)-1, { NULL } }, {CF_USETIME, CO_INT, "usetime", (char *) 1, (char *)-1, { .pint = &usetime } }, {CF_USETOPIC, CO_INT, "usetopicbar",(char *) 1, (char *)-1, { NULL } }, diff --git a/vchat-howto b/vchat-howto index da9ac6f..22c34b7 100755 --- a/vchat-howto +++ b/vchat-howto @@ -70,6 +70,29 @@ type: $ echo host=vchat.berlin.ccc.de >> ~/.vchat/config +If you want to ignore SSL-warnings due to missing CA-files, type: + +$ echo ignssl=1 >> ~/.vchat/config + +If you don't want to ignore SSL-warnings, get the root-certificates from: + + http://www.cacert.org/certs/class3.txt + and + http://www.cacert.org/certs/root.txt + +and copy them into your openssl-certs directory. For example: + + # cp root.txt /etc/ssl/certs/ + # cp class3.txt /etc/ssl/certs/ + # cd /etc/ssl/certs + # ln -s root.txt `openssl x509 -in root.txt -hash | head -n 1`.0 + # ln -s class3.txt `openssl x509 -in class3.txt -hash | head -n 1`.0 + +Now you can type: + + $ echo ignssl=0 >> ~/.vchat/config + + If you want a seperate private message window, type: $ echo messages=10 >> ~/.vchat/config diff --git a/vchat-protocol.c b/vchat-protocol.c index fc89cf3..274e856 100755 --- a/vchat-protocol.c +++ b/vchat-protocol.c @@ -33,6 +33,7 @@ /* local includes */ #include "vchat.h" +#include "vchat-ssl.h" /* version of this module */ char *vchat_io_version = "$Id$"; @@ -43,14 +44,7 @@ unsigned int usingcert = 1; /* locally global variables */ /* SSL-connection */ -static SSL *sslconn = NULL; - -/* declaration of an OpenSSL function which isn't exported by the includes, - * but by the library. we use it to set the accepted list of ciphers */ -STACK_OF(SSL_CIPHER) * ssl_create_cipher_list (const SSL_METHOD * meth, STACK_OF (SSL_CIPHER) ** pref, STACK_OF (SSL_CIPHER) ** sorted, const unsigned char *rule_str); - -static char *sslpubkey = NULL; /* servers public key extracted from X.509 certificate */ -static int sslpubkeybits = 0; /* length of server public key */ +static BIO *sslconn = NULL; /* declaration of local helper functions */ static void usersignon (char *); @@ -80,281 +74,90 @@ static int getportnum (char *port); extern int status; int usessl = 1; +int ignssl = 0; char *encoding; /* connects to server */ int vcconnect (char *server, char *port) { - /* used for tilde expansion of cert & key filenames */ - char *tildex = NULL; - /* buffer for X.509 subject of server certificate */ - char subjbuf[256]; - /* variables used to split the subject */ - char *subjv = NULL; - char *subjn = NULL; - char *subjc = NULL; - char *subjh = NULL; - /* pointer to key in certificate */ - EVP_PKEY *certpubkey = NULL; - /* temporary result */ - int result; - /* protocol independent server addresses */ - struct addrinfo hints, *addr, *tmpaddr; - /* SSL-context */ - SSL_CTX *sslctx = NULL; - /* SSL server certificate */ - X509 *sslserv = NULL; - /* SSL method function */ - SSL_METHOD *sslmeth = NULL; - - /* pointer to tilde-expanded certificate/keyfile-names */ - char *certfile = NULL, *keyfile = NULL; - - /* variable for verify return */ - long verify; - - memset( &hints, 0, sizeof(hints)); - /* Expect v4 and v6 */ - hints.ai_family = PF_UNSPEC; - hints.ai_socktype = SOCK_STREAM; - if( getaddrinfo( server, port, &hints, &addr )) - return 0; - for( tmpaddr = addr; addr; addr = addr->ai_next ) - { - if( (serverfd = socket( addr->ai_family, addr->ai_socktype, addr->ai_protocol)) < 0) - continue; - if( connect( serverfd, addr->ai_addr, addr->ai_addrlen ) < 0) - { - close( serverfd ); - serverfd = -1; - continue; - } - break; - } - if( serverfd < 0 ) - return 0; - freeaddrinfo( tmpaddr ); + /* used for tilde expansion of cert & key filenames */ + char *tildex = NULL; - /* inform user */ - snprintf (tmpstr, TMPSTRSIZE, getformatstr(FS_CONNECTED), server, getportnum(port)); - writechan (tmpstr); + /* vchat connection x509 store */ + vc_x509store_t vc_store; - usessl = getintoption(CF_USESSL); + /* SSL-context */ + SSL_CTX *sslctx = NULL; - /* do we want to use SSL? */ - if (usessl) - { - /* set ssl method -> SSLv2, SSLv3 & TLSv1 client */ - sslmeth = SSLv23_client_method (); - /* generate new SSL context */ - sslctx = SSL_CTX_new (sslmeth); - - /* set passphrase-callback to own function from vchat-ui.c */ - SSL_CTX_set_default_passwd_cb (sslctx, passprompt); - - /* set our list of accepted ciphers */ - ssl_create_cipher_list (sslctx->method, &(sslctx->cipher_list), &(sslctx->cipher_list_by_id), (unsigned char*)getstroption (CF_CIPHERSUITE)); - - /* get name of certificate file */ - certfile = getstroption (CF_CERTFILE); - - /* do we have a certificate file? */ - if (certfile) - { - /* does the filename start with a tilde? expand it! */ - if (certfile[0] == '~') - tildex = tilde_expand (certfile); - else - tildex = certfile; - - if (usingcert) { - /* try to load certificate */ - result = SSL_CTX_use_certificate_file (sslctx, tildex, SSL_FILETYPE_PEM); - if (!result) - { - /* failed, inform user */ - snprintf (tmpstr, TMPSTRSIZE, "!! Loading user certificate fails: %s", ERR_error_string (ERR_get_error (), NULL)); - writecf (FS_ERR,tmpstr); - } - else - { - /* get name of key file */ - keyfile = getstroption (CF_KEYFILE); - - /* if we don't have a key file, the key may be in the cert file */ - if (!keyfile) - keyfile = certfile; - - /* does the filename start with a tilde? expand it! */ - if (keyfile[0] == '~') - tildex = tilde_expand (keyfile); - else - tildex = keyfile; - - /* try to load key (automatically asks for passphrase, if encrypted */ - result = SSL_CTX_use_PrivateKey_file (sslctx, tildex, SSL_FILETYPE_PEM); - if (!result) - { - /* failed, inform user */ - snprintf (tmpstr, TMPSTRSIZE, "!! Loading private key fails: %s", ERR_error_string (ERR_get_error (), NULL)); - writecf (FS_ERR,tmpstr); - } - else - { - /* check if OpenSSL thinks key & cert belong together */ - result = SSL_CTX_check_private_key (sslctx); - if (!result) - { - /* they don't, inform user */ - snprintf (tmpstr, TMPSTRSIZE, "!! Verifying key and certificate fails: %s", ERR_error_string (ERR_get_error (), NULL)); - writecf (FS_ERR,tmpstr); - } - } - } - } - } - - /* don't worry to much about servers X.509 certificate chain */ - SSL_CTX_set_verify_depth (sslctx, 0); - - /* debug massage about verify mode */ - snprintf (tmpstr, TMPSTRSIZE, "# Connecting with verify depth %d in mode %d", SSL_CTX_get_verify_depth (sslctx), SSL_CTX_get_verify_mode (sslctx)); - writecf (FS_DBG,tmpstr); - - /* generate new SSL connection object and associate - * filedescriptor of server connection */ - sslconn = SSL_new (sslctx); - SSL_set_fd (sslconn, serverfd); - - /* try SSL handshake */ - if (SSL_connect (sslconn) <= 0) - { - /* no SSL connection possible */ - snprintf (tmpstr, TMPSTRSIZE, "!! SSL Connect fails: %s", ERR_error_string (ERR_get_error (), NULL)); - writecf (FS_ERR,tmpstr); - return 0; - } + /* pointer to tilde-expanded certificate/keyfile-names */ + char *certfile = NULL, *keyfile = NULL; - /* debug message about used symmetric cipher */ - snprintf (tmpstr, TMPSTRSIZE, "# SSL connection uses %s cipher (%d bits)", SSL_get_cipher_name (sslconn), SSL_get_cipher_bits (sslconn, NULL)); - writecf (FS_DBG,tmpstr); - - /* if we can't get the servers certificate something is damn wrong */ - if (!(sslserv = SSL_get_peer_certificate (sslconn))) - return 0; - - /* ugly dump of server certificate */ - /* TODO: make this happen elsewhere, and preferably only when user wants - * or a new key needs to be added to the known_hosts */ - writecf (FS_DBG,"# SSL Server information:"); - /* copy X.509 to local buffer */ - strncpy (subjbuf, sslserv->name, 256); - /* split a subject line and print the fullname/value pairs */ - subjv = &subjbuf[1]; - while (subjv) - { - subjc = strchr (subjv, '='); - subjc[0] = '\0'; - subjc++; - /* yeah, ugly */ - if (!strcasecmp ("C", subjv)) - { - subjn = "Country "; - } - else if (!strcasecmp ("ST", subjv)) - { - subjn = "State "; - } - else if (!strcasecmp ("L", subjv)) - { - subjn = "Location "; - } - else if (!strcasecmp ("O", subjv)) - { - subjn = "Organization"; - } - else if (!strcasecmp ("OU", subjv)) - { - subjn = "Organiz.unit"; - } - else if (!strcasecmp ("CN", subjv)) - { - subjn = "Common name "; - subjh = subjc; - } - else if (!strcasecmp ("Email", subjv)) - { - subjn = "Emailaddress"; - } - else - { - subjn = "UNKNOWN "; - } - subjv = strchr (subjc, '/'); - if (subjv) - { - subjv[0] = '\0'; - subjv++; - } - /* print value pair */ - snprintf (tmpstr, TMPSTRSIZE, "# %s: %s", subjn, subjc); - writecf (FS_DBG,tmpstr); - } + SSL_library_init (); + SSL_load_error_strings(); - /* check if verifying the server's certificate yields any errors */ - verify = SSL_get_verify_result (sslconn); - if (verify) - { - /* it does - yield a warning to the user */ - snprintf (tmpstr, TMPSTRSIZE, "!! Certificate verification fails: %s", X509_verify_cert_error_string (verify)); - writecf (FS_ERR,tmpstr); - } + vc_init_x509store(&vc_store); - /* check if servers name in certificate matches the hostname we connected to */ - if (subjh) - if (strcasecmp (server, subjh)) - { - /* it doesn't - yield a warning and show difference */ - writecf (FS_ERR,"!! Server name does not match name in presented certificate!"); - snprintf (tmpstr, TMPSTRSIZE, "!! '%s' != '%s'", server, subjh); - writecf (FS_ERR,tmpstr); - } - - /* try to get the servers public key */ - certpubkey = X509_get_pubkey (sslserv); - if (certpubkey == NULL) - { - /* huh, we can't? */ - writecf (FS_ERR,"!! Can't get the public key associated to the certificate"); - } - /* check what type of key we've got here */ - else if (certpubkey->type == EVP_PKEY_RSA) - { - /* RSA key, convert bignum values from OpenSSL's structures to - * something readable */ - sslpubkey = BN_bn2hex (certpubkey->pkey.rsa->n); - sslpubkeybits = BN_num_bits (certpubkey->pkey.rsa->n); - /* dump keylength and hexstring of servers key to user */ - snprintf (tmpstr, TMPSTRSIZE, "# RSA public key%s: %d %s", (certpubkey->pkey.rsa-> d) ? " (private key available)" : "", sslpubkeybits, sslpubkey); - writecf (FS_DBG,tmpstr); - /* TODO: known_hosts check here ... */ - } - else if (certpubkey->type == EVP_PKEY_DSA) - { - /* Can't handle (and didn't encounter) DSA keys */ - writecf (FS_ERR,"# DSA Public Key (output currently not supported)"); - /* TODO: known_hosts check here ... */ - } + vc_x509store_setflags(&vc_store, VC_X509S_SSL_VERIFY_PEER); + /* get name of certificate file */ + certfile = getstroption (CF_CERTFILE); + + /* do we have a certificate file? */ + if (certfile) { + /* does the filename start with a tilde? expand it! */ + if (certfile[0] == '~') + tildex = tilde_expand (certfile); else - { - writecf (FS_ERR,"# Public Key type not supported"); - /* TODO: fail known_hosts check ... */ - } - } + tildex = certfile; + + if (usingcert) { + + vc_x509store_setflags(&vc_store, VC_X509S_USE_CERTIFICATE); + vc_x509store_setcertfile(&vc_store, tildex); + + /* get name of key file */ + keyfile = getstroption (CF_KEYFILE); + + /* if we don't have a key file, the key may be in the cert file */ + if (!keyfile) + keyfile = certfile; - /* if we didn't fail until now, we've got a connection. */ - return 1; + /* does the filename start with a tilde? expand it! */ + if (keyfile[0] == '~') + tildex = tilde_expand (keyfile); + else + tildex = keyfile; + + vc_x509store_set_pkeycb(&vc_store, (vc_askpass_cb_t)passprompt); + vc_x509store_setkeyfile(&vc_store, tildex); + + /* check if OpenSSL thinks key & cert belong together */ + /* result = SSL_CTX_check_private_key (sslctx); THS TODO (-> + * vchat-ssl.c) */ + } + } + + usessl = getintoption(CF_USESSL); + vc_x509store_setignssl(&vc_store, getintoption(CF_IGNSSL)); + + sslconn = vc_connect(server, getportnum(port), usessl, &vc_store, &sslctx); + + if(sslconn == NULL) { + exitui(); + exit(-1); + } + + serverfd = BIO_get_fd(sslconn, 0); + + /* inform user */ + snprintf (tmpstr, TMPSTRSIZE, getformatstr(FS_CONNECTED), server, getportnum(port)); + writechan (tmpstr); + + /* dump x509 details here... TODO */ + writecf (FS_DBG,"# SSL Server information: TODO :)"); + + /* if we didn't fail until now, we've got a connection. */ + return 1; } /* disconnect from server */ @@ -1021,11 +824,8 @@ networkinput (void) char *ltmp = buf; buf[BUFSIZE-1] = '\0'; /* sanity stop */ - /* check if we use ssl or if we don't and receive data at offset */ - if (!usessl) - bytes = recv (serverfd, &buf[bufoff], BUFSIZE-1 - bufoff, 0); - else - bytes = SSL_read (sslconn, &buf[bufoff], BUFSIZE-1 - bufoff); + /* receive data at offset */ + bytes = BIO_read (sslconn, &buf[bufoff], BUFSIZE-1 - bufoff); /* no bytes transferred? raise error message, bail out */ if (bytes < 0) @@ -1093,23 +893,11 @@ networkoutput (char *msg) fprintf (stderr, ">| %s\n", msg); #endif - /* TODO: err .. rework this (blocking) code */ + /* send data to server */ + if (BIO_write (sslconn, msg, strlen (msg)) != strlen (msg)) + writecf (FS_ERR,"Message sending fuzzy."); - /* check if we use ssl or if we don't and send data to server */ - if (!usessl) { - if (send (serverfd, msg, strlen (msg), 0) != strlen (msg)) { - writecf (FS_ERR,"Message sending fuzzy."); - } - } else if (SSL_write (sslconn, msg, strlen (msg)) != strlen (msg)) { - writecf (FS_ERR,"Message sending fuzzy."); - } - - /* check if we use ssl or if we don't and send line termination to server */ - if (!usessl) { - if (send (serverfd, "\r\n", 2, 0) != 2) { - writecf (FS_ERR,"Message sending fuzzy!"); - } - } else if (SSL_write (sslconn, "\r\n", 2) != 2) { - writecf (FS_ERR,"Message sending fuzzy."); - } + /* send line termination to server */ + if (BIO_write (sslconn, "\r\n", 2) != 2) + writecf (FS_ERR,"Message sending fuzzy."); } 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 @@ +/* + * vchat-client - alpha version + * vchat-ssl.c - handling of SSL connection and X509 certificate + * verification + * + * Copyright (C) 2007 Thorsten Schroeder + * + * This program is free software. It can be redistributed and/or modified, + * provided that this copyright notice is kept intact. This program is + * distributed in the hope that it will be useful, but without any warranty; + * without even the implied warranty of merchantability or fitness for a + * particular purpose. In no event shall the copyright holder be liable for + * any direct, indirect, incidental or special damages arising in any way out + * of the use of this software. + * + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "vchat.h" +#include "vchat-ssl.h" + +char *vchat_ssl_version = "$Id$"; + +static int ignore_ssl; + +#define VC_CTX_ERR_EXIT(se, cx) do { \ + snprintf(tmpstr, TMPSTRSIZE, "CREATE CTX: %s", \ + ERR_error_string (ERR_get_error (), NULL)); \ + writecf(FS_ERR, tmpstr); \ + if(se) X509_STORE_free(se); \ + if(cx) SSL_CTX_free(cx); \ + return(0); \ + } while(0) + +#define VC_SETCERT_ERR_EXIT(se, cx, err) do { \ + snprintf(tmpstr, TMPSTRSIZE, "CREATE CTX: %s", err); \ + writecf(FS_ERR, tmpstr); \ + if(se) X509_STORE_free(se); \ + if(cx) SSL_CTX_free(cx); \ + return(NULL); \ + } while(0) + +SSL_CTX * vc_create_sslctx( vc_x509store_t *vc_store ) +{ + int i = 0; + int n = 0; + int flags = 0; + int r = 0; + SSL_CTX *ctx = NULL; + X509_STORE *store = NULL; + vc_x509verify_cb_t verify_callback = NULL; + + if( !(ctx = SSL_CTX_new(SSLv3_method())) ) + VC_CTX_ERR_EXIT(store, ctx); + + if( !(store = vc_x509store_create(vc_store)) ) + VC_CTX_ERR_EXIT(store, ctx); + + SSL_CTX_set_cert_store(ctx, store); + store = NULL; + SSL_CTX_set_options(ctx, SSL_OP_ALL|SSL_OP_NO_SSLv2); + SSL_CTX_set_cipher_list(ctx, "ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH"); + + SSL_CTX_set_verify_depth (ctx, 2); + + if( !(verify_callback = vc_store->callback) ) + verify_callback = vc_verify_callback; + + if( !(vc_store->flags & VC_X509S_SSL_VERIFY_MASK) ) { + writecf(FS_DBG, tmpstr); + flags = SSL_VERIFY_NONE; + } + else { + if(vc_store->flags & VC_X509S_SSL_VERIFY_PEER) + flags |= SSL_VERIFY_PEER; + if(vc_store->flags & VC_X509S_SSL_VERIFY_FAIL_IF_NO_PEER_CERT) + flags |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT; + if(vc_store->flags & VC_X509S_SSL_VERIFY_CLIENT_ONCE) + flags |= SSL_VERIFY_CLIENT_ONCE; + } + + SSL_CTX_set_verify(ctx, flags, verify_callback); + + if(vc_store->flags & VC_X509S_USE_CERTIFICATE) { + if(vc_store->use_certfile) + SSL_CTX_use_certificate_chain_file(ctx, vc_store->use_certfile); + else { + SSL_CTX_use_certificate(ctx, + sk_X509_value(vc_store->use_certs, 0)); + for(i=0,n=sk_X509_num(vc_store->use_certs); iuse_certs, i)); + } + + SSL_CTX_set_default_passwd_cb(ctx, vc_store->askpass_callback); + + if(vc_store->use_keyfile) { + r=SSL_CTX_use_PrivateKey_file(ctx, vc_store->use_keyfile, + SSL_FILETYPE_PEM); + } else if(vc_store->use_key) + r=SSL_CTX_use_PrivateKey(ctx, vc_store->use_key); + + if(r!=1) + VC_SETCERT_ERR_EXIT(store, ctx, "Load private key failed"); + + } + + SSL_CTX_set_app_data(ctx, vc_store); + return(ctx); +} + +#define VC_CONNSSL_ERR_EXIT(_cx, cx, cn) do { \ + snprintf(tmpstr, TMPSTRSIZE, "[SSL ERROR] %s", \ + ERR_error_string (ERR_get_error (), NULL)); \ + writecf(FS_ERR, tmpstr); \ + if(cn) BIO_free_all(cn); \ + if(*cx) SSL_CTX_free(*cx); \ + if(_cx) *cx = 0; \ + return(0); \ + } while(0) + +BIO * vc_connect_ssl(char *host, int port, vc_x509store_t *vc_store, + SSL_CTX **ctx) +{ + BIO *conn = NULL; + int _ctx = 0; + + if(*ctx) { + CRYPTO_add( &((*ctx)->references), 1, CRYPTO_LOCK_SSL_CTX ); + if( vc_store && vc_store != SSL_CTX_get_app_data(*ctx) ) { + SSL_CTX_set_cert_store(*ctx, vc_x509store_create(vc_store)); + SSL_CTX_set_app_data(*ctx, vc_store); + } + } else { + *ctx = vc_create_sslctx(vc_store); + _ctx = 1; + } + + if( !(conn = BIO_new_ssl_connect(*ctx)) ) + VC_CONNSSL_ERR_EXIT(_ctx, ctx, conn); + + BIO_set_conn_hostname(conn, host); + BIO_set_conn_int_port(conn, &port); + + fflush(stdout); + if(BIO_do_connect(conn) <= 0) + VC_CONNSSL_ERR_EXIT(_ctx, ctx, conn); + + if(_ctx) + SSL_CTX_free(*ctx); + + return(conn); +} + +#define VC_CONN_ERR_EXIT(cn) do { \ + snprintf(tmpstr, TMPSTRSIZE, "[SSL ERROR] %s", \ + ERR_error_string(ERR_get_error(), NULL)); \ + if(ERR_get_error()) \ + writecf(FS_ERR, tmpstr); \ + if(cn) BIO_free_all(cn); \ + return(NULL); \ + } while(0) + +#define VC_VERIFICATION_ERR_EXIT(cn, err) do { \ + snprintf(tmpstr, TMPSTRSIZE, \ + "[SSL ERROR] certificate verify failed: %s", err); \ + writecf(FS_ERR, tmpstr); \ + if(cn && !ignore_ssl) { BIO_free_all(cn); return(NULL); } \ + } while(0) + +BIO * vc_connect( char *host, int port, int use_ssl, + vc_x509store_t *vc_store, SSL_CTX **ctx) +{ + BIO *conn = NULL; + SSL *ssl = NULL; + + if(use_ssl) { + if( !(conn = vc_connect_ssl(host, port, vc_store, ctx)) ) + VC_CONN_ERR_EXIT(conn); + + + BIO_get_ssl(conn, &ssl); + if(!vc_verify_cert_hostname(SSL_get_peer_certificate(ssl), host)) + VC_VERIFICATION_ERR_EXIT(conn, "Hostname does not match!"); + + return(conn); + } + + *ctx = 0; + + if( !(conn = BIO_new_connect(host)) ) + VC_CONN_ERR_EXIT(conn); + + BIO_set_conn_int_port(conn, &port); + + if(BIO_do_connect(conn) <= 0) + VC_CONN_ERR_EXIT(conn); + + return(conn); +} + +int vc_verify_cert_hostname(X509 *cert, char *host) +{ + + int i = 0; + int j = 0; + int n = 0; + int extcount = 0; + int ok = 0; + + X509_NAME *subj = NULL; + const char *extstr = NULL; + CONF_VALUE *nval = NULL; + unsigned char *data = NULL; + X509_EXTENSION *ext = NULL; + X509V3_EXT_METHOD *meth = NULL; + STACK_OF(CONF_VALUE) *val = NULL; + + char name[256]; + memset(&name, 0, sizeof(name)); + + if((extcount = X509_get_ext_count(cert)) > 0) { + + for(i=0; !ok && i < extcount; i++) { + + meth = NULL; + + ext = X509_get_ext(cert, i); + extstr = OBJ_nid2sn(OBJ_obj2nid(X509_EXTENSION_get_object(ext))); + + if(!strcasecmp(extstr, "subjectAltName")) { + + if( !(meth = X509V3_EXT_get(ext)) ) + break; + + if( !(meth->d2i) ) + break; + + data = ext->value->data; + + val = meth->i2v(meth, meth->d2i(0, &data, ext->value->length), 0); + for( j=0, n=sk_CONF_VALUE_num(val); jname, "DNS") && + !strcasecmp(nval->value, host) ) { + ok = 1; + break; + } + } + } + } + } + + if( !ok && (subj = X509_get_subject_name(cert)) && + X509_NAME_get_text_by_NID(subj, NID_commonName, + name, sizeof(name)) > 0 ) { + name[sizeof(name)-1] = '\0'; + if(!strcasecmp(name, host)) + ok = 1; + } + + //printf("[*] vc_verify_cert_hostname() return: %d\n", ok); + return(ok); +} + +int vc_verify_cert(X509 *cert, vc_x509store_t *vc_store) +{ + int result = -1; + X509_STORE *store = NULL; + X509_STORE_CTX *ctx = NULL; + + if( !(store = vc_x509store_create(vc_store)) ) + return(result); + + if( (ctx = X509_STORE_CTX_new()) != 0 ) { + if(X509_STORE_CTX_init(ctx, store, cert, 0) == 1) + result = (X509_verify_cert(ctx) == 1); + X509_STORE_CTX_free(ctx); + } + + X509_STORE_free(store); + return(result); +} + +#define VC_STORE_ERR_EXIT(s) do { \ + fprintf(stderr, "[E] SSL_STORE: %s\n", ERR_error_string (ERR_get_error (), NULL)); \ + if(s) X509_STORE_free(s); \ + return(0); \ + } while(0) + +X509_STORE *vc_x509store_create(vc_x509store_t *vc_store) +{ + int i = 0; + int n = 0; + X509_STORE *store = NULL; + X509_LOOKUP *lookup = NULL; + + store = X509_STORE_new(); + + if(vc_store->callback) + X509_STORE_set_verify_cb_func(store, vc_store->callback); + else + X509_STORE_set_verify_cb_func(store, vc_verify_callback); + + if( !(lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file())) ) + VC_STORE_ERR_EXIT(store); + + if(!vc_store->cafile) { + if( !(vc_store->flags & VC_X509S_NODEF_CAFILE) ) + X509_LOOKUP_load_file(lookup, 0, X509_FILETYPE_DEFAULT); + } else if( !X509_LOOKUP_load_file(lookup, vc_store->cafile, + X509_FILETYPE_PEM) ) + VC_STORE_ERR_EXIT(store); + + if(vc_store->crlfile) { + if( !X509_load_crl_file(lookup, vc_store->crlfile, + X509_FILETYPE_PEM) ) + VC_STORE_ERR_EXIT(store); + + X509_STORE_set_flags( store, + X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL ); + } + + if( !(lookup = X509_STORE_add_lookup(store, X509_LOOKUP_hash_dir())) ) + VC_STORE_ERR_EXIT(store); + + if( !vc_store->capath ) { + if( !(vc_store->flags & VC_X509S_NODEF_CAPATH) ) + X509_LOOKUP_add_dir(lookup, 0, X509_FILETYPE_DEFAULT); + } else if( !X509_LOOKUP_add_dir(lookup, vc_store->capath, + X509_FILETYPE_PEM) ) + VC_STORE_ERR_EXIT(store); + + for( i=0, n=sk_X509_num(vc_store->certs); icerts, i)) ) + VC_STORE_ERR_EXIT(store); + + for( i=0, n=sk_X509_CRL_num(vc_store->crls); icrls, i)) ) + VC_STORE_ERR_EXIT(store); + + return(store); +} + +int vc_verify_callback(int ok, X509_STORE_CTX *store) +{ + if(!ok) { + /* XXX handle action/abort */ + if(!ignore_ssl) + snprintf(tmpstr, TMPSTRSIZE, "[SSL ERROR] %s", + X509_verify_cert_error_string(store->error)); + else + snprintf(tmpstr, TMPSTRSIZE, "[SSL ERROR] %s (ignored)", + X509_verify_cert_error_string(store->error)); + + writecf(FS_ERR, tmpstr); + ok = ignore_ssl; + } + return(ok); +} + +void vc_x509store_setflags(vc_x509store_t *store, int flags) +{ + store->flags |= flags; +} + +void vc_x509store_setignssl(vc_x509store_t *store, int ignore) +{ + store->ignore_ssl |= ignore; + ignore_ssl = ignore; +} + +void vc_x509store_clearflags(vc_x509store_t *store, int flags) +{ + store->flags &= ~flags; +} + +void vc_x509store_setcb(vc_x509store_t *store, + vc_x509verify_cb_t callback) +{ + store->callback = callback; +} + +void vc_x509store_set_pkeycb(vc_x509store_t *store, + vc_askpass_cb_t callback) +{ + store->askpass_callback = callback; +} + +void vc_x509store_addcert(vc_x509store_t *store, X509 *cert) +{ + sk_X509_push(store->certs, cert); +} + +void vc_x509store_setcafile(vc_x509store_t *store, char *file) +{ + if( store->cafile) free(store->cafile); + store->cafile = ( file ? strdup(file) : 0 ); +} + +void vc_x509store_setcapath(vc_x509store_t *store, char *path) +{ + if( store->capath) free(store->capath); + store->capath = ( path ? strdup(path) : 0 ); +} + +void vc_x509store_setcrlfile(vc_x509store_t *store, char *file) +{ + if( store->crlfile) free(store->crlfile); + store->crlfile = ( file ? strdup(file) : 0 ); +} + +void vc_x509store_setkeyfile(vc_x509store_t *store, char *file) +{ + if( store->use_keyfile) free(store->use_keyfile); + store->use_keyfile = ( file ? strdup(file) : 0 ); +} + +void vc_x509store_setcertfile(vc_x509store_t *store, char *file) +{ + if( store->use_certfile) free(store->use_certfile); + store->use_certfile = ( file ? strdup(file) : 0 ); +} + + +void vc_init_x509store(vc_x509store_t *s) +{ + s->cafile = NULL; + s->capath = NULL; + s->crlfile = NULL; + s->callback = NULL; + s->askpass_callback = NULL; + s->certs = sk_X509_new_null(); + s->crls = sk_X509_CRL_new_null(); + s->use_certfile = NULL; + s->use_certs = sk_X509_new_null(); + s->use_keyfile = NULL; + s->use_key = NULL; + s->flags = 0; + s->ignore_ssl = 0; +} + +void vc_cleanup_x509store(vc_x509store_t *s) +{ + if(s->cafile) free(s->cafile); + if(s->capath) free(s->capath); + if(s->crlfile) free(s->crlfile); + if(s->use_certfile) free(s->use_certfile); + if(s->use_keyfile) free(s->use_keyfile); + if(s->use_key) free(s->use_key); + sk_X509_free(s->certs); + sk_X509_free(s->crls); + sk_X509_free(s->use_certs); + s->ignore_ssl = 0; +} diff --git a/vchat-ssl.h b/vchat-ssl.h new file mode 100755 index 0000000..58e9dec --- /dev/null +++ b/vchat-ssl.h @@ -0,0 +1,53 @@ + +/* types */ + +typedef int (*vc_x509verify_cb_t)(int, X509_STORE_CTX *); +typedef int (*vc_askpass_cb_t)(char *, int, int, void *); +typedef struct { + char *cafile; + char *capath; + char *crlfile; + vc_x509verify_cb_t callback; + vc_askpass_cb_t askpass_callback; + STACK_OF(X509) *certs; + STACK_OF(X509_CRL) *crls; + char *use_certfile; + STACK_OF(X509) *use_certs; + char *use_keyfile; + EVP_PKEY *use_key; + int flags; + int ignore_ssl; +} vc_x509store_t; + +/* prototypes */ + +BIO * vc_connect(char *, int , int, vc_x509store_t *, SSL_CTX **); +BIO * vc_connect_ssl(char *, int, vc_x509store_t *, SSL_CTX **); +SSL_CTX * vc_create_sslctx( vc_x509store_t *); +void vc_init_x509store(vc_x509store_t *); +void vc_cleanup_x509store(vc_x509store_t *); +void vc_x509store_setcafile(vc_x509store_t *, char *); +void vc_x509store_setcapath(vc_x509store_t *, char *); +void vc_x509store_setcrlfile(vc_x509store_t *, char *); +void vc_x509store_setkeyfile(vc_x509store_t *, char *); +void vc_x509store_setcertfile(vc_x509store_t *, char *); +void vc_x509store_addcert(vc_x509store_t *, X509 *); +void vc_x509store_setcb(vc_x509store_t *, vc_x509verify_cb_t); +void vc_x509store_set_pkeycb(vc_x509store_t *, vc_askpass_cb_t); +void vc_x509store_setflags(vc_x509store_t *, int); +void vc_x509store_setignssl(vc_x509store_t *, int); +void vc_x509store_clearflags(vc_x509store_t *, int); +int vc_verify_cert(X509 *, vc_x509store_t *); +int vc_verify_cert_hostname(X509 *, char *); +int vc_verify_callback(int, X509_STORE_CTX *); +X509_STORE * vc_x509store_create(vc_x509store_t *); + +#define VC_X509S_NODEF_CAFILE 0x01 +#define VC_X509S_NODEF_CAPATH 0x02 +#define VC_X509S_USE_CERTIFICATE 0x04 +#define VC_X509S_SSL_VERIFY_NONE 0x10 +#define VC_X509S_SSL_VERIFY_PEER 0x20 +#define VC_X509S_SSL_VERIFY_FAIL_IF_NO_PEER_CERT 0x40 +#define VC_X509S_SSL_VERIFY_CLIENT_ONCE 0x80 +#define VC_X509S_SSL_VERIFY_MASK 0xF0 + diff --git a/vchat-ui.c b/vchat-ui.c index 51e33d5..ce0260c 100755 --- a/vchat-ui.c +++ b/vchat-ui.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -35,7 +36,7 @@ char *vchat_ui_version = "$Id$"; /* externally used variables */ /* current string in topic window */ -char topicstr[TOPICSTRSIZE] = "[] VChat 0.16"; +char topicstr[TOPICSTRSIZE] = "[] VChat 0.17"; /* current string in console window */ char consolestr[CONSOLESTRSIZE] = "[ Get help: .h for server /h for client commands"; diff --git a/vchat.h b/vchat.h index fbc9a2e..83bae1f 100755 --- a/vchat.h +++ b/vchat.h @@ -37,7 +37,7 @@ typedef struct servermessage servermessage; typedef enum { CO_NIL, CO_STR, CO_INT } conftype; typedef enum { CF_NIL, CF_NICK, CF_FROM, CF_SERVERHOST, CF_SERVERPORT, CF_CIPHERSUITE, CF_CONFIGFILE, CF_CERTFILE, CF_KEYFILE, CF_FORMFILE, CF_LOGINSCRIPT, -CF_USESSL, CF_USECERT, CF_PRIVHEIGHT, CF_PRIVCOLLAPS, CF_HSCROLL, CF_CHANNEL, CF_USETIME, +CF_USESSL, CF_IGNSSL, CF_USECERT, CF_PRIVHEIGHT, CF_PRIVCOLLAPS, CF_HSCROLL, CF_CHANNEL, CF_USETIME, CF_USETOPIC, CF_SCROLLBPRIV, CF_SCROLLBACK, CF_SCROLLBPRIVT, CF_SCROLLBACKT, CF_ENCODING, CF_BELLPRIV } confopt; -- cgit v1.2.3