add many comments

This commit is contained in:
kakwa 2016-09-11 22:01:22 +02:00
parent 1d53fdeb90
commit 96587d3461
3 changed files with 90 additions and 27 deletions

View File

@ -146,6 +146,9 @@ int rfc3161_handler(struct mg_connection *conn, void *context) {
bool is_tsq = 0; bool is_tsq = 0;
// go through every headers to find Content-Type
// and check if it's set to "application/timestamp-query"
// if it's the case, set is_tsq (is time-stamp query) to True
for (int i = 0; i < request_info->num_headers; i++) { for (int i = 0; i < request_info->num_headers; i++) {
const char *h_name = request_info->http_headers[i].name; const char *h_name = request_info->http_headers[i].name;
const char *h_value = request_info->http_headers[i].value; const char *h_value = request_info->http_headers[i].value;
@ -159,13 +162,22 @@ int rfc3161_handler(struct mg_connection *conn, void *context) {
char *serial_id = NULL; char *serial_id = NULL;
// Send HTTP reply to the client // Send HTTP reply to the client.
//
// If it's a time-stamp query.
if (is_tsq) { if (is_tsq) {
// Recover query content from http request.
char *query = calloc(request_info->content_length, sizeof(char)); char *query = calloc(request_info->content_length, sizeof(char));
int query_len = mg_read(conn, query, request_info->content_length); int query_len = mg_read(conn, query, request_info->content_length);
// Log the query as DEBUG log in hexadecimal format
log_hex(ct, LOG_DEBUG, "query hexdump content", (unsigned char *)query, log_hex(ct, LOG_DEBUG, "query hexdump content", (unsigned char *)query,
request_info->content_length); request_info->content_length);
// Get an OpenSSL TS_RESP_CTX (wrapped inside ts_resp_ctx_wrapper
// structure).
// get_ctxw recovers the first unused TS_RESP_CTX
// in the ct->ts_ctx_pool pool of TS_RESP_CTX.
// (TS_RESP_CTX are not thread safe)
ts_resp_ctx_wrapper *ctx_w = get_ctxw(ct); ts_resp_ctx_wrapper *ctx_w = get_ctxw(ct);
if (ctx_w == NULL) { if (ctx_w == NULL) {
resp_code = 500; resp_code = 500;
@ -173,56 +185,60 @@ int rfc3161_handler(struct mg_connection *conn, void *context) {
"Unable to get an OpenSSL ts_context in the pool"); "Unable to get an OpenSSL ts_context in the pool");
} else { } else {
// create the response
resp_code = create_response(ct, query, query_len, ctx_w->ts_ctx, resp_code = create_response(ct, query, query_len, ctx_w->ts_ctx,
&content_length, &content, &serial_id); &content_length, &content, &serial_id);
// free the TS_RESP_CTX used
ctx_w->available = 1; ctx_w->available = 1;
} }
// respond according to create_response return code
switch (resp_code) { switch (resp_code) {
case 200: case 200:
mg_printf(conn, mg_printf(conn, "HTTP/1.1 200 OK\r\n"
"HTTP/1.1 200 OK\r\n" "Content-Type: application/timestamp-reply\r\n"
"Content-Type: application/timestamp-reply\r\n" "Content-Length: %d\r\n"
"Content-Length: %d\r\n" // Always set Content-Length "\r\n",
"\r\n",
(int)content_length); (int)content_length);
mg_write(conn, content, content_length); mg_write(conn, content, content_length);
log_hex(ct, LOG_DEBUG, "response hexdump content", content, log_hex(ct, LOG_DEBUG, "response hexdump content", content,
content_length); content_length);
break; break;
case 400: case 400:
mg_printf(conn, mg_printf(conn, "HTTP/1.1 400 Bad Request\r\n"
"HTTP/1.1 400 Bad Request\r\n" "Content-Type: text/plain\r\n"
"Content-Type: text/plain\r\n" "Content-Length: 12\r\n"
"Content-Length: 12\r\n" // Always set Content-Length "\r\n"
"\r\n" "client error");
"client error");
break; break;
default: default:
mg_printf(conn, mg_printf(conn, "HTTP/1.1 500 Internal Server Error\r\n"
"HTTP/1.1 500 Internal Server Error\r\n" "Content-Type: text/plain\r\n"
"Content-Type: text/plain\r\n" "Content-Length: 17\r\n"
"Content-Length: 17\r\n" // Always set Content-Length "\r\n"
"\r\n" "uts-server error");
"uts-server error");
} }
free(query); free(query);
free(content); free(content);
} else { } else {
// default reply if we don't have a time-stamp request
resp_code = 200; resp_code = 200;
mg_printf(conn, mg_printf(conn, "HTTP/1.1 200 OK\r\n"
"HTTP/1.1 200 OK\r\n" "Content-Type: text/plain\r\n"
"Content-Type: text/plain\r\n" "Content-Length: 46\r\n"
"Content-Length: 46\r\n" // Always set Content-Length "\r\n"
"\r\n" "uts-server, a simple RFC 3161 timestamp server");
"uts-server, a simple RFC 3161 timestamp server");
} }
// initialize a serial_id if not created by create_response
if (serial_id == NULL) { if (serial_id == NULL) {
serial_id = calloc(9, sizeof(char)); serial_id = calloc(9, sizeof(char));
serial_id = rand_string(serial_id, 8); serial_id = rand_string(serial_id, 8);
} }
// some debugging logs
log_request_debug(request_info, serial_id, ct); log_request_debug(request_info, serial_id, ct);
// end of some timer stuff
diff = clock() - start; diff = clock() - start;
// log the request
log_request(request_info, serial_id, ct, resp_code, log_request(request_info, serial_id, ct, resp_code,
(diff * 1000000 / CLOCKS_PER_SEC)); (diff * 1000000 / CLOCKS_PER_SEC));
free(serial_id); free(serial_id);

View File

@ -66,6 +66,7 @@ int add_oid_section(rfc3161_context *ct, CONF *conf) {
return 1; return 1;
} }
// initialize OpenSSL global black magic...
void init_ssl() { void init_ssl() {
SSL_load_error_strings(); SSL_load_error_strings();
ERR_load_BIO_strings(); ERR_load_BIO_strings();
@ -73,6 +74,7 @@ void init_ssl() {
ERR_load_TS_strings(); ERR_load_TS_strings();
} }
// free OpenSSL global black magic...
void free_ssl() { void free_ssl() {
CONF_modules_unload(1); CONF_modules_unload(1);
EVP_cleanup(); EVP_cleanup();
@ -84,28 +86,41 @@ void free_ssl() {
OBJ_cleanup(); OBJ_cleanup();
} }
// recover a ts wrapper // Recover a ts wrapper from a pool of TS_RESP_CTX.
// Used because TS_RESP_CTX is not thread safe.
ts_resp_ctx_wrapper *get_ctxw(rfc3161_context *ct) { ts_resp_ctx_wrapper *get_ctxw(rfc3161_context *ct) {
ts_resp_ctx_wrapper *ret = NULL; ts_resp_ctx_wrapper *ret = NULL;
// itarate on the 'numthreads' TS_RESP_CTX we have in the pool
// we have as much as TS_RESP_CTX as parallele handlers
for (int i = 0; i < ct->numthreads; i++) { for (int i = 0; i < ct->numthreads; i++) {
// wait until we have exclusive access to read and maybe
// write the ts_resp_ctx_wrapper structure.
pthread_mutex_lock(&ct->ts_ctx_pool[i].lock); pthread_mutex_lock(&ct->ts_ctx_pool[i].lock);
// if the TS_RESP_CTX is available,
// take it and mark it as reserved (available = 0)
if (ct->ts_ctx_pool[i].available) { if (ct->ts_ctx_pool[i].available) {
ct->ts_ctx_pool[i].available = 0; ct->ts_ctx_pool[i].available = 0;
ret = &(ct->ts_ctx_pool[i]); ret = &(ct->ts_ctx_pool[i]);
// unlock the the ts_resp_ctx_wrapper
pthread_mutex_unlock(&ct->ts_ctx_pool[i].lock); pthread_mutex_unlock(&ct->ts_ctx_pool[i].lock);
// return the ts_resp_ctx_wrapper
return ret; return ret;
} }
// unlock in case the ts_resp_ctx_wrapper was not available
pthread_mutex_unlock(&ct->ts_ctx_pool[i].lock); pthread_mutex_unlock(&ct->ts_ctx_pool[i].lock);
} }
// default return if no TS_RESP_CTX wa available
return ret; return ret;
} }
// create a TS_RESP_CTX (OpenSSL Time-Stamp Response Context)
TS_RESP_CTX *create_tsctx(rfc3161_context *ct, CONF *conf, const char *section, TS_RESP_CTX *create_tsctx(rfc3161_context *ct, CONF *conf, const char *section,
const char *policy) { const char *policy) {
unsigned long err_code; unsigned long err_code;
unsigned long err_code_prev = 0; unsigned long err_code_prev = 0;
TS_RESP_CTX *resp_ctx = NULL; TS_RESP_CTX *resp_ctx = NULL;
// recover the section defining the default tsa
if ((section = TS_CONF_get_tsa_section(conf, section)) == NULL) { if ((section = TS_CONF_get_tsa_section(conf, section)) == NULL) {
uts_logger(ct, LOG_ERR, "failed to get or use '%s' in section [ %s ]", uts_logger(ct, LOG_ERR, "failed to get or use '%s' in section [ %s ]",
"default_tsa", "tsa"); "default_tsa", "tsa");
@ -115,6 +130,7 @@ TS_RESP_CTX *create_tsctx(rfc3161_context *ct, CONF *conf, const char *section,
uts_logger(ct, LOG_ERR, "failed to initialize tsa context"); uts_logger(ct, LOG_ERR, "failed to initialize tsa context");
goto end; goto end;
} }
// recover and set various parameters
TS_RESP_CTX_set_serial_cb(resp_ctx, serial_cb, NULL); TS_RESP_CTX_set_serial_cb(resp_ctx, serial_cb, NULL);
if (!TS_CONF_set_crypto_device(conf, section, NULL)) { if (!TS_CONF_set_crypto_device(conf, section, NULL)) {
uts_logger(ct, LOG_ERR, "failed to get or use '%s' in section [ %s ]", uts_logger(ct, LOG_ERR, "failed to get or use '%s' in section [ %s ]",
@ -189,6 +205,7 @@ TS_RESP_CTX *create_tsctx(rfc3161_context *ct, CONF *conf, const char *section,
} }
return resp_ctx; return resp_ctx;
end: end:
// log the OpenSSL errors if any in case of error
while ((err_code = ERR_get_error())) { while ((err_code = ERR_get_error())) {
if (err_code_prev != err_code) { if (err_code_prev != err_code) {
uts_logger(ct, LOG_DEBUG, "OpenSSL exception: '%s'", uts_logger(ct, LOG_DEBUG, "OpenSSL exception: '%s'",
@ -199,6 +216,7 @@ end:
} }
err_code_prev = err_code; err_code_prev = err_code;
} }
// free the TS_RESP_CTX if initialization has faile
TS_RESP_CTX_free(resp_ctx); TS_RESP_CTX_free(resp_ctx);
return NULL; return NULL;
} }
@ -215,10 +233,13 @@ int create_response(rfc3161_context *ct, char *query, int query_len,
unsigned long err_code; unsigned long err_code;
unsigned long err_code_prev = 0; unsigned long err_code_prev = 0;
// create the input bio for OpenSSL containing the query
if ((query_bio = BIO_new_mem_buf(query, query_len)) == NULL) { if ((query_bio = BIO_new_mem_buf(query, query_len)) == NULL) {
uts_logger(ct, LOG_ERR, "failed to parse query"); uts_logger(ct, LOG_ERR, "failed to parse query");
goto end; goto end;
} }
// generate the response
if ((ts_response = TS_RESP_create_response(resp_ctx, query_bio)) == NULL) { if ((ts_response = TS_RESP_create_response(resp_ctx, query_bio)) == NULL) {
uts_logger(ct, LOG_ERR, "failed to create ts response"); uts_logger(ct, LOG_ERR, "failed to create ts response");
goto end; goto end;
@ -252,6 +273,7 @@ end:
serial_hex = calloc(SERIAL_ID_SIZE, sizeof(char)); serial_hex = calloc(SERIAL_ID_SIZE, sizeof(char));
strncpy(serial_hex, " NO ID ", SERIAL_ID_SIZE + 2); strncpy(serial_hex, " NO ID ", SERIAL_ID_SIZE + 2);
} }
// get a short version of the serial (150 bits in hexa is a bit long) // get a short version of the serial (150 bits in hexa is a bit long)
strncpy(*serial_id, serial_hex, SERIAL_ID_SIZE); strncpy(*serial_id, serial_hex, SERIAL_ID_SIZE);
@ -271,6 +293,7 @@ end:
bptr->data, *serial_id); bptr->data, *serial_id);
// emit logs according the return value // emit logs according the return value
// and set the return code
long status = ASN1_INTEGER_get(ts_response->status_info->status); long status = ASN1_INTEGER_get(ts_response->status_info->status);
switch (status) { switch (status) {
case TS_STATUS_GRANTED: case TS_STATUS_GRANTED:
@ -312,7 +335,7 @@ end:
ret = 500; ret = 500;
} }
// log the openssl errors // log the OpenSSL errors if any
while ((err_code = ERR_get_error())) { while ((err_code = ERR_get_error())) {
if (err_code_prev != err_code) { if (err_code_prev != err_code) {
ERR_load_TS_strings(); ERR_load_TS_strings();
@ -331,6 +354,9 @@ end:
return ret; return ret;
} }
// Build a random serial for each request.
// It's less painful to manage than an incremental serial stored in a file
// and a 150 bits size is more than enough to prevent collision.
static ASN1_INTEGER *serial_cb(TS_RESP_CTX *ctx, void *data42) { static ASN1_INTEGER *serial_cb(TS_RESP_CTX *ctx, void *data42) {
unsigned char data[150] = {0}; unsigned char data[150] = {0};
RAND_bytes(data, sizeof(data)); RAND_bytes(data, sizeof(data));

View File

@ -136,6 +136,7 @@ void skeleton_daemon() {
// openlog("uts-server", LOG_PID, LOG_DAEMON); // openlog("uts-server", LOG_PID, LOG_DAEMON);
} }
// log a binary blob as hexadecimal
void log_hex(rfc3161_context *ct, int priority, char *id, void log_hex(rfc3161_context *ct, int priority, char *id,
unsigned char *content, int content_length) { unsigned char *content, int content_length) {
if (priority > ct->loglevel && !ct->stdout_dbg) if (priority > ct->loglevel && !ct->stdout_dbg)
@ -154,22 +155,26 @@ void log_hex(rfc3161_context *ct, int priority, char *id,
free(out); free(out);
} }
// logger function
void uts_logger(rfc3161_context *ct, int priority, char *fmt, ...) { void uts_logger(rfc3161_context *ct, int priority, char *fmt, ...) {
// ignore all messages less critical than the loglevel // ignore all messages less critical than the loglevel
// except if the debug flag is set // except if the debug flag is set
if (priority > ct->loglevel && !ct->stdout_dbg) if (priority > ct->loglevel && !ct->stdout_dbg)
return; return;
// build the out log message
FILE *stream; FILE *stream;
char *out; char *out;
size_t len; size_t len;
stream = open_memstream(&out, &len); stream = open_memstream(&out, &len);
va_list args; va_list args;
va_start(args, fmt); va_start(args, fmt);
vfprintf(stream, fmt, args); vfprintf(stream, fmt, args);
va_end(args); va_end(args);
fflush(stream); fflush(stream);
fclose(stream); fclose(stream);
// if in debugging mode, also log to stdout
if (ct->stdout_dbg) { if (ct->stdout_dbg) {
switch (priority) { switch (priority) {
case LOG_EMERG: case LOG_EMERG:
@ -209,6 +214,7 @@ void uts_logger(rfc3161_context *ct, int priority, char *fmt, ...) {
free(out); free(out);
} }
// OpenSSL file openner (use for opening the configuration file
static BIO *bio_open_default(rfc3161_context *ct, const char *filename, static BIO *bio_open_default(rfc3161_context *ct, const char *filename,
int format) { int format) {
BIO *ret; BIO *ret;
@ -229,6 +235,7 @@ static BIO *bio_open_default(rfc3161_context *ct, const char *filename,
return NULL; return NULL;
} }
// loading the configuration file and parsing it using the OpenSSL parser
static CONF *load_config_file(rfc3161_context *ct, const char *filename) { static CONF *load_config_file(rfc3161_context *ct, const char *filename) {
long errorline = -1; long errorline = -1;
BIO *in; BIO *in;
@ -260,13 +267,18 @@ static CONF *load_config_file(rfc3161_context *ct, const char *filename) {
return NULL; return NULL;
} }
// initialize the rfc3161_context according to the conf_file content
int set_params(rfc3161_context *ct, char *conf_file, char *conf_wd) { int set_params(rfc3161_context *ct, char *conf_file, char *conf_wd) {
// chdir in configuration file directory
// (some parameters like certificates can be declared
// relatively to the configuration file).
chdir(conf_wd); chdir(conf_wd);
int ret = 1; int ret = 1;
int http_counter = 0; int http_counter = 0;
int numthreads = 42; int numthreads = 42;
NCONF_free(ct->conf); NCONF_free(ct->conf);
// load the configuration file
ct->conf = load_config_file(ct, conf_file); ct->conf = load_config_file(ct, conf_file);
if (ct->conf == NULL) if (ct->conf == NULL)
goto end; goto end;
@ -297,6 +309,7 @@ int set_params(rfc3161_context *ct, char *conf_file, char *conf_wd) {
; ;
} }
} }
// parse the options to get the civetweb options and a few other things
for (int i = 0; i < RFC3161_OPTIONS_LEN; i++) { for (int i = 0; i < RFC3161_OPTIONS_LEN; i++) {
int type = rfc3161_options[i].type; int type = rfc3161_options[i].type;
const char *name = rfc3161_options[i].name; const char *name = rfc3161_options[i].name;
@ -311,6 +324,8 @@ int set_params(rfc3161_context *ct, char *conf_file, char *conf_wd) {
uts_logger(ct, LOG_DEBUG, "configuration param['%s'] = '%s'", name, uts_logger(ct, LOG_DEBUG, "configuration param['%s'] = '%s'", name,
value); value);
switch (type) { switch (type) {
// if it's an http (civetweb) option, put it in the http_options buffer
// like civetweb is expected it.
case HTTP_OPTIONS: case HTTP_OPTIONS:
if (value != NULL) { if (value != NULL) {
ct->http_options[http_counter] = name; ct->http_options[http_counter] = name;
@ -318,6 +333,8 @@ int set_params(rfc3161_context *ct, char *conf_file, char *conf_wd) {
ct->http_options[http_counter] = value; ct->http_options[http_counter] = value;
http_counter++; http_counter++;
} }
// recover the num_threads parameter as it's used to
// initialize the TS_RESP_CTX pool
if (strcmp(name, "num_threads") == 0) if (strcmp(name, "num_threads") == 0)
numthreads = atoi(value); numthreads = atoi(value);
break; break;
@ -331,6 +348,9 @@ int set_params(rfc3161_context *ct, char *conf_file, char *conf_wd) {
if (!add_oid_section(ct, ct->conf)) if (!add_oid_section(ct, ct->conf))
ret = 0; ret = 0;
// initialize the TS_RESP_CTX pool
// as TS_RESP_CTX is not thread safe,
// creates 'num_threads' TS_RESP_CTX (one per thread)
ct->ts_ctx_pool = calloc(numthreads, sizeof(ts_resp_ctx_wrapper)); ct->ts_ctx_pool = calloc(numthreads, sizeof(ts_resp_ctx_wrapper));
ct->numthreads = numthreads; ct->numthreads = numthreads;
for (int i = 0; i < numthreads; i++) { for (int i = 0; i < numthreads; i++) {
@ -339,6 +359,7 @@ int set_params(rfc3161_context *ct, char *conf_file, char *conf_wd) {
if (ct->ts_ctx_pool[i].ts_ctx == NULL) if (ct->ts_ctx_pool[i].ts_ctx == NULL)
ret = 0; ret = 0;
} }
// like any good daemon, return to '/' once the configuration is loaded
chdir("/"); chdir("/");
return ret; return ret;