Browse Source

Add ability to sign configs using rspamd.

tags/0.6.0
Vsevolod Stakhov 10 years ago
parent
commit
fc9211cf8d
5 changed files with 266 additions and 0 deletions
  1. 3
    0
      CMakeLists.txt
  2. 15
    0
      config.h.in
  3. 126
    0
      src/main.c
  4. 112
    0
      src/util.c
  5. 10
    0
      src/util.h

+ 3
- 0
CMakeLists.txt View 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)

+ 15
- 0
config.h.in View File

@@ -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

+ 126
- 0
src/main.c View 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);

+ 112
- 0
src/util.c View 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
*/

+ 10
- 0
src/util.h View 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

Loading…
Cancel
Save