* [PATCH] credential: do not store credentials received from helpers @ 2012-04-07 3:34 Jeff King 2012-04-07 4:12 ` Shawn Pearce 0 siblings, 1 reply; 10+ messages in thread From: Jeff King @ 2012-04-07 3:34 UTC (permalink / raw) To: git The typical call flow for the credential API is something like: 1. Network code (like http.c) wants a credential. It calls credential_fill() to get it, and one of the two happens: a. We call out to any helpers with a "get" request, one of which provides the credential. b. No helper provides us the credential, so we ask the user. 2. The network code uses the credential. Let's imagine that the request completes successfully. 3. The network code informs the credential subsystem of success by calling credential_approve(). The credential code then calls out to any helpers with a "store" request, so they can optionally store it if they want. In the case of (1b), this is a good thing. The credential comes from the user, gets used, and then gets put into storage for later use again. But in the case of (1a), we end up feeding the helper with a credential that came from itself already (or possibly from another helper). Most of the time, this is harmless and slightly inefficient. But it has two user-visible impacts: 1. If you have two helpers, you end up propagating data between them. For instance, imagine you have a helper "git-credential-wallet" which pulls data from a a read-only password wallet. You might configure git like this: [credential] helper = wallet helper = cache to check the wallet first, and then fall back to asking the user and caching the result. But when we do get something out of the wallet, git will then ask for it to be stored in the wallet (which is a no-op), and the cache. So your credential migrates from the wallet into the cache, which may not be what you want (e.g., because the wallet implements more strict security policies than the cache). 2. If you use a time-based storage helper like "git-credential-cache", every time you run a git command which uses the credential, it will also re-insert the credential after use, freshening the cache timestamp. So the cache will eventually expire N time units after the last _use_, not after the time the user actually typed the password. This is probably not what most users expect or want (and if they do, we should do it explicitly by providing an option to refresh the timestamp on use). We can solve this by marking a credential that comes from a helper, so we don't bother feeding it back to the helpers. The credential struct already has an "approved" flag so that we try to store it only once, rather than for each successful http request. We can use the same flag to "pre-approve" a credential which comes from a helper, and enver try to store it at all. Signed-off-by: Jeff King <peff@peff.net> --- Whew, that was a long explanation. Fortunately the patch text itself is pleasantly simple. credential.c | 4 +++- t/t5550-http-fetch.sh | 6 +++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/credential.c b/credential.c index 62d1c56..813e77a 100644 --- a/credential.c +++ b/credential.c @@ -272,8 +272,10 @@ void credential_fill(struct credential *c) for (i = 0; i < c->helpers.nr; i++) { credential_do(c, c->helpers.items[i].string, "get"); - if (c->username && c->password) + if (c->username && c->password) { + c->approved = 1; return; + } } credential_getpass(c); diff --git a/t/t5550-http-fetch.sh b/t/t5550-http-fetch.sh index e5e6b8f..82e0d37 100755 --- a/t/t5550-http-fetch.sh +++ b/t/t5550-http-fetch.sh @@ -104,13 +104,17 @@ test_expect_success 'http auth can request both user and pass' ' test_expect_success 'http auth respects credential helper config' ' test_config_global credential.helper "!f() { cat >/dev/null + echo helper: \$* >>helper-trace echo username=user@host echo password=user@host }; f" && + >helper-trace && >askpass-query && echo wrong >askpass-response && git clone "$HTTPD_URL/auth/repo.git" clone-auth-helper && - expect_askpass none + expect_askpass none && + echo "helper: get" >helper-expect && + test_cmp helper-expect helper-trace ' test_expect_success 'http auth can get username from config' ' -- 1.7.10.rc4.31.g355de ^ permalink raw reply related [flat|nested] 10+ messages in thread
* Re: [PATCH] credential: do not store credentials received from helpers 2012-04-07 3:34 [PATCH] credential: do not store credentials received from helpers Jeff King @ 2012-04-07 4:12 ` Shawn Pearce 2012-04-07 4:56 ` Jeff King 2012-04-07 4:56 ` Junio C Hamano 0 siblings, 2 replies; 10+ messages in thread From: Shawn Pearce @ 2012-04-07 4:12 UTC (permalink / raw) To: Jeff King; +Cc: git On Fri, Apr 6, 2012 at 20:34, Jeff King <peff@peff.net> wrote: > 2. If you use a time-based storage helper like > "git-credential-cache", every time you run a git > command which uses the credential, it will also > re-insert the credential after use, freshening the > cache timestamp. So the cache will eventually expire N > time units after the last _use_, not after the time the > user actually typed the password. This is probably not > what most users expect or want (and if they do, we > should do it explicitly by providing an option to > refresh the timestamp on use). So if I use the cache helper, and its set to expire at the default of 15 minutes, I have to type my password in every 15 minutes, even if I am doing a Git operation roughly every 8 minutes during a work day? > We can solve this by marking a credential that comes from a > helper, so we don't bother feeding it back to the helpers. > The credential struct already has an "approved" flag so > that we try to store it only once, rather than for each > successful http request. We can use the same flag to > "pre-approve" a credential which comes from a helper, and > enver try to store it at all. This breaks one of my credential helpers. I have a helper that generates a password by asking a remote system to generate a short lived password based on other authentication systems that I can't describe. Once I have that password, its good for $X time. The helper just dumps it out to Git, and Git turns around and stores it into the cache for me. This means later requests will keep that credential in the cache, and avoid making that remote system call every time I do a Git network command. I guess I now need to change my helper to cache git credential-cache itself and store the password into the cache if it wants to use the cache? Should we update the credential helper documentation at the same time as this change to make it clear Git won't cache passwords returned from helpers, but a helper could call the credential-cache itself if it wanted to reuse the existing cache service? ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH] credential: do not store credentials received from helpers 2012-04-07 4:12 ` Shawn Pearce @ 2012-04-07 4:56 ` Jeff King 2012-04-07 5:21 ` Jeff King 2012-04-07 4:56 ` Junio C Hamano 1 sibling, 1 reply; 10+ messages in thread From: Jeff King @ 2012-04-07 4:56 UTC (permalink / raw) To: Shawn Pearce; +Cc: git On Fri, Apr 06, 2012 at 09:12:39PM -0700, Shawn O. Pearce wrote: > On Fri, Apr 6, 2012 at 20:34, Jeff King <peff@peff.net> wrote: > > 2. If you use a time-based storage helper like > > "git-credential-cache", every time you run a git > > command which uses the credential, it will also > > re-insert the credential after use, freshening the > > cache timestamp. So the cache will eventually expire N > > time units after the last _use_, not after the time the > > user actually typed the password. This is probably not > > what most users expect or want (and if they do, we > > should do it explicitly by providing an option to > > refresh the timestamp on use). > > So if I use the cache helper, and its set to expire at the default of > 15 minutes, I have to type my password in every 15 minutes, even if I > am doing a Git operation roughly every 8 minutes during a work day? Yes. It's less convenient, but safer and more predictable (you put your password in at 2:30, it's gone at 2:45). Keep in mind that you can also bump the cache time. And like I said, if we do want have it behave the other way, that's OK, but it should be explicit (and it can be optional, even if it defaults to auto-refresh on use). Or you could even do something more complex. gpg-agent will refresh the timestamp on use, but still has a "max ttl" that drops a credential N seconds after it was input, even if it was used recently. But making any decision like that means that the daemon needs to actually know whether the timestamp came from the user, or whether it was simply accessed and regurgitated to us. > This breaks one of my credential helpers. > > I have a helper that generates a password by asking a remote system to > generate a short lived password based on other authentication systems > that I can't describe. Once I have that password, its good for $X > time. > > The helper just dumps it out to Git, and Git turns around and stores > it into the cache for me. This means later requests will keep that > credential in the cache, and avoid making that remote system call > every time I do a Git network command. I considered your use case when writing the patch and thought "no, that's too insane. Nobody would want that." But leave it to you to prove me wrong. :) I'm torn. What you are doing is totally reasonable for your case. At the same time, having it happen without the user's knowledge could have surprising security implications, because you're leaking passwords from one helper to the other (in the example in the commit message, I mentioned a high-security helper followed by "cache". Which is probably not that bad. But imagine if it were "store"). > I guess I now need to change my helper to cache git credential-cache > itself and store the password into the cache if it wants to use the > cache? That's one option, though it is a little bit of a pain to implement (not hard, but not one line of code). It would be very easy to add a "credential.storeHelperCredentials" option that defaulted to off (that name is horrible, but you get the point). > Should we update the credential helper documentation at the same time > as this change to make it clear Git won't cache passwords returned > from helpers, but a helper could call the credential-cache itself if > it wanted to reuse the existing cache service? Yeah, I'll add a documentation update when I re-roll; but we first need to figure out what the recommended course of action is. One thing I don't like about having your helper call credential-cache is that it is a layering violation; it is only guessing that putting the data into credential-cache will be useful in the first place. But that depends on the user's config. It could have be using an alternative caching helper (or none at all). So I think an explicit "it's OK to leak credentials between helpers" flag is our best bet. And it's dirt simple to code. -Peff ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH] credential: do not store credentials received from helpers 2012-04-07 4:56 ` Jeff King @ 2012-04-07 5:21 ` Jeff King 0 siblings, 0 replies; 10+ messages in thread From: Jeff King @ 2012-04-07 5:21 UTC (permalink / raw) To: Shawn Pearce; +Cc: git On Sat, Apr 07, 2012 at 12:56:12AM -0400, Jeff King wrote: > > So if I use the cache helper, and its set to expire at the default of > > 15 minutes, I have to type my password in every 15 minutes, even if I > > am doing a Git operation roughly every 8 minutes during a work day? > > Yes. It's less convenient, but safer and more predictable (you put your > password in at 2:30, it's gone at 2:45). Keep in mind that you can also > bump the cache time. And like I said, if we do want have it behave the > other way, that's OK, but it should be explicit (and it can be optional, > even if it defaults to auto-refresh on use). And here's what the optional version looks like: diff --git a/credential-cache--daemon.c b/credential-cache--daemon.c index 390f194..1f801f7 100644 --- a/credential-cache--daemon.c +++ b/credential-cache--daemon.c @@ -136,6 +136,9 @@ static void serve_one_client(FILE *in, FILE *out) else if (!strcmp(action.buf, "get")) { struct credential_cache_entry *e = lookup_credential(&c); if (e) { + int new_expiration = time(NULL) + timeout; + if (new_expiration > e->expiration) + e->expiration = new_expiration; fprintf(out, "username=%s\n", e->item.username); fprintf(out, "password=%s\n", e->item.password); } diff --git a/credential-cache.c b/credential-cache.c index 9a03792..5751b48 100644 --- a/credential-cache.c +++ b/credential-cache.c @@ -87,6 +87,7 @@ int main(int argc, const char **argv) { char *socket_path = NULL; int timeout = 900; + int refresh = 0; const char *op; const char * const usage[] = { "git credential-cache [options] <action>", @@ -97,6 +98,8 @@ int main(int argc, const char **argv) "number of seconds to cache credentials"), OPT_STRING(0, "socket", &socket_path, "path", "path of cache-daemon socket"), + OPT_BOOL(0, "refresh-on-use", &refresh, + "refresh timestamp when credential is accessed"), OPT_END() }; @@ -112,7 +115,9 @@ int main(int argc, const char **argv) if (!strcmp(op, "exit")) do_cache(socket_path, op, timeout, 0); - else if (!strcmp(op, "get") || !strcmp(op, "erase")) + else if (!strcmp(op, "get")) + do_cache(socket_path, op, refresh ? timeout : 0, FLAG_RELAY); + else if(!strcmp(op, "erase")) do_cache(socket_path, op, timeout, FLAG_RELAY); else if (!strcmp(op, "store")) do_cache(socket_path, op, timeout, FLAG_RELAY|FLAG_SPAWN); ^ permalink raw reply related [flat|nested] 10+ messages in thread
* Re: [PATCH] credential: do not store credentials received from helpers 2012-04-07 4:12 ` Shawn Pearce 2012-04-07 4:56 ` Jeff King @ 2012-04-07 4:56 ` Junio C Hamano 2012-04-07 5:09 ` Jeff King 1 sibling, 1 reply; 10+ messages in thread From: Junio C Hamano @ 2012-04-07 4:56 UTC (permalink / raw) To: Shawn Pearce; +Cc: Jeff King, git Shawn Pearce <spearce@spearce.org> writes: > On Fri, Apr 6, 2012 at 20:34, Jeff King <peff@peff.net> wrote: >> 2. If you use a time-based storage helper like >> "git-credential-cache", every time you run a git >> command which uses the credential, it will also >> re-insert the credential after use, freshening the >> cache timestamp. So the cache will eventually expire N >> time units after the last _use_, not after the time the >> user actually typed the password. This is probably not >> what most users expect or want (and if they do, we >> should do it explicitly by providing an option to >> refresh the timestamp on use). > > So if I use the cache helper, and its set to expire at the default of > 15 minutes, I have to type my password in every 15 minutes, even if I > am doing a Git operation roughly every 8 minutes during a work day? I think what Peff meant was to set the expiration time of the cache helper a lot longer (say 20 hours, as opposed to 15 minutes) but once it learns a credential material from your helper, leave the timer running without resetting it every time the user uses the credential (your 8 minutes). So I do not see a fundamental problem in this part. >> We can solve this by marking a credential that comes from a >> helper, so we don't bother feeding it back to the helpers. >> The credential struct already has an "approved" flag so >> that we try to store it only once, rather than for each >> successful http request. We can use the same flag to >> "pre-approve" a credential which comes from a helper, and >> enver try to store it at all. > > This breaks one of my credential helpers. > > I have a helper that generates a password by asking a remote system to > generate a short lived password based on other authentication systems > that I can't describe. Once I have that password, its good for $X > time. Well, $X is something your helper knows but other helpers don't, so... > The helper just dumps it out to Git, and Git turns around and stores > it into the cache for me. This means later requests will keep that > credential in the cache, and avoid making that remote system call > every time I do a Git network command. I guess I now need to change my > helper to cache git credential-cache itself and store the password > into the cache if it wants to use the cache? ... logically the knowledge of cache expiration time belongs to your helper, but "after the first use, it is OK to keep using it for N hours" is so common that the two-helper workaround is very tempting. I am afraid that "do not trigger the cache helper" might be throwing the baby with bathwater to solve the real problem the patch tries to address, which is: Peff> 2. If you use a time-based storage helper like Peff> "git-credential-cache", every time you run a git Peff> command which uses the credential, it will also Peff> re-insert the credential after use, freshening the Peff> cache timestamp. So the cache will eventually expire N Peff> time units after the last _use_, not after the time the Peff> user actually typed the password. This is probably not Shouldn't the memory cache based helper already have enough clue to tell when a new entry is first inserted vs when the existing entry it supplied came back from the network layer after use? If there is not enough clue with the current network-layer-to-helper protocol, then wouldn't it be a better approach to add that, so that the memory cache helper can make more intelligent management of its timer? Once that is fixed, I would imagine that you can tell your users to use two helpers (yours and generic caching one) and configure them so that (1) the caching one is asked first and then fall back to ask yours, and (2) the expiration time of the caching one is set close to $X. ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH] credential: do not store credentials received from helpers 2012-04-07 4:56 ` Junio C Hamano @ 2012-04-07 5:09 ` Jeff King 2012-04-08 5:05 ` Junio C Hamano 0 siblings, 1 reply; 10+ messages in thread From: Jeff King @ 2012-04-07 5:09 UTC (permalink / raw) To: Junio C Hamano; +Cc: Shawn Pearce, git On Fri, Apr 06, 2012 at 09:56:18PM -0700, Junio C Hamano wrote: > I am afraid that "do not trigger the cache helper" might be throwing the > baby with bathwater to solve the real problem the patch tries to address, > which is: > > Peff> 2. If you use a time-based storage helper like > Peff> "git-credential-cache", every time you run a git > Peff> command which uses the credential, it will also > Peff> re-insert the credential after use, freshening the > Peff> cache timestamp. So the cache will eventually expire N > Peff> time units after the last _use_, not after the time the > Peff> user actually typed the password. This is probably not Actually, that was not the real problem. The real problem I had was the leakage between helpers. I just noticed this one while thinking about it. So the very thing that is useful to Shawn is also potentially dangerous to people who are doing something less clever. > Shouldn't the memory cache based helper already have enough clue to tell > when a new entry is first inserted vs when the existing entry it supplied > came back from the network layer after use? If there is not enough clue > with the current network-layer-to-helper protocol, then wouldn't it be a > better approach to add that, so that the memory cache helper can make more > intelligent management of its timer? You can approximate it. The daemon sees that somebody is inserting the same thing that it already there, and can guess that it probably came from the daemon in the first place. There are some corner cases with expiration boundaries (we get the credential, the daemon expires it, then the http request succeeds, and we tell the daemon "hey, store this"). > Once that is fixed, I would imagine that you can tell your users to use > two helpers (yours and generic caching one) and configure them so that (1) > the caching one is asked first and then fall back to ask yours, and (2) > the expiration time of the caching one is set close to $X. Yeah, in my other response to Shawn, I mentioned that we could add a flag to do the "leaking" behavior if that's what the user wants. But it would have the side effect of refreshing his timestamp on each use, so his $X would not expire (although that is also the case now, and he hasn't complained). So I actually do think he would be better to implement the caching inside his helper, even if it is by calling out to git-credential-cache. And as a bonus, it makes configuration easier on the users (they don't have to configure the cache helper separately, and they don't have to set the timeout on it manually). -Peff ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH] credential: do not store credentials received from helpers 2012-04-07 5:09 ` Jeff King @ 2012-04-08 5:05 ` Junio C Hamano 2012-04-08 6:40 ` Jeff King 0 siblings, 1 reply; 10+ messages in thread From: Junio C Hamano @ 2012-04-08 5:05 UTC (permalink / raw) To: Jeff King; +Cc: Junio C Hamano, Shawn Pearce, git Jeff King <peff@peff.net> writes: > On Fri, Apr 06, 2012 at 09:56:18PM -0700, Junio C Hamano wrote: > >> I am afraid that "do not trigger the cache helper" might be throwing the >> baby with bathwater to solve the real problem the patch tries to address, >> which is: >> >> Peff> 2. If you use a time-based storage helper like >> Peff> "git-credential-cache", every time you run a git >> Peff> command which uses the credential, it will also >> Peff> re-insert the credential after use, freshening the >> Peff> cache timestamp. So the cache will eventually expire N >> Peff> time units after the last _use_, not after the time the >> Peff> user actually typed the password. This is probably not > > Actually, that was not the real problem. The real problem I had was the > leakage between helpers. I just noticed this one while thinking about > it. So the very thing that is useful to Shawn is also potentially > dangerous to people who are doing something less clever. > >> Shouldn't the memory cache based helper already have enough clue to tell >> when a new entry is first inserted vs when the existing entry it supplied >> came back from the network layer after use? If there is not enough clue >> with the current network-layer-to-helper protocol, then wouldn't it be a >> better approach to add that, so that the memory cache helper can make more >> intelligent management of its timer? > > You can approximate it. The daemon sees that somebody is inserting the > same thing that it already there, and can guess that it probably came > from the daemon in the first place. There are some corner cases with > expiration boundaries (we get the credential, the daemon expires it, > then the http request succeeds, and we tell the daemon "hey, store > this"). > >> Once that is fixed, I would imagine that you can tell your users to use >> two helpers (yours and generic caching one) and configure them so that (1) >> the caching one is asked first and then fall back to ask yours, and (2) >> the expiration time of the caching one is set close to $X. > > Yeah, in my other response to Shawn, I mentioned that we could add a > flag to do the "leaking" behavior if that's what the user wants. But it > would have the side effect of refreshing his timestamp on each use, so > his $X would not expire (although that is also the case now, and he > hasn't complained). > > So I actually do think he would be better to implement the caching > inside his helper, even if it is by calling out to git-credential-cache. While I'm for keeping the interface simple, at the same time, "I have this credential obtained and it is valid for $X time duration" sounds like a very common thing, and it is somewhat a shame for the API to force its users (i.e. helpers) to reimplement the caching logic over and over. The network layer (i.e. the one that gets the authentication material from helpers and uses it to talk with a remote git service on the other end) could be thought of as a proxy server that gets information from its true source (i.e. helpers) and relays it to the browser (i.e. the remote git service). In that context, it would be natural for the API to allow the source of the truth (i.e. the helpers) to pass its information to the proxy with validity duration, so that the proxy can handle the expiration in a way hidden from the information sources and the browser. That proxy (i.e. the network layer) could use git-credential-cache as a non-volatile memory but that will become an implementation detail hidden from both the remote service and the helpers. So,... I dunno. ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH] credential: do not store credentials received from helpers 2012-04-08 5:05 ` Junio C Hamano @ 2012-04-08 6:40 ` Jeff King 2012-04-08 7:07 ` Jeff King 2012-04-08 7:13 ` Jeff King 0 siblings, 2 replies; 10+ messages in thread From: Jeff King @ 2012-04-08 6:40 UTC (permalink / raw) To: Junio C Hamano; +Cc: Shawn Pearce, git On Sat, Apr 07, 2012 at 10:05:00PM -0700, Junio C Hamano wrote: > > So I actually do think he would be better to implement the caching > > inside his helper, even if it is by calling out to git-credential-cache. > > While I'm for keeping the interface simple, at the same time, "I have this > credential obtained and it is valid for $X time duration" sounds like a > very common thing, and it is somewhat a shame for the API to force its > users (i.e. helpers) to reimplement the caching logic over and over. But the caching _must_ be part of a helper, because the parent git process is not long-lived. If I understand you correctly, you are advocating adding a "ttl" field, then transparently chaining to the cache helper when it exists. We can do that, but the whole point of implementing a generic helper protocol was to let people decide to do things like that themselves (which is exactly what Shawn has done). There's no reason that git needs to lock you into one particular implementation of caching or storage; that's why we have configurable helpers. There's no reason that git should only cache when there is a ttl field (you still may want to cache for 15 minutes, even if the credential will last longer). But nor should git say "you always must cache". Perhaps you don't like the complexity or the security implications. So I'd much rather leave it up to the user to configure their helpers rather than trying to run credential-cache behind the scenes. There are basically two problems to solve. The first is getting data from one helper to the other. One way to implement that is by just wrapping the real helper inside a caching layer. That can even be generic. In fact, my initial version of the credential helpers had a "chain" parameter, so you could ask credential-cache to run a sub-helper and cache its response. The original use was that "getpass" would be its own helper, so you could plug in a custom version. Since nobody seemed to want to do that, I dropped it in the name of simplicity. You can easily write a generic wrapper like this: #!/bin/sh cache=$1 chain=$2 case "$3" in get) cat >request eval "$cache get" <request >response if test -s response; then cat response else eval "$chain" >response cat request response | eval "$cache store" cat response fi ;; *) eval "$cache $3" ;; esac though obviously you would write it in a real language that doesn't involve storing the intermediate states on disk (and rather than eval, you'd take the usual helper specification). And we could easily provide such a wrapper, and Shawn's config would look like: [credential] helper = "wrapper cache real-helper" Another way to do it is by listing the helpers sequentially, and feeding the output of one to the input of another during a "store". As I said, I'm uncomfortable with the security implications of doing that by default. Your proposal side steps it by implying that cache is special, and does not have these security implications. Which is mostly true (though it also is limiting). So the obvious thing would be to mark helpers as either "yes, it's OK to share my output with others" or "yes, it's OK for me to see the output of others". And then Shawn's config looks like: [credential] helper = cache helper = real-helper The second issue is that of communicating the ttl or expiration between helpers. That's easy enough. The protocol allows arbitrary key/value pairs. We typically just drop ones we don't care about, but we could retain them and pass them along. -Peff ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH] credential: do not store credentials received from helpers 2012-04-08 6:40 ` Jeff King @ 2012-04-08 7:07 ` Jeff King 2012-04-08 7:13 ` Jeff King 1 sibling, 0 replies; 10+ messages in thread From: Jeff King @ 2012-04-08 7:07 UTC (permalink / raw) To: Junio C Hamano; +Cc: Shawn Pearce, git On Sun, Apr 08, 2012 at 02:40:59AM -0400, Jeff King wrote: > One way to implement that is by just wrapping the real helper inside a > caching layer. That can even be generic. Here's a C implementation of the shell sketch I posted earlier. Obviously missing documentation, and only lightly tested, but just to give a sense of what it would look like. You can exercise it manually with: { # simulate git's input echo protocol=https echo host=example.com } | git credential-wrap cache '!f() { # note whether we ran or not echo >&2 Generating... # and simulate output echo username=fake.username echo password=fake.password }; f' get or configure it with: git config credential.helper 'wrap cache your-real-helper' --- Makefile | 1 + credential-wrap.c | 32 ++++++++++++++++++++++++++++++++ credential.c | 4 ++-- credential.h | 3 +++ 4 files changed, 38 insertions(+), 2 deletions(-) create mode 100644 credential-wrap.c diff --git a/Makefile b/Makefile index be1957a..c91bb23 100644 --- a/Makefile +++ b/Makefile @@ -463,6 +463,7 @@ PROGRAM_OBJS += upload-pack.o PROGRAM_OBJS += http-backend.o PROGRAM_OBJS += sh-i18n--envsubst.o PROGRAM_OBJS += credential-store.o +PROGRAM_OBJS += credential-wrap.o # Binary suffix, set to .exe for Windows builds X = diff --git a/credential-wrap.c b/credential-wrap.c new file mode 100644 index 0000000..f4aadc4 --- /dev/null +++ b/credential-wrap.c @@ -0,0 +1,32 @@ +#include "cache.h" +#include "credential.h" + +int main(int argc, const char **argv) +{ + struct credential c = CREDENTIAL_INIT; + const char *storage, *source, *action; + + if (argc != 4) + usage("git credential-wrap <storage> <source> <action>"); + storage = argv[1]; + source = argv[2]; + action = argv[3]; + + if (credential_read(&c, stdin) < 0) + die("unable to read input credential"); + + if (!strcmp(action, "get")) { + credential_do(&c, storage, "get"); + if (!c.username || !c.password) { + credential_do(&c, source, "get"); + if (!c.username || !c.password) + return 0; + credential_do(&c, storage, "store"); + } + credential_write(&c, stdout); + } + else + credential_do(&c, storage, action); + + return 0; +} diff --git a/credential.c b/credential.c index 813e77a..13409e1 100644 --- a/credential.c +++ b/credential.c @@ -191,7 +191,7 @@ static void credential_write_item(FILE *fp, const char *key, const char *value) fprintf(fp, "%s=%s\n", key, value); } -static void credential_write(const struct credential *c, FILE *fp) +void credential_write(const struct credential *c, FILE *fp) { credential_write_item(fp, "protocol", c->protocol); credential_write_item(fp, "host", c->host); @@ -241,7 +241,7 @@ static int run_credential_helper(struct credential *c, return 0; } -static int credential_do(struct credential *c, const char *helper, +int credential_do(struct credential *c, const char *helper, const char *operation) { struct strbuf cmd = STRBUF_INIT; diff --git a/credential.h b/credential.h index 96ea41b..daf3e81 100644 --- a/credential.h +++ b/credential.h @@ -30,4 +30,7 @@ void credential_from_url(struct credential *, const char *url); int credential_match(const struct credential *have, const struct credential *want); +int credential_do(struct credential *, const char *helper, const char *action); +void credential_write(const struct credential *, FILE *); + #endif /* CREDENTIAL_H */ -- 1.7.10.11.g901cee ^ permalink raw reply related [flat|nested] 10+ messages in thread
* Re: [PATCH] credential: do not store credentials received from helpers 2012-04-08 6:40 ` Jeff King 2012-04-08 7:07 ` Jeff King @ 2012-04-08 7:13 ` Jeff King 1 sibling, 0 replies; 10+ messages in thread From: Jeff King @ 2012-04-08 7:13 UTC (permalink / raw) To: Junio C Hamano; +Cc: Shawn Pearce, git On Sun, Apr 08, 2012 at 02:40:59AM -0400, Jeff King wrote: > The second issue is that of communicating the ttl or expiration between > helpers. That's easy enough. The protocol allows arbitrary key/value > pairs. We typically just drop ones we don't care about, but we could > retain them and pass them along. And here's a rough patch for that. This is just to get an idea of the scale, and which parts of the code need changed. I'd probably use a key/value store instead of a string_list. On top of this, credential-cache would have to learn to respect a TTL variable in the input (actually, it does already respect "timeout" which is added on the way from the cache client to the cache daemon, but the parsing around that would have to be cleaned up a bit). --- credential.c | 14 +++++++++++--- credential.h | 3 ++- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/credential.c b/credential.c index 13409e1..2237e7e 100644 --- a/credential.c +++ b/credential.c @@ -9,6 +9,7 @@ void credential_init(struct credential *c) { memset(c, 0, sizeof(*c)); c->helpers.strdup_strings = 1; + c->extra.strdup_strings = 1; } void credential_clear(struct credential *c) @@ -19,6 +20,7 @@ void credential_clear(struct credential *c) free(c->username); free(c->password); string_list_clear(&c->helpers, 0); + string_list_clear(&c->extra, 0); credential_init(c); } @@ -174,10 +176,11 @@ int credential_read(struct credential *c, FILE *fp) c->path = xstrdup(value); } /* - * Ignore other lines; we don't know what they mean, but - * this future-proofs us when later versions of git do - * learn new lines, and the helpers are updated to match. + * Save other lines so they can be fed back to the helper or + * transported to other helpers. */ + *(value-1) = '='; + string_list_append(&c->extra, line.buf); } strbuf_release(&line); @@ -193,11 +196,16 @@ static void credential_write_item(FILE *fp, const char *key, const char *value) void credential_write(const struct credential *c, FILE *fp) { + int i; + credential_write_item(fp, "protocol", c->protocol); credential_write_item(fp, "host", c->host); credential_write_item(fp, "path", c->path); credential_write_item(fp, "username", c->username); credential_write_item(fp, "password", c->password); + + for (i = 0; i < c->extra.nr; i++) + fprintf(fp, "%s\n", c->extra.items[i].string); } static int run_credential_helper(struct credential *c, diff --git a/credential.h b/credential.h index daf3e81..5f98527 100644 --- a/credential.h +++ b/credential.h @@ -5,6 +5,7 @@ struct credential { struct string_list helpers; + struct string_list extra; unsigned approved:1, configured:1, use_http_path:1; @@ -16,7 +17,7 @@ struct credential { char *path; }; -#define CREDENTIAL_INIT { STRING_LIST_INIT_DUP } +#define CREDENTIAL_INIT { STRING_LIST_INIT_DUP, STRING_LIST_INIT_DUP } void credential_init(struct credential *); void credential_clear(struct credential *); -- 1.7.10.11.g901cee ^ permalink raw reply related [flat|nested] 10+ messages in thread
end of thread, other threads:[~2012-04-08 7:13 UTC | newest] Thread overview: 10+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2012-04-07 3:34 [PATCH] credential: do not store credentials received from helpers Jeff King 2012-04-07 4:12 ` Shawn Pearce 2012-04-07 4:56 ` Jeff King 2012-04-07 5:21 ` Jeff King 2012-04-07 4:56 ` Junio C Hamano 2012-04-07 5:09 ` Jeff King 2012-04-08 5:05 ` Junio C Hamano 2012-04-08 6:40 ` Jeff King 2012-04-08 7:07 ` Jeff King 2012-04-08 7:13 ` Jeff King
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).