diff options
author | Vsevolod Stakhov <vsevolod@highsecure.ru> | 2013-08-22 14:57:03 +0100 |
---|---|---|
committer | Vsevolod Stakhov <vsevolod@highsecure.ru> | 2013-08-22 14:57:03 +0100 |
commit | fc9211cf8dea975503e28d1cba6a625fe9464e81 (patch) | |
tree | f53976bbc904fd16a7a4311e110d27d0d8806c95 | |
parent | 05d349875b7b475359c779997464c2589979e545 (diff) | |
download | rspamd-fc9211cf8dea975503e28d1cba6a625fe9464e81.tar.gz rspamd-fc9211cf8dea975503e28d1cba6a625fe9464e81.zip |
Add ability to sign configs using rspamd.
-rw-r--r-- | CMakeLists.txt | 3 | ||||
-rw-r--r-- | config.h.in | 15 | ||||
-rw-r--r-- | src/main.c | 126 | ||||
-rw-r--r-- | src/util.c | 112 | ||||
-rw-r--r-- | src/util.h | 10 |
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 |