Merge branch 'bc/http-empty-auth'
authorJunio C Hamano <gitster@pobox.com>
Wed, 24 Feb 2016 21:25:57 +0000 (13:25 -0800)
committerJunio C Hamano <gitster@pobox.com>
Wed, 24 Feb 2016 21:25:57 +0000 (13:25 -0800)
Some authentication methods do not need username or password, but
libcurl needs some hint that it needs to perform authentication.
Supplying an empty username and password string is a valid way to
do so, but you can set the http.[<url>.]emptyAuth configuration
variable to achieve the same, if you find it cleaner.

* bc/http-empty-auth:
http: add option to try authentication without username

1  2 
Documentation/config.txt
http.c
diff --combined Documentation/config.txt
index 01cca0a70177091f4580a06ea53e498d3e5be4e4,d9abfbb4277ac93b5697ccd2e8299c8c5d17ae66..542cb229c86415aeb338814c2caf4e746d3c33f5
@@@ -308,15 -308,6 +308,15 @@@ core.trustctime:
        crawlers and some backup systems).
        See linkgit:git-update-index[1]. True by default.
  
 +core.untrackedCache::
 +      Determines what to do about the untracked cache feature of the
 +      index. It will be kept, if this variable is unset or set to
 +      `keep`. It will automatically be added if set to `true`. And
 +      it will automatically be removed, if set to `false`. Before
 +      setting it to `true`, you should check that mtime is working
 +      properly on your system.
 +      See linkgit:git-update-index[1]. `keep` by default.
 +
  core.checkStat::
        Determines which stat fields to match between the index
        and work tree. The user can set this to 'default' or
@@@ -879,8 -870,6 +879,8 @@@ When preserve, also pass `--preserve-me
  so that locally committed merge commits will not be flattened
  by running 'git pull'.
  +
 +When the value is `interactive`, the rebase is run in interactive mode.
 ++
  *NOTE*: this is a possibly dangerous operation; do *not* use
  it unless you understand the implications (see linkgit:git-rebase[1]
  for details).
@@@ -1254,10 -1243,6 +1254,10 @@@ format.coverLetter:
        format-patch is invoked, but in addition can be set to "auto", to
        generate a cover-letter only when there's more than one patch.
  
 +format.outputDirectory::
 +      Set a custom directory to store the resulting files instead of the
 +      current working directory.
 +
  filter.<driver>.clean::
        The command which is used to convert the content of a worktree
        file to a blob upon checkin.  See linkgit:gitattributes[5] for
@@@ -1465,14 -1450,6 +1465,14 @@@ grep.extendedRegexp:
        option is ignored when the 'grep.patternType' option is set to a value
        other than 'default'.
  
 +grep.threads::
 +      Number of grep worker threads to use.
 +      See `grep.threads` in linkgit:git-grep[1] for more information.
 +
 +grep.fallbackToNoIndex::
 +      If set to true, fall back to git grep --no-index if git grep
 +      is executed outside of a git repository.  Defaults to false.
 +
  gpg.program::
        Use this custom program instead of "gpg" found on $PATH when
        making or verifying a PGP signature. The program must support the
@@@ -1619,35 -1596,16 +1619,41 @@@ help.htmlPath:
  
  http.proxy::
        Override the HTTP proxy, normally configured using the 'http_proxy',
 -      'https_proxy', and 'all_proxy' environment variables (see
 -      `curl(1)`).  This can be overridden on a per-remote basis; see
 -      remote.<name>.proxy
 +      'https_proxy', and 'all_proxy' environment variables (see `curl(1)`). In
 +      addition to the syntax understood by curl, it is possible to specify a
 +      proxy string with a user name but no password, in which case git will
 +      attempt to acquire one in the same way it does for other credentials. See
 +      linkgit:gitcredentials[7] for more information. The syntax thus is
 +      '[protocol://][user[:password]@]proxyhost[:port]'. This can be overridden
 +      on a per-remote basis; see remote.<name>.proxy
 +
 +http.proxyAuthMethod::
 +      Set the method with which to authenticate against the HTTP proxy. This
 +      only takes effect if the configured proxy string contains a user name part
 +      (i.e. is of the form 'user@host' or 'user@host:port'). This can be
 +      overridden on a per-remote basis; see `remote.<name>.proxyAuthMethod`.
 +      Both can be overridden by the 'GIT_HTTP_PROXY_AUTHMETHOD' environment
 +      variable.  Possible values are:
 ++
 +--
 +* `anyauth` - Automatically pick a suitable authentication method. It is
 +  assumed that the proxy answers an unauthenticated request with a 407
 +  status code and one or more Proxy-authenticate headers with supported
 +  authentication methods. This is the default.
 +* `basic` - HTTP Basic authentication
 +* `digest` - HTTP Digest authentication; this prevents the password from being
 +  transmitted to the proxy in clear text
 +* `negotiate` - GSS-Negotiate authentication (compare the --negotiate option
 +  of `curl(1)`)
 +* `ntlm` - NTLM authentication (compare the --ntlm option of `curl(1)`)
 +--
  
+ http.emptyAuth::
+       Attempt authentication without seeking a username or password.  This
+       can be used to attempt GSS-Negotiate authentication without specifying
+       a username in the URL, as libcurl normally requires a username for
+       authentication.
  http.cookieFile::
        File containing previously stored cookie lines which should be used
        in the Git http session, if they match the server. The file format
@@@ -2122,7 -2080,7 +2128,7 @@@ pack.indexVersion:
        larger than 2 GB.
  +
  If you have an old Git that does not understand the version 2 `*.idx` file,
 -cloning or fetching over a non native protocol (e.g. "http" and "rsync")
 +cloning or fetching over a non native protocol (e.g. "http")
  that will copy both `*.pack` file and corresponding `*.idx` file from the
  other side may give you a repository that cannot be accessed with your
  older version of Git. If the `*.pack` file is smaller than 2 GB, however,
@@@ -2197,8 -2155,6 +2203,8 @@@ When preserve, also pass `--preserve-me
  so that locally committed merge commits will not be flattened
  by running 'git pull'.
  +
 +When the value is `interactive`, the rebase is run in interactive mode.
 ++
  *NOTE*: this is a possibly dangerous operation; do *not* use
  it unless you understand the implications (see linkgit:git-rebase[1]
  for details).
