Skip to content

Commit ca9ccbf

Browse files
bk2204gitster
authored andcommitted
credential: gate new fields on capability
We support the new credential and authtype fields, but we lack a way to indicate to a credential helper that we'd like them to be used. Without some sort of indication, the credential helper doesn't know if it should try to provide us a username and password, or a pre-encoded credential. For example, the helper might prefer a more restricted Bearer token if pre-encoded credentials are possible, but might have to fall back to more general username and password if not. Let's provide a simple way to indicate whether Git (or, for that matter, the helper) is capable of understanding the authtype and credential fields. We send this capability when we generate a request, and the other side may reply to indicate to us that it does, too. For now, don't enable sending capabilities for the HTTP code. In a future commit, we'll introduce appropriate handling for that code, which requires more in-depth work. The logic for determining whether a capability is supported may seem complex, but it is not. At each stage, we emit the capability to the following stage if all preceding stages have declared it. Thus, if the caller to git credential fill didn't declare it, then we won't send it to the helper, and if fill's caller did send but the helper doesn't understand it, then we won't send it on in the response. If we're an internal user, then we know about all capabilities and will request them. For "git credential approve" and "git credential reject", we set the helper capability before calling the helper, since we assume that the input we're getting from the external program comes from a previous call to "git credential fill", and thus we'll invoke send a capability to the helper if and only if we got one from the standard input, which is the correct behavior. Signed-off-by: brian m. carlson <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 6a6d6fb commit ca9ccbf

File tree

9 files changed

+215
-24
lines changed

9 files changed

+215
-24
lines changed

builtin/credential-cache--daemon.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ 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+
if (credential_read(c, fh, CREDENTIAL_OP_HELPER) < 0)
119119
return -1;
120120
return 0;
121121
}

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: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,17 @@ 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 (credential_read(&c, stdin, CREDENTIAL_OP_INITIAL) < 0)
2121
die("unable to read credential from stdin");
2222

2323
if (!strcmp(op, "fill")) {
24-
credential_fill(&c);
25-
credential_write(&c, stdout);
24+
credential_fill(&c, 0);
25+
credential_write(&c, stdout, CREDENTIAL_OP_RESPONSE);
2626
} else if (!strcmp(op, "approve")) {
27+
credential_set_all_capabilities(&c, CREDENTIAL_OP_HELPER);
2728
credential_approve(&c);
2829
} else if (!strcmp(op, "reject")) {
30+
credential_set_all_capabilities(&c, CREDENTIAL_OP_HELPER);
2931
credential_reject(&c);
3032
} else {
3133
usage(usage_msg);

credential.c

Lines changed: 57 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,29 @@ void credential_clear(struct credential *c)
3434
credential_init(c);
3535
}
3636

37+
static void credential_set_capability(struct credential_capability *capa,
38+
enum credential_op_type op_type)
39+
{
40+
switch (op_type) {
41+
case CREDENTIAL_OP_INITIAL:
42+
capa->request_initial = 1;
43+
break;
44+
case CREDENTIAL_OP_HELPER:
45+
capa->request_helper = 1;
46+
break;
47+
case CREDENTIAL_OP_RESPONSE:
48+
capa->response = 1;
49+
break;
50+
}
51+
}
52+
53+
54+
void credential_set_all_capabilities(struct credential *c,
55+
enum credential_op_type op_type)
56+
{
57+
credential_set_capability(&c->capa_authtype, op_type);
58+
}
59+
3760
int credential_match(const struct credential *want,
3861
const struct credential *have, int match_password)
3962
{
@@ -210,7 +233,26 @@ static void credential_getpass(struct credential *c)
210233
PROMPT_ASKPASS);
211234
}
212235

