Skip to content

Commit c5c9acf

Browse files
committed
Merge branch 'bc/credential-scheme-enhancement'
The credential helper protocol, together with the HTTP layer, have been enhanced to support authentication schemes different from username & password pair, like Bearer and NTLM. * bc/credential-scheme-enhancement: credential: add method for querying capabilities credential-cache: implement authtype capability t: add credential tests for authtype credential: add support for multistage credential rounds t5563: refactor for multi-stage authentication docs: set a limit on credential line length credential: enable state capability credential: add an argument to keep state http: add support for authtype and credential docs: indicate new credential protocol fields credential: add a field called "ephemeral" credential: gate new fields on capability credential: add a field for pre-encoded credentials http: use new headers for each object request remote-curl: reset headers on new request credential: add an authtype field
2 parents d25ad94 + ffff4ac commit c5c9acf

16 files changed

+1025
-120
lines changed

Documentation/git-credential.txt

Lines changed: 101 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ git-credential - Retrieve and store user credentials
88
SYNOPSIS
99
--------
1010
------------------
11-
'git credential' (fill|approve|reject)
11+
'git credential' (fill|approve|reject|capability)
1212
------------------
1313

1414
DESCRIPTION
@@ -41,6 +41,9 @@ If the action is `reject`, git-credential will send the description to
4141
any configured credential helpers, which may erase any stored
4242
credentials matching the description.
4343

44+
If the action is `capability`, git-credential will announce any capabilities
45+
it supports to standard output.
46+
4447
If the action is `approve` or `reject`, no output should be emitted.
4548

4649
TYPICAL USE OF GIT CREDENTIAL
@@ -111,7 +114,9 @@ attribute per line. Each attribute is specified by a key-value pair,
111114
separated by an `=` (equals) sign, followed by a newline.
112115

113116
The key may contain any bytes except `=`, newline, or NUL. The value may
114-
contain any bytes except newline or NUL.
117+
contain any bytes except newline or NUL. A line, including the trailing
118+
newline, may not exceed 65535 bytes in order to allow implementations to
119+
parse efficiently.
115120

116121
Attributes with keys that end with C-style array brackets `[]` can have
117122
multiple values. Each instance of a multi-valued attribute forms an
@@ -178,6 +183,61 @@ empty string.
178183
Components which are missing from the URL (e.g., there is no
179184
username in the example above) will be left unset.
180185

186+
`authtype`::
187+
This indicates that the authentication scheme in question should be used.
188+
Common values for HTTP and HTTPS include `basic`, `bearer`, and `digest`,
189+
although the latter is insecure and should not be used. If `credential`
190+
is used, this may be set to an arbitrary string suitable for the protocol in
191+
question (usually HTTP).
192+
+
193+
This value should not be sent unless the appropriate capability (see below) is
194+
provided on input.
195+
196+
`credential`::
197+
The pre-encoded credential, suitable for the protocol in question (usually
198+
HTTP). If this key is sent, `authtype` is mandatory, and `username` and
199+
`password` are not used. For HTTP, Git concatenates the `authtype` value and
200+
this value with a single space to determine the `Authorization` header.
201+
+
202+
This value should not be sent unless the appropriate capability (see below) is
203+
provided on input.
204+
205+
`ephemeral`::
206+
This boolean value indicates, if true, that the value in the `credential`
207+
field should not be saved by the credential helper because its usefulness is
208+
limited in time. For example, an HTTP Digest `credential` value is computed
209+
using a nonce and reusing it will not result in successful authentication.
210+
This may also be used for situations with short duration (e.g., 24-hour)
211+
credentials. The default value is false.
212+
+
213+
The credential helper will still be invoked with `store` or `erase` so that it
214+
can determine whether the operation was successful.
215+
+
216+
This value should not be sent unless the appropriate capability (see below) is
217+
provided on input.
218+
219+
`state[]`::
220+
This value provides an opaque state that will be passed back to this helper
221+
if it is called again. Each different credential helper may specify this
222+
once. The value should include a prefix unique to the credential helper and
223+
should ignore values that don't match its prefix.
224+
+
225+
This value should not be sent unless the appropriate capability (see below) is
226+
provided on input.
227+
228+
`continue`::
229+
This is a boolean value, which, if enabled, indicates that this
230+
authentication is a non-final part of a multistage authentication step. This
231+
is common in protocols such as NTLM and Kerberos, where two rounds of client
232+
authentication are required, and setting this flag allows the credential
233+
helper to implement the multistage authentication step. This flag should
234+
only be sent if a further stage is required; that is, if another round of
235+
authentication is expected.
236+
+
237+
This value should not be sent unless the appropriate capability (see below) is
238+
provided on input. This attribute is 'one-way' from a credential helper to
239+
pass information to Git (or other programs invoking `git credential`).
240+
181241
`wwwauth[]`::
182242

