[Dovecot] mandatory client certificates and crl check in
ssl-proxy-openssl.c
HenkJan Wolthuis
hj.wolthuis at kaw.nl
Thu May 11 17:36:58 EEST 2006
hello,
I made a modification to ssl-proxy-openssl.c (patch attached) zo that it
a) disconnects when no client certificate is presented
b) checks the client certificate against the crl for our root cert. (so
you can't use a revoked client cert.)
c) returns the CommonName from the client cert. in
ssl_proxy_get_peer_name (this way it's easier to use dovecot as
imap-proxy with a passwd-like userdb, ssl_require_client_cert and
ssl_username_from_cert, it "binds" the emailuser to the
clientcertificate, a clientcert. can access only the account from the
userdb)
in order to use it, the CAfile must be a file which contains the
CAcertificate (pem format) followed by the CRL (also in pem format).
(servercert and the clientcerts are signed with a self-signed rootcert)
there are some issues with the patch:
a) it needs openssl > 0.9.7 for the CRL checking
b) ssl_verify_client_cert now returns 0 in case of an invalid cert. was
there a reason why it always returned 1?
c) i'm not too happy with the commonname extraction code, is it secure??
d) i've no experience with programming openssl or dovecot
e) i haven't programmed in C for at least 8 years......
does anyone here have more issues, corrections, comments on the patch?
can/should this functionality be implemented in dovecot? (conf-file option?)
--
groeten,
HenkJan Wolthuis
-------------- next part --------------
--- ssl-proxy-openssl.c.orig 2006-04-04 10:32:58.000000000 +0200
+++ ssl-proxy-openssl.c 2006-05-11 14:08:02.000000000 +0200
@@ -11,6 +11,7 @@
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
+#include <string.h>
#ifdef HAVE_OPENSSL
@@ -508,9 +509,42 @@
if (x509 == NULL)
return NULL; /* we should have had it.. */
+
+ // ORIG: X509_NAME_oneline(X509_get_subject_name(x509), buf, sizeof(buf));
+ // ORIG: name = t_strndup(buf, sizeof(buf));
+ // ORIG: X509_free(x509);
+
+ /* HJHJ get CommonName from peer-certificate */
X509_NAME_oneline(X509_get_subject_name(x509), buf, sizeof(buf));
- name = t_strndup(buf, sizeof(buf));
+
+ char *tmp;
+ char *start;
+ start = buf;
+ tmp = strstr( buf, "CN=" );
+ if( NULL != tmp )
+ {
+ if( strlen(tmp) > 3 ) /* CN= + something */
+ {
+ start = tmp + 3;
+ tmp = strstr( start, "/" );
+ if( NULL != tmp ) /* tmp == NULL: CN is last element in buf */
+ { *tmp = '\0'; }
+ }
+ else /* empty CN= */
+ {
+ *start = '\0';
+ i_warning("get_peer_name empty CN=");
+ }
+ }
+ else
+ {
+ *start = '\0';
+ i_warning("get_peer_name NO CN=");
+ } /* CN= not found (no certificate or certificate without commonname??) */
+
+ name = t_strndup(start, strlen(start) );
X509_free(x509);
+ /* HJHJ */
return *name == '\0' ? NULL : name;
}
@@ -582,10 +616,26 @@
proxy = SSL_get_ex_data(ssl, extdata_index);
proxy->cert_received = TRUE;
+ // ORIG: if (!preverify_ok)
+ // ORIG: proxy->cert_broken = TRUE;
+
+ // ORIG: return 1;
+
+ /* HJHJ */
+ char buf[1024];
+ X509_NAME_oneline( X509_get_subject_name(ctx->current_cert),buf,sizeof(buf));
+
if (!preverify_ok)
+ {
proxy->cert_broken = TRUE;
+ i_warning("BAD CERT %s: %s",X509_verify_cert_error_string(ctx->error),buf);
+ }
+ else
+ { i_warning("CERT: %s",buf); }
- return 1;
+ return preverify_ok;
+ /* HJHJ */
+
}
static int
@@ -666,10 +716,20 @@
if (getenv("SSL_VERIFY_CLIENT_CERT") != NULL) {
SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER |
- SSL_VERIFY_CLIENT_ONCE,
+ SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
ssl_verify_client_cert);
}
+ /* HJHJ */
+#if OPENSSL_VERSION_NUMBER >= 0x00907000L
+ X509_STORE *store;
+ if( (store=SSL_CTX_get_cert_store(ssl_ctx)) != NULL )
+ { X509_STORE_set_flags( store, X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL); }
+ else
+ { i_warning("X509 get cert store failed..."); }
+#endif
+ /* HJHJ */
+
/* PRNG initialization might want to use /dev/urandom, make sure it
does it before chrooting. We might not have enough entropy at
the first try, so this function may fail. It's still been
More information about the dovecot
mailing list