]> git.ipfire.org Git - thirdparty/rspamd.git/commitdiff
[Feature] Finish all features of dkim_keygen in Lua
authorVsevolod Stakhov <vsevolod@rspamd.com>
Fri, 7 Apr 2023 15:38:50 +0000 (16:38 +0100)
committerVsevolod Stakhov <vsevolod@rspamd.com>
Fri, 7 Apr 2023 15:39:22 +0000 (16:39 +0100)
lualib/rspamadm/dkim_keygen.lua
src/lua/lua_cryptobox.c
src/lua/lua_text.c
src/rspamadm/dkim_keygen.c [deleted file]

index a957d8fde108b103014659496a55d591fb700945..05be734366dfe830c9e67c9bcb730155c2120bb5 100644 (file)
@@ -16,6 +16,7 @@ limitations under the License.
 
 local argparse = require "argparse"
 local rspamd_util = require "rspamd_util"
+local rspamd_cryptobox = require "rspamd_cryptobox"
 
 local parser = argparse()
     :name 'rspamadm dkim_keygen'
@@ -64,6 +65,8 @@ parser:option '--priv-output'
         ['der'] = 'der',
       }
       :default 'pem'
+parser:flag '-f --force'
+      :description 'Force overwrite of existing files'
 
 local function split_string(input, max_length)
   max_length = max_length or 253
@@ -114,6 +117,9 @@ local function gen_rsa_key(opts)
 
   local sk,pk = rsa.keypair(opts.bits or 1024)
   if opts.privkey then
+    if opts.force then
+      os.remove(opts.privkey)
+    end
     sk:save(opts.privkey, opts.priv_output)
   else
     sk:save("-", opts.priv_output)
@@ -122,6 +128,25 @@ local function gen_rsa_key(opts)
   print_public_key(opts, tostring(pk))
 end
 
+local function gen_eddsa_key(opts)
+  local sk,pk = rspamd_cryptobox.gen_dkim_keypair(opts.type)
+
+  if opts.privkey and opts.force then
+    os.remove(opts.privkey)
+  end
+  if not sk:save_in_file(opts.privkey, tonumber('0600', 8)) then
+    io.stderr:write('cannot save private key to ' .. (opts.privkey or 'stdout') .. '\n')
+    os.exit(1)
+  end
+
+  if not opts.privkey then
+    io.write("\n")
+    io.flush()
+  end
+
+  print_public_key(opts, tostring(pk))
+end
+
 local function handler(args)
   local opts = parser:parse(args)
 
index e342610ce49e4788605140bb6d311173db19e21b..2a7b0c2026c965a79f8c060bc25a96f25f5cd7ad 100644 (file)
@@ -2700,6 +2700,39 @@ lua_cryptobox_gen_dkim_keypair (lua_State *L)
                rspamd_explicit_memzero (pk, sizeof (pk));
                rspamd_explicit_memzero (sk, sizeof (sk));
        }
+       else if (strcmp (alg_str, "ed25519-seed") == 0) {
+               rspamd_sig_pk_t pk;
+               rspamd_sig_sk_t sk;
+               gchar *b64_data;
+               gsize b64_len;
+
+               rspamd_cryptobox_keypair_sig (pk, sk, RSPAMD_CRYPTOBOX_MODE_25519);
+
+               /* Process private key */
+               b64_data = rspamd_encode_base64 (sk,
+                       32,
+                       -1, &b64_len);
+
+               priv_out = lua_newuserdata (L, sizeof (*priv_out));
+               rspamd_lua_setclass (L, "rspamd{text}", -1);
+               priv_out->start = b64_data;
+               priv_out->len = b64_len;
+               priv_out->flags = RSPAMD_TEXT_FLAG_OWN|RSPAMD_TEXT_FLAG_WIPE;
+
+               /* Process public key */
+               b64_data = rspamd_encode_base64 (pk,
+                       rspamd_cryptobox_pk_sig_bytes (RSPAMD_CRYPTOBOX_MODE_25519),
+                       -1, &b64_len);
+
+               pub_out = lua_newuserdata (L, sizeof (*pub_out));
+               rspamd_lua_setclass (L, "rspamd{text}", -1);
+               pub_out->start = b64_data;
+               pub_out->len = b64_len;
+               pub_out->flags = RSPAMD_TEXT_FLAG_OWN;
+
+               rspamd_explicit_memzero (pk, sizeof (pk));
+               rspamd_explicit_memzero (sk, sizeof (sk));
+       }
        else {
                return luaL_error (L, "invalid algorithm %s", alg_str);
        }
