From b5c06cd72be5ee4d0ebdf381c0b40825dcfa4a0b Mon Sep 17 00:00:00 2001
From: Brendan Cully <brendan@kublai.com>
Date: Mon, 2 Apr 2007 15:20:58 -0700
Subject: [PATCH] Validate msgid in APOP authentication. Closes #2846

---
 pop_auth.c |  7 +++++++
 rfc822.c   | 46 ++++++++++++++++++++++++++++++++++++++++++++++
 rfc822.h   |  1 +
 3 files changed, 54 insertions(+)

diff --git a/pop_auth.c b/pop_auth.c
index 930f5713..c77c630d 100644
--- a/pop_auth.c
+++ b/pop_auth.c
@@ -183,6 +183,13 @@ static pop_auth_res_t pop_auth_apop (POP_DATA *pop_data, const char *method)
   if (!pop_data->timestamp)
     return POP_A_UNAVAIL;
 
+  if (rfc822_valid_msgid (pop_data->timestamp) < 0)
+  {
+    mutt_error _("POP timestamp is invalid!");
+    mutt_sleep (2);
+    return POP_A_UNAVAIL;
+  }
+
   mutt_message _("Authenticating (APOP)...");
 
   /* Compute the authentication hash to send to the server */
diff --git a/rfc822.c b/rfc822.c
index 7d9e4bb8..7b05e303 100644
--- a/rfc822.c
+++ b/rfc822.c
@@ -792,6 +792,52 @@ ADDRESS *rfc822_append (ADDRESS **a, ADDRESS *b)
   return tmp;
 }
 
+/* incomplete. Only used to thwart the APOP MD5 attack (#2846). */
+int rfc822_valid_msgid (const char *msgid)
+{
+  /* msg-id         = "<" addr-spec ">"
+   * addr-spec      = local-part "@" domain
+   * local-part     = word *("." word)
+   * word           = atom / quoted-string
+   * atom           = 1*<any CHAR except specials, SPACE and CTLs>
+   * CHAR           = ( 0.-127. )
+   * specials       = "(" / ")" / "<" / ">" / "@"
+                    / "," / ";" / ":" / "\" / <">
+		    / "." / "[" / "]"
+   * SPACE          = ( 32. )
+   * CTLS           = ( 0.-31., 127.)
+   * quoted-string  = <"> *(qtext/quoted-pair) <">
+   * qtext          = <any CHAR except <">, "\" and CR>
+   * CR             = ( 13. )
+   * quoted-pair    = "\" CHAR
+   * domain         = sub-domain *("." sub-domain)
+   * sub-domain     = domain-ref / domain-literal
+   * domain-ref     = atom
+   * domain-literal = "[" *(dtext / quoted-pair) "]"
+   */
+
+  char* dom;
+  unsigned int l, i;
+
+  if (!msgid || !*msgid)
+    return -1;
+
+  l = mutt_strlen (msgid);
+  if (l < 5) /* <atom@atom> */
+    return -1;
+  if (msgid[0] != '<' || msgid[l-1] != '>')
+    return -1;
+  if (!(dom = strrchr (msgid, '@')))
+    return -1;
+
+  /* TODO: complete parser */
+  for (i = 0; i < l; i++)
+    if ((unsigned char)msgid[i] > 127)
+      return -1;
+
+  return 0;
+}
+
 #ifdef TESTING
 int safe_free (void **p)	/* __SAFE_FREE_CHECKED__ */
 {
diff --git a/rfc822.h b/rfc822.h
index 3c8d1765..5cabdf31 100644
--- a/rfc822.h
+++ b/rfc822.h
@@ -52,6 +52,7 @@ int rfc822_write_address (char *, size_t, ADDRESS *, int);
 void rfc822_write_address_single (char *, size_t, ADDRESS *, int);
 void rfc822_free_address (ADDRESS **addr);
 void rfc822_cat (char *, size_t, const char *, const char *);
+int rfc822_valid_msgid (const char *msgid);
 
 extern int RFC822Error;
 extern const char *RFC822Errors[];
-- 
2.40.0