summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVsevolod Stakhov <vsevolod@highsecure.ru>2013-08-22 14:57:03 +0100
committerVsevolod Stakhov <vsevolod@highsecure.ru>2013-08-22 14:57:03 +0100
commitfc9211cf8dea975503e28d1cba6a625fe9464e81 (patch)
treef53976bbc904fd16a7a4311e110d27d0d8806c95
parent05d349875b7b475359c779997464c2589979e545 (diff)
downloadrspamd-fc9211cf8dea975503e28d1cba6a625fe9464e81.tar.gz
rspamd-fc9211cf8dea975503e28d1cba6a625fe9464e81.zip
Add ability to sign configs using rspamd.
-rw-r--r--CMakeLists.txt3
-rw-r--r--config.h.in15
-rw-r--r--src/main.c126
-rw-r--r--src/util.c112
-rw-r--r--src/util.h10
5 files changed, 266 insertions, 0 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index a14e44383..f0a6954e2 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -895,6 +895,9 @@ CHECK_INCLUDE_FILES(pwd.h HAVE_PWD_H)
CHECK_INCLUDE_FILES(grp.h HAVE_GRP_H)
CHECK_INCLUDE_FILES(glob.h HAVE_GLOB_H)
CHECK_INCLUDE_FILES(poll.h HAVE_POLL_H)
+CHECK_INCLUDE_FILES(readpassphrase.h HAVE_READPASSPHRASE_H)
+CHECK_INCLUDE_FILES(termios.h HAVE_TERMIOS_H)
+CHECK_INCLUDE_FILES(paths.h HAVE_PATHS_H)
CHECK_INCLUDE_FILES(sys/sendfile.h HAVE_SYS_SENDFILE_H)
CHECK_INCLUDE_FILES(linux/falloc.h HAVE_LINUX_FALLOC_H)
CHECK_INCLUDE_FILES(sys/eventfd.h HAVE_SYS_EVENTFD_H)
diff --git a/config.h.in b/config.h.in
index 31a0dd2d6..dd1af0f55 100644
--- a/config.h.in
+++ b/config.h.in
@@ -203,6 +203,9 @@
#cmakedefine HAVE_FETCH_H 1
#cmakedefine CURL_FOUND 1
+#cmakedefine HAVE_READPASSPHRASE_H 1
+#cmakedefine HAVE_TERMIOS_H 1
+
#if (defined(__i386__) || defined(__x86_64__) || defined(_M_IX86))
/* Use murmur hash for UTHash for these platforms */
#define HASH_FUNCTION HASH_MUR
@@ -431,6 +434,18 @@
#include <curl/curl.h>
#endif
+#ifdef HAVE_READPASSPHRASE_H
+#include <readpassphrase.h>
+#endif
+
+#ifdef HAVE_TERMIOS_H
+#include <termios.h>
+#endif
+
+#ifdef HAVE_PATHS_H
+#include <paths.h>
+#endif
+
#include <errno.h>
#include <signal.h>
#ifdef HAVE_SIGINFO_H
diff --git a/src/main.c b/src/main.c
index 607685720..105a7002b 100644
--- a/src/main.c
+++ b/src/main.c
@@ -38,6 +38,8 @@
#include <openssl/rand.h>
#include <openssl/err.h>
#include <openssl/evp.h>
+#include <openssl/rsa.h>
+#include <openssl/pem.h>
#endif
/* 2 seconds to fork new process in place of dead one */
@@ -64,6 +66,8 @@ static gboolean config_test = FALSE;
static gboolean no_fork = FALSE;
static gchar **cfg_names = NULL;
static gchar **lua_tests = NULL;
+static gchar **sign_configs = NULL;
+static gchar *privkey = NULL;
static gchar *rspamd_user = NULL;
static gchar *rspamd_group = NULL;
static gchar *rspamd_pidfile = NULL;
@@ -97,6 +101,8 @@ static GOptionEntry entries[] =
{ "debug", 'd', 0, G_OPTION_ARG_NONE, &is_debug, "Force debug output", NULL },
{ "insecure", 'i', 0, G_OPTION_ARG_NONE, &is_insecure, "Ignore running workers as privileged users (insecure)", NULL },
{ "test-lua", 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &lua_tests, "Specify lua file(s) to test", NULL },
+ { "sign-config", 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &sign_configs, "Specify config file(s) to sign", NULL },
+ { "private-key", 0, 0, G_OPTION_ARG_FILENAME, &privkey, "Specify private key to sign", NULL },
{ NULL, 0, 0, G_OPTION_ARG_NONE, NULL, NULL, NULL }
};
@@ -874,6 +880,121 @@ perform_lua_tests (struct config_file *cfg)
return res;
}
+static gint
+perform_configs_sign (void)
+{
+#ifndef HAVE_OPENSSL
+ msg_err ("cannot sign files without openssl support");
+ return EXIT_FAILURE;
+#else
+ gint i, tests_num, res = EXIT_SUCCESS, fd;
+ gchar *cur_file, in_file[PATH_MAX], out_file[PATH_MAX];
+ gsize siglen;
+ struct stat st;
+ gpointer map, sig;
+ EVP_PKEY *key = NULL;
+ BIO *fbio;
+ EVP_PKEY_CTX *key_ctx;
+
+ /* Load private key */
+ fbio = BIO_new_file (privkey, "r");
+ if (fbio == NULL) {
+ msg_err ("cannot open private key %s, %s", privkey,
+ ERR_error_string (ERR_get_error (), NULL));
+ return ERR_get_error ();
+ }
+ if (!PEM_read_bio_PrivateKey (fbio, &key, rspamd_read_passphrase, NULL)) {
+ msg_err ("cannot read private key %s, %s", privkey,
+ ERR_error_string (ERR_get_error (), NULL));
+ return ERR_get_error ();
+ }
+
+ key_ctx = EVP_PKEY_CTX_new (key, NULL);
+ if (key_ctx == NULL) {
+ msg_err ("cannot parse private key %s, %s", privkey,
+ ERR_error_string (ERR_get_error (), NULL));
+ return ERR_get_error ();
+ }
+
+ if (EVP_PKEY_sign_init (key_ctx) <= 0) {
+ msg_err ("cannot parse private key %s, %s", privkey,
+ ERR_error_string (ERR_get_error (), NULL));
+ return ERR_get_error ();
+ }
+ if (EVP_PKEY_CTX_set_rsa_padding (key_ctx, RSA_PKCS1_PADDING) <= 0) {
+ msg_err ("cannot init private key %s, %s", privkey,
+ ERR_error_string (ERR_get_error (), NULL));
+ return ERR_get_error ();
+ }
+ if (EVP_PKEY_CTX_set_signature_md (key_ctx, EVP_sha256 ()) <= 0) {
+ msg_err ("cannot init signature private key %s, %s", privkey,
+ ERR_error_string (ERR_get_error (), NULL));
+ return ERR_get_error ();
+ }
+
+ tests_num = g_strv_length (sign_configs);
+
+ for (i = 0; i < tests_num; i ++) {
+ cur_file = sign_configs[i];
+ if (realpath (cur_file, in_file) == NULL) {
+ msg_err ("cannot resolve %s: %s", cur_file, strerror (errno));
+ continue;
+ }
+ if (stat (in_file, &st) == -1) {
+ msg_err ("cannot stat %s: %s", in_file, strerror (errno));
+ continue;
+ }
+ if ((fd = open (in_file, O_RDONLY)) == -1) {
+ msg_err ("cannot open %s: %s", in_file, strerror (errno));
+ continue;
+ }
+
+ if ((map = mmap (NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0)) == MAP_FAILED) {
+ close (fd);
+ msg_err ("cannot mmap %s: %s", in_file, strerror (errno));
+ continue;
+ }
+
+ close (fd);
+ /* Now try to sign */
+ if (EVP_PKEY_sign (key_ctx, NULL, &siglen, map, st.st_size) <= 0) {
+ msg_err ("cannot sign %s using private key %s, %s", in_file, privkey,
+ ERR_error_string (ERR_get_error (), NULL));
+ munmap (map, st.st_size);
+ continue;
+ }
+
+ sig = OPENSSL_malloc (siglen);
+ if (EVP_PKEY_sign (key_ctx, sig, &siglen, map, st.st_size) <= 0) {
+ msg_err ("cannot sign %s using private key %s, %s", in_file, privkey,
+ ERR_error_string (ERR_get_error (), NULL));
+ munmap (map, st.st_size);
+ continue;
+ }
+
+ munmap (map, st.st_size);
+
+ rspamd_snprintf (out_file, sizeof (out_file), "%s.sig", in_file);
+ fd = open (out_file, O_WRONLY | O_CREAT | O_TRUNC, 00644);
+ if (fd == -1) {
+ msg_err ("cannot open output file %s: %s", out_file, strerror (errno));
+ continue;
+ }
+ if (write (fd, sig, siglen) == -1) {
+ msg_err ("cannot write to output file %s: %s", out_file, strerror (errno));
+ }
+ close (fd);
+ }
+
+ /* Cleanup */
+ EVP_PKEY_CTX_free (key_ctx);
+ EVP_PKEY_free (key);
+ BIO_free (fbio);
+
+ return res;
+#endif
+}
+
gint
main (gint argc, gchar **argv, gchar **env)
{
@@ -997,6 +1118,11 @@ main (gint argc, gchar **argv, gchar **env)
exit (perform_lua_tests (rspamd_main->cfg));
}
+ /* If we want to sign configs, just do it */
+ if (sign_configs != NULL && privkey != NULL) {
+ exit (perform_configs_sign ());
+ }
+
/* Load config */
if (! load_rspamd_config (rspamd_main->cfg, TRUE)) {
exit (EXIT_FAILURE);
diff --git a/src/util.c b/src/util.c
index ce95e6c9e..90497c803 100644
--- a/src/util.c
+++ b/src/util.c
@@ -2249,6 +2249,118 @@ parse_http_date (const gchar *header, gsize len)
return (time_t) time;
}
+static volatile sig_atomic_t saved_signo[NSIG];
+
+static
+void read_pass_tmp_sig_handler (int s)
+{
+
+ saved_signo[s] = 1;
+}
+
+#ifndef _PATH_TTY
+# define _PATH_TTY "/dev/tty"
+#endif
+
+gint
+rspamd_read_passphrase (gchar *buf, gint size, gint rwflag, gpointer key)
+{
+#ifdef HAVE_PASSPHRASE_H
+ gint len = 0;
+ gchar pass[BUFSIZ];
+
+ if (readpassphrase ("Enter passphrase: ", buf, size, RPP_ECHO_OFF | RPP_REQUIRE_TTY) == NULL) {
+ return 0;
+ }
+
+ return strlen (buf);
+#else
+ struct sigaction sa, savealrm, saveint, savehup, savequit, saveterm;
+ struct sigaction savetstp, savettin, savettou, savepipe;
+ struct termios term, oterm;
+ gint input, output, i;
+ gchar *end, *p, ch;
+
+restart:
+ if ((input = output = open (_PATH_TTY, O_RDWR | O_CLOEXEC)) == -1) {
+ errno = ENOTTY;
+ return 0;
+ }
+
+ /* Turn echo off */
+ if (tcgetattr (input, &oterm) != 0) {
+ errno = ENOTTY;
+ return 0;
+ }
+ memcpy(&term, &oterm, sizeof(term));
+ term.c_lflag &= ~(ECHO | ECHONL);
+ (void)tcsetattr(input, TCSAFLUSH, &term);
+ (void)write (output, "Enter passphrase: ", sizeof ("Enter passphrase: ") - 1);
+
+ /* Save the current sighandler */
+ for (i = 0; i < NSIG; i++) {
+ saved_signo[i] = 0;
+ }
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = 0;
+ sa.sa_handler = read_pass_tmp_sig_handler;
+ (void)sigaction (SIGALRM, &sa, &savealrm);
+ (void)sigaction (SIGHUP, &sa, &savehup);
+ (void)sigaction (SIGINT, &sa, &saveint);
+ (void)sigaction (SIGPIPE, &sa, &savepipe);
+ (void)sigaction (SIGQUIT, &sa, &savequit);
+ (void)sigaction (SIGTERM, &sa, &saveterm);
+ (void)sigaction (SIGTSTP, &sa, &savetstp);
+ (void)sigaction (SIGTTIN, &sa, &savettin);
+ (void)sigaction (SIGTTOU, &sa, &savettou);
+
+ /* Now read a passphrase */
+ p = buf;
+ end = p + size - 1;
+ while (read (input, &ch, 1) == 1 && ch != '\n' && ch != '\r') {
+ if (p < end) {
+ *p++ = ch;
+ }
+ }
+ *p = '\0';
+ (void)write (output, "\n", 1);
+
+ /* Restore terminal state */
+ if (memcmp (&term, &oterm, sizeof (term)) != 0) {
+ while (tcsetattr (input, TCSAFLUSH, &oterm) == -1 &&
+ errno == EINTR && !saved_signo[SIGTTOU]);
+ }
+
+ /* Restore signal handlers */
+ (void)sigaction (SIGALRM, &savealrm, NULL);
+ (void)sigaction (SIGHUP, &savehup, NULL);
+ (void)sigaction (SIGINT, &saveint, NULL);
+ (void)sigaction (SIGQUIT, &savequit, NULL);
+ (void)sigaction (SIGPIPE, &savepipe, NULL);
+ (void)sigaction (SIGTERM, &saveterm, NULL);
+ (void)sigaction (SIGTSTP, &savetstp, NULL);
+ (void)sigaction (SIGTTIN, &savettin, NULL);
+ (void)sigaction (SIGTTOU, &savettou, NULL);
+
+ close (input);
+
+ /* Send signals pending */
+ for (i = 0; i < NSIG; i++) {
+ if (saved_signo[i]) {
+ kill(getpid(), i);
+ switch (i) {
+ case SIGTSTP:
+ case SIGTTIN:
+ case SIGTTOU:
+ goto restart;
+ }
+ }
+ }
+
+ return p - buf;
+#endif
+}
+
/*
* vi:ts=4
*/
diff --git a/src/util.h b/src/util.h
index 5fa2eaff9..c95cc5448 100644
--- a/src/util.h
+++ b/src/util.h
@@ -419,4 +419,14 @@ gboolean parse_ipmask_v4 (const char *line, struct in_addr *ina, int *mask);
*/
time_t parse_http_date (const gchar *header, gsize len);
+/**
+ * Read passphrase from tty
+ * @param buf buffer to fill with a password
+ * @param size size of the buffer
+ * @param rwflag unused flag
+ * @param key unused key
+ * @return size of password read
+ */
+gint rspamd_read_passphrase (gchar *buf, gint size, gint rwflag, gpointer key);
+
#endif