]> source.dussan.org Git - rspamd.git/commitdiff
Add `pw` utility to manage rspamd passwords.
authorVsevolod Stakhov <vsevolod@highsecure.ru>
Thu, 24 Sep 2015 17:19:58 +0000 (18:19 +0100)
committerVsevolod Stakhov <vsevolod@highsecure.ru>
Thu, 24 Sep 2015 17:19:58 +0000 (18:19 +0100)
src/rspamadm/CMakeLists.txt
src/rspamadm/commands.c
src/rspamadm/pw.c [new file with mode: 0644]
src/rspamadm/rspamadm.c
src/rspamd.c
src/rspamd.h

index ba9814f8d6b61202963508a9831ee87c78c3c104..7518985a618da839d7fde2e29c553ab46445b82b 100644 (file)
@@ -1,4 +1,4 @@
-SET(RSPAMADMSRC rspamadm.c commands.c)
+SET(RSPAMADMSRC rspamadm.c commands.c pw.c)
 
 ADD_EXECUTABLE(rspamadm ${RSPAMADMSRC})
 TARGET_LINK_LIBRARIES(rspamadm rspamd-server)
index 26f6b02b80e069800eed720a3a20e9788d59d7d4..c8e61b6a7cb724a0522a482df34526d638f6de02 100644 (file)
  */
 #include "rspamadm.h"
 
+extern struct rspamadm_command pw_command;
+
 const struct rspamadm_command *commands[] = {
        &help_command,
+       &pw_command,
        NULL
 };
 