183243
When an HTTP response is received by Git that includes one or more
@@ -189,7 +249,45 @@ attribute 'wwwauth[]', where the order of the attributes is the same as
189249
they appear in the HTTP response. This attribute is 'one-way' from Git
190250
to pass additional information to credential helpers.
191251

192-
Unrecognised attributes are silently discarded.
252+
`capability[]`::
253+
This signals that Git, or the helper, as appropriate, supports the capability
254+
in question. This can be used to provide better, more specific data as part
255+
of the protocol. A `capability[]` directive must precede any value depending
256+
on it and these directives _should_ be the first item announced in the
257+
protocol.
258+
+
259+
There are two currently supported capabilities. The first is `authtype`, which
260+
indicates that the `authtype`, `credential`, and `ephemeral` values are
261+
understood. The second is `state`, which indicates that the `state[]` and
262+
`continue` values are understood.
263+
+
264+
It is not obligatory to use the additional features just because the capability
265+
is supported, but they should not be provided without the capability.
266+
267+
Unrecognised attributes and capabilities are silently discarded.
268+
269+
[[CAPA-IOFMT]]
270+
CAPABILITY INPUT/OUTPUT FORMAT
271+
------------------------------
272+
273+
For `git credential capability`, the format is slightly different. First, a
274+
`version 0` announcement is made to indicate the current version of the
275+
protocol, and then each capability is announced with a line like `capability
276+
authtype`. Credential helpers may also implement this format, again with the
277+
`capability` argument. Additional lines may be added in the future; callers
278+
should ignore lines which they don't understand.
279+
280+
Because this is a new part of the credential helper protocol, older versions of
281+
Git, as well as some credential helpers, may not support it. If a non-zero
282+
exit status is received, or if the first line doesn't start with the word
283+
`version` and a space, callers should assume that no capabilities are supported.
284+
285+
The intention of this format is to differentiate it from the credential output
286+
in an unambiguous way. It is possible to use very simple credential helpers
287+
(e.g., inline shell scripts) which always produce identical output. Using a
288+
distinct format allows users to continue to use this syntax without having to
289+
worry about correctly implementing capability advertisements or accidentally
290+
confusing callers querying for capabilities.
193291

194292
GIT
195293
---

