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],
struct hostent;
struct Privs;
struct AuthRequest;
+struct SSLConnection;
/*
* Structures
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 */
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. */
#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 */
#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)
#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)
#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. */
struct Client;
struct StatDesc;
+struct SSLListener;
enum ListenerFlag {
/** Port is currently accepting connections. */
LISTEN_IPV4,
/** Port listens for IPv6 connections. */
LISTEN_IPV6,
+ /** Port listens for SSL connections. */
+ LISTEN_SSL,
/** Sentinel for counting listener flags. */
LISTEN_LAST_FLAG
};
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,
/*
* 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);
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. */
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 {
--- /dev/null
+/** @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 */
s_stats.c \
s_user.c \
send.c \
- uping.c \
+ ssl.c \
+ uping.c \
userload.c \
whocmds.c \
whowas.c \
TOKEN(AUTOCONNECT),
TOKEN(PROGRAM),
TOKEN(DNS),
+ TOKEN(SSL),
+ TOKEN(CERTFILE),
+ TOKEN(KEYFILE),
+ TOKEN(CAFILE),
#undef TOKEN
{ "administrator", ADMIN },
{ "apass_opmode", TPRIV_APASS_OPMODE },
%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
%%
/* 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 ';';
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 '}' ';'
FlagSet(&flags_here, LISTEN_IPV6);
break;
}
+
if (link->flags & 65535)
port = link->flags & 65535;
add_listener(port, link->value.cp, pass, &flags_here);
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) {
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);
#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 */
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;
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)
{
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);
/** 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
#include "s_misc.h"
#include "s_user.h"
#include "send.h"
+#include "ssl.h"
#include "struct.h"
#include "sys.h"
#include "uping.h"
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,
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;
}
{
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);
* @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;
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;
++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
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)
{
return 0;
}
}
-
+
/*
* For server connections, we process as many as we can without
* worrying about the time of day or anything :)
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;
#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"
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;
{ FLAG_CHSERV, 'k' },
{ FLAG_DEBUG, 'g' },
{ FLAG_ACCOUNT, 'r' },
- { FLAG_HIDDENHOST, 'x' }
+ { FLAG_HIDDENHOST, 'x' },
+ { FLAG_SSLCONN, 'S' }
};
/** Length of #userModeList. */
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)) {
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
*/
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 {
assert(0 < MsgQLength(&(con_sendQ(con))));
send_queued(con_client(con));
}
+ ssl_connection_flush(NULL);
}
}
--- /dev/null
+/*
+ * 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
+