diff --git a/src/rspamadm/pw.c b/src/rspamadm/pw.c
new file mode 100644 (file)
index 0000000..c2a5ca6
--- /dev/null
@@ -0,0 +1,273 @@
+/*
+ * Copyright (c) 2015, Vsevolod Stakhov
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *      * Redistributions of source code must retain the above copyright
+ *        notice, this list of conditions and the following disclaimer.
+ *      * Redistributions in binary form must reproduce the above copyright
+ *        notice, this list of conditions and the following disclaimer in the
+ *        documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR ''AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "util.h"
+#include "ottery.h"
+#include "cryptobox.h"
+#include "rspamd.h"
+#include "rspamadm.h"
+
+static void rspamadm_pw (gint argc, gchar **argv);
+static const char *rspamadm_pw_help (gboolean full_help);
+
+static gboolean do_encrypt = FALSE;
+static gboolean do_check = FALSE;
+static gboolean quiet = FALSE;
+static gchar *password = NULL;
+
+struct rspamadm_command pw_command = {
+       .name = "pw",
+       .flags = 0,
+       .help = rspamadm_pw_help,
+       .run = rspamadm_pw
+};
+
+static GOptionEntry entries[] = {
+               {"encrypt", 'e', 0, G_OPTION_ARG_NONE, &do_encrypt,
+                               "Encrypt password", NULL},
+               {"check", 'c', 0, G_OPTION_ARG_NONE, &do_check,
+                               "Check password", NULL},
+               {"quiet", 'q', 0, G_OPTION_ARG_NONE, &quiet,
+                               "Supress output", NULL},
+               {"password", 'p', 0, G_OPTION_ARG_STRING, &password,
+                               "Input password", NULL},
+               {NULL, 0, 0, G_OPTION_ARG_NONE, NULL, NULL, NULL}
+};
+
+static const char *
+rspamadm_pw_help (gboolean full_help)
+{
+       const char *help_str;
+
+       if (full_help) {
+               help_str = "Manipulate with passwords in rspamd\n"
+                               "Usage: rspamadm pw [command]\n"
+                               "Where commands are:\n"
+                               "--encrypt: encrypt password (this is a default command)\n"
+                               "--check: check encrypted password using encrypted password\n"
+                               "--help: shows available options and commands";
+       }
+       else {
+               help_str = "Manage rspamd passwords";
+       }
+
+       return help_str;
+}
+
+static void
+rspamadm_pw_encrypt (void)
+{
+       const struct rspamd_controller_pbkdf *pbkdf;
+       guchar *salt, *key;
+       gchar *encoded_salt, *encoded_key;
+       gsize plen;
+
+       pbkdf = &pbkdf_list[0];
+       g_assert (pbkdf != NULL);
+
+       if (password == NULL) {
+               plen = 8192;
+               password = g_malloc0 (plen);
+               plen = rspamd_read_passphrase (password, plen, 0, NULL);
+       }
+       else {
+               plen = strlen (password);
+       }
+
+       if (plen == 0) {
+               fprintf (stderr, "Invalid password\n");
+               exit (EXIT_FAILURE);
+       }
+
+       salt = g_alloca (pbkdf->salt_len);
+       key = g_alloca (pbkdf->key_len);
+       ottery_rand_bytes (salt, pbkdf->salt_len);
+       /* Derive key */
+       rspamd_cryptobox_pbkdf (password, strlen (password),
+                       salt, pbkdf->salt_len, key, pbkdf->key_len, pbkdf->rounds);
+
+       encoded_salt = rspamd_encode_base32 (salt, pbkdf->salt_len);
+       encoded_key = rspamd_encode_base32 (key, pbkdf->key_len);
+
+       rspamd_printf ("$%d$%s$%s\n", pbkdf->id, encoded_salt,
+                       encoded_key);
+
+       g_free (encoded_salt);
+       g_free (encoded_key);
+       rspamd_explicit_memzero (password, plen);
+       g_free (password);
+}
+
+static const gchar *
+rspamd_encrypted_password_get_str (const gchar *password, gsize skip,
+               gsize *length)
+{
+       const gchar *str, *start, *end;
+       gsize size;
+
+       start = password + skip;
+       end = start;
+       size = 0;
+
+       while (*end != '\0' && g_ascii_isalnum (*end)) {
+               size++;
+               end++;
+       }
+
+       if (size) {
+               str = start;
+               *length = size;
+       }
+       else {
+               str = NULL;
+       }
+
+       return str;
+}
+
+static void
+rspamadm_pw_check (void)
+{
+       const struct rspamd_controller_pbkdf *pbkdf;
+       GIOChannel *in;
+       GString *encrypted_pwd;
+       const gchar *salt, *hash;
+       guchar *salt_decoded, *key_decoded, *local_key;
+       gsize salt_len, key_len;
+       gchar test_password[8192];
+       gsize plen, term = 0;
+
+       if (password == NULL) {
+               encrypted_pwd = g_string_new ("");
+               in = g_io_channel_unix_new (STDIN_FILENO);
+               printf ("Enter encrypted password: ");
+               fflush (stdout);
+               g_io_channel_read_line_string (in, encrypted_pwd, &term, NULL);
+
+               if (term != 0) {
+                       g_string_erase (encrypted_pwd, term, encrypted_pwd->len - term);
+               }
+               g_io_channel_unref (in);
+       }
+       else {
+               encrypted_pwd = g_string_new (password);
+       }
+
+       pbkdf = &pbkdf_list[0];
+       g_assert (pbkdf != NULL);
+
+       /* get salt */
+       salt = rspamd_encrypted_password_get_str (encrypted_pwd->str, 3, &salt_len);
+       /* get hash */
+       hash = rspamd_encrypted_password_get_str (encrypted_pwd->str,
+                       3 + salt_len + 1,
+                       &key_len);
+       if (salt != NULL && hash != NULL) {
+
+               /* decode salt */
+               salt_decoded = rspamd_decode_base32 (salt, salt_len, &salt_len);
+
+               if (salt_decoded == NULL || salt_len != pbkdf->salt_len) {
+                       /* We have some unknown salt here */
+                       msg_err ("incorrect salt: %z, while %z expected",
+                                       salt_len, pbkdf->salt_len);
+                       exit (EXIT_FAILURE);
+               }
+
+               key_decoded = rspamd_decode_base32 (hash, key_len, &key_len);
+
+               if (key_decoded == NULL || key_len != pbkdf->key_len) {
+                       /* We have some unknown salt here */
+                       msg_err ("incorrect key: %z, while %z expected",
+                                       key_len, pbkdf->key_len);
+                       exit (EXIT_FAILURE);
+               }
+
+               plen = rspamd_read_passphrase (test_password, sizeof (test_password),
+                               0, NULL);
+               if (plen == 0) {
+                       fprintf (stderr, "Invalid password\n");
+                       exit (EXIT_FAILURE);
+               }
+
+               local_key = g_alloca (pbkdf->key_len);
+               rspamd_cryptobox_pbkdf (test_password, plen,
+                               salt_decoded, salt_len,
+                               local_key, pbkdf->key_len, pbkdf->rounds);
+               rspamd_explicit_memzero (test_password, plen);
+
+               if (!rspamd_constant_memcmp (key_decoded, local_key, pbkdf->key_len)) {
+                       if (!quiet) {
+                               printf ("password incorrect\n");
+                       }
+                       exit (EXIT_FAILURE);
+               }
+
+               g_free (salt_decoded);
+               g_free (key_decoded);
+               g_string_free (encrypted_pwd, TRUE);
+       }
+       else {
+               msg_err ("bad encrypted password format");
+               exit (EXIT_FAILURE);
+       }
+
+       if (!quiet) {
+               printf ("password correct\n");
+       }
+}
+
+static void
+rspamadm_pw (gint argc, gchar **argv)
+{
+       GOptionContext *context;
+       GError *error = NULL;
+
+       context = g_option_context_new ("pw [--encrypt | --check] - manage rspamd passwords");
+       g_option_context_set_summary (context,
+                       "Summary:\n  Rspamd administration utility version "
+                                       RVERSION
+                                       "\n  Release id: "
+                                       RID);
+       g_option_context_add_main_entries (context, entries, NULL);
+
+       if (!g_option_context_parse (context, &argc, &argv, &error)) {
+               fprintf (stderr, "option parsing failed: %s\n", error->message);
+               g_error_free (error);
+               exit (1);
+       }
+
+
+       if (!do_encrypt && !do_check) {
+               do_encrypt = TRUE;
+       }
+
+       if (do_encrypt) {
+               rspamadm_pw_encrypt ();
+       }
+       else if (do_check) {
+               rspamadm_pw_check ();
+       }
+}
index 235eb0accbc8de2e65abe4b05d7685b49617cbab..9105b74b8d268857aa143f8bc30484057aa5561e 100644 (file)
@@ -282,7 +282,7 @@ main (gint argc, gchar **argv, gchar **env)
                        nargv[i] = g_strdup (argv[i + nargc]);
                }
 
