]> git.ipfire.org Git - thirdparty/git.git/blobdiff - http.c
Merge branch 'rs/pull-leakfix'
[thirdparty/git.git] / http.c
diff --git a/http.c b/http.c
index c4dfdac11fe109b1dd5c8e5204af74d6a6f0d0d0..3b12843a5b2341615d341e5c1fd49d05dcb893a5 100644 (file)
--- a/http.c
+++ b/http.c
@@ -18,7 +18,7 @@
 
 static struct trace_key trace_curl = TRACE_KEY_INIT(CURL);
 static int trace_curl_data = 1;
-static struct string_list cookies_to_redact = STRING_LIST_INIT_DUP;
+static int trace_curl_redact = 1;
 #if LIBCURL_VERSION_NUM >= 0x070a08
 long int git_curl_ipresolve = CURL_IPRESOLVE_WHATEVER;
 #else
@@ -86,6 +86,13 @@ static long curl_low_speed_time = -1;
 static int curl_ftp_no_epsv;
 static const char *curl_http_proxy;
 static const char *http_proxy_authmethod;
+
+static const char *http_proxy_ssl_cert;
+static const char *http_proxy_ssl_key;
+static const char *http_proxy_ssl_ca_info;
+static struct credential proxy_cert_auth = CREDENTIAL_INIT;
+static int proxy_ssl_cert_password_required;
+
 static struct {
        const char *name;
        long curlauth_param;
@@ -150,7 +157,7 @@ static unsigned long empty_auth_useless =
 
 static struct curl_slist *pragma_header;
 static struct curl_slist *no_pragma_header;
-static struct curl_slist *extra_http_headers;
+static struct string_list extra_http_headers = STRING_LIST_INIT_DUP;
 
 static struct active_request_slot *active_queue_head;
 
@@ -365,6 +372,20 @@ static int http_options(const char *var, const char *value, void *cb)
        if (!strcmp("http.proxyauthmethod", var))
                return git_config_string(&http_proxy_authmethod, var, value);
 
+       if (!strcmp("http.proxysslcert", var))
+               return git_config_string(&http_proxy_ssl_cert, var, value);
+
+       if (!strcmp("http.proxysslkey", var))
+               return git_config_string(&http_proxy_ssl_key, var, value);
+
+       if (!strcmp("http.proxysslcainfo", var))
+               return git_config_string(&http_proxy_ssl_ca_info, var, value);
+
+       if (!strcmp("http.proxysslcertpasswordprotected", var)) {
+               proxy_ssl_cert_password_required = git_config_bool(var, value);
+               return 0;
+       }
+
        if (!strcmp("http.cookiefile", var))
                return git_config_pathname(&curl_cookie_file, var, value);
        if (!strcmp("http.savecookies", var)) {
@@ -414,11 +435,9 @@ static int http_options(const char *var, const char *value, void *cb)
                if (!value) {
                        return config_error_nonbool(var);
                } else if (!*value) {
-                       curl_slist_free_all(extra_http_headers);
-                       extra_http_headers = NULL;
+                       string_list_clear(&extra_http_headers, 0);
                } else {
-                       extra_http_headers =
-                               curl_slist_append(extra_http_headers, value);
+                       string_list_append(&extra_http_headers, value);
                }
                return 0;
        }
@@ -513,9 +532,11 @@ static void set_proxyauth_name_password(CURL *result)
 #else
                struct strbuf s = STRBUF_INIT;
 
-               strbuf_addstr_urlencode(&s, proxy_auth.username, 1);
+               strbuf_addstr_urlencode(&s, proxy_auth.username,
+                                       is_rfc3986_unreserved);
                strbuf_addch(&s, ':');
-               strbuf_addstr_urlencode(&s, proxy_auth.password, 1);
+               strbuf_addstr_urlencode(&s, proxy_auth.password,
+                                       is_rfc3986_unreserved);
                curl_proxyuserpwd = strbuf_detach(&s, NULL);
                curl_easy_setopt(result, CURLOPT_PROXYUSERPWD, curl_proxyuserpwd);
 #endif
@@ -566,6 +587,22 @@ static int has_cert_password(void)
        return 1;
 }
 
