On Sun, Apr 19, 2020 at 09:26:46AM -0700, Kevin wrote in
<[email protected]>:
However, I have opened a placeholder ticket #222 for future
consideration.
For the same future consideration, please find attached a proposed patch.
Deterministic it is not (unless you want to save the seed and a message
counter somewhere), guaranteed to be unique, it is.
The local part of the Message-ID using this patch is based on two base64
encoded random 64 bit values and it should (untested though) also work on
systems with different RAND_MAX values.
Feel free to ignore/replace/amend the patch as desired. If you would like
me to make any further changes to it, I'm also happy to do this.
Warm regards,
Remco
A Message-ID should be globally unique. Currently mutt generates this ID
based on the current date and time, followed by ".G", followed by a letter
A to Z (A for the 1st and 27th email sent, Z for the 26th, etc.), followed
by the pid of the active mutt process, followed by "@" and the configured
fqdn.
This can lead to information being leaked as to an users email habits and
activities, which might be undesirable.
By replacing everything left of the "@" in the Message-ID with two random
strings we no longer include this information. As the strings are based on
two 64 bit random numbers, I will buy a treat for anyone who accidentally
is confronted with non unique message ID's.
---
protos.h | 1 +
sendlib.c | 64 ++++++++++++++++++++++++++++++++++++++++++++++---------
2 files changed, 55 insertions(+), 10 deletions(-)
diff --git a/protos.h b/protos.h
index e1bfe746..000f9650 100644
--- a/protos.h
+++ b/protos.h
@@ -147,6 +147,7 @@ char *mutt_expand_path (char *, size_t);
char *_mutt_expand_path (char *, size_t, int);
char *mutt_find_hook (int, const char *);
char *mutt_gecos_name (char *, size_t, struct passwd *);
+char *mutt_gen_base64_enc_rand (void);
char *mutt_gen_msgid (void);
char *mutt_get_body_charset (char *, size_t, BODY *);
const char *mutt_get_name (ADDRESS *);
diff --git a/sendlib.c b/sendlib.c
index cdec5beb..5d19ffa5 100644
--- a/sendlib.c
+++ b/sendlib.c
@@ -58,6 +58,24 @@
#define EX_OK 0
#endif
+/* When constructing a Message-ID, we like to find a 64 bit random value for
+ * the random component. As the size of RAND_MAX can vary between systems, we
+ * may have to call the random() function repeatedly to get 64 bits of random
+ * values. Here we determine how many times we have to call this function to
+ * get at least 64 bits of random goodness. */
+#if RAND_MAX/256 >= 0xFFFFFFFFFFFFFF
+ #define RAND_LOOP_COUNT 1
+#elif RAND_MAX/256 >= 0xFFFFFF
+ #define RAND_LOOP_COUNT 2
+#elif RAND_MAX/256 >= 0x3FFFF
+ #define RAND_LOOP_COUNT 3
+#elif RAND_MAX/256 >= 0x1FF
+ #define RAND_LOOP_COUNT 4
+#else
+ #define RAND_LOOP_COUNT 5
+#endif
+
+
/* If you are debugging this file, comment out the following line. */
#define NDEBUG
@@ -79,8 +97,6 @@ const char B64Chars[64] = {
'8', '9', '+', '/'
};
-static char MsgIdPfx = 'A';
-
static void transform_to_7bit (BODY *a, FILE *fpin);
static void encode_quoted (FGETCONV * fc, FILE *fout, int istext)
@@ -2398,22 +2414,50 @@ const char *mutt_fqdn(short may_hide_host)
return p;
}
+/* Generate a 64 bit random number. Based on the size of RAND_MAX,
+ * multiple loops might be required. */
+uint64_t rand_uint64(void) {
+ uint64_t r = 0;
+ for (int i=RAND_LOOP_COUNT; i > 0; i--) {
+ r = r*(RAND_MAX + (uint64_t) 1) + random();
+ }
+ return r;
+}
+
+/* Return a Base64 encoded representation of a 64 bit random number */
+char *mutt_gen_base64_enc_rand (void)
+{
+ unsigned char rbuf[8];
+ unsigned char result[12];
+ uint64_t r = 0;
+
+ r = rand_uint64();
+
+ rbuf[0] = r & 0xFF;
+ rbuf[1] = (r >> 8) & 0xFF;
+ rbuf[2] = (r >> 16) & 0xFF;
+ rbuf[3] = (r >> 24) & 0xFF;
+ rbuf[4] = (r >> 32) & 0xFF;
+ rbuf[5] = (r >> 40) & 0xFF;
+ rbuf[6] = (r >> 48) & 0xFF;
+ rbuf[7] = (r >> 56) & 0xFF;
+
+ mutt_to_base64(result, rbuf, 8, 12);
+ result[11] = '\0'; /* The padded '=' at the end adds no value to our result
*/
+
+ return (safe_strdup ((char*)result));
+}
+
char *mutt_gen_msgid (void)
{
char buf[SHORT_STRING];
- time_t now;
- struct tm *tm;
const char *fqdn;
- now = time (NULL);
- tm = gmtime (&now);
if (!(fqdn = mutt_fqdn(0)))
fqdn = NONULL(Hostname);
- snprintf (buf, sizeof (buf), "<%d%02d%02d%02d%02d%02d.G%c%u@%s>",
- tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour,
- tm->tm_min, tm->tm_sec, MsgIdPfx, (unsigned int)getpid (), fqdn);
- MsgIdPfx = (MsgIdPfx == 'Z') ? 'A' : MsgIdPfx + 1;
+ snprintf (buf, sizeof (buf), "<%s.%s@%s>", mutt_gen_base64_enc_rand(),
+ mutt_gen_base64_enc_rand(), fqdn);
return (safe_strdup (buf));
}
--
2.25.3