Commit 7affc2f7dec48dc886e9838b102bdd42d06b1d2d

Carlos Martín Nieto 2013-08-11T23:30:47

Include username in each credential type Key-based authentication also needs an username, so include it in each one. Also stop assuming a default username of "git" in the ssh transport which has no business making such a decision.

diff --git a/include/git2/transport.h b/include/git2/transport.h
index 1cc200e..e61b104 100644
--- a/include/git2/transport.h
+++ b/include/git2/transport.h
@@ -59,6 +59,7 @@ typedef int (*git_cred_sign_callback)(void *, ...);
 /* A ssh key file and passphrase */
 typedef struct git_cred_ssh_keyfile_passphrase {
 	git_cred parent;
+	char *username;
 	char *publickey;
 	char *privatekey;
 	char *passphrase;
@@ -67,13 +68,22 @@ typedef struct git_cred_ssh_keyfile_passphrase {
 /* A ssh public key and authentication callback */
 typedef struct git_cred_ssh_publickey {
 	git_cred parent;
+	char *username;
 	char *publickey;
-    size_t publickey_len;
+	size_t publickey_len;
 	void *sign_callback;
 	void *sign_data;
 } git_cred_ssh_publickey;
 
 /**
+ * Check whether a credential object contains username information.
+ *
+ * @param cred object to check
+ * @return 1 if the credential object has non-NULL username, 0 otherwise
+ */
+GIT_EXTERN(int) git_cred_has_username(git_cred *cred);
+
+/**
  * Creates a new plain-text username and password credential object.
  * The supplied credential parameter will be internally duplicated.
  *
@@ -92,6 +102,7 @@ GIT_EXTERN(int) git_cred_userpass_plaintext_new(
  * The supplied credential parameter will be internally duplicated.
  *
  * @param out The newly created credential object.
+ * @param username username to use to authenticate
  * @param publickey The path to the public key of the credential.
  * @param privatekey The path to the private key of the credential.
  * @param passphrase The passphrase of the credential.
@@ -99,6 +110,7 @@ GIT_EXTERN(int) git_cred_userpass_plaintext_new(
  */
 GIT_EXTERN(int) git_cred_ssh_keyfile_passphrase_new(
 	git_cred **out,
+	const char *username,
 	const char *publickey,
 	const char *privatekey,
     const char *passphrase);
@@ -108,6 +120,7 @@ GIT_EXTERN(int) git_cred_ssh_keyfile_passphrase_new(
  * The supplied credential parameter will be internally duplicated.
  *
  * @param out The newly created credential object.
+ * @param username username to use to authenticate
  * @param publickey The bytes of the public key.
  * @param publickey_len The length of the public key in bytes.
  * @param sign_fn The callback method for authenticating.
@@ -116,6 +129,7 @@ GIT_EXTERN(int) git_cred_ssh_keyfile_passphrase_new(
  */
 GIT_EXTERN(int) git_cred_ssh_publickey_new(
 	git_cred **out,
+	const char *username,
 	const char *publickey,
     size_t publickey_len,
     git_cred_sign_callback sign_fn,
diff --git a/src/transports/cred.c b/src/transports/cred.c
index a6727e9..35aaf4f 100644
--- a/src/transports/cred.c
+++ b/src/transports/cred.c
@@ -9,6 +9,31 @@
 #include "smart.h"
 #include "git2/cred_helpers.h"
 
+int git_cred_has_username(git_cred *cred)
+{
+	int ret = 0;
+
+	switch (cred->credtype) {
+	case GIT_CREDTYPE_USERPASS_PLAINTEXT: {
+		git_cred_userpass_plaintext *c = (git_cred_userpass_plaintext *)cred;
+		ret = !!c->username;
+		break;
+	}
+	case GIT_CREDTYPE_SSH_KEYFILE_PASSPHRASE: {
+		git_cred_ssh_keyfile_passphrase *c = (git_cred_ssh_keyfile_passphrase *)cred;
+		ret = !!c->username;
+		break;
+	}
+	case GIT_CREDTYPE_SSH_PUBLICKEY: {
+		git_cred_ssh_publickey *c = (git_cred_ssh_publickey *)cred;
+		ret = !!c->username;
+		break;
+	}
+	}
+
+	return ret;
+}
+
 static void plaintext_free(struct git_cred *cred)
 {
 	git_cred_userpass_plaintext *c = (git_cred_userpass_plaintext *)cred;
@@ -64,6 +89,7 @@ static void ssh_keyfile_passphrase_free(struct git_cred *cred)
 	git_cred_ssh_keyfile_passphrase *c =
 		(git_cred_ssh_keyfile_passphrase *)cred;
 
+	git__free(c->username);
 	git__free(c->publickey);
 	git__free(c->privatekey);
 
@@ -82,6 +108,7 @@ static void ssh_publickey_free(struct git_cred *cred)
 {
 	git_cred_ssh_publickey *c = (git_cred_ssh_publickey *)cred;
 
+	git__free(c->username);
 	git__free(c->publickey);
 
 	git__memzero(c, sizeof(*c));
@@ -90,6 +117,7 @@ static void ssh_publickey_free(struct git_cred *cred)
 
 int git_cred_ssh_keyfile_passphrase_new(
 	git_cred **cred,
+	const char *username,
 	const char *publickey,
 	const char *privatekey,
 	const char *passphrase)
@@ -104,6 +132,11 @@ int git_cred_ssh_keyfile_passphrase_new(
 	c->parent.credtype = GIT_CREDTYPE_SSH_KEYFILE_PASSPHRASE;
 	c->parent.free = ssh_keyfile_passphrase_free;
 
+	if (username) {
+		c->username = git__strdup(username);
+		GITERR_CHECK_ALLOC(c->username);
+	}
+
 	c->privatekey = git__strdup(privatekey);
 	GITERR_CHECK_ALLOC(c->privatekey);
 
@@ -123,6 +156,7 @@ int git_cred_ssh_keyfile_passphrase_new(
 
 int git_cred_ssh_publickey_new(
 	git_cred **cred,
+	const char *username,
 	const char *publickey,
 	size_t publickey_len,
 	git_cred_sign_callback sign_callback,
@@ -138,6 +172,11 @@ int git_cred_ssh_publickey_new(
 	c->parent.credtype = GIT_CREDTYPE_SSH_PUBLICKEY;
 	c->parent.free = ssh_publickey_free;
 
+	if (username) {
+		c->username = git__strdup(username);
+		GITERR_CHECK_ALLOC(c->username);
+	}
+
 	if (publickey_len > 0) {
 		c->publickey = git__malloc(publickey_len);
 		GITERR_CHECK_ALLOC(c->publickey);
diff --git a/src/transports/ssh.c b/src/transports/ssh.c
index 7fb53bc..1258a8e 100644
--- a/src/transports/ssh.c
+++ b/src/transports/ssh.c
@@ -17,7 +17,6 @@
 #define OWNING_SUBTRANSPORT(s) ((ssh_subtransport *)(s)->parent.subtransport)
 
 static const char prefix_ssh[] = "ssh://";
-static const char default_user[] = "git";
 static const char cmd_uploadpack[] = "git-upload-pack";
 static const char cmd_receivepack[] = "git-receive-pack";
 
@@ -214,11 +213,10 @@ static int git_ssh_extract_url_parts(
 	if (at) {
 		start = at+1;
 		*username = git__substrdup(url, at - url);
+		GITERR_CHECK_ALLOC(*username);
 	} else {
-		start = url;
-		*username = git__strdup(default_user);
+		*username = NULL;
 	}
-	GITERR_CHECK_ALLOC(*username);
 
 	*host = git__substrdup(start, colon - start);
 	GITERR_CHECK_ALLOC(*host);
@@ -237,19 +235,23 @@ static int _git_ssh_authenticate_session(
 		switch (cred->credtype) {
 		case GIT_CREDTYPE_USERPASS_PLAINTEXT: {
 			git_cred_userpass_plaintext *c = (git_cred_userpass_plaintext *)cred;
-			rc = libssh2_userauth_password(session, c->username, c->password);
+			user = c->username ? c->username : user;
+			rc = libssh2_userauth_password(session, user, c->password);
 			break;
 		}
 		case GIT_CREDTYPE_SSH_KEYFILE_PASSPHRASE: {
 			git_cred_ssh_keyfile_passphrase *c = (git_cred_ssh_keyfile_passphrase *)cred;
+			user = c->username ? c->username : user;
 			rc = libssh2_userauth_publickey_fromfile(
-				session, user, c->publickey, c->privatekey, c->passphrase);
+				session, c->username, c->publickey, c->privatekey, c->passphrase);
 			break;
 		}
 		case GIT_CREDTYPE_SSH_PUBLICKEY: {
 			git_cred_ssh_publickey *c = (git_cred_ssh_publickey *)cred;
+
+			user = c->username ? c->username : user;
 			rc = libssh2_userauth_publickey(
-				session, user, (const unsigned char *)c->publickey,
+				session, c->username, (const unsigned char *)c->publickey,
 				c->publickey_len, c->sign_callback, &c->sign_data);
 			break;
 		}
@@ -351,9 +353,9 @@ static int _git_ssh_setup_conn(
 	}
 	assert(t->cred);
 
-	if (!user) {
-		user = git__strdup(default_user);
-		GITERR_CHECK_ALLOC(user);
+	if (!user && !git_cred_has_username(t->cred)) {
+		giterr_set_str(GITERR_NET, "Cannot authenticate without a username");
+		goto on_error;
 	}
 
 	if (_git_ssh_session_create(&session, s->socket) < 0)