changeset 938:40175f3dcef7

SSL server certificate verification This patch enables SSL server certificate verification.
author Jefferson Ogata <ogata@antibozo.net>
date Sat, 08 Jul 2006 23:32:49 +0200
parents 548def60b810
children 12fa2ae6445d
files mcabber/connwrap/connwrap.c mcabber/connwrap/connwrap.h mcabber/libjabber/jconn.c mcabber/mcabberrc.example mcabber/src/main.c
diffstat 5 files changed, 159 insertions(+), 20 deletions(-) [+]
line wrap: on
line diff
--- 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",