-               targc = argc - nargc - 1;
+               targc = argc - nargc;
                targv = nargv;
                cmd->run (targc, targv);
                g_strfreev (nargv);
index bc13f6e473f2e4ce7490e95a26602c9eada0bdf1..e15a598a431c7a788db327ae62b6d1d2651172cc 100644 (file)
@@ -150,8 +150,6 @@ static GOptionEntry entries[] =
        { NULL, 0, 0, G_OPTION_ARG_NONE, NULL, NULL, NULL }
 };
 
-extern const struct rspamd_controller_pbkdf pbkdf_list[];
-
 #ifndef HAVE_SA_SIGINFO
 static void
 sig_handler (gint signo)
@@ -1018,7 +1016,7 @@ do_encrypt_password (void)
        const struct rspamd_controller_pbkdf *pbkdf;
        guchar *salt, *key;
        gchar *encoded_salt, *encoded_key;
-       gchar password[BUFSIZ];
+       gchar password[8192];
        gsize plen;
 
        pbkdf = &pbkdf_list[0];
index 5104040a9114f45277f9c59810ec8e70b6473cf1..f23798ddc7a7cc4bc13dd5f59827c54f4f7db898 100644 (file)
@@ -224,6 +224,9 @@ void register_custom_controller_command (const gchar *name,
        gboolean privilleged,
        gboolean require_message);
 
+#define RSPAMD_PBKDF_ID_V1 1
+extern const struct rspamd_controller_pbkdf pbkdf_list[];
+
 #endif
 
 /*