added basic ssl support to ircu upstream-ssl
authorpk910 <philipp@zoelle1.de>
Sun, 8 Nov 2015 18:11:56 +0000 (19:11 +0100)
committerpk910 <philipp@zoelle1.de>
Sun, 8 Nov 2015 18:31:43 +0000 (19:31 +0100)
15 files changed:
configure.in
include/client.h
include/listener.h
include/s_bsd.h
include/s_conf.h
include/ssl.h [new file with mode: 0644]
ircd/Makefile.in
ircd/ircd_lexer.l
ircd/ircd_parser.y
ircd/listener.c
ircd/s_auth.c
ircd/s_bsd.c
ircd/s_user.c
ircd/send.c
ircd/ssl.c [new file with mode: 0644]

index e07e2b34a6873e585fa279aced6ef4701e5c02d0..d4205cfa3cf51734f64d3e7680d763c0a87094e2 100644 (file)
@@ -226,6 +226,24 @@ else
 fi
 AC_SUBST(ENGINE_C)
 
+have_gnutls="no"
+AC_CHECK_LIB(gnutls, gnutls_init, [
+  AC_CHECK_HEADERS(gnutls/gnutls.h, [
+    LIBS="$LIBS -lgnutls"
+    have_gnutls="yes"
+  ])
+])
+if test x"$have_gnutls" = xno; then
+  if test x$is_win32 = xyes ; then
+    openssl_deps="-lcrypto -lgdi32"
+  else
+    openssl_deps="-lcrypto"
+  fi
+  AC_CHECK_LIB([ssl],[SSL_library_init], [
+    LIBS="$LIBS -lssl $openssl_deps"
+  ], [], $openssl_deps)
+fi
+
 dnl Now look for --enable-debug
 AC_MSG_CHECKING([whether to enable debug mode])
 AC_ARG_ENABLE([debug],
index f93a3ee425e3032a6de1a7df24f1410a4241168b..a07138348caebcfa0d82d741f7729c2a5e2007b5 100644 (file)
@@ -56,6 +56,7 @@ struct Whowas;
 struct hostent;
 struct Privs;
 struct AuthRequest;
+struct SSLConnection;
 
 /*
  * Structures
@@ -166,6 +167,7 @@ enum Flag
     FLAG_DEBUG,                     /**< send global debug/anti-hack info */
     FLAG_ACCOUNT,                   /**< account name has been set */
     FLAG_HIDDENHOST,                /**< user's host is hidden */
+    FLAG_SSLCONN,                   /**< SSL Connection */
     FLAG_LAST_FLAG,                 /**< number of flags */
     FLAG_LOCAL_UMODES = FLAG_LOCOP, /**< First local mode flag */
     FLAG_GLOBAL_UMODES = FLAG_OPER  /**< First global mode flag */
@@ -231,6 +233,7 @@ struct Connection
   struct CapSet       con_capab;     /**< Client capabilities (from us) */
   struct CapSet       con_active;    /**< Active capabilities (to us) */
   struct AuthRequest* con_auth;      /**< Auth request for client */
+  struct SSLConnection* con_ssl;     /**< SSL connection for client */
 };
 
 /** Magic constant to identify valid Connection structures. */
