@@ -111,6 +111,8 @@ static int http_proactive_auth;
111111static const char * user_agent ;
112112static int curl_empty_auth ;
113113
114+ enum http_follow_config http_follow_config = HTTP_FOLLOW_INITIAL ;
115+
114116#if LIBCURL_VERSION_NUM >= 0x071700
115117/* Use CURLOPT_KEYPASSWD as is */
116118#elif LIBCURL_VERSION_NUM >= 0x070903
@@ -366,6 +368,16 @@ static int http_options(const char *var, const char *value, void *cb)
366368 return 0 ;
367369 }
368370
371+ if (!strcmp ("http.followredirects" , var )) {
372+ if (value && !strcmp (value , "initial" ))
373+ http_follow_config = HTTP_FOLLOW_INITIAL ;
374+ else if (git_config_bool (var , value ))
375+ http_follow_config = HTTP_FOLLOW_ALWAYS ;
376+ else
377+ http_follow_config = HTTP_FOLLOW_NONE ;
378+ return 0 ;
379+ }
380+
369381 /* Fall back on the default ones */
370382 return git_default_config (var , value , cb );
371383}
@@ -717,7 +729,6 @@ static CURL *get_curl_handle(void)
717729 curl_low_speed_time );
718730 }
719731
720- curl_easy_setopt (result , CURLOPT_FOLLOWLOCATION , 1 );
721732 curl_easy_setopt (result , CURLOPT_MAXREDIRS , 20 );
722733#if LIBCURL_VERSION_NUM >= 0x071301
723734 curl_easy_setopt (result , CURLOPT_POSTREDIR , CURL_REDIR_POST_ALL );
@@ -734,6 +745,7 @@ static CURL *get_curl_handle(void)
734745 if (is_transport_allowed ("ftps" ))
735746 allowed_protocols |= CURLPROTO_FTPS ;
736747 curl_easy_setopt (result , CURLOPT_REDIR_PROTOCOLS , allowed_protocols );
748+ curl_easy_setopt (result , CURLOPT_PROTOCOLS , allowed_protocols );
737749#else
738750 if (transport_restrict_protocols ())
739751 warning ("protocol restrictions not applied to curl redirects because\n"
@@ -1044,6 +1056,16 @@ struct active_request_slot *get_active_slot(void)
10441056 curl_easy_setopt (slot -> curl , CURLOPT_FAILONERROR , 1 );
10451057 curl_easy_setopt (slot -> curl , CURLOPT_RANGE , NULL );
10461058
1059+ /*
1060+ * Default following to off unless "ALWAYS" is configured; this gives
1061+ * callers a sane starting point, and they can tweak for individual
1062+ * HTTP_FOLLOW_* cases themselves.
1063+ */
1064+ if (http_follow_config == HTTP_FOLLOW_ALWAYS )
1065+ curl_easy_setopt (slot -> curl , CURLOPT_FOLLOWLOCATION , 1 );
1066+ else
1067+ curl_easy_setopt (slot -> curl , CURLOPT_FOLLOWLOCATION , 0 );
1068+
10471069#if LIBCURL_VERSION_NUM >= 0x070a08
10481070 curl_easy_setopt (slot -> curl , CURLOPT_IPRESOLVE , git_curl_ipresolve );
10491071#endif
@@ -1286,9 +1308,12 @@ static int handle_curl_result(struct slot_results *results)
12861308 * If we see a failing http code with CURLE_OK, we have turned off
12871309 * FAILONERROR (to keep the server's custom error response), and should
12881310 * translate the code into failure here.
1311+ *
1312+ * Likewise, if we see a redirect (30x code), that means we turned off
1313+ * redirect-following, and we should treat the result as an error.
12891314 */
12901315 if (results -> curl_result == CURLE_OK &&
1291- results -> http_code >= 400 ) {
1316+ results -> http_code >= 300 ) {
12921317 results -> curl_result = CURLE_HTTP_RETURNED_ERROR ;
12931318 /*
12941319 * Normally curl will already have put the "reason phrase"
@@ -1607,6 +1632,9 @@ static int http_request(const char *url,
16071632 strbuf_addstr (& buf , " no-cache" );
16081633 if (options && options -> keep_error )
16091634 curl_easy_setopt (slot -> curl , CURLOPT_FAILONERROR , 0 );
1635+ if (options && options -> initial_request &&
1636+ http_follow_config == HTTP_FOLLOW_INITIAL )
1637+ curl_easy_setopt (slot -> curl , CURLOPT_FOLLOWLOCATION , 1 );
16101638
16111639 headers = curl_slist_append (headers , buf .buf );
16121640
@@ -1655,16 +1683,16 @@ static int http_request(const char *url,
16551683 *
16561684 * Note that this assumes a sane redirect scheme. It's entirely possible
16571685 * in the example above to end up at a URL that does not even end in
1658- * "info/refs". In such a case we simply punt, as there is not much we can
1659- * do (and such a scheme is unlikely to represent a real git repository,
1660- * which means we are likely about to abort anyway) .
1686+ * "info/refs". In such a case we die. There's not much we can do, such a
1687+ * scheme is unlikely to represent a real git repository, and failing to
1688+ * rewrite the base opens options for malicious redirects to do funny things .
16611689 */
16621690static int update_url_from_redirect (struct strbuf * base ,
16631691 const char * asked ,
16641692 const struct strbuf * got )
16651693{
16661694 const char * tail ;
1667- size_t tail_len ;
1695+ size_t new_len ;
16681696
16691697 if (!strcmp (asked , got -> buf ))
16701698 return 0 ;
@@ -1673,14 +1701,16 @@ static int update_url_from_redirect(struct strbuf *base,
16731701 die ("BUG: update_url_from_redirect: %s is not a superset of %s" ,
16741702 asked , base -> buf );
16751703
1676- tail_len = strlen (tail );
1677-
1678- if (got -> len < tail_len ||
1679- strcmp (tail , got -> buf + got -> len - tail_len ))
1680- return 0 ; /* insane redirect scheme */
1704+ new_len = got -> len ;
1705+ if (!strip_suffix_mem (got -> buf , & new_len , tail ))
1706+ die (_ ("unable to update url base from redirection:\n"
1707+ " asked for: %s\n"
1708+ " redirect: %s" ),
1709+ asked , got -> buf );
16811710
16821711 strbuf_reset (base );
1683- strbuf_add (base , got -> buf , got -> len - tail_len );
1712+ strbuf_add (base , got -> buf , new_len );
1713+
16841714 return 1 ;
16851715}
16861716
0 commit comments