From 02acfd7d701b46329b201cde4086b864a61ba33d Mon Sep 17 00:00:00 2001 From: Tatsuhiro Tsujikawa Date: Sat, 2 Nov 2013 01:10:18 +0900 Subject: [PATCH] nghttpx: Add --verify-client option This option requires client certificate and successful verification. Use --cacert option to add CA certificates as necessary. --- src/shrpx.cc | 21 +++++++++++++++++++++ src/shrpx_config.cc | 12 ++++-------- src/shrpx_config.h | 3 +++ src/shrpx_ssl.cc | 24 +++++++++++++++++++++--- 4 files changed, 49 insertions(+), 11 deletions(-) diff --git a/src/shrpx.cc b/src/shrpx.cc index d092fb2d..50f7907c 100644 --- a/src/shrpx.cc +++ b/src/shrpx.cc @@ -417,6 +417,7 @@ void fill_default_config() mod_config()->write_rate = 0; mod_config()->write_burst = 0; mod_config()->npn_list = nullptr; + mod_config()->verify_client = false; } } // namespace @@ -593,6 +594,7 @@ void print_help(std::ostream& out) << " comma only and any white spaces are treated\n" << " as a part of protocol string.\n" << " Default: " << DEFAULT_NPN_LIST << "\n" + << " --verify-client Require and verify client certificate.\n" << "\n" << " HTTP/2.0 and SPDY:\n" << " -c, --spdy-max-concurrent-streams=\n" @@ -730,6 +732,7 @@ int main(int argc, char **argv) {"write-rate", required_argument, &flag, 36}, {"write-burst", required_argument, &flag, 37}, {"npn-list", required_argument, &flag, 38}, + {"verify-client", no_argument, &flag, 39}, {nullptr, 0, nullptr, 0 } }; int option_index = 0; @@ -933,6 +936,10 @@ int main(int argc, char **argv) // --npn-list cmdcfgs.push_back(std::make_pair(SHRPX_OPT_NPN_LIST, optarg)); break; + case 39: + // --verify-client + cmdcfgs.push_back(std::make_pair(SHRPX_OPT_VERIFY_CLIENT, "yes")); + break; default: break; } @@ -975,6 +982,20 @@ int main(int argc, char **argv) parse_config_npn_list(DEFAULT_NPN_LIST); } + if(!get_config()->subcerts.empty()) { + mod_config()->cert_tree = ssl::cert_lookup_tree_new(); + } + + for(auto& keycert : get_config()->subcerts) { + auto ssl_ctx = ssl::create_ssl_context(keycert.first.c_str(), + keycert.second.c_str()); + if(ssl::cert_lookup_tree_add_cert_from_file + (get_config()->cert_tree, ssl_ctx, keycert.second.c_str()) == -1) { + LOG(FATAL) << "Failed to add sub certificate."; + exit(EXIT_FAILURE); + } + } + if(get_config()->cert_file && get_config()->private_key_file) { mod_config()->default_ssl_ctx = ssl::create_ssl_context(get_config()->private_key_file, diff --git a/src/shrpx_config.cc b/src/shrpx_config.cc index 73750fbd..8f9c6f51 100644 --- a/src/shrpx_config.cc +++ b/src/shrpx_config.cc @@ -100,6 +100,7 @@ const char SHRPX_OPT_READ_BURST[] = "read-burst"; const char SHRPX_OPT_WRITE_RATE[] = "write-rate"; const char SHRPX_OPT_WRITE_BURST[] = "write-burst"; const char SHRPX_OPT_NPN_LIST[] = "npn-list"; +const char SHRPX_OPT_VERIFY_CLIENT[] = "verify-client"; namespace { Config *config = nullptr; @@ -335,14 +336,7 @@ int parse_config(const char *opt, const char *optarg) if(sp) { std::string keyfile(optarg, sp); // TODO Do we need private key for subcert? - auto ssl_ctx = ssl::create_ssl_context(keyfile.c_str(), sp+1); - if(!get_config()->cert_tree) { - mod_config()->cert_tree = ssl::cert_lookup_tree_new(); - } - if(ssl::cert_lookup_tree_add_cert_from_file(get_config()->cert_tree, - ssl_ctx, sp+1) == -1) { - return -1; - } + mod_config()->subcerts.emplace_back(keyfile, sp+1); } } else if(util::strieq(opt, SHRPX_OPT_SYSLOG)) { mod_config()->syslog = util::strieq(optarg, "yes"); @@ -413,6 +407,8 @@ int parse_config(const char *opt, const char *optarg) mod_config()->write_burst = strtoul(optarg, nullptr, 10); } else if(util::strieq(opt, SHRPX_OPT_NPN_LIST)) { parse_config_npn_list(optarg); + } else if(util::strieq(opt, SHRPX_OPT_VERIFY_CLIENT)) { + mod_config()->verify_client = util::strieq(optarg, "yes"); } else if(util::strieq(opt, "conf")) { LOG(WARNING) << "conf is ignored"; } else { diff --git a/src/shrpx_config.h b/src/shrpx_config.h index 91e323da..5cf704ba 100644 --- a/src/shrpx_config.h +++ b/src/shrpx_config.h @@ -91,6 +91,7 @@ extern const char SHRPX_OPT_READ_BURST[]; extern const char SHRPX_OPT_WRITE_RATE[]; extern const char SHRPX_OPT_WRITE_BURST[]; extern const char SHRPX_OPT_NPN_LIST[]; +extern const char SHRPX_OPT_VERIFY_CLIENT[]; union sockaddr_union { sockaddr sa; @@ -184,6 +185,8 @@ struct Config { char **npn_list; // The number of elements in npn_list size_t npn_list_len; + // The list of (private key file, certificate file) pair + std::vector> subcerts; }; const Config* get_config(); diff --git a/src/shrpx_ssl.cc b/src/shrpx_ssl.cc index b18d5b18..c226dad8 100644 --- a/src/shrpx_ssl.cc +++ b/src/shrpx_ssl.cc @@ -73,9 +73,14 @@ int next_proto_cb(SSL *s, const unsigned char **data, unsigned int *len, namespace { int verify_callback(int preverify_ok, X509_STORE_CTX *ctx) { - // We don't verify the client certificate. Just request it for the - // testing purpose. - return 1; + if(!preverify_ok) { + int err = X509_STORE_CTX_get_error(ctx); + int depth = X509_STORE_CTX_get_error_depth(ctx); + LOG(ERROR) << "client certificate verify error:num=" << err << ":" + << X509_verify_cert_error_string(err) + << ":depth=" << depth; + } + return preverify_ok; } } // namespace @@ -213,6 +218,19 @@ SSL_CTX* create_ssl_context(const char *private_key_file, DIE(); } if(get_config()->verify_client) { + if(SSL_CTX_set_default_verify_paths(ssl_ctx) != 1) { + LOG(WARNING) << "Could not load system trusted ca certificates: " + << ERR_error_string(ERR_get_error(), nullptr); + } + if(get_config()->cacert) { + if(SSL_CTX_load_verify_locations(ssl_ctx, get_config()->cacert, nullptr) + != 1) { + LOG(FATAL) << "Could not load trusted ca certificates from " + << get_config()->cacert << ": " + << ERR_error_string(ERR_get_error(), nullptr); + DIE(); + } + } SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE | SSL_VERIFY_FAIL_IF_NO_PEER_CERT,