Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
tree: 0b88039481
Fetching contributors…

Cannot retrieve contributors at this time

1325 lines (1167 sloc) 39.119 kB
/**
* configuration.c
*
* Author: Brane F. Gracnar
*/
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdarg.h>
#include <stdlib.h>
#include <ctype.h>
#include <unistd.h>
#include <getopt.h>
#include <libgen.h>
#include <sys/types.h>
#include <pwd.h>
#include <grp.h>
#include <sys/stat.h>
#include <syslog.h>
#include "configuration.h"
#include "version.h"
#define ADDR_LEN 150
#define PORT_LEN 6
#define CFG_BOOL_ON "on"
// BEGIN: configuration parameters
#define CFG_CIPHERS "ciphers"
#define CFG_SSL_ENGINE "ssl-engine"
#define CFG_PREFER_SERVER_CIPHERS "prefer-server-ciphers"
#define CFG_BACKEND "backend"
#define CFG_FRONTEND "frontend"
#define CFG_WORKERS "workers"
#define CFG_BACKLOG "backlog"
#define CFG_KEEPALIVE "keepalive"
#define CFG_CHROOT "chroot"
#define CFG_USER "user"
#define CFG_GROUP "group"
#define CFG_QUIET "quiet"
#define CFG_SYSLOG "syslog"
#define CFG_SYSLOG_FACILITY "syslog-facility"
#define CFG_PARAM_SYSLOG_FACILITY 11015
#define CFG_DAEMON "daemon"
#define CFG_WRITE_IP "write-ip"
#define CFG_WRITE_PROXY "write-proxy"
#define CFG_PEM_FILE "pem-file"
#define CFG_PROXY_PROXY "proxy-proxy"
#ifdef USE_SHARED_CACHE
#define CFG_SHARED_CACHE "shared-cache"
#define CFG_SHARED_CACHE_LISTEN "shared-cache-listen"
#define CFG_SHARED_CACHE_PEER "shared-cache-peer"
#define CFG_SHARED_CACHE_MCASTIF "shared-cache-if"
#endif
#ifndef NO_CONFIG_FILE
#define FMT_STR "%s = %s\n"
#define FMT_QSTR "%s = \"%s\"\n"
#define FMT_ISTR "%s = %d\n"
#define CONFIG_MAX_LINES 10000
#define CONFIG_BUF_SIZE 1024
#define CFG_PARAM_CFGFILE 10000
#define CFG_PARAM_DEFCFG 10001
#define CFG_CONFIG "config"
#define CFG_CONFIG_DEFAULT "default-config"
#endif
// END: configuration parameters
static char var_buf[CONFIG_BUF_SIZE];
static char val_buf[CONFIG_BUF_SIZE];
static char error_buf[CONFIG_BUF_SIZE];
static char tmp_buf[150];
// for testing configuration only
#include <openssl/ssl.h>
SSL_CTX * init_openssl();
static void config_error_set (char *fmt, ...) {
memset(error_buf, '\0', sizeof(error_buf));
va_list args;
va_start(args, fmt);
vsnprintf(error_buf, (sizeof(error_buf) - 1), fmt, args);
va_end(args);
}
char * config_error_get (void) {
return error_buf;
}
void config_die (char *fmt, ...) {
va_list args;
va_start(args, fmt);
vfprintf(stderr, fmt, args);
va_end(args);
fprintf(stderr, "\n");
exit(1);
}
stud_config * config_new (void) {
stud_config *r = NULL;
r = malloc(sizeof(stud_config));
if (r == NULL) {
config_error_set("Unable to allocate memory for configuration structure: %s", strerror(errno));
return NULL;
}
// set default values
r->ETYPE = ENC_TLS;
r->PMODE = SSL_SERVER;
r->WRITE_IP_OCTET = 0;
r->WRITE_PROXY_LINE = 0;
r->PROXY_PROXY_LINE = 0;
r->CHROOT = NULL;
r->UID = 0;
r->GID = 0;
r->FRONT_IP = NULL;
r->FRONT_PORT = strdup("8443");
r->BACK_IP = strdup("127.0.0.1");
r->BACK_PORT = strdup("8000");
r->NCORES = 1;
r->CERT_FILES = NULL;
r->CIPHER_SUITE = NULL;
r->ENGINE = NULL;
r->BACKLOG = 100;
#ifdef USE_SHARED_CACHE
r->SHARED_CACHE = 0;
r->SHCUPD_IP = NULL;
r->SHCUPD_PORT = NULL;
for (int i = 0 ; i < MAX_SHCUPD_PEERS; i++)
memset(&r->SHCUPD_PEERS[i], 0, sizeof(shcupd_peer_opt));
r->SHCUPD_MCASTIF = NULL;
r->SHCUPD_MCASTTTL = NULL;
#endif
r->QUIET = 0;
r->SYSLOG = 0;
r->SYSLOG_FACILITY = LOG_DAEMON;
r->TCP_KEEPALIVE_TIME = 3600;
r->DAEMONIZE = 0;
r->PREFER_SERVER_CIPHERS = 0;
return r;
}
void config_destroy (stud_config *cfg) {
// printf("config_destroy() in pid %d: %p\n", getpid(), cfg);
if (cfg == NULL) return;
// free all members!
if (cfg->CHROOT != NULL) free(cfg->CHROOT);
if (cfg->FRONT_IP != NULL) free(cfg->FRONT_IP);
if (cfg->FRONT_PORT != NULL) free(cfg->FRONT_PORT);
if (cfg->BACK_IP != NULL) free(cfg->BACK_IP);
if (cfg->BACK_PORT != NULL) free(cfg->BACK_PORT);
if (cfg->CERT_FILES != NULL) {
struct cert_files *curr = cfg->CERT_FILES, *next;
while (cfg->CERT_FILES != NULL) {
next = curr->NEXT;
free(curr);
curr = next;
}
}
if (cfg->CIPHER_SUITE != NULL) free(cfg->CIPHER_SUITE);
if (cfg->ENGINE != NULL) free(cfg->ENGINE);
#ifdef USE_SHARED_CACHE
if (cfg->SHCUPD_IP != NULL) free(cfg->SHCUPD_IP);
if (cfg->SHCUPD_PORT != NULL) free(cfg->SHCUPD_PORT);
for (int i = 0; i < MAX_SHCUPD_PEERS; i++) {
if (cfg->SHCUPD_PEERS[i].ip != NULL)
free(cfg->SHCUPD_PEERS[i].ip);
if (cfg->SHCUPD_PEERS[i].port != NULL)
free(cfg->SHCUPD_PEERS[i].port);
}
if (cfg->SHCUPD_MCASTIF != NULL) free(cfg->SHCUPD_MCASTIF);
if (cfg->SHCUPD_MCASTTTL != NULL) free(cfg->SHCUPD_MCASTTTL);
#endif
free(cfg);
}
char * config_get_param (char *str) {
char *ptr;
int i;
if (str == NULL) return NULL;
/** empty string? */
if (strlen(str) < 1 || str[0] == '\n' || strcmp(str, "\r\n") == 0) return NULL;
ptr = str;
/** comments? */
if (str[0] == '#') return NULL;
/** first alpha character */
while (ptr != NULL && ! isalpha(*ptr))
ptr++;
/** overwrite alpha chars */
memset(var_buf, '\0', sizeof(var_buf));
i = 0;
while(ptr != NULL && (isalnum(*ptr) || *ptr == '-')) {
var_buf[i] = *ptr;
i++;
ptr++;
}
if (strlen(var_buf) < 1) return NULL;
return var_buf;
}
char * config_get_value (char *str) {
char *ptr;
int i = 0;
if (str == NULL) return NULL;
if (strlen(str) < 1) return NULL;
/** find '=' char */
ptr = str;
while (ptr != NULL && (*ptr) != '=')
ptr++;
ptr++;
/** skip whitespaces **/
while (ptr != NULL && ! isgraph(*ptr))
ptr++;
/** no value found? */
if (ptr == NULL) return NULL;
/** overwrite alpha chars */
memset(val_buf, '\0', sizeof(val_buf));
while(ptr != NULL && isgraph(*ptr)) {
val_buf[i++] = *ptr;
ptr++;
}
if (strlen(val_buf) < 1) return NULL;
return val_buf;
}
char * str_rtrim(char *str) {
char *ptr;
int len;
len = strlen(str);
ptr = str + len - 1;
while (ptr >= str && (isspace((int)*ptr ) || (char) *ptr == '"' || (char) *ptr == '\'')) --ptr;
ptr[1] = '\0';
return str;
}
char * str_ltrim(char *str) {
char *ptr;
int len;
for (ptr = str; (*ptr && (isspace((int)*ptr) || (char) *ptr == '"' || (char) *ptr == '\'')); ++ptr);
len = strlen(ptr);
memmove(str, ptr, len + 1);
return str;
}
char * str_trim(char *str) {
char *ptr;
ptr = str_rtrim(str);
str = str_ltrim(ptr);
return str;
}
char * config_assign_str (char **dst, char *v) {
if (*dst == NULL) {
if (v != NULL && strlen(v) > 0)
*dst = strdup(v);
} else {
if (v != NULL && strlen(v) > 0) {
// we assume that there is enough room for v in *dst
memset(*dst, '\0', strlen(v) + 1);
memcpy(*dst, v, strlen(v));
}
else
free(*dst);
}
return *dst;
}
int config_param_val_bool (char *val, int *res) {
if (val == NULL) return 0;
if (
strcasecmp(val, CFG_BOOL_ON) == 0 ||
strcasecmp(val, "yes") == 0 ||
strcasecmp(val, "y") == 0 ||
strcasecmp(val, "true") == 0 ||
strcasecmp(val, "t") == 0 ||
strcasecmp(val, "1") == 0) {
*res = 1;
}
return 1;
}
char * config_param_val_str (char *val) {
return strdup(val);
}
int config_param_host_port_wildcard (char *str, char **addr, char **port, int wildcard_okay) {
int len = (str != NULL) ? strlen(str) : 0;
if (str == NULL || ! len) {
config_error_set("Invalid/unset host/port string.");
return 0;
}
// address/port buffers
char port_buf[PORT_LEN];
char addr_buf[ADDR_LEN];
memset(port_buf, '\0', sizeof(port_buf));
memset(addr_buf, '\0', sizeof(addr_buf));
// NEW FORMAT: [address]:port
if (*str == '[') {
char *ptr = str + 1;
char *x = strrchr(ptr, ']');
if (x == NULL) {
config_error_set("Invalid address '%s'.", str);
return 0;
}
// address
memcpy(addr_buf, ptr, (x - ptr));
// port
x += 2;
memcpy(port_buf, x, sizeof(port_buf) - 1);
}
// OLD FORMAT: address,port
else {
char *x = strrchr(str, ',');
if (x == NULL) {
config_error_set("Invalid address string '%s'", str);
return 0;
}
// addr
int addr_len = x - str;
memcpy(addr_buf, str, addr_len);
// port
memcpy(port_buf, (++x), sizeof(port_buf));
}
// printf("PARSED ADDR '%s', PORT '%s'\n", addr_buf, port_buf);
// check port
int p = atoi(port_buf);
if (p < 1 || p > 65536) {
config_error_set("Invalid port number '%s'", port_buf);
return 0;
}
// write
if (strcmp(addr_buf, "*") == 0) {
if (wildcard_okay)
free(*addr);
else {
config_error_set("Invalid address: wildcards are not allowed.");
return 0;
}
} else {
//if (*addr != NULL) free(*addr);
*addr = strdup(addr_buf);
}
// if (**port != NULL) free(*port);
*port = strdup(port_buf);
// printf("ADDR FINAL: '%s', '%s'\n", *addr, *port);
return 1;
}
int config_param_host_port (char *str, char **addr, char **port) {
return config_param_host_port_wildcard(str, addr, port, 0);
}
int config_param_val_int (char *str, int *dst) {
*dst = (str != NULL) ? atoi(str) : 0;
return 1;
}
int config_param_val_int_pos (char *str, int *dst) {
int num = 0;
if (str != NULL)
num = atoi(str);
if (num < 1) {
config_error_set("Not a positive number.");
return 0;
}
*dst = num;
return 1;
}
int config_param_val_intl (char *str, long int *dst) {
*dst = (str != NULL) ? atol(str) : 0;
return 1;
}
int config_param_val_intl_pos (char *str, long int *dst) {
long int num = 0;
if (str != NULL)
num = atol(str);
if (num < 1) {
config_error_set("Not a positive number.");
return 0;
}
*dst = num;
return 1;
}
#ifdef USE_SHARED_CACHE
/* Parse mcast and ttl options */
int config_param_shcupd_mcastif (char *str, char **iface, char **ttl) {
char buf[150];
char *sp;
if (strlen(str) >= sizeof buf) {
config_error_set("Invalid option for IFACE[,TTL]");
return 0;
}
sp = strchr(str, ',');
if (!sp) {
if (!strcmp(str, "*"))
*iface = NULL;
else
*iface = str;
*ttl = NULL;
return 1;
}
else if (!strncmp(str, "*", sp - str)) {
*iface = NULL;
}
else {
*sp = 0;
*iface = str;
}
*ttl = sp + 1;
return 1;
}
int config_param_shcupd_peer (char *str, stud_config *cfg) {
if (cfg == NULL) {
config_error_set("Configuration pointer is NULL.");
return 0;
}
// parse result
int r = 1;
// find place for new peer
int offset = 0;
int i = 0;
for (i = 0; i < MAX_SHCUPD_PEERS; i++) {
if (cfg->SHCUPD_PEERS[i].ip == NULL && cfg->SHCUPD_PEERS[i].port == NULL) {
offset = i;
break;
}
}
if (i >= MAX_SHCUPD_PEERS) {
config_error_set(
"Reached maximum number of shared cache update peers (%d).",
MAX_SHCUPD_PEERS
);
return 0;
}
// create place for new peer
char *addr = malloc(ADDR_LEN);
if (addr == NULL) {
config_error_set(
"Unable to allocate memory for new shared cache update peer address: %s",
strerror(errno)
);
r = 0;
goto outta_parse_peer;
}
memset(addr, '\0', ADDR_LEN);
char *port = malloc(PORT_LEN);
if (port == NULL) {
config_error_set(
"Unable to allocate memory for new shared cache update peer port: %s",
strerror(errno)
);
r = 0;
goto outta_parse_peer;
}
memset(port, '\0', PORT_LEN);
// try to parse address
if (! config_param_host_port(str, &addr, &port)) {
r = 0;
goto outta_parse_peer;
}
outta_parse_peer:
if (! r) {
if (addr != NULL) free(addr);
if (port != NULL) free(port);
} else {
cfg->SHCUPD_PEERS[offset].ip = addr;
cfg->SHCUPD_PEERS[offset].port = port;
}
return r;
}
#endif /* USE_SHARED_CACHE */
void config_param_validate (char *k, char *v, stud_config *cfg, char *file, int line) {
int r = 1;
struct stat st;
if (strcmp(k, "tls") == 0) {
//cfg->ENC_TLS = 1;
}
else if (strcmp(k, "ssl") == 0) {
//cfg->ENC_TLS = 0;
}
else if (strcmp(k, CFG_CIPHERS) == 0) {
if (v != NULL && strlen(v) > 0) {
config_assign_str(&cfg->CIPHER_SUITE, v);
}
}
else if (strcmp(k, CFG_SSL_ENGINE) == 0) {
if (v != NULL && strlen(v) > 0) {
config_assign_str(&cfg->ENGINE, v);
}
}
else if (strcmp(k, CFG_PREFER_SERVER_CIPHERS) == 0) {
r = config_param_val_bool(v, &cfg->PREFER_SERVER_CIPHERS);
}
else if (strcmp(k, CFG_FRONTEND) == 0) {
r = config_param_host_port_wildcard(v, &cfg->FRONT_IP, &cfg->FRONT_PORT, 1);
}
else if (strcmp(k, CFG_BACKEND) == 0) {
r = config_param_host_port(v, &cfg->BACK_IP, &cfg->BACK_PORT);
}
else if (strcmp(k, CFG_WORKERS) == 0) {
r = config_param_val_intl_pos(v, &cfg->NCORES);
}
else if (strcmp(k, CFG_BACKLOG) == 0) {
r = config_param_val_int(v, &cfg->BACKLOG);
if (r && cfg->BACKLOG < -1) cfg->BACKLOG = -1;
}
else if (strcmp(k, CFG_KEEPALIVE) == 0) {
r = config_param_val_int_pos(v, &cfg->TCP_KEEPALIVE_TIME);
}
#ifdef USE_SHARED_CACHE
else if (strcmp(k, CFG_SHARED_CACHE) == 0) {
r = config_param_val_int(v, &cfg->SHARED_CACHE);
}
else if (strcmp(k, CFG_SHARED_CACHE_LISTEN) == 0) {
if (v != NULL && strlen(v) > 0)
r = config_param_host_port_wildcard(v, &cfg->SHCUPD_IP, &cfg->SHCUPD_PORT, 1);
}
else if (strcmp(k, CFG_SHARED_CACHE_PEER) == 0) {
r = config_param_shcupd_peer(v, cfg);
}
else if (strcmp(k, CFG_SHARED_CACHE_MCASTIF) == 0) {
r = config_param_shcupd_mcastif(v, &cfg->SHCUPD_MCASTIF, &cfg->SHCUPD_MCASTTTL);
}
#endif
else if (strcmp(k, CFG_CHROOT) == 0) {
if (v != NULL && strlen(v) > 0) {
// check directory
if (stat(v, &st) != 0) {
config_error_set("Unable to stat directory '%s': %s'.", v, strerror(errno));
r = 0;
} else {
if (! S_ISDIR(st.st_mode)) {
config_error_set("Bad chroot directory '%s': Not a directory.", v, strerror(errno));
r = 0;
} else {
config_assign_str(&cfg->CHROOT, v);
}
}
}
}
else if (strcmp(k, CFG_USER) == 0) {
if (v != NULL && strlen(v) > 0) {
struct passwd *passwd;
passwd = getpwnam(v);
if (!passwd) {
config_error_set("Invalid user '%s'.", v);
r = 0;
} else {
cfg->UID = passwd->pw_uid;
cfg->GID = passwd->pw_gid;
}
}
}
else if (strcmp(k, CFG_GROUP) == 0) {
if (v != NULL && strlen(v) > 0) {
struct group *grp;
grp = getgrnam(v);
if (!grp) {
config_error_set("Invalid group '%s'.", v);
r = 0;
} else {
cfg->GID = grp->gr_gid;
}
}
}
else if (strcmp(k, CFG_QUIET) == 0) {
r = config_param_val_bool(v, &cfg->QUIET);
}
else if (strcmp(k, CFG_SYSLOG) == 0) {
r = config_param_val_bool(v, &cfg->SYSLOG);
}
else if (strcmp(k, CFG_SYSLOG_FACILITY) == 0) {
r = 1;
if (!strcmp(v, "auth") || !strcmp(v, "authpriv"))
cfg->SYSLOG_FACILITY = LOG_AUTHPRIV;
else if (!strcmp(v, "cron"))
cfg->SYSLOG_FACILITY = LOG_CRON;
else if (!strcmp(v, "daemon"))
cfg->SYSLOG_FACILITY = LOG_DAEMON;
else if (!strcmp(v, "ftp"))
cfg->SYSLOG_FACILITY = LOG_FTP;
else if (!strcmp(v, "local0"))
cfg->SYSLOG_FACILITY = LOG_LOCAL0;
else if (!strcmp(v, "local1"))
cfg->SYSLOG_FACILITY = LOG_LOCAL1;
else if (!strcmp(v, "local2"))
cfg->SYSLOG_FACILITY = LOG_LOCAL2;
else if (!strcmp(v, "local3"))
cfg->SYSLOG_FACILITY = LOG_LOCAL3;
else if (!strcmp(v, "local4"))
cfg->SYSLOG_FACILITY = LOG_LOCAL4;
else if (!strcmp(v, "local5"))
cfg->SYSLOG_FACILITY = LOG_LOCAL5;
else if (!strcmp(v, "local6"))
cfg->SYSLOG_FACILITY = LOG_LOCAL6;
else if (!strcmp(v, "local7"))
cfg->SYSLOG_FACILITY = LOG_LOCAL7;
else if (!strcmp(v, "lpr"))
cfg->SYSLOG_FACILITY = LOG_LPR;
else if (!strcmp(v, "mail"))
cfg->SYSLOG_FACILITY = LOG_MAIL;
else if (!strcmp(v, "news"))
cfg->SYSLOG_FACILITY = LOG_NEWS;
else if (!strcmp(v, "user"))
cfg->SYSLOG_FACILITY = LOG_USER;
else if (!strcmp(v, "uucp"))
cfg->SYSLOG_FACILITY = LOG_UUCP;
else {
config_error_set("Invalid facility '%s'.", v);
r = 0;
}
}
else if (strcmp(k, CFG_DAEMON) == 0) {
r = config_param_val_bool(v, &cfg->DAEMONIZE);
}
else if (strcmp(k, CFG_WRITE_IP) == 0) {
r = config_param_val_bool(v, &cfg->WRITE_IP_OCTET);
}
else if (strcmp(k, CFG_WRITE_PROXY) == 0) {
r = config_param_val_bool(v, &cfg->WRITE_PROXY_LINE);
}
else if (strcmp(k, CFG_PROXY_PROXY) == 0) {
r = config_param_val_bool(v, &cfg->PROXY_PROXY_LINE);
}
else if (strcmp(k, CFG_PEM_FILE) == 0) {
if (v != NULL && strlen(v) > 0) {
if (stat(v, &st) != 0) {
config_error_set("Unable to stat x509 certificate PEM file '%s': ", v, strerror(errno));
r = 0;
}
else if (! S_ISREG(st.st_mode)) {
config_error_set("Invalid x509 certificate PEM file '%s': Not a file.", v);
r = 0;
} else {
struct cert_files *cert = calloc(1, sizeof(*cert));
config_assign_str(&cert->CERT_FILE, v);
cert->NEXT = cfg->CERT_FILES;
cfg->CERT_FILES = cert;
}
}
}
else {
fprintf(
stderr,
"Ignoring unknown configuration key '%s' in configuration file '%s', line %d\n",
k, file, line
);
}
if (! r) {
if (file != NULL)
config_die("Error in configuration file '%s', line %d: %s\n", file, line, config_error_get());
else
config_die("Invalid parameter '%s': %s", k, config_error_get());
}
}
#ifndef NO_CONFIG_FILE
int config_file_parse (char *file, stud_config *cfg) {
if (cfg == NULL)
config_die("Undefined stud options; THIS IS A BUG!\n");
char line[CONFIG_BUF_SIZE];
FILE *fd = NULL;
// should we read stdin?
if (file == NULL || strlen(file) < 1 || strcmp(file, "-") == 0) {
fd = stdin;
} else {
fd = fopen(file, "r");
}
if (fd == NULL)
config_die("Unable to open configuration file '%s': %s\n", file, strerror(errno));
// read config
int i = 0;
while (i < CONFIG_MAX_LINES) {
memset(line, '\0', sizeof(line));
if (fgets(line, (sizeof(line) - 1), fd) == NULL) break;
i++;
// get configuration key
char *key, *val;
key = config_get_param(line);
if (key == NULL) continue;
// get configuration key value...
val = config_get_value(line);
if (val == NULL) continue;
str_trim(val);
// printf("File '%s', line %d, key: '%s', value: '%s'\n", file, i, key, val);
// validate configuration key => value
config_param_validate(key, val, cfg, file, i);
}
fclose(fd);
return 1;
}
#endif /* NO_CONFIG_FILE */
char * config_disp_str (char *str) {
return (str == NULL) ? "" : str;
}
char * config_disp_bool (int v) {
return (v > 0) ? CFG_BOOL_ON : "off";
}
char * config_disp_uid (uid_t uid) {
memset(tmp_buf, '\0', sizeof(tmp_buf));
if (uid == 0 && geteuid() != 0) return tmp_buf;
struct passwd *pw = getpwuid(uid);
if (pw) {
memcpy(tmp_buf, pw->pw_name, strlen(pw->pw_name));
}
return tmp_buf;
}
char * config_disp_gid (gid_t gid) {
memset(tmp_buf, '\0', sizeof(tmp_buf));
if (gid == 0 && geteuid() != 0) return tmp_buf;
struct group *gr = getgrgid(gid);
if (gr) {
memcpy(tmp_buf, gr->gr_name, strlen(gr->gr_name));
}
return tmp_buf;
}
char * config_disp_hostport (char *host, char *port) {
memset(tmp_buf, '\0', sizeof(tmp_buf));
if (host == NULL && port == NULL)
return "";
strcat(tmp_buf, "[");
if (host == NULL)
strcat(tmp_buf, "*");
else {
strncat(tmp_buf, host, 40);
}
strcat(tmp_buf, "]:");
strncat(tmp_buf, port, 5);
return tmp_buf;
}
const char * config_disp_log_facility (int facility) {
switch (facility)
{
case LOG_AUTHPRIV:
return "authpriv";
case LOG_CRON:
return "cron";
case LOG_DAEMON:
return "daemon";
case LOG_FTP:
return "ftp";
case LOG_LOCAL0:
return "local0";
case LOG_LOCAL1:
return "local1";
case LOG_LOCAL2:
return "local2";
case LOG_LOCAL3:
return "local3";
case LOG_LOCAL4:
return "local4";
case LOG_LOCAL5:
return "local5";
case LOG_LOCAL6:
return "local6";
case LOG_LOCAL7:
return "local7";
case LOG_LPR:
return "lpr";
case LOG_MAIL:
return "mail";
case LOG_NEWS:
return "news";
case LOG_USER:
return "user";
case LOG_UUCP:
return "uucp";
default:
return "UNKNOWN";
}
}
void config_print_usage_fd (char *prog, stud_config *cfg, FILE *out) {
if (out == NULL) out = stderr;
fprintf(out, "Usage: %s [OPTIONS] PEM\n\n", basename(prog));
fprintf(out, "This is stud, The Scalable TLS Unwrapping Daemon.\n\n");
#ifndef NO_CONFIG_FILE
fprintf(out, "CONFIGURATION:\n");
fprintf(out, "\n");
fprintf(out, " --config=FILE Load configuration from specified file.\n");
fprintf(out, " --default-config Prints default configuration to stdout.\n");
fprintf(out, "\n");
#endif
fprintf(out, "ENCRYPTION METHODS:\n");
fprintf(out, "\n");
fprintf(out, " --tls TLSv1 (default)\n");
fprintf(out, " --ssl SSLv3 (implies no TLSv1)\n");
fprintf(out, " -c --ciphers=SUITE Sets allowed ciphers (Default: \"%s\")\n", config_disp_str(cfg->CIPHER_SUITE));
fprintf(out, " -e --ssl-engine=NAME Sets OpenSSL engine (Default: \"%s\")\n", config_disp_str(cfg->ENGINE));
fprintf(out, " -O --prefer-server-ciphers Prefer server list order\n");
fprintf(out, "\n");
fprintf(out, "SOCKET:\n");
fprintf(out, "\n");
fprintf(out, " --client Enable client proxy mode\n");
fprintf(out, " -b --backend=HOST,PORT Backend [connect] (default is \"%s\")\n", config_disp_hostport(cfg->BACK_IP, cfg->BACK_PORT));
fprintf(out, " -f --frontend=HOST,PORT Frontend [bind] (default is \"%s\")\n", config_disp_hostport(cfg->FRONT_IP, cfg->FRONT_PORT));
#ifdef USE_SHARED_CACHE
fprintf(out, "\n");
fprintf(out, " -U --shared-cache-listen=HOST,PORT\n");
fprintf(out, " Accept cache updates on UDP (Default: \"%s\")\n", config_disp_hostport(cfg->SHCUPD_IP, cfg->SHCUPD_PORT));
fprintf(out, " NOTE: This option requires enabled SSL session cache.\n");
fprintf(out, " -P --shared-cache-peer=HOST,PORT\n");
fprintf(out, " Send cache updates to specified peer\n");
fprintf(out, " NOTE: This option can be specified multiple times.\n");
fprintf(out, " -M --shared-cache-if=IFACE[,TTL]\n");
fprintf(out, " Force iface and ttl to receive and send multicast updates\n");
#endif
fprintf(out, "\n");
fprintf(out, "PERFORMANCE:\n");
fprintf(out, "\n");
fprintf(out, " -n --workers=NUM Number of worker processes (Default: %ld)\n", cfg->NCORES);
fprintf(out, " -B --backlog=NUM Set listen backlog size (Default: %d)\n", cfg->BACKLOG);
fprintf(out, " -k --keepalive=SECS TCP keepalive on client socket (Default: %d)\n", cfg->TCP_KEEPALIVE_TIME);
#ifdef USE_SHARED_CACHE
fprintf(out, " -C --session-cache=NUM Enable and set SSL session cache to specified number\n");
fprintf(out, " of sessions (Default: %d)\n", cfg->SHARED_CACHE);
#endif
fprintf(out, "\n");
fprintf(out, "SECURITY:\n");
fprintf(out, "\n");
fprintf(out, " -r --chroot=DIR Sets chroot directory (Default: \"%s\")\n", config_disp_str(cfg->CHROOT));
fprintf(out, " -u --user=USER Set uid/gid after binding the socket (Default: \"%s\")\n", config_disp_uid(cfg->UID));
fprintf(out, " -g --group=GROUP Set gid after binding the socket (Default: \"%s\")\n", config_disp_gid(cfg->GID));
fprintf(out, "\n");
fprintf(out, "LOGGING:\n");
fprintf(out, " -q --quiet Be quiet; emit only error messages\n");
fprintf(out, " -s --syslog Send log message to syslog in addition to stderr/stdout\n");
fprintf(out, " --syslog-facility=FACILITY Syslog facility to use (Default: \"%s\")\n", config_disp_log_facility(cfg->SYSLOG_FACILITY));
fprintf(out, "\n");
fprintf(out, "OTHER OPTIONS:\n");
fprintf(out, " --daemon Fork into background and become a daemon (Default: %s)\n", config_disp_bool(cfg->DAEMONIZE));
fprintf(out, " --write-ip Write 1 octet with the IP family followed by the IP\n");
fprintf(out, " address in 4 (IPv4) or 16 (IPv6) octets little-endian\n");
fprintf(out, " to backend before the actual data\n");
fprintf(out, " (Default: %s)\n", config_disp_bool(cfg->WRITE_IP_OCTET));
fprintf(out, " --write-proxy Write HaProxy's PROXY (IPv4 or IPv6) protocol line\n" );
fprintf(out, " before actual data\n");
fprintf(out, " (Default: %s)\n", config_disp_bool(cfg->WRITE_PROXY_LINE));
fprintf(out, " --proxy-proxy Proxy HaProxy's PROXY (IPv4 or IPv6) protocol line\n" );
fprintf(out, " before actual data\n");
fprintf(out, " (Default: %s)\n", config_disp_bool(cfg->PROXY_PROXY_LINE));
fprintf(out, "\n");
fprintf(out, " -t --test Test configuration and exit\n");
fprintf(out, " -V --version Print program version and exit\n");
fprintf(out, " -h --help This help message\n");
}
#ifndef NO_CONFIG_FILE
void config_print_default (FILE *fd, stud_config *cfg) {
if (fd == NULL) return;
fprintf(fd, "#\n");
fprintf(fd, "# stud(8), The Scalable TLS Unwrapping Daemon's configuration\n");
fprintf(fd, "#\n");
fprintf(fd, "\n");
fprintf(fd, "# NOTE: all config file parameters can be overriden\n");
fprintf(fd, "# from command line!\n");
fprintf(fd, "\n");
fprintf(fd, "# Listening address. REQUIRED.\n");
fprintf(fd, "#\n");
fprintf(fd, "# type: string\n");
fprintf(fd, "# syntax: [HOST]:PORT\n");
fprintf(fd, FMT_QSTR, CFG_FRONTEND, config_disp_hostport(cfg->FRONT_IP, cfg->FRONT_PORT));
fprintf(fd, "\n");
fprintf(fd, "# Upstream server address. REQUIRED.\n");
fprintf(fd, "#\n");
fprintf(fd, "# type: string\n");
fprintf(fd, "# syntax: [HOST]:PORT.\n");
fprintf(fd, FMT_QSTR, CFG_BACKEND, config_disp_hostport(cfg->BACK_IP, cfg->BACK_PORT));
fprintf(fd, "\n");
fprintf(fd, "# SSL x509 certificate file. REQUIRED.\n");
fprintf(fd, "# List multiple certs to use SNI. Certs are used in the order they\n");
fprintf(fd, "# are listed; the last cert listed will be used if none of the others match\n");
fprintf(fd, "#\n");
fprintf(fd, "# type: string\n");
fprintf(fd, FMT_QSTR, CFG_PEM_FILE, "");
fprintf(fd, "\n");
fprintf(fd, "# SSL protocol.\n");
fprintf(fd, "#\n");
fprintf(fd, "# tls = on\n");
fprintf(fd, "# ssl = off\n");
fprintf(fd, "\n");
fprintf(fd, "# List of allowed SSL ciphers.\n");
fprintf(fd, "#\n");
fprintf(fd, "# Run openssl ciphers for list of available ciphers.\n");
fprintf(fd, "# type: string\n");
fprintf(fd, FMT_QSTR, CFG_CIPHERS, config_disp_str(cfg->CIPHER_SUITE));
fprintf(fd, "\n");
fprintf(fd, "# Enforce server cipher list order\n");
fprintf(fd, "#\n");
fprintf(fd, "# type: boolean\n");
fprintf(fd, FMT_STR, CFG_PREFER_SERVER_CIPHERS, config_disp_bool(cfg->PREFER_SERVER_CIPHERS));
fprintf(fd, "\n");
fprintf(fd, "# Use specified SSL engine\n");
fprintf(fd, "#\n");
fprintf(fd, "# type: string\n");
fprintf(fd, FMT_QSTR, CFG_SSL_ENGINE, config_disp_str(cfg->ENGINE));
fprintf(fd, "\n");
fprintf(fd, "# Number of worker processes\n");
fprintf(fd, "#\n");
fprintf(fd, "# type: integer\n");
fprintf(fd, FMT_ISTR, CFG_WORKERS, (int) cfg->NCORES);
fprintf(fd, "\n");
fprintf(fd, "# Listen backlog size\n");
fprintf(fd, "#\n");
fprintf(fd, "# type: integer\n");
fprintf(fd, FMT_ISTR, CFG_BACKLOG, cfg->BACKLOG);
fprintf(fd, "\n");
fprintf(fd, "# TCP socket keepalive interval in seconds\n");
fprintf(fd, "#\n");
fprintf(fd, "# type: integer\n");
fprintf(fd, FMT_ISTR, CFG_KEEPALIVE, cfg->TCP_KEEPALIVE_TIME);
fprintf(fd, "\n");
#ifdef USE_SHARED_CACHE
fprintf(fd, "# SSL session cache size\n");
fprintf(fd, "#\n");
fprintf(fd, "# type: integer\n");
fprintf(fd, FMT_ISTR, CFG_SHARED_CACHE, cfg->SHARED_CACHE);
fprintf(fd, "\n");
fprintf(fd, "# Accept shared SSL cache updates on specified listener.\n");
fprintf(fd, "#\n");
fprintf(fd, "# type: string\n");
fprintf(fd, "# syntax: [HOST]:PORT\n");
fprintf(fd, FMT_QSTR, CFG_SHARED_CACHE_LISTEN, config_disp_hostport(cfg->SHCUPD_IP, cfg->SHCUPD_PORT));
fprintf(fd, "\n");
fprintf(fd, "# Shared cache peer address.\n");
fprintf(fd, "# Multiple stud processes on multiple hosts (host limit: %d)\n", MAX_SHCUPD_PEERS);
fprintf(fd, "# can share SSL session cache by sending updates to peers.\n");
fprintf(fd, "#\n");
fprintf(fd, "# NOTE: This parameter can be specified multiple times in order\n");
fprintf(fd, "# to specify multiple peers.\n");
fprintf(fd, "#\n");
fprintf(fd, "# type: string\n");
fprintf(fd, "# syntax: [HOST]:PORT\n");
fprintf(fd, "# " FMT_QSTR, CFG_SHARED_CACHE_PEER, config_disp_hostport(NULL, NULL));
for (int i = 0; i < MAX_SHCUPD_PEERS; i++) {
if (cfg->SHCUPD_PEERS[i].ip == NULL && cfg->SHCUPD_PEERS[i].port == NULL) break;
fprintf(fd, FMT_QSTR, CFG_SHARED_CACHE_PEER, config_disp_hostport(cfg->SHCUPD_PEERS[i].ip, cfg->SHCUPD_PEERS[i].port));
}
fprintf(fd, "\n");
fprintf(fd, "# Shared cache interface name and optional TTL\n");
fprintf(fd, "#\n");
fprintf(fd, "# type: string\n");
fprintf(fd, "# syntax: iface[,TTL]\n");
fprintf(fd, "# %s = \"%s", CFG_SHARED_CACHE_MCASTIF, config_disp_str(cfg->SHCUPD_MCASTIF));
if (cfg->SHCUPD_MCASTTTL != NULL && strlen(cfg->SHCUPD_MCASTTTL) > 0) {
fprintf(fd, ",%s", cfg->SHCUPD_MCASTTTL);
}
fprintf(fd, "\"\n");
fprintf(fd, "\n");
#endif
fprintf(fd, "# Chroot directory\n");
fprintf(fd, "#\n");
fprintf(fd, "# type: string\n");
fprintf(fd, FMT_QSTR, CFG_CHROOT, config_disp_str(cfg->CHROOT));
fprintf(fd, "\n");
fprintf(fd, "# Set uid after binding a socket\n");
fprintf(fd, "#\n");
fprintf(fd, "# type: string\n");
fprintf(fd, FMT_QSTR, CFG_USER, config_disp_uid(cfg->UID));
fprintf(fd, "\n");
fprintf(fd, "# Set gid after binding a socket\n");
fprintf(fd, "#\n");
fprintf(fd, "# type: string\n");
fprintf(fd, FMT_QSTR, CFG_GROUP, config_disp_gid(cfg->GID));
fprintf(fd, "\n");
fprintf(fd, "# Quiet execution, report only error messages\n");
fprintf(fd, "#\n");
fprintf(fd, "# type: boolean\n");
fprintf(fd, FMT_STR, CFG_QUIET, config_disp_bool(cfg->QUIET));
fprintf(fd, "\n");
fprintf(fd, "# Use syslog for logging\n");
fprintf(fd, "#\n");
fprintf(fd, "# type: boolean\n");
fprintf(fd, FMT_STR, CFG_SYSLOG, config_disp_bool(cfg->SYSLOG));
fprintf(fd, "\n");
fprintf(fd, "# Syslog facility to use\n");
fprintf(fd, "#\n");
fprintf(fd, "# type: string\n");
fprintf(fd, FMT_QSTR, CFG_SYSLOG_FACILITY, config_disp_log_facility(cfg->SYSLOG_FACILITY));
fprintf(fd, "\n");
fprintf(fd, "# Run as daemon\n");
fprintf(fd, "#\n");
fprintf(fd, "# type: boolean\n");
fprintf(fd, FMT_STR, CFG_DAEMON, config_disp_bool(cfg->DAEMONIZE));
fprintf(fd, "\n");
fprintf(fd, "# Report client address by writing IP before sending data\n");
fprintf(fd, "#\n");
fprintf(fd, "# NOTE: This option is mutually exclusive with option %s and %s.\n", CFG_WRITE_PROXY, CFG_PROXY_PROXY);
fprintf(fd, "#\n");
fprintf(fd, "# type: boolean\n");
fprintf(fd, FMT_STR, CFG_WRITE_IP, config_disp_bool(cfg->WRITE_IP_OCTET));
fprintf(fd, "\n");
fprintf(fd, "# Report client address using SENDPROXY protocol, see\n");
fprintf(fd, "# http://haproxy.1wt.eu/download/1.5/doc/proxy-protocol.txt\n");
fprintf(fd, "# for details.\n");
fprintf(fd, "#\n");
fprintf(fd, "# NOTE: This option is mutually exclusive with option %s and %s.\n", CFG_WRITE_IP, CFG_PROXY_PROXY);
fprintf(fd, "#\n");
fprintf(fd, "# type: boolean\n");
fprintf(fd, FMT_STR, CFG_WRITE_PROXY, config_disp_bool(cfg->WRITE_PROXY_LINE));
fprintf(fd, "\n");
fprintf(fd, "# Proxy an existing SENDPROXY protocol header through this request.\n");
fprintf(fd, "#\n");
fprintf(fd, "# NOTE: This option is mutually exclusive with option %s and %s.\n", CFG_WRITE_IP, CFG_WRITE_PROXY);
fprintf(fd, "#\n");
fprintf(fd, "# type: boolean\n");
fprintf(fd, FMT_STR, CFG_PROXY_PROXY, config_disp_bool(cfg->PROXY_PROXY_LINE));
fprintf(fd, "\n");
fprintf(fd, "# EOF\n");
}
#endif /* NO_CONFIG_FILE */
void config_print_usage (char *prog, stud_config *cfg) {
config_print_usage_fd(prog, cfg, stdout);
}
void config_parse_cli(int argc, char **argv, stud_config *cfg) {
static int tls = 0, ssl = 0;
static int client = 0;
int c, i;
int test_only = 0;
char *prog;
struct option long_options[] = {
#ifndef NO_CONFIG_FILE
{ CFG_CONFIG, 1, NULL, CFG_PARAM_CFGFILE },
{ CFG_CONFIG_DEFAULT, 0, NULL, CFG_PARAM_DEFCFG },
#endif
{ "tls", 0, &tls, 1},
{ "ssl", 0, &ssl, 1},
{ "client", 0, &client, 1},
{ CFG_CIPHERS, 1, NULL, 'c' },
{ CFG_PREFER_SERVER_CIPHERS, 0, NULL, 'O' },
{ CFG_BACKEND, 1, NULL, 'b' },
{ CFG_FRONTEND, 1, NULL, 'f' },
{ CFG_WORKERS, 1, NULL, 'n' },
{ CFG_BACKLOG, 1, NULL, 'B' },
#ifdef USE_SHARED_CACHE
{ CFG_SHARED_CACHE, 1, NULL, 'C' },
{ CFG_SHARED_CACHE_LISTEN, 1, NULL, 'U' },
{ CFG_SHARED_CACHE_PEER, 1, NULL, 'P' },
{ CFG_SHARED_CACHE_MCASTIF, 1, NULL, 'M' },
#endif
{ CFG_KEEPALIVE, 1, NULL, 'k' },
{ CFG_CHROOT, 1, NULL, 'r' },
{ CFG_USER, 1, NULL, 'u' },
{ CFG_GROUP, 1, NULL, 'g' },
{ CFG_QUIET, 0, NULL, 'q' },
{ CFG_SYSLOG, 0, NULL, 's' },
{ CFG_SYSLOG_FACILITY, 1, NULL, CFG_PARAM_SYSLOG_FACILITY },
{ CFG_DAEMON, 0, &cfg->DAEMONIZE, 1 },
{ CFG_WRITE_IP, 0, &cfg->WRITE_IP_OCTET, 1 },
{ CFG_WRITE_PROXY, 0, &cfg->WRITE_PROXY_LINE, 1 },
{ CFG_PROXY_PROXY, 0, &cfg->PROXY_PROXY_LINE, 1 },
{ "test", 0, NULL, 't' },
{ "version", 0, NULL, 'V' },
{ "help", 0, NULL, 'h' },
{ 0, 0, 0, 0 }
};
while (1) {
int option_index = 0;
c = getopt_long(
argc, argv,
"c:e:Ob:f:n:B:C:U:P:M:k:r:u:g:qstVh",
long_options, &option_index
);
if (c == -1)
break;
switch (c) {
case 0:
break;
#ifndef NO_CONFIG_FILE
case CFG_PARAM_CFGFILE:
if (!config_file_parse(optarg, cfg))
config_die("%s", config_error_get());
break;
case CFG_PARAM_DEFCFG:
config_print_default(stdout, cfg);
exit(0);
break;
#endif
case CFG_PARAM_SYSLOG_FACILITY:
config_param_validate(CFG_SYSLOG_FACILITY, optarg, cfg, NULL, 0);
break;
case 'c':
config_param_validate(CFG_CIPHERS, optarg, cfg, NULL, 0);
break;
case 'e':
config_param_validate(CFG_SSL_ENGINE, optarg, cfg, NULL, 0);
break;
case 'O':
config_param_validate(CFG_PREFER_SERVER_CIPHERS, CFG_BOOL_ON, cfg, NULL, 0);
break;
case 'b':
config_param_validate(CFG_BACKEND, optarg, cfg, NULL, 0);
break;
case 'f':
config_param_validate(CFG_FRONTEND, optarg, cfg, NULL, 0);
break;
case 'n':
config_param_validate(CFG_WORKERS, optarg, cfg, NULL, 0);
break;
case 'B':
config_param_validate(CFG_BACKLOG, optarg, cfg, NULL, 0);
break;
#ifdef USE_SHARED_CACHE
case 'C':
config_param_validate(CFG_SHARED_CACHE, optarg, cfg, NULL, 0);
break;
case 'U':
config_param_validate(CFG_SHARED_CACHE_LISTEN, optarg, cfg, NULL, 0);
break;
case 'P':
config_param_validate(CFG_SHARED_CACHE_PEER, optarg, cfg, NULL, 0);
break;
case 'M':
config_param_validate(CFG_SHARED_CACHE_MCASTIF, optarg, cfg, NULL, 0);
break;
#endif
case 'k':
config_param_validate(CFG_KEEPALIVE, optarg, cfg, NULL, 0);
break;
case 'r':
config_param_validate(CFG_CHROOT, optarg, cfg, NULL, 0);
break;
case 'u':
config_param_validate(CFG_USER, optarg, cfg, NULL, 0);
break;
case 'g':
config_param_validate(CFG_GROUP, optarg, cfg, NULL, 0);
break;
case 'q':
config_param_validate(CFG_QUIET, CFG_BOOL_ON, cfg, NULL, 0);
break;
case 's':
config_param_validate(CFG_SYSLOG, CFG_BOOL_ON, cfg, NULL, 0);
break;
case 't':
test_only = 1;
break;
case 'V':
printf("%s %s\n", basename(argv[0]), STUD_VERSION);
exit(0);
break;
case 'h':
config_print_usage(argv[0], cfg);
exit(0);
break;
default:
config_die("Invalid command line parameters. Run %s --help for instructions.", basename(argv[0]));
}
}
prog = argv[0];
if (tls && ssl)
config_die("Options --tls and --ssl are mutually exclusive.");
else {
if (ssl)
cfg->ETYPE = ENC_SSL;
else if (tls)
cfg->ETYPE = ENC_TLS;
}
if (client) {
cfg->PMODE = SSL_CLIENT;
}
if (cfg->WRITE_IP_OCTET && cfg->WRITE_PROXY_LINE)
config_die("Options --write-ip and --write-proxy are mutually exclusive.");
if (cfg->WRITE_PROXY_LINE && cfg->PROXY_PROXY_LINE)
config_die("Options --write-proxy and --proxy-proxy are mutually exclusive.");
if (cfg->WRITE_IP_OCTET && cfg->PROXY_PROXY_LINE)
config_die("Options --write-ip and --proxy-proxy are mutually exclusive.");
if (cfg->DAEMONIZE) {
cfg->SYSLOG = 1;
cfg->QUIET = 1;
}
#ifdef USE_SHARED_CACHE
if (cfg->SHCUPD_IP != NULL && ! cfg->SHARED_CACHE)
config_die("Shared cache update listener is defined, but shared cache is disabled.");
#endif
// Any arguments left are presumed to be PEM files
argc -= optind;
argv += optind;
for (i = 0; i < argc; i++) {
config_param_validate(CFG_PEM_FILE, argv[i], cfg, NULL, 0);
}
if (cfg->PMODE == SSL_SERVER && cfg->CERT_FILES == NULL) {
config_die("No x509 certificate PEM file specified!");
}
// was this only a test?
if (test_only) {
fprintf(stderr, "Trying to initialize SSL contexts with your certificates");
if (!init_openssl()) {
config_die("Error initializing OpenSSL.");
}
printf("%s configuration looks ok.\n", basename(prog));
exit(0);
}
}
Jump to Line
Something went wrong with that request. Please try again.