@@@ -2457,11 -2413,6 +2463,11 @@@ remote.<name>.proxy:
        the proxy to use for that remote.  Set to the empty string to
        disable proxying for that remote.
  
 +remote.<name>.proxyAuthMethod::
 +      For remotes that require curl (http, https and ftp), the method to use for
 +      authenticating against the proxy in use (probably set in
 +      `remote.<name>.proxy`). See `http.proxyAuthMethod`.
 +
  remote.<name>.fetch::
        The default set of "refspec" for linkgit:git-fetch[1]. See
        linkgit:git-fetch[1].
@@@ -2830,16 -2781,6 +2836,16 @@@ user.name:
        Can be overridden by the 'GIT_AUTHOR_NAME' and 'GIT_COMMITTER_NAME'
        environment variables.  See linkgit:git-commit-tree[1].
  
 +user.useConfigOnly::
 +      Instruct Git to avoid trying to guess defaults for 'user.email'
 +      and 'user.name', and instead retrieve the values only from the
 +      configuration. For example, if you have multiple email addresses
 +      and would like to use a different one for each repository, then
 +      with this configuration option set to `true` in the global config
 +      along with a name, Git will prompt you to set up an email before
 +      making new commits in a newly cloned repository.
 +      Defaults to `false`.
 +
  user.signingKey::
        If linkgit:git-tag[1] or linkgit:git-commit[1] is not selecting the
        key you want it to automatically when creating a signed tag or
diff --combined http.c
index f543b142781644900721cd16492faf276e020645,fe494ab36c6828ff2a6123b087877a3884fdb61c..279b6f2e9a35d69c6ce36416fd00662e4e3511b0
--- 1/http.c
--- 2/http.c
+++ b/http.c
  #include "gettext.h"
  #include "transport.h"
  
 +#if LIBCURL_VERSION_NUM >= 0x070a08
 +long int git_curl_ipresolve = CURL_IPRESOLVE_WHATEVER;
 +#else
 +long int git_curl_ipresolve;
 +#endif
  int active_requests;
  int http_is_verbose;
  size_t http_post_buffer = 16 * LARGE_PACKET_MAX;
@@@ -67,31 -62,12 +67,32 @@@ static long curl_low_speed_limit = -1
  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 struct {
 +      const char *name;
 +      long curlauth_param;
 +} proxy_authmethods[] = {
 +      { "basic", CURLAUTH_BASIC },
 +      { "digest", CURLAUTH_DIGEST },
 +      { "negotiate", CURLAUTH_GSSNEGOTIATE },
 +      { "ntlm", CURLAUTH_NTLM },
 +#ifdef LIBCURL_CAN_HANDLE_AUTH_ANY
 +      { "anyauth", CURLAUTH_ANY },
 +#endif
 +      /*
 +       * CURLAUTH_DIGEST_IE has no corresponding command-line option in
 +       * curl(1) and is not included in CURLAUTH_ANY, so we leave it out
 +       * here, too
 +       */
 +};
 +static struct credential proxy_auth = CREDENTIAL_INIT;
 +static const char *curl_proxyuserpwd;
  static const char *curl_cookie_file;
  static int curl_save_cookies;
  struct credential http_auth = CREDENTIAL_INIT;
  static int http_proactive_auth;
  static const char *user_agent;
