Files
rmps/rmps.c

291 lines
8.7 KiB
C

#include "log_trace.h"
#include "confparser.h"
#include "thread_pool.h"
#include "rmps.h"
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
/* included for openssl */
#include <openssl/ssl.h>
#include <openssl/err.h>
static void rmps_shutdown();
static void signal_handler(int sig);
static void set_env();
static void daemonize(const char *rundir);
static void spawn_pidfile(const char *pidfile);
static inline int set_reuse_addr(int sockfd);
static int open_listener(int port);
static void cleanup();
static void signal_handler(int sig);
//static void show_certs(SSL *ssl);
static void load_certificates(SSL_CTX *ctx, const char *certfile,
const char *keyfile, const char *cafile);
static SSL_CTX* init_server_ctx(const char *cipherlist);
//static void servlet(SSL *ssl);
static int pid_file_handle;
static void cleanup()
{
log_trace(VERBOSE, "Deleting pidfile %s", conf.rmps.pidfile);
if (unlink(conf.rmps.pidfile) != 0)
log_trace( WARNING,
"Failed to delete pidfile %s. Reason code: %d",
conf.rmps.pidfile, errno );
}
static void signal_handler(int sig)
{
switch (sig) {
case SIGHUP:
log_trace(WARNING, "Received SIGHUP signal. Ignoring...");
break;
case SIGINT:
case SIGTERM:
log_trace(INFO, "Received SIGTERM signal.");
log_trace(INFO, "RMPS is shutting down...");
rmps_shutdown();
log_trace(INFO, "RMPS has been stopped properly.");
_exit(EXIT_SUCCESS);
break;
default:
log_trace(WARNING, "Unhandled signal %s", strsignal(sig));
break;
}
}
static void rmps_shutdown()
{
close(pid_file_handle);
cleanup();
}
static void set_env()
{
struct sigaction new_sigaction;
sigset_t new_sigset;
/* Set signal mask - signals we want to block */
sigemptyset(&new_sigset);
sigaddset(&new_sigset, SIGCHLD); /* ignore child - i.e. we don't need to wait for it */
sigaddset(&new_sigset, SIGTSTP); /* ignore Tty stop signals */
sigaddset(&new_sigset, SIGTTOU); /* ignore Tty background writes */
sigaddset(&new_sigset, SIGTTIN); /* ignore Tty background reads */
sigprocmask(SIG_BLOCK, &new_sigset, NULL); /* Block the above specified signals */
/* Set up a signal handler */
new_sigaction.sa_handler = signal_handler;
sigemptyset(&new_sigaction.sa_mask);
new_sigaction.sa_flags = 0;
/* Signals to handle */
sigaction(SIGHUP, &new_sigaction, NULL); /* catch hangup signal */
sigaction(SIGTERM, &new_sigaction, NULL); /* catch term signal */
sigaction(SIGINT, &new_sigaction, NULL); /* catch interrupt signal */
}
static void daemonize(const char *rundir)
{
int pid, sid, i;
/* Check if parent process id is set */
if (getppid() == 1) {
/* PPID exists, therefore we are already a daemon */
return;
}
/* Fork*/
pid = fork();
if (pid < 0) {
/* Could not fork */
log_trace(ERROR, "Failed to fork the parent process. Exiting!");
exit(EXIT_FAILURE);
}
if (pid > 0) {
/* Child created ok, so exit parent process */
log_trace(INFO, "Child process created: %d", pid);
exit(EXIT_SUCCESS);
}
/* Child continues */
umask(027); /* Set file permissions 750 */
/* Get a new process group */
sid = setsid();
if (sid < 0) {
log_trace(ERROR, "Failed to create a process group. Exiting!");
exit(EXIT_FAILURE);
}
/* Close all file descriptors because we fork */
close(0); /* stdin */
close(1); /* stdout */
close(2); /* stderr */
/* Route I/O connections */
/* Open STDIN */
i = open("/dev/null", O_RDWR);
/* STDOUT */
dup(i);
/* STDERR */
dup(i);
chdir(rundir); /* change running directory */
}
static void spawn_pidfile(const char *pidfile)
{
char str[10];
/* Ensure only one copy */
pid_file_handle = open(pidfile, O_RDWR|O_CREAT, 0600);
if (pid_file_handle == -1) {
/* Couldn't open lock file */
log_trace(ERROR, "Could not create PID file %s - Exiting!", pidfile);
exit(EXIT_FAILURE);
}
/* Try to lock file */
if (lockf(pid_file_handle, F_TLOCK, 0) == -1) {
/* Couldn't get lock on lock file */
log_trace(ERROR, "Could not lock PID file %s - Exiting!", pidfile);
exit(EXIT_FAILURE);
}
/* Get and format PID */
sprintf(str, "%d\n", getpid());
/* write pid to lockfile */
write(pid_file_handle, str, strlen(str));
}
static inline int set_reuse_addr(int sockfd)
{
int yes = 1;
return setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR,
&yes, sizeof(yes));
}
static int open_listener(int port)
{
int sd;
struct sockaddr_in addr;
bzero(&addr, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = INADDR_ANY;
sd = socket(addr.sin_family, SOCK_STREAM, 0);
if (sd < 0) {
log_trace(ERROR, "Failed to create socket");
log_trace(INFO, "RMPS failed to start, shutting down...");
atexit(cleanup);
}
if (set_reuse_addr(sd) < 0) {
log_trace(ERROR, "Failed to set reuse on address - Aborting...", port);
log_trace(INFO, "RMPS failed to start, shutting down...");
atexit(cleanup);
}
if (bind(sd, (struct sockaddr*)&addr, sizeof(addr)) != 0) {
log_trace(ERROR, "Failed to bind on port: %d - Aborting...", port);
log_trace(INFO, "RMPS failed to start, shutting down...");
atexit(cleanup);
}
if (listen(sd, 10) != 0) {
log_trace(ERROR, "Failed to start listener on port %d - Aborting...", port);
log_trace(INFO, "RMPS failed to start, shutting down...");
atexit(cleanup);
}
return sd;
}
/* Init server and create context */
static SSL_CTX* init_server_ctx(const char *cipherlist)
{
SSL_CTX *ctx;
char ciphers[1024];
OpenSSL_add_all_algorithms(); /* load & register all cryptos, etc. */
OpenSSL_add_all_ciphers(); /* load & register all cryptos, etc. */
SSL_load_error_strings(); /* load all error messages */
SSL_library_init();
ctx = SSL_CTX_new(TLSv1_2_method()); /* create new context from method */
if (ctx == NULL) {
log_trace(ERROR, "SSL_CTX_new() returned NULL - Aborting...");
log_trace(ERROR, "RMPS failed to start, shutting down...");
exit(EXIT_FAILURE);
}
SSL_CTX_set_verify( ctx, SSL_VERIFY_PEER |
SSL_VERIFY_CLIENT_ONCE |
SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
NULL);
ciphers[0] = 0;
strcat(ciphers, "-ALL"); /* Disable any ciphers we have by default */
strcat(ciphers, cipherlist);
/* This is very delicate, try to understand the ciphers */
SSL_CTX_set_cipher_list(ctx, cipherlist);
log_trace(VERBOSE, "cipherlist = %s", cipherlist);
return ctx;
}
/*---------------------------------------------------------------------*/
/*--- LoadCertificates - load from files. ---*/
/*---------------------------------------------------------------------*/
void load_certificates(SSL_CTX* ctx, const char *certfile,
const char *keyfile, const char *cafile)
{
/* set the local certificate from certfile */
if (SSL_CTX_use_certificate_file(ctx, certfile, SSL_FILETYPE_PEM) <= 0) {
log_trace(ERROR, "Failed to load certfile! SSL error below:");
log_ssl();
log_trace(INFO, "RMPS failed to start, shutting down...");
atexit(cleanup);
}
/* set the private key from KeyFile (may be the same as CertFile) */
if (SSL_CTX_use_PrivateKey_file(ctx, keyfile, SSL_FILETYPE_PEM) <= 0) {
log_trace(ERROR, "Failed to load keyfile! SSL error below:");
log_ssl();
log_trace(INFO, "RMPS failed to start, shutting down...");
atexit(cleanup);
}
/* verify private key */
if (!SSL_CTX_check_private_key(ctx)) {
log_trace(ERROR, "Private key does not match the public certificate.");
log_trace(INFO, "RMPS failed to start, shutting down...");
atexit(cleanup);
}
if (cafile != NULL) {
SSL_CTX_set_client_CA_list(ctx, SSL_load_client_CA_file(cafile));
SSL_CTX_load_verify_locations(ctx, cafile, NULL);
//SSL_CTX_set_verify_depth(ctx, 1);
}
}
int launch_rmps(struct conf_table *conf, int fork_flag)
{
int server;
log_trace(INFO, "Starting up RMPS...");
/* Set signal handling */
set_env();
/* Deamonize */
if (fork_flag)
daemonize("/tmp/");
/* Spawn & lock pidfile */
spawn_pidfile(conf->rmps.pidfile);
SSL_CTX *ctx;
ctx = init_server_ctx(conf->rmps.cipherlist);
/* openssl req -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem -days XXX -nodes
* -nodes is for not protecing with a passphrase
* http://stackoverflow.com/questions/10175812/how-to-create-a-self-signed-certificate-with-openssl
*/
log_trace(VERBOSE, "Loading crypto certs and keys.");
load_certificates(ctx, conf->rmps.certfile, conf->rmps.keyfile, conf->rmps.cafile);
log_trace(VERBOSE, "Starting listener on port: %d", atoi(conf->rmps.bind_on_port));
server = open_listener(atoi(conf->rmps.bind_on_port));
log_trace(VERBOSE, "Creating mutex for thread pool.");
ssl_pt_mutex(server, ctx, conf->rmps.threadpoolsize);
return 0;
}