diff --git a/Makefile b/Makefile index 3f16378..d095d14 100755 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ CC=gcc CFLAGS=-g -std=gnu99 -c -lconfig -ldl -I./lib -BINFLAGS=-g -rdynamic -ldl -lconfig +BINFLAGS=-g -rdynamic -ldl -lconfig -lssl -lcrypto SRC=./src OBJ=./build OBJECTS=$(OBJ)/*.o diff --git a/lib/irc.h b/lib/irc.h index 85177d8..87ffbf6 100755 --- a/lib/irc.h +++ b/lib/irc.h @@ -18,10 +18,12 @@ #include #else #include +#include +#include #endif -#define OUTBUF_SIZE 60000 -#define INBUF_SIZE 60000 +#define OUTBUF_SIZE 1200000 +#define INBUF_SIZE 1200000 struct irc_conn { @@ -29,6 +31,9 @@ struct irc_conn SOCKET srv_fd; #else FILE *srv_fd; + int ssl_fd; + SSL *ssl; + SSL_CTX *ctx; #endif char nick[50]; @@ -38,6 +43,14 @@ struct irc_conn char port[5]; char real_name[512]; +#ifdef _WIN32 + BOOL use_ssl; + BOOL verify_ssl; +#else + bool use_ssl; + bool verify_ssl; +#endif + char db_file[256]; char log_file[256]; struct db_table *db; diff --git a/lib/util.h b/lib/util.h index 8776554..79774d9 100755 --- a/lib/util.h +++ b/lib/util.h @@ -9,6 +9,16 @@ #include "logger.h" +#define DEBUG 1 + +#ifdef DEBUG +#define BREAKPOINT() asm("int $3") +#else +#define BREAKPOINT() +#endif + + + #ifdef _WIN32 #define true TRUE #define false FALSE @@ -34,7 +44,14 @@ MY_API void strlcpy(char *to, const char *from, int len); MY_API char *basename(char *path); #endif +// skip - skip over characters in a string +// s - the string to skip over +// c - the character to skip over +// +// returns a pointer to the first non-matching character MY_API char *skip(char *s, char c); + +// trim - remove leading and trailing whitespace from a string MY_API void trim(char *s); #endif diff --git a/src/config.c b/src/config.c index 4c99f95..307eab4 100755 --- a/src/config.c +++ b/src/config.c @@ -13,6 +13,7 @@ struct irc_conn read_config(struct irc_conn bot, char *file) const config_setting_t *autoload; const char *base = (const char*)malloc(sizeof(char) * 1024); const char *mod = NULL; + int boolbase; char *modpath = (char *)malloc(sizeof(char) * 500); @@ -54,6 +55,13 @@ struct irc_conn read_config(struct irc_conn bot, char *file) if (config_lookup_string(cf, "bot.log", &base)) strlcpy(bot.log_file, base, sizeof bot.log_file); + if (config_lookup_bool(cf, "server.ssl", &boolbase)) + bot.use_ssl = boolbase; + + if (config_lookup_bool(cf, "server.ssl_verify", &boolbase)) + bot.verify_ssl = boolbase; + + config_destroy(cf); return bot; diff --git a/src/irc.c b/src/irc.c index b7762b8..98a3a1e 100755 --- a/src/irc.c +++ b/src/irc.c @@ -21,6 +21,7 @@ #ifdef _WIN32 #include #include +#include #define FDOPEN _fdopen #define SETBUF setbuf #else @@ -29,6 +30,9 @@ #include #include #include +#include +#include +#include #define FDOPEN fdopen #define SETBUF setbuf #endif @@ -41,6 +45,43 @@ void irc_connect(struct irc_conn *bot) struct sockaddr_in server; struct hostent *host; + // SChannel stuff + SCHANNEL_CRED schannelCred; + CtxtHandle ctxtHandle; + SecBufferDesc outBufferDesc; + SecBuffer outBuffer; + SECURITY_STATUS secStatus; + DWORD dwSSPIFlags; + + if (bot->use_ssl) + { + ZeroMemory(&schannelCred, sizeof(schannelCred)); + ZeroMemory(&ctxtHandle, sizeof(ctxtHandle)); + ZeroMemory(&outBufferDesc, sizeof(outBufferDesc)); + ZeroMemory(&outBuffer, sizeof(outBuffer)); + + // init outbufferdesc and outbuffer + outBufferDesc.ulVersion = SECBUFFER_VERSION; + outBufferDesc.cBuffers = 1; + outBufferDesc.pBuffers = &outBuffer; + outBuffer.BufferType = SECBUFFER_TOKEN; + outBuffer.cbBuffer = 0; + outBuffer.pvBuffer = NULL; + + + // setup the credentials + schannelCred.dwVersion = SCHANNEL_CRED_VERSION; + schannelCred.grbitEnabledProtocols = SP_PROT_TLS1_2_CLIENT; + schannelCred.dwFlags = SCH_CRED_NO_DEFAULT_CREDS | SCH_CRED_NO_SYSTEM_MAPPER; + schannelCred.cCreds = 1; + schannelCred.paCred = &bot->cred; + schannelCred.hRootStore = NULL; + schannelCred.dwMinimumCipherStrength = 128; + schannelCred.dwMaximumCipherStrength = 128; + schannelCred.dwSessionLifespan = 0; + } + + sprintf(titlebuf, "xbot [connecting]: %s:%s", bot->host, bot->port); SetConsoleTitle(titlebuf); @@ -89,6 +130,26 @@ void irc_connect(struct irc_conn *bot) return; } + if (bot->use_ssl) + { + // perform the handshake + secStatus = InitalizeSecurityContet(NULL, NULL, NULL, dwSSPIFlags, 0, 0, NULL, 0, &ctxtHandle, &outBufferDesc, NULL, NULL); + if (secStatus != SEC_I_CONTINUE_NEEDED) + { + eprint("Error: Handshake failed\n"); + exit(EXIT_FAILURE); + } + + + // send the handshake + if (send(bot->srv_fd, outBuffer.pvBuffer, outBuffer.cbBuffer, 0) == SOCKET_ERROR) + { + eprint("Error: Handshake failed\n"); + exit(EXIT_FAILURE); + } + + } + sprintf(titlebuf, "xbot [connected]: %s:%s", bot->host, bot->port); SetConsoleTitle(titlebuf); #else @@ -96,6 +157,32 @@ void irc_connect(struct irc_conn *bot) struct addrinfo hints; struct addrinfo *res, *r; + if (bot->use_ssl) + { + SSL_library_init(); + SSL_load_error_strings(); + bot->ctx = SSL_CTX_new(SSLv23_client_method()); + if (bot->ctx == NULL) + { + eprint("Error: Cannot create SSL context\n"); + } + + if (bot->verify_ssl) + { + SSL_CTX_set_verify(bot->ctx, SSL_VERIFY_PEER, NULL); + } + else + { + SSL_CTX_set_verify(bot->ctx, SSL_VERIFY_NONE, NULL); + } + + if ((bot->ssl = SSL_new(bot->ctx)) == NULL) + { + eprint("Error: Cannot create SSL object\n"); + } + } + + memset(&hints, 0, sizeof hints); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; @@ -126,8 +213,24 @@ void irc_connect(struct irc_conn *bot) eprint("[IRC] Error: Cannot connect to host '%s'\n", bot->host); } - xlog("[IRC] Connected!\n"); + + if (bot->use_ssl) + { + if (SSL_set_fd(bot->ssl, srv_fd) == 0) + { + eprint("Error: Cannot set SSL file descriptor\n"); + } + + if (SSL_connect(bot->ssl) != 1) + { + eprint("Error: Cannot connect to SSL server\n"); + } + + bot->ssl_fd = srv_fd; + } + bot->srv_fd = FDOPEN(srv_fd, "r+"); + xlog("[IRC] Connected!\n"); #endif } @@ -137,8 +240,11 @@ void irc_auth(struct irc_conn *bot) irc_raw(bot, "USER %s \" %s :%s", bot->user, bot->host, bot->real_name); #ifndef _WIN32 - fflush(bot->srv_fd); - SETBUF(bot->srv_fd, NULL); + if (!bot->use_ssl) + { + fflush(bot->srv_fd); + SETBUF(bot->srv_fd, NULL); + } #endif } @@ -182,7 +288,18 @@ void irc_raw(struct irc_conn *bot, char *fmt, ...) sprintf(outbuf, "%s\r\n", bot->out); send(bot->srv_fd, outbuf, strlen(outbuf), 0); #else - fprintf(bot->srv_fd, "%s\r\n", bot->out); + if (bot->use_ssl) + { + sprintf(outbuf, "%s\r\n", bot->out); + if (SSL_write(bot->ssl, outbuf, strlen(outbuf)) <= 0) + { + eprint("Error: Cannot write to SSL server\n"); + } + } + else + { + fprintf(bot->srv_fd, "%s\r\n", bot->out); + } #endif } diff --git a/src/main.c b/src/main.c index a54be9b..5b7fc87 100755 --- a/src/main.c +++ b/src/main.c @@ -32,15 +32,19 @@ static time_t trespond; int main(int argc, char **argv) { - int n; + int n = 0; fd_set rd; struct irc_conn bot; struct timeval tv; struct timeval last_ping; char conf[1024]; + unsigned long ssl_err; char *p; - int bytesRecv; + char buf[1024]; + int bytesRecv = 0; + int totalBytesRecv = 0; + #ifdef _WIN32 HICON hIcon; @@ -146,8 +150,15 @@ int main(int argc, char **argv) #ifdef _WIN32 FD_SET(bot.srv_fd, &rd); #else - FD_SET(0, &rd); - FD_SET(fileno(bot.srv_fd), &rd); + if (bot.use_ssl) + { + FD_SET(SSL_get_fd(bot.ssl), &rd); + } + else + { + FD_SET(0, &rd); + FD_SET(fileno(bot.srv_fd), &rd); + } #endif tv.tv_sec = 1; tv.tv_usec = 0; @@ -163,7 +174,7 @@ int main(int argc, char **argv) return -1; } #else - n = select(fileno(bot.srv_fd) + 1, &rd, 0, 0, &tv); + n = select(bot.use_ssl ? bot.ssl_fd + 1 : fileno(bot.srv_fd) + 1, &rd, 0, 0, &tv); if (n < 0) { if (errno == EINTR) @@ -229,16 +240,60 @@ int main(int argc, char **argv) free(p); #else - if (FD_ISSET(fileno(bot.srv_fd), &rd)) + if (FD_ISSET(bot.use_ssl ? bot.ssl_fd : fileno(bot.srv_fd), &rd)) { - if (fgets(bot.in, INBUF_SIZE, bot.srv_fd) == NULL) + if (bot.use_ssl) { - eprint("xbot: remote host closed connection\n"); - return 0; + bytesRecv = SSL_read(bot.ssl, bot.in, INBUF_SIZE); + if (bytesRecv <= 0) + { + eprint("xbot: error on SSL_read()\n"); + + ssl_err = ERR_get_error(); + if (ssl_err) + { + eprint("SSL error: %s\n", ERR_error_string(ssl_err, NULL)); + } + + + return -1; + + } + + bot.in[bytesRecv] = '\0'; + + while (1) + { + // remove \r + p = strchr(bot.in, '\r'); + p = strchr(bot.in, '\n'); + if (p == NULL) + break; + + *p = '\0'; + + // remove \r at end of line + if (p[-1] == '\r') + p[-1] = '\0'; + + irc_parse_raw(&bot, bot.in); + memmove(bot.in, p + 1, strlen(p + 1) + 1); + } + + free(p); + } + else + { + if (fgets(bot.in, INBUF_SIZE, bot.srv_fd) == NULL) + { + eprint("xbot: remote host closed connection\n"); + return 0; + } + + printf("recv: [%s]\n", bot.in); + irc_parse_raw(&bot, bot.in); } - printf("recv: %s\r\n", bot.in); - irc_parse_raw(&bot, bot.in); #endif trespond = time(NULL); } diff --git a/src/util.c b/src/util.c index 7ea5b73..3a4e0be 100755 --- a/src/util.c +++ b/src/util.c @@ -33,6 +33,7 @@ void eprint(char *fmt, ...) xlog("%s", bufout); } + #if defined(__GLIBC__) && (__GLIBC__ < 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ < 38)) || defined(_WIN32) void strlcpy(char *to, const char *from, int len) { @@ -49,6 +50,7 @@ char *basename(char *path) } #endif + char *skip(char *s, char c) { while (*s != c && *s != '\0') diff --git a/xbot.cfg b/xbot.cfg index fe9d7aa..147d976 100755 --- a/xbot.cfg +++ b/xbot.cfg @@ -8,7 +8,7 @@ bot: real = "xbot"; # owner of the bot (nick!user@host) - admin = "ab3800!*@owner.ephasic.org"; + admin = "ab3800!*@*"; # database file name db = "xbot.db"; @@ -19,8 +19,11 @@ bot: server: { - host = "memphis.ephasic.org"; - port = "6667"; + host = "irc.newsrouters.ca"; + port = "6697"; + + ssl = true; + ssl_verify = false; }; mods: @@ -29,5 +32,5 @@ mods: blacklist = (); # config option for mods/autojoin.so - mod_autojoin = ("#lobby", "#bots"); + mod_autojoin = ("#Lobby", "#Bots"); };