nghttpx: Add client fingerprint and subject name to mruby env

This commit is contained in:
Tatsuhiro Tsujikawa 2017-10-29 00:25:20 +09:00
parent c573c80bd3
commit 9f80a82c1a
4 changed files with 108 additions and 0 deletions

View File

@ -368,6 +368,14 @@ respectively.
Return the TLS SNI value which client sent in this connection. 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 .. rb:class:: Request
Object to represent request from client. The modification to Object to represent request from client. The modification to

View File

@ -34,6 +34,7 @@
#include "shrpx_mruby.h" #include "shrpx_mruby.h"
#include "shrpx_mruby_module.h" #include "shrpx_mruby_module.h"
#include "shrpx_log.h" #include "shrpx_log.h"
#include "shrpx_tls.h"
namespace shrpx { namespace shrpx {
@ -140,6 +141,61 @@ mrb_value env_get_tls_sni(mrb_state *mrb, mrb_value self) {
} }
} // namespace } // 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) { void init_env_class(mrb_state *mrb, RClass *module) {
auto env_class = auto env_class =
mrb_define_class_under(mrb, module, "Env", mrb->object_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_ARGS_NONE());
mrb_define_method(mrb, env_class, "tls_sni", env_get_tls_sni, mrb_define_method(mrb, env_class, "tls_sni", env_get_tls_sni,
MRB_ARGS_NONE()); 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 } // namespace mruby

View File

@ -1920,6 +1920,37 @@ int verify_ocsp_response(SSL_CTX *ssl_ctx, const uint8_t *ocsp_resp,
return 0; 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 tls
} // namespace shrpx } // namespace shrpx

View File

@ -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, int verify_ocsp_response(SSL_CTX *ssl_ctx, const uint8_t *ocsp_resp,
size_t ocsp_resplen); 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 tls
} // namespace shrpx } // namespace shrpx