Merge pull request #1037 from nghttp2/nghttpx-mruby-tls-client-vars
nghttpx: Add client fingerprint and subject name to mruby env
This commit is contained in:
commit
1a1a216d5a
|
@ -368,6 +368,14 @@ respectively.
|
|||
|
||||
Return the TLS SNI value which client sent in this connection.
|
||||
|
||||
.. rb:attr_reader:: tls_client_fingerprint
|
||||
|
||||
Return the SHA-256 fingerprint of a client certificate.
|
||||
|
||||
.. rb:attr_reader:: tls_client_subject_name
|
||||
|
||||
Return the subject name of a client certificate.
|
||||
|
||||
.. rb:class:: Request
|
||||
|
||||
Object to represent request from client. The modification to
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
#include "shrpx_mruby.h"
|
||||
#include "shrpx_mruby_module.h"
|
||||
#include "shrpx_log.h"
|
||||
#include "shrpx_tls.h"
|
||||
|
||||
namespace shrpx {
|
||||
|
||||
|
@ -140,6 +141,61 @@ 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) {
|
||||
auto data = static_cast<MRubyAssocData *>(mrb->ud);
|
||||
auto downstream = data->downstream;
|
||||
auto upstream = downstream->get_upstream();
|
||||
auto handler = upstream->get_client_handler();
|
||||
auto ssl = handler->get_ssl();
|
||||
|
||||
if (!ssl) {
|
||||
return mrb_str_new_static(mrb, "", 0);
|
||||
}
|
||||
|
||||
auto x = SSL_get_peer_certificate(ssl);
|
||||
if (!x) {
|
||||
return mrb_str_new_static(mrb, "", 0);
|
||||
}
|
||||
|
||||
// Fingerprint is SHA-256, so we need 32 bytes buffer.
|
||||
std::array<uint8_t, 32> buf;
|
||||
auto slen = tls::get_x509_fingerprint(buf.data(), buf.size(), x);
|
||||
if (slen == -1) {
|
||||
mrb_raise(mrb, E_RUNTIME_ERROR, "could not compute client fingerprint");
|
||||
}
|
||||
|
||||
// TODO Use template version of format_hex
|
||||
auto &balloc = downstream->get_block_allocator();
|
||||
auto f = util::format_hex(balloc,
|
||||
StringRef{std::begin(buf), std::begin(buf) + slen});
|
||||
return mrb_str_new(mrb, f.c_str(), f.size());
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
mrb_value env_get_tls_client_subject_name(mrb_state *mrb, mrb_value self) {
|
||||
auto data = static_cast<MRubyAssocData *>(mrb->ud);
|
||||
auto downstream = data->downstream;
|
||||
auto upstream = downstream->get_upstream();
|
||||
auto handler = upstream->get_client_handler();
|
||||
auto ssl = handler->get_ssl();
|
||||
|
||||
if (!ssl) {
|
||||
return mrb_str_new_static(mrb, "", 0);
|
||||
}
|
||||
|
||||
auto x = SSL_get_peer_certificate(ssl);
|
||||
if (!x) {
|
||||
return mrb_str_new_static(mrb, "", 0);
|
||||
}
|
||||
|
||||
auto &balloc = downstream->get_block_allocator();
|
||||
auto name = tls::get_x509_subject_name(balloc, x);
|
||||
return mrb_str_new(mrb, name.c_str(), name.size());
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void init_env_class(mrb_state *mrb, RClass *module) {
|
||||
auto env_class =
|
||||
mrb_define_class_under(mrb, module, "Env", mrb->object_class);
|
||||
|
@ -159,6 +215,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_subject_name",
|
||||
env_get_tls_client_subject_name, MRB_ARGS_NONE());
|
||||
}
|
||||
|
||||
} // namespace mruby
|
||||
|
|
|
@ -1920,6 +1920,37 @@ 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);
|
||||
unsigned int len = dstlen;
|
||||
if (X509_digest(x, EVP_sha256(), dst, &len) != 1) {
|
||||
return -1;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
StringRef get_x509_subject_name(BlockAllocator &balloc, X509 *x) {
|
||||
auto nm = X509_get_subject_name(x);
|
||||
|
||||
auto b = BIO_new(BIO_s_mem());
|
||||
if (!b) {
|
||||
return StringRef{};
|
||||
}
|
||||
|
||||
// Not documented, but it seems that X509_NAME_print_ex returns the
|
||||
// number of bytes written into b.
|
||||
auto slen = X509_NAME_print_ex(b, nm, 0, XN_FLAG_RFC2253);
|
||||
if (slen <= 0) {
|
||||
return StringRef{};
|
||||
}
|
||||
|
||||
auto iov = make_byte_ref(balloc, slen + 1);
|
||||
BIO_read(b, iov.base, slen);
|
||||
BIO_free(b);
|
||||
iov.base[slen] = '\0';
|
||||
return StringRef{iov.base, static_cast<size_t>(slen)};
|
||||
}
|
||||
|
||||
} // namespace tls
|
||||
|
||||
} // namespace shrpx
|
||||
|
|
|
@ -269,6 +269,15 @@ 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);
|
||||
|
||||
// Returns subject name of |x|. If this function fails to get subject
|
||||
// name, it returns an empty string.
|
||||
StringRef get_x509_subject_name(BlockAllocator &balloc, X509 *x);
|
||||
|
||||
} // namespace tls
|
||||
|
||||
} // namespace shrpx
|
||||
|
|
Loading…
Reference in New Issue