]> code.citadel.org Git - citadel.git/commitdiff
split dkim into signing and binding modules
authorArt Cancro <ajc@citadel.org>
Wed, 15 May 2024 15:22:45 +0000 (15:22 +0000)
committerArt Cancro <ajc@citadel.org>
Wed, 15 May 2024 15:22:45 +0000 (15:22 +0000)
This allows citserver and dkimtester to be built cleanly

citadel/server/modules/smtp/dkim.c
citadel/server/modules/smtp/dkim_bindings.c [new file with mode: 0644]
citadel/tests/dkimtester/Makefile

index b8835d36f931282e65a99330a052f1cb32fcf596..f2a5d9d25aa8cdd0d2d9c6b4bec4d2751c90e0cb 100644 (file)
@@ -29,7 +29,6 @@
 #include <openssl/err.h>
 #include <openssl/evp.h>
 #include <libcitadel.h>
-#include "../../config.h"
 
 // This utility function is used by the body canonicalizer
 char *dkim_rtrim(char *str) {
@@ -579,145 +578,3 @@ void dkim_sign(StrBuf *email, char *pkey_in, char *domain, char *selector) {
 }
 
 
-#ifndef DKIM_VERIFY_SIGNATURE
-// Generate a private key and selector for DKIM if needed.  This is called during server startup.
-void dkim_init(void) {
-
-       char *dkim_private_key = CtdlGetConfigStr("dkim_private_key");
-       if (!IsEmptyStr(dkim_private_key)) {
-               syslog(LOG_DEBUG, "dkim: private key exists and will continue to be used.");
-       }
-       else {
-               EVP_PKEY_CTX *ctx;
-               EVP_PKEY *pkey = NULL;  
-               BIO *bio = NULL;
-               ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, NULL);
-               if (ctx) {
-                       if (
-                               (EVP_PKEY_keygen_init(ctx) == 1)
-                               && (EVP_PKEY_CTX_set_rsa_keygen_bits(ctx, 2048) == 1)
-                               && (EVP_PKEY_keygen(ctx, &pkey) == 1)
-                       ) {
-                               syslog(LOG_DEBUG, "dkim: generated private key");
-                               bio = BIO_new(BIO_s_mem());
-                               if (bio) {
-                                       PEM_write_bio_PrivateKey(bio, pkey, NULL, NULL, 0, NULL, NULL);
-                                       char *b64key = malloc(4096);
-                                       if (b64key) {
-                                               size_t readbytes;
-                                               BIO_read_ex(bio, b64key, 4096, &readbytes);
-                                               b64key[readbytes] = 0;
-                                               char *nl = NULL;
-                                               while (nl=strchr(b64key, '\n'), nl) {           // convert newlines to underscores
-                                                       *nl = '_';
-                                               }
-                                               CtdlSetConfigStr("dkim_private_key", b64key);
-                                               free(b64key);
-                                       }
-                                       free(bio);
-                               }
-                       }
-                       EVP_PKEY_CTX_free(ctx);
-               }
-       }
-
-       char *dkim_selector = CtdlGetConfigStr("dkim_selector");
-       if (dkim_selector) {
-               syslog(LOG_DEBUG, "dkim: selector exists: %s", dkim_selector);
-       }
-       else {
-               // Quick and dirty algorithm to make up a five letter nonsense word as a selector
-               char new_selector[6];
-               int i;
-               for (i=0; i<5; ++i) {
-                       new_selector[i] = (rand() % 26) + 'a';
-               }
-               new_selector[5] = 0;
-               syslog(LOG_DEBUG, "dkim: selector created: %s", new_selector);
-               CtdlSetConfigStr("dkim_selector", new_selector);
-       }
-}
-
-
-// If the DKIM key, DKIM selector, or set of signing domains has changed, we need to tell the administrator about it.
-void dkim_check_advisory(char *inetcfg_in) {
-
-       // If there is no DKIM ... there is nothing to discuss
-       if (IsEmptyStr(CtdlGetConfigStr("dkim_private_key"))) return;
-       if (IsEmptyStr(CtdlGetConfigStr("dkim_selector"))) return;
-
-       // We're going to build a hash of the private key, the selector, and all signing domains.
-       // The way we build it doesn't matter, and it doesn't even have to be secure.
-       // This is just to let us know that we have to post an update to the administrator if the hash changes.
-
-       StrBuf *hashsrc = NewStrBuf();
-       if (!hashsrc) {
-               return;
-       }
-
-       StrBufAppendBufPlain(hashsrc, CtdlGetConfigStr("dkim_private_key"), strlen(CtdlGetConfigStr("dkim_private_key")), 0);
-       StrBufAppendBufPlain(hashsrc, CtdlGetConfigStr("dkim_selector"), strlen(CtdlGetConfigStr("dkim_selector")), 0);
-
-       char *ptr = inetcfg_in;
-       while (ptr && *ptr) {
-               char *sep = strchr(ptr, '|');
-               if (sep && !strncasecmp(sep+1, HKEY("localhost"))) {
-                       StrBufAppendBufPlain(hashsrc, ptr, sep-ptr, 0);
-               }
-               ptr = strchr(ptr, '\n');
-               if (ptr) ++ptr;
-       }
-
-       // make a hash from the string...
-       unsigned char *config_hash = malloc(SHA256_DIGEST_LENGTH);
-       SHA256((unsigned char *)ChrPtr(hashsrc), StrLength(hashsrc), config_hash);
-       FreeStrBuf(&hashsrc);
-
-       // base64 encode it...
-       char *encoded_config_hash = malloc(SHA256_DIGEST_LENGTH * 2);
-       CtdlEncodeBase64(encoded_config_hash, config_hash, SHA256_DIGEST_LENGTH, BASE64_NO_LINEBREAKS);
-       free(config_hash);                                                      // all we need now is the encoded hash
-
-       // Does it match the saved hash?
-       if (    (IsEmptyStr(CtdlGetConfigStr("dkim_config_hash")))
-               || (strcmp(encoded_config_hash, CtdlGetConfigStr("dkim_config_hash")))
-       ) {
-               // No?  Post an Aide notification.
-               StrBuf *message = NewStrBuf();
-               StrBufAppendPrintf(message, "%s",
-                       " \n"
-                       " Your domain configuration may have changed.\n"
-                       " To allow the DKIM signatures of outbound mail to be verified, "
-                       "please ensure that the following DNS records are created:\n"
-                       " \n"
-               );
-
-               ptr = inetcfg_in;
-               while (ptr && *ptr) {
-                       char *sep = strchr(ptr, '|');
-                       if (sep && !strncasecmp(sep+1, HKEY("localhost"))) {
-                               StrBufAppendPrintf(message, " Host name  : %s._domainkey.", CtdlGetConfigStr("dkim_selector"));
-                               StrBufAppendBufPlain(message, ptr, sep-ptr, 0);
-                               StrBufAppendBufPlain(message, HKEY("\r\n"), 0);
-                               StrBufAppendPrintf(message, " Record type: TXT\n");
-                               StrBufAppendBufPlain(message, HKEY(" Value      : v=DKIM1;k=rsa;p="), 0);
-
-                               // figure out the public key and get it
-
-                               StrBufAppendPrintf(message, "\n \n");
-                       }
-                       ptr = strchr(ptr, '\n');
-                       if (ptr) ++ptr;
-               }
-
-#if 0
-               CtdlAideMessage(ChrPtr(message), "DKIM records");
-#endif
-               FreeStrBuf(&message);
-       }
-
-       // Save it to the config database so we don't do this except when it changes.
-       CtdlSetConfigStr("dkim_config_hash", encoded_config_hash);
-       free(encoded_config_hash);
-}
-#endif
diff --git a/citadel/server/modules/smtp/dkim_bindings.c b/citadel/server/modules/smtp/dkim_bindings.c
new file mode 100644 (file)
index 0000000..e762ecf
--- /dev/null
@@ -0,0 +1,178 @@
+// DKIM bindings to Citadel Server
+//
+// (C) 2024 by Art Cancro
+//
+// This program is open source software.  Use, duplication, or disclosure is subject to the GNU General Public License v3.
+
+// Make sure we don't accidentally use any deprecated API calls
+#define OPENSSL_NO_DEPRECATED_3_0
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <time.h>
+#include <assert.h>
+#include <syslog.h>
+#include <openssl/rand.h>
+#include <openssl/rsa.h>
+#include <openssl/engine.h>
+#include <openssl/sha.h>
+#include <openssl/hmac.h>
+#include <openssl/evp.h>
+#include <openssl/bio.h>
+#include <openssl/pem.h>
+#include <openssl/buffer.h>
+#include <openssl/err.h>
+#include <openssl/evp.h>
+#include <libcitadel.h>
+#include "../../config.h"
+#include "../../msgbase.h"
+
+
+// Generate a private key and selector for DKIM if needed.  This is called during server startup.
+void dkim_init(void) {
+
+       char *dkim_private_key = CtdlGetConfigStr("dkim_private_key");
+       if (!IsEmptyStr(dkim_private_key)) {
+               syslog(LOG_DEBUG, "dkim: private key exists and will continue to be used.");
+       }
+       else {
+               EVP_PKEY_CTX *ctx;
+               EVP_PKEY *pkey = NULL;  
+               BIO *bio = NULL;
+               ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, NULL);
+               if (ctx) {
+                       if (
+                               (EVP_PKEY_keygen_init(ctx) == 1)
+                               && (EVP_PKEY_CTX_set_rsa_keygen_bits(ctx, 2048) == 1)
+                               && (EVP_PKEY_keygen(ctx, &pkey) == 1)
+                       ) {
+                               syslog(LOG_DEBUG, "dkim: generated private key");
+                               bio = BIO_new(BIO_s_mem());
+                               if (bio) {
+                                       PEM_write_bio_PrivateKey(bio, pkey, NULL, NULL, 0, NULL, NULL);
+                                       char *b64key = malloc(4096);
+                                       if (b64key) {
+                                               size_t readbytes;
+                                               BIO_read_ex(bio, b64key, 4096, &readbytes);
+                                               b64key[readbytes] = 0;
+                                               char *nl = NULL;
+                                               while (nl=strchr(b64key, '\n'), nl) {           // convert newlines to underscores
+                                                       *nl = '_';
+                                               }
+                                               CtdlSetConfigStr("dkim_private_key", b64key);
+                                               free(b64key);
+                                       }
+                                       free(bio);
+                               }
+                       }
+                       EVP_PKEY_CTX_free(ctx);
+               }
+       }
+
+       char *dkim_selector = CtdlGetConfigStr("dkim_selector");
+       if (dkim_selector) {
+               syslog(LOG_DEBUG, "dkim: selector exists: %s", dkim_selector);
+       }
+       else {
+               // Quick and dirty algorithm to make up a five letter nonsense word as a selector
+               char new_selector[6];
+               int i;
+               for (i=0; i<5; ++i) {
+                       new_selector[i] = (rand() % 26) + 'a';
+               }
+               new_selector[5] = 0;
+               syslog(LOG_DEBUG, "dkim: selector created: %s", new_selector);
+               CtdlSetConfigStr("dkim_selector", new_selector);
+       }
+}
+
+
+// If the DKIM key, DKIM selector, or set of signing domains has changed, we need to tell the administrator about it.
+void dkim_check_advisory(char *inetcfg_in) {
+
+       // If there is no DKIM ... there is nothing to discuss
+       if (IsEmptyStr(CtdlGetConfigStr("dkim_private_key"))) return;
+       if (IsEmptyStr(CtdlGetConfigStr("dkim_selector"))) return;
+
+       // We're going to build a hash of the private key, the selector, and all signing domains.
+       // The way we build it doesn't matter, and it doesn't even have to be secure.
+       // This is just to let us know that we have to post an update to the administrator if the hash changes.
+
+       StrBuf *hashsrc = NewStrBuf();
+       if (!hashsrc) {
+               return;
+       }
+
+       StrBufAppendBufPlain(hashsrc, CtdlGetConfigStr("dkim_private_key"), strlen(CtdlGetConfigStr("dkim_private_key")), 0);
+       StrBufAppendBufPlain(hashsrc, CtdlGetConfigStr("dkim_selector"), strlen(CtdlGetConfigStr("dkim_selector")), 0);
+
+       char *ptr = inetcfg_in;
+       while (ptr && *ptr) {
+               char *sep = strchr(ptr, '|');
+               if (sep && !strncasecmp(sep+1, HKEY("localhost"))) {
+                       StrBufAppendBufPlain(hashsrc, ptr, sep-ptr, 0);
+               }
+               ptr = strchr(ptr, '\n');
+               if (ptr) ++ptr;
+       }
+
+       // make a hash from the string...
+       unsigned char *config_hash = malloc(SHA256_DIGEST_LENGTH);
+       SHA256((unsigned char *)ChrPtr(hashsrc), StrLength(hashsrc), config_hash);
+       FreeStrBuf(&hashsrc);
+
+       // base64 encode it...
+       char *encoded_config_hash = malloc(SHA256_DIGEST_LENGTH * 2);
+       CtdlEncodeBase64(encoded_config_hash, config_hash, SHA256_DIGEST_LENGTH, BASE64_NO_LINEBREAKS);
+       free(config_hash);                                                      // all we need now is the encoded hash
+
+       // Does it match the saved hash?
+       if (    (IsEmptyStr(CtdlGetConfigStr("dkim_config_hash")))
+               || (strcmp(encoded_config_hash, CtdlGetConfigStr("dkim_config_hash")))
+       ) {
+               // No?  Post an Aide notification.
+               StrBuf *message = NewStrBuf();
+               StrBufAppendBufPlain(message, HKEY(
+                       "Content-type: text/plain\r\n"
+                       "\r\n"
+                       "Your domain configuration may have changed.\r\n"
+                       "To allow the DKIM signatures of outbound mail to be verified,\r\n"
+                       "please ensure that the following DNS records are created:\r\n"
+                       "\r\n"
+               ),0);
+
+               ptr = inetcfg_in;
+               while (ptr && *ptr) {
+                       char *sep = strchr(ptr, '|');
+                       if (sep && !strncasecmp(sep+1, HKEY("localhost"))) {
+                               StrBufAppendPrintf(message, "Host name  : %s._domainkey.", CtdlGetConfigStr("dkim_selector"));
+                               StrBufAppendBufPlain(message, ptr, sep-ptr, 0);
+                               StrBufAppendBufPlain(message, HKEY("\r\n"), 0);
+                               StrBufAppendPrintf(message, "Record type: TXT\r\n");
+                               StrBufAppendBufPlain(message, HKEY("Value      : v=DKIM1;k=rsa;p="), 0);
+
+                               // figure out the public key and get it
+
+                               StrBufAppendPrintf(message, "\r\n");
+                       }
+                       if (ptr) ++ptr;
+               }
+
+               quickie_message("Citadel",
+                       NULL,                           // from
+                       NULL,                           // to
+                       AIDEROOM,                       // room
+                       ChrPtr(message),                // text
+                       FMT_RFC822,                     // format
+                       "DKIM records"                  // subject
+               );
+               FreeStrBuf(&message);
+       }
+
+       // Save it to the config database so we don't do this except when it changes.
+       CtdlSetConfigStr("dkim_config_hash", encoded_config_hash);
+       free(encoded_config_hash);
+}
index 0ab4467da4cb3e513c37040d8a24250fd67d3d83..d308ec2fcc086015112aecc8020f992af9935aba 100644 (file)
@@ -1,7 +1,7 @@
 # Makefile
 
 dkimtester: dkimtester.c ../../server/modules/smtp/dkim.c
-       cc -DCTDLDIR=/tmp -DDKIM_VERIFY_SIGNATURE -g dkimtester.c ../../server/modules/smtp/dkim.c -lcrypto -lcitadel -o dkimtester
+       cc -DDKIM_VERIFY_SIGNATURE -g dkimtester.c ../../server/modules/smtp/dkim.c -lcrypto -lcitadel -o dkimtester
 
 clean:
        rm -f dkimtester