2011-12-10 18:31:11 +08:00
|
|
|
#ifndef CREDENTIAL_H
|
|
|
|
#define CREDENTIAL_H
|
|
|
|
|
|
|
|
#include "string-list.h"
|
2023-02-28 01:20:19 +08:00
|
|
|
#include "strvec.h"
|
2011-12-10 18:31:11 +08:00
|
|
|
|
2019-11-18 05:04:53 +08:00
|
|
|
/**
|
credential: clear expired c->credential, unify secret clearing
When a struct credential expires, credential_fill() clears c->password
so that clients don't try to use it later. However, a struct cred that
uses an alternate authtype won't have a password, but might have a
credential stored in c->credential.
This is a problem, for example, when an OAuth2 bearer token is used. In
the system I'm using, the OAuth2 configuration generates and caches a
bearer token that is valid for an hour. After the token expires, git
needs to call back into the credential helper to use a stored refresh
token to get a new bearer token. But if c->credential is still non-NULL,
git will instead try to use the expired token and fail with an error:
fatal: Authentication failed for 'https://<oauth2-enabled-server>/repository'
And on the server:
[auth_openidc:error] [client <ip>:34012] oidc_proto_validate_exp: "exp" validation failure (1717522989): JWT expired 224 seconds ago
Fix this by clearing both c->password and c->credential for an expired
struct credential. While we're at it, use credential_clear_secrets()
wherever both c->password and c->credential are being cleared.
Update comments in credential.h to mention the new struct fields.
Signed-off-by: Aaron Plattner <aplattner@nvidia.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2024-06-07 02:35:16 +08:00
|
|
|
* The credentials API provides an abstracted way of gathering
|
|
|
|
* authentication credentials from the user.
|
2019-11-18 05:04:53 +08:00
|
|
|
*
|
|
|
|
* Typical setup
|
|
|
|
* -------------
|
|
|
|
*
|
|
|
|
* ------------
|
|
|
|
* +-----------------------+
|
|
|
|
* | Git code (C) |--- to server requiring --->
|
|
|
|
* | | authentication
|
|
|
|
* |.......................|
|
|
|
|
* | C credential API |--- prompt ---> User
|
|
|
|
* +-----------------------+
|
|
|
|
* ^ |
|
|
|
|
* | pipe |
|
|
|
|
* | v
|
|
|
|
* +-----------------------+
|
|
|
|
* | Git credential helper |
|
|
|
|
* +-----------------------+
|
|
|
|
* ------------
|
|
|
|
*
|
|
|
|
* The Git code (typically a remote-helper) will call the C API to obtain
|
|
|
|
* credential data like a login/password pair (credential_fill). The
|
|
|
|
* API will itself call a remote helper (e.g. "git credential-cache" or
|
|
|
|
* "git credential-store") that may retrieve credential data from a
|
|
|
|
* store. If the credential helper cannot find the information, the C API
|
|
|
|
* will prompt the user. Then, the caller of the API takes care of
|
|
|
|
* contacting the server, and does the actual authentication.
|
|
|
|
*
|
|
|
|
* C API
|
|
|
|
* -----
|
|
|
|
*
|
|
|
|
* The credential C API is meant to be called by Git code which needs to
|
|
|
|
* acquire or store a credential. It is centered around an object
|
|
|
|
* representing a single credential and provides three basic operations:
|
|
|
|
* fill (acquire credentials by calling helpers and/or prompting the user),
|
|
|
|
* approve (mark a credential as successfully used so that it can be stored
|
|
|
|
* for later use), and reject (mark a credential as unsuccessful so that it
|
|
|
|
* can be erased from any persistent storage).
|
|
|
|
*
|
|
|
|
* Example
|
|
|
|
* ~~~~~~~
|
|
|
|
*
|
|
|
|
* The example below shows how the functions of the credential API could be
|
|
|
|
* used to login to a fictitious "foo" service on a remote host:
|
|
|
|
*
|
|
|
|
* -----------------------------------------------------------------------
|
|
|
|
* int foo_login(struct foo_connection *f)
|
|
|
|
* {
|
|
|
|
* int status;
|
|
|
|
* // Create a credential with some context; we don't yet know the
|
|
|
|
* // username or password.
|
|
|
|
*
|
|
|
|
* struct credential c = CREDENTIAL_INIT;
|
|
|
|
* c.protocol = xstrdup("foo");
|
|
|
|
* c.host = xstrdup(f->hostname);
|
|
|
|
*
|
|
|
|
* // Fill in the username and password fields by contacting
|
|
|
|
* // helpers and/or asking the user. The function will die if it
|
|
|
|
* // fails.
|
|
|
|
* credential_fill(&c);
|
|
|
|
*
|
|
|
|
* // Otherwise, we have a username and password. Try to use it.
|
|
|
|
*
|
|
|
|
* status = send_foo_login(f, c.username, c.password);
|
|
|
|
* switch (status) {
|
|
|
|
* case FOO_OK:
|
|
|
|
* // It worked. Store the credential for later use.
|
|
|
|
* credential_accept(&c);
|
|
|
|
* break;
|
|
|
|
* case FOO_BAD_LOGIN:
|
|
|
|
* // Erase the credential from storage so we don't try it again.
|
|
|
|
* credential_reject(&c);
|
|
|
|
* break;
|
|
|
|
* default:
|
|
|
|
* // Some other error occurred. We don't know if the
|
|
|
|
* // credential is good or bad, so report nothing to the
|
|
|
|
* // credential subsystem.
|
|
|
|
* }
|
|
|
|
*
|
|
|
|
* // Free any associated resources.
|
|
|
|
* credential_clear(&c);
|
|
|
|
*
|
|
|
|
* return status;
|
|
|
|
* }
|
|
|
|
* -----------------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
|
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 <sandals@crustytoothpaste.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2024-04-17 08:02:29 +08:00
|
|
|
/*
|
|
|
|
* These values define the kind of operation we're performing and the
|
|
|
|
* capabilities at each stage. The first is either an external request (via git
|
|
|
|
* credential fill) or an internal request (e.g., via the HTTP) code. The
|
|
|
|
* second is the call to the credential helper, and the third is the response
|
|
|
|
* we're providing.
|
|
|
|
*
|
|
|
|
* At each stage, we will emit the capability only if the previous stage
|
|
|
|
* supported it.
|
|
|
|
*/
|
|
|
|
enum credential_op_type {
|
|
|
|
CREDENTIAL_OP_INITIAL = 1,
|
|
|
|
CREDENTIAL_OP_HELPER = 2,
|
|
|
|
CREDENTIAL_OP_RESPONSE = 3,
|
|
|
|
};
|
|
|
|
|
|
|
|
struct credential_capability {
|
|
|
|
unsigned request_initial:1,
|
|
|
|
request_helper:1,
|
|
|
|
response:1;
|
|
|
|
};
|
2019-11-18 05:04:53 +08:00
|
|
|
|
|
|
|
/**
|
credential: clear expired c->credential, unify secret clearing
When a struct credential expires, credential_fill() clears c->password
so that clients don't try to use it later. However, a struct cred that
uses an alternate authtype won't have a password, but might have a
credential stored in c->credential.
This is a problem, for example, when an OAuth2 bearer token is used. In
the system I'm using, the OAuth2 configuration generates and caches a
bearer token that is valid for an hour. After the token expires, git
needs to call back into the credential helper to use a stored refresh
token to get a new bearer token. But if c->credential is still non-NULL,
git will instead try to use the expired token and fail with an error:
fatal: Authentication failed for 'https://<oauth2-enabled-server>/repository'
And on the server:
[auth_openidc:error] [client <ip>:34012] oidc_proto_validate_exp: "exp" validation failure (1717522989): JWT expired 224 seconds ago
Fix this by clearing both c->password and c->credential for an expired
struct credential. While we're at it, use credential_clear_secrets()
wherever both c->password and c->credential are being cleared.
Update comments in credential.h to mention the new struct fields.
Signed-off-by: Aaron Plattner <aplattner@nvidia.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2024-06-07 02:35:16 +08:00
|
|
|
* This struct represents a single login credential (typically a
|
|
|
|
* username/password combination) along with any associated
|
|
|
|
* context. All string fields should be heap-allocated (or NULL if
|
|
|
|
* they are not known or not applicable). The meaning of the
|
|
|
|
* individual context fields is the same as their counterparts in
|
|
|
|
* the helper protocol.
|
2019-11-18 05:04:53 +08:00
|
|
|
*
|
|
|
|
* This struct should always be initialized with `CREDENTIAL_INIT` or
|
|
|
|
* `credential_init`.
|
|
|
|
*/
|
2011-12-10 18:31:11 +08:00
|
|
|
struct credential {
|
2019-11-18 05:04:53 +08:00
|
|
|
|
|
|
|
/**
|
|
|
|
* A `string_list` of helpers. Each string specifies an external
|
|
|
|
* helper which will be run, in order, to either acquire or store
|
|
|
|
* credentials. This list is filled-in by the API functions
|
|
|
|
* according to the corresponding configuration variables before
|
|
|
|
* consulting helpers, so there usually is no need for a caller to
|
|
|
|
* modify the helpers field at all.
|
|
|
|
*/
|
2011-12-10 18:31:11 +08:00
|
|
|
struct string_list helpers;
|
2019-11-18 05:04:53 +08:00
|
|
|
|
2023-02-28 01:20:19 +08:00
|
|
|
/**
|
|
|
|
* A `strvec` of WWW-Authenticate header values. Each string
|
|
|
|
* is the value of a WWW-Authenticate header in an HTTP response,
|
|
|
|
* in the order they were received in the response.
|
|
|
|
*/
|
|
|
|
struct strvec wwwauth_headers;
|
|
|
|
|
credential: add an argument to keep state
Until now, our credential code has mostly deal with usernames and
passwords and we've let libcurl deal with the variant of authentication
to be used. However, now that we have the credential value, the
credential helper can take control of the authentication, so the value
provided might be something that's generated, such as a Digest hash
value.
In such a case, it would be helpful for a credential helper that gets an
erase or store command to be able to keep track of an identifier for the
original secret that went into the computation. Furthermore, some types
of authentication, such as NTLM and Kerberos, actually need two round
trips to authenticate, which will require that the credential helper
keep some state.
In order to allow for these use cases and others, allow storing state in
a field called "state[]". This value is passed back to the credential
helper that created it, which avoids confusion caused by parsing values
from different helpers.
Signed-off-by: brian m. carlson <sandals@crustytoothpaste.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2024-04-17 08:02:33 +08:00
|
|
|
/**
|
credential: add support for multistage credential rounds
Over HTTP, NTLM and Kerberos require two rounds of authentication on the
client side. It's possible that there are custom authentication schemes
that also implement this same approach. Since these are tricky schemes
to implement and the HTTP library in use may not always handle them
gracefully on all systems, it would be helpful to allow the credential
helper to implement them instead for increased portability and
robustness.
To allow this to happen, add a boolean flag, continue, that indicates
that instead of failing when we get a 401, we should retry another round
of authentication. However, this necessitates some changes in our
current credential code so that we can make this work.
Keep the state[] headers between iterations, but only use them to send
to the helper and only consider the new ones we read from the credential
helper to be valid on subsequent iterations. That avoids us passing
stale data when we finally approve or reject the credential. Similarly,
clear the multistage and wwwauth[] values appropriately so that we
don't pass stale data or think we're trying a multiround response when
we're not. Remove the credential values so that we can actually fill a
second time with new responses.
Limit the number of iterations of reauthentication we do to 3. This
means that if there's a problem, we'll terminate with an error message
instead of retrying indefinitely and not informing the user (and
possibly conducting a DoS on the server).
In our tests, handle creating multiple response output files from our
helper so we can verify that each of the messages sent is correct.
Signed-off-by: brian m. carlson <sandals@crustytoothpaste.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2024-04-17 08:02:37 +08:00
|
|
|
* A `strvec` of state headers received from credential helpers.
|
credential: add an argument to keep state
Until now, our credential code has mostly deal with usernames and
passwords and we've let libcurl deal with the variant of authentication
to be used. However, now that we have the credential value, the
credential helper can take control of the authentication, so the value
provided might be something that's generated, such as a Digest hash
value.
In such a case, it would be helpful for a credential helper that gets an
erase or store command to be able to keep track of an identifier for the
original secret that went into the computation. Furthermore, some types
of authentication, such as NTLM and Kerberos, actually need two round
trips to authenticate, which will require that the credential helper
keep some state.
In order to allow for these use cases and others, allow storing state in
a field called "state[]". This value is passed back to the credential
helper that created it, which avoids confusion caused by parsing values
from different helpers.
Signed-off-by: brian m. carlson <sandals@crustytoothpaste.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2024-04-17 08:02:33 +08:00
|
|
|
*/
|
|
|
|
struct strvec state_headers;
|
|
|
|
|
credential: add support for multistage credential rounds
Over HTTP, NTLM and Kerberos require two rounds of authentication on the
client side. It's possible that there are custom authentication schemes
that also implement this same approach. Since these are tricky schemes
to implement and the HTTP library in use may not always handle them
gracefully on all systems, it would be helpful to allow the credential
helper to implement them instead for increased portability and
robustness.
To allow this to happen, add a boolean flag, continue, that indicates
that instead of failing when we get a 401, we should retry another round
of authentication. However, this necessitates some changes in our
current credential code so that we can make this work.
Keep the state[] headers between iterations, but only use them to send
to the helper and only consider the new ones we read from the credential
helper to be valid on subsequent iterations. That avoids us passing
stale data when we finally approve or reject the credential. Similarly,
clear the multistage and wwwauth[] values appropriately so that we
don't pass stale data or think we're trying a multiround response when
we're not. Remove the credential values so that we can actually fill a
second time with new responses.
Limit the number of iterations of reauthentication we do to 3. This
means that if there's a problem, we'll terminate with an error message
instead of retrying indefinitely and not informing the user (and
possibly conducting a DoS on the server).
In our tests, handle creating multiple response output files from our
helper so we can verify that each of the messages sent is correct.
Signed-off-by: brian m. carlson <sandals@crustytoothpaste.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2024-04-17 08:02:37 +08:00
|
|
|
/**
|
|
|
|
* A `strvec` of state headers to send to credential helpers.
|
|
|
|
*/
|
|
|
|
struct strvec state_headers_to_send;
|
|
|
|
|
2023-02-28 01:20:19 +08:00
|
|
|
/**
|
|
|
|
* Internal use only. Keeps track of if we previously matched against a
|
|
|
|
* WWW-Authenticate header line in order to re-fold future continuation
|
|
|
|
* lines into one value.
|
|
|
|
*/
|
|
|
|
unsigned header_is_last_match:1;
|
|
|
|
|
2011-12-10 18:31:24 +08:00
|
|
|
unsigned approved:1,
|
2024-04-17 08:02:30 +08:00
|
|
|
ephemeral:1,
|
credential: make relevance of http path configurable
When parsing a URL into a credential struct, we carefully
record each part of the URL, including the path on the
remote host, and use the result as part of the credential
context.
This had two practical implications:
1. Credential helpers which store a credential for later
access are likely to use the "path" portion as part of
the storage key. That means that a request to
https://example.com/foo.git
would not use the same credential that was stored in an
earlier request for:
https://example.com/bar.git
2. The prompt shown to the user includes all relevant
context, including the path.
In most cases, however, users will have a single password
per host. The behavior in (1) will be inconvenient, and the
prompt in (2) will be overly long.
This patch introduces a config option to toggle the
relevance of http paths. When turned on, we use the path as
before. When turned off, we drop the path component from the
context: helpers don't see it, and it does not appear in the
prompt.
This is nothing you couldn't do with a clever credential
helper at the start of your stack, like:
[credential "http://"]
helper = "!f() { grep -v ^path= ; }; f"
helper = your_real_helper
But doing this:
[credential]
useHttpPath = false
is way easier and more readable. Furthermore, since most
users will want the "off" behavior, that is the new default.
Users who want it "on" can set the variable (either for all
credentials, or just for a subset using
credential.*.useHttpPath).
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2011-12-10 18:31:34 +08:00
|
|
|
configured:1,
|
credential: add support for multistage credential rounds
Over HTTP, NTLM and Kerberos require two rounds of authentication on the
client side. It's possible that there are custom authentication schemes
that also implement this same approach. Since these are tricky schemes
to implement and the HTTP library in use may not always handle them
gracefully on all systems, it would be helpful to allow the credential
helper to implement them instead for increased portability and
robustness.
To allow this to happen, add a boolean flag, continue, that indicates
that instead of failing when we get a 401, we should retry another round
of authentication. However, this necessitates some changes in our
current credential code so that we can make this work.
Keep the state[] headers between iterations, but only use them to send
to the helper and only consider the new ones we read from the credential
helper to be valid on subsequent iterations. That avoids us passing
stale data when we finally approve or reject the credential. Similarly,
clear the multistage and wwwauth[] values appropriately so that we
don't pass stale data or think we're trying a multiround response when
we're not. Remove the credential values so that we can actually fill a
second time with new responses.
Limit the number of iterations of reauthentication we do to 3. This
means that if there's a problem, we'll terminate with an error message
instead of retrying indefinitely and not informing the user (and
possibly conducting a DoS on the server).
In our tests, handle creating multiple response output files from our
helper so we can verify that each of the messages sent is correct.
Signed-off-by: brian m. carlson <sandals@crustytoothpaste.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2024-04-17 08:02:37 +08:00
|
|
|
multistage: 1,
|
credential: let helpers tell us to quit
When we are trying to fill a credential, we loop over the
set of defined credential-helpers, then fall back to running
askpass, and then finally prompt on the terminal. Helpers
which cannot find a credential are free to tell us nothing,
but they cannot currently ask us to stop prompting.
This patch lets them provide a "quit" attribute, which asks
us to stop the process entirely (avoiding running more
helpers, as well as the askpass/terminal prompt).
This has a few possible uses:
1. A helper which prompts the user itself (e.g., in a
dialog) can provide a "cancel" button to the user to
stop further prompts.
2. Some helpers may know that prompting cannot possibly
work. For example, if their role is to broker a ticket
from an external auth system and that auth system
cannot be contacted, there is no point in continuing
(we need a ticket to authenticate, and the user cannot
provide one by typing it in).
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2014-12-04 11:46:48 +08:00
|
|
|
quit:1,
|
credential: use the last matching username in the config
Everywhere else in the codebase, we use the rule that the last matching
configuration option is the one that takes effect. This is helpful
because it allows more specific configuration settings (e.g., per-repo
configuration) to override less specific settings (e.g., per-user
configuration).
However, in the credential code, we didn't honor this setting, and
instead picked the first setting we had, and stuck with it. This was
likely to ensure we picked the value from the URL, which we want to
honor over the configuration.
It's possible to do both, though, so let's check if the value is the one
we've gotten over our protocol connection, which if present will have
come from the URL, and keep it if so. Otherwise, let's overwrite the
value with the latest version we've got from the configuration, so we
keep the last configuration value.
Signed-off-by: brian m. carlson <bk2204@github.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-02-20 10:24:12 +08:00
|
|
|
use_http_path:1,
|
|
|
|
username_from_proto:1;
|
2011-12-10 18:31:11 +08:00
|
|
|
|
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 <sandals@crustytoothpaste.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2024-04-17 08:02:29 +08:00
|
|
|
struct credential_capability capa_authtype;
|
credential: add an argument to keep state
Until now, our credential code has mostly deal with usernames and
passwords and we've let libcurl deal with the variant of authentication
to be used. However, now that we have the credential value, the
credential helper can take control of the authentication, so the value
provided might be something that's generated, such as a Digest hash
value.
In such a case, it would be helpful for a credential helper that gets an
erase or store command to be able to keep track of an identifier for the
original secret that went into the computation. Furthermore, some types
of authentication, such as NTLM and Kerberos, actually need two round
trips to authenticate, which will require that the credential helper
keep some state.
In order to allow for these use cases and others, allow storing state in
a field called "state[]". This value is passed back to the credential
helper that created it, which avoids confusion caused by parsing values
from different helpers.
Signed-off-by: brian m. carlson <sandals@crustytoothpaste.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2024-04-17 08:02:33 +08:00
|
|
|
struct credential_capability capa_state;
|
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 <sandals@crustytoothpaste.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2024-04-17 08:02:29 +08:00
|
|
|
|
2011-12-10 18:31:11 +08:00
|
|
|
char *username;
|
|
|
|
char *password;
|
credential: add a field for pre-encoded credentials
At the moment, our credential code wants to find a username and password
for access, which, for HTTP, it will pass to libcurl to encode and
process. However, many users want to use authentication schemes that
libcurl doesn't support, such as Bearer authentication. In these
schemes, the secret is not a username and password pair, but some sort
of token that meets the production for authentication data in the RFC.
In fact, in general, it's useful to allow our credential helper to have
knowledge about what specifically to put in the protocol header. Thus,
add a field, credential, which contains data that's preencoded to be
suitable for the protocol in question. If we have such data, we need
neither a username nor a password, so make that adjustment as well.
It is in theory possible to reuse the password field for this. However,
if we do so, we must know whether the credential helper supports our new
scheme before sending it data, which necessitates some sort of
capability inquiry, because otherwise an uninformed credential helper
would store our preencoded data as a password, which would fail the next
time we attempted to connect to the remote server. This design is
substantially simpler, and we can hint to the credential helper that we
support this approach with a simple new field instead of needing to
query it first.
Signed-off-by: brian m. carlson <sandals@crustytoothpaste.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2024-04-17 08:02:28 +08:00
|
|
|
char *credential;
|
2011-12-10 18:31:11 +08:00
|
|
|
char *protocol;
|
|
|
|
char *host;
|
|
|
|
char *path;
|
2023-04-21 17:47:59 +08:00
|
|
|
char *oauth_refresh_token;
|
credential: new attribute password_expiry_utc
Some passwords have an expiry date known at generation. This may be
years away for a personal access token or hours for an OAuth access
token.
When multiple credential helpers are configured, `credential fill` tries
each helper in turn until it has a username and password, returning
early. If Git authentication succeeds, `credential approve`
stores the successful credential in all helpers. If authentication
fails, `credential reject` erases matching credentials in all helpers.
Helpers implement corresponding operations: get, store, erase.
The credential protocol has no expiry attribute, so helpers cannot
store expiry information. Even if a helper returned an improvised
expiry attribute, git credential discards unrecognised attributes
between operations and between helpers.
This is a particular issue when a storage helper and a
credential-generating helper are configured together:
[credential]
helper = storage # eg. cache or osxkeychain
helper = generate # eg. oauth
`credential approve` stores the generated credential in both helpers
without expiry information. Later `credential fill` may return an
expired credential from storage. There is no workaround, no matter how
clever the second helper. The user sees authentication fail (a retry
will succeed).
Introduce a password expiry attribute. In `credential fill`, ignore
expired passwords and continue to query subsequent helpers.
In the example above, `credential fill` ignores the expired password
and a fresh credential is generated. If authentication succeeds,
`credential approve` replaces the expired password in storage.
If authentication fails, the expired credential is erased by
`credential reject`. It is unnecessary but harmless for storage
helpers to self prune expired credentials.
Add support for the new attribute to credential-cache.
Eventually, I hope to see support in other popular storage helpers.
Example usage in a credential-generating helper
https://github.com/hickford/git-credential-oauth/pull/16
Signed-off-by: M Hickford <mirth.hickford@gmail.com>
Reviewed-by: Calvin Wan <calvinwan@google.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2023-02-18 14:32:57 +08:00
|
|
|
timestamp_t password_expiry_utc;
|
credential: add an authtype field
When Git makes an HTTP request, it can negotiate the type of
authentication to use with the server provided the authentication scheme
is one of a few well-known types (Basic, Digest, NTLM, or Negotiate).
However, some servers wish to use other types of authentication, such as
the Bearer type from OAuth2. Since libcurl doesn't natively support
this type, it isn't possible to use it, and the user is forced to
specify the Authorization header using the http.extraheader setting.
However, storing a plaintext token in the repository configuration is
not very secure, especially if a repository can be shared by multiple
parties. We already have support for many types of secure credential
storage by using credential helpers, so let's teach credential helpers
how to produce credentials for an arbitrary scheme.
If the credential helper specifies an authtype field, then it specifies
an authentication scheme (e.g., Bearer) and the password field specifies
the raw authentication token, with any encoding already specified. We
reuse the password field for this because some credential helpers store
the metadata without encryption even though the password is encrypted,
and we'd like to avoid insecure storage if an older version of the
credential helper gets ahold of the data.
The username is not used in this case, but it is still preserved for the
purpose of finding the right credential if the user has multiple
accounts.
If the authtype field is not specified, then the password behaves as
normal and it is passed along with the username to libcurl.
Signed-off-by: brian m. carlson <sandals@crustytoothpaste.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2024-04-17 08:02:25 +08:00
|
|
|
|
|
|
|
/**
|
|
|
|
* The authorization scheme to use. If this is NULL, libcurl is free to
|
|
|
|
* negotiate any scheme it likes.
|
|
|
|
*/
|
|
|
|
char *authtype;
|
2011-12-10 18:31:11 +08:00
|
|
|
};
|
|
|
|
|
2021-07-01 18:51:25 +08:00
|
|
|
#define CREDENTIAL_INIT { \
|
|
|
|
.helpers = STRING_LIST_INIT_DUP, \
|
credential: new attribute password_expiry_utc
Some passwords have an expiry date known at generation. This may be
years away for a personal access token or hours for an OAuth access
token.
When multiple credential helpers are configured, `credential fill` tries
each helper in turn until it has a username and password, returning
early. If Git authentication succeeds, `credential approve`
stores the successful credential in all helpers. If authentication
fails, `credential reject` erases matching credentials in all helpers.
Helpers implement corresponding operations: get, store, erase.
The credential protocol has no expiry attribute, so helpers cannot
store expiry information. Even if a helper returned an improvised
expiry attribute, git credential discards unrecognised attributes
between operations and between helpers.
This is a particular issue when a storage helper and a
credential-generating helper are configured together:
[credential]
helper = storage # eg. cache or osxkeychain
helper = generate # eg. oauth
`credential approve` stores the generated credential in both helpers
without expiry information. Later `credential fill` may return an
expired credential from storage. There is no workaround, no matter how
clever the second helper. The user sees authentication fail (a retry
will succeed).
Introduce a password expiry attribute. In `credential fill`, ignore
expired passwords and continue to query subsequent helpers.
In the example above, `credential fill` ignores the expired password
and a fresh credential is generated. If authentication succeeds,
`credential approve` replaces the expired password in storage.
If authentication fails, the expired credential is erased by
`credential reject`. It is unnecessary but harmless for storage
helpers to self prune expired credentials.
Add support for the new attribute to credential-cache.
Eventually, I hope to see support in other popular storage helpers.
Example usage in a credential-generating helper
https://github.com/hickford/git-credential-oauth/pull/16
Signed-off-by: M Hickford <mirth.hickford@gmail.com>
Reviewed-by: Calvin Wan <calvinwan@google.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2023-02-18 14:32:57 +08:00
|
|
|
.password_expiry_utc = TIME_MAX, \
|
2023-02-28 01:20:19 +08:00
|
|
|
.wwwauth_headers = STRVEC_INIT, \
|
credential: add an argument to keep state
Until now, our credential code has mostly deal with usernames and
passwords and we've let libcurl deal with the variant of authentication
to be used. However, now that we have the credential value, the
credential helper can take control of the authentication, so the value
provided might be something that's generated, such as a Digest hash
value.
In such a case, it would be helpful for a credential helper that gets an
erase or store command to be able to keep track of an identifier for the
original secret that went into the computation. Furthermore, some types
of authentication, such as NTLM and Kerberos, actually need two round
trips to authenticate, which will require that the credential helper
keep some state.
In order to allow for these use cases and others, allow storing state in
a field called "state[]". This value is passed back to the credential
helper that created it, which avoids confusion caused by parsing values
from different helpers.
Signed-off-by: brian m. carlson <sandals@crustytoothpaste.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2024-04-17 08:02:33 +08:00
|
|
|
.state_headers = STRVEC_INIT, \
|
credential: add support for multistage credential rounds
Over HTTP, NTLM and Kerberos require two rounds of authentication on the
client side. It's possible that there are custom authentication schemes
that also implement this same approach. Since these are tricky schemes
to implement and the HTTP library in use may not always handle them
gracefully on all systems, it would be helpful to allow the credential
helper to implement them instead for increased portability and
robustness.
To allow this to happen, add a boolean flag, continue, that indicates
that instead of failing when we get a 401, we should retry another round
of authentication. However, this necessitates some changes in our
current credential code so that we can make this work.
Keep the state[] headers between iterations, but only use them to send
to the helper and only consider the new ones we read from the credential
helper to be valid on subsequent iterations. That avoids us passing
stale data when we finally approve or reject the credential. Similarly,
clear the multistage and wwwauth[] values appropriately so that we
don't pass stale data or think we're trying a multiround response when
we're not. Remove the credential values so that we can actually fill a
second time with new responses.
Limit the number of iterations of reauthentication we do to 3. This
means that if there's a problem, we'll terminate with an error message
instead of retrying indefinitely and not informing the user (and
possibly conducting a DoS on the server).
In our tests, handle creating multiple response output files from our
helper so we can verify that each of the messages sent is correct.
Signed-off-by: brian m. carlson <sandals@crustytoothpaste.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2024-04-17 08:02:37 +08:00
|
|
|
.state_headers_to_send = STRVEC_INIT, \
|
2021-07-01 18:51:25 +08:00
|
|
|
}
|
2011-12-10 18:31:11 +08:00
|
|
|
|
2019-11-18 05:04:53 +08:00
|
|
|
/* Initialize a credential structure, setting all fields to empty. */
|
2011-12-10 18:31:11 +08:00
|
|
|
void credential_init(struct credential *);
|
2019-11-18 05:04:53 +08:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Free any resources associated with the credential structure, returning
|
|
|
|
* it to a pristine initialized state.
|
|
|
|
*/
|
2011-12-10 18:31:11 +08:00
|
|
|
void credential_clear(struct credential *);
|
|
|
|
|
2019-11-18 05:04:53 +08:00
|
|
|
/**
|
|
|
|
* Instruct the credential subsystem to fill the username and
|
credential: clear expired c->credential, unify secret clearing
When a struct credential expires, credential_fill() clears c->password
so that clients don't try to use it later. However, a struct cred that
uses an alternate authtype won't have a password, but might have a
credential stored in c->credential.
This is a problem, for example, when an OAuth2 bearer token is used. In
the system I'm using, the OAuth2 configuration generates and caches a
bearer token that is valid for an hour. After the token expires, git
needs to call back into the credential helper to use a stored refresh
token to get a new bearer token. But if c->credential is still non-NULL,
git will instead try to use the expired token and fail with an error:
fatal: Authentication failed for 'https://<oauth2-enabled-server>/repository'
And on the server:
[auth_openidc:error] [client <ip>:34012] oidc_proto_validate_exp: "exp" validation failure (1717522989): JWT expired 224 seconds ago
Fix this by clearing both c->password and c->credential for an expired
struct credential. While we're at it, use credential_clear_secrets()
wherever both c->password and c->credential are being cleared.
Update comments in credential.h to mention the new struct fields.
Signed-off-by: Aaron Plattner <aplattner@nvidia.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2024-06-07 02:35:16 +08:00
|
|
|
* password (or authtype and credential) fields of the passed
|
|
|
|
* credential struct by first consulting helpers, then asking the
|
|
|
|
* user. After this function returns, either the username and
|
|
|
|
* password fields or the credential field of the credential are
|
|
|
|
* guaranteed to be non-NULL. If an error occurs, the function
|
|
|
|
* will die().
|
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 <sandals@crustytoothpaste.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2024-04-17 08:02:29 +08:00
|
|
|
*
|
|
|
|
* If all_capabilities is set, this is an internal user that is prepared
|
|
|
|
* to deal with all known capabilities, and we should advertise that fact.
|
2019-11-18 05:04:53 +08:00
|
|
|
*/
|
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 <sandals@crustytoothpaste.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2024-04-17 08:02:29 +08:00
|
|
|
void credential_fill(struct credential *, int all_capabilities);
|
2019-11-18 05:04:53 +08:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Inform the credential subsystem that the provided credentials
|
|
|
|
* were successfully used for authentication. This will cause the
|
|
|
|
* credential subsystem to notify any helpers of the approval, so
|
|
|
|
* that they may store the result to be used again. Any errors
|
|
|
|
* from helpers are ignored.
|
|
|
|
*/
|
2011-12-10 18:31:11 +08:00
|
|
|
void credential_approve(struct credential *);
|
2019-11-18 05:04:53 +08:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Inform the credential subsystem that the provided credentials
|
|
|
|
* have been rejected. This will cause the credential subsystem to
|
|
|
|
* notify any helpers of the rejection (which allows them, for
|
|
|
|
* example, to purge the invalid credentials from storage). It
|
credential: clear expired c->credential, unify secret clearing
When a struct credential expires, credential_fill() clears c->password
so that clients don't try to use it later. However, a struct cred that
uses an alternate authtype won't have a password, but might have a
credential stored in c->credential.
This is a problem, for example, when an OAuth2 bearer token is used. In
the system I'm using, the OAuth2 configuration generates and caches a
bearer token that is valid for an hour. After the token expires, git
needs to call back into the credential helper to use a stored refresh
token to get a new bearer token. But if c->credential is still non-NULL,
git will instead try to use the expired token and fail with an error:
fatal: Authentication failed for 'https://<oauth2-enabled-server>/repository'
And on the server:
[auth_openidc:error] [client <ip>:34012] oidc_proto_validate_exp: "exp" validation failure (1717522989): JWT expired 224 seconds ago
Fix this by clearing both c->password and c->credential for an expired
struct credential. While we're at it, use credential_clear_secrets()
wherever both c->password and c->credential are being cleared.
Update comments in credential.h to mention the new struct fields.
Signed-off-by: Aaron Plattner <aplattner@nvidia.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2024-06-07 02:35:16 +08:00
|
|
|
* will also free() the username, password, and credential fields
|
|
|
|
* of the credential and set them to NULL (readying the credential
|
|
|
|
* for another call to `credential_fill`). Any errors from helpers
|
|
|
|
* are ignored.
|
2019-11-18 05:04:53 +08:00
|
|
|
*/
|
2011-12-10 18:31:11 +08:00
|
|
|
void credential_reject(struct credential *);
|
|
|
|
|
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 <sandals@crustytoothpaste.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2024-04-17 08:02:29 +08:00
|
|
|
/**
|
|
|
|
* Enable all of the supported credential flags in this credential.
|
|
|
|
*/
|
|
|
|
void credential_set_all_capabilities(struct credential *c,
|
|
|
|
enum credential_op_type op_type);
|
|
|
|
|
credential: add support for multistage credential rounds
Over HTTP, NTLM and Kerberos require two rounds of authentication on the
client side. It's possible that there are custom authentication schemes
that also implement this same approach. Since these are tricky schemes
to implement and the HTTP library in use may not always handle them
gracefully on all systems, it would be helpful to allow the credential
helper to implement them instead for increased portability and
robustness.
To allow this to happen, add a boolean flag, continue, that indicates
that instead of failing when we get a 401, we should retry another round
of authentication. However, this necessitates some changes in our
current credential code so that we can make this work.
Keep the state[] headers between iterations, but only use them to send
to the helper and only consider the new ones we read from the credential
helper to be valid on subsequent iterations. That avoids us passing
stale data when we finally approve or reject the credential. Similarly,
clear the multistage and wwwauth[] values appropriately so that we
don't pass stale data or think we're trying a multiround response when
we're not. Remove the credential values so that we can actually fill a
second time with new responses.
Limit the number of iterations of reauthentication we do to 3. This
means that if there's a problem, we'll terminate with an error message
instead of retrying indefinitely and not informing the user (and
possibly conducting a DoS on the server).
In our tests, handle creating multiple response output files from our
helper so we can verify that each of the messages sent is correct.
Signed-off-by: brian m. carlson <sandals@crustytoothpaste.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2024-04-17 08:02:37 +08:00
|
|
|
/**
|
|
|
|
* Clear the secrets in this credential, but leave other data intact.
|
|
|
|
*
|
|
|
|
* This is useful for resetting credentials in preparation for a subsequent
|
|
|
|
* stage of filling.
|
|
|
|
*/
|
|
|
|
void credential_clear_secrets(struct credential *c);
|
|
|
|
|
2024-04-17 08:02:40 +08:00
|
|
|
/**
|
|
|
|
* Print a list of supported capabilities and version numbers to standard
|
|
|
|
* output.
|
|
|
|
*/
|
|
|
|
void credential_announce_capabilities(struct credential *c, FILE *fp);
|
|
|
|
|
credential: add support for multistage credential rounds
Over HTTP, NTLM and Kerberos require two rounds of authentication on the
client side. It's possible that there are custom authentication schemes
that also implement this same approach. Since these are tricky schemes
to implement and the HTTP library in use may not always handle them
gracefully on all systems, it would be helpful to allow the credential
helper to implement them instead for increased portability and
robustness.
To allow this to happen, add a boolean flag, continue, that indicates
that instead of failing when we get a 401, we should retry another round
of authentication. However, this necessitates some changes in our
current credential code so that we can make this work.
Keep the state[] headers between iterations, but only use them to send
to the helper and only consider the new ones we read from the credential
helper to be valid on subsequent iterations. That avoids us passing
stale data when we finally approve or reject the credential. Similarly,
clear the multistage and wwwauth[] values appropriately so that we
don't pass stale data or think we're trying a multiround response when
we're not. Remove the credential values so that we can actually fill a
second time with new responses.
Limit the number of iterations of reauthentication we do to 3. This
means that if there's a problem, we'll terminate with an error message
instead of retrying indefinitely and not informing the user (and
possibly conducting a DoS on the server).
In our tests, handle creating multiple response output files from our
helper so we can verify that each of the messages sent is correct.
Signed-off-by: brian m. carlson <sandals@crustytoothpaste.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2024-04-17 08:02:37 +08:00
|
|
|
/**
|
|
|
|
* Prepares the credential for the next iteration of the helper protocol by
|
|
|
|
* updating the state headers to send with the ones read by the last iteration
|
|
|
|
* of the protocol.
|
|
|
|
*
|
|
|
|
* Except for internal callers, this should be called exactly once between
|
|
|
|
* reading credentials with `credential_fill` and writing them.
|
|
|
|
*/
|
|
|
|
void credential_next_state(struct credential *c);
|
|
|
|
|
2024-04-17 08:02:39 +08:00
|
|
|
/**
|
|
|
|
* Return true if the capability is enabled for an operation of op_type.
|
|
|
|
*/
|
|
|
|
int credential_has_capability(const struct credential_capability *capa,
|
|
|
|
enum credential_op_type op_type);
|
|
|
|
|
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 <sandals@crustytoothpaste.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2024-04-17 08:02:29 +08:00
|
|
|
int credential_read(struct credential *, FILE *,
|
|
|
|
enum credential_op_type);
|
|
|
|
void credential_write(const struct credential *, FILE *,
|
|
|
|
enum credential_op_type);
|
2019-11-18 05:04:53 +08:00
|
|
|
|
2020-03-12 13:31:11 +08:00
|
|
|
/*
|
|
|
|
* Parse a url into a credential struct, replacing any existing contents.
|
|
|
|
*
|
2020-03-18 09:12:01 +08:00
|
|
|
* If the url can't be parsed (e.g., a missing "proto://" component), the
|
2020-05-05 09:39:05 +08:00
|
|
|
* resulting credential will be empty and the function will return an
|
|
|
|
* error (even in the "gently" form).
|
2020-03-12 13:31:11 +08:00
|
|
|
*
|
|
|
|
* If we encounter a component which cannot be represented as a credential
|
|
|
|
* value (e.g., because it contains a newline), the "gently" form will return
|
|
|
|
* an error but leave the broken state in the credential object for further
|
|
|
|
* examination. The non-gentle form will issue a warning to stderr and return
|
|
|
|
* an empty credential.
|
|
|
|
*/
|
2011-12-10 18:31:17 +08:00
|
|
|
void credential_from_url(struct credential *, const char *url);
|
2020-03-12 13:31:11 +08:00
|
|
|
int credential_from_url_gently(struct credential *, const char *url, int quiet);
|
2019-11-18 05:04:53 +08:00
|
|
|
|
2020-05-05 09:39:06 +08:00
|
|
|
int credential_match(const struct credential *want,
|
2023-06-16 03:19:32 +08:00
|
|
|
const struct credential *have, int match_password);
|
2011-12-10 18:31:11 +08:00
|
|
|
|
|
|
|
#endif /* CREDENTIAL_H */
|