@@ -457,6 +460,8 @@ struct Client {
 #define con_active(con)         (&(con)->con_active)
 /** Get the auth request for the connection. */
 #define con_auth(con)          ((con)->con_auth)
+/** Get the ssl connection for the connection. */
+#define con_ssl(con)           ((con)->con_ssl)
 
 #define STAT_CONNECTING         0x001 /**< connecting to another server */
 #define STAT_HANDSHAKE          0x002 /**< pass - server sent */
@@ -581,6 +586,8 @@ struct Client {
 #define IsAccount(x)            HasFlag(x, FLAG_ACCOUNT)
 /** Return non-zero if the client has set mode +x (hidden host). */
 #define IsHiddenHost(x)         HasFlag(x, FLAG_HIDDENHOST)
+/** Return non-zero if the client has set mode +S (SSL Connection). */
+#define IsSSLConn(x)            HasFlag(x, FLAG_SSLCONN)
 /** Return non-zero if the client has an active PING request. */
 #define IsPingSent(x)           HasFlag(x, FLAG_PINGSENT)
 
@@ -627,6 +634,9 @@ struct Client {
 #define SetAccount(x)           SetFlag(x, FLAG_ACCOUNT)
 /** Mark a client as having mode +x (hidden host). */
 #define SetHiddenHost(x)        SetFlag(x, FLAG_HIDDENHOST)
+/** Mark a client as having mode +S (SSL Connection). */
+#define SetSSLConn(x)           SetFlag(x, FLAG_SSLCONN)
+
 /** Mark a client as having a pending PING. */
 #define SetPingSent(x)          SetFlag(x, FLAG_PINGSENT)
 
@@ -660,6 +670,8 @@ struct Client {
 #define ClearServNotice(x)      ClrFlag(x, FLAG_SERVNOTICE)
 /** Remove mode +x (hidden host) from the client. */
 #define ClearHiddenHost(x)      ClrFlag(x, FLAG_HIDDENHOST)
+/** Remove mode +S (SSL Connection) from the client. */
+#define ClearSSLConn(x)         ClrFlag(x, FLAG_SSLCONN)
 /** Clear the client's pending PING flag. */
 #define ClearPingSent(x)        ClrFlag(x, FLAG_PINGSENT)
 /** Clear the client's HUB flag. */
index 47dfff1645d87690f34b532961112179c559b30a..f2fcd0008600477d64094b0c0e37080b4ea8bddc 100644 (file)
@@ -40,6 +40,7 @@
 
 struct Client;
 struct StatDesc;
+struct SSLListener;
 
 enum ListenerFlag {
   /** Port is currently accepting connections. */
@@ -52,6 +53,8 @@ enum ListenerFlag {
   LISTEN_IPV4,
   /** Port listens for IPv6 connections. */
   LISTEN_IPV6,
+  /** Port listens for SSL connections. */
+  LISTEN_SSL,
   /** Sentinel for counting listener flags. */
   LISTEN_LAST_FLAG
 };
@@ -72,10 +75,12 @@ struct Listener {
   struct irc_in_addr mask;             /**< listener hostmask */
   struct Socket    socket_v4;          /**< describe IPv4 socket to event system */
   struct Socket    socket_v6;          /**< describe IPv6 socket to event system */
+  struct SSLListener* ssl_listener;    /**< ssl listener if listening for ssl connections */
 };
 
 #define listener_server(LISTENER) FlagHas(&(LISTENER)->flags, LISTEN_SERVER)
 #define listener_active(LISTENER) FlagHas(&(LISTENER)->flags, LISTEN_ACTIVE)
+#define listener_ssl(LISTENER) FlagHas(&(LISTENER)->flags, LISTEN_SSL)
 
 extern void        add_listener(int port, const char* vaddr_ip, 
                                 const char* mask,
index 8b2231dcd3c3a121741d6c267eb4acc6ae61f5bf..acb24b7c6828b12855e35930cf2bfa6e22f21fe9 100644 (file)
@@ -59,6 +59,7 @@ extern struct irc_sockaddr VirtualHost_dns_v6;
 /*
  * Proto types
  */
+extern int completed_connection(struct Client* cptr);
 extern unsigned int deliver_it(struct Client *cptr, struct MsgQ *buf);
 extern int connect_server(struct ConfItem* aconf, struct Client* by);
 extern int  net_close_unregistered_connections(struct Client* source);
index 301e9ab2f3b30e4f9f49efa4303505421c2afe9b..35ba1190323433b02318f427a511cbf33e823a73 100644 (file)
@@ -65,6 +65,7 @@ struct ConfItem
   time_t hold;        /**< Earliest time to attempt an outbound
                          connect on this ConfItem. */
   int dns_pending;    /**< A dns request is pending. */
+  int usessl;         /**< establish SSL connection */
   int flags;          /**< Additional modifiers for item. */
   int addrbits;       /**< Number of bits valid in ConfItem::address. */
   struct Privs privs; /**< Privileges for opers. */
@@ -102,6 +103,10 @@ struct LocalConf {
   char*          location1;   /**< First line of location information. */
   char*          location2;   /**< Second line of location information. */
   char*          contact;     /**< Admin contact information. */
+  
+  char*          sslcertfile; /**< SSL certificate file. */
+  char*          sslkeyfile;  /**< SSL private key file. */
+  char*          sslcafile;   /**< SSL CA file. */
 };
 
 enum {
diff --git a/include/ssl.h b/include/ssl.h
new file mode 100644 (file)
index 0000000..dc910b1
--- /dev/null
@@ -0,0 +1,97 @@
+/** @file ssl.h
+ * @brief Declarations for ssl handler.
+ * @version $Id$
+ */
+#ifndef INCLUDED_ssl_h
+#define INCLUDED_ssl_h
+#include "ircd_osdep.h"
+
+enum SSLFlag {
+  SSLFLAG_INCOMING,
+  SSLFLAG_OUTGOING,
+  SSLFLAG_READY,
+  SSLFLAG_HANDSHAKE,
+  SSLFLAG_HANDSHAKE_R,
+  SSLFLAG_HANDSHAKE_W,
+
+  SSLFLAG_LAST
+};
+
+/** Declare flagset type for ssl flags. */
+DECLARE_FLAGSET(SSLFlags, SSLFLAG_LAST);
+
+enum SSLDataType {
+  SSLData_Client
+};
+
+#if defined(HAVE_GNUTLS_GNUTLS_H)
+#include <gnutls/gnutls.h>
+
+struct SSLConnection {
+  struct SSLFlags flags;
+  gnutls_session_t session;
+  gnutls_certificate_client_credentials credentials;
+};
+
+struct SSLListener {
+  struct SSLFlags flags;
+  gnutls_priority_t priority;
+  gnutls_certificate_credentials_t credentials;
+};
+
+#elif defined(HAVE_OPENSSL_SSL_H)
+#include <openssl/rand.h>
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+
+struct SSLConnection {
+  struct SSLFlags flags;
+  SSL *session;
+};
+
+struct SSLOutConnection {
+  struct SSLFlags flags;
+  SSL *session;
+  SSL_CTX *context;
+};
+
+struct SSLListener {
+  struct SSLFlags flags;
+  
+  SSL *listener;
+  SSL_CTX *context;
+};
+
+#else
+
+struct SSLConnection {
+  struct SSLFlags flags;
+  //just unused
+};
+
+struct SSLListener {
+  struct SSLFlags flags;
+  //just unused
+};
+#endif
+
+#define ssl_handshake(x)   (FlagHas(&(x)->flags, SSLFLAG_HANDSHAKE))
+#define ssl_wantwrite(x)   (FlagHas(&(x)->flags, SSLFLAG_HANDSHAKE_W))
+#define ssl_wantread(x)   (FlagHas(&(x)->flags, SSLFLAG_HANDSHAKE_R))
+
+
+extern void ssl_free_connection(struct SSLConnection *connection);
+extern void ssl_free_listener(struct SSLListener *listener);
+
+extern struct SSLListener *ssl_create_listener();
+extern struct SSLConnection *ssl_create_connect(int fd, void *data, enum SSLDataType datatype);
+
+extern struct SSLConnection *ssl_start_handshake_listener(struct SSLListener *listener, int fd, void *data, enum SSLDataType datatype);
+extern void ssl_start_handshake_connect(struct SSLConnection *connection);
+
+IOResult ssl_recv_decrypt(struct SSLConnection *connection, char *buf, unsigned int buflen, unsigned int *len);
+IOResult ssl_send_encrypt(struct SSLConnection *connection, struct MsgQ* buf, unsigned int *count_in, unsigned int *count_out);
+IOResult ssl_send_encrypt_plain(struct SSLConnection *connection, char *buf, int len);
+extern int ssl_connection_flush(struct SSLConnection *connection);
+
+#endif /* INCLUDED_parse_h */
index 01b69caec1ad3e3eb335dd2194640176b2410b9d..f8867cdcbe90612992bdb0d19bc9716e37ad75e7 100644 (file)
@@ -210,7 +210,8 @@ IRCD_SRC = \
        s_stats.c \
        s_user.c \
        send.c \
-       uping.c \
+       ssl.c \
+  uping.c \
        userload.c \
        whocmds.c \
        whowas.c \
index 78818e81b1d555625c588f0437a5c7f4615178e8..c149a63241ecca1cecfb77957db1664a25e7aed0 100644 (file)
@@ -105,6 +105,10 @@ static struct lexer_token {
   TOKEN(AUTOCONNECT),
   TOKEN(PROGRAM),
   TOKEN(DNS),
+  TOKEN(SSL),
+  TOKEN(CERTFILE),
+  TOKEN(KEYFILE),
+  TOKEN(CAFILE),
 #undef TOKEN
   { "administrator", ADMIN },
   { "apass_opmode", TPRIV_APASS_OPMODE },
index 64a112cb9e54000097c2ef1df176debd3c6b7db9..38d725a41461ad571d6085a4f6d2c14e42940e3d 100644 (file)
@@ -175,6 +175,10 @@ static void free_slist(struct SLink **link) {
 %token PROGRAM
 %token TOK_IPV4 TOK_IPV6
 %token DNS
+%token SSL
+%token CERTFILE
+%token KEYFILE
+%token CAFILE
 /* and now a lot of privileges... */
 %token TPRIV_CHAN_LIMIT TPRIV_MODE_LCHAN TPRIV_DEOP_LCHAN TPRIV_WALK_LCHAN
 %token TPRIV_LOCAL_KILL TPRIV_REHASH TPRIV_RESTART TPRIV_DIE
@@ -199,7 +203,7 @@ static void free_slist(struct SLink **link) {
 %%
 /* Blocks in the config file... */
 blocks: blocks block | block;
-block: adminblock | generalblock | classblock | connectblock |
+block: adminblock | generalblock | classblock | connectblock | sslblock |
        uworldblock | operblock | portblock | jupeblock | clientblock |
        killblock | cruleblock | motdblock | featuresblock | quarantineblock |
        pseudoblock | iauthblock | error ';';
@@ -406,6 +410,40 @@ admincontact: CONTACT '=' QSTRING ';'
  localConf.contact = $3;
 };
 
+sslblock: SSL
+{
+  MyFree(localConf.sslcertfile);
+  MyFree(localConf.sslkeyfile);
+  MyFree(localConf.sslcafile);
+  localConf.sslcertfile = localConf.sslkeyfile = localConf.sslcafile = NULL;
+}
+'{' sslitems '}' ';'
+{
+  if (localConf.sslcertfile == NULL)
+    DupString(localConf.sslcertfile, "");
+  if (localConf.sslkeyfile == NULL)
+    DupString(localConf.sslkeyfile, "");
+  if (localConf.sslcafile == NULL)
+    DupString(localConf.sslcafile, "");
+};
+sslitems: sslitems sslitem | sslitem;
+sslitem: sslcertfile | sslkeyfile | sslcafile;
+sslcertfile: CERTFILE '=' QSTRING ';'
+{
+  MyFree(localConf.sslcertfile);
+  localConf.sslcertfile = $3;
+};
+sslkeyfile: KEYFILE '=' QSTRING ';'
+{
+  MyFree(localConf.sslkeyfile);
+  localConf.sslkeyfile = $3;
+};
+sslcafile: CAFILE '=' QSTRING ';'
+{
+  MyFree(localConf.sslcafile);
+  localConf.sslcafile = $3;
+};
+
 classblock: CLASS {
   tping = 90;
 } '{' classitems '}' ';'
@@ -722,6 +760,7 @@ portblock: PORT '{' portitems '}' ';' {
       FlagSet(&flags_here, LISTEN_IPV6);
       break;
     }
+    
     if (link->flags & 65535)
       port = link->flags & 65535;
     add_listener(port, link->value.cp, pass, &flags_here);
@@ -733,7 +772,7 @@ portblock: PORT '{' portitems '}' ';' {
   port = 0;
 };
 portitems: portitem portitems | portitem;
-portitem: portnumber | portvhost | portvhostnumber | portmask | portserver | porthidden;
+portitem: portnumber | portvhost | portvhostnumber | portmask | portserver | portssl | porthidden;
 portnumber: PORT '=' address_family NUMBER ';'
 {
   if ($4 < 1 || $4 > 65535) {
@@ -783,6 +822,14 @@ portserver: SERVER '=' YES ';'
   FlagClr(&listen_flags, LISTEN_SERVER);
 };
 
+portssl: SSL '=' YES ';'
+{
+  FlagSet(&listen_flags, LISTEN_SSL);
+} | SSL '=' NO ';'
+{
+  FlagClr(&listen_flags, LISTEN_SSL);
+};
+
 porthidden: HIDDEN '=' YES ';'
 {
   FlagSet(&listen_flags, LISTEN_HIDDEN);
index a3c9e93785e2fd3ce4a302c3a3ca99323e9d8d94..5339fd9f52896cfc4f28c280a31fe70be750564f 100644 (file)
@@ -40,6 +40,7 @@
 #include "s_misc.h"
 #include "s_stats.h"
 #include "send.h"
+#include "ssl.h"
 #include "sys.h"         /* MAXCLIENTS */
 
 /* #include <assert.h> -- Now using assert in ircd_log.h */
@@ -131,7 +132,7 @@ void show_ports(struct Client* sptr, const struct StatDesc* sd,
                 char* param)
 {
   struct Listener *listener = 0;
-  char flags[8];
+  char flags[9];
   int show_hidden = IsOper(sptr);
   int count = (IsOper(sptr) || MyUser(sptr)) ? 100 : 8;
   int port = 0;
@@ -147,6 +148,11 @@ void show_ports(struct Client* sptr, const struct StatDesc* sd,
       continue;
     len = 0;
     flags[len++] = listener_server(listener) ? 'S' : 'C';
+    
+    if (FlagHas(&listener->flags, LISTEN_SSL))
+    {
+      flags[len++] = 'E';
+    }
     if (FlagHas(&listener->flags, LISTEN_HIDDEN))
     {
       if (!show_hidden)
@@ -310,8 +316,18 @@ void add_listener(int port, const char* vhost_ip, const char* mask,
   {
     new_listener = 1;
     listener = make_listener(port, &vaddr);
+    
+    
   }
   memcpy(&listener->flags, flags, sizeof(listener->flags));
+  
+  if(FlagHas(&listener->flags, LISTEN_SSL) && !listener->ssl_listener) {
+    listener->ssl_listener = ssl_create_listener();
+  } else if(!FlagHas(&listener->flags, LISTEN_SSL) && listener->ssl_listener) {
+    ssl_free_listener(listener->ssl_listener);
+    listener->ssl_listener = NULL;
+  }
+  
   FlagSet(&listener->flags, LISTEN_ACTIVE);
   if (mask)
     ipmask_parse(mask, &listener->mask, &listener->mask_bits);
index b3e993af3137b147814043082d07d2626e9daec6..b2102ad92835ee2fd71edf23e6b2895a1e747db5 100644 (file)
@@ -141,7 +141,10 @@ typedef enum {
 
 /** Sends response \a r (from #ReportType) to client \a c. */
 #define sendheader(c, r) \
-   send(cli_fd(c), HeaderMessages[(r)].message, HeaderMessages[(r)].length, 0)
+   if(cli_connect(c)->con_ssl) \
+     ssl_send_encrypt_plain(cli_connect(c)->con_ssl, HeaderMessages[(r)].message, HeaderMessages[(r)].length); \
+   else \
+     send(cli_fd(c), HeaderMessages[(r)].message, HeaderMessages[(r)].length, 0)
 
 /** Enumeration of IAuth connection flags. */
 enum IAuthFlag
index 7e3205af764cf2f3d72694d4d54363cb3b262f0f..5fd6d9193eb1ed0e1d55fd2727754abfcb05ee4a 100644 (file)
@@ -53,6 +53,7 @@
 #include "s_misc.h"
 #include "s_user.h"
 #include "send.h"
+#include "ssl.h"
 #include "struct.h"
 #include "sys.h"
 #include "uping.h"
@@ -248,6 +249,7 @@ static int connect_inet(struct ConfItem* aconf, struct Client* cptr)
     cli_fd(cptr) = -1;
     return 0;
   }
+  
   if (!socket_add(&(cli_socket(cptr)), client_sock_callback,
                  (void*) cli_connect(cptr),
                  (result == IO_SUCCESS) ? SS_CONNECTED : SS_CONNECTING,
@@ -258,6 +260,21 @@ static int connect_inet(struct ConfItem* aconf, struct Client* cptr)
     cli_fd(cptr) = -1;
     return 0;
   }
+  
+  if(aconf->usessl) {
+    struct SSLConnection *ssl = ssl_create_connect(cli_fd(cptr), cptr, SSLData_Client);
+    cli_connect(cptr)->con_ssl = ssl;
+    if(ssl_handshake(ssl)) {
+      unsigned int events = 0;
+      if(ssl_wantread(ssl))
+        events |= SOCK_EVENT_READABLE;
+      if(ssl_wantwrite(ssl))
+        events |= SOCK_EVENT_WRITABLE;
+      socket_events(&(cli_socket(cptr)), SOCK_ACTION_SET | events);
+      result = IO_BLOCKED;
+    }
+  }
+  
   cli_freeflag(cptr) |= FREEFLAG_SOCKET;
   return 1;
 }
@@ -274,9 +291,16 @@ unsigned int deliver_it(struct Client *cptr, struct MsgQ *buf)
 {
   unsigned int bytes_written = 0;
   unsigned int bytes_count = 0;
+  IOResult result;
   assert(0 != cptr);
 
-  switch (os_sendv_nonb(cli_fd(cptr), buf, &bytes_count, &bytes_written)) {
+  if(cli_connect(cptr)->con_ssl) {
+    result = ssl_send_encrypt(cli_connect(cptr)->con_ssl, buf, &bytes_count, &bytes_written);
+  } else {
+    result = os_sendv_nonb(cli_fd(cptr), buf, &bytes_count, &bytes_written);
+  }
+  
+  switch (result) {
   case IO_SUCCESS:
     ClrFlag(cptr, FLAG_BLOCKED);
 
@@ -302,7 +326,7 @@ unsigned int deliver_it(struct Client *cptr, struct MsgQ *buf)
  * @param cptr Client to which we have connected, with all ConfItem structs attached.
  * @return Zero on failure (caller should exit_client()), non-zero on success.
  */
-static int completed_connection(struct Client* cptr)
+int completed_connection(struct Client* cptr)
 {
   struct ConfItem *aconf;
   time_t newts;
@@ -403,6 +427,11 @@ void close_connection(struct Client *cptr)
   else
     ServerStats->is_ni++;
 
+  if(cli_connect(cptr)->con_ssl) {
+    ssl_free_connection(cli_connect(cptr)->con_ssl);
+    cli_connect(cptr)->con_ssl = NULL;
+  }
+  
   if (-1 < cli_fd(cptr)) {
     flush_connections(cptr);
     LocalClientArray[cli_fd(cptr)] = 0;
@@ -540,8 +569,20 @@ void add_connection(struct Listener* listener, int fd) {
   ++listener->ref_count;
 
   Count_newunknown(UserStats);
-  /* if we've made it this far we can put the client on the auth query pile */
-  start_auth(new_client);
+  
+  if(listener_ssl(listener)) {
+    struct Connection* con = cli_connect(new_client);
+    con->con_ssl = ssl_start_handshake_listener(listener->ssl_listener, fd, new_client, SSLData_Client);
+    unsigned int events = 0;
+    if(ssl_wantread(con->con_ssl))
+      events |= SOCK_EVENT_READABLE;
+    if(ssl_wantwrite(con->con_ssl))
+      events |= SOCK_EVENT_WRITABLE;
+    socket_events(&(cli_socket(new_client)), SOCK_ACTION_SET | events);
+  } else {
+    /* if we've made it this far we can put the client on the auth query pile */
+    start_auth(new_client);
+  }
 }
 
 /** Determines whether to tell the events engine we're interested in
@@ -577,7 +618,16 @@ static int read_packet(struct Client *cptr, int socket_ready)
   if (socket_ready &&
       !(IsUser(cptr) &&
        DBufLength(&(cli_recvQ(cptr))) > feature_int(FEAT_CLIENT_FLOOD))) {
-    switch (os_recv_nonb(cli_fd(cptr), readbuf, sizeof(readbuf), &length)) {
+    
+    /* Handle SSL Sockets
+    */
+    int recvret;
+    if(cli_connect(cptr)->con_ssl) {
+      recvret = ssl_recv_decrypt(cli_connect(cptr)->con_ssl, readbuf, sizeof(readbuf), &length);
+    } else {
+      recvret = os_recv_nonb(cli_fd(cptr), readbuf, sizeof(readbuf), &length);
+    }
+    switch (recvret) {
     case IO_SUCCESS:
       if (length)
       {
@@ -596,7 +646,7 @@ static int read_packet(struct Client *cptr, int socket_ready)
       return 0;
     }
   }
-
+  
   /*
    * For server connections, we process as many as we can without
    * worrying about the time of day or anything :)
@@ -855,7 +905,10 @@ static void client_sock_callback(struct Event* ev)
     break;
 
   case ET_CONNECT: /* socket connection completed */
-    if (!completed_connection(cptr) || IsDead(cptr))
+    if(cli_connect(cptr)->con_ssl) {
+      ssl_start_handshake_connect(cli_connect(cptr)->con_ssl);
+    }
+    else if (!completed_connection(cptr) || IsDead(cptr))
       fallback = cli_info(cptr);
     break;
 
index 1f0e0d92ddd990ff543c0445af741d7d72464555..632e185b5348bfa146b473cf193342ff7743f448 100644 (file)
@@ -57,6 +57,7 @@
 #include "s_misc.h"
 #include "s_serv.h" /* max_client_count */
 #include "send.h"
+#include "ssl.h"
 #include "struct.h"
 #include "supported.h"
 #include "sys.h"
@@ -395,6 +396,10 @@ int register_user(struct Client *cptr, struct Client *sptr)
                            cli_info(sptr), NumNick(cptr) /* two %s's */);
 
     IPcheck_connect_succeeded(sptr);
+    
+    if(cli_connect(sptr)->con_ssl) {
+      SetSSLConn(sptr);
+    }
   }
   else {
     struct Client *acptr = user->server;
@@ -499,7 +504,8 @@ static const struct UserMode {
   { FLAG_CHSERV,      'k' },
   { FLAG_DEBUG,       'g' },
   { FLAG_ACCOUNT,     'r' },
-  { FLAG_HIDDENHOST,  'x' }
+  { FLAG_HIDDENHOST,  'x' },
+  { FLAG_SSLCONN,     'S' }
 };
 
 /** Length of #userModeList. */
@@ -1076,6 +1082,12 @@ int set_user_mode(struct Client *cptr, struct Client *sptr, int parc,
       case 'x':
         if (what == MODE_ADD)
          do_host_hiding = 1;
+      case 'S':
+        if (what == MODE_ADD)
+          SetSSLConn(sptr);
+        else
+          ClearSSLConn(sptr);
+        break;
        break;
       case 'r':
        if (*(p + 1) && (what == MODE_ADD)) {
@@ -1102,6 +1114,10 @@ int set_user_mode(struct Client *cptr, struct Client *sptr, int parc,
       ClearLocOp(sptr);
     if (!FlagHas(&setflags, FLAG_ACCOUNT) && IsAccount(sptr))
       ClrFlag(sptr, FLAG_ACCOUNT);
+    if (!FlagHas(&setflags, FLAG_SSLCONN) && IsSSLConn(sptr))
+      ClrFlag(sptr, FLAG_SSLCONN);
+    else if (FlagHas(&setflags, FLAG_SSLCONN) && !IsSSLConn(sptr))
+      SetFlag(sptr, FLAG_SSLCONN);
     /*
      * new umode; servers can set it, local users cannot;
      * prevents users from /kick'ing or /mode -o'ing
index 1afbaec34ccf12c7a2ebfb791c8aa704f9994729..b9309b85337fb67f7a5a41f3459c759721648ea3 100644 (file)
@@ -152,7 +152,10 @@ kill_highest_sendq(int servers_too)
  */
 void flush_connections(struct Client* cptr)
 {
+  struct SSLConnection *ssl = cli_connect(cptr)->con_ssl;
   if (cptr) {
+    if(ssl)
+      ssl_connection_flush(ssl);
     send_queued(cptr);
   }
   else {
@@ -161,6 +164,7 @@ void flush_connections(struct Client* cptr)
       assert(0 < MsgQLength(&(con_sendQ(con))));
       send_queued(con_client(con));
     }
+    ssl_connection_flush(NULL);
   }
 }
 
diff --git a/ircd/ssl.c b/ircd/ssl.c
new file mode 100644 (file)
index 0000000..e407bd4
--- /dev/null
@@ -0,0 +1,416 @@
+/*
+ * IRC - Internet Relay Chat, ircd/ssl.c
+ * Copyright (C) 2015 pk910 (Philipp Kreil)
+ *
+ * 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.
+ */
+/** @file
+ * @brief Implementation of functions for handling local clients.
+ * @version $Id$
+ */
+#include "config.h"
+
+#include "client.h"
+#include "ssl.h"
+#include "class.h"
+#include "ircd.h"
+#include "ircd_features.h"
+#include "ircd_log.h"
+#include "ircd_reply.h"
+#include "list.h"
+#include "msgq.h"
+#include "numeric.h"
+#include "s_conf.h"
+#include "s_debug.h"
+#include "send.h"
+#include "struct.h"
+
+/* #include <assert.h> -- Now using assert in ircd_log.h */
+#include <string.h>
+
+#ifndef IOV_MAX
+#define IOV_MAX 16     /**< minimum required length of an iovec array */
+#endif
+
+#if defined(HAVE_OPENSSL_SSL_H)
+
+static struct SSLPendingConections {
+  struct SSLConnection *connection;
+  struct SSLPendingConections *next;
+  
+  void *data;
+  enum SSLDataType datatype;
+};
+
+struct SSLPendingConections *firstPendingConection = NULL;
+int ssl_is_initialized = 0;
+
+static void ssl_init() {
+  if(ssl_is_initialized)
+    return;
+  ssl_is_initialized = 1;
+       SSL_library_init();
+       OpenSSL_add_all_algorithms(); /* load & register all cryptos, etc. */
+       SSL_load_error_strings();
+}
+
+void ssl_free_connection(struct SSLConnection *connection) {
+  SSL_CTX *context = NULL;
+  if(FlagHas(&connection->flags, SSLFLAG_OUTGOING)) {
+    struct SSLOutConnection *outconn = (struct SSLOutConnection *)connection;
+    context = outconn->context;
+  }
+  SSL_shutdown(connection->session);
+  SSL_free(connection->session);
+  if(context)
+    SSL_CTX_free(context);
+  free(connection);
+}
+
+void ssl_free_listener(struct SSLListener *listener) {
+  SSL_CTX_free(listener->context);
+  free(listener);
+}
+
+static void ssl_handshake_completed(struct SSLConnection *connection, int success) {
+  struct SSLPendingConections *pending, *lastPending = NULL;
+  for(pending = firstPendingConection; pending; pending = pending->next) {
+    if(pending->connection == connection) {
+      if(lastPending)
+        lastPending->next = pending->next;
+      else
+        firstPendingConection = pending->next;
+      switch(pending->datatype) {
+        case SSLData_Client: {
+            struct Client *cptr = (struct Client *) pending->data;
+            if(success) {
+              if(FlagHas(&connection->flags, SSLFLAG_INCOMING))
+                start_auth(cptr);
+              else if(!completed_connection(cptr))
+                exit_client_msg(cptr, cptr, &me, "Registration failed.");
+            } else
+              exit_client_msg(cptr, cptr, &me, "SSL Handshake failed.");
+          }
+          break;
+      }
+      free(pending);
+    }
+    lastPending = pending;
+  }
+}
+
+static int ssl_handshake_outgoing(struct SSLConnection *connection) {
+  int ret = SSL_do_handshake(connection->session);
+  FlagClr(&connection->flags, SSLFLAG_HANDSHAKE_R);
+  FlagClr(&connection->flags, SSLFLAG_HANDSHAKE_W);
+  
+       switch(SSL_get_error(connection->session, ret)) {
+               case SSL_ERROR_NONE:
+                       FlagClr(&connection->flags, SSLFLAG_HANDSHAKE);
+      FlagSet(&connection->flags, SSLFLAG_READY);
+      
+      ssl_handshake_completed(connection, 1);
+                       break;
+               case SSL_ERROR_WANT_READ:
+                       FlagSet(&connection->flags, SSLFLAG_HANDSHAKE_R);
+                       break;
+               case SSL_ERROR_WANT_WRITE:
+                       FlagSet(&connection->flags, SSLFLAG_HANDSHAKE_W);
+                       break;
+               default:
+                       
+                       break;
+       }
+}
+
+struct SSLConnection *ssl_create_connect(int fd, void *data, enum SSLDataType datatype) {
+  struct SSLOutConnection *connection = malloc(sizeof(*connection));
+  struct SSLConnection *sslconn = (struct SSLConnection *)connection;
+  struct SSLPendingConections *pending = NULL;
+  
+  if(!connection)
+    return NULL;
+  
+  if(!ssl_is_initialized)
+    ssl_init();
+  
+  connection->context = SSL_CTX_new(SSLv23_client_method());
+       if(!connection->context) {
+               goto ssl_create_connect_failed;
+       }
+       connection->session = SSL_new(connection->context);
+       if(!connection->session) {
+               goto ssl_create_connect_failed;
+       }
+       if(!SSL_set_fd(connection->session, fd)) {
+               goto ssl_create_connect_failed;
+       }
+       SSL_set_connect_state(connection->session);
+       FlagSet(&connection->flags, SSLFLAG_OUTGOING);
+  FlagSet(&connection->flags, SSLFLAG_HANDSHAKE);
+  
+  pending = malloc(sizeof(*pending));
+  if(!pending) {
+    goto ssl_create_connect_failed;
+  }
+  pending->connection = connection;
+  pending->next = firstPendingConection;
+  firstPendingConection = pending;
+  
+  pending->data = data;
+  pending->datatype = datatype;
+  
+       return sslconn;
+ssl_create_connect_failed:
+       free(connection);
+       return NULL;
+}
+
+void ssl_start_handshake_connect(struct SSLConnection *connection) {
+  ssl_handshake_outgoing(connection);
+}
+
+struct SSLListener *ssl_create_listener() {
+  if(!ssl_is_initialized)
+    ssl_init();
+  
+  struct SSLListener *listener = calloc(1, sizeof(*listener));
+  listener->context = SSL_CTX_new(SSLv23_server_method());
+  if(!listener->context) {
+    goto ssl_create_listener_failed;
+  }
+  
+  char *certfile = conf_get_local()->sslcertfile;
+  char *keyfile = conf_get_local()->sslkeyfile;
+  char *cafile = conf_get_local()->sslcafile;
+  
+  if(!certfile) {
+    goto ssl_create_listener_failed;
+  }
+  if(!keyfile) {
+    keyfile = certfile;
+  }
+  
+  /* load certificate */
+  if(SSL_CTX_use_certificate_file(listener->context, certfile, SSL_FILETYPE_PEM) <= 0) {
+    goto ssl_create_listener_failed;
+  }
+  /* load keyfile */
+  if(SSL_CTX_use_PrivateKey_file(listener->context, keyfile, SSL_FILETYPE_PEM) <= 0) {
+    goto ssl_create_listener_failed;
+  }
+  /* check certificate and keyfile */
+  if(!SSL_CTX_check_private_key(listener->context)) {
+    goto ssl_create_listener_failed;
+  }
+  /* load cafile */
+  if(cafile && cafile[0] && SSL_CTX_load_verify_locations(listener->context, cafile, NULL) <= 0) {
+    goto ssl_create_listener_failed;
+  }
+  FlagSet(&listener->flags, SSLFLAG_READY);
+  return listener;
+ssl_create_listener_failed:
+  free(listener);
+  return NULL;
+}
+
+static int ssl_handshake_incoming(struct SSLConnection *connection) {
+  int result = SSL_accept(connection->session);
+       FlagClr(&connection->flags, SSLFLAG_HANDSHAKE_R);
+  FlagClr(&connection->flags, SSLFLAG_HANDSHAKE_W);
+       switch(SSL_get_error(connection->session, result)) {
+               case SSL_ERROR_NONE:
+                       FlagClr(&connection->flags, SSLFLAG_HANDSHAKE);
+      FlagSet(&connection->flags, SSLFLAG_READY);
+      
+      ssl_handshake_completed(connection, 1);
+                       return 0;
+               case SSL_ERROR_WANT_READ:
+                       FlagSet(&connection->flags, SSLFLAG_HANDSHAKE_R);
+      return 1;
+               case SSL_ERROR_WANT_WRITE:
+      FlagSet(&connection->flags, SSLFLAG_HANDSHAKE_W);
+                       return 1;
+               default:
+      //unset connection! 
+      //Handshake error!
+      ssl_handshake_completed(connection, 0);
+                       return 0;
+       }
+  return 0;
+}
+
+struct SSLConnection *ssl_start_handshake_listener(struct SSLListener *listener, int fd, void *data, enum SSLDataType datatype) {
+  if(!listener)
+    return NULL;
+  struct SSLPendingConections *pending = NULL;
+  struct SSLConnection *connection = malloc(sizeof(*connection));
+  connection->session = SSL_new(listener->context);
+  if(!connection->session) {
+    goto ssl_start_handshake_listener_failed;
+  }
+  if(!SSL_set_fd(connection->session, fd)) {
+    goto ssl_start_handshake_listener_failed;
+  }
+  FlagSet(&connection->flags, SSLFLAG_INCOMING);
+  FlagSet(&connection->flags, SSLFLAG_HANDSHAKE);
+  FlagSet(&connection->flags, SSLFLAG_HANDSHAKE_R);
+  
+  pending = malloc(sizeof(*pending));
+  if(!pending) {
+    goto ssl_start_handshake_listener_failed;
+  }
+  pending->connection = connection;
+  pending->next = firstPendingConection;
+  firstPendingConection = pending;
+  
+  pending->data = data;
+  pending->datatype = datatype;
+  
+  ssl_handshake_incoming(connection);
+       return connection;
+ssl_start_handshake_listener_failed:
+       free(connection);
+       return NULL;
+}
+
+IOResult ssl_recv_decrypt(struct SSLConnection *connection, char *buf, unsigned int buflen, unsigned int *len) {
+  if(FlagHas(&connection->flags, SSLFLAG_HANDSHAKE)) {
+    if(FlagHas(&connection->flags, SSLFLAG_INCOMING)) {
+      ssl_handshake_incoming(connection);
+      return IO_BLOCKED;
+    }
+    if(FlagHas(&connection->flags, SSLFLAG_OUTGOING)) {
+      ssl_handshake_outgoing(connection);
+      return IO_BLOCKED;
+    }
+  }
+  
+  *len = SSL_read(connection->session, buf, buflen);
+  FlagClr(&connection->flags, SSLFLAG_HANDSHAKE_R);
+  int err = SSL_get_error(connection->session, *len);
+  switch(err) {
+    case SSL_ERROR_NONE:
+       return IO_SUCCESS;
+               case SSL_ERROR_ZERO_RETURN:
+                 return IO_FAILURE;
+               case SSL_ERROR_WANT_READ:
+      FlagSet(&connection->flags, SSLFLAG_HANDSHAKE_R);
+                       return IO_BLOCKED;
+               case SSL_ERROR_WANT_WRITE:
+                       FlagSet(&connection->flags, SSLFLAG_HANDSHAKE_W);
+                       return IO_BLOCKED;
+               case SSL_ERROR_SYSCALL:
+                       return IO_FAILURE;
+               default:
+      return IO_FAILURE;
+  }
+}
+
+static ssize_t ssl_writev(SSL *ssl, const struct iovec *vector, int count) {
+  char *buffer;
+  register char *bp;
+  size_t bytes, to_copy;
+  int i;
+
+  /* Find the total number of bytes to be written.  */
+  bytes = 0;
+  for (i = 0; i < count; ++i)
+    bytes += vector[i].iov_len;
+
+  /* Allocate a temporary buffer to hold the data.  */
+  buffer = (char *) alloca (bytes);
+
+  /* Copy the data into BUFFER.  */
+  to_copy = bytes;
+  bp = buffer;
+  for (i = 0; i < count; ++i) {
+    size_t copy = ((vector[i].iov_len) > (to_copy) ? (to_copy) : (vector[i].iov_len));
+    memcpy ((void *) bp, (void *) vector[i].iov_base, copy);
+    bp += copy;
+    to_copy -= copy;
+    if (to_copy == 0)
+      break;
+  }
+  return SSL_write(ssl, buffer, bytes);
+}
+
+IOResult ssl_send_encrypt_plain(struct SSLConnection *connection, char* buf, int len) {
+  return SSL_write(connection->session, buf, len);
+}
+
+IOResult ssl_send_encrypt(struct SSLConnection *connection, struct MsgQ* buf, unsigned int *count_in, unsigned int *count_out) {
+  int res;
+  int count;
+  struct iovec iov[IOV_MAX];
+
+  assert(0 != buf);
+  assert(0 != count_in);
+  assert(0 != count_out);
+
+  *count_in = 0;
+  count = msgq_mapiov(buf, iov, IOV_MAX, count_in);
+  res = ssl_writev(connection->session, iov, count);
+
+  switch(SSL_get_error(connection->session, res)) {
+    case SSL_ERROR_NONE:
+               case SSL_ERROR_ZERO_RETURN:
+      *count_out = (unsigned) res;
+      return IO_SUCCESS;
+    case SSL_ERROR_WANT_READ:
+      FlagSet(&connection->flags, SSLFLAG_HANDSHAKE_R);
+                       return IO_BLOCKED;
+    case SSL_ERROR_WANT_WRITE:
+      FlagSet(&connection->flags, SSLFLAG_HANDSHAKE_W);
+                       return IO_BLOCKED;
+    default:
+      *count_out = 0;
+      return IO_FAILURE;
+  }
+}
+
+int ssl_connection_flush(struct SSLConnection *connection) {
+  if(connection) {
+    if(ssl_handshake(connection)) {
+      if(FlagHas(&connection->flags, SSLFLAG_INCOMING)) {
+        return ssl_handshake_incoming(connection);
+      }
+      if(FlagHas(&connection->flags, SSLFLAG_OUTGOING)) {
+        return ssl_handshake_outgoing(connection);
+      }
+    }
+  } else {
+    struct SSLPendingConections *curr, *last = NULL, *next;
+    for(curr = firstPendingConection; curr; curr = next) {
+      next = curr->next;
+      if(!ssl_connection_flush(curr->connection)) {
+        // connection is already in auth process here, curr is freed!
+        continue;
+      }
+      last = curr;
+    }
+  }
+  return 0;
+}
+
+#else
+void ssl_free_connection(struct SSLConnection *connection) {}
+void ssl_free_listener(struct SSLConnection *listener) {}
+struct SSLListener *ssl_create_listener() { return NULL; }
+struct SSLConnection *ssl_start_handshake_listener(struct SSLListener *listener, int fd, void *data, enum SSLDataType datatype) { return NULL; }
+IOResult ssl_recv_decrypt(struct SSLConnection *connection, char *buf, int *len) { return IO_FAILURE; }
+int ssl_connection_flush(struct SSLConnection *connection) { return 0; };
+#endif
+