From d1ac67f6d73f24a165ccc008440bb8b208ae140f Mon Sep 17 00:00:00 2001 From: Dirk Engling Date: Mon, 16 May 2022 15:53:39 +0200 Subject: Decouple IO openssl's BIO abstraction and split connection and tls handling to allow for other TLS libs --- Makefile | 15 +- vchat-client.c | 46 ++--- vchat-commands.c | 13 +- vchat-config.h | 1 + vchat-connection.c | 223 +++++++++++++++++++++++++ vchat-connection.h | 9 + vchat-protocol.c | 186 ++------------------- vchat-ssl.c | 475 ---------------------------------------------------- vchat-ssl.h | 23 --- vchat-tls.c | 481 +++++++++++++++++++++++++++++++++++++++++++++++++++++ vchat-tls.h | 30 ++++ vchat-ui.c | 16 +- vchat.h | 10 +- 13 files changed, 799 insertions(+), 729 deletions(-) create mode 100644 vchat-connection.c create mode 100644 vchat-connection.h delete mode 100755 vchat-ssl.c delete mode 100755 vchat-ssl.h create mode 100755 vchat-tls.c create mode 100755 vchat-tls.h diff --git a/Makefile b/Makefile index 1796234..3eb2efa 100755 --- a/Makefile +++ b/Makefile @@ -26,11 +26,13 @@ CFLAGS += $(OLDREADLINE) ## enable debug code #CFLAGS += -DDEBUG +#LDFLAGS = -L"/usr/local/opt/openssl@1.1/lib" + ## the install prefix best is /usr/local PREFIX=/usr/local -LIBS = -lreadline -lncursesw -lssl -lcrypto -OBJS = vchat-client.o vchat-ui.o vchat-protocol.o vchat-user.o vchat-commands.o vchat-ssl.o +LIBS = -lssl -lcrypto -lncurses -lreadline +OBJS = vchat-client.o vchat-ui.o vchat-protocol.o vchat-user.o vchat-commands.o vchat-tls.o vchat-connection.o ############################################## @@ -66,7 +68,7 @@ clean: ############################################## vchat-client: $(OBJS) - $(CC) $(CFLAGS) -o vchat-client $(OBJS) $(LIBS) + $(CC) $(CFLAGS) -o vchat-client $(OBJS) $(LIBS) $(LDFLAGS) vchat-client.o: vchat-client.c vchat-config.h Makefile $(CC) $(CFLAGS) -o vchat-client.o -c vchat-client.c @@ -83,8 +85,11 @@ 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-tls.o: vchat-tls.c vchat-tls.h + $(CC) $(CFLAGS) -o vchat-tls.o -c vchat-tls.c + +vchat-connection.o: vchat-connection.c vchat-connection.h + $(CC) $(CFLAGS) -o vchat-connection.o -c vchat-connection.c #vchat-client.1: vchat-client.sgml # docbook2man vchat-client.sgml diff --git a/vchat-client.c b/vchat-client.c index 0e480be..d6a3db3 100755 --- a/vchat-client.c +++ b/vchat-client.c @@ -29,6 +29,7 @@ #include #include "vchat.h" +#include "vchat-connection.h" #include "vchat-user.h" /* version of this module */ @@ -43,7 +44,6 @@ int status = 1; int ownquit = 0; /* we set this, we DONT want to quit */ int wantreconnect = 0; -unsigned int want_tcp_keepalive = 0; static int reconnect_delay = 6; static time_t reconnect_time = 0; @@ -51,17 +51,10 @@ static time_t reconnect_time = 0; /* error string to show after exit */ char errstr[ERRSTRSIZE] = "\0"; -/* locally global variables */ -/* our list of filedescriptors */ -static fd_set masterfds; - /* declaration of configuration array */ #include "vchat-config.h" -/* servers filedescriptor from vchat-protocol.c */ -extern int serverfd; - -void setnoption (char *, char *); +void setnoption (const char *, char *); static void parsecfg(char *line) { int bytes; @@ -264,7 +257,7 @@ setstroption (confopt option, char *string) /* set-named-option, puts string to variable named by name */ void -setnoption (char *name, char *string) +setnoption (const char *name, char *string) { int i; #ifdef DEBUG @@ -354,11 +347,8 @@ cleanup (int signal) exitui (); /* clear userlist */ ul_clear (); - /* close server connection */ - if (serverfd > 0) { - close (serverfd); - serverfd = -1; - } + vc_disconnect(); + /* inform user if we where killed by signal */ if (signal > 1) { @@ -394,12 +384,9 @@ void calleverysecond( void ) { void eventloop (void) { - /* get fresh copy of filedescriptor list */ - fd_set readfds = masterfds; - struct timeval tv = { 1, 0}; + int poll_result = vc_poll( 1 /* second timeout */ ); - switch (select (serverfd + 2, &readfds, NULL, NULL, &tv)) - { + switch (poll_result) { case -1: /* EINTR is most likely a SIGWINCH - ignore for now */ if (errno != EINTR) @@ -411,19 +398,19 @@ eventloop (void) writecf (FS_ERR,tmpstr); /* see this as an error condition and bail out */ status = 0; - } - break; + } + break; case 0: /* time out reached */ calleverysecond(); break; default: /* something to read from user & we're logged in or have a cert? */ - if (FD_ISSET (0, &readfds) ) + if (poll_result & 1) userinput (); /* something to read from server? */ - if (serverfd!=-1 && FD_ISSET (serverfd, &readfds)) + if (poll_result & 2) networkinput (); break; } @@ -511,12 +498,8 @@ main (int argc, char **argv) initui (); while( status ) { - /* add stdin to masterfds */ - FD_ZERO (&masterfds); - FD_SET (0, &masterfds); - /* attempt connection */ - if (vcconnect (getstroption(CF_SERVERHOST), getstroption(CF_SERVERPORT))) { + if (vc_connect (getstroption(CF_SERVERHOST), getstroption(CF_SERVERPORT))) { snprintf (tmpstr, TMPSTRSIZE, "Could not connect to server, %s.", strerror(errno)); strncpy(errstr,tmpstr,TMPSTRSIZE-2); errstr[TMPSTRSIZE-2] = '\0'; @@ -531,8 +514,7 @@ main (int argc, char **argv) } else status = 0; } else { - /* add serverfd to masterfds, reset reconnect delay */ - FD_SET (serverfd, &masterfds); + /* reset reconnect delay */ reconnect_delay = 6; reconnect_time = 0; } @@ -541,7 +523,7 @@ main (int argc, char **argv) eventloop (); /* sanely close connection to server */ - vcdisconnect (); + vc_disconnect (); if( !ownquit && ( getintoption( CF_AUTORECONN ) || wantreconnect ) ) status = 1; diff --git a/vchat-commands.c b/vchat-commands.c index 06c9010..dc46b9c 100755 --- a/vchat-commands.c +++ b/vchat-commands.c @@ -25,6 +25,7 @@ /* local includes */ #include "vchat.h" +#include "vchat-connection.h" #include "vchat-help.h" #include "vchat-user.h" @@ -146,7 +147,7 @@ dothink( char *tail, char nice ) /* send users message to server */ snprintf (tmpstr, TMPSTRSIZE, ".%c %s", nice, tail); - networkoutput (tmpstr); + vc_sendmessage (tmpstr); /* show action in channel window */ snprintf (tmpstr, TMPSTRSIZE, nice == 'O' ? getformatstr(FS_TXPUBNTHOUGHT) : getformatstr(FS_TXPUBTHOUGHT), tail); @@ -163,7 +164,7 @@ doaction( char *tail ) if( *tail ) { /* send users message to server */ snprintf (tmpstr, TMPSTRSIZE, ".a %s", tail); - networkoutput (tmpstr); + vc_sendmessage (tmpstr); /* show action in channel window */ snprintf (tmpstr, TMPSTRSIZE, getformatstr(FS_TXPUBACTION), own_nick_get(), tail); @@ -194,7 +195,7 @@ privatemessagetx ( char *tail ) { /* form message and send to server */ snprintf (tmpstr, TMPSTRSIZE, ".m %s %s", tail, mesg); - networkoutput (tmpstr); + vc_sendmessage (tmpstr); /* show message in private window */ snprintf (tmpstr, TMPSTRSIZE, getformatstr(FS_TXPRIVMSG), tail, mesg); @@ -254,7 +255,7 @@ handleline (char *line) /* generic server command, send to server, show to user */ snprintf (tmpstr, TMPSTRSIZE, getformatstr(FS_COMMAND), line); writechan (tmpstr); - networkoutput (line); + vc_sendmessage (line); break; } break; @@ -274,7 +275,7 @@ output_default(char *line ) { snprintf (tmpstr, TMPSTRSIZE, getformatstr(FS_TXPUBMSG), own_nick_get(), line); /* send original line to server */ - networkoutput (line); + vc_sendmessage (line); /* output message to channel window */ writechan (tmpstr); @@ -430,7 +431,7 @@ command_quit(char *tail) { /* send users message to server */ snprintf (tmpstr, TMPSTRSIZE, ".x %s", tail); - networkoutput (tmpstr); + vc_sendmessage (tmpstr); /* show action in channel window */ writechan (tmpstr); diff --git a/vchat-config.h b/vchat-config.h index f7123d7..954a029 100755 --- a/vchat-config.h +++ b/vchat-config.h @@ -36,6 +36,7 @@ static volatile configoption configoptions[] = { {CF_CONFIGFILE, CO_STR, "conffile", "~/.vchat/config", NULL, { NULL } }, {CF_CERTFILE, CO_STR, "certfile", "~/.vchat/cert", NULL, { NULL } }, {CF_KEYFILE, CO_STR, "keyfile", "~/.vchat/key", NULL, { NULL } }, + {CF_CAFILE, CO_STR, "cafile", "~/.vchat/ca", NULL, { NULL } }, {CF_FORMFILE, CO_STR, "formatfile", "~/.vchat/formats", NULL, { NULL } }, {CF_LOGINSCRIPT, CO_STR, "loginscript","~/.vchat/loginscript", NULL, { NULL } }, {CF_FINGERPRINT, CO_STR, "fingerprint","~/.vchat/fingerprint", NULL, { NULL } }, diff --git a/vchat-connection.c b/vchat-connection.c new file mode 100644 index 0000000..e3d79e9 --- /dev/null +++ b/vchat-connection.c @@ -0,0 +1,223 @@ +/* + * vchat-client - alpha version + * vchat-connection.c - handling of server connection and tls library dispatch + * + * Copyright (C) 2022 Dirk Engling + * + * 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 + +/* For tilde_expand */ +#include + +#include "vchat.h" +#include "vchat-connection.h" +#include "vchat-tls.h" + +static int serverfd = -1; +unsigned int want_tcp_keepalive = 0; + +/* Generic tcp connector, blocking */ +static int connect_tcp_socket( const char *server, const char *port ) { + struct addrinfo hints, *res, *res0; + int s, error; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = PF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + error = getaddrinfo( server, port, &hints, &res0 ); + if (error) return -1; + s = -1; + for (res = res0; res; res = res->ai_next) { + s = socket(res->ai_family, res->ai_socktype, res->ai_protocol); + if (s < 0) continue; + if (connect(s, res->ai_addr, res->ai_addrlen) < 0) { + close(s); + s = -1; + continue; + } + break; /* okay we got one */ + } + freeaddrinfo(res0); + + if (want_tcp_keepalive) { + int one=1; + setsockopt(s,SOL_SOCKET,SO_KEEPALIVE,&one,sizeof(one)); + } + return s; +} + +/* Return a tilde expanded path in a malloced buffer or NULL */ +static char *get_tilde_expanded(confopt opt) { + char *str = getstroption (opt); + if (!str) + return str; + if (str[0] == '~') + return tilde_expand (str); + return strdup(str); +} + +/* connects to server */ +int +vc_connect (const char *server, const char *port) +{ + /* vchat connection x509 store */ + vc_x509store_t *vc_store; + + /* pointer to tilde-expanded certificate/keyfile-names */ + char *certfile = NULL; + + /* Connect to the server */ + serverfd = connect_tcp_socket( server, port ); + if( serverfd < 0 ) { + /* inform user */ + snprintf (tmpstr, TMPSTRSIZE, getformatstr(FS_CANTCONNECT), server, port ); + writechan (tmpstr); + return -1; + } + + if (!getintoption(CF_USESSL)) + return 0; + + /* If SSL is requested, get our ssl-BIO running */ + vc_store = vc_init_x509store(); + if( !vc_store ) { + snprintf (tmpstr, TMPSTRSIZE, getformatstr(FS_ERR), "Out of memory" ); + writechan (tmpstr); + return -1; + } + + /* get name of certificate file */ + certfile = get_tilde_expanded (CF_CERTFILE); + /* do we have a certificate file? */ + if (certfile) { + /* get name of key file */ + char *keyfile = get_tilde_expanded (CF_KEYFILE); + + vc_x509store_setflags(vc_store, VC_X509S_USE_CERTIFICATE); + vc_x509store_setcertfile(vc_store, certfile); + + vc_x509store_set_pkeycb(vc_store, (vc_askpass_cb_t)passprompt); + /* if we don't have a key file, the key may be in the cert file */ + vc_x509store_setkeyfile(vc_store, keyfile ? keyfile : certfile); + + free(keyfile); + free(certfile); + } + + if (getintoption(CF_VERIFYSSL)) { + /* get name of key file */ + char *cafile = get_tilde_expanded (CF_CAFILE); + if (cafile) { + vc_x509store_setflags(vc_store, VC_X509S_NODEF_CAFILE); + vc_x509store_setcafile(vc_store, cafile); + } + vc_x509store_setflags(vc_store, VC_X509S_SSL_VERIFY_PEER); + free(cafile); + } + + /* upgrade our plain BIO to ssl */ + int result = vc_tls_connect( serverfd, vc_store ); + vc_cleanup_x509store(vc_store); + + if (result) { + close(serverfd); + serverfd = -1; + errno = EIO; + snprintf (tmpstr, TMPSTRSIZE, getformatstr(FS_CANTCONNECT), server, port ); + writechan (tmpstr); + return -1; + } + + /* inform user */ + snprintf (tmpstr, TMPSTRSIZE, getformatstr(FS_CONNECTED), server, port); + writechan (tmpstr); + +#ifdef DEBUG + dumpfile = fopen( "dumpfile", "a"); +#endif + + /* if we didn't fail until now, we've got a connection. */ + return 0; +} + +/* Poll for activity on the socket or stdin */ +int vc_poll (int timeout_seconds) { + fd_set readfds; + FD_ZERO (&readfds); + FD_SET (0, &readfds); + if (serverfd != -1) + FD_SET (serverfd, &readfds); + struct timeval tv = { timeout_seconds, 0}; + int result = select (serverfd + 2, &readfds, NULL, NULL, &tv); + if (result <= 0) + return result; + result = FD_ISSET (0, &readfds) ? 1 : 0; + if (serverfd != -1) + result += FD_ISSET (serverfd, &readfds) ? 2 : 0; + return result; +} + +/* disconnect from server */ +void +vc_disconnect () { + if (serverfd > 0) { + close(serverfd); + serverfd = -1; + } + vc_tls_cleanup(); + loggedin = 0; +} + +void +vc_sendmessage (const char *msg) +{ +#ifdef DEBUG + /* debugging? log network output! */ + fprintf (dumpfile, ">| %s (%zd)\n", msg, strlen(msg)); +#endif + + if (getintoption(CF_USESSL)) { + /* send data to server */ + if (vc_tls_sendmessage (msg, strlen (msg)) != strlen (msg)) + writecf (FS_ERR,"Message sending fuzzy."); + + /* send line termination to server */ + if (vc_tls_sendmessage ("\r\n", 2) != 2) + writecf (FS_ERR,"Message sending fuzzy."); + } else { + /* send data to server */ + if (write (serverfd, msg, strlen (msg)) != strlen (msg)) + writecf (FS_ERR,"Message sending fuzzy."); + + /* send line termination to server */ + if (write (serverfd, "\r\n", 2) != 2) + writecf (FS_ERR,"Message sending fuzzy."); + } +} + +ssize_t +vc_receivemessage (char *buffer, size_t size) { + if (getintoption(CF_USESSL)) + return vc_tls_receivemessage (buffer, size); + else + return read(serverfd, buffer, size); +} diff --git a/vchat-connection.h b/vchat-connection.h new file mode 100644 index 0000000..140e467 --- /dev/null +++ b/vchat-connection.h @@ -0,0 +1,9 @@ +#pragma once + +#include + +int vc_connect(const char *host, const char *port); +void vc_sendmessage(const char *message); +ssize_t vc_receivemessage(char *buffer, size_t size); +int vc_poll(); +void vc_disconnect(); diff --git a/vchat-protocol.c b/vchat-protocol.c index 0073956..b7d654e 100755 --- a/vchat-protocol.c +++ b/vchat-protocol.c @@ -1,6 +1,6 @@ /* * vchat-client - alpha version - * vchat-protocol.c - handling of server connection & messages + * vchat-protocol.c - handling of server messages * * Copyright (C) 2001 Andreas Kotes * @@ -15,38 +15,26 @@ */ /* general includes */ -#include -#include +#include #include -#include #include -#include -#include -#include +#include #include #include #include -// TO BE GONE -#include - +#ifdef DEBUG FILE * dumpfile; +#endif /* local includes */ #include "vchat.h" #include "vchat-user.h" -#include "vchat-ssl.h" +#include "vchat-connection.h" /* version of this module */ const char *vchat_io_version = "vchat-protocol.c $Id$"; -/* externally used variables */ -int serverfd = -1; - -/* locally global variables */ -/* our connection BIO */ -static BIO *server_conn = NULL; - /* declaration of local helper functions */ static void usersignon (char *); static void usersignoff (char *); @@ -74,137 +62,6 @@ static void pmnotsent (char *message); extern int status; char *encoding; -static int connect_socket( char *server, char *port ) { - struct addrinfo hints, *res, *res0; - int s, error; - - memset(&hints, 0, sizeof(hints)); - hints.ai_family = PF_UNSPEC; - hints.ai_socktype = SOCK_STREAM; - error = getaddrinfo( server, port, &hints, &res0 ); - if (error) return -1; - s = -1; - for (res = res0; res; res = res->ai_next) { - s = socket(res->ai_family, res->ai_socktype, res->ai_protocol); - if (s < 0) continue; - if (connect(s, res->ai_addr, res->ai_addrlen) < 0) { - close(s); - s = -1; - continue; - } - break; /* okay we got one */ - } - freeaddrinfo(res0); - - if (want_tcp_keepalive) { /* global from vchat-client.c */ - int one=1; - setsockopt(s,SOL_SOCKET,SO_KEEPALIVE,&one,sizeof(one)); - } - return s; -} - -/* connects to server */ -int -vcconnect (char *server, char *port) -{ - /* used for tilde expansion of cert & key filenames */ - char *tildex = NULL; - - /* vchat connection x509 store */ - vc_x509store_t *vc_store; - - /* pointer to tilde-expanded certificate/keyfile-names */ - char *certfile = NULL, *keyfile = NULL; - - /* Connect to the server */ - serverfd = connect_socket( server, port ); - if( serverfd < 0 ) { - /* inform user */ - snprintf (tmpstr, TMPSTRSIZE, getformatstr(FS_CANTCONNECT), server, port ); - writechan (tmpstr); - return -1; - } - /* Abstract server IO in openssls BIO */ - server_conn = BIO_new_socket( serverfd, 1 ); - - /* If SSL is requested, get our ssl-BIO running */ - if( server_conn && getintoption(CF_USESSL) ) { - vc_store = vc_init_x509store(); - if( !vc_store ) { - snprintf (tmpstr, TMPSTRSIZE, getformatstr(FS_ERR), "Out of memory" ); - writechan (tmpstr); - return -1; - } - - 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 - tildex = certfile; - - 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; - - /* 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); - } - - /* upgrade our plain BIO to ssl */ - if( vc_connect_ssl( &server_conn, vc_store ) ) { - BIO_free_all( server_conn ); - server_conn = NULL; - errno = EIO; - } - } - - if( !server_conn ) { - /* inform user */ - snprintf (tmpstr, TMPSTRSIZE, getformatstr(FS_CANTCONNECT), server, port ); - writechan (tmpstr); - return -1; - } - - /* inform user */ - snprintf (tmpstr, TMPSTRSIZE, getformatstr(FS_CONNECTED), server, port); - writechan (tmpstr); - - dumpfile = fopen( "dumpfile", "a"); - - /* if we didn't fail until now, we've got a connection. */ - return 0; -} - -/* disconnect from server */ -void -vcdisconnect () { - BIO_free_all( server_conn ); - server_conn = 0; - if (serverfd>0) { - close(serverfd); - serverfd = -1; - } - loggedin = 0; -} - /* handle a pm not sent error * format: 412 %s */ static void @@ -264,7 +121,7 @@ serverlogin (char *message) { int utf8=!strcmp(nl_langinfo(CODESET), "UTF-8"); if (utf8) - networkoutput(".e utf8"); + vc_sendmessage(".e utf8"); } /* parse and handle an idle message @@ -401,9 +258,9 @@ justloggedin (char *message) void ownjoin (int channel) { - networkoutput(".t"); + vc_sendmessage(".t"); snprintf(tmpstr, TMPSTRSIZE, ".S %d",channel); - networkoutput(tmpstr); + vc_sendmessage(tmpstr); } /* this user changes his nick */ @@ -443,7 +300,7 @@ nickerr (char *message) /* form login message and send it to server */ snprintf (tmpstr, TMPSTRSIZE, ".l %s %s %d", own_nick_get(), getstroption (CF_FROM), getintoption (CF_CHANNEL)); - networkoutput (tmpstr); + vc_sendmessage (tmpstr); } } @@ -476,7 +333,7 @@ login (char *message) { /* form login message and send it to server */ snprintf (tmpstr, TMPSTRSIZE, ".l %s %s %d", own_nick_get(), getstroption (CF_FROM), getintoption (CF_CHANNEL)); - networkoutput (tmpstr); + vc_sendmessage (tmpstr); } /* parse and handle anon login request @@ -496,7 +353,7 @@ anonlogin (char *message) /* form login message and send it to server */ snprintf (tmpstr, TMPSTRSIZE, ".l %s %s %d", own_nick_get(), getstroption (CF_FROM), getintoption (CF_CHANNEL)); - networkoutput (tmpstr); + vc_sendmessage (tmpstr); } /* parse and handle list of nicks (from '.S') @@ -849,7 +706,7 @@ networkinput (void) buf[BUFSIZE-1] = '\0'; /* sanity stop */ /* receive data at offset */ - bytes = BIO_read (server_conn, &buf[bufoff], BUFSIZE-1 - bufoff); + bytes = vc_receivemessage(&buf[bufoff], BUFSIZE-1 - bufoff); /* no bytes transferred? raise error message, bail out */ if (bytes < 0) @@ -908,20 +765,3 @@ networkinput (void) bufoff = 0; } } - -void -networkoutput (char *msg) -{ -#ifdef DEBUG - /* debugging? log network output! */ - fprintf (dumpfile, ">| %s (%zd)\n", msg, strlen(msg)); -#endif - - /* send data to server */ - if (BIO_write (server_conn, msg, strlen (msg)) != strlen (msg)) - writecf (FS_ERR,"Message sending fuzzy."); - - /* send line termination to server */ - if (BIO_write (server_conn, "\r\n", 2) != 2) - writecf (FS_ERR,"Message sending fuzzy."); -} 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 @@ -/* - * 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 - -#include - -#include "vchat.h" -#include "vchat-ssl.h" - -const char *vchat_ssl_version = "vchat-ssl.c $Id$"; - -typedef int (*vc_x509verify_cb_t)(int, X509_STORE_CTX *); -struct vc_x509store_t { - 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; -}; - -static void vc_cleanup_x509store(vc_x509store_t *); // Should not be static but is unused -static SSL_CTX * vc_create_sslctx( vc_x509store_t *vc_store ); -static int vc_verify_callback(int, X509_STORE_CTX *); -static X509_STORE * vc_x509store_create(vc_x509store_t *); -static void vc_x509store_clearflags(vc_x509store_t *, int); -static void vc_x509store_setcafile(vc_x509store_t *, char *); -static void vc_x509store_setcapath(vc_x509store_t *, char *); -static void vc_x509store_setcrlfile(vc_x509store_t *, char *); -static void vc_x509store_addcert(vc_x509store_t *, X509 *); -static void vc_x509store_setcb(vc_x509store_t *, vc_x509verify_cb_t); - -#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) - -static 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; - - /* Explicitly use TLSv1 (or maybe later) */ - if( !(ctx = SSL_CTX_new(SSLv23_client_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; - /* Disable some insecure protocols explicitly */ - SSL_CTX_set_options(ctx, SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3); - if (getstroption(CF_CIPHERSUITE)) - SSL_CTX_set_cipher_list(ctx, getstroption(CF_CIPHERSUITE)); - else - SSL_CTX_set_cipher_list(ctx, "ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-SHA"); - - SSL_CTX_set_verify_depth (ctx, getintoption(CF_VERIFYSSL)); - - 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 || !SSL_CTX_check_private_key(ctx)) - VC_SETCERT_ERR_EXIT(store, ctx, "Load private key failed"); - } - - SSL_CTX_set_app_data(ctx, vc_store); - return(ctx); -} - -int vc_connect_ssl( BIO **conn, vc_x509store_t *vc_store ) -{ - SSL_CTX * ctx = vc_create_sslctx(vc_store); - X509 *peercert = NULL; - BIO *ssl_conn = NULL; - const SSL *sslp = NULL; - const SSL_CIPHER * cipher = NULL; - - /* To display and check server fingerprint */ - char fingerprint[EVP_MAX_MD_SIZE*4]; - unsigned char fingerprint_bin[EVP_MAX_MD_SIZE]; - unsigned int fingerprint_len; - - FILE *fingerprint_file = NULL; - char * fp = fingerprint; - - long result, j; - - if( !ctx ) - return 1; - - ssl_conn = BIO_new_ssl(ctx, 1); - SSL_CTX_free(ctx); - - if( !ssl_conn ) - goto ssl_error; - - BIO_push( ssl_conn, *conn ); - *conn = ssl_conn; - fflush(stdout); - - if( BIO_do_handshake( *conn ) <= 0 ) - goto ssl_error; - - /* Show information about cipher used */ - /* Get cipher object */ - BIO_get_ssl(ssl_conn, &sslp); - if (sslp) - cipher = SSL_get_current_cipher(sslp); - if (cipher) { - char cipher_desc[TMPSTRSIZE]; - snprintf(tmpstr, TMPSTRSIZE, "[SSL CIPHER ] %s", SSL_CIPHER_description(cipher, cipher_desc, TMPSTRSIZE)); - writecf(FS_SERV, tmpstr); - } else { - snprintf(tmpstr, TMPSTRSIZE, "[SSL ERROR ] Cipher not known / SSL object can't be queried!"); - writecf(FS_ERR, tmpstr); - } - - /* Accept being connected, _if_ verification passed */ - if (!sslp) - goto ssl_error; - - peercert = SSL_get_peer_certificate(sslp); - if (!peercert) - goto ssl_error; - - /* show basic information about peer cert */ - snprintf(tmpstr, TMPSTRSIZE, "[SSL SUBJECT ] %s", X509_NAME_oneline(X509_get_subject_name(peercert),0,0)); - writecf(FS_SERV, tmpstr); - snprintf(tmpstr, TMPSTRSIZE, "[SSL ISSUER ] %s", X509_NAME_oneline(X509_get_issuer_name(peercert),0,0)); - writecf(FS_SERV, tmpstr); - - /* calculate fingerprint */ - if (!X509_digest(peercert,EVP_sha1(),fingerprint_bin,&fingerprint_len)) - goto ssl_error; - - assert ( ( fingerprint_len > 1 ) && (fingerprint_len <= EVP_MAX_MD_SIZE )); - for (j=0; j<(int)fingerprint_len; j++) - fp += sprintf(fp, "%02X:", fingerprint_bin[j]); - assert ( fp > fingerprint ); - fp[-1] = 0; - snprintf(tmpstr, TMPSTRSIZE, "[SSL FINGERPRINT ] %s (from server)", fingerprint); - writecf(FS_SERV, tmpstr); - - /* we don't need the peercert anymore */ - X509_free(peercert); - - /* verify fingerprint */ - if (getintoption(CF_PINFINGER)) { - - fingerprint_file = fopen(tilde_expand(getstroption(CF_FINGERPRINT)), "r"); - if (fingerprint_file) { - - /* Read fingerprint from file */ - char old_fingerprint[EVP_MAX_MD_SIZE*4]; - char * r = fgets(old_fingerprint, sizeof(old_fingerprint), fingerprint_file); - fclose(fingerprint_file); - - if (r) { - /* chomp */ - char *nl = strchr(r, '\n'); - if (nl) *nl = 0; - - /* verify fingerprint matches stored version */ - if (!strcmp(fingerprint, old_fingerprint)) - return 0; - } - - snprintf(tmpstr, TMPSTRSIZE, "[SSL FINGERPRINT ] %s (from %s)", r ? old_fingerprint : "", getstroption(CF_FINGERPRINT)); - writecf(FS_ERR, tmpstr); - writecf(FS_ERR, "[SSL CONNECT ERROR] Fingerprint mismatch! Server cert updated?"); - return 1; - } - - fingerprint_file = fopen(tilde_expand(getstroption(CF_FINGERPRINT)), "w"); - if (!fingerprint_file) { - snprintf (tmpstr, TMPSTRSIZE, "[WARNING] Can't write fingerprint file, %s.", strerror(errno)); - writecf(FS_ERR, tmpstr); - } else { - fputs(fingerprint, fingerprint_file); - fclose(fingerprint_file); - writecf(FS_SERV, "Stored fingerprint."); - } - return 0; - } - - /* If verify of x509 chain was requested, do the check here */ - result = SSL_get_verify_result(sslp); - - if (result == X509_V_OK) - return 0; - - if (getintoption(CF_IGNSSL)) { - writecf(FS_ERR, "[SSL VERIFY ERROR ] FAILURE IGNORED!!!"); - return 0; - } - -ssl_error: - snprintf(tmpstr, TMPSTRSIZE, "[SSL CONNECT ERROR] %s", ERR_error_string (ERR_get_error (), NULL)); - writecf(FS_ERR, tmpstr); - - return 1; -} - -#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) { - snprintf(tmpstr, TMPSTRSIZE, "[SSL VERIFY ERROR ] %s", - X509_verify_cert_error_string(X509_STORE_CTX_get_error(store))); - writecf(FS_ERR, tmpstr); - } - return (ok | getintoption(CF_IGNSSL)); -} - -void vc_x509store_setflags(vc_x509store_t *store, int flags) -{ - store->flags |= flags; -} - -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) -{ - free(store->cafile); - store->cafile = ( file ? strdup(file) : 0 ); -} - -void vc_x509store_setcapath(vc_x509store_t *store, char *path) -{ - free(store->capath); - store->capath = ( path ? strdup(path) : 0 ); -} - -void vc_x509store_setcrlfile(vc_x509store_t *store, char *file) -{ - free(store->crlfile); - store->crlfile = ( file ? strdup(file) : 0 ); -} - -void vc_x509store_setkeyfile(vc_x509store_t *store, char *file) -{ - free(store->use_keyfile); - store->use_keyfile = ( file ? strdup(file) : 0 ); -} - -void vc_x509store_setcertfile(vc_x509store_t *store, char *file) -{ - free(store->use_certfile); - store->use_certfile = ( file ? strdup(file) : 0 ); -} - -#if 0 -int vc_tls_read() -{ - -} - -int vc_tls_read() -{ - -} -#endif - -vc_x509store_t *vc_init_x509store() -{ - vc_x509store_t *s = malloc(sizeof(vc_x509store_t)); - if (s) { - - static int sslinit; - if( !sslinit++ ) { - SSL_library_init (); - SSL_load_error_strings(); - } - - 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; - } - return s; -} - -void vc_cleanup_x509store(vc_x509store_t *s) -{ - free(s->cafile); - free(s->capath); - free(s->crlfile); - free(s->use_certfile); - free(s->use_keyfile); - free(s->use_key); - sk_X509_free(s->certs); - sk_X509_CRL_free(s->crls); - sk_X509_free(s->use_certs); -} - -const char *vchat_ssl_version_external = "OpenSSL implementation; version unknown"; -void vchat_ssl_get_version_external() -{ - char tmpstr[TMPSTRSIZE]; - snprintf(tmpstr, TMPSTRSIZE, "%s with %s", SSLeay_version(SSLEAY_VERSION), SSLeay_version(SSLEAY_CFLAGS)); - vchat_ssl_version_external = strdup(tmpstr); -} diff --git a/vchat-ssl.h b/vchat-ssl.h deleted file mode 100755 index 8dc1bfc..0000000 --- a/vchat-ssl.h +++ /dev/null @@ -1,23 +0,0 @@ - -/* prototypes */ - -struct vc_x509store_t; -typedef struct vc_x509store_t vc_x509store_t; -typedef int (*vc_askpass_cb_t)(char *, int, int, void *); - -vc_x509store_t *vc_init_x509store(); -void vc_x509store_set_pkeycb(vc_x509store_t *, vc_askpass_cb_t); -void vc_x509store_setflags(vc_x509store_t *, int); -void vc_x509store_setkeyfile(vc_x509store_t *, char *); -void vc_x509store_setcertfile(vc_x509store_t *, char *); -int vc_connect_ssl(BIO **conn, 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-tls.c b/vchat-tls.c new file mode 100755 index 0000000..a6e5e2d --- /dev/null +++ b/vchat-tls.c @@ -0,0 +1,481 @@ +/* + * vchat-client - alpha version + * vchat-tls.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 + +#include + +#include "vchat.h" +#include "vchat-tls.h" + +const char *vchat_ssl_version = "vchat-tlsl.c $Id$"; + +/* connection BIO for openssl */ +static BIO *server_conn = NULL; + +typedef int (*vc_x509verify_cb_t)(int, X509_STORE_CTX *); +struct vc_x509store_t { + 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; +}; + +static SSL_CTX * vc_create_sslctx( vc_x509store_t *vc_store ); +static int vc_verify_callback(int, X509_STORE_CTX *); +static X509_STORE * vc_x509store_create(vc_x509store_t *); +static void vc_x509store_clearflags(vc_x509store_t *, int); +static void vc_x509store_setcapath(vc_x509store_t *, char *); +static void vc_x509store_setcrlfile(vc_x509store_t *, char *); +static void vc_x509store_addcert(vc_x509store_t *, X509 *); +static void vc_x509store_setcb(vc_x509store_t *, vc_x509verify_cb_t); + +#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) + +static 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; + + /* Explicitly use TLSv1 (or maybe later) */ + if( !(ctx = SSL_CTX_new(TLS_client_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; + /* Disable some insecure protocols explicitly */ + SSL_CTX_set_options(ctx, SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3); + if (getstroption(CF_CIPHERSUITE)) + SSL_CTX_set_cipher_list(ctx, getstroption(CF_CIPHERSUITE)); + else + SSL_CTX_set_cipher_list(ctx, "ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-SHA"); + + SSL_CTX_set_verify_depth (ctx, getintoption(CF_VERIFYSSL)); + + 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 || !SSL_CTX_check_private_key(ctx)) + VC_SETCERT_ERR_EXIT(store, ctx, "Load private key failed"); + } + + SSL_CTX_set_app_data(ctx, vc_store); + return(ctx); +} + +int vc_tls_connect( int serverfd, vc_x509store_t *vc_store ) +{ + SSL_CTX * ctx = vc_create_sslctx(vc_store); + X509 *peercert = NULL; + BIO *ssl_conn = NULL; + const SSL *sslp = NULL; + const SSL_CIPHER * cipher = NULL; + + server_conn = BIO_new_socket( serverfd, 1 ); + + /* To display and check server fingerprint */ + char fingerprint[EVP_MAX_MD_SIZE*4]; + unsigned char fingerprint_bin[EVP_MAX_MD_SIZE]; + unsigned int fingerprint_len; + + FILE *fingerprint_file = NULL; + char * fp = fingerprint; + + long result, j; + + if( !ctx ) + goto all_errors; + + ssl_conn = BIO_new_ssl(ctx, 1); + SSL_CTX_free(ctx); + + if( !ssl_conn ) + goto ssl_error; + + BIO_push( ssl_conn, server_conn ); + server_conn = ssl_conn; + fflush(stdout); + + if( BIO_do_handshake( server_conn ) <= 0 ) + goto ssl_error; + + /* Show information about cipher used */ + /* Get cipher object */ + BIO_get_ssl(ssl_conn, &sslp); + if (!sslp) + goto ssl_error; + + cipher = SSL_get_current_cipher(sslp); + if (cipher) { + char cipher_desc[TMPSTRSIZE]; + snprintf(tmpstr, TMPSTRSIZE, "[SSL CIPHER ] %s", SSL_CIPHER_description(cipher, cipher_desc, TMPSTRSIZE)); + writecf(FS_SERV, tmpstr); + } else { + snprintf(tmpstr, TMPSTRSIZE, "[SSL ERROR ] Cipher not known / SSL object can't be queried!"); + writecf(FS_ERR, tmpstr); + } + + /* Accept being connected, _if_ verification passed */ + peercert = SSL_get_peer_certificate(sslp); + if (!peercert) + goto ssl_error; + + /* show basic information about peer cert */ + snprintf(tmpstr, TMPSTRSIZE, "[SSL SUBJECT ] %s", X509_NAME_oneline(X509_get_subject_name(peercert),0,0)); + writecf(FS_SERV, tmpstr); + snprintf(tmpstr, TMPSTRSIZE, "[SSL ISSUER ] %s", X509_NAME_oneline(X509_get_issuer_name(peercert),0,0)); + writecf(FS_SERV, tmpstr); + + /* calculate fingerprint */ + if (!X509_digest(peercert,EVP_sha1(),fingerprint_bin,&fingerprint_len)) + goto ssl_error; + + assert ( ( fingerprint_len > 1 ) && (fingerprint_len <= EVP_MAX_MD_SIZE )); + for (j=0; j<(int)fingerprint_len; j++) + fp += sprintf(fp, "%02X:", fingerprint_bin[j]); + assert ( fp > fingerprint ); + fp[-1] = 0; + snprintf(tmpstr, TMPSTRSIZE, "[SSL FINGERPRINT ] %s (from server)", fingerprint); + writecf(FS_SERV, tmpstr); + + /* we don't need the peercert anymore */ + X509_free(peercert); + + /* verify fingerprint */ + if (getintoption(CF_PINFINGER)) { + + fingerprint_file = fopen(tilde_expand(getstroption(CF_FINGERPRINT)), "r"); + if (fingerprint_file) { + + /* Read fingerprint from file */ + char old_fingerprint[EVP_MAX_MD_SIZE*4]; + char * r = fgets(old_fingerprint, sizeof(old_fingerprint), fingerprint_file); + fclose(fingerprint_file); + + if (r) { + /* chomp */ + char *nl = strchr(r, '\n'); + if (nl) *nl = 0; + + /* verify fingerprint matches stored version */ + if (!strcmp(fingerprint, old_fingerprint)) + return 0; + } + + snprintf(tmpstr, TMPSTRSIZE, "[SSL FINGERPRINT ] %s (from %s)", r ? old_fingerprint : "", getstroption(CF_FINGERPRINT)); + writecf(FS_ERR, tmpstr); + writecf(FS_ERR, "[SSL CONNECT ERROR] Fingerprint mismatch! Server cert updated?"); + return 1; + } + + fingerprint_file = fopen(tilde_expand(getstroption(CF_FINGERPRINT)), "w"); + if (!fingerprint_file) { + snprintf (tmpstr, TMPSTRSIZE, "[WARNING] Can't write fingerprint file, %s.", strerror(errno)); + writecf(FS_ERR, tmpstr); + } else { + fputs(fingerprint, fingerprint_file); + fclose(fingerprint_file); + writecf(FS_SERV, "Stored fingerprint."); + } + return 0; + } + + /* If verify of x509 chain was requested, do the check here */ + result = SSL_get_verify_result(sslp); + + if (result == X509_V_OK) + return 0; + + if (getintoption(CF_IGNSSL)) { + writecf(FS_ERR, "[SSL VERIFY ERROR ] FAILURE IGNORED!!!"); + return 0; + } + +ssl_error: + snprintf(tmpstr, TMPSTRSIZE, "[SSL CONNECT ERROR] %s", ERR_error_string (ERR_get_error (), NULL)); + writecf(FS_ERR, tmpstr); +all_errors: + BIO_free_all( server_conn ); + server_conn = NULL; + return 1; +} + +#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) { + snprintf(tmpstr, TMPSTRSIZE, "[SSL VERIFY ERROR ] %s", + X509_verify_cert_error_string(X509_STORE_CTX_get_error(store))); + writecf(FS_ERR, tmpstr); + } + return (ok | getintoption(CF_IGNSSL)); +} + +void vc_x509store_setflags(vc_x509store_t *store, int flags) +{ + store->flags |= flags; +} + +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) +{ + free(store->cafile); + store->cafile = ( file ? strdup(file) : 0 ); +} + +void vc_x509store_setcapath(vc_x509store_t *store, char *path) +{ + free(store->capath); + store->capath = ( path ? strdup(path) : 0 ); +} + +void vc_x509store_setcrlfile(vc_x509store_t *store, char *file) +{ + free(store->crlfile); + store->crlfile = ( file ? strdup(file) : 0 ); +} + +void vc_x509store_setkeyfile(vc_x509store_t *store, char *file) +{ + free(store->use_keyfile); + store->use_keyfile = ( file ? strdup(file) : 0 ); +} + +void vc_x509store_setcertfile(vc_x509store_t *store, char *file) +{ + free(store->use_certfile); + store->use_certfile = ( file ? strdup(file) : 0 ); +} + +vc_x509store_t *vc_init_x509store() +{ + vc_x509store_t *s = malloc(sizeof(vc_x509store_t)); + if (s) { + + static int sslinit; + if( !sslinit++ ) { + SSL_library_init (); + SSL_load_error_strings(); + } + + 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; + } + return s; +} + +void vc_cleanup_x509store(vc_x509store_t *s) +{ + free(s->cafile); + free(s->capath); + free(s->crlfile); + free(s->use_certfile); + free(s->use_keyfile); + free(s->use_key); + sk_X509_free(s->certs); + sk_X509_CRL_free(s->crls); + sk_X509_free(s->use_certs); + free(s); +} + +ssize_t vc_tls_sendmessage(const void *buf, size_t size) { + return BIO_write(server_conn, buf, size); +} + +ssize_t vc_tls_receivemessage(void *buf, size_t size) { + return BIO_read (server_conn, buf, size); +} + +void vc_tls_cleanup() { + BIO_free_all( server_conn ); + server_conn = NULL; +} + +const char *vchat_ssl_version_external = "OpenSSL implementation; version unknown"; +void vchat_ssl_get_version_external() +{ + char tmpstr[TMPSTRSIZE]; + snprintf(tmpstr, TMPSTRSIZE, "%s with %s", SSLeay_version(SSLEAY_VERSION), SSLeay_version(SSLEAY_CFLAGS)); + vchat_ssl_version_external = strdup(tmpstr); +} diff --git a/vchat-tls.h b/vchat-tls.h new file mode 100755 index 0000000..8d33ebd --- /dev/null +++ b/vchat-tls.h @@ -0,0 +1,30 @@ +#pragma once + +/* prototypes */ + +struct vc_x509store_t; +typedef struct vc_x509store_t vc_x509store_t; +typedef int (*vc_askpass_cb_t)(char *, int, int, void *); + +vc_x509store_t *vc_init_x509store(); +void vc_x509store_set_pkeycb(vc_x509store_t *, vc_askpass_cb_t); +void vc_x509store_setflags(vc_x509store_t *, int); +void vc_x509store_setkeyfile(vc_x509store_t *, char *); +void vc_x509store_setcertfile(vc_x509store_t *, char *); +void vc_x509store_setcafile(vc_x509store_t *, char *); +void vc_cleanup_x509store(vc_x509store_t *s); + +int vc_tls_connect(int serverfd, vc_x509store_t * ); +ssize_t vc_tls_sendmessage(const void *buf, size_t size); +ssize_t vc_tls_receivemessage(void *buf, size_t size); +void vc_tls_cleanup(); + +#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 82a6840..745ada8 100755 --- a/vchat-ui.c +++ b/vchat-ui.c @@ -492,7 +492,7 @@ writescr ( WINDOW *win, struct sb_entry *entry ) { /* store original attributes */ WATTR_GET( win, orgattr); attrbuffer[ 0 ] = orgattr; - + /* copy time string */ for( i = 0; i < timelen; i++ ) if( tmp[ i ] == 1 ) { @@ -871,13 +871,13 @@ gettextwidth (const char *textbuffer) do switch( *(textbuffer++) ) { case 1: if (!*(textbuffer++)) return width; - break; + break; case 0: return width; break; - default: + default: width++; - break; + break; } while( 1 ); } @@ -920,20 +920,20 @@ getsbeheight (struct sb_entry *entry, const int xwidth, int needstime ) break; case 1: if (!*textbuffer++) return lines; - break; + break; case 0: return lines; break; case 2: textbuffer=entry->what; break; - default: + default: if( curx++ == xwidth ) { curx = 0; lines++; } - break; + break; } while( 1 ); - + } /* Check, which kind of filter we have to apply: diff --git a/vchat.h b/vchat.h index 4e33190..31615ec 100755 --- a/vchat.h +++ b/vchat.h @@ -30,7 +30,7 @@ typedef struct servermessage servermessage; /* configuration types and variable numbers */ 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_CIPHERSUITE, CF_CONFIGFILE, CF_CERTFILE, CF_KEYFILE, CF_CAFILE, CF_FORMFILE, CF_LOGINSCRIPT, CF_FINGERPRINT, CF_PINFINGER, CF_USESSL, CF_IGNSSL, CF_VERIFYSSL, CF_USECERT, CF_PRIVHEIGHT, CF_PRIVCOLLAPS, CF_HSCROLL, CF_CHANNEL, CF_USETIME, CF_USETOPIC, CF_SCROLLBPRIV, CF_SCROLLBACK, CF_SCROLLBPRIVT, CF_SCROLLBACKT, CF_ENCODING, @@ -86,7 +86,7 @@ void loadcfg (char *file,int complain,void (*lineparser) (char *)); void loadformats (char *file); void cleanup(int signal); -/* configuration helper funktions from vchat-client.c */ +/* configuration helper functions from vchat-client.c */ char *getformatstr (formtstr id); char *getstroption (confopt option); void setstroption (confopt option, char *string); @@ -143,10 +143,6 @@ void handlequery ( char *line ); /* vchat-protocol.c */ extern const char *vchat_io_version; -/* connect/disconnect */ -int vcconnect (char *server, char *port); -void vcdisconnect (); - /* network I/O */ void networkinput (void); void networkoutput (char *); @@ -173,7 +169,7 @@ typedef struct { char *help; } commandentry; -/* vchat-ssl.c */ +/* vchat-tls.c */ extern const char *vchat_ssl_version; extern const char *vchat_ssl_version_external; void vchat_ssl_get_version_external(); -- cgit v1.2.3