From: Michael Hampton Date: Sun, 6 Jan 2002 10:33:11 +0000 (+0000) Subject: * SSL/TLS support for the Citadel/UX wire protocol X-Git-Tag: v7.86~6628 X-Git-Url: https://code.citadel.org/?a=commitdiff_plain;h=02a65d30355c16dc32afd48ba637e83ef825212a;p=citadel.git * SSL/TLS support for the Citadel/UX wire protocol --- diff --git a/citadel/ChangeLog b/citadel/ChangeLog index 2882b925d..ade92145c 100644 --- a/citadel/ChangeLog +++ b/citadel/ChangeLog @@ -1,4 +1,7 @@ $Log$ + Revision 590.42 2002/01/06 10:33:10 error + * SSL/TLS support for the Citadel/UX wire protocol + Revision 590.41 2002/01/06 08:54:58 error * user_ops.c: fixed become_session() when calling EVT_LOGOUT session hooks @@ -3086,4 +3089,3 @@ Sat Jul 11 00:20:48 EDT 1998 Nathan Bryant Fri Jul 10 1998 Art Cancro * Initial CVS import - diff --git a/citadel/Makefile.in b/citadel/Makefile.in index a75f55795..1967e840f 100644 --- a/citadel/Makefile.in +++ b/citadel/Makefile.in @@ -63,6 +63,7 @@ LIBOBJS=@LIBOBJS@ CL_LIBOBJS=@CL_LIBOBJS@ PTHREAD_DEFS=@PTHREAD_DEFS@ PTHREAD_LIBS=@PTHREAD_LIBS@ +SSL_LIBS=@SSL_LIBS@ CLIENT_PTLIBS=@CLIENT_PTLIBS@ INSTALL=@INSTALL@ INSTALL_DATA=@INSTALL_DATA@ @@ -81,12 +82,12 @@ VPATH=$(srcdir) DOMAIN=@DOMAIN@ -SOURCES=aidepost.c citadel.c citmail.c citserver.c client_chat.c commands.c \ - config.c control.c $(DATABASE) dynloader.c file_ops.c \ - housekeeping.c ipc_c_tcp.c locate_host.c \ +SOURCES=aidepost.c citadel.c citmail.c citserver.c client_chat.c \ + client_crypto.c commands.c config.c control.c $(DATABASE) \ + dynloader.c file_ops.c housekeeping.c ipc_c_tcp.c locate_host.c \ logging.c messages.c msgbase.c msgform.c \ netsetup.c policy.c readlog.c migratenet.c \ - room_ops.c rooms.c routines.c routines2.c serv_chat.c \ + room_ops.c rooms.c routines.c routines2.c serv_chat.c serv_crypto.c \ serv_info.c serv_test.c setup.c snprintf.c stats.c serv_vcard.c \ support.c sysdep.c tools.c user_ops.c userlist.c serv_expire.c \ whobbs.c sendcommand.c mime_parser.c base64.c getutline.c \ @@ -114,13 +115,13 @@ serv_modules: $(SERV_MODULES) citadel$(EXEEXT): ipc_c_tcp$(CX) citadel$(CX) rooms$(CX) routines$(CX) \ routines2$(CX) messages$(CX) \ - client_passwords$(CX) md5$(CX) \ + client_passwords$(CX) md5$(CX) client_crypto$(CX) \ commands$(CX) client_chat$(CX) serv_info$(CX) tools$(CX) $(LIBOBJS) $(CC) ipc_c_tcp$(CX) citadel$(CX) rooms$(CX) routines$(CX) \ routines2$(CX) messages$(CX) \ commands$(CX) client_chat$(CX) serv_info$(CX) tools$(CX) \ - client_passwords$(CX) md5$(CX) \ - $(LIBOBJS) $(LDFLAGS) -o citadel $(NETLIBS) $(CLIENT_PTLIBS) + client_passwords$(CX) md5$(CX) client_crypto$(CX) \ + $(LIBOBJS) $(LDFLAGS) -o citadel $(NETLIBS) $(SSL_LIBS) $(CLIENT_PTLIBS) .c.o: $(CC) $(CFLAGS) $(CPPFLAGS) $(DEFS) -c $< @@ -139,11 +140,11 @@ parsedate.ro: parsedate.c LIBSERV_OBJS = user_ops.lo citserver.lo sysdep.lo dynloader.lo tools.lo $(DATABASE:.c=.lo) \ control.lo policy.lo config.lo support.lo room_ops.lo file_ops.lo msgbase.lo \ locate_host.lo housekeeping.lo logging.lo mime_parser.lo html.lo internet_addressing.lo \ - parsedate.lo genstamp.lo clientsocket.lo $(AUTH) $(LIBOBJS:.o=.lo) + serv_crypto.lo parsedate.lo genstamp.lo clientsocket.lo $(AUTH) $(LIBOBJS:.o=.lo) libcitserver.la: $(LIBSERV_OBJS) $(LIBTOOL) $(CC) $(LDFLAGS) -rpath $(prefix) -no-undefined -avoid-version \ - -o libcitserver.la $(LIBSERV_OBJS) $(GDBM) $(LIBS) $(PTHREAD_LIBS) $(NETLIBS) $(RESOLV) + -o libcitserver.la $(LIBSERV_OBJS) $(GDBM) $(LIBS) $(PTHREAD_LIBS) $(NETLIBS) $(SSL_LIBS) $(RESOLV) citserver: $(SERV_OBJS) $(LIBTOOL) libcitserver.la $(LIBTOOL) --mode=link $(CC) $(SERV_OBJS) $(LDFLAGS) libcitserver.la -o \ diff --git a/citadel/client_crypto.c b/citadel/client_crypto.c new file mode 100644 index 000000000..5fdb3b6fb --- /dev/null +++ b/citadel/client_crypto.c @@ -0,0 +1,297 @@ +/* $Id$ */ + +#include "sysdep.h" +#ifdef HAVE_OPENSSL +#include +#include +#include +#endif +#ifdef HAVE_PTHREAD_H +#include +#endif +#include +#include "citadel.h" +#include "client_crypto.h" + +#ifdef HAVE_OPENSSL +SSL *ssl; +SSL_CTX *ssl_ctx; +int ssl_is_connected = 0; +#ifdef THREADED_CLIENT +pthread_mutex_t **Critters; /* Things that need locking */ +#endif /* THREADED_CLIENT */ + +extern int serv_sock; +extern int server_is_local; +#endif /* HAVE_OPENSSL */ + + +#ifdef HAVE_OPENSSL +/* + * input binary data from encrypted connection + */ +void serv_read_ssl(char *buf, int bytes) +{ + int len, rlen; + char junk[1]; + + len = 0; + while (len < bytes) { + if (SSL_want_read(ssl)) { + if ((SSL_write(ssl, junk, 0)) < 1) { + fprintf(stderr, "SSL_write in serv_read:\n"); + ERR_print_errors_fp(stderr); + } + } + rlen = SSL_read(ssl, &buf[len], bytes - len); + if (rlen < 1) { + long errval; + + errval = SSL_get_error(ssl, rlen); + if (errval == SSL_ERROR_WANT_READ || + errval == SSL_ERROR_WANT_WRITE) { + sleep(1); + continue; + } + if (errval == SSL_ERROR_ZERO_RETURN || + errval == SSL_ERROR_SSL) { + endtls(); + serv_read(&buf[len], bytes - len); + return; + } + fprintf(stderr, "SSL_read in serv_read:\n"); + ERR_print_errors_fp(stderr); + connection_died(); + return; + } + len += rlen; + } +} + + +/* + * send binary to server encrypted + */ +void serv_write_ssl(char *buf, int nbytes) +{ + int bytes_written = 0; + int retval; + char junk[1]; + + while (bytes_written < nbytes) { + if (SSL_want_write(ssl)) { + if ((SSL_read(ssl, junk, 0)) < 1) { + fprintf(stderr, "SSL_read in serv_write:\n"); + ERR_print_errors_fp(stderr); + } + } + retval = SSL_write(ssl, &buf[bytes_written], + nbytes - bytes_written); + if (retval < 1) { + long errval; + + errval = SSL_get_error(ssl, retval); + if (errval == SSL_ERROR_WANT_READ || + errval == SSL_ERROR_WANT_WRITE) { + sleep(1); + continue; + } + if (errval == SSL_ERROR_ZERO_RETURN || + errval == SSL_ERROR_SSL) { + endtls(); + serv_write(&buf[bytes_written], + nbytes - bytes_written); + return; + } + fprintf(stderr, "SSL_write in serv_write:\n"); + ERR_print_errors_fp(stderr); + connection_died(); + return; + } + bytes_written += retval; + } +} + + +void ssl_lock(int mode, int n, const char *file, int line) +{ +#ifdef THREADED_CLIENT + if (mode & CRYPTO_LOCK) + pthread_mutex_lock(Critters[n]); + else + pthread_mutex_unlock(Critters[n]); +#endif /* THREADED_CLIENT */ +} +#endif /* HAVE_OPENSSL */ + + +/* + * starttls() starts SSL/TLS if possible + * Returns 1 if the session is encrypted, 0 otherwise + */ +int starttls(void) +{ +#ifdef HAVE_OPENSSL + int a; + char buf[SIZ]; + SSL_METHOD *ssl_method; + DH *dh; + + /* TLS is pointless when server is local */ + if (server_is_local) { + return 0; + } + + /* Get started */ + ssl = NULL; + ssl_ctx = NULL; + dh = NULL; + SSL_load_error_strings(); + SSLeay_add_ssl_algorithms(); + + /* Set up the SSL context in which we will oeprate */ + ssl_method = SSLv23_client_method(); + ssl_ctx = SSL_CTX_new(ssl_method); + if (!ssl_ctx) { + fprintf(stderr, "SSL_CTX_new failed: %s\n", + ERR_reason_error_string(ERR_get_error())); + return 0; + } + /* Any reasonable cipher we can get */ + if (!(SSL_CTX_set_cipher_list(ssl_ctx, CIT_CIPHERS))) { + fprintf(stderr, "No ciphers available for encryption\n"); + SSL_CTX_free(ssl_ctx); + ssl_ctx = NULL; + return 0; + } + SSL_CTX_set_session_cache_mode(ssl_ctx, SSL_SESS_CACHE_BOTH); + + /* Load DH parameters into the context */ + dh = DH_new(); + if (!dh) { + fprintf(stderr, "Can't allocate a DH object: %s\n", + ERR_reason_error_string(ERR_get_error())); + return 0; + } + if (!(BN_hex2bn(&(dh->p), DH_P))) { + fprintf(stderr, "Can't assign DH_P: %s\n", + ERR_reason_error_string(ERR_get_error())); + return 0; + } + if (!(BN_hex2bn(&(dh->g), DH_G))) { + fprintf(stderr, "Can't assign DH_G: %s\n", + ERR_reason_error_string(ERR_get_error())); + return 0; + } + dh->length = DH_L; + SSL_CTX_set_tmp_dh(ssl_ctx, dh); + DH_free(dh); + +#ifdef THREADED_CLIENT + /* OpenSSL requires callbacks for threaded clients */ + CRYPTO_set_locking_callback(ssl_lock); + CRYPTO_set_id_callback(pthread_self); + + /* OpenSSL requires us to do semaphores for threaded clients */ + Critters = malloc(CRYPTO_num_locks() * sizeof (pthread_mutex_t *)); + if (!Critters) { + perror("malloc failed"); + exit(1); + } else { + for (a = 0; a < CRYPTO_num_locks(); a++) { + Critters[a] = malloc(sizeof (pthread_mutex_t)); + if (!Critters[a]) { + perror("malloc failed"); + exit(1); + } + pthread_mutex_init(Critters[a], NULL); + } + } +#endif /* THREADED_CLIENT */ + + /* New SSL object */ + ssl = SSL_new(ssl_ctx); + if (!ssl) { + fprintf(stderr, "SSL_new failed: %s\n", + ERR_reason_error_string(ERR_get_error())); + SSL_CTX_free(ssl_ctx); + ssl_ctx = NULL; + return 0; + } + /* Pointless flag waving */ +#if SSLEAY_VERSION_NUMBER >= 0x0922 + SSL_set_session_id_context(ssl, "Citadel/UX SID", 14); +#endif + if (!RAND_status()) { + fprintf(stderr, "PRNG not properly seeded\n"); + return 0; + } + + /* Associate network connection with SSL object */ + if (SSL_set_fd(ssl, serv_sock) < 1) { + fprintf(stderr, "SSL_set_fd failed: %s\n", + ERR_reason_error_string(ERR_get_error())); + SSL_CTX_free(ssl_ctx); + ssl_ctx = NULL; + SSL_free(ssl); + ssl = NULL; + return 0; + } + + printf("Requesting encryption...\r"); + fflush(stdout); + + /* Ready to start SSL/TLS */ + serv_puts("STLS"); + serv_gets(buf); + if (buf[0] != '2') { + fprintf(stderr, "Server can't start TLS: %s\n", &buf[4]); + return 0; + } + + /* Do SSL/TLS handshake */ + if ((a = SSL_connect(ssl)) < 1) { + fprintf(stderr, "SSL_connect failed: %s\n", + ERR_reason_error_string(ERR_get_error())); + SSL_CTX_free(ssl_ctx); + ssl_ctx = NULL; + SSL_free(ssl); + ssl = NULL; + return 0; + } + BIO_set_close(ssl->rbio, BIO_NOCLOSE); + { + int bits, alg_bits; + + bits = SSL_CIPHER_get_bits(SSL_get_current_cipher(ssl), &alg_bits); + printf("Encrypting with %s cipher %s (%d of %d bits)\n", + SSL_CIPHER_get_version(SSL_get_current_cipher(ssl)), + SSL_CIPHER_get_name(SSL_get_current_cipher(ssl)), + bits, alg_bits); + } + ssl_is_connected = 1; + return 1; +#else + return 0; +#endif /* HAVE_OPENSSL */ +} + + +/* + * void endtls() - end SSL/TLS session + */ +void endtls(void) +{ +#ifdef HAVE_OPENSSL + if (ssl) { + SSL_shutdown(ssl); + SSL_free(ssl); + ssl = NULL; + } + ssl_is_connected = 0; + if (ssl_ctx) { + SSL_CTX_free(ssl_ctx); + ssl_ctx = NULL; + } +#endif +} diff --git a/citadel/client_crypto.h b/citadel/client_crypto.h new file mode 100644 index 000000000..0fed70922 --- /dev/null +++ b/citadel/client_crypto.h @@ -0,0 +1,17 @@ +/* $Id$ */ + +/* Shared Diffie-Hellman parameters */ +#define DH_P "1A74527AEE4EE2568E85D4FB2E65E18C9394B9C80C42507D7A6A0DBE9A9A54B05A9A96800C34C7AA5297095B69C88901EEFD127F969DCA26A54C0E0B5C5473EBAEB00957D2633ECAE3835775425DE66C0DE6D024DBB17445E06E6B0C78415E589B8814F08531D02FD43778451E7685541079CFFB79EF0D26EFEEBBB69D1E80383" +#define DH_G "2" +#define DH_L 1024 +#define CIT_CIPHERS "ALL:RC4+RSA:+SSLv2:@STRENGTH" /* see ciphers(1) */ + +int starttls(void); +void endtls(void); +void serv_read(char *buf, int bytes); +void serv_write(char *buf, int nbytes); +#ifdef HAVE_OPENSSL +void serv_read_ssl(char *buf, int bytes); +void serv_write_ssl(char *buf, int nbytes); +void ssl_lock(int mode, int n, const char *file, int line); +#endif /* HAVE_OPENSSL */ diff --git a/citadel/configure.ac b/citadel/configure.ac index df039663c..59326f427 100644 --- a/citadel/configure.ac +++ b/citadel/configure.ac @@ -316,7 +316,8 @@ if test "$with_ssl" != "no"; then if test "x$ac_cv_openssldir" != "xno" ; then AC_DEFINE(HAVE_OPENSSL) - LIBS="$saved_LIBS -lssl -lcrypto" + LIBS="$saved_LIBS" + SSL_LIBS="-lssl -lcrypto" dnl Need to recover ssldir - test above runs in subshell ssldir=$ac_cv_openssldir if test ! -z "$ssldir" -a "x$ssldir" != "x/usr" -a "x$ssldir" != "x(system)"; then @@ -472,6 +473,7 @@ AC_SUBST(CLIENT_PTLIBS) AC_SUBST(CL_LIBOBJS) AC_SUBST(CX) AC_SUBST(DATABASE) +AC_SUBST(SSL_LIBS) AC_CONFIG_HEADER(sysdep.h) AC_CONFIG_FILES([Makefile weekly]) AC_OUTPUT diff --git a/citadel/ipc_c_tcp.c b/citadel/ipc_c_tcp.c index 28217ed57..a5a086daa 100644 --- a/citadel/ipc_c_tcp.c +++ b/citadel/ipc_c_tcp.c @@ -30,6 +30,9 @@ #include "citadel_decls.h" #include "ipc.h" #include "tools.h" +#if defined(HAVE_OPENSSL) && defined(CIT_CLIENT) +#include "client_crypto.h" +#endif #ifndef HAVE_SNPRINTF #include "snprintf.h" #endif @@ -53,6 +56,11 @@ int server_is_local = 0; int serv_sock; +#if defined(HAVE_OPENSSL) && defined(CIT_CLIENT) +extern int ssl_is_connected; +#endif + + void connection_died(void) { fprintf(stderr, "\r" "Your connection to this Citadel server is broken.\n" @@ -161,6 +169,12 @@ void serv_read(char *buf, int bytes) { int len, rlen; +#if defined(HAVE_OPENSSL) && defined(CIT_CLIENT) + if (ssl_is_connected) { + serv_read_ssl(buf, bytes); + return; + } +#endif len = 0; while (len < bytes) { rlen = read(serv_sock, &buf[len], bytes - len); @@ -168,7 +182,7 @@ void serv_read(char *buf, int bytes) connection_died(); return; } - len = len + rlen; + len += rlen; } } @@ -180,6 +194,13 @@ void serv_write(char *buf, int nbytes) { int bytes_written = 0; int retval; + +#if defined(HAVE_OPENSSL) && defined(CIT_CLIENT) + if (ssl_is_connected) { + serv_write_ssl(buf, nbytes); + return; + } +#endif while (bytes_written < nbytes) { retval = write(serv_sock, &buf[bytes_written], nbytes - bytes_written); @@ -187,7 +208,7 @@ void serv_write(char *buf, int nbytes) connection_died(); return; } - bytes_written = bytes_written + retval; + bytes_written += retval; } } @@ -301,4 +322,3 @@ char serv_getc(void) return (ch); } - diff --git a/citadel/ipcdef.h b/citadel/ipcdef.h index 203f4454d..4e27f6e6b 100644 --- a/citadel/ipcdef.h +++ b/citadel/ipcdef.h @@ -81,3 +81,4 @@ struct CtdlServInfo { void serv_puts(char *buf); void serv_gets(char *buf); +void connection_died(void); diff --git a/citadel/md5.h b/citadel/md5.h index 64beb6188..ff8d0c835 100644 --- a/citadel/md5.h +++ b/citadel/md5.h @@ -19,7 +19,9 @@ char *make_apop_string(char *realpass, char *nonce, char *buffer); /* * This is needed to make RSAREF happy on some MS-DOS compilers. */ +#ifndef HAVE_OPENSSL typedef struct MD5Context MD5_CTX; +#endif #define MD5_DIGEST_LEN 16 #define MD5_HEXSTRING_SIZE 33 diff --git a/citadel/serv_crypto.c b/citadel/serv_crypto.c new file mode 100644 index 000000000..6f913e030 --- /dev/null +++ b/citadel/serv_crypto.c @@ -0,0 +1,330 @@ +/* $Id$ */ + +#include +#include +#include "sysdep.h" +#ifdef HAVE_OPENSSL +#include +#include +#include +#endif +#ifdef HAVE_PTHREAD_H +#include +#endif +#include "server.h" +#include "serv_crypto.h" +#include "sysdep_decls.h" +#include "dynloader.h" + + +#ifdef HAVE_OPENSSL +SSL_CTX *ssl_ctx; /* SSL context */ +pthread_mutex_t **SSLCritters; /* Things needing locking */ + + +void init_ssl(void) +{ + SSL_METHOD *ssl_method; + DH *dh; + + if (!RAND_status()) { + lprintf(2, "PRNG not adequately seeded, won't do SSL/TLS\n"); + return; + } + SSLCritters = mallok(CRYPTO_num_locks() * sizeof (pthread_mutex_t *)); + if (!SSLCritters) { + lprintf(1, "citserver: can't allocate memory!!\n"); + /* Nothing's been initialized, just die */ + exit(1); + } else { + int a; + + for (a=0; a= 0x00906000L + SSL_CTX_set_mode(ssl_ctx, SSL_CTX_get_mode(ssl_ctx) | + SSL_MODE_AUTO_RETRY); +#endif +#endif + CRYPTO_set_locking_callback(ssl_lock); + CRYPTO_set_id_callback(pthread_self); + + /* Load DH parameters into the context */ + dh = DH_new(); + if (!dh) { + lprintf(2, "init_ssl() can't allocate a DH object: %s\n", + ERR_reason_error_string(ERR_get_error())); + SSL_CTX_free(ssl_ctx); + ssl_ctx = NULL; + return; + } + if (!(BN_hex2bn(&(dh->p), DH_P))) { + lprintf(2, "init_ssl() can't assign DH_P: %s\n", + ERR_reason_error_string(ERR_get_error())); + SSL_CTX_free(ssl_ctx); + ssl_ctx = NULL; + return; + } + if (!(BN_hex2bn(&(dh->g), DH_G))) { + lprintf(2, "init_ssl() can't assign DH_G: %s\n", + ERR_reason_error_string(ERR_get_error())); + SSL_CTX_free(ssl_ctx); + ssl_ctx = NULL; + return; + } + dh->length = DH_L; + SSL_CTX_set_tmp_dh(ssl_ctx, dh); + DH_free(dh); + + /* Finally let the server know we're here */ + CtdlRegisterProtoHook(cmd_stls, "STLS", "Start SSL/TLS session"); + CtdlRegisterProtoHook(cmd_gtls, "GTLS", "Get SSL/TLS session status"); + CtdlRegisterProtoHook(cmd_etls, "ETLS", "End SSL/TLS session"); + CtdlRegisterSessionHook(endtls_atlogout, EVT_STOP); +} + + +/* + * client_write_ssl() Send binary data to the client encrypted. + */ +void client_write_ssl(char *buf, int nbytes) +{ + int retval; + int nremain; + char junk[1]; + + nremain = nbytes; + + while (nremain > 0) { + if (SSL_want_write(CC->ssl)) { + if ((SSL_read(CC->ssl, junk, 0)) < 1) { + lprintf(9, "SSL_read in client_write:\n"); + ERR_print_errors_fp(stderr); + } + } + retval = SSL_write(CC->ssl, &buf[nbytes - nremain], nremain); + if (retval < 1) { + long errval; + + errval = SSL_get_error(CC->ssl, retval); + if (errval == SSL_ERROR_WANT_READ || + errval == SSL_ERROR_WANT_WRITE) { + sleep(1); + continue; + } + lprintf(9, "SSL_write got error %ld\n", errval); + endtls(1); + client_write(&buf[nbytes - nremain], nremain); + return; + } + nremain -= retval; + } +} + + +/* + * client_read_ssl() - read data from the encrypted layer. + */ +int client_read_ssl(char *buf, int bytes, int timeout) +{ + int len,rlen; + fd_set rfds; + struct timeval tv; + int retval; + int s; + char junk[1]; + + len = 0; + while(lenssl->rbio, NULL); + FD_SET(s, &rfds); + tv.tv_sec = timeout; + tv.tv_usec = 0; + + retval = select(s+1, &rfds, NULL, NULL, &tv); + + if (FD_ISSET(s, &rfds) == 0) { + return(0); + } + + if (SSL_want_read(CC->ssl)) { + if ((SSL_write(CC->ssl, junk, 0)) < 1) { + lprintf(9, "SSL_write in client_read:\n"); + ERR_print_errors_fp(stderr); + } + } + rlen = SSL_read(CC->ssl, &buf[len], bytes-len); + if (rlen<1) { + long errval; + + errval = SSL_get_error(CC->ssl, rlen); + if (errval == SSL_ERROR_WANT_READ || + errval == SSL_ERROR_WANT_WRITE) { + sleep(1); + continue; + } + lprintf(9, "SSL_read got error %ld\n", errval); + endtls(1); + return (client_read_to(&buf[len], bytes - len, timeout)); + } + len += rlen; + } + return(1); +} + + +/* + * cmd_stls() starts SSL/TLS encryption for the current session + */ +void cmd_stls(char *params) +{ + int retval, bits, alg_bits; + + if (!ssl_ctx) { + cprintf("%d No SSL_CTX available\n", ERROR); + return; + } + if (!(CC->ssl = SSL_new(ssl_ctx))) { + lprintf(2, "SSL_new failed: %s\n", + ERR_reason_error_string(ERR_peek_error())); + cprintf("%d SSL_new: %s\n", ERROR, + ERR_reason_error_string(ERR_get_error())); + return; + } + if (!(SSL_set_fd(CC->ssl, CC->client_socket))) { + lprintf(2, "SSL_set_fd failed: %s\n", + ERR_reason_error_string(ERR_peek_error())); + SSL_free(CC->ssl); + CC->ssl = NULL; + cprintf("%d SSL_set_fd: %s\n", ERROR, + ERR_reason_error_string(ERR_get_error())); + return; + } + cprintf("%d \n", OK); + retval = SSL_accept(CC->ssl); + if (retval < 1) { + /* + * Can't notify the client of an error here; they will + * discover the problem at the SSL layer and should + * revert to unencrypted communications. + */ + long errval; + + errval = SSL_get_error(CC->ssl, retval); + lprintf(2, "SSL_accept failed: %s\n", + ERR_reason_error_string(ERR_get_error())); + SSL_free(CC->ssl); + CC->ssl = NULL; + return; + } + BIO_set_close(CC->ssl->rbio, BIO_NOCLOSE); + bits = SSL_CIPHER_get_bits(SSL_get_current_cipher(CC->ssl), &alg_bits); + lprintf(3, "Session %d using %s on %s (%d of %d bits)\n", CC->cs_pid, + SSL_CIPHER_get_name(SSL_get_current_cipher(CC->ssl)), + SSL_CIPHER_get_version(SSL_get_current_cipher(CC->ssl)), + bits, alg_bits); + CC->redirect_ssl = 1; +} + + +/* + * cmd_gtls() returns status info about the TLS connection + */ +void cmd_gtls(char *params) +{ + int bits, alg_bits; + + if (!CC->ssl || !CC->redirect_ssl) { + cprintf("%d Session is not encrypted.\n", ERROR); + return; + } + bits = SSL_CIPHER_get_bits(SSL_get_current_cipher(CC->ssl), &alg_bits); + cprintf("%d %s|%s|%d|%d\n", OK, + SSL_CIPHER_get_version(SSL_get_current_cipher(CC->ssl)), + SSL_CIPHER_get_name(SSL_get_current_cipher(CC->ssl)), + alg_bits, bits); +} + + +/* Logout function hook */ +void endtls_atlogout(void) +{ + endtls(1); +} + + +/* Command function hook */ +void cmd_etls(char *params) +{ + endtls(0); +} + + +/* + * endtls() shuts down the TLS connection + * Parameter is NULL for client request, CitContext * for server request + * + * WARNING: This may make your session vulnerable to a known plaintext + * attack in the current implmentation. + */ +void endtls(int who) +{ + lprintf(7, "Session %d ending SSL/TLS%s\n", CC->cs_pid, + (who) ? "" : " at client request"); + + if (!who) { + if (!CC->ssl) { + cprintf("%d Connection is not encrypted.\n", ERROR); + return; + } + cprintf("%d Now stop encryption.\n", OK); + } else if (!CC->ssl) { + return; + } + + SSL_shutdown(CC->ssl); + SSL_free(CC->ssl); + CC->ssl = NULL; + CC->redirect_ssl = 0; +} + + +/* + * ssl_lock() callback for OpenSSL mutex locks + */ +void ssl_lock(int mode, int n, const char *file, int line) +{ + if (mode & CRYPTO_LOCK) + pthread_mutex_lock(SSLCritters[n]); + else + pthread_mutex_unlock(SSLCritters[n]); +} +#endif /* HAVE_OPENSSL */ diff --git a/citadel/serv_crypto.h b/citadel/serv_crypto.h new file mode 100644 index 000000000..45e1a5dd9 --- /dev/null +++ b/citadel/serv_crypto.h @@ -0,0 +1,19 @@ +/* $Id$ */ + +/* Shared Diffie-Hellman parameters */ +#define DH_P "1A74527AEE4EE2568E85D4FB2E65E18C9394B9C80C42507D7A6A0DBE9A9A54B05A9A96800C34C7AA5297095B69C88901EEFD127F969DCA26A54C0E0B5C5473EBAEB00957D2633ECAE3835775425DE66C0DE6D024DBB17445E06E6B0C78415E589B8814F08531D02FD43778451E7685541079CFFB79EF0D26EFEEBBB69D1E80383" +#define DH_G "2" +#define DH_L 1024 +#define CIT_CIPHERS "ALL:RC4+RSA:+SSLv2:@STRENGTH" /* see ciphers(1) */ + +#ifdef HAVE_OPENSSL +void init_ssl(void); +void client_write_ssl (char *buf, int nbytes); +int client_read_ssl (char *buf, int bytes, int timeout); +void cmd_stls(char *params); +void cmd_gtls(char *params); +void cmd_etls(char *params); +void endtls_atlogout(void); +void endtls(int who); +void ssl_lock(int mode, int n, const char *file, int line); +#endif diff --git a/citadel/server.h b/citadel/server.h index 5dbc22b70..1a1655ae6 100644 --- a/citadel/server.h +++ b/citadel/server.h @@ -32,6 +32,9 @@ #endif /* __CYGWIN__ */ #include "citadel.h" +#ifdef HAVE_OPENSSL +#include +#endif #define CTDLMESSAGE_MAGIC 0x159d struct CtdlMessage { @@ -123,6 +126,10 @@ struct CitContext { /* Redirect this session's output to somewhere else? */ FILE *redirect_fp; int redirect_sock; +#ifdef HAVE_OPENSSL + SSL *ssl; + int redirect_ssl; +#endif /* A linked list of all express messages sent to us. */ struct ExpressMessage *FirstExpressMessage; diff --git a/citadel/sysdep.c b/citadel/sysdep.c index c45e2f776..fdb38cc27 100644 --- a/citadel/sysdep.c +++ b/citadel/sysdep.c @@ -62,6 +62,7 @@ #include "database.h" #include "housekeeping.h" #include "tools.h" +#include "serv_crypto.h" #ifdef HAVE_SYS_SELECT_H #include @@ -234,6 +235,10 @@ static RETSIGTYPE signal_cleanup(int signum) { void init_sysdep(void) { int a; +#ifdef HAVE_OPENSSL + init_ssl(); +#endif + /* Set up a bunch of semaphores to be used for critical sections */ for (a=0; aredirect_ssl) { + client_write_ssl(buf, nbytes); + return; + } +#endif + if (CC->redirect_fp != NULL) { fwrite(buf, nbytes, 1, CC->redirect_fp); return; @@ -533,6 +544,11 @@ int client_read_to(char *buf, int bytes, int timeout) struct timeval tv; int retval; +#ifdef HAVE_OPENSSL + if (CC->redirect_ssl) { + return (client_read_ssl(buf, bytes, timeout)); + } +#endif len = 0; while(lenlogged_in = 0; /* @@ -457,11 +455,7 @@ void logout(struct CitContext *who) } /* Do modular stuff... */ - if (who != CC) - saved_cc = CC; - become_session(who); PerformSessionHooks(EVT_LOGOUT); - become_session(saved_cc); } #ifdef ENABLE_CHKPWD