diff --git a/bip.conf.1 b/bip.conf.1 index 694e12b..76a553a 100644 --- a/bip.conf.1 +++ b/bip.conf.1 @@ -246,6 +246,12 @@ allows a "ssh-like" private key generation scheme. Note that in basic mode: \fBssl_check_store\fP (default: \fBnot set\fP) This repository is browsed by BIP when a SSL certificate or CA check is needed. +.TP +\fBssl_client_certfile\fP (default: \fBnot set\fP) +Some networks (OFTC at least) allow you to authenticate to nickserv services +using a client side certificate. Make this variable point to the .pem file to +use this feature. + .SH CONNECTION SUB-SECTION Each connection section associates a user to the networks he wants to connect diff --git a/samples/bip.conf b/samples/bip.conf index 4b86e31..81b5c87 100644 --- a/samples/bip.conf +++ b/samples/bip.conf @@ -85,8 +85,9 @@ user { # bipmkpw password = "3880f2b39b3b9cb507b052b695d2680859bfc327"; - # Set this to true if you want "bip4ever" to have admin privileges on bip - # He'll be able to RELOAD bip and see all users' configuration (except pass) + # Set this to true if you want "bip4ever" to have admin privileges on + # bip He'll be able to RELOAD bip and see all users' configuration + # (except pass) admin = true; # When bip_use_notice is true, bip will send internal messages like @@ -114,6 +115,12 @@ user { # `c_rehash .' in it ssl_check_store = "/home/bip4ever/.bip/trustedcerts.txt"; + # Some networks (OFTC at least) allow you to authenticate to nickserv + # using client side certificates, see + # http://www.oftc.net/oftc/NickServ/CertFP + # This is where you put your user's certificate. + # ssl_client_certfile = "/home/bip4ever/.bip/bip4ever_client_auth.pem"; + # These will be the default for each connections default_nick = "bip4ever"; default_user = "bip4ever"; diff --git a/src/bip.c b/src/bip.c index feb63cb..6c5245c 100644 --- a/src/bip.c +++ b/src/bip.c @@ -684,6 +684,7 @@ static int add_user(bip_t *bip, list_t *data, struct historical_directives *hds) FREE(u->default_realname); #ifdef HAVE_LIBSSL FREE(u->ssl_check_store); + FREE(u->ssl_client_certfile); #endif } @@ -753,7 +754,11 @@ static int add_user(bip_t *bip, list_t *data, struct historical_directives *hds) case LEX_SSL_CHECK_STORE: MOVE_STRING(u->ssl_check_store, t->pdata); break; + case LEX_SSL_CLIENT_CERTFILE: + MOVE_STRING(u->ssl_client_certfile, t->pdata); + break; #else + case LEX_SSL_CLIENT_CERTFILE: case LEX_SSL_CHECK_MODE: case LEX_SSL_CHECK_STORE: mylog(LOG_WARN, "Found SSL option whereas bip is " @@ -878,6 +883,7 @@ void user_kill(bip_t *bip, struct user *user) #ifdef HAVE_LIBSSL MAYFREE(user->ssl_check_store); + MAYFREE(user->ssl_client_certfile); #endif free(user); } @@ -1522,6 +1528,9 @@ noroom: bip_notify(ic, "SSL check mode '%s', stored into '%s'", checkmode2text(u->ssl_check_mode), STRORNULL(u->ssl_check_store)); + if (u->ssl_client_certfile) + bip_notify(ic, "SSL client certificate stored into '%s'", + u->ssl_client_certfile); #endif bip_notify(ic, "Defaults nick: %s, user: %s, realname: %s", STRORNULL(u->default_nick), STRORNULL(u->default_username), diff --git a/src/conf.y b/src/conf.y index 83161cd..d43b9d4 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_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_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 +%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_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 %union { int number; @@ -150,6 +150,8 @@ usr_command: LEX_SSL_CHECK_MODE, $3); } | LEX_SSL_CHECK_STORE LEX_EQ LEX_STRING { $$ = tuple_s_new( LEX_SSL_CHECK_STORE, $3); } + | LEX_SSL_CLIENT_CERTFILE LEX_EQ LEX_STRING { $$ = tuple_s_new( + LEX_SSL_CLIENT_CERTFILE, $3); } | LEX_DEFAULT_USER LEX_EQ LEX_STRING { $$ = tuple_s_new(LEX_DEFAULT_USER, $3); } | LEX_DEFAULT_NICK LEX_EQ LEX_STRING { diff --git a/src/connection.c b/src/connection.c index bf8246c..053c54c 100644 --- a/src/connection.c +++ b/src/connection.c @@ -1057,6 +1057,14 @@ connection_t *accept_new(connection_t *cn) connection_free(conn); return NULL; } + if (!SSL_CTX_use_certificate_chain_file(sslctx, + conf_ssl_certfile)) + mylog(LOG_WARN, "SSL: Unable to load " + "certificate file"); + if (!SSL_CTX_use_PrivateKey_file(sslctx, + conf_ssl_certfile, + SSL_FILETYPE_PEM)) + mylog(LOG_WARN, "SSL: Unable to load key file"); } conn->ssl_h = SSL_new(sslctx); @@ -1154,14 +1162,8 @@ prng_end: /* allocated by function */ ctx = SSL_CTX_new(SSLv23_method()); - if (!SSL_CTX_use_certificate_chain_file(ctx,conf_ssl_certfile)) - mylog(LOG_WARN, "SSL: Unable to load certificate file"); - if (!SSL_CTX_use_PrivateKey_file(ctx, conf_ssl_certfile, - SSL_FILETYPE_PEM)) - mylog(LOG_WARN, "SSL: Unable to load key file"); - SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_BOTH); - SSL_CTX_set_timeout(ctx,(long)60); + SSL_CTX_set_timeout(ctx, (long)60); SSL_CTX_set_options(ctx, SSL_OP_ALL); return ctx; @@ -1321,7 +1323,7 @@ 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 *check_store, int timeout) + char *check_store, char *ssl_client_certfile, int timeout) { connection_t *conn; @@ -1330,6 +1332,7 @@ static connection_t *_connection_new_SSL(char *dsthostname, char *dstport, mylog(LOG_ERROR, "SSL context initialization failed"); return conn; } + conn->cert = NULL; conn->ssl_check_mode = check_mode; @@ -1368,6 +1371,18 @@ static connection_t *_connection_new_SSL(char *dsthostname, char *dstport, fatal("Unknown SSL cert check mode."); } + if (ssl_client_certfile) { + if (!SSL_CTX_use_certificate_chain_file(conn->ssl_ctx_h, + ssl_client_certfile)) + mylog(LOG_WARN, "SSL: Unable to load certificate file"); + else if (!SSL_CTX_use_PrivateKey_file(conn->ssl_ctx_h, + ssl_client_certfile, SSL_FILETYPE_PEM)) + mylog(LOG_WARN, "SSL: Unable to load key file"); + else + mylog(LOG_INFO, "SSL: using %s pem file as client SSL " + "certificate", ssl_client_certfile); + } + conn->ssl_h = SSL_new(conn->ssl_ctx_h); if (conn->ssl_h == NULL) { mylog(LOG_ERROR, "Unable to allocate SSL structures"); @@ -1392,7 +1407,7 @@ static connection_t *_connection_new_SSL(char *dsthostname, char *dstport, connection_t *connection_new(char *dsthostname, int dstport, char *srchostname, int srcport, int ssl, int ssl_check_mode, char *ssl_check_store, - int timeout) + char *ssl_client_certfile, int timeout) { char dstportbuf[20], srcportbuf[20], *tmp; #ifndef HAVE_LIBSSL @@ -1412,7 +1427,8 @@ 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, timeout); + tmp, ssl_check_mode, ssl_check_store, + ssl_client_certfile, timeout); else #endif return _connection_new(dsthostname, dstportbuf, srchostname, diff --git a/src/connection.h b/src/connection.h index 84c1a63..cb53ac1 100644 --- a/src/connection.h +++ b/src/connection.h @@ -92,7 +92,7 @@ typedef struct connection { connection_t *connection_new(char *dsthostname, int dstport, char *srchostname, int srcport, int ssl, int ssl_check_mode, - char *ssl_check_store,int timeout); + 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); void connection_free(connection_t *cn); diff --git a/src/irc.c b/src/irc.c index d0ff2d9..a7e5fff 100644 --- a/src/irc.c +++ b/src/irc.c @@ -2053,6 +2053,7 @@ connection_t *irc_server_connect(struct link *link) #ifdef HAVE_LIBSSL link->network->ssl, link->ssl_check_mode, link->user->ssl_check_store, + link->user->ssl_client_certfile, #else 0, 0, NULL, #endif @@ -2470,9 +2471,12 @@ struct link *irc_link_new() void link_kill(bip_t *bip, struct link *link) { - list_remove(&bip->conn_list, CONN(link->l_server)); - server_cleanup(link->l_server); - irc_server_free(link->l_server); + /* in case in never got connected */ + if (link->l_server) { + list_remove(&bip->conn_list, CONN(link->l_server)); + server_cleanup(link->l_server); + irc_server_free(link->l_server); + } while (link->l_clientc) { struct link_client *lc = link->l_clientv[0]; if (lc == bip->reloading_client) diff --git a/src/irc.h b/src/irc.h index eb831d6..575eefa 100644 --- a/src/irc.h +++ b/src/irc.h @@ -88,6 +88,7 @@ struct user { #ifdef HAVE_LIBSSL int ssl_check_mode; char *ssl_check_store; + char *ssl_client_certfile; #endif hash_t connections; diff --git a/src/lex.l b/src/lex.l index 8516068..97f04c1 100644 --- a/src/lex.l +++ b/src/lex.l @@ -87,6 +87,7 @@ list_t *parse_conf(FILE *file, int *err) "ssl" { return LEX_SSL; } "ssl_check_mode" { return LEX_SSL_CHECK_MODE; } "ssl_check_store" { return LEX_SSL_CHECK_STORE; } +"ssl_client_certfile" { return LEX_SSL_CLIENT_CERTFILE; } "key" { return LEX_KEY; } "channel" { return LEX_CHANNEL; } "log_level" { return LEX_LOG_LEVEL; }