213-
int credential_read(struct credential *c, FILE *fp)
236+
static int credential_has_capability(const struct credential_capability *capa,
237+
enum credential_op_type op_type)
238+
{
239+
/*
240+
* We're checking here if each previous step indicated that we had the
241+
* capability. If it did, then we want to pass it along; conversely, if
242+
* it did not, we don't want to report that to our caller.
243+
*/
244+
switch (op_type) {
245+
case CREDENTIAL_OP_HELPER:
246+
return capa->request_initial;
247+
case CREDENTIAL_OP_RESPONSE:
248+
return capa->request_initial && capa->request_helper;
249+
default:
250+
return 0;
251+
}
252+
}
253+
254+
int credential_read(struct credential *c, FILE *fp,
255+
enum credential_op_type op_type)
214256
{
215257
struct strbuf line = STRBUF_INIT;
216258

@@ -249,6 +291,8 @@ int credential_read(struct credential *c, FILE *fp)
249291
c->path = xstrdup(value);
250292
} else if (!strcmp(key, "wwwauth[]")) {
251293
strvec_push(&c->wwwauth_headers, value);
294+
} else if (!strcmp(key, "capability[]") && !strcmp(value, "authtype")) {
295+
credential_set_capability(&c->capa_authtype, op_type);
252296
} else if (!strcmp(key, "password_expiry_utc")) {
253297
errno = 0;
254298
c->password_expiry_utc = parse_timestamp(value, NULL, 10);
@@ -288,14 +332,19 @@ static void credential_write_item(FILE *fp, const char *key, const char *value,
288332
fprintf(fp, "%s=%s\n", key, value);
289333
}
290334

291-
void credential_write(const struct credential *c, FILE *fp)
335+
void credential_write(const struct credential *c, FILE *fp,
336+
enum credential_op_type op_type)
292337
{
338+
if (credential_has_capability(&c->capa_authtype, op_type)) {
339+
credential_write_item(fp, "capability[]", "authtype", 0);
340+
credential_write_item(fp, "authtype", c->authtype, 0);
341+
credential_write_item(fp, "credential", c->credential, 0);
342+
}
293343
credential_write_item(fp, "protocol", c->protocol, 1);
294344
credential_write_item(fp, "host", c->host, 1);
295345
credential_write_item(fp, "path", c->path, 0);
296346
credential_write_item(fp, "username", c->username, 0);
297347
credential_write_item(fp, "password", c->password, 0);
298-
credential_write_item(fp, "credential", c->credential, 0);
299348
credential_write_item(fp, "oauth_refresh_token", c->oauth_refresh_token, 0);
300349
if (c->password_expiry_utc != TIME_MAX) {
301350
char *s = xstrfmt("%"PRItime, c->password_expiry_utc);
@@ -304,7 +353,6 @@ void credential_write(const struct credential *c, FILE *fp)
304353
}
305354
for (size_t i = 0; i < c->wwwauth_headers.nr; i++)
306355
credential_write_item(fp, "wwwauth[]", c->wwwauth_headers.v[i], 0);
307-
credential_write_item(fp, "authtype", c->authtype, 0);
308356
}
309357

310358
static int run_credential_helper(struct credential *c,
@@ -327,14 +375,14 @@ static int run_credential_helper(struct credential *c,
327375

328376
fp = xfdopen(helper.in, "w");
329377
sigchain_push(SIGPIPE, SIG_IGN);
330-
credential_write(c, fp);
378+
credential_write(c, fp, want_output ? CREDENTIAL_OP_HELPER : CREDENTIAL_OP_RESPONSE);
331379
fclose(fp);
332380
sigchain_pop(SIGPIPE);
333381

334382
if (want_output) {
335383
int r;
336384
fp = xfdopen(helper.out, "r");
337-
r = credential_read(c, fp);
385+
r = credential_read(c, fp, CREDENTIAL_OP_HELPER);
338386
fclose(fp);
339387
if (r < 0) {
340388
finish_command(&helper);
@@ -367,14 +415,16 @@ static int credential_do(struct credential *c, const char *helper,
367415
return r;
368416
}
369417

370-
void credential_fill(struct credential *c)
418+
void credential_fill(struct credential *c, int all_capabilities)
371419
{
372420
int i;
373421

374422
if ((c->username && c->password) || c->credential)
375423
return;
376424

377425
credential_apply_config(c);
426+
if (all_capabilities)
427+
credential_set_all_capabilities(c, CREDENTIAL_OP_INITIAL);
378428

379429
for (i = 0; i < c->helpers.nr; i++) {
380430
credential_do(c, c->helpers.items[i].string, "get");

credential.h

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,27 @@
9393
* -----------------------------------------------------------------------
9494
*/
9595

96+
/*
97+
* These values define the kind of operation we're performing and the
98+
* capabilities at each stage. The first is either an external request (via git
99+
* credential fill) or an internal request (e.g., via the HTTP) code. The
100+
* second is the call to the credential helper, and the third is the response
101+
* we're providing.
102+
*
103+
* At each stage, we will emit the capability only if the previous stage
104+
* supported it.
105+
*/
106+
enum credential_op_type {
107+
CREDENTIAL_OP_INITIAL = 1,
108+
CREDENTIAL_OP_HELPER = 2,
109+
CREDENTIAL_OP_RESPONSE = 3,
110+
};
111+
112+
struct credential_capability {
113+
unsigned request_initial:1,
114+
request_helper:1,
115+
response:1;
116+
};
96117

97118
/**
98119
* This struct represents a single username/password combination
@@ -136,6 +157,8 @@ struct credential {
136157
use_http_path:1,
137158
username_from_proto:1;
138159

160+
struct credential_capability capa_authtype;
161+
139162
char *username;
140163
char *password;
141164
char *credential;
@@ -174,8 +197,11 @@ void credential_clear(struct credential *);
174197
* returns, the username and password fields of the credential are
175198
* guaranteed to be non-NULL. If an error occurs, the function will
176199
* die().
200+
*
201+
* If all_capabilities is set, this is an internal user that is prepared
202+
* to deal with all known capabilities, and we should advertise that fact.
177203
*/
178-
void credential_fill(struct credential *);
204+
void credential_fill(struct credential *, int all_capabilities);
179205

180206
/**
181207
* Inform the credential subsystem that the provided credentials
@@ -198,8 +224,16 @@ void credential_approve(struct credential *);
198224
*/
199225
void credential_reject(struct credential *);
200226

201-
int credential_read(struct credential *, FILE *);
202-
void credential_write(const struct credential *, FILE *);
227+
/**
228+
* Enable all of the supported credential flags in this credential.
229+
*/
230+
void credential_set_all_capabilities(struct credential *c,
231+
enum credential_op_type op_type);
232+
233+
int credential_read(struct credential *, FILE *,
234+
enum credential_op_type);
235+
void credential_write(const struct credential *, FILE *,
236+
enum credential_op_type);
203237

204238
/*
205239
* Parse a url into a credential struct, replacing any existing contents.

http.c

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -569,7 +569,7 @@ static void init_curl_http_auth(CURL *result)
569569
return;
570570
}
571571

572-
credential_fill(&http_auth);
572+
credential_fill(&http_auth, 0);
573573

574574
curl_easy_setopt(result, CURLOPT_USERNAME, http_auth.username);
575575
curl_easy_setopt(result, CURLOPT_PASSWORD, http_auth.password);
@@ -596,7 +596,7 @@ static void init_curl_proxy_auth(CURL *result)
596596
{
597597
if (proxy_auth.username) {
598598
if (!proxy_auth.password)
599-
credential_fill(&proxy_auth);
599+
credential_fill(&proxy_auth, 0);
600600
set_proxyauth_name_password(result);
601601
}
602602

@@ -630,7 +630,7 @@ static int has_cert_password(void)
630630
cert_auth.host = xstrdup("");
631631
cert_auth.username = xstrdup("");
632632
cert_auth.path = xstrdup(ssl_cert);
633-
credential_fill(&cert_auth);
633+
credential_fill(&cert_auth, 0);
634634
}
635635
return 1;
636636
}
@@ -645,7 +645,7 @@ static int has_proxy_cert_password(void)
645645
proxy_cert_auth.host = xstrdup("");
646646
proxy_cert_auth.username = xstrdup("");
647647
proxy_cert_auth.path = xstrdup(http_proxy_ssl_cert);
648-
credential_fill(&proxy_cert_auth);
648+
credential_fill(&proxy_cert_auth, 0);
649649
}
650650
return 1;
651651
}
@@ -2190,7 +2190,7 @@ static int http_request_reauth(const char *url,
21902190
BUG("Unknown http_request target");
21912191
}
21922192

2193-
credential_fill(&http_auth);
2193+
credential_fill(&http_auth, 0);
21942194

21952195
return http_request(url, result, target, options);
21962196
}

imap-send.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -944,7 +944,7 @@ static void server_fill_credential(struct imap_server_conf *srvc, struct credent
944944
cred->username = xstrdup_or_null(srvc->user);
945945
cred->password = xstrdup_or_null(srvc->pass);
946946

947-
credential_fill(cred);
947+
credential_fill(cred, 1);
948948

949949
if (!srvc->user)
950950
srvc->user = xstrdup(cred->username);

remote-curl.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -926,7 +926,7 @@ static int post_rpc(struct rpc_state *rpc, int stateless_connect, int flush_rece
926926
do {
927927
err = probe_rpc(rpc, &results);
928928
if (err == HTTP_REAUTH)
929-
credential_fill(&http_auth);
929+
credential_fill(&http_auth, 0);
930930
} while (err == HTTP_REAUTH);
931931
if (err != HTTP_OK)
932932
return -1;
@@ -1044,7 +1044,7 @@ static int post_rpc(struct rpc_state *rpc, int stateless_connect, int flush_rece
10441044
rpc->any_written = 0;
10451045
err = run_slot(slot, NULL);
10461046
if (err == HTTP_REAUTH && !large_request) {
1047-
credential_fill(&http_auth);
1047+
credential_fill(&http_auth, 0);
10481048
curl_slist_free_all(headers);
10491049
goto retry;
10501050
}

0 commit comments

Comments
 (0)