Re: [PATCHv2 0/13] credential helpers

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Thu, Dec 08, 2011 at 01:34:29PM -0800, Junio C Hamano wrote:

> Jeff King <peff@xxxxxxxx> writes:
> 
> > Because the pattern takes 0 or more lines and no terminator, we can't
> > distinguish between empty or truncated input and the empty pattern.
> 
> I agree that such a positive "Ok here is the end of specification" marker
> is a good idea, even if we do not worry about "an empty set".
> 
> When the requestor wants to specify the credentials with host and user,
> but the wire is cut after host is communicated but before user is, we do
> want to notice the communication error, instead of silently erasing all
> the credentials on the host regardless of the user.

OK, I've tweaked the series to require an end-of-credential marker (a
blank line) both in input and output.

In addition, I've changed the code that runs helpers to make reading
from the helpers an all-or-nothing thing (instead of incrementally
ovewriting our credential as we read from it). Before, if a helper
exited with error, we would happily use its partial result. Instead, we
now read its response into a holding area, and only copy it into our
credential when we get a successful exit code. This lets us detect
truncation when reading from the helper, too.

It works, and it detects truncated output both ways properly (I know
because I had to update every test, since the old output was missing the
end-of-credential marker).

It makes me a little sad, because the original format (relying on EOF)
was so Unix-y. You could make a helper like this:

  echo password=`gpg -qd ~/.secret.gpg`

and now you must remember to tack an extra "echo" at the end. Not a big
deal, but it somehow just feels less elegant to my gut.  OTOH, classic
Unix constructs have always been a nightmare for robustness and error
checking[1], so this is certainly nothing new.

The diff from this tip to the old tip is below to give you a sense of
the magnitude of the change (the individual changes are squashed into
their respective patches for the next re-roll, of course). I'll hold off
on posting the whole series to see if we get any more comments.

-Peff

[1] I mean things like:

      grep foo bar | sed 's/some/transformation/'

    where we totally ignore errors from grep, and where a truncated
    output on the pipe would just subtly generate wrong answers.
---

 Documentation/technical/api-credentials.txt |    2 +-
 credential-cache--daemon.c                  |    1 +
 credential-cache.c                          |    2 +
 credential-store.c                          |    1 +
 credential.c                                |   39 +++++++++++++++++++++++---
 t/lib-credential.sh                         |    1 +
 t/t0300-credentials.sh                      |    3 ++
 t/t5550-http-fetch.sh                       |    1 +
 8 files changed, 44 insertions(+), 6 deletions(-)

diff --git a/Documentation/technical/api-credentials.txt b/Documentation/technical/api-credentials.txt
index 21ca6a2..0aac899 100644
--- a/Documentation/technical/api-credentials.txt
+++ b/Documentation/technical/api-credentials.txt
@@ -199,7 +199,7 @@ followed by a newline. The key may contain any bytes except `=`,
 newline, or NUL. The value may contain any bytes except newline or NUL.
 In both cases, all bytes are treated as-is (i.e., there is no quoting,
 and one cannot transmit a value with newline or NUL in it). The list of
-attributes is terminated by a blank line or end-of-file.
+attributes is terminated by a blank line.
 
 Git will send the following attributes (but may not send all of
 them for a given credential; for example, a `host` attribute makes no
diff --git a/credential-cache--daemon.c b/credential-cache--daemon.c
index 390f194..38403645 100644
--- a/credential-cache--daemon.c
+++ b/credential-cache--daemon.c
@@ -138,6 +138,7 @@ static void serve_one_client(FILE *in, FILE *out)
 		if (e) {
 			fprintf(out, "username=%s\n", e->item.username);
 			fprintf(out, "password=%s\n", e->item.password);
+			fprintf(out, "\n");
 		}
 	}
 	else if (!strcmp(action.buf, "exit"))
diff --git a/credential-cache.c b/credential-cache.c
index dc98372..5b8d8c9 100644
--- a/credential-cache.c
+++ b/credential-cache.c
@@ -70,6 +70,8 @@ static void do_cache(const char *socket, const char *action, int timeout,
 		if (strbuf_read(&buf, 0, 0) < 0)
 			die_errno("unable to relay credential");
 	}
+	else
+		strbuf_addch(&buf, '\n');
 
 	if (!send_request(socket, &buf))
 		return;
diff --git a/credential-store.c b/credential-store.c
index ed58768..00e38f0 100644
--- a/credential-store.c
+++ b/credential-store.c
@@ -43,6 +43,7 @@ static void print_entry(struct credential *c)
 {
 	printf("username=%s\n", c->username);
 	printf("password=%s\n", c->password);
+	printf("\n");
 }
 
 static void print_line(struct strbuf *buf)
