[PATCH/RFC] add lame win32 credential-helper

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

 



Signed-off-by: Erik Faye-Lund <kusmabite@xxxxxxxxx>
---

I got curious what a credential-helper that uses Windows'
Credential Manager would look like; this is the result.

Some parts of the code is heavily inspired by Jay Soffian's
OSX-keychain work.

Not that it's useful yet, since the core-git code for the
credential-helper support doesn't compile on Windows. So
it's not fully tested, I've only read the interface
documentation and experimented with it from the command
line.

 contrib/credential-wincred/Makefile                |    8 +
 .../credential-wincred/git-credential-wincred.c    |  271 ++++++++++++++++++++
 2 files changed, 279 insertions(+), 0 deletions(-)
 create mode 100644 contrib/credential-wincred/Makefile
 create mode 100644 contrib/credential-wincred/git-credential-wincred.c

diff --git a/contrib/credential-wincred/Makefile b/contrib/credential-wincred/Makefile
new file mode 100644
index 0000000..b4f098f
--- /dev/null
+++ b/contrib/credential-wincred/Makefile
@@ -0,0 +1,8 @@
+all: git-credential-wincred.exe
+
+CC = gcc
+RM = rm -f
+CFLAGS = -O2 -Wall
+
+git-credential-wincred.exe : git-credential-wincred.c
+	$(LINK.c) $^ $(LOADLIBES) $(LDLIBS) -o $@
diff --git a/contrib/credential-wincred/git-credential-wincred.c b/contrib/credential-wincred/git-credential-wincred.c
new file mode 100644
index 0000000..5c0e2d3
--- /dev/null
+++ b/contrib/credential-wincred/git-credential-wincred.c
@@ -0,0 +1,271 @@
+#include <windows.h>
+#include <stdio.h>
+
+/* MinGW doesn't have wincred.h, let's extract the stuff we need instead */
+
+typedef struct _CREDENTIAL_ATTRIBUTE {
+	LPWSTR Keyword;
+	DWORD  Flags;
+	DWORD  ValueSize;
+	LPBYTE Value;
+} CREDENTIAL_ATTRIBUTE, *PCREDENTIAL_ATTRIBUTE;
+
+typedef struct _CREDENTIALW {
+	DWORD                 Flags;
+	DWORD                 Type;
+	LPWSTR                TargetName;
+	LPWSTR                Comment;
+	FILETIME              LastWritten;
+	DWORD                 CredentialBlobSize;
+	LPBYTE                CredentialBlob;
+	DWORD                 Persist;
+	DWORD                 AttributeCount;
+	PCREDENTIAL_ATTRIBUTE Attributes;
+	LPWSTR                TargetAlias;
+	LPWSTR                UserName;
+} CREDENTIALW, *PCREDENTIALW;
+
+typedef struct _CREDUI_INFOW {
+	DWORD   cbSize;
+	HWND    hwndParent;
+	LPWSTR  pszMessageText;
+	LPWSTR  pszCaptionText;
+	HBITMAP hbmBanner;
+} CREDUI_INFOW, *PCREDUI_INFOW;
+
+#define CRED_TYPE_GENERIC 1
+#define CRED_PERSIST_LOCAL_MACHINE 2
+#define CREDUIWIN_GENERIC 1
+#define CREDUIWIN_CHECKBOX 2
+#define CREDUIWIN_IN_CRED_ONLY 32
+#define CRED_PACK_GENERIC_CREDENTIALS 4
+
+
+typedef BOOL (WINAPI *CredWriteWT)(PCREDENTIALW, DWORD);
+typedef BOOL (WINAPI *CredUnPackAuthenticationBufferWT)(DWORD, PVOID, DWORD,
+    LPWSTR, DWORD *, LPWSTR, DWORD *, LPWSTR, DWORD *);
+typedef DWORD (WINAPI *CredUIPromptForWindowsCredentialsWT)(PCREDUI_INFOW,
+    DWORD, ULONG *, LPCVOID, ULONG, LPVOID *, ULONG *, BOOL *, DWORD);
+typedef BOOL (WINAPI *CredEnumerateWT)(LPCWSTR, DWORD, DWORD *,
+    PCREDENTIALW **);
+typedef BOOL (WINAPI *CredPackAuthenticationBufferWT)(DWORD, LPWSTR, LPWSTR,
+    PBYTE, DWORD *);
+typedef VOID (WINAPI *CredFreeT)(PVOID);
+typedef BOOL (WINAPI *CredDeleteWT)(LPCWSTR, DWORD, DWORD);
+
+static HMODULE advapi, credui;
+static CredWriteWT CredWriteW;
+static CredUnPackAuthenticationBufferWT CredUnPackAuthenticationBufferW;
+static CredUIPromptForWindowsCredentialsWT CredUIPromptForWindowsCredentialsW;
+static CredEnumerateWT CredEnumerateW;
+static CredPackAuthenticationBufferWT CredPackAuthenticationBufferW;
+static CredFreeT CredFree;
+static CredDeleteWT CredDeleteW;
+
+static void die(const char *err, ...)
+{
+	char msg[4096];
+	va_list params;
+	va_start(params, err);
+	vsnprintf(msg, sizeof(msg), err, params);
+	fprintf(stderr, "%s\n", msg);
+	va_end(params);
+	exit(1);
+}
+
+static void emit_user_pass(WCHAR *username, WCHAR *password)
+{
+	if (username)
+		wprintf(L"username=%s\n", username);
+	if (password)
+		wprintf(L"password=%s\n", password);
+}
+
+static int find_credentials(WCHAR *target, WCHAR *username)
+{
+	WCHAR user_buf[256], pass_buf[256];
+	DWORD user_buf_size = sizeof(user_buf) - 1,
+	      pass_buf_size = sizeof(pass_buf) - 1;
+	CREDENTIALW **creds, *cred = NULL;
+	DWORD num_creds;
+
+	if (!CredEnumerateW(target, 0, &num_creds, &creds))
+		return -1;
+
+	if (!username) {
+		/* no username was specified, just pick the first one */
+		cred = creds[0];
+	} else {
+		/* search for the first credential that matches username */
+		int i;
+		for (i = 0; i < num_creds; ++i)
+			if (!wcscmp(username, creds[i]->UserName)) {
+				cred = creds[i];
+				break;
+			}
+		if (!cred)
+			return -1;
+	}
+
+	if (!CredUnPackAuthenticationBufferW(0, cred->CredentialBlob,
+	    cred->CredentialBlobSize, user_buf, &user_buf_size, NULL, NULL,
+	    pass_buf, &pass_buf_size))
+		return -1;
+
+	CredFree(creds);
+
+	/* zero terminate */
+	user_buf[user_buf_size] = L'\0';
+	pass_buf[pass_buf_size] = L'\0';
+
+	emit_user_pass(user_buf, pass_buf);
+	return 0;
+}
+
+/* also saves the credentials if the user tells it to */
+static int ask_credentials(WCHAR *target, WCHAR *comment, WCHAR *username)
+{
+	BOOL save = FALSE;
+	LPVOID auth_buf = NULL;
+	ULONG auth_buf_size = 0;
+	WCHAR user_buf[256], pass_buf[256];
+	DWORD user_buf_size = sizeof(user_buf) - 1,
+	      pass_buf_size = sizeof(pass_buf) - 1;
+	BYTE in_buf[1024];
+	DWORD in_buf_size = sizeof(in_buf);
+	DWORD err;
+	ULONG package = 0;
+	CREDUI_INFOW info = {
+		sizeof(info), NULL,
+		comment ? comment : target, L"Enter password", NULL
+	};
+
+	if (username)
+		CredPackAuthenticationBufferW(0, username, L"",
+		    in_buf, &in_buf_size);
+	err = CredUIPromptForWindowsCredentialsW(&info, 0, &package,
+	    in_buf, in_buf_size, &auth_buf, &auth_buf_size,
+	    &save, CREDUIWIN_GENERIC | CREDUIWIN_CHECKBOX);
+	if (err == ERROR_CANCELLED)
+		return 0;
+	if (err != ERROR_SUCCESS)
+		return -1;
+
+	if (!CredUnPackAuthenticationBufferW(0, auth_buf, auth_buf_size,
+	    user_buf, &user_buf_size, NULL, NULL,
+	    pass_buf, &pass_buf_size))
+		return -1;
+
+	/* zero terminate */
+	user_buf[user_buf_size] = L'\0';
+	pass_buf[pass_buf_size] = L'\0';
+
+	emit_user_pass(user_buf, pass_buf);
+
+	if (save) {
+		CREDENTIALW cred;
+		cred.Flags = 0;
+		cred.Type = CRED_TYPE_GENERIC;
+		cred.TargetName = target;
+		cred.Comment = comment;
+		cred.CredentialBlobSize = auth_buf_size;
+		cred.CredentialBlob = auth_buf;
+		cred.Persist = CRED_PERSIST_LOCAL_MACHINE;
+		cred.AttributeCount = 0;
+		cred.Attributes = NULL;
+		cred.TargetAlias = NULL;
+		cred.UserName = user_buf;
+		if (!CredWriteW(&cred, 0))
+			fprintf(stderr, "failed to write credentials\n");
+	}
+	return 0;
+}
+
+static void delete_credentials(WCHAR *target, WCHAR *username)
+{
+	WCHAR temp[4096];
+
+	wcscpy(temp, target);
+	if (username) {
+		wcscat(temp, L"|");
+		wcscat(temp, username);
+	}
+	if (!CredDeleteW(target, CRED_TYPE_GENERIC, 0))
+		die("failed to delete credentials");
+}
+
+int main(int argc, char *argv[])
+{
+	const char *usage =
+	    "Usage: git credential-osxkeychain --unique=TOKEN [options]\n"
+	    "Options:\n"
+	    "    --description=DESCRIPTION\n"
+	    "    --username=USERNAME\n"
+	    "    --reject";
+	WCHAR desc_buf[4096], *description = NULL,
+	      user_buf[256], *username = NULL,
+	      unique_buf[1024], *unique = NULL;
+	int i, reject = 0;
+
+	for (i = 1; i < argc; ++i) {
+		const char *arg = argv[i];
+		if (!strncmp(arg, "--description=", 14)) {
+			MultiByteToWideChar(CP_UTF8, 0, arg + 14, -1,
+			    desc_buf, sizeof(desc_buf));
+			description = desc_buf;
+		} else if (!strncmp(arg, "--username=", 11)) {
+			MultiByteToWideChar(CP_UTF8, 0, arg + 11, -1,
+			    user_buf, sizeof(user_buf));
+			username = user_buf;
+		} else if (!strncmp(arg, "--unique=", 9)) {
+			MultiByteToWideChar(CP_UTF8, 0, arg + 9, -1,
+			    unique_buf, sizeof(unique_buf));
+			unique = unique_buf;
+		} else if (!strcmp(arg, "--reject")) {
+			reject = 1;
+		} else if (!strcmp(arg, "--help")) {
+			die(usage);
+		} else
+			die("Unrecognized argument `%s'; try --help", arg);
+	}
+
+	if (!unique)
+		die("Must specify --unique=TOKEN; try --help");
+
+	/* load DLLs */
+	advapi = LoadLibrary("advapi32.dll");
+	credui = LoadLibrary("credui.dll");
+	if (!advapi || !credui)
+		die("failed to load DLLs");
+
+	/* get function pointers */
+	CredWriteW = (CredWriteWT)GetProcAddress(advapi, "CredWriteW");
+	CredUnPackAuthenticationBufferW = (CredUnPackAuthenticationBufferWT)
+	    GetProcAddress(credui, "CredUnPackAuthenticationBufferW");
+	CredUIPromptForWindowsCredentialsW =
+	    (CredUIPromptForWindowsCredentialsWT)GetProcAddress(credui,
+	    "CredUIPromptForWindowsCredentialsW");
+	CredEnumerateW = (CredEnumerateWT)GetProcAddress(advapi,
+	    "CredEnumerateW");
+	CredPackAuthenticationBufferW = (CredPackAuthenticationBufferWT)
+	    GetProcAddress(credui, "CredPackAuthenticationBufferW");
+	CredFree = (CredFreeT)GetProcAddress(advapi, "CredFree");
+	CredDeleteW = (CredDeleteWT)GetProcAddress(advapi, "CredDeleteW");
+	if (!CredWriteW || !CredUnPackAuthenticationBufferW ||
+	    !CredUIPromptForWindowsCredentialsW || !CredEnumerateW ||
+	    !CredPackAuthenticationBufferW || !CredFree || !CredDeleteW)
+		die("failed to load functions");
+
+	if (reject) {
+		delete_credentials(unique, username);
+		return 0;
+	}
+
+	if (!find_credentials(unique, username))
+		return 0;
+
+	if (!ask_credentials(unique, description, username))
+		return 0;
+
+	return -1;
+}
-- 
1.7.6.355.g842ba.dirty

--
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]