builtin/credential-cache--daemon.c

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,9 @@ static int read_request(FILE *fh, struct credential *c,
115115
return error("client sent bogus timeout line: %s", item.buf);
116116
*timeout = atoi(p);
117117

118-
if (credential_read(c, fh) < 0)
118+
credential_set_all_capabilities(c, CREDENTIAL_OP_INITIAL);
119+
120+
if (credential_read(c, fh, CREDENTIAL_OP_HELPER) < 0)
119121
return -1;
120122
return 0;
121123
}
@@ -131,8 +133,18 @@ static void serve_one_client(FILE *in, FILE *out)
131133
else if (!strcmp(action.buf, "get")) {
132134
struct credential_cache_entry *e = lookup_credential(&c);
133135
if (e) {
134-
fprintf(out, "username=%s\n", e->item.username);
135-
fprintf(out, "password=%s\n", e->item.password);
136+
e->item.capa_authtype.request_initial = 1;
137+
e->item.capa_authtype.request_helper = 1;
138+
139+
fprintf(out, "capability[]=authtype\n");
140+
if (e->item.username)
141+
fprintf(out, "username=%s\n", e->item.username);
142+
if (e->item.password)
143+
fprintf(out, "password=%s\n", e->item.password);
144+
if (credential_has_capability(&c.capa_authtype, CREDENTIAL_OP_HELPER) && e->item.authtype)
145+
fprintf(out, "authtype=%s\n", e->item.authtype);
146+
if (credential_has_capability(&c.capa_authtype, CREDENTIAL_OP_HELPER) && e->item.credential)
147+
fprintf(out, "credential=%s\n", e->item.credential);
136148
if (e->item.password_expiry_utc != TIME_MAX)
137149
fprintf(out, "password_expiry_utc=%"PRItime"\n",
138150
e->item.password_expiry_utc);
@@ -157,8 +169,10 @@ static void serve_one_client(FILE *in, FILE *out)
157169
else if (!strcmp(action.buf, "store")) {
158170
if (timeout < 0)
159171
warning("cache client didn't specify a timeout");
160-
else if (!c.username || !c.password)
172+
else if ((!c.username || !c.password) && (!c.authtype && !c.credential))
161173
warning("cache client gave us a partial credential");
174+
else if (c.ephemeral)
175+
warning("not storing ephemeral credential");
162176
else {
163177
remove_credential(&c, 0);
164178
cache_credential(&c, timeout);

builtin/credential-cache.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#include "builtin.h"
2+
#include "credential.h"
23
#include "gettext.h"
34
#include "parse-options.h"
45
#include "path.h"
@@ -127,6 +128,13 @@ static char *get_socket_path(void)
127128
return socket;
128129
}
129130

131+
static void announce_capabilities(void)
132+
{
133+
struct credential c = CREDENTIAL_INIT;
134+
c.capa_authtype.request_initial = 1;
135+
credential_announce_capabilities(&c, stdout);
136+
}
137+
130138
int cmd_credential_cache(int argc, const char **argv, const char *prefix)
131139
{
132140
char *socket_path = NULL;
@@ -163,6 +171,8 @@ int cmd_credential_cache(int argc, const char **argv, const char *prefix)
163171
do_cache(socket_path, op, timeout, FLAG_RELAY);
164172
else if (!strcmp(op, "store"))
165173
do_cache(socket_path, op, timeout, FLAG_RELAY|FLAG_SPAWN);
174+
else if (!strcmp(op, "capability"))
175+
announce_capabilities();
166176
else
167177
; /* ignore unknown operation */
168178

builtin/credential-store.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,7 @@ int cmd_credential_store(int argc, const char **argv, const char *prefix)
205205
if (!fns.nr)
206206
die("unable to set up default path; use --file");
207207

208-
if (credential_read(&c, stdin) < 0)
208+
if (credential_read(&c, stdin, CREDENTIAL_OP_HELPER) < 0)
209209
die("unable to read credential");
210210

211211
if (!strcmp(op, "get"))

builtin/credential.c

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,24 @@ int cmd_credential(int argc, const char **argv, const char *prefix UNUSED)
1717
usage(usage_msg);
1818
op = argv[1];
1919

20-
if (credential_read(&c, stdin) < 0)
20+
if (!strcmp(op, "capability")) {
21+
credential_set_all_capabilities(&c, CREDENTIAL_OP_INITIAL);
22+
credential_announce_capabilities(&c, stdout);
23+
return 0;
24+
}
25+
26+
if (credential_read(&c, stdin, CREDENTIAL_OP_INITIAL) < 0)
2127
die("unable to read credential from stdin");
2228

2329
if (!strcmp(op, "fill")) {
24-
credential_fill(&c);
25-
credential_write(&c, stdout);
30+
credential_fill(&c, 0);
31+
credential_next_state(&c);
32+
credential_write(&c, stdout, CREDENTIAL_OP_RESPONSE);
2633
} else if (!strcmp(op, "approve")) {
34+
credential_set_all_capabilities(&c, CREDENTIAL_OP_HELPER);
2735
credential_approve(&c);
2836
} else if (!strcmp(op, "reject")) {
37+
credential_set_all_capabilities(&c, CREDENTIAL_OP_HELPER);
2938
credential_reject(&c);
3039
} else {
3140
usage(usage_msg);

0 commit comments

Comments
 (0)