SSL server certificate verification
This patch enables SSL server certificate verification.
--- 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;
--- 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);
--- 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;
}
--- 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,
--- 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",