diff --git a/Changes b/Changes index a025c82c7..4939e5726 100644 --- a/Changes +++ b/Changes @@ -1850,3 +1850,11 @@ stuff instead of hacking configure directly :P . - Made the timesynch log output more clear and understandable. - Added an 'UnrealIRCd started' log message on startup. +- Added support for STARTTLS. This allows users to switch to SSL without + having to use a special SSL-only port, they can simply switch to SSL on + any port. This is currently only supported by few clients (such as KVIrc 4). + This functionality can be disabled by setting set::ssl::options::no-starttls, + for example if you don't want to offer SSL to your users and only want it + to be used for server to server links. + Naturally, the IRCd must be compiled with SSL support for STARTTLS to work. +- Fixed SSL_ERROR_WANT_READ in IRCd_ssl_write() diff --git a/doc/unreal32docs.html b/doc/unreal32docs.html index 87885a643..70d84c33e 100644 --- a/doc/unreal32docs.html +++ b/doc/unreal32docs.html @@ -2468,6 +2468,8 @@ set { Disallows connections from people with self-signed certificates.

set::ssl::options::verify-certificate;
Makes Unreal determine if the SSL certificate is valid before allowing connection.

+

set::ssl::options::no-starttls;
+ Disable STARTTLS. STARTTLS allows clients to use SSL on regular (non-SSL) ports.

set::throttle::period <timevalue>
How long a user must wait before reconnecting more than set::throttle::connections times.