index b6f0948c3879f7d75bdfc06e3cd910bf0446fdba..bafab3c0836f8506f52c2bba70947a75e9994f43 100644 (file)
@@ -1118,12 +1118,12 @@ lua_text_save_in_file (lua_State *L)
                        fname = luaL_checkstring (L, 2);
 
                        if (lua_type (L, 3) == LUA_TNUMBER) {
-                               mode = lua_tonumber (L, 3);
+                               mode = lua_tointeger(L, 3);
                        }
                }
                else if (lua_type (L, 2) == LUA_TNUMBER) {
                        /* Created fd */
-                       fd = lua_tonumber (L, 2);
+                       fd = lua_tointeger (L, 2);
                }
 
                if (fd == -1) {
diff --git a/src/rspamadm/dkim_keygen.c b/src/rspamadm/dkim_keygen.c
deleted file mode 100644 (file)
index 1bbb717..0000000
+++ /dev/null
@@ -1,385 +0,0 @@
-/*-
- * Copyright 2016 Vsevolod Stakhov
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#include "config.h"
-#include "rspamadm.h"
-#include "printf.h"
-#include "str_util.h"
-#include "libcryptobox/cryptobox.h"
-#include "contrib/libottery/ottery.h"
-#include "lua/lua_common.h"
-#include "unix-std.h"
-
-#include <openssl/rsa.h>
-#include <openssl/bn.h>
-#include <openssl/pem.h>
-
-static gchar *privkey_file = NULL;
-static gchar *selector = NULL;
-static gchar *domain = NULL;
-static guint bits = 1024;
-static gchar *type = "rsa";
-
-static void rspamadm_dkim_keygen (gint argc, gchar **argv,
-                                                                 const struct rspamadm_command *cmd);
-static const char *rspamadm_dkim_keygen_help (gboolean full_help,
-                                                                                         const struct rspamadm_command *cmd);
-static void rspamadm_dkim_keygen_lua_subrs (gpointer pL);
-
-struct rspamadm_command dkim_keygen_command = {
-               .name = "dkim_keygen",
-               .flags = 0,
-               .help = rspamadm_dkim_keygen_help,
-               .run = rspamadm_dkim_keygen,
-               .lua_subrs = rspamadm_dkim_keygen_lua_subrs,
-};
-
-static GOptionEntry entries[] = {
-               {"domain",  'd', 0, G_OPTION_ARG_STRING, &domain,
-                               "Use the specified domain", NULL},
-               {"selector",  's', 0, G_OPTION_ARG_STRING, &selector,
-                               "Use the specified selector", NULL},
-               {"privkey",  'k', 0, G_OPTION_ARG_STRING, &privkey_file,
-                               "Save private key in the specified file", NULL},
-               {"bits",  'b', 0, G_OPTION_ARG_INT, &bits,
-                               "Set key length to N bits (1024 by default)", NULL},
-               {"type",  't', 0, G_OPTION_ARG_STRING, &type,
-                               "Key type: rsa or ed25519 (rsa by default)", NULL},
-               {NULL,       0,   0, G_OPTION_ARG_NONE, NULL, NULL, NULL}
-};
-
-static const char *
-rspamadm_dkim_keygen_help (gboolean full_help, const struct rspamadm_command *cmd)
-{
-       const char *help_str;
-
-       if (full_help) {
-               help_str = "Create key pairs for dkim signing\n\n"
-                               "Usage: rspamadm dkim_keygen -s selector -d domain [-k privkey] [-b bits]\n"
-                               "Where options are:\n\n"
-                               "-d: use the specified domain\n"
-                               "-s: use the specified selector\n"
-                               "-k: save private key to file instead of printing it to stdout\n"
-                               "-b: set number of bits instead of 1024\n"
-                               "--help: shows available options and commands";
-       }
-       else {
-               help_str = "Create dkim key pairs";
-       }
-
-       return help_str;
-}
-
-static void
-rspamd_dkim_generate_rsa_keypair (const gchar *domain, const gchar *selector,
-                                                                 const gchar *priv_fname, const gchar *pub_fname,
-                                                                 guint keylen)
-{
-       BIGNUM *e;
-       RSA *r;
-       BIO *pubout, *privout;
-       EVP_PKEY *pk;
-       gint rc;
-       glong publen;
-       gsize b64_len;
-       gchar *pubdata, *b64_data;
-       FILE *pubfile = NULL;
-
-       if (bits > 4096 || bits < 512) {
-               fprintf (stderr, "Bits number must be in the interval 512...4096\n");
-               exit (EXIT_FAILURE);
-       }
-
-       e = BN_new ();
-       r = RSA_new ();
-       pk = EVP_PKEY_new ();
-       g_assert (BN_set_word (e, RSA_F4) == 1);
-       g_assert (RSA_generate_key_ex (r, bits, e, NULL) == 1);
-       g_assert (EVP_PKEY_set1_RSA (pk, r) == 1);
-
-       if (priv_fname) {
-               int fd = open (priv_fname, O_WRONLY | O_CREAT | O_TRUNC, 0640);
-
-               if (fd < 0) {
-                       rspamd_fprintf (stderr, "cannot open output file %s: %s\n",
-                                       priv_fname, strerror (errno));
-                       exit (EXIT_FAILURE);
-               }
-
-               FILE *fp = fdopen (fd, "w");
-
-               if (fp == NULL) {
-                       close (fd);
-                       rspamd_fprintf (stderr, "cannot open output file %s: %s\n",
-                                       priv_fname, strerror (errno));
-                       exit (EXIT_FAILURE);
-               }
-
-               privout = BIO_new_fp (fp, BIO_CLOSE);
-
-               if (privout == NULL) {
-                       fclose (fp);
-                       rspamd_fprintf (stderr, "cannot open output file %s: %s\n",
-                                       priv_fname, strerror (errno));
-                       exit (EXIT_FAILURE);
-               }
-       } else {
-               privout = BIO_new_fp (stdout, BIO_NOCLOSE);
-       }
-
-       rc = PEM_write_bio_PrivateKey (privout, pk, NULL, NULL, 0, NULL, NULL);
-
-       if (rc != 1) {
-               rspamd_fprintf (stderr, "cannot write key to the output file %s: %s\n",
-                               priv_fname ? priv_fname : "stdout", strerror (errno));
-               exit (EXIT_FAILURE);
-       }
-
-       BIO_free (privout);
-       fflush (stdout);
-
-       pubout = BIO_new (BIO_s_mem ());
-
-       rc = i2d_RSA_PUBKEY_bio (pubout, r);
-       publen = BIO_get_mem_data (pubout, &pubdata);
-
-       g_assert (publen > 0);
-       b64_data = rspamd_encode_base64 (pubdata, publen, -1, &b64_len);
-
-       if (pub_fname) {
-               pubfile = fopen (pub_fname, "w");
-
-               if (pubfile == NULL) {
-                       rspamd_fprintf (stderr, "cannot open output file %s: %s\n",
-                                       pub_fname, strerror (errno));
-                       exit (EXIT_FAILURE);
-               }
-       } else {
-               pubfile = stdout;
-       }
-
-       if (b64_len < 255 - 2) {
-               rspamd_fprintf (pubfile, "%s._domainkey IN TXT ( \"v=DKIM1; k=rsa; \"\n"
-                                                                "\t\"p=%s\" ) ;\n",
-                               selector ? selector : "selector",
-                               b64_data);
-       } else {
-               guint i;
-               gint step = 253, remain = b64_len;
-
-               rspamd_fprintf (pubfile, "%s._domainkey IN TXT ( \"v=DKIM1; k=rsa; \"\n",
-                               selector ? selector : "selector");
-
-               for (i = 0; i < b64_len; i += step, remain -= step) {
-                       if (i == 0) {
-                               rspamd_fprintf (pubfile, "\t\"p=%*s\"\n", MIN(step, remain), &b64_data[i]);
-                       } else {
-                               step = 255;
-                               rspamd_fprintf (pubfile, "\t\"%*s\"\n", MIN(step, remain), &b64_data[i]);
-                       }
-               }
-
-               rspamd_fprintf (pubfile, ") ; \n");
-       }
-
-       if (pubfile != stdout) {
-               fclose (pubfile);
-       }
-
-       g_free (b64_data);
-       BIO_free (pubout);
-       EVP_PKEY_free (pk);
-       RSA_free (r);
-       BN_free (e);
-}
-
-static void
-rspamd_dkim_generate_ed25519_keypair (const gchar *domain, const gchar *selector,
-                                                                 const gchar *priv_fname, const gchar *pub_fname,
-                                                                 guint keylen, gboolean seeded)
-{
-       rspamd_sig_sk_t ed_sk;
-       rspamd_sig_pk_t ed_pk;
-       gchar *base64_pk, *base64_sk;
-       FILE *pubfile = NULL, *privfile = NULL;
-
-       rspamd_cryptobox_keypair_sig (ed_pk, ed_sk, RSPAMD_CRYPTOBOX_MODE_25519);
-       if (seeded) {
-               /* Just encode seed, not the full sk */
-               base64_sk = rspamd_encode_base64_common (ed_sk, 32, 0, NULL, FALSE,
-                               RSPAMD_TASK_NEWLINES_LF);
-       }
-       else {
-               base64_sk = rspamd_encode_base64_common (ed_sk,
-                               rspamd_cryptobox_sk_sig_bytes (RSPAMD_CRYPTOBOX_MODE_25519),
-                               0, NULL, FALSE,
-                               RSPAMD_TASK_NEWLINES_LF);
-       }
-       base64_pk = rspamd_encode_base64_common (ed_pk, sizeof (ed_pk), 0, NULL, FALSE,
-                       RSPAMD_TASK_NEWLINES_LF);
-
-       /* Cleanup sensitive data */
-       rspamd_explicit_memzero (ed_sk, sizeof (ed_sk));
-
-       if (priv_fname) {
-               privfile = fopen (priv_fname, "w");
-
-               if (privfile == NULL) {
-                       rspamd_fprintf (stderr, "cannot open output file %s: %s\n",
-                                       priv_fname, strerror (errno));
-                       rspamd_explicit_memzero (base64_sk, strlen (base64_sk));
-                       g_free (base64_sk);
-                       g_free (base64_pk);
-                       exit (EXIT_FAILURE);
-               }
-       }
-       else {
-               privfile = stdout;
-       }
-
-       if (rspamd_fprintf (privfile, "%s\n", base64_sk) == -1) {
-               rspamd_fprintf (stderr, "cannot write to output file %s: %s\n",
-                               priv_fname, strerror (errno));
-               rspamd_explicit_memzero (base64_sk, strlen (base64_sk));
-               g_free (base64_sk);
-               g_free (base64_pk);
-
-               if (privfile != stdout) {
-                       fclose (privfile);
-               }
-
-               exit (EXIT_FAILURE);
-       }
-
-       if (privfile != stdout) {
-               fclose (privfile);
-       }
-
-       if (pub_fname) {
-               pubfile = fopen (pub_fname, "w");
-
-               if (pubfile == NULL) {
-                       rspamd_fprintf (stderr, "cannot open output file %s: %s\n",
-                                       pub_fname, strerror (errno));
-                       rspamd_explicit_memzero (base64_sk, strlen (base64_sk));
-                       g_free (base64_sk);
-                       g_free (base64_pk);
-                       exit (EXIT_FAILURE);
-               }
-       }
-       else {
-               pubfile = stdout;
-       }
-
-       rspamd_fprintf (pubfile, "%s._domainkey IN TXT ( \"v=DKIM1; k=ed25519; \"\n"
-                                                        "\t\"p=%s\" ) ;\n",
-                       selector ? selector : "selector",
-                       base64_pk);
-
-       if (pubfile != stdout) {
-               fclose (pubfile);
-       }
-
-       rspamd_explicit_memzero (base64_sk, strlen (base64_sk));
-       g_free (base64_sk);
-       g_free (base64_pk);
-}
-
-static void
-rspamadm_dkim_generate_keypair (const gchar *domain, const gchar *selector,
-               const gchar *priv_fname, const gchar *pub_fname, guint keylen)
-{
-       if (strcmp (type, "rsa") == 0) {
-               rspamd_dkim_generate_rsa_keypair (domain, selector, priv_fname,
-                               pub_fname, keylen);
-       }
-       else if (strcmp (type, "ed25519") == 0) {
-               rspamd_dkim_generate_ed25519_keypair (domain, selector, priv_fname,
-                               pub_fname, keylen, FALSE);
-       }
-       else if (strcmp (type, "ed25519-seed") == 0) {
-               rspamd_dkim_generate_ed25519_keypair (domain, selector, priv_fname,
-                               pub_fname, keylen, TRUE);
-       }
-       else {
-               fprintf (stderr, "invalid key type: %s\n", type);
-               exit (EXIT_FAILURE);
-       }
-}
-
-static gint
-rspamadm_dkim_keygen_lua_generate (lua_State *L)
-{
-       const gchar *domain = luaL_checkstring (L, 1);
-       const gchar *selector = luaL_checkstring (L, 2);
-       const gchar *privfile = NULL, *pubfile = NULL;
-       guint key_bits = 1024;
-
-       if (domain == NULL || selector == NULL) {
-               return luaL_error (L, "invalid arguments");
-       }
-
-       if (lua_type (L, 3) == LUA_TSTRING) {
-               privfile = lua_tostring (L, 3);
-       }
-
-       if (lua_type (L, 4) == LUA_TSTRING) {
-               pubfile = lua_tostring (L, 4);
-       }
-
-       if (lua_type (L, 5) == LUA_TNUMBER) {
-               key_bits = lua_tonumber (L, 5);
-       }
-
-       rspamadm_dkim_generate_keypair (domain, selector, privfile, pubfile, key_bits);
-
-       return 0;
-}
-
-static void
-rspamadm_dkim_keygen_lua_subrs (gpointer pL)
-{
-       lua_State *L = pL;
-
-       lua_pushstring (L, "dkim_keygen");
-       lua_pushcfunction (L, rspamadm_dkim_keygen_lua_generate);
-       lua_settable (L, -3);
-}
-
-static void
-rspamadm_dkim_keygen (gint argc, gchar **argv, const struct rspamadm_command *cmd)
-{
-       GOptionContext *context;
-       GError *error = NULL;
-
-       context = g_option_context_new (
-                       "dkim_keygen - create dkim keys");
-       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);
-               g_option_context_free (context);
-               exit (EXIT_FAILURE);
-       }
-
-       g_option_context_free (context);
-       rspamadm_dkim_generate_keypair (domain, selector, privkey_file, NULL, bits);
-}