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;