From 6aed28b40a0410ec47d40c8c7296d8d10bae7576 Mon Sep 17 00:00:00 2001
From: Kevin McCarthy <kevin@8t8.us>
Date: Fri, 13 Jul 2018 11:16:33 -0700
Subject: [PATCH] Sanitize POP bcache paths.

Protect against bcache directory path traversal for UID values.

Thanks for Jeriko One for the bug report and patch, which this commit
is based upon.
---
 pop.c | 31 +++++++++++++++++++++++++------
 1 file changed, 25 insertions(+), 6 deletions(-)

diff --git a/pop.c b/pop.c
index d9d95fbe..288166de 100644
--- a/pop.c
+++ b/pop.c
@@ -40,6 +40,25 @@
 #define HC_FEXT		"hcache"	/* extension for hcache as POP lacks paths */
 #endif
 
+/**
+ * cache_id - Make a message-cache-compatible id
+ * @param id POP message id
+ * @retval ptr Sanitised string
+ *
+ * The POP message id may contain '/' and other awkward characters.
+ *
+ * @note This function returns a pointer to a static buffer.
+ */
+static const char *cache_id(const char *id)
+{
+  static char clean[SHORT_STRING];
+
+  strfcpy (clean, id, sizeof(clean));
+  mutt_sanitize_filename (clean, 1);
+
+  return clean;
+}
+
 /* write line to file */
 static int fetch_message (char *line, void *file)
 {
@@ -205,7 +224,7 @@ static int msg_cache_check (const char *id, body_cache_t *bcache, void *data)
   /* message not found in context -> remove it from cache
    * return the result of bcache, so we stop upon its first error
    */
-  return mutt_bcache_del (bcache, id);
+  return mutt_bcache_del (bcache, cache_id (id));
 }
 
 #ifdef USE_HCACHE
@@ -355,7 +374,7 @@ static int pop_fetch_headers (CONTEXT *ctx)
        *        - if we also have a body: read
        *        - if we don't have a body: new
        */
-      bcached = mutt_bcache_exists (pop_data->bcache, ctx->hdrs[i]->data) == 0;
+      bcached = mutt_bcache_exists (pop_data->bcache, cache_id (ctx->hdrs[i]->data)) == 0;
       ctx->hdrs[i]->old = 0;
       ctx->hdrs[i]->read = 0;
       if (hcached)
@@ -531,7 +550,7 @@ static int pop_fetch_message (CONTEXT* ctx, MESSAGE* msg, int msgno)
   unsigned short bcache = 1;
 
   /* see if we already have the message in body cache */
-  if ((msg->fp = mutt_bcache_get (pop_data->bcache, h->data)))
+  if ((msg->fp = mutt_bcache_get (pop_data->bcache, cache_id (h->data))))
     return 0;
 
   /*
@@ -578,7 +597,7 @@ static int pop_fetch_message (CONTEXT* ctx, MESSAGE* msg, int msgno)
 			M_PROGRESS_SIZE, NetInc, h->content->length + h->content->offset - 1);
 
     /* see if we can put in body cache; use our cache as fallback */
-    if (!(msg->fp = mutt_bcache_put (pop_data->bcache, h->data, 1)))
+    if (!(msg->fp = mutt_bcache_put (pop_data->bcache, cache_id (h->data), 1)))
     {
       /* no */
       bcache = 0;
@@ -624,7 +643,7 @@ static int pop_fetch_message (CONTEXT* ctx, MESSAGE* msg, int msgno)
    * portion of the headers, those required for the main display.
    */
   if (bcache)
-    mutt_bcache_commit (pop_data->bcache, h->data);
+    mutt_bcache_commit (pop_data->bcache, cache_id (h->data));
   else
   {
     cache->index = h->index;
@@ -704,7 +723,7 @@ static int pop_sync_mailbox (CONTEXT *ctx, int *index_hint)
 	snprintf (buf, sizeof (buf), "DELE %d\r\n", ctx->hdrs[i]->refno);
 	if ((ret = pop_query (pop_data, buf, sizeof (buf))) == 0)
 	{
-	  mutt_bcache_del (pop_data->bcache, ctx->hdrs[i]->data);
+	  mutt_bcache_del (pop_data->bcache, cache_id (ctx->hdrs[i]->data));
 #if USE_HCACHE
 	  mutt_hcache_delete (hc, ctx->hdrs[i]->data, strlen);
 #endif
-- 
2.18.0

