nghttpx: Add --verify-client option

This option requires client certificate and successful verification.
Use --cacert option to add CA certificates as necessary.
This commit is contained in:
Tatsuhiro Tsujikawa 2013-11-02 01:10:18 +09:00
parent 1835bda02e
commit 02acfd7d70
4 changed files with 49 additions and 11 deletions

View File

@ -417,6 +417,7 @@ void fill_default_config()
mod_config()->write_rate = 0; mod_config()->write_rate = 0;
mod_config()->write_burst = 0; mod_config()->write_burst = 0;
mod_config()->npn_list = nullptr; mod_config()->npn_list = nullptr;
mod_config()->verify_client = false;
} }
} // namespace } // namespace
@ -593,6 +594,7 @@ void print_help(std::ostream& out)
<< " comma only and any white spaces are treated\n" << " comma only and any white spaces are treated\n"
<< " as a part of protocol string.\n" << " as a part of protocol string.\n"
<< " Default: " << DEFAULT_NPN_LIST << "\n" << " Default: " << DEFAULT_NPN_LIST << "\n"
<< " --verify-client Require and verify client certificate.\n"
<< "\n" << "\n"
<< " HTTP/2.0 and SPDY:\n" << " HTTP/2.0 and SPDY:\n"
<< " -c, --spdy-max-concurrent-streams=<NUM>\n" << " -c, --spdy-max-concurrent-streams=<NUM>\n"
@ -730,6 +732,7 @@ int main(int argc, char **argv)
{"write-rate", required_argument, &flag, 36}, {"write-rate", required_argument, &flag, 36},
{"write-burst", required_argument, &flag, 37}, {"write-burst", required_argument, &flag, 37},
{"npn-list", required_argument, &flag, 38}, {"npn-list", required_argument, &flag, 38},
{"verify-client", no_argument, &flag, 39},
{nullptr, 0, nullptr, 0 } {nullptr, 0, nullptr, 0 }
}; };
int option_index = 0; int option_index = 0;
@ -933,6 +936,10 @@ int main(int argc, char **argv)
// --npn-list // --npn-list
cmdcfgs.push_back(std::make_pair(SHRPX_OPT_NPN_LIST, optarg)); cmdcfgs.push_back(std::make_pair(SHRPX_OPT_NPN_LIST, optarg));
break; break;
case 39:
// --verify-client
cmdcfgs.push_back(std::make_pair(SHRPX_OPT_VERIFY_CLIENT, "yes"));
break;
default: default:
break; break;
} }
@ -975,6 +982,20 @@ int main(int argc, char **argv)
parse_config_npn_list(DEFAULT_NPN_LIST); 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) { if(get_config()->cert_file && get_config()->private_key_file) {
mod_config()->default_ssl_ctx = mod_config()->default_ssl_ctx =
ssl::create_ssl_context(get_config()->private_key_file, ssl::create_ssl_context(get_config()->private_key_file,

View File

@ -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_RATE[] = "write-rate";
const char SHRPX_OPT_WRITE_BURST[] = "write-burst"; const char SHRPX_OPT_WRITE_BURST[] = "write-burst";
const char SHRPX_OPT_NPN_LIST[] = "npn-list"; const char SHRPX_OPT_NPN_LIST[] = "npn-list";
const char SHRPX_OPT_VERIFY_CLIENT[] = "verify-client";
namespace { namespace {
Config *config = nullptr; Config *config = nullptr;
@ -335,14 +336,7 @@ int parse_config(const char *opt, const char *optarg)
if(sp) { if(sp) {
std::string keyfile(optarg, sp); std::string keyfile(optarg, sp);
// TODO Do we need private key for subcert? // TODO Do we need private key for subcert?
auto ssl_ctx = ssl::create_ssl_context(keyfile.c_str(), sp+1); mod_config()->subcerts.emplace_back(keyfile, 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;
}
} }
} else if(util::strieq(opt, SHRPX_OPT_SYSLOG)) { } else if(util::strieq(opt, SHRPX_OPT_SYSLOG)) {
mod_config()->syslog = util::strieq(optarg, "yes"); 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); mod_config()->write_burst = strtoul(optarg, nullptr, 10);
} else if(util::strieq(opt, SHRPX_OPT_NPN_LIST)) { } else if(util::strieq(opt, SHRPX_OPT_NPN_LIST)) {
parse_config_npn_list(optarg); 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")) { } else if(util::strieq(opt, "conf")) {
LOG(WARNING) << "conf is ignored"; LOG(WARNING) << "conf is ignored";
} else { } else {

View File

@ -91,6 +91,7 @@ extern const char SHRPX_OPT_READ_BURST[];
extern const char SHRPX_OPT_WRITE_RATE[]; extern const char SHRPX_OPT_WRITE_RATE[];
extern const char SHRPX_OPT_WRITE_BURST[]; extern const char SHRPX_OPT_WRITE_BURST[];
extern const char SHRPX_OPT_NPN_LIST[]; extern const char SHRPX_OPT_NPN_LIST[];
extern const char SHRPX_OPT_VERIFY_CLIENT[];
union sockaddr_union { union sockaddr_union {
sockaddr sa; sockaddr sa;
@ -184,6 +185,8 @@ struct Config {
char **npn_list; char **npn_list;
// The number of elements in npn_list // The number of elements in npn_list
size_t npn_list_len; size_t npn_list_len;
// The list of (private key file, certificate file) pair
std::vector<std::pair<std::string, std::string>> subcerts;
}; };
const Config* get_config(); const Config* get_config();

View File

@ -73,9 +73,14 @@ int next_proto_cb(SSL *s, const unsigned char **data, unsigned int *len,
namespace { namespace {
int verify_callback(int preverify_ok, X509_STORE_CTX *ctx) int verify_callback(int preverify_ok, X509_STORE_CTX *ctx)
{ {
// We don't verify the client certificate. Just request it for the if(!preverify_ok) {
// testing purpose. int err = X509_STORE_CTX_get_error(ctx);
return 1; 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 } // namespace
@ -213,6 +218,19 @@ SSL_CTX* create_ssl_context(const char *private_key_file,
DIE(); DIE();
} }
if(get_config()->verify_client) { 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_CTX_set_verify(ssl_ctx,
SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE | SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE |
SSL_VERIFY_FAIL_IF_NO_PEER_CERT, SSL_VERIFY_FAIL_IF_NO_PEER_CERT,