diff --git a/doc/gmnisrvini.scd b/doc/gmnisrvini.scd index 69e8129..2c82b53 100644 --- a/doc/gmnisrvini.scd +++ b/doc/gmnisrvini.scd @@ -175,6 +175,12 @@ The following environment variables will be set: | *TLS_VERSION* : TLSv1.3 : The negotiated TLS version. +| *AUTH_TYPE* +: CERTIFICATE +: Compatibility with RFC 3785. +| *TLS_CLIENT_HASH* +: SHA256:BD3A388021A92017B781504A3D24F324BF9DE11CE72606AB445D98A8EB00C5A8 +: Unique fingerprint of the client certificate. \[1]: gemini://example.org/cgi-bin/foo.sh/bar?hello=world diff --git a/src/serve.c b/src/serve.c index 4b63dde..d77c2ff 100644 --- a/src/serve.c +++ b/src/serve.c @@ -199,7 +199,36 @@ serve_cgi(struct gmnisrv_client *client, const char *path, setenv("TLS_CIPHER", SSL_CIPHER_get_name(cipher), 1); setenv("TLS_VERSION", SSL_CIPHER_get_version(cipher), 1); - // TODO: Client certificate details + // barebones client cert implementation + // adapted from openssl(1)'s implementation + // TODO: support REMOTE_USER, TLS_CLIENT_NOT_{BEFORE,AFTER}, + // TLS_CLIENT_SERIAL_NUMBER + X509 *client_cert = SSL_get_peer_certificate(client->ssl); + if (client_cert != NULL) { + // 32 bytes because we're always using SHA256, but + // possibly change to EVP_MAX_MD_SIZE to support all + // of openssl's hash funcs + unsigned char digest[32]; + + if (X509_digest(client_cert, EVP_sha256(), digest, NULL)) { + setenv("AUTH_TYPE", "CERTIFICATE", 1); + // 32*2 because converting to hex doubles length + // +7 for "SHA256:" prefix + // +1 for null char + char hex_digest[32*2 + 7 + 1]; + strncat(hex_digest, "SHA256:", 8); + + char *cur_pos = hex_digest + 7; + for (int i = 0; i < 32; ++i) { + cur_pos += sprintf(cur_pos, "%02X", digest[i]); + } + setenv("TLS_CLIENT_HASH", hex_digest, 1); + } else { + const char *error = "Out of memory"; + client_submit_response(client, + GEMINI_STATUS_TEMPORARY_FAILURE, error, NULL); + } + } execlp(path, path, NULL); server_error("execlp: %s", strerror(errno)); diff --git a/src/tls.c b/src/tls.c index 26785a0..284cbef 100644 --- a/src/tls.c +++ b/src/tls.c @@ -14,6 +14,14 @@ #include "tls.h" #include "util.h" +static int +always_true_callback(X509_STORE_CTX *ctx, void *arg) +{ + (void)(ctx); + (void)(arg); + return 1; +} + static int tls_host_gencert(struct gmnisrv_tls *tlsconf, struct gmnisrv_host *host, const char *crtpath, const char *keypath) @@ -185,6 +193,9 @@ tls_init(struct gmnisrv_config *conf) assert(r == 1); SSL_CTX_set_tlsext_servername_callback(conf->tls.ssl_ctx, NULL); + SSL_CTX_set_verify(conf->tls.ssl_ctx, SSL_VERIFY_PEER, NULL); + // use always_true_callback to ignore errors such as self-signed error + SSL_CTX_set_cert_verify_callback(conf->tls.ssl_ctx, always_true_callback, NULL); // TLS re-negotiation is a fucking STUPID idea // I'm gating this behind an #ifdef based on an optimistic assumption