Merge branch 'mc/credential-helper-www-authenticate'

Allow information carried on the WWW-AUthenticate header to be
passed to the credential helpers.

* mc/credential-helper-www-authenticate:
  credential: add WWW-Authenticate header to cred requests
  http: read HTTP WWW-Authenticate response headers
  t5563: add tests for basic and anoymous HTTP access
This commit is contained in:
Junio C Hamano
2023-03-17 14:03:10 -07:00
9 changed files with 538 additions and 1 deletions

111
http.c
View File

@ -182,6 +182,115 @@ size_t fwrite_buffer(char *ptr, size_t eltsize, size_t nmemb, void *buffer_)
return nmemb;
}
/*
* A folded header continuation line starts with any number of spaces or
* horizontal tab characters (SP or HTAB) as per RFC 7230 section 3.2.
* It is not a continuation line if the line starts with any other character.
*/
static inline int is_hdr_continuation(const char *ptr, const size_t size)
{
return size && (*ptr == ' ' || *ptr == '\t');
}
static size_t fwrite_wwwauth(char *ptr, size_t eltsize, size_t nmemb, void *p)
{
size_t size = eltsize * nmemb;
struct strvec *values = &http_auth.wwwauth_headers;
struct strbuf buf = STRBUF_INIT;
const char *val;
size_t val_len;
/*
* Header lines may not come NULL-terminated from libcurl so we must
* limit all scans to the maximum length of the header line, or leverage
* strbufs for all operations.
*
* In addition, it is possible that header values can be split over
* multiple lines as per RFC 7230. 'Line folding' has been deprecated
* but older servers may still emit them. A continuation header field
* value is identified as starting with a space or horizontal tab.
*
* The formal definition of a header field as given in RFC 7230 is:
*
* header-field = field-name ":" OWS field-value OWS
*
* field-name = token
* field-value = *( field-content / obs-fold )
* field-content = field-vchar [ 1*( SP / HTAB ) field-vchar ]
* field-vchar = VCHAR / obs-text
*
* obs-fold = CRLF 1*( SP / HTAB )
* ; obsolete line folding
* ; see Section 3.2.4
*/
/* Start of a new WWW-Authenticate header */
if (skip_iprefix_mem(ptr, size, "www-authenticate:", &val, &val_len)) {
strbuf_add(&buf, val, val_len);
/*
* Strip the CRLF that should be present at the end of each
* field as well as any trailing or leading whitespace from the
* value.
*/
strbuf_trim(&buf);
strvec_push(values, buf.buf);
http_auth.header_is_last_match = 1;
goto exit;
}
/*
* This line could be a continuation of the previously matched header
* field. If this is the case then we should append this value to the
* end of the previously consumed value.
*/
if (http_auth.header_is_last_match && is_hdr_continuation(ptr, size)) {
/*
* Trim the CRLF and any leading or trailing from this line.
*/
strbuf_add(&buf, ptr, size);
strbuf_trim(&buf);
/*
* At this point we should always have at least one existing
* value, even if it is empty. Do not bother appending the new
* value if this continuation header is itself empty.
*/
if (!values->nr) {
BUG("should have at least one existing header value");
} else if (buf.len) {
char *prev = xstrdup(values->v[values->nr - 1]);
/* Join two non-empty values with a single space. */
const char *const sp = *prev ? " " : "";
strvec_pop(values);
strvec_pushf(values, "%s%s%s", prev, sp, buf.buf);
free(prev);
}
goto exit;
}
/* Not a continuation of a previously matched auth header line. */
http_auth.header_is_last_match = 0;
/*
* If this is a HTTP status line and not a header field, this signals
* a different HTTP response. libcurl writes all the output of all
* response headers of all responses, including redirects.
* We only care about the last HTTP request response's headers so clear
* the existing array.
*/
if (skip_iprefix_mem(ptr, size, "http/", &val, &val_len))
strvec_clear(values);
exit:
strbuf_release(&buf);
return size;
}
size_t fwrite_null(char *ptr, size_t eltsize, size_t nmemb, void *strbuf)
{
return nmemb;
@ -1896,6 +2005,8 @@ static int http_request(const char *url,
fwrite_buffer);
}
curl_easy_setopt(slot->curl, CURLOPT_HEADERFUNCTION, fwrite_wwwauth);
accept_language = http_get_accept_language_header();
if (accept_language)