]> git.ipfire.org Git - thirdparty/rspamd.git/commitdiff
Add ability to sign configs using rspamd.
authorVsevolod Stakhov <vsevolod@highsecure.ru>
Thu, 22 Aug 2013 13:57:03 +0000 (14:57 +0100)
committerVsevolod Stakhov <vsevolod@highsecure.ru>
Thu, 22 Aug 2013 13:57:03 +0000 (14:57 +0100)
CMakeLists.txt
config.h.in
src/main.c
src/util.c
src/util.h

index a14e44383b6183f8455d688e3d2bf18b4735f827..f0a6954e2b2911ef028d128e5e3ef5670ef7c92f 100644 (file)
@@ -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)
index 31a0dd2d63c9e75c551844ab66bd40f8e1ee3a36..dd1af0f550235a0d320f0239bfa6e946d622ed4f 100644 (file)
 #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
 #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
index 6076857207f1b6d714e85084fe79f9c1496cf444..105a7002b0e26b0caf7a556ef9a794c52b1410e9 100644 (file)
@@ -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);
index ce95e6c9ee9ae415f20e0403ac9084d75095391f..90497c803f8c8794fd0163bcba18fda9f6a21489 100644 (file)
@@ -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
  */
index 5fa2eaff955e7867417e96a8e25c1c3f8d92e391..c95cc54482c3177aa5a325b2ac9d8ac9242ca31d 100644 (file)
@@ -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