Blame SOURCES/0026-curl-7.61.1-CVE-2020-8285.patch

6c1422
From 22b3d1cf0216f4369f01678c587da265c2e465af Mon Sep 17 00:00:00 2001
6c1422
From: Daniel Stenberg <daniel@haxx.se>
6c1422
Date: Sat, 28 Nov 2020 00:27:21 +0100
6c1422
Subject: [PATCH] ftp: make wc_statemach loop instead of recurse
6c1422
6c1422
CVE-2020-8285
6c1422
6c1422
Fixes #6255
6c1422
Bug: https://curl.se/docs/CVE-2020-8285.html
6c1422
Reported-by: xnynx on github
6c1422
6c1422
Upstream-commit: 69a358f2186e04cf44698b5100332cbf1ee7f01d
6c1422
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
6c1422
---
6c1422
 lib/ftp.c | 204 +++++++++++++++++++++++++++---------------------------
6c1422
 1 file changed, 103 insertions(+), 101 deletions(-)
6c1422
6c1422
diff --git a/lib/ftp.c b/lib/ftp.c
6c1422
index 7dbf080..482ab3a 100644
6c1422
--- a/lib/ftp.c
6c1422
+++ b/lib/ftp.c
6c1422
@@ -3786,130 +3786,132 @@ static CURLcode init_wc_data(struct connectdata *conn)
6c1422
   return result;
6c1422
 }
6c1422
 
6c1422
-/* This is called recursively */
6c1422
 static CURLcode wc_statemach(struct connectdata *conn)
