ca677c
From fe9b4ce23f82a3b2564123bbc3c674cc07a04f2b Mon Sep 17 00:00:00 2001
ca677c
From: Thomas Vegas <>
ca677c
Date: Sat, 31 Aug 2019 16:59:56 +0200
ca677c
Subject: [PATCH 1/2] tftp: return error when packet is too small for options
ca677c
ca677c
Upstream-commit: 82f3ba3806a34fe94dcf9e5c9b88deda6679ca1b
ca677c
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
ca677c
---
ca677c
 lib/tftp.c | 76 +++++++++++++++++++++++++++++++++---------------------
ca677c
 1 file changed, 46 insertions(+), 30 deletions(-)
ca677c
ca677c
diff --git a/lib/tftp.c b/lib/tftp.c
ca677c
index 022648a..8ab9d71 100644
ca677c
--- a/lib/tftp.c
ca677c
+++ b/lib/tftp.c
ca677c
@@ -405,13 +405,14 @@ static CURLcode tftp_parse_option_ack(tftp_state_data_t *state,
ca677c
   return CURLE_OK;
ca677c
 }
ca677c
 
ca677c
-static size_t tftp_option_add(tftp_state_data_t *state, size_t csize,
ca677c
-                              char *buf, const char *option)
ca677c
+static CURLcode tftp_option_add(tftp_state_data_t *state, size_t *csize,
ca677c
+                                char *buf, const char *option)
ca677c
 {
ca677c
-  if(( strlen(option) + csize + 1 ) > (size_t)state->blksize)
ca677c
-    return 0;
ca677c
+  if(( strlen(option) + *csize + 1) > (size_t)state->blksize)
ca677c
+    return CURLE_TFTP_ILLEGAL;
ca677c
   strcpy(buf, option);
ca677c
-  return( strlen(option) + 1 );
ca677c
+  *csize += strlen(option) + 1;
ca677c
+  return CURLE_OK;
ca677c
 }
ca677c
 
