diff --git a/bip.conf.5 b/bip.conf.5 index d153f07..be1a9d3 100644 --- a/bip.conf.5 +++ b/bip.conf.5 @@ -78,6 +78,14 @@ defined. Set this to the full path of the cert/key pair bip should use to accept clients SSL connections. +.TP +\fBclient_side_ciphers\fP +OpenSSL cipher lists used for clients SSL connections. + +.TP +\fBssl_default_ciphers\fP +OpenSSL cipher lists used for server connections. + .TP \fBip\fP Ignored for the time being. @@ -147,6 +155,10 @@ If true, BIP will connect to this network using SSL only. You cannot mix SSL servers and non-SSL servers in the same network section. This is by choice, we believe it's a bad idea. +.TP +\fBssl_ciphers\fP (override global ssl_default_ciphers) +OpenSSL cipher lists used for this network. + .TP \fBname\fP It's the network name used in the \fBconnection section\fP. Please note that diff --git a/samples/bip.conf b/samples/bip.conf index c361441..84b0b4c 100644 --- a/samples/bip.conf +++ b/samples/bip.conf @@ -20,6 +20,12 @@ client_side_ssl = false; # serve SSL clients. If unset, it defaults to /bip.pem #client_side_ssl_pem = "/path/to/pemfile"; +# OpenSSL cipher lists used with SSL client connections. +#client_side_ciphers = "ECDHE-RSA-AES128-GCM-SHA256"; + +# Default OpenSSL cipher lists used with outgoing connections to IRC servers. +#ssl_default_ciphers = "ECDHE-RSA-AES128-GCM-SHA256"; + # Define where the pidfile should be stored. Defaults to /bip.pid #pid_file="/var/run/bip/bip.pid"; diff --git a/samples/bip.vim b/samples/bip.vim index 0d63b96..46aeb74 100644 --- a/samples/bip.vim +++ b/samples/bip.vim @@ -55,7 +55,8 @@ syn region bipMain start=/\%^/ end=/\%$/ syn keyword bipKeyword contained nextgroup=bipBoolV client_side_ssl \ log log_system syn keyword bipKeyword contained nextgroup=bipStringV log_root - \ log_format pid_file client_side_ssl_pem + \ log_format pid_file client_side_ssl_pem client_side_ciphers + \ ssl_default_ciphers syn keyword bipKeyword contained nextgroup=bipNumericV port log_level \ log_sync_interval syn keyword bipKeyword contained nextgroup=bipIPV ip @@ -64,7 +65,7 @@ syn keyword bipKeyword contained nextgroup=bipIPV ip syn region bipNetwork contained matchgroup=Macro \ start=/network\s*{\s*/ end=/};/ \ contains=bipNKeyword,bipServer,bipComment,bipEndError,bipWhite -syn keyword bipNKeyword contained nextgroup=bipStringV name +syn keyword bipNKeyword contained nextgroup=bipStringV name ciphers syn keyword bipNKeyword contained nextgroup=bipBoolV ssl " User block (level 1) @@ -85,7 +86,7 @@ syn region bipConnection contained matchgroup=Macro \ start=/connection\s*{\s*/ end=/};/ \ contains=bipCoKeyword,bipChannel,bipComment,bipEndError,bipWhite syn keyword bipCoKeyword contained nextgroup=bipBoolV follow_nick - \ ignore_first_nick + \ ignore_first_nick log syn keyword bipCoKeyword contained nextgroup=bipStringV name user nick \ network password vhost away_nick on_connect_send realname \ no_client_away_msg ssl_check_mode diff --git a/src/bip.c b/src/bip.c index 5da112f..502ed04 100644 --- a/src/bip.c +++ b/src/bip.c @@ -41,6 +41,8 @@ unsigned short conf_port; int conf_css; #ifdef HAVE_LIBSSL char *conf_ssl_certfile; +char *conf_client_ciphers; +char *conf_server_default_ciphers; #endif int conf_daemonize; char *conf_pid_file; @@ -358,6 +360,9 @@ static int add_network(bip_t *bip, list_t *data) case LEX_SSL: n->ssl = t->ndata; break; + case LEX_CIPHERS: + MOVE_STRING(n->ciphers, t->pdata); + break; #endif case LEX_SERVER: n->serverv = bip_realloc(n->serverv, (n->serverc + 1) @@ -383,6 +388,12 @@ static int add_network(bip_t *bip, list_t *data) free(t->pdata); free(t); } + +#ifdef HAVE_LIBSSL + if (!n->ciphers) { + n->ciphers = conf_server_default_ciphers; + } +#endif return 1; } @@ -996,6 +1007,12 @@ int fireup(bip_t *bip, FILE *conf) conf_reconn_timer = t->ndata; break; #ifdef HAVE_LIBSSL + case LEX_DEFAULT_CIPHERS: + MOVE_STRING(conf_server_default_ciphers, t->pdata); + break; + case LEX_CSS_CIPHERS: + MOVE_STRING(conf_client_ciphers, t->pdata); + break; case LEX_CSS: conf_css = t->ndata; break; @@ -1003,7 +1020,9 @@ int fireup(bip_t *bip, FILE *conf) MOVE_STRING(conf_ssl_certfile, t->pdata); break; #else + case LEX_DEFAULT_CIPHERS: case LEX_CSS: + case LEX_CSS_CIPHERS: case LEX_CSS_PEM: mylog(LOG_WARN, "Found SSL option whereas bip is " "not built with SSL support."); @@ -1191,6 +1210,8 @@ int main(int argc, char **argv) conf_pid_file = NULL; #ifdef HAVE_LIBSSL conf_ssl_certfile = NULL; + conf_client_ciphers = NULL; + conf_server_default_ciphers = NULL; #endif while ((ch = getopt(argc, argv, "hvnf:s:")) != -1) { diff --git a/src/conf.y b/src/conf.y index c483716..0125888 100644 --- a/src/conf.y +++ b/src/conf.y @@ -68,7 +68,7 @@ struct tuple *tuple_l_new(int type, void *p) %} -%token LEX_IP LEX_EQ LEX_PORT LEX_CSS LEX_SEMICOLON LEX_CONNECTION LEX_NETWORK LEX_LBRA LEX_RBRA LEX_USER LEX_NAME LEX_NICK LEX_SERVER LEX_PASSWORD LEX_SRCIP LEX_HOST LEX_VHOST LEX_SOURCE_PORT LEX_NONE LEX_COMMENT LEX_BUNCH LEX_REALNAME LEX_SSL LEX_SSL_CHECK_MODE LEX_SSL_CHECK_STORE LEX_SSL_CLIENT_CERTFILE LEX_CHANNEL LEX_KEY LEX_LOG_ROOT LEX_LOG_FORMAT LEX_LOG_LEVEL LEX_BACKLOG_LINES LEX_BACKLOG_NO_TIMESTAMP LEX_BACKLOG LEX_LOG LEX_LOG_SYSTEM LEX_LOG_SYNC_INTERVAL LEX_FOLLOW_NICK LEX_ON_CONNECT_SEND LEX_AWAY_NICK LEX_PID_FILE LEX_IGN_FIRST_NICK LEX_ALWAYS_BACKLOG LEX_BLRESET_ON_TALK LEX_BLRESET_CONNECTION LEX_DEFAULT_USER LEX_DEFAULT_NICK LEX_DEFAULT_REALNAME LEX_NO_CLIENT_AWAY_MSG LEX_BL_MSG_ONLY LEX_ADMIN LEX_BIP_USE_NOTICE LEX_CSS_PEM LEX_AUTOJOIN_ON_KICK LEX_IGNORE_CAPAB LEX_RECONN_TIMER +%token LEX_IP LEX_EQ LEX_PORT LEX_CSS LEX_SEMICOLON LEX_CONNECTION LEX_NETWORK LEX_LBRA LEX_RBRA LEX_USER LEX_NAME LEX_NICK LEX_SERVER LEX_PASSWORD LEX_SRCIP LEX_HOST LEX_VHOST LEX_SOURCE_PORT LEX_NONE LEX_COMMENT LEX_BUNCH LEX_REALNAME LEX_SSL LEX_SSL_CHECK_MODE LEX_SSL_CHECK_STORE LEX_SSL_CLIENT_CERTFILE LEX_CIPHERS LEX_CSS_CIPHERS LEX_DEFAULT_CIPHERS LEX_CHANNEL LEX_KEY LEX_LOG_ROOT LEX_LOG_FORMAT LEX_LOG_LEVEL LEX_BACKLOG_LINES LEX_BACKLOG_NO_TIMESTAMP LEX_BACKLOG LEX_LOG LEX_LOG_SYSTEM LEX_LOG_SYNC_INTERVAL LEX_FOLLOW_NICK LEX_ON_CONNECT_SEND LEX_AWAY_NICK LEX_PID_FILE LEX_IGN_FIRST_NICK LEX_ALWAYS_BACKLOG LEX_BLRESET_ON_TALK LEX_BLRESET_CONNECTION LEX_DEFAULT_USER LEX_DEFAULT_NICK LEX_DEFAULT_REALNAME LEX_NO_CLIENT_AWAY_MSG LEX_BL_MSG_ONLY LEX_ADMIN LEX_BIP_USE_NOTICE LEX_CSS_PEM LEX_AUTOJOIN_ON_KICK LEX_IGNORE_CAPAB LEX_RECONN_TIMER %union { int number; @@ -99,6 +99,8 @@ command: | LEX_PORT LEX_EQ LEX_INT { $$ = tuple_i_new(LEX_PORT, $3); } | LEX_CSS LEX_EQ LEX_BOOL { $$ = tuple_i_new(LEX_CSS, $3); } | LEX_CSS_PEM LEX_EQ LEX_STRING { $$ = tuple_s_new(LEX_CSS_PEM, $3); } + | LEX_CSS_CIPHERS LEX_EQ LEX_STRING { $$ = tuple_s_new(LEX_CSS_CIPHERS, $3); } + | LEX_DEFAULT_CIPHERS LEX_EQ LEX_STRING { $$ = tuple_s_new(LEX_DEFAULT_CIPHERS, $3); } | LEX_LOG LEX_EQ LEX_BOOL { $$ = tuple_i_new(LEX_LOG, $3); } | LEX_LOG_SYSTEM LEX_EQ LEX_BOOL { $$ = tuple_i_new(LEX_LOG_SYSTEM, $3); } | LEX_LOG_SYNC_INTERVAL LEX_EQ LEX_INT { $$ = tuple_i_new( @@ -132,6 +134,7 @@ network: net_command: LEX_NAME LEX_EQ LEX_STRING { $$ = tuple_s_new(LEX_NAME, $3); } | LEX_SSL LEX_EQ LEX_BOOL { $$ = tuple_i_new(LEX_SSL, $3); } + | LEX_CIPHERS LEX_EQ LEX_STRING { $$ = tuple_s_new(LEX_CIPHERS, $3); } | LEX_SERVER LEX_LBRA server LEX_RBRA { $$ = tuple_l_new(LEX_SERVER, $3); } diff --git a/src/connection.c b/src/connection.c index 37cda12..431dd07 100644 --- a/src/connection.c +++ b/src/connection.c @@ -24,8 +24,9 @@ static int ssl_cx_idx; extern FILE *conf_global_log_file; static BIO *errbio = NULL; extern char *conf_ssl_certfile; +extern char *conf_client_ciphers; static int SSLize(connection_t *cn, int *nc); -static SSL_CTX *SSL_init_context(void); +static SSL_CTX *SSL_init_context(char *ciphers); /* SSH like trust management */ int link_add_untrusted(void *ls, X509 *cert); #endif @@ -1240,7 +1241,7 @@ connection_t *accept_new(connection_t *cn) mylog(LOG_DEBUG, "No SSL context available for " "accepted connections. " "Initializing..."); - if (!(sslctx = SSL_init_context())) { + if (!(sslctx = SSL_init_context(conf_client_ciphers))) { mylog(LOG_ERROR, "SSL context initialization " "failed"); connection_free(conn); @@ -1303,7 +1304,7 @@ static connection_t *_connection_new(char *dsthostname, char *dstport, } #ifdef HAVE_LIBSSL -static SSL_CTX *SSL_init_context(void) +static SSL_CTX *SSL_init_context(char *ciphers) { int fd, flags, ret, rng; char buf[1025]; @@ -1358,6 +1359,10 @@ prng_end: SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_BOTH); SSL_CTX_set_timeout(ctx, (long)60); SSL_CTX_set_options(ctx, SSL_OP_ALL); + if (ciphers && !SSL_CTX_set_cipher_list(ctx, ciphers)) { + SSL_CTX_free(ctx); + return NULL; + } return ctx; } @@ -1518,13 +1523,13 @@ static int SSLize(connection_t *cn, int *nc) } static connection_t *_connection_new_SSL(char *dsthostname, char *dstport, - char *srchostname, char *srcport, int check_mode, + char *srchostname, char *srcport, char *ciphers, int check_mode, char *check_store, char *ssl_client_certfile, int timeout) { connection_t *conn; conn = connection_init(1, 1, timeout, 0); - if (!(conn->ssl_ctx_h = SSL_init_context())) { + if (!(conn->ssl_ctx_h = SSL_init_context(ciphers))) { mylog(LOG_ERROR, "SSL context initialization failed"); return conn; } @@ -1634,12 +1639,13 @@ static connection_t *_connection_new_SSL(char *dsthostname, char *dstport, #endif connection_t *connection_new(char *dsthostname, int dstport, char *srchostname, - int srcport, int ssl, int ssl_check_mode, char *ssl_check_store, - char *ssl_client_certfile, int timeout) + int srcport, int ssl, char *ssl_ciphers, int ssl_check_mode, + char *ssl_check_store, char *ssl_client_certfile, int timeout) { char dstportbuf[20], srcportbuf[20], *tmp; #ifndef HAVE_LIBSSL (void)ssl; + (void)ssl_ciphers; (void)ssl_check_mode; (void)ssl_check_store; (void)ssl_client_certfile; @@ -1656,7 +1662,7 @@ connection_t *connection_new(char *dsthostname, int dstport, char *srchostname, #ifdef HAVE_LIBSSL if (ssl) return _connection_new_SSL(dsthostname, dstportbuf, srchostname, - tmp, ssl_check_mode, ssl_check_store, + tmp, ssl_ciphers, ssl_check_mode, ssl_check_store, ssl_client_certfile, timeout); else #endif diff --git a/src/connection.h b/src/connection.h index 5fa7d2e..9e8a254 100644 --- a/src/connection.h +++ b/src/connection.h @@ -91,7 +91,7 @@ typedef struct connection { } connection_t; connection_t *connection_new(char *dsthostname, int dstport, char *srchostname, - int srcport, int ssl, int ssl_check_mode, + int srcport, int ssl, char *ssl_ciphers, int ssl_check_mode, char *ssl_check_store, char *ssl_client_certfile, int timeout); connection_t *listen_new(char *hostname, int port, int ssl); connection_t *accept_new(connection_t *cn); diff --git a/src/irc.c b/src/irc.c index f9a6271..f46f4dd 100644 --- a/src/irc.c +++ b/src/irc.c @@ -2171,11 +2171,13 @@ connection_t *irc_server_connect(struct link *link) link->network->serverv[link->cur_server].port, link->vhost, link->bind_port, #ifdef HAVE_LIBSSL - link->network->ssl, link->ssl_check_mode, + link->network->ssl, + link->network->ciphers, + link->ssl_check_mode, link->user->ssl_check_store, link->user->ssl_client_certfile, #else - 0, 0, NULL, NULL, + 0, NULL, 0, NULL, NULL, #endif CONNECT_TIMEOUT); assert(conn); diff --git a/src/irc.h b/src/irc.h index a0940d3..bc590a1 100644 --- a/src/irc.h +++ b/src/irc.h @@ -93,6 +93,7 @@ struct network char *name; #ifdef HAVE_LIBSSL int ssl; + char *ciphers; #endif int serverc; struct server *serverv; diff --git a/src/lex.l b/src/lex.l index 4c6c97b..098b92b 100644 --- a/src/lex.l +++ b/src/lex.l @@ -81,6 +81,8 @@ list_t *parse_conf(FILE *file, int *err) "ssl_check_mode" { return LEX_SSL_CHECK_MODE; } "ssl_check_store" { return LEX_SSL_CHECK_STORE; } "ssl_client_certfile" { return LEX_SSL_CLIENT_CERTFILE; } +"ssl_default_ciphers" { return LEX_DEFAULT_CIPHERS; } +"ciphers" { return LEX_CIPHERS; } "key" { return LEX_KEY; } "autojoin_on_kick" { return LEX_AUTOJOIN_ON_KICK; } "channel" { return LEX_CHANNEL; } @@ -108,6 +110,7 @@ list_t *parse_conf(FILE *file, int *err) "pid_file" { return LEX_PID_FILE; } "bip_use_notice" { return LEX_BIP_USE_NOTICE; } "client_side_ssl_pem" { return LEX_CSS_PEM; } +"client_side_ciphers" { return LEX_CSS_CIPHERS; } "ignore_server_capab" { return LEX_IGNORE_CAPAB; } "reconn_timer" { return LEX_RECONN_TIMER; } \"[^"]*\" {