+#if LIBCURL_VERSION_NUM >= 0x073400
+static int has_proxy_cert_password(void)
+{
+       if (http_proxy_ssl_cert == NULL || proxy_ssl_cert_password_required != 1)
+               return 0;
+       if (!proxy_cert_auth.password) {
+               proxy_cert_auth.protocol = xstrdup("cert");
+               proxy_cert_auth.host = xstrdup("");
+               proxy_cert_auth.username = xstrdup("");
+               proxy_cert_auth.path = xstrdup(http_proxy_ssl_cert);
+               credential_fill(&proxy_cert_auth);
+       }
+       return 1;
+}
+#endif
+
 #if LIBCURL_VERSION_NUM >= 0x071900
 static void set_curl_keepalive(CURL *c)
 {
@@ -605,8 +642,9 @@ static void redact_sensitive_header(struct strbuf *header)
 {
        const char *sensitive_header;
 
-       if (skip_prefix(header->buf, "Authorization:", &sensitive_header) ||
-           skip_prefix(header->buf, "Proxy-Authorization:", &sensitive_header)) {
+       if (trace_curl_redact &&
+           (skip_prefix(header->buf, "Authorization:", &sensitive_header) ||
+            skip_prefix(header->buf, "Proxy-Authorization:", &sensitive_header))) {
                /* The first token is the type, which is OK to log */
                while (isspace(*sensitive_header))
                        sensitive_header++;
@@ -615,20 +653,15 @@ static void redact_sensitive_header(struct strbuf *header)
                /* Everything else is opaque and possibly sensitive */
                strbuf_setlen(header,  sensitive_header - header->buf);
                strbuf_addstr(header, " <redacted>");
-       } else if (cookies_to_redact.nr &&
+       } else if (trace_curl_redact &&
                   skip_prefix(header->buf, "Cookie:", &sensitive_header)) {
                struct strbuf redacted_header = STRBUF_INIT;
-               char *cookie;
+               const char *cookie;
 
                while (isspace(*sensitive_header))
                        sensitive_header++;
 
-               /*
-                * The contents of header starting from sensitive_header will
-                * subsequently be overridden, so it is fine to mutate this
-                * string (hence the assignment to "char *").
-                */
-               cookie = (char *) sensitive_header;
+               cookie = sensitive_header;
 
                while (cookie) {
                        char *equals;
@@ -641,14 +674,8 @@ static void redact_sensitive_header(struct strbuf *header)
                                strbuf_addstr(&redacted_header, cookie);
                                continue;
                        }
-                       *equals = 0; /* temporarily set to NUL for lookup */
-                       if (string_list_lookup(&cookies_to_redact, cookie)) {
-                               strbuf_addstr(&redacted_header, cookie);
-                               strbuf_addstr(&redacted_header, "=<redacted>");
-                       } else {
-                               *equals = '=';
-                               strbuf_addstr(&redacted_header, cookie);
-                       }
+                       strbuf_add(&redacted_header, cookie, equals - cookie);
+                       strbuf_addstr(&redacted_header, "=<redacted>");
                        if (semicolon) {
                                /*
                                 * There are more cookies. (Or, for some
@@ -681,8 +708,8 @@ static void curl_dump_header(const char *text, unsigned char *ptr, size_t size,
        for (header = headers; *header; header++) {
                if (hide_sensitive_header)
                        redact_sensitive_header(*header);
-               strbuf_insert((*header), 0, text, strlen(text));
-               strbuf_insert((*header), strlen(text), ": ", 2);
+               strbuf_insertstr((*header), 0, text);
+               strbuf_insertstr((*header), strlen(text), ": ");
                strbuf_rtrim((*header));
                strbuf_addch((*header), '\n');
                trace_strbuf(&trace_curl, (*header));
@@ -767,6 +794,12 @@ static int curl_trace(CURL *handle, curl_infotype type, char *data, size_t size,
        return 0;
 }
 
+void http_trace_curl_no_data(void)
+{
+       trace_override_envvar(&trace_curl, "1");
+       trace_curl_data = 0;
+}
+
 void setup_curl_trace(CURL *handle)
 {
        if (!trace_want(&trace_curl))
@@ -925,8 +958,14 @@ static CURL *get_curl_handle(void)
 #if LIBCURL_VERSION_NUM >= 0x073400
                curl_easy_setopt(result, CURLOPT_PROXY_CAINFO, NULL);
 #endif
-       } else if (ssl_cainfo != NULL)
-               curl_easy_setopt(result, CURLOPT_CAINFO, ssl_cainfo);
+       } else if (ssl_cainfo != NULL || http_proxy_ssl_ca_info != NULL) {
+               if (ssl_cainfo != NULL)
+                       curl_easy_setopt(result, CURLOPT_CAINFO, ssl_cainfo);
+#if LIBCURL_VERSION_NUM >= 0x073400
+               if (http_proxy_ssl_ca_info != NULL)
+                       curl_easy_setopt(result, CURLOPT_PROXY_CAINFO, http_proxy_ssl_ca_info);
+#endif
+       }
 
        if (curl_low_speed_limit > 0 && curl_low_speed_time > 0) {
                curl_easy_setopt(result, CURLOPT_LOW_SPEED_LIMIT,
@@ -950,15 +989,12 @@ static CURL *get_curl_handle(void)
        warning(_("Protocol restrictions not supported with cURL < 7.19.4"));
 #endif
        if (getenv("GIT_CURL_VERBOSE"))
-               curl_easy_setopt(result, CURLOPT_VERBOSE, 1L);
+               http_trace_curl_no_data();
        setup_curl_trace(result);
        if (getenv("GIT_TRACE_CURL_NO_DATA"))
                trace_curl_data = 0;
-       if (getenv("GIT_REDACT_COOKIES")) {
-               string_list_split(&cookies_to_redact,
-                                 getenv("GIT_REDACT_COOKIES"), ',', -1);
-               string_list_sort(&cookies_to_redact);
-       }
+       if (!git_env_bool("GIT_TRACE_REDACT", 1))
+               trace_curl_redact = 0;
 
        curl_easy_setopt(result, CURLOPT_USERAGENT,
                user_agent ? user_agent : git_user_agent());
@@ -1019,9 +1055,18 @@ static CURL *get_curl_handle(void)
                                CURLOPT_PROXYTYPE, CURLPROXY_SOCKS4);
 #endif
 #if LIBCURL_VERSION_NUM >= 0x073400
-               else if (starts_with(curl_http_proxy, "https"))
-                       curl_easy_setopt(result,
-                               CURLOPT_PROXYTYPE, CURLPROXY_HTTPS);
+               else if (starts_with(curl_http_proxy, "https")) {
+                       curl_easy_setopt(result, CURLOPT_PROXYTYPE, CURLPROXY_HTTPS);
+
+                       if (http_proxy_ssl_cert)
+                               curl_easy_setopt(result, CURLOPT_PROXY_SSLCERT, http_proxy_ssl_cert);
+
+                       if (http_proxy_ssl_key)
+                               curl_easy_setopt(result, CURLOPT_PROXY_SSLKEY, http_proxy_ssl_key);
+
+                       if (has_proxy_cert_password())
+                               curl_easy_setopt(result, CURLOPT_PROXY_KEYPASSWD, proxy_cert_auth.password);
+               }
 #endif
                if (strstr(curl_http_proxy, "://"))
                        credential_from_url(&proxy_auth, curl_http_proxy);
@@ -1074,6 +1119,7 @@ void http_init(struct remote *remote, const char *url, int proactive_auth)
 
        git_config(urlmatch_config_entry, &config);
        free(normalized_url);
+       string_list_clear(&config.vars, 1);
 
 #if LIBCURL_VERSION_NUM >= 0x073800
        if (http_ssl_backend) {
@@ -1160,6 +1206,13 @@ void http_init(struct remote *remote, const char *url, int proactive_auth)
                max_requests = DEFAULT_MAX_REQUESTS;
 #endif
 
+       set_from_env(&http_proxy_ssl_cert, "GIT_PROXY_SSL_CERT");
+       set_from_env(&http_proxy_ssl_key, "GIT_PROXY_SSL_KEY");
+       set_from_env(&http_proxy_ssl_ca_info, "GIT_PROXY_SSL_CAINFO");
+
+       if (getenv("GIT_PROXY_SSL_CERT_PASSWORD_PROTECTED"))
+               proxy_ssl_cert_password_required = 1;
+
        if (getenv("GIT_CURL_FTP_NO_EPSV"))
                curl_ftp_no_epsv = 1;
 
@@ -1200,8 +1253,7 @@ void http_cleanup(void)
 #endif
        curl_global_cleanup();
 
-       curl_slist_free_all(extra_http_headers);
-       extra_http_headers = NULL;
+       string_list_clear(&extra_http_headers, 0);
 
        curl_slist_free_all(pragma_header);
        pragma_header = NULL;
@@ -1231,6 +1283,12 @@ void http_cleanup(void)
        }
        ssl_cert_password_required = 0;
 
+       if (proxy_cert_auth.password != NULL) {
+               memset(proxy_cert_auth.password, 0, strlen(proxy_cert_auth.password));
+               FREE_AND_NULL(proxy_cert_auth.password);
+       }
+       proxy_ssl_cert_password_required = 0;
+
        FREE_AND_NULL(cached_accept_language);
 }
 
@@ -1625,10 +1683,11 @@ int run_one_slot(struct active_request_slot *slot,
 
 struct curl_slist *http_copy_default_headers(void)
 {
-       struct curl_slist *headers = NULL, *h;
+       struct curl_slist *headers = NULL;
+       const struct string_list_item *item;
 
-       for (h = extra_http_headers; h; h = h->next)
-               headers = curl_slist_append(headers, h->data);
+       for_each_string_list_item(item, &extra_http_headers)
+               headers = curl_slist_append(headers, item->string);
 
        return headers;
 }
@@ -2202,70 +2261,74 @@ void release_http_pack_request(struct http_pack_request *preq)
 
 int finish_http_pack_request(struct http_pack_request *preq)
 {
-       struct packed_git **lst;
-       struct packed_git *p = preq->target;
-       char *tmp_idx;
-       size_t len;
        struct child_process ip = CHILD_PROCESS_INIT;
-
-       close_pack_index(p);
+       int tmpfile_fd;
+       int ret = 0;
 
        fclose(preq->packfile);
        preq->packfile = NULL;
 
-       lst = preq->lst;
-       while (*lst != p)
-               lst = &((*lst)->next);
-       *lst = (*lst)->next;
-
-       if (!strip_suffix(preq->tmpfile.buf, ".pack.temp", &len))
-               BUG("pack tmpfile does not end in .pack.temp?");
-       tmp_idx = xstrfmt("%.*s.idx.temp", (int)len, preq->tmpfile.buf);
+       tmpfile_fd = xopen(preq->tmpfile.buf, O_RDONLY);
 
        argv_array_push(&ip.args, "index-pack");
-       argv_array_pushl(&ip.args, "-o", tmp_idx, NULL);
-       argv_array_push(&ip.args, preq->tmpfile.buf);
+       argv_array_push(&ip.args, "--stdin");
        ip.git_cmd = 1;
-       ip.no_stdin = 1;
-       ip.no_stdout = 1;
+       ip.in = tmpfile_fd;
+       if (preq->generate_keep) {
+               argv_array_pushf(&ip.args, "--keep=git %"PRIuMAX,
+                                (uintmax_t)getpid());
+               ip.out = 0;
+       } else {
+               ip.no_stdout = 1;
+       }
 
        if (run_command(&ip)) {
-               unlink(preq->tmpfile.buf);
-               unlink(tmp_idx);
-               free(tmp_idx);
-               return -1;
+               ret = -1;
+               goto cleanup;
        }
 
-       unlink(sha1_pack_index_name(p->hash));
+cleanup:
+       close(tmpfile_fd);
+       unlink(preq->tmpfile.buf);
+       return ret;
+}
 
-       if (finalize_object_file(preq->tmpfile.buf, sha1_pack_name(p->hash))
-        || finalize_object_file(tmp_idx, sha1_pack_index_name(p->hash))) {
-               free(tmp_idx);
-               return -1;
-       }
+void http_install_packfile(struct packed_git *p,
+                          struct packed_git **list_to_remove_from)
+{
+       struct packed_git **lst = list_to_remove_from;
+
+       while (*lst != p)
+               lst = &((*lst)->next);
+       *lst = (*lst)->next;
 
        install_packed_git(the_repository, p);
-       free(tmp_idx);
-       return 0;
 }
 
 struct http_pack_request *new_http_pack_request(
-       struct packed_git *target, const char *base_url)
+       const unsigned char *packed_git_hash, const char *base_url) {
+
+       struct strbuf buf = STRBUF_INIT;
+
+       end_url_with_slash(&buf, base_url);
+       strbuf_addf(&buf, "objects/pack/pack-%s.pack",
+               hash_to_hex(packed_git_hash));
+       return new_direct_http_pack_request(packed_git_hash,
+                                           strbuf_detach(&buf, NULL));
+}
+
+struct http_pack_request *new_direct_http_pack_request(
+       const unsigned char *packed_git_hash, char *url)
 {
        off_t prev_posn = 0;
-       struct strbuf buf = STRBUF_INIT;
        struct http_pack_request *preq;
 
        preq = xcalloc(1, sizeof(*preq));
        strbuf_init(&preq->tmpfile, 0);
-       preq->target = target;
 
-       end_url_with_slash(&buf, base_url);
-       strbuf_addf(&buf, "objects/pack/pack-%s.pack",
-               hash_to_hex(target->hash));
-       preq->url = strbuf_detach(&buf, NULL);
+       preq->url = url;
 
-       strbuf_addf(&preq->tmpfile, "%s.temp", sha1_pack_name(target->hash));
+       strbuf_addf(&preq->tmpfile, "%s.temp", sha1_pack_name(packed_git_hash));
        preq->packfile = fopen(preq->tmpfile.buf, "a");
        if (!preq->packfile) {
                error("Unable to open local file %s for pack",
@@ -2289,7 +2352,7 @@ struct http_pack_request *new_http_pack_request(
                if (http_is_verbose)
                        fprintf(stderr,
                                "Resuming fetch of pack %s at byte %"PRIuMAX"\n",
-                               hash_to_hex(target->hash),
+                               hash_to_hex(packed_git_hash),
                                (uintmax_t)prev_posn);
                http_opt_request_remainder(preq->slot->curl, prev_posn);
        }