ca677c
 static CURLcode tftp_connect_for_tx(tftp_state_data_t *state,
ca677c
@@ -498,31 +499,46 @@ static CURLcode tftp_send_first(tftp_state_data_t *state, tftp_event_t event)
ca677c
     sbytes = 4 + strlen(filename) + strlen(mode);
ca677c
 
ca677c
     /* add tsize option */
ca677c
-    if(data->set.upload && (data->set.infilesize != -1))
ca677c
-      snprintf( buf, sizeof(buf), "%" FORMAT_OFF_T, data->set.infilesize );
ca677c
-    else
ca677c
-      strcpy(buf, "0"); /* the destination is large enough */
ca677c
-
ca677c
-    sbytes += tftp_option_add(state, sbytes,
ca677c
-                              (char *)state->spacket.data+sbytes,
ca677c
-                              TFTP_OPTION_TSIZE);
ca677c
-    sbytes += tftp_option_add(state, sbytes,
ca677c
-                              (char *)state->spacket.data+sbytes, buf);
ca677c
-    /* add blksize option */
ca677c
-    snprintf( buf, sizeof(buf), "%d", state->requested_blksize );
ca677c
-    sbytes += tftp_option_add(state, sbytes,
ca677c
-                              (char *)state->spacket.data+sbytes,
ca677c
-                              TFTP_OPTION_BLKSIZE);
ca677c
-    sbytes += tftp_option_add(state, sbytes,
ca677c
-                              (char *)state->spacket.data+sbytes, buf );
ca677c
-
ca677c
-    /* add timeout option */
ca677c
-    snprintf( buf, sizeof(buf), "%d", state->retry_time);
ca677c
-    sbytes += tftp_option_add(state, sbytes,
ca677c
-                              (char *)state->spacket.data+sbytes,
ca677c
-                              TFTP_OPTION_INTERVAL);
ca677c
-    sbytes += tftp_option_add(state, sbytes,
ca677c
-                              (char *)state->spacket.data+sbytes, buf );
ca677c
+    {
ca677c
+      CURLcode result;
ca677c
+      if(data->set.upload && (data->set.infilesize != -1))
ca677c
+        snprintf( buf, sizeof(buf), "%" FORMAT_OFF_T, data->set.infilesize );
ca677c
+      else
ca677c
+        strcpy(buf, "0"); /* the destination is large enough */
ca677c
+
ca677c
+      result = tftp_option_add(state, &sbytes,
ca677c
+                               (char *)state->spacket.data + sbytes,
ca677c
+                               TFTP_OPTION_TSIZE);
ca677c
+      if(result == CURLE_OK)
ca677c
+        result = tftp_option_add(state, &sbytes,
ca677c
+                                 (char *)state->spacket.data + sbytes, buf);
ca677c
+
ca677c
+      /* add blksize option */
ca677c
+      snprintf(buf, sizeof(buf), "%d", state->requested_blksize);
ca677c
+      if(result == CURLE_OK)
ca677c
+        result = tftp_option_add(state, &sbytes,
ca677c
+                                 (char *)state->spacket.data + sbytes,
ca677c
+                                 TFTP_OPTION_BLKSIZE);
ca677c
+      if(result == CURLE_OK)
ca677c
+        result = tftp_option_add(state, &sbytes,
ca677c
+                                 (char *)state->spacket.data + sbytes, buf);
ca677c
+
ca677c
+      /* add timeout option */
ca677c
+      snprintf(buf, sizeof(buf), "%d", state->retry_time);
ca677c
+      if(result == CURLE_OK)
ca677c
+        result = tftp_option_add(state, &sbytes,
ca677c
+                                 (char *)state->spacket.data + sbytes,
ca677c
+                                 TFTP_OPTION_INTERVAL);
ca677c
+      if(result == CURLE_OK)
ca677c
+        result = tftp_option_add(state, &sbytes,
ca677c
+                                 (char *)state->spacket.data + sbytes, buf);
ca677c
+
ca677c
+      if(result != CURLE_OK) {
ca677c
+        failf(data, "TFTP buffer too small for options");
ca677c
+        free(filename);
ca677c
+        return CURLE_TFTP_ILLEGAL;
ca677c
+      }
ca677c
+    }
ca677c
 
ca677c
     /* the typecase for the 3rd argument is mostly for systems that do
ca677c
        not have a size_t argument, like older unixes that want an 'int' */
ca677c
-- 
ca677c
2.20.1
ca677c
ca677c
ca677c
From ea5ca5f9ee1a4588061fc8b5a10c5b65fb68a0cd Mon Sep 17 00:00:00 2001
ca677c
From: Thomas Vegas <>
ca677c
Date: Sat, 31 Aug 2019 17:30:51 +0200
ca677c
Subject: [PATCH 2/2] tftp: Alloc maximum blksize, and use default unless OACK
ca677c
 is received
ca677c
ca677c
Fixes potential buffer overflow from 'recvfrom()', should the server
ca677c
return an OACK without blksize.
ca677c
ca677c
Bug: https://curl.haxx.se/docs/CVE-2019-5482.html
ca677c
CVE-2019-5482
ca677c
ca677c
Upstream-commit: facb0e4662415b5f28163e853dc6742ac5fafb3d
ca677c
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
ca677c
---
ca677c
 lib/tftp.c | 12 +++++++++---
ca677c
 1 file changed, 9 insertions(+), 3 deletions(-)
ca677c
ca677c
diff --git a/lib/tftp.c b/lib/tftp.c
ca677c
index 8ab9d71..af3b288 100644
ca677c
--- a/lib/tftp.c
ca677c
+++ b/lib/tftp.c
ca677c
@@ -961,6 +961,7 @@ static CURLcode tftp_connect(struct connectdata *conn, bool *done)
ca677c
   CURLcode code;
ca677c
   tftp_state_data_t *state;
ca677c
   int blksize, rc;
ca677c
+  int need_blksize;
ca677c
 
ca677c
   blksize = TFTP_BLKSIZE_DEFAULT;
ca677c
 
ca677c
@@ -979,15 +980,20 @@ static CURLcode tftp_connect(struct connectdata *conn, bool *done)
ca677c
       return CURLE_TFTP_ILLEGAL;
ca677c
   }
ca677c
 
ca677c
+  need_blksize = blksize;
ca677c
+  /* default size is the fallback when no OACK is received */
ca677c
+  if(need_blksize < TFTP_BLKSIZE_DEFAULT)
ca677c
+    need_blksize = TFTP_BLKSIZE_DEFAULT;
ca677c
+
ca677c
   if(!state->rpacket.data) {
ca677c
-    state->rpacket.data = calloc(1, blksize + 2 + 2);
ca677c
+    state->rpacket.data = calloc(1, need_blksize + 2 + 2);
ca677c
 
ca677c
     if(!state->rpacket.data)
ca677c
       return CURLE_OUT_OF_MEMORY;
ca677c
   }
ca677c
 
ca677c
   if(!state->spacket.data) {
ca677c
-    state->spacket.data = calloc(1, blksize + 2 + 2);
ca677c
+    state->spacket.data = calloc(1, need_blksize + 2 + 2);
ca677c
 
ca677c
     if(!state->spacket.data)
ca677c
       return CURLE_OUT_OF_MEMORY;
ca677c
@@ -1001,7 +1007,7 @@ static CURLcode tftp_connect(struct connectdata *conn, bool *done)
ca677c
   state->sockfd = state->conn->sock[FIRSTSOCKET];
ca677c
   state->state = TFTP_STATE_START;
ca677c
   state->error = TFTP_ERR_NONE;
ca677c
-  state->blksize = blksize;
ca677c
+  state->blksize = TFTP_BLKSIZE_DEFAULT; /* Unless updated by OACK response */
ca677c
   state->requested_blksize = blksize;
ca677c
 
ca677c
   ((struct sockaddr *)&state->local_addr)->sa_family =
ca677c
-- 
ca677c
2.20.1
ca677c