6c1422
 {
6c1422
   struct WildcardData * const wildcard = &(conn->data->wildcard);
6c1422
   CURLcode result = CURLE_OK;
6c1422
 
6c1422
-  switch(wildcard->state) {
6c1422
-  case CURLWC_INIT:
6c1422
-    result = init_wc_data(conn);
6c1422
-    if(wildcard->state == CURLWC_CLEAN)
6c1422
-      /* only listing! */
6c1422
-      break;
6c1422
-    wildcard->state = result ? CURLWC_ERROR : CURLWC_MATCHING;
6c1422
-    break;
6c1422
+  for(;;) {
6c1422
+    switch(wildcard->state) {
6c1422
+    case CURLWC_INIT:
6c1422
+      result = init_wc_data(conn);
6c1422
+      if(wildcard->state == CURLWC_CLEAN)
6c1422
+        /* only listing! */
6c1422
+        return result;
6c1422
+      wildcard->state = result ? CURLWC_ERROR : CURLWC_MATCHING;
6c1422
+      return result;
6c1422
 
6c1422
-  case CURLWC_MATCHING: {
6c1422
-    /* In this state is LIST response successfully parsed, so lets restore
6c1422
-       previous WRITEFUNCTION callback and WRITEDATA pointer */
6c1422
-    struct ftp_wc *ftpwc = wildcard->protdata;
6c1422
-    conn->data->set.fwrite_func = ftpwc->backup.write_function;
6c1422
-    conn->data->set.out = ftpwc->backup.file_descriptor;
6c1422
-    ftpwc->backup.write_function = ZERO_NULL;
6c1422
-    ftpwc->backup.file_descriptor = NULL;
6c1422
-    wildcard->state = CURLWC_DOWNLOADING;
6c1422
-
6c1422
-    if(Curl_ftp_parselist_geterror(ftpwc->parser)) {
6c1422
-      /* error found in LIST parsing */
6c1422
-      wildcard->state = CURLWC_CLEAN;
6c1422
-      return wc_statemach(conn);
6c1422
-    }
6c1422
-    if(wildcard->filelist.size == 0) {
6c1422
-      /* no corresponding file */
6c1422
-      wildcard->state = CURLWC_CLEAN;
6c1422
-      return CURLE_REMOTE_FILE_NOT_FOUND;
6c1422
+    case CURLWC_MATCHING: {
6c1422
+      /* In this state is LIST response successfully parsed, so lets restore
6c1422
+         previous WRITEFUNCTION callback and WRITEDATA pointer */
6c1422
+      struct ftp_wc *ftpwc = wildcard->protdata;
6c1422
+      conn->data->set.fwrite_func = ftpwc->backup.write_function;
6c1422
+      conn->data->set.out = ftpwc->backup.file_descriptor;
6c1422
+      ftpwc->backup.write_function = ZERO_NULL;
6c1422
+      ftpwc->backup.file_descriptor = NULL;
6c1422
+      wildcard->state = CURLWC_DOWNLOADING;
6c1422
+
6c1422
+      if(Curl_ftp_parselist_geterror(ftpwc->parser)) {
6c1422
+        /* error found in LIST parsing */
6c1422
+        wildcard->state = CURLWC_CLEAN;
6c1422
+        continue;
6c1422
+      }
6c1422
+      if(wildcard->filelist.size == 0) {
6c1422
+        /* no corresponding file */
6c1422
+        wildcard->state = CURLWC_CLEAN;
6c1422
+        return CURLE_REMOTE_FILE_NOT_FOUND;
6c1422
+      }
6c1422
+      continue;
6c1422
     }
6c1422
-    return wc_statemach(conn);
6c1422
-  }
6c1422
 
6c1422
-  case CURLWC_DOWNLOADING: {
6c1422
-    /* filelist has at least one file, lets get first one */
6c1422
-    struct ftp_conn *ftpc = &conn->proto.ftpc;
6c1422
-    struct curl_fileinfo *finfo = wildcard->filelist.head->ptr;
6c1422
+    case CURLWC_DOWNLOADING: {
6c1422
+      /* filelist has at least one file, lets get first one */
6c1422
+      struct ftp_conn *ftpc = &conn->proto.ftpc;
6c1422
+      struct curl_fileinfo *finfo = wildcard->filelist.head->ptr;
6c1422
 
6c1422
-    char *tmp_path = aprintf("%s%s", wildcard->path, finfo->filename);
6c1422
-    if(!tmp_path)
6c1422
-      return CURLE_OUT_OF_MEMORY;
6c1422
+      char *tmp_path = aprintf("%s%s", wildcard->path, finfo->filename);
6c1422
+      if(!tmp_path)
6c1422
+        return CURLE_OUT_OF_MEMORY;
6c1422
 
6c1422
-    /* switch default "state.pathbuffer" and tmp_path, good to see
6c1422
-       ftp_parse_url_path function to understand this trick */
6c1422
-    Curl_safefree(conn->data->state.pathbuffer);
6c1422
-    conn->data->state.pathbuffer = tmp_path;
6c1422
-    conn->data->state.path = tmp_path;
6c1422
-
6c1422
-    infof(conn->data, "Wildcard - START of \"%s\"\n", finfo->filename);
6c1422
-    if(conn->data->set.chunk_bgn) {
6c1422
-      long userresponse;
6c1422
-      Curl_set_in_callback(conn->data, true);
6c1422
-      userresponse = conn->data->set.chunk_bgn(
6c1422
-        finfo, wildcard->customptr, (int)wildcard->filelist.size);
6c1422
-      Curl_set_in_callback(conn->data, false);
6c1422
-      switch(userresponse) {
6c1422
-      case CURL_CHUNK_BGN_FUNC_SKIP:
6c1422
-        infof(conn->data, "Wildcard - \"%s\" skipped by user\n",
6c1422
-              finfo->filename);
6c1422
-        wildcard->state = CURLWC_SKIP;
6c1422
-        return wc_statemach(conn);
6c1422
-      case CURL_CHUNK_BGN_FUNC_FAIL:
6c1422
-        return CURLE_CHUNK_FAILED;
6c1422
+      /* switch default "state.pathbuffer" and tmp_path, good to see
6c1422
+         ftp_parse_url_path function to understand this trick */
6c1422
+      Curl_safefree(conn->data->state.pathbuffer);
6c1422
+      conn->data->state.pathbuffer = tmp_path;
6c1422
+      conn->data->state.path = tmp_path;
6c1422
+
6c1422
+      infof(conn->data, "Wildcard - START of \"%s\"\n", finfo->filename);
6c1422
+      if(conn->data->set.chunk_bgn) {
6c1422
+        long userresponse;
6c1422
+        Curl_set_in_callback(conn->data, true);
6c1422
+        userresponse = conn->data->set.chunk_bgn(
6c1422
+          finfo, wildcard->customptr, (int)wildcard->filelist.size);
6c1422
+        Curl_set_in_callback(conn->data, false);
6c1422
+        switch(userresponse) {
6c1422
+        case CURL_CHUNK_BGN_FUNC_SKIP:
6c1422
+          infof(conn->data, "Wildcard - \"%s\" skipped by user\n",
6c1422
+                finfo->filename);
6c1422
+          wildcard->state = CURLWC_SKIP;
6c1422
+          continue;
6c1422
+        case CURL_CHUNK_BGN_FUNC_FAIL:
6c1422
+          return CURLE_CHUNK_FAILED;
6c1422
+        }
6c1422
       }
6c1422
-    }
6c1422
 
6c1422
-    if(finfo->filetype != CURLFILETYPE_FILE) {
6c1422
-      wildcard->state = CURLWC_SKIP;
6c1422
-      return wc_statemach(conn);
6c1422
-    }
6c1422
+      if(finfo->filetype != CURLFILETYPE_FILE) {
6c1422
+        wildcard->state = CURLWC_SKIP;
6c1422
+        continue;
6c1422
+      }
6c1422
 
6c1422
-    if(finfo->flags & CURLFINFOFLAG_KNOWN_SIZE)
6c1422
-      ftpc->known_filesize = finfo->size;
6c1422
+      if(finfo->flags & CURLFINFOFLAG_KNOWN_SIZE)
6c1422
+        ftpc->known_filesize = finfo->size;
6c1422
 
6c1422
-    result = ftp_parse_url_path(conn);
6c1422
-    if(result)
6c1422
-      return result;
6c1422
+      result = ftp_parse_url_path(conn);
6c1422
+      if(result)
6c1422
+        return result;
6c1422
 
6c1422
-    /* we don't need the Curl_fileinfo of first file anymore */
6c1422
-    Curl_llist_remove(&wildcard->filelist, wildcard->filelist.head, NULL);
6c1422
+      /* we don't need the Curl_fileinfo of first file anymore */
6c1422
+      Curl_llist_remove(&wildcard->filelist, wildcard->filelist.head, NULL);
6c1422
 
6c1422
-    if(wildcard->filelist.size == 0) { /* remains only one file to down. */
6c1422
-      wildcard->state = CURLWC_CLEAN;
6c1422
-      /* after that will be ftp_do called once again and no transfer
6c1422
-         will be done because of CURLWC_CLEAN state */
6c1422
-      return CURLE_OK;
6c1422
+      if(wildcard->filelist.size == 0) { /* remains only one file to down. */
6c1422
+        wildcard->state = CURLWC_CLEAN;
6c1422
+        /* after that will be ftp_do called once again and no transfer
6c1422
+           will be done because of CURLWC_CLEAN state */
6c1422
+        return CURLE_OK;
6c1422
+      }
6c1422
+      return result;
6c1422
     }
6c1422
-  } break;
6c1422
 
6c1422
-  case CURLWC_SKIP: {
6c1422
-    if(conn->data->set.chunk_end) {
6c1422
-      Curl_set_in_callback(conn->data, true);
6c1422
-      conn->data->set.chunk_end(conn->data->wildcard.customptr);
6c1422
-      Curl_set_in_callback(conn->data, false);
6c1422
+    case CURLWC_SKIP: {
6c1422
+      if(conn->data->set.chunk_end) {
6c1422
+        Curl_set_in_callback(conn->data, true);
6c1422
+        conn->data->set.chunk_end(conn->data->wildcard.customptr);
6c1422
+        Curl_set_in_callback(conn->data, false);
6c1422
+      }
6c1422
+      Curl_llist_remove(&wildcard->filelist, wildcard->filelist.head, NULL);
6c1422
+      wildcard->state = (wildcard->filelist.size == 0) ?
6c1422
+        CURLWC_CLEAN : CURLWC_DOWNLOADING;
6c1422
+      continue;
6c1422
     }
6c1422
-    Curl_llist_remove(&wildcard->filelist, wildcard->filelist.head, NULL);
6c1422
-    wildcard->state = (wildcard->filelist.size == 0) ?
6c1422
-                      CURLWC_CLEAN : CURLWC_DOWNLOADING;
6c1422
-    return wc_statemach(conn);
6c1422
-  }
6c1422
 
6c1422
-  case CURLWC_CLEAN: {
6c1422
-    struct ftp_wc *ftpwc = wildcard->protdata;
6c1422
-    result = CURLE_OK;
6c1422
-    if(ftpwc)
6c1422
-      result = Curl_ftp_parselist_geterror(ftpwc->parser);
6c1422
+    case CURLWC_CLEAN: {
6c1422
+      struct ftp_wc *ftpwc = wildcard->protdata;
6c1422
+      result = CURLE_OK;
6c1422
+      if(ftpwc)
6c1422
+        result = Curl_ftp_parselist_geterror(ftpwc->parser);
6c1422
 
6c1422
-    wildcard->state = result ? CURLWC_ERROR : CURLWC_DONE;
6c1422
-  } break;
6c1422
+      wildcard->state = result ? CURLWC_ERROR : CURLWC_DONE;
6c1422
+      return result;
6c1422
+    }
6c1422
 
6c1422
-  case CURLWC_DONE:
6c1422
-  case CURLWC_ERROR:
6c1422
-  case CURLWC_CLEAR:
6c1422
-    if(wildcard->dtor)
6c1422
-      wildcard->dtor(wildcard->protdata);
6c1422
-    break;
6c1422
+    case CURLWC_DONE:
6c1422
+    case CURLWC_ERROR:
6c1422
+    case CURLWC_CLEAR:
6c1422
+      if(wildcard->dtor)
6c1422
+        wildcard->dtor(wildcard->protdata);
6c1422
+      return result;
6c1422
+    }
6c1422
   }
6c1422
-
6c1422
-  return result;
6c1422
+  /* UNREACHABLE */
6c1422
 }
6c1422
 
6c1422
 /***********************************************************************
6c1422
-- 
6c1422
2.26.2
6c1422