blob: 18a4723a0226709286bb57a0e898eeea4f82ca10 [file] [log] [blame]
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <getopt.h>
#include "../log.h"
#include "internal.h"
#include "evhtp/evhtp.h"
#ifndef EVHTP_DISABLE_SSL
static void
http__callback_(evhtp_request_t * req, void * arg) {
return evhtp_send_reply(req, EVHTP_RES_OK);
}
static int
ssl__x509_verify_(int ok, X509_STORE_CTX * store) {
char buf[256];
X509 * err_cert;
int err;
int depth;
SSL * ssl;
evhtp_connection_t * connection;
evhtp_ssl_cfg_t * ssl_cfg;
err_cert = X509_STORE_CTX_get_current_cert(store);
err = X509_STORE_CTX_get_error(store);
depth = X509_STORE_CTX_get_error_depth(store);
ssl = X509_STORE_CTX_get_ex_data(store, SSL_get_ex_data_X509_STORE_CTX_idx());
X509_NAME_oneline(X509_get_subject_name(err_cert), buf, 256);
connection = SSL_get_app_data(ssl);
ssl_cfg = connection->htp->ssl_cfg;
if (depth > ssl_cfg->verify_depth) {
ok = 0;
err = X509_V_ERR_CERT_CHAIN_TOO_LONG;
X509_STORE_CTX_set_error(store, err);
}
if (!ok) {
log_error("SSL: verify error:num=%d:%s:depth=%d:%s", err,
X509_verify_cert_error_string(err), depth, buf);
}
return ok;
}
enum {
OPTARG_CERT = 1000,
OPTARG_KEY,
OPTARG_CA,
OPTARG_CAPATH,
OPTARG_CIPHERS,
OPTARG_VERIFY_PEER,
OPTARG_ENFORCE_PEER_CERT,
OPTARG_VERIFY_DEPTH,
OPTARG_ENABLE_CACHE,
OPTARG_CACHE_TIMEOUT,
OPTARG_CACHE_SIZE,
OPTARG_CTX_TIMEOUT,
OPTARG_ENABLE_PROTOCOL,
OPTARG_DISABLE_PROTOCOL
};
static const char * help =
"Usage %s [opts] <host>:<port>\n"
" -cert <file> : Server PEM-encoded X.509 Certificate file\n"
" -key <file> : Server PEM-encoded Private Key file\n"
" -ca <file> : File of PEM-encoded Server CA Certificates\n"
" -capath <path> : Directory of PEM-encoded CA Certificates for Client Auth\n"
" -ciphers <str> : Accepted SSL Ciphers\n"
" -verify-peer : Enable SSL client verification\n"
" -enforce-peer-cert : Reject clients without a cert\n"
" -verify-depth <n> : Maximum depth of CA Certificates in Client Certificate verification\n"
" -enable-protocol <p> : Enable one of the following protocols: SSLv2, SSLv3, TLSv1, or ALL\n"
" -disable-protocol <p> : Disable one of the following protocols: SSLv2, SSLv3, TLSv1, or ALL\n"
" -ctx-timeout <n> : SSL Session Timeout (SSL >= 1.0)\n";
evhtp_ssl_cfg_t *
parse__ssl_opts_(int argc, char ** argv) {
int opt = 0;
int long_index = 0;
int ssl_verify_mode = 0;
struct stat f_stat;
evhtp_ssl_cfg_t * ssl_config = calloc(1, sizeof(evhtp_ssl_cfg_t));
ssl_config->ssl_opts = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1;
static struct option long_options[] = {
{ "cert", required_argument, 0, OPTARG_CERT },
{ "key", required_argument, 0, OPTARG_KEY },
{ "ca", required_argument, 0, OPTARG_CA },
{ "capath", required_argument, 0, OPTARG_CAPATH },
{ "ciphers", required_argument, 0, OPTARG_CIPHERS },
{ "verify-peer", no_argument, 0, OPTARG_VERIFY_PEER },
{ "enforce-peer-cert", no_argument, 0, OPTARG_ENFORCE_PEER_CERT },
{ "verify-depth", required_argument, 0, OPTARG_VERIFY_DEPTH },
{ "enable-cache", no_argument, 0, OPTARG_ENABLE_CACHE },
{ "cache-timeout", required_argument, 0, OPTARG_CACHE_TIMEOUT },
{ "cache-size", required_argument, 0, OPTARG_CACHE_SIZE },
{ "enable-protocol", required_argument, 0, OPTARG_ENABLE_PROTOCOL },
{ "disable-protocol", required_argument, 0, OPTARG_DISABLE_PROTOCOL },
{ "ctx-timeout", required_argument, 0, OPTARG_CTX_TIMEOUT },
{ "help", no_argument, 0, 'h' },
{ NULL, 0, 0, 0 }
};
while ((opt = getopt_long_only(argc, argv, "", long_options, &long_index)) != -1) {
switch (opt) {
case 'h':
printf(help, argv[0]);
exit(EXIT_FAILURE);
case OPTARG_CERT:
ssl_config->pemfile = strdup(optarg);
break;
case OPTARG_KEY:
ssl_config->privfile = strdup(optarg);
break;
case OPTARG_CA:
ssl_config->cafile = strdup(optarg);
break;
case OPTARG_CAPATH:
ssl_config->capath = strdup(optarg);
break;
case OPTARG_CIPHERS:
ssl_config->ciphers = strdup(optarg);
break;
case OPTARG_VERIFY_DEPTH:
ssl_config->verify_depth = atoi(optarg);
break;
case OPTARG_VERIFY_PEER:
ssl_verify_mode |= SSL_VERIFY_PEER;
break;
case OPTARG_ENFORCE_PEER_CERT:
ssl_verify_mode |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
break;
case OPTARG_ENABLE_CACHE:
ssl_config->scache_type = evhtp_ssl_scache_type_internal;
break;
case OPTARG_CACHE_TIMEOUT:
ssl_config->scache_timeout = atoi(optarg);
break;
case OPTARG_CACHE_SIZE:
ssl_config->scache_size = atoi(optarg);
break;
case OPTARG_CTX_TIMEOUT:
ssl_config->ssl_ctx_timeout = atoi(optarg);
break;
case OPTARG_ENABLE_PROTOCOL:
if (!strcasecmp(optarg, "SSLv2")) {
ssl_config->ssl_opts &= ~SSL_OP_NO_SSLv2;
} else if (!strcasecmp(optarg, "SSLv3")) {
ssl_config->ssl_opts &= ~SSL_OP_NO_SSLv3;
} else if (!strcasecmp(optarg, "TLSv1")) {
ssl_config->ssl_opts &= ~SSL_OP_NO_TLSv1;
} else if (!strcasecmp(optarg, "ALL")) {
ssl_config->ssl_opts = 0;
}
break;
case OPTARG_DISABLE_PROTOCOL:
if (!strcasecmp(optarg, "SSLv2")) {
ssl_config->ssl_opts |= SSL_OP_NO_SSLv2;
} else if (!strcasecmp(optarg, "SSLv3")) {
ssl_config->ssl_opts |= SSL_OP_NO_SSLv3;
} else if (!strcasecmp(optarg, "TLSv1")) {
ssl_config->ssl_opts |= SSL_OP_NO_TLSv1;
} else if (!strcasecmp(optarg, "ALL")) {
ssl_config->ssl_opts = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1;
}
break;
default:
break;
} /* switch */
}
if (ssl_verify_mode != 0) {
ssl_config->verify_peer = ssl_verify_mode;
ssl_config->x509_verify_cb = ssl__x509_verify_;
}
if (ssl_config->pemfile) {
if (stat(ssl_config->pemfile, &f_stat) != 0) {
log_error("Cannot load SSL cert '%s' (%s)", ssl_config->pemfile, strerror(errno));
exit(EXIT_FAILURE);
}
}
if (ssl_config->privfile) {
if (stat(ssl_config->privfile, &f_stat) != 0) {
log_error("Cannot load SSL key '%s' (%s)", ssl_config->privfile, strerror(errno));
exit(EXIT_FAILURE);
}
}
if (ssl_config->cafile) {
if (stat(ssl_config->cafile, &f_stat) != 0) {
log_error("Cannot find SSL CA File '%s' (%s)", ssl_config->cafile, strerror(errno));
exit(EXIT_FAILURE);
}
}
if (ssl_config->capath) {
if (stat(ssl_config->capath, &f_stat) != 0) {
log_error("Cannot find SSL CA PATH '%s' (%s)", ssl_config->capath, strerror(errno));
exit(EXIT_FAILURE);
}
}
return ssl_config;
} /* parse__ssl_opts_ */
#endif
int
main(int argc, char ** argv) {
#ifndef EVHTP_DISABLE_SSL
evhtp_t * htp;
struct event_base * evbase;
evbase = event_base_new();
evhtp_alloc_assert(evbase);
htp = evhtp_new(evbase, NULL);
evhtp_alloc_assert(htp);
evhtp_ssl_init(htp, parse__ssl_opts_(argc, argv));
evhtp_set_gencb(htp, http__callback_, NULL);
evhtp_bind_socket(htp, "127.0.0.1", 4443, 128);
{
struct sockaddr_in sin;
socklen_t len = sizeof(struct sockaddr);
uint16_t port;
getsockname(
evconnlistener_get_fd(htp->server),
(struct sockaddr *)&sin, &len);
port = ntohs(sin.sin_port);
log_info("curl https://127.0.0.1:%d/", port);
}
event_base_loop(evbase, 0);
return 0;
#else
log_error("Not compiled with SSL support, go away");
return EXIT_FAILURE;
#endif
}