# HG changeset patch # User Jefferson Ogata # Date 1152394369 -7200 # Node ID 40175f3dcef7d25e5495bc9d9175f68b30f7c48c # Parent 548def60b8105472ff846983322b02e9e1d84aa9 SSL server certificate verification This patch enables SSL server certificate verification. diff -r 548def60b810 -r 40175f3dcef7 mcabber/connwrap/connwrap.c --- a/mcabber/connwrap/connwrap.c Sat Jul 08 22:07:30 2006 +0200 +++ b/mcabber/connwrap/connwrap.c Sat Jul 08 23:32:49 2006 +0200 @@ -33,6 +33,106 @@ static SSL_CTX *ctx = 0; +/* verify > 0 indicates verify depth as well */ +static int verify = -1; +static const char *cafile = NULL; +static const char *capath = NULL; +static const char *cipherlist = NULL; +static const char *peer = NULL; +static const char *sslerror = NULL; + +static int verify_cb(int preverify_ok, X509_STORE_CTX *cx) +{ + X509 *cert; + X509_NAME *nm; + int lastpos; + + if(!preverify_ok) { + long err = X509_STORE_CTX_get_error(cx); + + sslerror = X509_verify_cert_error_string(err); + return 0; + } + + if (peer == NULL) + return 1; + + if ((cert = X509_STORE_CTX_get_current_cert(cx)) == NULL) { + sslerror = "internal SSL error"; + return 0; + } + + /* We only want to look at the peername if we're working on the peer + * certificate. */ + if (cert != cx->cert) + return 1; + + if ((nm = X509_get_subject_name (cert)) == NULL) { + sslerror = "internal SSL error"; + return 0; + } + + for(lastpos = -1; ; ) { + X509_NAME_ENTRY *e; + ASN1_STRING *a; + ASN1_STRING *p; + int match; + + lastpos = X509_NAME_get_index_by_NID(nm, NID_commonName, lastpos); + if (lastpos == -1) + break; + if ((e = X509_NAME_get_entry(nm, lastpos)) == NULL) { + sslerror = "internal SSL error"; + return 0; + } + if ((a = X509_NAME_ENTRY_get_data(e)) == NULL) { + sslerror = "internal SSL error"; + return 0; + } + if ((p = ASN1_STRING_type_new(ASN1_STRING_type(a))) == NULL) { + sslerror = "internal SSL error"; + return 0; + } + (void) ASN1_STRING_set(p, peer, -1); + match = !ASN1_STRING_cmp(a, p); + ASN1_STRING_free(p); + if(match) + return 1; + } + + sslerror = "server certificate cn mismatch"; + return 0; +} + +static void init(void) { + if(ctx) + return; + + SSL_library_init(); + SSL_load_error_strings(); + +#ifdef HAVE_SSLEAY + SSLeay_add_all_algorithms(); +#else + OpenSSL_add_all_algorithms(); +#endif + + /* May need to use distinct SSLEAY bindings below... */ + + //ctx = SSL_CTX_new(SSLv23_method()); + ctx = SSL_CTX_new(SSLv23_client_method()); + if(cipherlist) + (void)SSL_CTX_set_cipher_list(ctx, cipherlist); + if(cafile || capath) + (void)SSL_CTX_load_verify_locations(ctx, cafile, capath); + if(verify) { + SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, verify_cb); + if(verify > 0) + SSL_CTX_set_verify_depth(ctx, verify); + } else + SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, NULL); +} + typedef struct { int fd; SSL *ssl; } sslsock; static sslsock *socks = 0; @@ -54,22 +154,11 @@ p = &socks[sockcount-1]; - if(!ctx) { - SSL_library_init(); - SSL_load_error_strings(); - -#ifdef HAVE_SSLEAY - SSLeay_add_all_algorithms(); -#else - OpenSSL_add_all_algorithms(); -#endif - - //ctx = SSL_CTX_new(SSLv23_method()); - ctx = SSL_CTX_new(SSLv23_client_method()); - } + init (); p->ssl = SSL_new(ctx); SSL_set_fd(p->ssl, p->fd = fd); + sslerror = NULL; return p; } @@ -95,6 +184,26 @@ sockcount = nsockcount; } +void cw_set_ssl_options(int sslverify, const char *sslcafile, const char *sslcapath, const char *sslciphers, const char *sslpeer) { + verify = sslverify; + cafile = sslcafile; + capath = sslcapath; + cipherlist = sslciphers; + peer = sslpeer; +} + +const char *cw_get_ssl_error(void) { + return sslerror; +} + +#else + +void cw_set_ssl_options(int sslverify, const char *sslcafile, const char *sslcapath, const char *sslciphers, const char *sslpeer) { } + +const char *cw_get_ssl_error(void) { + return NULL; +} + #endif static char *bindaddr = 0, *proxyhost = 0, *proxyuser = 0, *proxypass = 0; diff -r 548def60b810 -r 40175f3dcef7 mcabber/connwrap/connwrap.h --- a/mcabber/connwrap/connwrap.h Sat Jul 08 22:07:30 2006 +0200 +++ b/mcabber/connwrap/connwrap.h Sat Jul 08 23:32:49 2006 +0200 @@ -35,6 +35,8 @@ void cw_close(int fd); +void cw_set_ssl_options(int sslverify, const char *sslcafile, const char *sslcapath, const char *sslciphers, const char *sslpeer); +const char *cw_get_ssl_error(void); void cw_setproxy(const char *aproxyhost, int aproxyport, const char *aproxyuser, const char *aproxypass); void cw_setbind(const char *abindaddr); diff -r 548def60b810 -r 40175f3dcef7 mcabber/libjabber/jconn.c --- a/mcabber/libjabber/jconn.c Sat Jul 08 22:07:30 2006 +0200 +++ b/mcabber/libjabber/jconn.c Sat Jul 08 23:32:49 2006 +0200 @@ -151,6 +151,8 @@ } else { /* subsequent calls to cw_nb_connect until it finishes negociation */ if (cw_nb_connect(j->fd, 0, 0, j->ssl, &j->cw_state)) { + if (cw_get_ssl_error()) + scr_LogPrint(LPRINT_LOGNORM, "jab_start: SSL negotiation failed: %s", cw_get_ssl_error()); STATE_EVT(JCONN_STATE_OFF); return; } diff -r 548def60b810 -r 40175f3dcef7 mcabber/mcabberrc.example --- a/mcabber/mcabberrc.example Sat Jul 08 22:07:30 2006 +0200 +++ b/mcabber/mcabberrc.example Sat Jul 08 23:32:49 2006 +0200 @@ -10,7 +10,6 @@ # # If password is not given, it will be interactively asked for. # If port is not given, default Jabber port will be used. -# Use ssl = 1 to enable SSL set username = yourusername # Note: if the password contains leading or trailing spaces, you must @@ -18,11 +17,27 @@ #set password = yourpassword set server = your.jabber.server #set port = 5222 -set ssl = 0 # If you don't know what a resource is, you can leave "mcabber" here. set resource = mcabber #set priority = 3 +# SSL options: +# Set ssl non-zero to use SSL (this also sets the default port to 5223). +# Set ssl_verify to 0 to disable certificate verification, or non-zero +# to set desired maximum CA verification depth. Use -1 to specify an +# unlimited depth. +# Set ssl_cafile to a path to a CA certificate file (may contain multiple +# CA certificates). +# Set ssl_capath to a directory containing CA certificates (use c_rehash +# to generate hash links). +# Set ssl_ciphers to a list of desired SSL ciphers (run "openssl ciphers" +# for candidate values). +set ssl = 0 +#set ssl_verify = -1 +#set ssl_cafile = /usr/share/ssl/certs/ca-bundle.crt +#set ssl_capath = +#set ssl_ciphers = + # Conference nickname # This nickname is used when joining a room, when no nick is explicitly # specified by the user. Note that when the nickname option is not set, diff -r 548def60b810 -r 40175f3dcef7 mcabber/src/main.c --- a/mcabber/src/main.c Sat Jul 08 22:07:30 2006 +0200 +++ b/mcabber/src/main.c Sat Jul 08 23:32:49 2006 +0200 @@ -68,6 +68,8 @@ const char *proxy_host; char *jid; int ssl; + int sslverify = -1; + const char *sslvopt = NULL, *cafile = NULL, *capath = NULL, *ciphers = NULL; unsigned int port; servername = settings_opt_get("server"); @@ -91,16 +93,25 @@ if (!resource) resource = "mcabber"; - ssl = (settings_opt_get_int("ssl") > 0); - port = (unsigned int) settings_opt_get_int("port"); + port = (unsigned int) settings_opt_get_int("port"); + + ssl = settings_opt_get_int("ssl"); + sslvopt = settings_opt_get("ssl_verify"); + if (sslvopt) + sslverify = settings_opt_get_int("ssl_verify"); + cafile = settings_opt_get("ssl_cafile"); + capath = settings_opt_get("ssl_capath"); + ciphers = settings_opt_get("ssl_ciphers"); #if !defined(HAVE_OPENSSL) && !defined(HAVE_GNUTLS) - if (ssl) { + if (ssl || sslvopt || cafile || capath || ciphers) { scr_LogPrint(LPRINT_LOGNORM, - "** Warning: SSL is NOT available, ignoring 'ssl' value"); - ssl = 0; + "** Warning: SSL is NOT available, ignoring ssl-related setting"); + ssl = sslverify = 0; + cafile = capath = ciphers = NULL; } #endif + cw_set_ssl_options(sslverify, cafile, capath, ciphers, servername); /* Connect to server */ scr_LogPrint(LPRINT_NORMAL|LPRINT_DEBUG, "Connecting to server: %s",