diff --git a/include/struct.h b/include/struct.h index 2bdbe3484..692700b64 100644 --- a/include/struct.h +++ b/include/struct.h @@ -232,6 +232,7 @@ typedef unsigned int u_int32_t; /* XXX Hope this works! */ #define STAT_LOG -7 /* logfile for -x */ #define STAT_CONNECTING -6 +#define STAT_SSL_STARTTLS_HANDSHAKE -8 #define STAT_SSL_CONNECT_HANDSHAKE -5 #define STAT_SSL_ACCEPT_HANDSHAKE -4 #define STAT_HANDSHAKE -3 @@ -254,9 +255,11 @@ typedef unsigned int u_int32_t; /* XXX Hope this works! */ #define IsLog(x) ((x)->status == STAT_LOG) #ifdef USE_SSL +#define IsSSLStartTLSHandshake(x) ((x)->status == STAT_SSL_STARTTLS_HANDSHAKE) #define IsSSLAcceptHandshake(x) ((x)->status == STAT_SSL_ACCEPT_HANDSHAKE) #define IsSSLConnectHandshake(x) ((x)->status == STAT_SSL_CONNECT_HANDSHAKE) -#define IsSSLHandshake(x) (IsSSLAcceptHandshake(x) || IsSSLConnectHandshake(x)) +#define IsSSLHandshake(x) (IsSSLAcceptHandshake(x) || IsSSLConnectHandshake(x) | IsSSLStartTLSHandshake(x)) +#define SetSSLStartTLSHandshake(x) ((x)->status = STAT_SSL_STARTTLS_HANDSHAKE) #define SetSSLAcceptHandshake(x) ((x)->status = STAT_SSL_ACCEPT_HANDSHAKE) #define SetSSLConnectHandshake(x) ((x)->status = STAT_SSL_CONNECT_HANDSHAKE) #endif @@ -935,6 +938,8 @@ typedef struct { #define SSLFLAG_FAILIFNOCERT 0x1 #define SSLFLAG_VERIFYCERT 0x2 #define SSLFLAG_DONOTACCEPTSELFSIGNED 0x4 +#define SSLFLAG_NOSTARTTLS 0x8 + struct Client { struct Client *next, *prev, *hnext; anUser *user; /* ...defined, if this is a User */ diff --git a/makefile.win32 b/makefile.win32 index 376882ce4..cecb588ce 100644 --- a/makefile.win32 +++ b/makefile.win32 @@ -188,7 +188,8 @@ MOD_FILES=SRC/MODULES/L_COMMANDS.C SRC/MODULES/M_CHGHOST.C SRC/MODULES/M_SDESC.C SRC/MODULES/M_NICK.C SRC/MODULES/M_USER.C SRC/MODULES/M_MODE.C \ SRC/MODULES/M_WATCH.C SRC/MODULES/M_PART.C SRC/MODULES/M_JOIN.C \ SRC/MODULES/M_MOTD.C SRC/MODULES/M_OPERMOTD.C SRC/MODULES/M_BOTMOTD.C \ - SRC/MODULES/M_LUSERS.C SRC/MODULES/M_NAMES.C SRC/MODULES/M_SVSNOLAG.C + SRC/MODULES/M_LUSERS.C SRC/MODULES/M_NAMES.C SRC/MODULES/M_SVSNOLAG.C \ + SRC/MODULES/M_STARTTLS.C DLL_FILES=SRC/MODULES/M_CHGHOST.DLL SRC/MODULES/M_SDESC.DLL SRC/MODULES/M_SETIDENT.DLL \ SRC/MODULES/M_SETNAME.DLL SRC/MODULES/M_SETHOST.DLL SRC/MODULES/M_CHGIDENT.DLL \ @@ -224,6 +225,7 @@ DLL_FILES=SRC/MODULES/M_CHGHOST.DLL SRC/MODULES/M_SDESC.DLL SRC/MODULES/M_SETIDE SRC/MODULES/M_WATCH.DLL SRC/MODULES/M_PART.DLL SRC/MODULES/M_JOIN.DLL \ SRC/MODULES/M_MOTD.DLL SRC/MODULES/M_OPERMOTD.DLL SRC/MODULES/M_BOTMOTD.DLL \ SRC/MODULES/M_LUSERS.DLL SRC/MODULES/M_NAMES.DLL SRC/MODULES/M_SVSNOLAG.DLL \ + SRC/MODULES/M_STARTTLS.DLL \ SRC/MODULES/CLOAK.DLL @@ -783,6 +785,9 @@ src/modules/cloak.dll: src/modules/cloak.c $(INCLUDES) src/modules/m_svsnolag.dll: src/modules/m_svsnolag.c $(INCLUDES) $(CC) $(MODCFLAGS) src/modules/m_svsnolag.c $(MODLFLAGS) +src/modules/m_starttls.dll: src/modules/m_starttls.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/m_starttls.c $(MODLFLAGS) + dummy: diff --git a/src/modules/Makefile.in b/src/modules/Makefile.in index 4765f7b52..e15fbb419 100644 --- a/src/modules/Makefile.in +++ b/src/modules/Makefile.in @@ -54,7 +54,7 @@ R_MODULES= \ m_connect.so m_dccallow.so m_userip.so m_nick.so m_user.so \ m_mode.so m_watch.so m_part.so m_join.so m_motd.so m_opermotd.so \ m_botmotd.so m_lusers.so m_names.so m_svsnolag.so m_addmotd.so \ - m_svslusers.so + m_svslusers.so m_starttls.so #note change of .c to .o COMMANDS=m_sethost.o m_chghost.o m_chgident.o m_setname.o m_setident.o \ @@ -76,7 +76,7 @@ COMMANDS=m_sethost.o m_chghost.o m_chgident.o m_setname.o m_setident.o \ m_svsfline.o m_dccdeny.o m_undccdeny.o m_whowas.o \ m_connect.o m_dccallow.o m_userip.o m_nick.o m_user.o \ m_mode.o m_watch.o m_part.o m_join.o m_motd.o m_opermotd.o \ - m_botmotd.o m_lusers.o m_names.o m_svsnolag.o + m_botmotd.o m_lusers.o m_names.o m_svsnolag.o m_starttls.o MODULES=commands.so cloak.so $(R_MODULES) @@ -407,6 +407,9 @@ m_names.o: m_names.c $(INCLUDES) m_svsnolag.o: m_svsnolag.c $(INCLUDES) $(CC) $(CFLAGS) $(MODULEFLAGS) -c m_svsnolag.c +m_starttls.o: m_starttls.c $(INCLUDES) + $(CC) $(CFLAGS) $(MODULEFLAGS) -c m_starttls.c + ############################################################################# # .so's section ############################################################################# @@ -819,6 +822,10 @@ m_svsnolag.so: m_svsnolag.c $(INCLUDES) $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ -o m_svsnolag.so m_svsnolag.c +m_starttls.so: m_starttls.c $(INCLUDES) + $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ + -o m_starttls.so m_starttls.c + ############################################################################# # and now the remaining modules... ############################################################################# diff --git a/src/modules/m_starttls.c b/src/modules/m_starttls.c new file mode 100644 index 000000000..e146a94d8 --- /dev/null +++ b/src/modules/m_starttls.c @@ -0,0 +1,128 @@ +/* + * IRC - Internet Relay Chat, src/modules/m_starttls.c + * (C) 2009 Syzop & The UnrealIRCd Team + * + * See file AUTHORS in IRC package for additional names of + * the programmers. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 1, or (at your option) + * any later version. + * + * 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. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#include "config.h" +#include "struct.h" +#include "common.h" +#include "sys.h" +#include "numeric.h" +#include "msg.h" +#include "proto.h" +#include "channel.h" +#include +#include +#include +#include +#include +#ifdef _WIN32 +#include +#endif +#include +#include "h.h" +#ifdef STRIPBADWORDS +#include "badwords.h" +#endif +#ifdef _WIN32 +#include "version.h" +#endif + +DLLFUNC CMD_FUNC(m_starttls); + +#define MSG_STARTTLS "STARTTLS" + +ModuleHeader MOD_HEADER(m_starttls) + = { + "m_starttls", + "$Id$", + "command /starttls", + "3.2-b8-1", + NULL + }; + +DLLFUNC int MOD_INIT(m_starttls)(ModuleInfo *modinfo) +{ + CommandAdd(modinfo->handle, MSG_STARTTLS, NULL, m_starttls, MAXPARA, M_UNREGISTERED); + MARK_AS_OFFICIAL_MODULE(modinfo); + return MOD_SUCCESS; +} + +DLLFUNC int MOD_LOAD(m_starttls)(int module_load) +{ + return MOD_SUCCESS; +} + +DLLFUNC int MOD_UNLOAD(m_starttls)(int module_unload) +{ + return MOD_SUCCESS; +} + +DLLFUNC CMD_FUNC(m_starttls) +{ + if (!MyConnect(sptr) || !IsUnknown(sptr)) + return 0; +#ifndef USE_SSL + /* sendnotice(sptr, "This server does not support SSL"); */ + /* or numeric 691? */ + /* actually... it's probably best to just act like we don't know this command...? */ + sendto_one(sptr, err_str(ERR_NOTREGISTERED), me.name, "STARTTLS"); + return 0; +#else + if (iConf.ssl_options & SSLFLAG_NOSTARTTLS) + { + sendto_one(sptr, err_str(ERR_NOTREGISTERED), me.name, "STARTTLS"); + return 0; + } + if (IsSecure(sptr)) + { + sendto_one(sptr, ":%s 691 %s :STARTTLS failed. Already using TLS.", me.name, sptr->name); + return 0; + } + dbuf_delete(&sptr->recvQ, 1000000); /* Clear up any remaining plaintext commands */ + sendto_one(sptr, ":%s 670 %s :STARTTLS successful, go ahead with TLS handshake", me.name, sptr->name); + // ^^ FIXME, use: RPL_STARTTLS + send_queued(sptr); + + SetSSLStartTLSHandshake(sptr); + Debug((DEBUG_DEBUG, "Starting SSL handshake (due to STARTTLS) for %s", sptr->sockhost)); + if ((sptr->ssl = SSL_new(ctx_server)) == NULL) + goto fail; + sptr->flags |= FLAGS_SSL; + SSL_set_fd(sptr->ssl, sptr->fd); + SSL_set_nonblocking(sptr->ssl); + if (!ircd_SSL_accept(sptr, sptr->fd)) { + Debug((DEBUG_DEBUG, "Failed SSL accept handshake in instance 1: %s", sptr->sockhost)); + SSL_set_shutdown(sptr->ssl, SSL_RECEIVED_SHUTDOWN); + SSL_smart_shutdown(sptr->ssl); + SSL_free(sptr->ssl); + goto fail; + } + + /* HANDSHAKE IN PROGRESS */ + return 0; +fail: + /* Failure */ + sendto_one(sptr, ":%s 691 %s :STARTTLS failed", me.name, sptr->name); // FIXME, use: ERR_STARTTLS + sptr->ssl = NULL; + sptr->flags &= ~FLAGS_SSL; + SetUnknown(sptr); + return 0; +#endif +} diff --git a/src/s_bsd.c b/src/s_bsd.c index 3264eb24a..07502cf02 100644 --- a/src/s_bsd.c +++ b/src/s_bsd.c @@ -1972,21 +1972,24 @@ deadsocket: !(DoingDNS(cptr) || DoingAuth(cptr)) #ifdef USE_SSL && - !(IsSSLAcceptHandshake(cptr) || IsSSLConnectHandshake(cptr)) + !(IsSSLHandshake(cptr)) #endif ) length = read_packet(cptr, &read_set); #ifdef USE_SSL if ((length != FLUSH_BUFFER) && (cptr->ssl != NULL) && - (IsSSLAcceptHandshake(cptr) || IsSSLConnectHandshake(cptr)) && + IsSSLHandshake(cptr) && FD_ISSET(cptr->fd, &read_set)) { if (!SSL_is_init_finished(cptr->ssl)) { - if (IsDead(cptr) || IsSSLAcceptHandshake(cptr) ? !ircd_SSL_accept(cptr, cptr->fd) : ircd_SSL_connect(cptr) < 0) + if (IsDead(cptr) || + (IsSSLAcceptHandshake(cptr) || IsSSLStartTLSHandshake(cptr)) ? !ircd_SSL_accept(cptr, cptr->fd) : ircd_SSL_connect(cptr) < 0) { length = -1; } + /* if (IsSSLStartTLSHandshake(cptr)) + length = read_packet(cptr, &read_set); */ } if (SSL_is_init_finished(cptr->ssl)) { @@ -1995,10 +1998,14 @@ deadsocket: Debug((DEBUG_ERROR, "ssl: start_of_normal_client_handshake(%s)", cptr->sockhost)); start_of_normal_client_handshake(cptr); } - else + else if (IsSSLConnectHandshake(cptr)) { Debug((DEBUG_ERROR, "ssl: completed_connection", cptr->name)); completed_connection(cptr); + } else if (IsSSLStartTLSHandshake(cptr)) + { + SetUnknown(cptr); + /* STARTTLS is now complete. We could send something, but I don't know why... */ } } diff --git a/src/s_conf.c b/src/s_conf.c index 57d5f556c..d0f646351 100644 --- a/src/s_conf.c +++ b/src/s_conf.c @@ -295,6 +295,7 @@ static OperFlag _SSLFlags[] = { { SSLFLAG_FAILIFNOCERT, "fail-if-no-clientcert" }, { SSLFLAG_DONOTACCEPTSELFSIGNED, "no-self-signed" }, { SSLFLAG_VERIFYCERT, "verify-certificate" }, + { SSLFLAG_NOSTARTTLS, "no-starttls" } }; #endif diff --git a/src/s_err.c b/src/s_err.c index 61a1466dd..007b76043 100644 --- a/src/s_err.c +++ b/src/s_err.c @@ -726,7 +726,7 @@ static char *replies[] = { /* 667 */ NULL, /* 668 */ NULL, /* 669 */ NULL, -/* 670 */ NULL, /* kineircd */ +/* 670 RPL_STARTTLS */ ":%s 670 %s :STARTTLS successful, go ahead with TLS handshake", /* kineircd */ /* 671 RPL_WHOISSECURE */ ":%s 671 %s %s :%s", /* our variation on the kineircd numeric */ /* 672 */ NULL, /* ithildin */ /* 673 */ NULL, /* ithildin */ @@ -747,7 +747,7 @@ static char *replies[] = { /* 688 */ NULL, /* kineircd */ /* 689 */ NULL, /* kineircd */ /* 690 */ NULL, /* kineircd */ -/* 691 */ NULL, +/* 691 ERR_STARTTLS */ ":%s 691 %s :%s", /* 692 */ NULL, /* 693 */ NULL, /* 694 */ NULL, diff --git a/src/ssl.c b/src/ssl.c index 9063e7173..26b2d1abb 100644 --- a/src/ssl.c +++ b/src/ssl.c @@ -490,6 +490,9 @@ int ircd_SSL_write(aClient *acptr, const void *buf, int sz) case SSL_ERROR_WANT_WRITE: SET_ERRNO(P_EWOULDBLOCK); return -1; + case SSL_ERROR_WANT_READ: + SET_ERRNO(P_EWOULDBLOCK); /* is this correct? next write will block if not read, so sounds right... */ + return -1; case SSL_ERROR_SSL: if(ERRNO == EAGAIN) return -1;