]> code.citadel.org Git - citadel.git/commitdiff
* SSL/TLS support for the Citadel/UX wire protocol
authorMichael Hampton <io_error@uncensored.citadel.org>
Sun, 6 Jan 2002 10:33:11 +0000 (10:33 +0000)
committerMichael Hampton <io_error@uncensored.citadel.org>
Sun, 6 Jan 2002 10:33:11 +0000 (10:33 +0000)
14 files changed:
citadel/ChangeLog
citadel/Makefile.in
citadel/client_crypto.c [new file with mode: 0644]
citadel/client_crypto.h [new file with mode: 0644]
citadel/configure.ac
citadel/ipc_c_tcp.c
citadel/ipcdef.h
citadel/md5.h
citadel/serv_crypto.c [new file with mode: 0644]
citadel/serv_crypto.h [new file with mode: 0644]
citadel/server.h
citadel/sysdep.c
citadel/techdoc/session.txt
citadel/user_ops.c

index 2882b925d0baf82f6ebd7e6c104ed5fbe9704124..ade92145c1d0fe6acf665cfaa39bd59c97429786 100644 (file)
@@ -1,4 +1,7 @@
  $Log$
  $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
 
  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 <bryant@cs.usm.maine.edu>
 
 Fri Jul 10 1998 Art Cancro <ajc@uncensored.citadel.org>
        * Initial CVS import 
 
 Fri Jul 10 1998 Art Cancro <ajc@uncensored.citadel.org>
        * Initial CVS import 
-
index a75f5579574a33bcd34df97b118d5c08d86a7f60..1967e840f857b0463f2f92ee6711237abe13f76e 100644 (file)
@@ -63,6 +63,7 @@ LIBOBJS=@LIBOBJS@
 CL_LIBOBJS=@CL_LIBOBJS@
 PTHREAD_DEFS=@PTHREAD_DEFS@
 PTHREAD_LIBS=@PTHREAD_LIBS@
 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@
 CLIENT_PTLIBS=@CLIENT_PTLIBS@
 INSTALL=@INSTALL@
 INSTALL_DATA=@INSTALL_DATA@
@@ -81,12 +82,12 @@ VPATH=$(srcdir)
 
 DOMAIN=@DOMAIN@
 
 
 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 \
        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 \
        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)  \
 
 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) \
        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 $<
 
 .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 \
 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 \
 
 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 \
 
 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 (file)
index 0000000..5fdb3b6
--- /dev/null
@@ -0,0 +1,297 @@
+/* $Id$ */
+
+#include "sysdep.h"
+#ifdef HAVE_OPENSSL
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+#include <openssl/rand.h>
+#endif
+#ifdef HAVE_PTHREAD_H
+#include <pthread.h>
+#endif
+#include <unistd.h>
+#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 (file)
index 0000000..0fed709
--- /dev/null
@@ -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 */
index df039663cd1fd6b02145b28d65a6cc8962a16cd0..59326f42754387b8b24a919c4b6d03297cf31b9f 100644 (file)
@@ -316,7 +316,8 @@ if test "$with_ssl" != "no"; then
        
        if test "x$ac_cv_openssldir" != "xno" ; then
                AC_DEFINE(HAVE_OPENSSL)
        
        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
                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(CL_LIBOBJS)
 AC_SUBST(CX)
 AC_SUBST(DATABASE)
+AC_SUBST(SSL_LIBS)
 AC_CONFIG_HEADER(sysdep.h)
 AC_CONFIG_FILES([Makefile weekly])
 AC_OUTPUT
 AC_CONFIG_HEADER(sysdep.h)
 AC_CONFIG_FILES([Makefile weekly])
 AC_OUTPUT
index 28217ed57d5999e3287cb46cc6b957dce997ae6d..a5a086daaa0a35835a632b3f3c8204517a013aff 100644 (file)
@@ -30,6 +30,9 @@
 #include "citadel_decls.h"
 #include "ipc.h"
 #include "tools.h"
 #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
 #ifndef HAVE_SNPRINTF
 #include "snprintf.h"
 #endif
@@ -53,6 +56,11 @@ int server_is_local = 0;
 
 int serv_sock;
 
 
 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"
 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;
 
 {
        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);
        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;
                }
                        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;
 {
        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);
        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;
                }
                        connection_died();
                        return;
                }
-               bytes_written = bytes_written + retval;
+               bytes_written += retval;
        }
 }
 
        }
 }
 
@@ -301,4 +322,3 @@ char serv_getc(void)
 
        return (ch);
 }
 
        return (ch);
 }
