From 5a0a71de03f447803ec94c8e208984e1af0260c8 Mon Sep 17 00:00:00 2001
From: Bram Matthys
Date: Sun, 6 Dec 2009 16:52:52 +0000
Subject: [PATCH] - 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()
---
Changes | 8 +++
doc/unreal32docs.html | 2 +
include/struct.h | 7 ++-
makefile.win32 | 7 ++-
src/modules/Makefile.in | 11 +++-
src/modules/m_starttls.c | 128 +++++++++++++++++++++++++++++++++++++++
src/s_bsd.c | 15 +++--
src/s_conf.c | 1 +
src/s_err.c | 4 +-
src/ssl.c | 3 +
10 files changed, 176 insertions(+), 10 deletions(-)
create mode 100644 src/modules/m_starttls.c
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;