diff --git a/Changes b/Changes index 7124918b1..df4e618a7 100644 --- a/Changes +++ b/Changes @@ -1589,3 +1589,13 @@ seen. gmtime warning still there - Fixed a bug with /who -h in some cases, found by Zer0, fixed by butter (#0000361) - Changed auth method sslpubkey into sslclientcert, which means it will check the X509 certificate of the user using X509_cmp. Also needing is some policy/conf setting to adjust if to reject invalid client certificates or whatever.. +- Added set::ssl::options, with three options: + fail-if-no-clientcert - If SSL client connects and doesn't provide a client certificate, abort connection immediately + verify-certificate - Check the certificate's validity using X509 methods, check if we trust CA's, etc. + It however does slip self signed certificates through UNLESS + no-self-signed - Don't allow self-signed certificates through (requires verify-certificate) +- Made conf parser mention if we make a link->options with CONNECT_SSL if we don't support SSL (and remove the CONNECT_SSL flag) +- Made conf parser mention if we make a SSL listener and we don't support SSL +- Added set::ssl::trusted-ca-file, if enabled, it will point the SSL stuff to use that file as trusted CA's (for verify-certificate) +- Made conf _not_ bitch that it doesn't know set::ssl +- Removed some leftover client certificate stuff diff --git a/include/dynconf.h b/include/dynconf.h index c6f682951..af341c138 100644 --- a/include/dynconf.h +++ b/include/dynconf.h @@ -76,6 +76,8 @@ struct zConfiguration { #ifdef USE_SSL char *x_server_cert_pem; char *x_server_key_pem; + char *trusted_ca_file; + long ssl_options; #endif aNetwork network; }; diff --git a/include/struct.h b/include/struct.h index fd1a46fa1..1c12ebb22 100644 --- a/include/struct.h +++ b/include/struct.h @@ -733,6 +733,9 @@ extern short Usermode_highest; #define CONNECT_AUTO 0x000004 #define CONNECT_QUARANTINE 0x000008 +#define SSLFLAG_FAILIFNOCERT 0x1 +#define SSLFLAG_VERIFYCERT 0x2 +#define SSLFLAG_DONOTACCEPTSELFSIGNED 0x4 struct Client { struct Client *next, *prev, *hnext; anUser *user; /* ...defined, if this is a User */ diff --git a/src/s_conf.c b/src/s_conf.c index 9f9c09aff..d43724fd3 100644 --- a/src/s_conf.c +++ b/src/s_conf.c @@ -229,6 +229,16 @@ static OperFlag _LogFlags[] = { { 0L, NULL } }; +#ifdef USE_SSL + +static OperFlag _SSLFlags[] = { + { SSLFLAG_FAILIFNOCERT, "fail-if-no-clientcert" }, + { SSLFLAG_VERIFYCERT, "verify-certificate" }, + { SSLFLAG_DONOTACCEPTSELFSIGNED, "no-self-signed" }, + { 0L, NULL } +}; + +#endif /* * Some prototypes */ @@ -1733,6 +1743,7 @@ int _conf_listen(ConfigFile *conf, ConfigEntry *ce) } if (!strcmp(cep->ce_varname, "options")) { + listen->options = 0; for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) { if (!cepp->ce_varname) @@ -1758,6 +1769,15 @@ int _conf_listen(ConfigFile *conf, ConfigEntry *ce) continue; } } +#ifndef USE_SSL + if (listen->options & LISTENER_SSL) + { + config_status("%s:%i: listen with SSL flag enabled on a non SSL compile", + cep->ce_fileptr->cf_filename, cep->ce_varlinenum, + cep->ce_varname); + listen->options &= ~LISTENER_SSL; + } +#endif } else { @@ -2230,6 +2250,14 @@ int _conf_link(ConfigFile *conf, ConfigEntry *ce) continue; } } +#ifndef USE_SSL + if (link->options & CONNECT_SSL) + { + config_status("%s:%i: link %s with SSL option enabled on a non-SSL compile", + cep->ce_fileptr->cf_filename, cep->ce_varlinenum, ce->ce_vardata); + link->options &= ~CONNECT_SSL; + } +#endif } else if (!strcmp(cep->ce_varname, "username")) { @@ -2300,8 +2328,8 @@ int _conf_link(ConfigFile *conf, ConfigEntry *ce) } int _conf_set(ConfigFile *conf, ConfigEntry *ce) { - ConfigEntry *cep; - ConfigEntry *cepp; + ConfigEntry *cep, *cepp, *ceppp; + OperFlag *ofl = NULL; char temp[512]; int i; #define CheckNull(x) if (!(x)->ce_vardata) { config_status("%s:%i: missing parameter", (x)->ce_fileptr->cf_filename, (x)->ce_varlinenum); continue; } @@ -2484,8 +2512,8 @@ int _conf_set(ConfigFile *conf, ConfigEntry *ce) CLOAK_KEY2, CLOAK_KEY3); CLOAK_KEYCRC = (long) crc32(temp, strlen(temp)); } -#ifdef USE_SSL else if (!strcmp(cep->ce_varname, "ssl")) { +#ifdef USE_SSL for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) { if (!strcmp(cepp->ce_varname, "egd")) { USE_EGD = 1; @@ -2504,9 +2532,40 @@ int _conf_set(ConfigFile *conf, ConfigEntry *ce) continue; ircstrdup(iConf.x_server_key_pem, cepp->ce_vardata); } + else if (!strcmp(cepp->ce_varname, "trusted-ca-file")) + { + if (!cepp->ce_vardata) + continue; + ircstrdup(iConf.trusted_ca_file, cepp->ce_vardata); + } + else if (!strcmp(cepp->ce_varname, "options")) + { + iConf.ssl_options = 0; + for (ceppp = cepp->ce_entries; ceppp; ceppp = ceppp->ce_next) + { + for (ofl = _SSLFlags; ofl->name; ofl++) + { + if (!strcmp(ceppp->ce_varname, ofl->name)) + { + iConf.ssl_options |= ofl->flag; + break; + } + } + } + if (!ofl->name) + { + config_status("%s:%i: unknown SSL flag '%s'", + ceppp->ce_fileptr->cf_filename, + ceppp->ce_varlinenum, ceppp->ce_varname); + } + if (iConf.ssl_options & SSLFLAG_DONOTACCEPTSELFSIGNED) + if (!iConf.ssl_options & SSLFLAG_VERIFYCERT) + iConf.ssl_options |= SSLFLAG_VERIFYCERT; + } + } - } #endif + } else { ConfigItem_unknown_ext *ca2 = MyMalloc(sizeof(ConfigItem_unknown_ext)); @@ -3809,6 +3868,7 @@ int rehash(aClient *cptr, aClient *sptr, int sig) #ifdef USE_SSL ircfree(iConf.x_server_cert_pem); ircfree(iConf.x_server_key_pem); + ircfree(iConf.trusted_ca_file); #endif bzero(&iConf, sizeof(iConf)); @@ -4202,6 +4262,11 @@ void report_dynconf(aClient *sptr) sptr->name, SSL_SERVER_CERT_PEM); sendto_one(sptr, ":%s %i %s :ssl::key: %s", me.name, RPL_TEXT, sptr->name, SSL_SERVER_KEY_PEM); + sendto_one(sptr, ":%s %i %s :ssl::trusted-ca-file: %s", iConf.trusted_ca_file ? iConf.trusted_ca_file : ""); + sendto_one(sptr, ":%s %i %s :ssl::options: %s %s %s", + iConf.ssl_options & SSLFLAG_FAILIFNOCERT ? "FAILIFNOCERT" : "", + iConf.ssl_options & SSLFLAG_VERIFYCERT ? "VERIFYCERT" : "", + iConf.ssl_options & SSLFLAG_DONOTACCEPTSELFSIGNED ? "DONOTACCEPTSELFSIGNED" : ""); #endif sendto_one(sptr, ":%s %i %s :options::show-opermotd: %d", me.name, RPL_TEXT, diff --git a/src/ssl.c b/src/ssl.c index 0e30ea3ad..76e2725ce 100644 --- a/src/ssl.c +++ b/src/ssl.c @@ -149,7 +149,23 @@ int ssl_pem_passwd_cb(char *buf, int size, int rwflag, void *password) static int ssl_verify_callback(int preverify_ok, X509_STORE_CTX *ctx) { - return 1; + int verify_err = 0; + + verify_err = X509_STORE_CTX_get_error(ctx); + ircd_log(LOG_ERROR, "%i", verify_err); + if (preverify_ok) + return 1; + if (iConf.ssl_options & SSLFLAG_VERIFYCERT) + { + if (verify_err == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT) + if (!(iConf.ssl_options & SSLFLAG_DONOTACCEPTSELFSIGNED)) + { + return 1; + } + return preverify_ok; + } + else + return 1; } @@ -163,7 +179,8 @@ void init_ctx_server(void) } SSL_CTX_set_default_passwd_cb(ctx_server, ssl_pem_passwd_cb); SSL_CTX_set_options(ctx_server, SSL_OP_NO_SSLv2); - SSL_CTX_set_verify(ctx_server, SSL_VERIFY_PEER|SSL_VERIFY_CLIENT_ONCE, ssl_verify_callback); + SSL_CTX_set_verify(ctx_server, SSL_VERIFY_PEER|SSL_VERIFY_CLIENT_ONCE + | (iConf.ssl_options & SSLFLAG_FAILIFNOCERT ? SSL_VERIFY_FAIL_IF_NO_PEER_CERT : 0), ssl_verify_callback); if (SSL_CTX_use_certificate_file(ctx_server, SSL_SERVER_CERT_PEM, SSL_FILETYPE_PEM) <= 0) { @@ -181,6 +198,14 @@ void init_ctx_server(void) ircd_log(LOG_ERROR, "Failed to check SSL private key"); exit(5); } + if (iConf.trusted_ca_file) + { + if (!SSL_CTX_load_verify_locations(ctx_server, iConf.trusted_ca_file, NULL)) + { + ircd_log(LOG_ERROR, "Failed to load Trusted CA's from %s", iConf.trusted_ca_file); + exit(6); + } + } } @@ -262,44 +287,6 @@ int ssl_handshake(aClient *cptr) cptr->ssl = NULL; return -1; } - - /* Get client's certificate (note: beware of dynamic - * allocation) - opt */ - /* We do not do this -Stskeeps */ - -#ifdef NO_CERTCHECKING - cptr->client_cert = - (struct X509 *)SSL_get_peer_certificate((SSL *) cptr->ssl); - - if (cptr->client_cert != NULL) - { - // log (L_DEBUG,"Client certificate:\n"); - - str = - X509_NAME_oneline(X509_get_subject_name((X509 *) cptr-> - client_cert), 0, 0); - CHK_NULL(str); - // log (L_DEBUG, "\t subject: %s\n", str); - free(str); - - str = - X509_NAME_oneline(X509_get_issuer_name((X509 *) cptr-> - client_cert), 0, 0); - CHK_NULL(str); - // log (L_DEBUG, "\t issuer: %s\n", str); - free(str); - - /* We could do all sorts of certificate - * verification stuff here before - * deallocating the certificate. */ - - X509_free((X509 *) cptr->client_cert); - } - else - { - // log (L_DEBUG, "Client does not have certificate.\n"); - } -#endif return 0; }