mod_ssl
for mod_authz_ldap
Certificate verification means mapping the pair (issuer DN, subject DN)
or even better the pair (issuer DN, serial number)
from the certificate to the user's directory node. mod_ssl
has access to all these components, but makes only the subject DN available
to other authentication and authorization modules.
However, CGI-programs do have access to all strings, but they are
exported to the environment only during the fixup phase, which happens
much later.
Internally, mod_ssl uses the ap_ctx_{get,set}
interface to store the subject's distinguished name,
and mod_authz_ldap and there is no reason why
mod_authz_ldap should not do the same.
However, the issuer distinguished name is not exported to the
context, which means we would have to either use direct SSL calls to
get at the issuer, or we could patch mod_ssl
to export
the issuer also.
Neither is the serial number, where we in addition have the problem how to represent large integers for the directory, so that search filters containing serial numbers can be formulated.
Patching is very easy, it is even easier to describe it here than to provide a patch for all the different versions of mod_ssl floating around. However, it has become a bit more difficult with the inclusion of the serial number code.
The following patch applies to the file ssl_engine_kernel.c
in the src/modules/ssl
directory of your apache tree.
*** ssl_engine_kernel.c Sun Feb 11 01:41:46 2001 --- /usr/local/src/networking/Web/apache_1.3.14/src/modules/ssl/ssl_engine_kernel.c Sun Feb 11 01:49:12 2001 *************** *** 216,221 **** --- 216,224 ---- * Predefine some client verification results */ ap_ctx_set(fb->ctx, "ssl::client::dn", NULL); + ap_ctx_set(fb->ctx, "ssl::client::serial", NULL); + ap_ctx_set(fb->ctx, "ssl::client::issuer::dn", NULL); + ap_ctx_set(fb->ctx, "ssl::client::certificate", NULL); ap_ctx_set(fb->ctx, "ssl::verify::error", NULL); ap_ctx_set(fb->ctx, "ssl::verify::info", NULL); SSL_set_verify_result(ssl, X509_V_OK); *************** *** 372,380 **** --- 375,437 ---- * Remember the peer certificate's DN */ if ((xs = SSL_get_peer_certificate(ssl)) != NULL) { + ASN1_INTEGER *bs; + cp = X509_NAME_oneline(X509_get_subject_name(xs), NULL, 0); ap_ctx_set(fb->ctx, "ssl::client::dn", ap_pstrdup(conn->pool, cp)); free(cp); + + cp = X509_NAME_oneline(X509_get_issuer_name(xs), NULL, 0); + ap_ctx_set(fb->ctx, "ssl::client::issuer::dn", + ap_pstrdup(conn->pool, cp)); + free(cp); + + /* the following code is adapted from crypto/asn1/t_x509.c */ + /* from the openssl distribution */ + do { /* we open a block here only to be allowed to */ + /* declare ASN1_INTEGER *bs */ + ASN1_INTEGER *bs; + char *cp1; + int i; + + bs = X509_get_serialNumber(xs); + cp1 = cp = ap_palloc(conn->pool, 1 + (3 * bs->length)); + memset(cp, 0, 1 + (3 * bs->length)); + if (bs->type == V_ASN1_NEG_INTEGER) { + *(cp1++) = '-'; + } + for (i = 0; i < bs->length; i++) { + if (i) *(cp1++) = ':'; + ap_snprintf(cp1, 3, "%02x", bs->data[i]); cp1 += 2; + } + ap_ctx_set(fb->ctx, "ssl::client::serial", cp); + } while (0); + + /* add the whole certificate in quoted form to the context */ + do { + BIO *bio_mem; + int l, j; + unsigned char *v; + char *cp, *p; + + /* read the data from the certificate */ + bio_mem = BIO_new(BIO_s_mem()); + i2d_X509_bio(bio_mem, xs); + l = BIO_ctrl_pending(bio_mem); + v = ap_palloc(conn->pool, l); + BIO_read(bio_mem, v, l); + BIO_free(bio_mem); + + /* quote the whole stuff */ + cp = ap_palloc(conn->pool, l * 3 + 1); + p = cp; + for (j = 0; j < l; j++) { + ap_snprintf(p, 4, "\\%02x", (unsigned char)v[j]); p+= 3; + } + + /* return the quoted certificate in the context */ + ap_ctx_set(fb->ctx, "ssl::client::certificate", cp); + } while (0); } /* *************** *** 1001,1006 **** --- 1058,1115 ---- ap_ctx_set(r->connection->client->ctx, "ssl::client::dn", ap_pstrdup(r->connection->pool, cp)); free(cp); + cp = X509_NAME_oneline(X509_get_issuer_name(cert), NULL, 0); + ap_ctx_set(r->connection->client->ctx, "ssl::client::issuer::dn", + ap_pstrdup(r->connection->pool, cp)); + free(cp); + /* the following code is adapted from crypto/asn1/t_x509.c */ + /* from the openssl distribution */ + do { /* we open a block here only to be allowed to */ + /* declare ASN1_INTEGER *bs */ + ASN1_INTEGER *bs; + char *cp1; + int i; + + bs = X509_get_serialNumber(cert); + cp1 = cp = ap_palloc(r->connection->pool, 1 + (3 * bs->length)); + memset(cp, 0, 1 + (3 * bs->length)); + if (bs->type == V_ASN1_NEG_INTEGER) { + *(cp1++) = '-'; + } + for (i = 0; i < bs->length; i++) { + if (i) *(cp1++) = ':'; + ap_snprintf(cp1, 3, "%02x", bs->data[i]); cp1 += 2; + } + ap_ctx_set(r->connection->client->ctx, + "ssl::client::serial", cp); + } while (0); + + /* add the whole certificate in quoted form to the context */ + do { + BIO *bio_mem; + int l, j; + unsigned char *v; + char *cp, *p; + + /* read the data from the certificate */ + bio_mem = BIO_new(BIO_s_mem()); + i2d_X509_bio(bio_mem, cert); + l = BIO_ctrl_pending(bio_mem); + v = ap_palloc(r->connection->pool, l); + BIO_read(bio_mem, v, l); + BIO_free(bio_mem); + + /* quote the whole stuff */ + cp = ap_palloc(r->connection->pool, l * 3 + 1); + p = cp; + for (j = 0; j < l; j++) { + ap_snprintf(p, 4, "\\%02x", (unsigned char)v[j]); p+= 3; + } + + /* return the quoted certificate in the context */ + ap_ctx_set(r->connection->client->ctx, + "ssl::client::certificate", cp); + } while (0); } /* *************** *** 1484,1489 **** --- 1593,1601 ---- ssl_log(s, SSL_LOG_ERROR, "Certificate Verification: Error (%d): %s", errnum, X509_verify_cert_error_string(errnum)); ap_ctx_set(conn->client->ctx, "ssl::client::dn", NULL); + ap_ctx_set(conn->client->ctx, "ssl::client::issuer::dn", NULL); + ap_ctx_set(conn->client->ctx, "ssl::client::serial", NULL); + ap_ctx_set(conn->client->ctx, "ssl::client::certificate", NULL); ap_ctx_set(conn->client->ctx, "ssl::verify::error", (void *)X509_verify_cert_error_string(errnum)); }