Patching 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));
      }

© 2000 Dr. Andreas Müller, Beratung und Entwicklung
$Id: modssl-patch.html,v 1.2 2001/11/28 19:51:29 afm Exp $