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

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