diff --git a/credential.c b/credential.c
index a17eafe..6d2a37d 100644
--- a/credential.c
+++ b/credential.c
@@ -147,8 +147,10 @@ int credential_read(struct credential *c, FILE *fp)
 		char *key = line.buf;
 		char *value = strchr(key, '=');
 
-		if (!line.len)
-			break;
+		if (!line.len) {
+			strbuf_release(&line);
+			return 0;
+		}
 
 		if (!value) {
 			warning("invalid credential line: %s", key);
@@ -181,7 +183,7 @@ int credential_read(struct credential *c, FILE *fp)
 	}
 
 	strbuf_release(&line);
-	return 0;
+	return -1;
 }
 
 static void credential_write_item(FILE *fp, const char *key, const char *value)
@@ -198,6 +200,26 @@ static void credential_write(const struct credential *c, FILE *fp)
 	credential_write_item(fp, "path", c->path);
 	credential_write_item(fp, "username", c->username);
 	credential_write_item(fp, "password", c->password);
+	putc('\n', fp);
+}
+
+static void credential_merge_one(char **dst, char **src)
+{
+	if (!*src)
+		return;
+	free(*dst);
+	*dst = *src;
+	*src = NULL;
+}
+
+static void credential_merge(struct credential *dst,
+			     struct credential *src)
+{
+	credential_merge_one(&dst->protocol, &src->protocol);
+	credential_merge_one(&dst->host, &src->host);
+	credential_merge_one(&dst->path, &src->path);
+	credential_merge_one(&dst->username, &src->username);
+	credential_merge_one(&dst->password, &src->password);
 }
 
 static int run_credential_helper(struct credential *c,
@@ -206,6 +228,7 @@ static int run_credential_helper(struct credential *c,
 {
 	struct child_process helper;
 	const char *argv[] = { NULL, NULL };
+	struct credential response = CREDENTIAL_INIT;
 	FILE *fp;
 
 	memset(&helper, 0, sizeof(helper));
@@ -227,17 +250,23 @@ static int run_credential_helper(struct credential *c,
 
 	if (want_output) {
 		int r;
+
 		fp = xfdopen(helper.out, "r");
-		r = credential_read(c, fp);
+		r = credential_read(&response, fp);
 		fclose(fp);
 		if (r < 0) {
+			credential_clear(&response);
 			finish_command(&helper);
 			return -1;
 		}
 	}
 
-	if (finish_command(&helper))
+	if (finish_command(&helper)) {
+		credential_clear(&response);
 		return -1;
+	}
+
+	credential_merge(c, &response);
 	return 0;
 }
 
diff --git a/t/lib-credential.sh b/t/lib-credential.sh
index fc34447..c0de4e9 100755
--- a/t/lib-credential.sh
+++ b/t/lib-credential.sh
@@ -5,6 +5,7 @@
 # separated by "--".
 check() {
 	read_chunk >stdin &&
+	echo >>stdin &&
 	read_chunk >expect-stdout &&
 	read_chunk >expect-stderr &&
 	test-credential "$@" <stdin >stdout 2>stderr &&
diff --git a/t/t0300-credentials.sh b/t/t0300-credentials.sh
index 885af8f..f0e77dc 100755
--- a/t/t0300-credentials.sh
+++ b/t/t0300-credentials.sh
@@ -9,6 +9,7 @@ test_expect_success 'setup helper scripts' '
 	whoami=`echo $0 | sed s/.*git-credential-//`
 	echo >&2 "$whoami: $*"
 	while IFS== read key value; do
+		test -z "$key" && break
 		echo >&2 "$whoami: $key=$value"
 		eval "$key=$value"
 	done
@@ -28,6 +29,7 @@ test_expect_success 'setup helper scripts' '
 	. ./dump
 	test -z "$user" || echo username=$user
 	test -z "$pass" || echo password=$pass
+	echo
 	EOF
 	chmod +x git-credential-verbatim &&
 
@@ -196,6 +198,7 @@ HELPER="!f() {
 		cat >/dev/null
 		echo username=foo
 		echo password=bar
+		echo
 	}; f"
 test_expect_success 'respect configured credentials' '
 	test_config credential.helper "$HELPER" &&
diff --git a/t/t5550-http-fetch.sh b/t/t5550-http-fetch.sh
index 95a133d..b817c69 100755
--- a/t/t5550-http-fetch.sh
+++ b/t/t5550-http-fetch.sh
@@ -106,6 +106,7 @@ test_expect_success 'http auth respects credential helper config' '
 		cat >/dev/null
 		echo username=user@host
 		echo password=user@host
+		echo
 	}; f" &&
 	>askpass-query &&
 	echo wrong >askpass-response &&
--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [Linux Kernel Development]     [Gcc Help]     [IETF Annouce]     [DCCP]     [Netdev]     [Networking]     [Security]     [V4L]     [Bugtraq]     [Yosemite]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux SCSI]     [Fedora Users]