~sircmpwn/gmnisrv

d7bd3c6a0db4c79f244d8880d86a899eb0f8ca01 — dece 3 years ago 0bf3cde
Support TLS_CLIENT_SERIAL_NUMBER in CGI
2 files changed, 18 insertions(+), 2 deletions(-)

M doc/gmnisrvini.scd
M src/serve.c
M doc/gmnisrvini.scd => doc/gmnisrvini.scd +3 -0
@@ 181,6 181,9 @@ The following environment variables will be set:
|  *TLS_CLIENT_HASH*
:  SHA256:BD3A388021A92017B781504A3D24F324BF9DE11CE72606AB445D98A8EB00C5A8
:  Unique fingerprint of the client certificate.
|  *TLS_CLIENT_SERIAL_NUMBER*
:  717823108622037499122666796829184010024179612962
:  Serial number (decimal) of the client certificate.

\[1]: gemini://example.org/cgi-bin/foo.sh/bar?hello=world


M src/serve.c => src/serve.c +15 -2
@@ 200,8 200,7 @@ serve_cgi(struct gmnisrv_client *client, const char *path,

		// barebones client cert implementation
		// adapted from openssl(1)'s implementation
		// TODO: support REMOTE_USER, TLS_CLIENT_NOT_{BEFORE,AFTER},
		//       TLS_CLIENT_SERIAL_NUMBER
		// TODO: support REMOTE_USER, TLS_CLIENT_NOT_{BEFORE,AFTER}
		X509 *client_cert = SSL_get_peer_certificate(client->ssl);
		if (client_cert != NULL) {
			// 32 bytes because we're always using SHA256, but


@@ 226,6 225,20 @@ serve_cgi(struct gmnisrv_client *client, const char *path,
				const char *error = "Out of memory";
				client_submit_response(client,
						GEMINI_STATUS_TEMPORARY_FAILURE, error, NULL);
				goto post_client_cert_parsing;
			}

			const ASN1_INTEGER *sn_asn = X509_get0_serialNumber(client_cert);
			BIGNUM *sn_bn = ASN1_INTEGER_to_BN(sn_asn, NULL);
			char *sn_dec = BN_bn2dec(sn_bn);
			BN_free(sn_bn);
			if (sn_dec != NULL) {
				setenv("TLS_CLIENT_SERIAL_NUMBER", sn_dec, 1);
				OPENSSL_free(sn_dec);
			} else {
				const char *error = "Invalid certificate serial number";
				client_submit_response(client,
						GEMINI_STATUS_CERTIFICATE_NOT_VALID, error, NULL);
			}
		}