-
index 203f4454d2901c254a3f55c3bfddbb662725c18c..4e27f6e6b529e1b82744d48a841417f03f8aae80 100644 (file)
@@ -81,3 +81,4 @@ struct CtdlServInfo {
 
 void serv_puts(char *buf);
 void serv_gets(char *buf);
 
 void serv_puts(char *buf);
 void serv_gets(char *buf);
+void connection_died(void);
index 64beb61881ae93ceb69e267fc7adb0871aef7c88..ff8d0c835ed0c05cd2329288ac7aa202d06cb53b 100644 (file)
@@ -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.
  */
 /*
  * This is needed to make RSAREF happy on some MS-DOS compilers.
  */
+#ifndef HAVE_OPENSSL
 typedef struct MD5Context MD5_CTX;
 typedef struct MD5Context MD5_CTX;
+#endif
 
 #define MD5_DIGEST_LEN         16
 #define MD5_HEXSTRING_SIZE     33
 
 #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 (file)
index 0000000..6f913e0
--- /dev/null
@@ -0,0 +1,330 @@
+/* $Id$ */
+
+#include <unistd.h>
+#include <sys/types.h>
+#include "sysdep.h"
+#ifdef HAVE_OPENSSL
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+#include <openssl/rand.h>
+#endif
+#ifdef HAVE_PTHREAD_H
+#include <pthread.h>
+#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<CRYPTO_num_locks(); a++) {
+                       SSLCritters[a] = mallok(sizeof (pthread_mutex_t));
+                       if (!SSLCritters[a]) {
+                               lprintf(1, "citserver: can't allocate memory!!\n");
+                               /* Nothing's been initialized, just die */
+                               exit(1);
+                       }
+                       pthread_mutex_init(SSLCritters[a], NULL);
+               }
+       }
+
+       /*
+        * Initialize SSL transport layer
+        */
+       SSL_library_init();
+       SSL_load_error_strings();
+       ssl_method = SSLv23_server_method();
+       if (!(ssl_ctx = SSL_CTX_new(ssl_method))) {
+               lprintf(2, "SSL_CTX_new failed: %s\n",
+                               ERR_reason_error_string(ERR_get_error()));
+               return;
+       }
+       if (!(SSL_CTX_set_cipher_list(ssl_ctx, CIT_CIPHERS))) {
+               lprintf(2, "SSL: No ciphers available\n");
+               SSL_CTX_free(ssl_ctx);
+               ssl_ctx = NULL;
+               return;
+       }
+#if 0
+#if SSLEAY_VERSION_NUMBER >= 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(len<bytes) {
+               FD_ZERO(&rfds);
+               s = BIO_get_fd(CC->ssl->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 (file)
index 0000000..45e1a5d
--- /dev/null
@@ -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
index 5dbc22b70868e35588b85c2c1dbb288b8e3320cb..1a1655ae684718e32de04089affd8cc1bb20c197 100644 (file)
@@ -32,6 +32,9 @@
 #endif /* __CYGWIN__ */
 
 #include "citadel.h"
 #endif /* __CYGWIN__ */
 
 #include "citadel.h"
+#ifdef HAVE_OPENSSL
+#include <openssl/ssl.h>
+#endif
 
 #define        CTDLMESSAGE_MAGIC               0x159d
 struct CtdlMessage {
 
 #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;
        /* 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;
 
        /* A linked list of all express messages sent to us. */
        struct ExpressMessage *FirstExpressMessage;
index c45e2f77681e44fd4fe424f5978e0334e6f521d2..fdb38cc277675fc5e1f11bc8aa5f1a8fd65892a7 100644 (file)
@@ -62,6 +62,7 @@
 #include "database.h"
 #include "housekeeping.h"
 #include "tools.h"
 #include "database.h"
 #include "housekeeping.h"
 #include "tools.h"
+#include "serv_crypto.h"
 
 #ifdef HAVE_SYS_SELECT_H
 #include <sys/select.h>
 
 #ifdef HAVE_SYS_SELECT_H
 #include <sys/select.h>
@@ -234,6 +235,10 @@ static RETSIGTYPE signal_cleanup(int signum) {
 void init_sysdep(void) {
        int a;
 
 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; a<MAX_SEMAPHORES; ++a) {
                pthread_mutex_init(&Critters[a], NULL);
        /* Set up a bunch of semaphores to be used for critical sections */
        for (a=0; a<MAX_SEMAPHORES; ++a) {
                pthread_mutex_init(&Critters[a], NULL);
@@ -464,7 +469,6 @@ DONE:       ++num_sessions;
 }
 
 
 }
 
 
-
 /*
  * client_write()   ...    Send binary data to the client.
  */
 /*
  * client_write()   ...    Send binary data to the client.
  */
@@ -475,6 +479,13 @@ void client_write(char *buf, int nbytes)
        int sock;
 
 
        int sock;
 
 
+#ifdef HAVE_OPENSSL
+       if (CC->redirect_ssl) {
+               client_write_ssl(buf, nbytes);
+               return;
+       }
+#endif
+
        if (CC->redirect_fp != NULL) {
                fwrite(buf, nbytes, 1, CC->redirect_fp);
                return;
        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;
 
        struct timeval tv;
        int retval;
 
+#ifdef HAVE_OPENSSL
+       if (CC->redirect_ssl) {
+               return (client_read_ssl(buf, bytes, timeout));
+       }
+#endif
        len = 0;
        while(len<bytes) {
                FD_ZERO(&rfds);
        len = 0;
        while(len<bytes) {
                FD_ZERO(&rfds);
@@ -1074,6 +1090,3 @@ SETUP_FD: memcpy(&readfds, &masterfds, sizeof masterfds);
        --num_threads;
        return NULL;
 }
        --num_threads;
        return NULL;
 }
-
-
-
index 0c1f0538e64cd431b1df3fb99eda30b5d5da634b..7403611c72699f7bbd6a38db2cd3038c2524e268 100644 (file)
@@ -1828,8 +1828,57 @@ supported:
       SMTP runqueue                (attempt immediate delivery of all messages
                                     in the outbound SMTP queue, ignoring any
                                     retry times stored there)
       SMTP runqueue                (attempt immediate delivery of all messages
                                     in the outbound SMTP queue, ignoring any
                                     retry times stored there)
+
+
+ STLS   (Start Transport Layer Security)
+
+ This command starts TLS on the current connection.  The current
+implementation uses OpenSSL on both the client and server end.  For future
+compatibility all clients must support at least TLSv1, and servers are
+guaranteed to support TLSv1.  During TLS negotiation (see below) the server
+and client may agree to use a different protocol.
+
+ The server returns ERROR if it does not support SSL or SSL initialization
+failed on the server; otherwise it returns OK.  Once the server returns OK and
+the client has read the response, the server and client immediately negotiate
+TLS (in OpenSSL, using SSL_connect() on the client and SSL_accept() on the
+server).  If negotiation fails, the server and client should attempt to resume
+the session unencrypted.  If either end is unable to resume the session, the
+connection should be closed.
+
+ This command may be run at any time.
   
   
   
   
+ GTLS   (Get Transport Layer Security Status)
+
+ This command returns information about the current connection.  The server
+returns OK plus several parameters if the connection is encrypted, and ERROR
+if the connection is not encrypted.  It is primarily used for debugging.  The
+command may be run at any time.
+ 0 - Protocol name, e.g. "SSLv3"
+ 1 - Cipher suite name, e.g. "ADH-RC4-MD5"
+ 2 - Cipher strength bits, e.g. 128
+ 3 - Cipher strength bits actually in use, e.g. 128
+
+
+ ETLS   (End Transport Layer Security)
+
+ This command shuts down TLS and resumes an unencrypted communications
+channel.  This command returns OK and then negotiates TLS shutdown.  The
+client should read the response before negotiating TLS shutdown (e.g. with
+SSL_shutdown() in OpenSSL).  The connection is then unencrypted.  The
+server may return ERROR if it does not support SSL or if the connection was
+not encrypted to begin with.  If either end is unable to resume the connection
+unencrypted, the connection should be closed.
+ This command is primarily for debugging.  Clients may end TLS at any time
+without calling this command.  If a client needs to end TLS and resume
+unencrypted communications, it should do so through the SSL/TLS layer (e.g.
+with SSL_shutdown() in OpenSSL) rather than using this command.  This command
+may be removed from future versions of Citadel/UX.
+
+
  ASYN   (ASYNchronous message support)
  
  Negotiate the use of asynchronous, or unsolicited, protocol messages.  The
  ASYN   (ASYNchronous message support)
  
  Negotiate the use of asynchronous, or unsolicited, protocol messages.  The
index 0db8d1e7f6cfd9aa92f09f77ba9185bd0ff7060c..f8aefce0d3426c8df61d5e3f7fd6cdb694618d5f 100644 (file)
@@ -430,8 +430,6 @@ void logged_in_response(void)
  */
 void logout(struct CitContext *who)
 {
  */
 void logout(struct CitContext *who)
 {
-       struct CitContext *saved_cc = NULL;
-
        who->logged_in = 0;
 
        /*
        who->logged_in = 0;
 
        /*
@@ -457,11 +455,7 @@ void logout(struct CitContext *who)
        }
 
        /* Do modular stuff... */
        }
 
        /* Do modular stuff... */
-       if (who != CC)
-               saved_cc = CC;
-       become_session(who);
        PerformSessionHooks(EVT_LOGOUT);
        PerformSessionHooks(EVT_LOGOUT);
-       become_session(saved_cc);
 }
 
 #ifdef ENABLE_CHKPWD
 }
 
 #ifdef ENABLE_CHKPWD