+ static int curl_empty_auth;
  
  #if LIBCURL_VERSION_NUM >= 0x071700
  /* Use CURLOPT_KEYPASSWD as is */
@@@ -184,9 -160,6 +185,9 @@@ static void finish_active_slot(struct a
  #else
                slot->results->auth_avail = 0;
  #endif
 +
 +              curl_easy_getinfo(slot->curl, CURLINFO_HTTP_CONNECTCODE,
 +                      &slot->results->http_connectcode);
        }
  
        /* Run callback if appropriate */
@@@ -284,9 -257,6 +285,9 @@@ static int http_options(const char *var
        if (!strcmp("http.proxy", var))
                return git_config_string(&curl_http_proxy, var, value);
  
 +      if (!strcmp("http.proxyauthmethod", var))
 +              return git_config_string(&http_proxy_authmethod, var, value);
 +
        if (!strcmp("http.cookiefile", var))
                return git_config_string(&curl_cookie_file, var, value);
        if (!strcmp("http.savecookies", var)) {
        if (!strcmp("http.useragent", var))
                return git_config_string(&user_agent, var, value);
  
+       if (!strcmp("http.emptyauth", var)) {
+               curl_empty_auth = git_config_bool(var, value);
+               return 0;
+       }
        /* Fall back on the default ones */
        return git_default_config(var, value, cb);
  }
  
  static void init_curl_http_auth(CURL *result)
  {
-       if (!http_auth.username)
+       if (!http_auth.username) {
+               if (curl_empty_auth)
+                       curl_easy_setopt(result, CURLOPT_USERPWD, ":");
                return;
+       }
  
        credential_fill(&http_auth);
  
  #endif
  }
  
 +/* *var must be free-able */
 +static void var_override(const char **var, char *value)
 +{
 +      if (value) {
 +              free((void *)*var);
 +              *var = xstrdup(value);
 +      }
 +}
 +
 +static void set_proxyauth_name_password(CURL *result)
 +{
 +#if LIBCURL_VERSION_NUM >= 0x071301
 +              curl_easy_setopt(result, CURLOPT_PROXYUSERNAME,
 +                      proxy_auth.username);
 +              curl_easy_setopt(result, CURLOPT_PROXYPASSWORD,
 +                      proxy_auth.password);
 +#else
 +              struct strbuf s = STRBUF_INIT;
 +
 +              strbuf_addstr_urlencode(&s, proxy_auth.username, 1);
 +              strbuf_addch(&s, ':');
 +              strbuf_addstr_urlencode(&s, proxy_auth.password, 1);
 +              curl_proxyuserpwd = strbuf_detach(&s, NULL);
 +              curl_easy_setopt(result, CURLOPT_PROXYUSERPWD, curl_proxyuserpwd);
 +#endif
 +}
 +
 +static void init_curl_proxy_auth(CURL *result)
 +{
 +      if (proxy_auth.username) {
 +              if (!proxy_auth.password)
 +                      credential_fill(&proxy_auth);
 +              set_proxyauth_name_password(result);
 +      }
 +
 +      var_override(&http_proxy_authmethod, getenv("GIT_HTTP_PROXY_AUTHMETHOD"));
 +
 +#if LIBCURL_VERSION_NUM >= 0x070a07 /* CURLOPT_PROXYAUTH and CURLAUTH_ANY */
 +      if (http_proxy_authmethod) {
 +              int i;
 +              for (i = 0; i < ARRAY_SIZE(proxy_authmethods); i++) {
 +                      if (!strcmp(http_proxy_authmethod, proxy_authmethods[i].name)) {
 +                              curl_easy_setopt(result, CURLOPT_PROXYAUTH,
 +                                              proxy_authmethods[i].curlauth_param);
 +                              break;
 +                      }
 +              }
 +              if (i == ARRAY_SIZE(proxy_authmethods)) {
 +                      warning("unsupported proxy authentication method %s: using anyauth",
 +                                      http_proxy_authmethod);
 +                      curl_easy_setopt(result, CURLOPT_PROXYAUTH, CURLAUTH_ANY);
 +              }
 +      }
 +      else
 +              curl_easy_setopt(result, CURLOPT_PROXYAUTH, CURLAUTH_ANY);
 +#endif
 +}
 +
  static int has_cert_password(void)
  {
        if (ssl_cert == NULL || ssl_cert_password_required != 1)
@@@ -551,31 -471,6 +560,31 @@@ static CURL *get_curl_handle(void
                curl_easy_setopt(result, CURLOPT_USE_SSL, CURLUSESSL_TRY);
  #endif
  
 +      /*
 +       * CURL also examines these variables as a fallback; but we need to query
 +       * them here in order to decide whether to prompt for missing password (cf.
 +       * init_curl_proxy_auth()).
 +       *
 +       * Unlike many other common environment variables, these are historically
 +       * lowercase only. It appears that CURL did not know this and implemented
 +       * only uppercase variants, which was later corrected to take both - with
 +       * the exception of http_proxy, which is lowercase only also in CURL. As
 +       * the lowercase versions are the historical quasi-standard, they take
 +       * precedence here, as in CURL.
 +       */
 +      if (!curl_http_proxy) {
 +              if (!strcmp(http_auth.protocol, "https")) {
 +                      var_override(&curl_http_proxy, getenv("HTTPS_PROXY"));
 +                      var_override(&curl_http_proxy, getenv("https_proxy"));
 +              } else {
 +                      var_override(&curl_http_proxy, getenv("http_proxy"));
 +              }
 +              if (!curl_http_proxy) {
 +                      var_override(&curl_http_proxy, getenv("ALL_PROXY"));
 +                      var_override(&curl_http_proxy, getenv("all_proxy"));
 +              }
 +      }
 +
        if (curl_http_proxy) {
                curl_easy_setopt(result, CURLOPT_PROXY, curl_http_proxy);
  #if LIBCURL_VERSION_NUM >= 0x071800
                        curl_easy_setopt(result,
                                CURLOPT_PROXYTYPE, CURLPROXY_SOCKS4);
  #endif
 +              if (strstr(curl_http_proxy, "://"))
 +                      credential_from_url(&proxy_auth, curl_http_proxy);
 +              else {
 +                      struct strbuf url = STRBUF_INIT;
 +                      strbuf_addf(&url, "http://%s", curl_http_proxy);
 +                      credential_from_url(&proxy_auth, url.buf);
 +                      strbuf_release(&url);
 +              }
 +
 +              curl_easy_setopt(result, CURLOPT_PROXY, proxy_auth.host);
        }
 -#if LIBCURL_VERSION_NUM >= 0x070a07
 -      curl_easy_setopt(result, CURLOPT_PROXYAUTH, CURLAUTH_ANY);
 -#endif
 +      init_curl_proxy_auth(result);
  
        set_curl_keepalive(result);
  
@@@ -641,9 -528,6 +650,9 @@@ void http_init(struct remote *remote, c
        if (remote && remote->http_proxy)
                curl_http_proxy = xstrdup(remote->http_proxy);
  
 +      if (remote)
 +              var_override(&http_proxy_authmethod, remote->http_proxy_authmethod);
 +
        pragma_header = curl_slist_append(pragma_header, "Pragma: no-cache");
        no_pragma_header = curl_slist_append(no_pragma_header, "Pragma:");
  
@@@ -742,18 -626,6 +751,18 @@@ void http_cleanup(void
                curl_http_proxy = NULL;
        }
  
 +      if (proxy_auth.password) {
 +              memset(proxy_auth.password, 0, strlen(proxy_auth.password));
 +              free(proxy_auth.password);
 +              proxy_auth.password = NULL;
 +      }
 +
 +      free((void *)curl_proxyuserpwd);
 +      curl_proxyuserpwd = NULL;
 +
 +      free((void *)http_proxy_authmethod);
 +      http_proxy_authmethod = NULL;
 +
        if (cert_auth.password != NULL) {
                memset(cert_auth.password, 0, strlen(cert_auth.password));
                free(cert_auth.password);
@@@ -829,14 -701,10 +838,14 @@@ struct active_request_slot *get_active_
        curl_easy_setopt(slot->curl, CURLOPT_HTTPGET, 1);
        curl_easy_setopt(slot->curl, CURLOPT_FAILONERROR, 1);
        curl_easy_setopt(slot->curl, CURLOPT_RANGE, NULL);
 +
 +#if LIBCURL_VERSION_NUM >= 0x070a08
 +      curl_easy_setopt(slot->curl, CURLOPT_IPRESOLVE, git_curl_ipresolve);
 +#endif
  #ifdef LIBCURL_CAN_HANDLE_AUTH_ANY
        curl_easy_setopt(slot->curl, CURLOPT_HTTPAUTH, http_auth_methods);
  #endif
-       if (http_auth.password)
+       if (http_auth.password || curl_empty_auth)
                init_curl_http_auth(slot->curl);
  
        return slot;
@@@ -1087,8 -955,6 +1096,8 @@@ static int handle_curl_result(struct sl
  
        if (results->curl_result == CURLE_OK) {
                credential_approve(&http_auth);
 +              if (proxy_auth.password)
 +                      credential_approve(&proxy_auth);
                return HTTP_OK;
        } else if (missing_target(results))
                return HTTP_MISSING_TARGET;
                        return HTTP_REAUTH;
                }
        } else {
 +              if (results->http_connectcode == 407)
 +                      credential_reject(&proxy_auth);
  #if LIBCURL_VERSION_NUM >= 0x070c00
                if (!curl_errorstr[0])
                        strlcpy(curl_errorstr,