diff --git a/doc/nghttpx.h2r b/doc/nghttpx.h2r index 46138042..107a0108 100644 --- a/doc/nghttpx.h2r +++ b/doc/nghttpx.h2r @@ -368,10 +368,14 @@ respectively. Return the TLS SNI value which client sent in this connection. - .. rb:attr_reader:: tls_client_fingerprint + .. rb:attr_reader:: tls_client_fingerprint_sha256 Return the SHA-256 fingerprint of a client certificate. + .. rb:attr_reader:: tls_client_fingerprint_sha1 + + Return the SHA-1 fingerprint of a client certificate. + .. rb:attr_reader:: tls_client_subject_name Return the subject name of a client certificate. diff --git a/gennghttpxfun.py b/gennghttpxfun.py index fffa3f8e..054444aa 100755 --- a/gennghttpxfun.py +++ b/gennghttpxfun.py @@ -191,7 +191,8 @@ LOGVARS = [ "tls_session_id", "tls_session_reused", "tls_sni", - "tls_client_fingerprint", + "tls_client_fingerprint_sha256", + "tls_client_fingerprint_sha1", "tls_client_subject_name", "backend_host", "backend_port", diff --git a/src/shrpx.cc b/src/shrpx.cc index 2656b717..b53ca7ee 100644 --- a/src/shrpx.cc +++ b/src/shrpx.cc @@ -2482,8 +2482,10 @@ Logging: the response. For HTTP/1, ALPN is always http/1.1, regardless of minor version. * $tls_cipher: cipher used for SSL/TLS connection. - * $tls_client_fingerprint: SHA-256 fingerprint of client - certificate. + * $tls_client_fingerprint_sha256: SHA-256 fingerprint of + client certificate. + * $tls_client_fingerprint_sha1: SHA-1 fingerprint of + client certificate. * $tls_client_subject_name: subject name in client certificate. * $tls_protocol: protocol for SSL/TLS connection. diff --git a/src/shrpx_config.cc b/src/shrpx_config.cc index 6969c4de..1efe0c04 100644 --- a/src/shrpx_config.cc +++ b/src/shrpx_config.cc @@ -510,15 +510,6 @@ LogFragmentType log_var_lookup_token(const char *name, size_t namelen) { break; } break; - case 22: - switch (name[21]) { - case 't': - if (util::strieq_l("tls_client_fingerprin", name, 21)) { - return SHRPX_LOGF_TLS_CLIENT_FINGERPRINT; - } - break; - } - break; case 23: switch (name[22]) { case 'e': @@ -528,6 +519,24 @@ LogFragmentType log_var_lookup_token(const char *name, size_t namelen) { break; } break; + case 27: + switch (name[26]) { + case '1': + if (util::strieq_l("tls_client_fingerprint_sha", name, 26)) { + return SHRPX_LOGF_TLS_CLIENT_FINGERPRINT_SHA1; + } + break; + } + break; + case 29: + switch (name[28]) { + case '6': + if (util::strieq_l("tls_client_fingerprint_sha25", name, 28)) { + return SHRPX_LOGF_TLS_CLIENT_FINGERPRINT_SHA256; + } + break; + } + break; } return SHRPX_LOGF_NONE; } diff --git a/src/shrpx_log.cc b/src/shrpx_log.cc index c803378c..84624eaa 100644 --- a/src/shrpx_log.cc +++ b/src/shrpx_log.cc @@ -533,7 +533,8 @@ void upstream_accesslog(const std::vector &lfv, } std::tie(p, last) = copy_escape(lgsp.sni, p, last); break; - case SHRPX_LOGF_TLS_CLIENT_FINGERPRINT: { + case SHRPX_LOGF_TLS_CLIENT_FINGERPRINT_SHA1: + case SHRPX_LOGF_TLS_CLIENT_FINGERPRINT_SHA256: { if (!lgsp.ssl) { std::tie(p, last) = copy('-', p, last); break; @@ -544,7 +545,10 @@ void upstream_accesslog(const std::vector &lfv, break; } std::array buf; - auto len = tls::get_x509_fingerprint(buf.data(), buf.size(), x); + auto len = tls::get_x509_fingerprint( + buf.data(), buf.size(), x, + lf.type == SHRPX_LOGF_TLS_CLIENT_FINGERPRINT_SHA256 ? EVP_sha256() + : EVP_sha1()); X509_free(x); if (len <= 0) { std::tie(p, last) = copy('-', p, last); diff --git a/src/shrpx_log.h b/src/shrpx_log.h index a413ce85..9584e6ae 100644 --- a/src/shrpx_log.h +++ b/src/shrpx_log.h @@ -138,7 +138,8 @@ enum LogFragmentType { SHRPX_LOGF_TLS_SESSION_REUSED, SHRPX_LOGF_SSL_SESSION_REUSED = SHRPX_LOGF_TLS_SESSION_REUSED, SHRPX_LOGF_TLS_SNI, - SHRPX_LOGF_TLS_CLIENT_FINGERPRINT, + SHRPX_LOGF_TLS_CLIENT_FINGERPRINT_SHA1, + SHRPX_LOGF_TLS_CLIENT_FINGERPRINT_SHA256, SHRPX_LOGF_TLS_CLIENT_SUBJECT_NAME, SHRPX_LOGF_BACKEND_HOST, SHRPX_LOGF_BACKEND_PORT, diff --git a/src/shrpx_mruby_module_env.cc b/src/shrpx_mruby_module_env.cc index 5a7b9469..9805e078 100644 --- a/src/shrpx_mruby_module_env.cc +++ b/src/shrpx_mruby_module_env.cc @@ -142,7 +142,7 @@ mrb_value env_get_tls_sni(mrb_state *mrb, mrb_value self) { } // namespace namespace { -mrb_value env_get_tls_client_fingerprint(mrb_state *mrb, mrb_value self) { +mrb_value env_get_tls_client_fingerprint_md(mrb_state *mrb, const EVP_MD *md) { auto data = static_cast(mrb->ud); auto downstream = data->downstream; auto upstream = downstream->get_upstream(); @@ -158,9 +158,9 @@ mrb_value env_get_tls_client_fingerprint(mrb_state *mrb, mrb_value self) { return mrb_str_new_static(mrb, "", 0); } - // Fingerprint is SHA-256, so we need 32 bytes buffer. + // Currently the largest hash value is SHA-256, which is 32 bytes. std::array buf; - auto slen = tls::get_x509_fingerprint(buf.data(), buf.size(), x); + auto slen = tls::get_x509_fingerprint(buf.data(), buf.size(), x, md); X509_free(x); if (slen == -1) { mrb_raise(mrb, E_RUNTIME_ERROR, "could not compute client fingerprint"); @@ -174,6 +174,19 @@ mrb_value env_get_tls_client_fingerprint(mrb_state *mrb, mrb_value self) { } } // namespace +namespace { +mrb_value env_get_tls_client_fingerprint_sha256(mrb_state *mrb, + mrb_value self) { + return env_get_tls_client_fingerprint_md(mrb, EVP_sha256()); +} +} // namespace + +namespace { +mrb_value env_get_tls_client_fingerprint_sha1(mrb_state *mrb, mrb_value self) { + return env_get_tls_client_fingerprint_md(mrb, EVP_sha1()); +} +} // namespace + namespace { mrb_value env_get_tls_client_subject_name(mrb_state *mrb, mrb_value self) { auto data = static_cast(mrb->ud); @@ -303,8 +316,10 @@ void init_env_class(mrb_state *mrb, RClass *module) { MRB_ARGS_NONE()); mrb_define_method(mrb, env_class, "tls_sni", env_get_tls_sni, MRB_ARGS_NONE()); - mrb_define_method(mrb, env_class, "tls_client_fingerprint", - env_get_tls_client_fingerprint, MRB_ARGS_NONE()); + mrb_define_method(mrb, env_class, "tls_client_fingerprint_sha256", + env_get_tls_client_fingerprint_sha256, MRB_ARGS_NONE()); + mrb_define_method(mrb, env_class, "tls_client_fingerprint_sha1", + env_get_tls_client_fingerprint_sha1, MRB_ARGS_NONE()); mrb_define_method(mrb, env_class, "tls_client_subject_name", env_get_tls_client_subject_name, MRB_ARGS_NONE()); mrb_define_method(mrb, env_class, "tls_cipher", env_get_tls_cipher, diff --git a/src/shrpx_tls.cc b/src/shrpx_tls.cc index 806ae103..8126b5ce 100644 --- a/src/shrpx_tls.cc +++ b/src/shrpx_tls.cc @@ -1920,10 +1920,10 @@ int verify_ocsp_response(SSL_CTX *ssl_ctx, const uint8_t *ocsp_resp, return 0; } -ssize_t get_x509_fingerprint(uint8_t *dst, size_t dstlen, X509 *x) { - assert(dstlen >= 32); +ssize_t get_x509_fingerprint(uint8_t *dst, size_t dstlen, const X509 *x, + const EVP_MD *md) { unsigned int len = dstlen; - if (X509_digest(x, EVP_sha256(), dst, &len) != 1) { + if (X509_digest(x, md, dst, &len) != 1) { return -1; } return len; diff --git a/src/shrpx_tls.h b/src/shrpx_tls.h index 6fd8d2b6..87ed7f3e 100644 --- a/src/shrpx_tls.h +++ b/src/shrpx_tls.h @@ -269,10 +269,12 @@ int proto_version_from_string(const StringRef &v); int verify_ocsp_response(SSL_CTX *ssl_ctx, const uint8_t *ocsp_resp, size_t ocsp_resplen); -// Stores SHA-256 fingerprint of |x| in |dst| of length |dstlen|. -// |dstlen| must be larger than 32 bytes. This function returns the -// number of bytes written in |dst|, or -1. -ssize_t get_x509_fingerprint(uint8_t *dst, size_t dstlen, X509 *x); +// Stores fingerprint of |x| in |dst| of length |dstlen|. |md| +// specifies hash function to use, and |dstlen| must be large enough +// to include hash value (e.g., 32 bytes for SHA-256). This function +// returns the number of bytes written in |dst|, or -1. +ssize_t get_x509_fingerprint(uint8_t *dst, size_t dstlen, const X509 *x, + const EVP_MD *md); // Returns subject name of |x|. If this function fails to get subject // name, it returns an empty string.