On Monday 29 November 2010, Dr Stephen Henson wrote:
> On 24/11/2010 07:07, Kaspar Brand wrote:
> > On 20.11.2010 20:24, Stefan Fritsch wrote:
> >> On Fri, 19 Nov 2010, Joe Orton wrote:
> >>> We could support this better by having a new set of exports:
> >>> SSL_{CLIENT,SERVER}_{I,S}_UTF8DN_*(_n)?
> >>>
> >>> (or something similarly named)
> >>>
> >>> which works the same as _DN_ but exports the attributes as a
> >>> UTF-8 byte seequence regardless of the underlying ASN.1 type;
> >>> this would be a relatively simple hack.
> >>
> >> Or have a (per vhost) directive that enables conversion for all
> >> SSL_*_S_DN_* and SSL_*_S_DN to UTF8. IMHO, this could even be
> >> enabled by default in 2.4.
> >
> > I prefer the latter approach, yes (there's already an awful lot
> > of SSL_* something variables).
> >
> > Given the fact that mod_ssl's current behavior with non-ASCII
> > characters (i.e., outside the 0-127 range) is mostly undefined
> > and/or sometimes even erroneous (a BMPString in the subject or
> > issuer DN will end up as an empty SSL_*_*_DN_* variable, due to
> > the initial null byte), I would suggest the following solution:
> >
> > - for all SSL_{CLIENT,SERVER}_{I,S}_DN_* variables, use UTF-8 by
> > default
> >
> > (i.e., adapt ssl_engine_vars.c:ssl_var_lookup_ssl_cert_dn() to
> > convert TeletexString, UniversalString and BMPString types to
> > UTF8String)
> >
> > - for SSL_{CLIENT,SERVER}_{I,S}_DN, don't use X509_NAME_oneline()
> >
> > any more and switch to X509_NAME_print_ex() instead. What flags
> > should be used is probably debatable - I would recommend to go
> > with XN_FLAG_RFC2253 (note that using XN_FLAG_ONELINE with
> > X509_NAME_print_ex doesn't produce the same string as
> > X509_NAME_oneline, so this will break backward compatibility
> > in any case)
> >
> > - add another parameter to the SSLOptions directive which allows
> >
> > re-enabling the "old" string rendering for
> > SSL_{CLIENT,SERVER}_{I,S}_DN (so the new default would be to
> > rely on X509_NAME_print_ex - even for 2.2 -, but people could
> > restore the current behavior through this option)
>
> It's a very good idea to avoid X509_NAME_oneline() wherever
> possible as it is highly broken, can produce ambiguous output (of
> the Bobby Tables variety) and at other times be just plain wrong
> (BMPStrings is one of many examples).
>
> We have to retain it in OpenSSL for backwards compatibility though.
> I'd throw it out tomorrow if I could get away with it.
>
> You can get a UTF8String from most string types using
> ASN1_STRING_to_UTF8(). This should be adequate for most purposes:
> it doesn't handle the more bizarre TeletexString shift conversions
> but those are rarely encountered in practice.
I have come up with the attached patch which more or less implements
Kaspar's suggestions. I am using
"XN_FLAG_RFC2253 & ~ASN1_STRFLGS_ESC_MSB" as flags for
X509_NAME_print_ex() for SSL_{CLIENT,SERVER}_{I,S}_DN. This changes
/C=US/ST=California/L=San Francisco/O=ASF/OU=httpd-
test/CN=ca/[email protected]
into
[email protected],CN=ca,OU=httpd-test,O=ASF,L=San
Francisco,ST=California,C=US
Is this what we want? We could use XN_FLAG_DN_REV to keep the old
order. I haven't tried UTF8 characters, yet.
Instead of an SSLOption (which would require a request_rec to lookup),
I have implemented a per-vhost option for restoring compatibility.
diff --git a/modules/ssl/mod_ssl.c b/modules/ssl/mod_ssl.c
index 3d090cb..fc36f6c 100644
--- a/modules/ssl/mod_ssl.c
+++ b/modules/ssl/mod_ssl.c
@@ -128,6 +128,8 @@ static const command_rec ssl_config_cmds[] = {
"Use the server's cipher ordering preference")
SSL_CMD_SRV(InsecureRenegotiation, FLAG,
"Enable support for insecure renegotiation")
+ SSL_CMD_SRV(LegacyDNVarFormat, FLAG,
+ "Use legacy format for SSL_{CLIENT,SERVER}_{I,S}_DN variables")
SSL_CMD_ALL(UserName, TAKE1,
"Set user name to SSL variable value")
SSL_CMD_SRV(StrictSNIVHostCheck, FLAG,
diff --git a/modules/ssl/ssl_engine_config.c b/modules/ssl/ssl_engine_config.c
index 4bccfe7..de4b621 100644
--- a/modules/ssl/ssl_engine_config.c
+++ b/modules/ssl/ssl_engine_config.c
@@ -186,6 +186,7 @@ static SSLSrvConfigRec *ssl_config_server_new(apr_pool_t *p)
sc->session_cache_timeout = UNSET;
sc->cipher_server_pref = UNSET;
sc->insecure_reneg = UNSET;
+ sc->legacy_dn_format = UNSET;
sc->proxy_ssl_check_peer_expire = SSL_ENABLED_UNSET;
sc->proxy_ssl_check_peer_cn = SSL_ENABLED_UNSET;
#ifndef OPENSSL_NO_TLSEXT
@@ -298,6 +299,7 @@ void *ssl_config_server_merge(apr_pool_t *p, void *basev, void *addv)
cfgMergeInt(session_cache_timeout);
cfgMergeBool(cipher_server_pref);
cfgMergeBool(insecure_reneg);
+ cfgMergeBool(legacy_dn_format);
cfgMerge(proxy_ssl_check_peer_expire, SSL_ENABLED_UNSET);
cfgMerge(proxy_ssl_check_peer_cn, SSL_ENABLED_UNSET);
#ifndef OPENSSL_NO_TLSEXT
@@ -669,6 +671,13 @@ const char *ssl_cmd_SSLInsecureRenegotiation(cmd_parms *cmd, void *dcfg, int fla
#endif
}
+const char *ssl_cmd_SSLLegacyDNVarFormat(cmd_parms *cmd, void *dcfg, int flag)
+{
+ SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+ sc->legacy_dn_format = flag?TRUE:FALSE;
+ return NULL;
+}
+
static const char *ssl_cmd_check_dir(cmd_parms *parms,
const char **dir)
diff --git a/modules/ssl/ssl_engine_vars.c b/modules/ssl/ssl_engine_vars.c
index d5cf63b..30bdd81 100644
--- a/modules/ssl/ssl_engine_vars.c
+++ b/modules/ssl/ssl_engine_vars.c
@@ -40,7 +40,7 @@
*/
static char *ssl_var_lookup_ssl(apr_pool_t *p, conn_rec *c, char *var);
-static char *ssl_var_lookup_ssl_cert(apr_pool_t *p, X509 *xs, char *var);
+static char *ssl_var_lookup_ssl_cert(apr_pool_t *p, X509 *xs, char *var, int legacy_format);
static char *ssl_var_lookup_ssl_cert_dn(apr_pool_t *p, X509_NAME *xsname, char *var);
static char *ssl_var_lookup_ssl_cert_valid(apr_pool_t *p, ASN1_UTCTIME *tm);
static char *ssl_var_lookup_ssl_cert_remain(apr_pool_t *p, ASN1_UTCTIME *tm);
@@ -358,13 +358,19 @@ static char *ssl_var_lookup_ssl(apr_pool_t *p, conn_rec *c, char *var)
}
else if (ssl != NULL && strlen(var) > 7 && strcEQn(var, "CLIENT_", 7)) {
if ((xs = SSL_get_peer_certificate(ssl)) != NULL) {
- result = ssl_var_lookup_ssl_cert(p, xs, var+7);
+ SSLSrvConfigRec *sc = mySrvConfigFromConn(c);
+ result = ssl_var_lookup_ssl_cert(p, xs, var+7, sc->legacy_dn_format);
X509_free(xs);
}
}
else if (ssl != NULL && strlen(var) > 7 && strcEQn(var, "SERVER_", 7)) {
- if ((xs = SSL_get_certificate(ssl)) != NULL)
- result = ssl_var_lookup_ssl_cert(p, xs, var+7);
+ if ((xs = SSL_get_certificate(ssl)) != NULL) {
+ SSLSrvConfigRec *sc = mySrvConfigFromConn(c);
+ result = ssl_var_lookup_ssl_cert(p, xs, var+7, sc->legacy_dn_format);
+ /* SSL_get_certificate is different from SSL_get_peer_certificate.
+ * No need to X509_free(xs).
+ */
+ }
}
else if (ssl != NULL && strcEQ(var, "COMPRESS_METHOD")) {
result = ssl_var_lookup_ssl_compress_meth(ssl);
@@ -386,13 +392,39 @@ static char *ssl_var_lookup_ssl(apr_pool_t *p, conn_rec *c, char *var)
return result;
}
-static char *ssl_var_lookup_ssl_cert(apr_pool_t *p, X509 *xs, char *var)
+static char *ssl_var_lookup_ssl_cert_dn_oneline(apr_pool_t *p,
+ X509_NAME *xsname,
+ int legacy_dn_format)
+{
+ char *result;
+ if (legacy_dn_format == TRUE) {
+ char *cp = X509_NAME_oneline(xsname, NULL, 0);
+ result = apr_pstrdup(p, cp);
+ modssl_free(cp);
+ }
+ else {
+ BIO* bio;
+ int n;
+ unsigned long flags = XN_FLAG_RFC2253 & ~ASN1_STRFLGS_ESC_MSB;
+ if ((bio = BIO_new(BIO_s_mem())) == NULL)
+ return NULL;
+ X509_NAME_print_ex(bio, xsname, 0, flags);
+ n = BIO_pending(bio);
+ result = apr_pcalloc(p, n+1);
+ n = BIO_read(bio, result, n);
+ result[n] = NUL;
+ BIO_free(bio);
+ }
+ return result;
+}
+
+static char *ssl_var_lookup_ssl_cert(apr_pool_t *p, X509 *xs, char *var,
+ int legacy_format)
{
char *result;
BOOL resdup;
X509_NAME *xsname;
int nid;
- char *cp;
result = NULL;
resdup = TRUE;
@@ -416,9 +448,7 @@ static char *ssl_var_lookup_ssl_cert(apr_pool_t *p, X509 *xs, char *var)
}
else if (strcEQ(var, "S_DN")) {
xsname = X509_get_subject_name(xs);
- cp = X509_NAME_oneline(xsname, NULL, 0);
- result = apr_pstrdup(p, cp);
- modssl_free(cp);
+ result = ssl_var_lookup_ssl_cert_dn_oneline(p, xsname, legacy_format);
resdup = FALSE;
}
else if (strlen(var) > 5 && strcEQn(var, "S_DN_", 5)) {
@@ -428,9 +458,7 @@ static char *ssl_var_lookup_ssl_cert(apr_pool_t *p, X509 *xs, char *var)
}
else if (strcEQ(var, "I_DN")) {
xsname = X509_get_issuer_name(xs);
- cp = X509_NAME_oneline(xsname, NULL, 0);
- result = apr_pstrdup(p, cp);
- modssl_free(cp);
+ result = ssl_var_lookup_ssl_cert_dn_oneline(p, xsname, legacy_format);
resdup = FALSE;
}
else if (strlen(var) > 5 && strcEQn(var, "I_DN_", 5)) {
@@ -516,13 +544,16 @@ static char *ssl_var_lookup_ssl_cert_dn(apr_pool_t *p, X509_NAME *xsname, char *
n =OBJ_obj2nid((ASN1_OBJECT *)X509_NAME_ENTRY_get_object(xsne));
if (n == ssl_var_lookup_ssl_cert_dn_rec[i].nid && idx-- == 0) {
- unsigned char *data = X509_NAME_ENTRY_get_data_ptr(xsne);
- /* cast needed from unsigned char to char */
- result = apr_pstrmemdup(p, (char *)data,
- X509_NAME_ENTRY_get_data_len(xsne));
+ unsigned char *string = NULL;
+ int ret = ASN1_STRING_to_UTF8(&string, X509_NAME_ENTRY_get_data(xsne));
+ if (ret >= 0) {
+ /* cast needed from unsigned char to char */
+ result = apr_pstrmemdup(p, (char *)string, ret);
+ modssl_free(string);
#if APR_CHARSET_EBCDIC
- ap_xlate_proto_from_ascii(result, X509_NAME_ENTRY_get_data_len(xsne));
+ ap_xlate_proto_from_ascii(result, ret);
#endif /* APR_CHARSET_EBCDIC */
+ }
break;
}
}
diff --git a/modules/ssl/ssl_private.h b/modules/ssl/ssl_private.h
index ae344a1..f390808 100644
--- a/modules/ssl/ssl_private.h
+++ b/modules/ssl/ssl_private.h
@@ -528,6 +528,7 @@ struct SSLSrvConfigRec {
int session_cache_timeout;
BOOL cipher_server_pref;
BOOL insecure_reneg;
+ BOOL legacy_dn_format;
modssl_ctx_t *server;
modssl_ctx_t *proxy;
ssl_enabled_t proxy_ssl_check_peer_expire;
@@ -602,7 +603,8 @@ const char *ssl_cmd_SSLRequire(cmd_parms *, void *, const char *);
const char *ssl_cmd_SSLUserName(cmd_parms *, void *, const char *);
const char *ssl_cmd_SSLRenegBufferSize(cmd_parms *cmd, void *dcfg, const char *arg);
const char *ssl_cmd_SSLStrictSNIVHostCheck(cmd_parms *cmd, void *dcfg, int flag);
-const char *ssl_cmd_SSLInsecureRenegotiation(cmd_parms *cmd, void *dcfg, int flag);
+const char *ssl_cmd_SSLInsecureRenegotiation(cmd_parms *cmd, void *dcfg, int flag);
+const char *ssl_cmd_SSLLegacyDNVarFormat(cmd_parms *cmd, void *dcfg, int flag);
const char *ssl_cmd_SSLProxyEngine(cmd_parms *cmd, void *dcfg, int flag);
const char *ssl_cmd_SSLProxyProtocol(cmd_parms *, void *, const char *);