merge(third_party/git): Merge squashed git subtree at v2.23.0
Merge commit '1b593e1ea4
' as 'third_party/git'
This commit is contained in:
commit
7ef0d62730
3629 changed files with 1139935 additions and 0 deletions
1
third_party/git/contrib/credential/gnome-keyring/.gitignore
vendored
Normal file
1
third_party/git/contrib/credential/gnome-keyring/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
git-credential-gnome-keyring
|
25
third_party/git/contrib/credential/gnome-keyring/Makefile
vendored
Normal file
25
third_party/git/contrib/credential/gnome-keyring/Makefile
vendored
Normal file
|
@ -0,0 +1,25 @@
|
|||
MAIN:=git-credential-gnome-keyring
|
||||
all:: $(MAIN)
|
||||
|
||||
CC = gcc
|
||||
RM = rm -f
|
||||
CFLAGS = -g -O2 -Wall
|
||||
PKG_CONFIG = pkg-config
|
||||
|
||||
-include ../../../config.mak.autogen
|
||||
-include ../../../config.mak
|
||||
|
||||
INCS:=$(shell $(PKG_CONFIG) --cflags gnome-keyring-1 glib-2.0)
|
||||
LIBS:=$(shell $(PKG_CONFIG) --libs gnome-keyring-1 glib-2.0)
|
||||
|
||||
SRCS:=$(MAIN).c
|
||||
OBJS:=$(SRCS:.c=.o)
|
||||
|
||||
%.o: %.c
|
||||
$(CC) $(CFLAGS) $(CPPFLAGS) $(INCS) -o $@ -c $<
|
||||
|
||||
$(MAIN): $(OBJS)
|
||||
$(CC) -o $@ $(LDFLAGS) $^ $(LIBS)
|
||||
|
||||
clean:
|
||||
@$(RM) $(MAIN) $(OBJS)
|
470
third_party/git/contrib/credential/gnome-keyring/git-credential-gnome-keyring.c
vendored
Normal file
470
third_party/git/contrib/credential/gnome-keyring/git-credential-gnome-keyring.c
vendored
Normal file
|
@ -0,0 +1,470 @@
|
|||
/*
|
||||
* Copyright (C) 2011 John Szakmeister <john@szakmeister.net>
|
||||
* 2012 Philipp A. Hartmann <pah@qo.cx>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Credits:
|
||||
* - GNOME Keyring API handling originally written by John Szakmeister
|
||||
* - ported to credential helper API by Philipp A. Hartmann
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <glib.h>
|
||||
#include <gnome-keyring.h>
|
||||
|
||||
#ifdef GNOME_KEYRING_DEFAULT
|
||||
|
||||
/* Modern gnome-keyring */
|
||||
|
||||
#include <gnome-keyring-memory.h>
|
||||
|
||||
#else
|
||||
|
||||
/*
|
||||
* Support ancient gnome-keyring, circ. RHEL 5.X.
|
||||
* GNOME_KEYRING_DEFAULT seems to have been introduced with Gnome 2.22,
|
||||
* and the other features roughly around Gnome 2.20, 6 months before.
|
||||
* Ubuntu 8.04 used Gnome 2.22 (I think). Not sure any distro used 2.20.
|
||||
* So the existence/non-existence of GNOME_KEYRING_DEFAULT seems like
|
||||
* a decent thing to use as an indicator.
|
||||
*/
|
||||
|
||||
#define GNOME_KEYRING_DEFAULT NULL
|
||||
|
||||
/*
|
||||
* ancient gnome-keyring returns DENIED when an entry is not found.
|
||||
* Setting NO_MATCH to DENIED will prevent us from reporting DENIED
|
||||
* errors during get and erase operations, but we will still report
|
||||
* DENIED errors during a store.
|
||||
*/
|
||||
#define GNOME_KEYRING_RESULT_NO_MATCH GNOME_KEYRING_RESULT_DENIED
|
||||
|
||||
#define gnome_keyring_memory_alloc g_malloc
|
||||
#define gnome_keyring_memory_free gnome_keyring_free_password
|
||||
#define gnome_keyring_memory_strdup g_strdup
|
||||
|
||||
static const char *gnome_keyring_result_to_message(GnomeKeyringResult result)
|
||||
{
|
||||
switch (result) {
|
||||
case GNOME_KEYRING_RESULT_OK:
|
||||
return "OK";
|
||||
case GNOME_KEYRING_RESULT_DENIED:
|
||||
return "Denied";
|
||||
case GNOME_KEYRING_RESULT_NO_KEYRING_DAEMON:
|
||||
return "No Keyring Daemon";
|
||||
case GNOME_KEYRING_RESULT_ALREADY_UNLOCKED:
|
||||
return "Already UnLocked";
|
||||
case GNOME_KEYRING_RESULT_NO_SUCH_KEYRING:
|
||||
return "No Such Keyring";
|
||||
case GNOME_KEYRING_RESULT_BAD_ARGUMENTS:
|
||||
return "Bad Arguments";
|
||||
case GNOME_KEYRING_RESULT_IO_ERROR:
|
||||
return "IO Error";
|
||||
case GNOME_KEYRING_RESULT_CANCELLED:
|
||||
return "Cancelled";
|
||||
case GNOME_KEYRING_RESULT_ALREADY_EXISTS:
|
||||
return "Already Exists";
|
||||
default:
|
||||
return "Unknown Error";
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Support really ancient gnome-keyring, circ. RHEL 4.X.
|
||||
* Just a guess for the Glib version. Glib 2.8 was roughly Gnome 2.12 ?
|
||||
* Which was released with gnome-keyring 0.4.3 ??
|
||||
*/
|
||||
#if GLIB_MAJOR_VERSION == 2 && GLIB_MINOR_VERSION < 8
|
||||
|
||||
static void gnome_keyring_done_cb(GnomeKeyringResult result, gpointer user_data)
|
||||
{
|
||||
gpointer *data = (gpointer *)user_data;
|
||||
int *done = (int *)data[0];
|
||||
GnomeKeyringResult *r = (GnomeKeyringResult *)data[1];
|
||||
|
||||
*r = result;
|
||||
*done = 1;
|
||||
}
|
||||
|
||||
static void wait_for_request_completion(int *done)
|
||||
{
|
||||
GMainContext *mc = g_main_context_default();
|
||||
while (!*done)
|
||||
g_main_context_iteration(mc, TRUE);
|
||||
}
|
||||
|
||||
static GnomeKeyringResult gnome_keyring_item_delete_sync(const char *keyring, guint32 id)
|
||||
{
|
||||
int done = 0;
|
||||
GnomeKeyringResult result;
|
||||
gpointer data[] = { &done, &result };
|
||||
|
||||
gnome_keyring_item_delete(keyring, id, gnome_keyring_done_cb, data,
|
||||
NULL);
|
||||
|
||||
wait_for_request_completion(&done);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/*
|
||||
* This credential struct and API is simplified from git's credential.{h,c}
|
||||
*/
|
||||
struct credential {
|
||||
char *protocol;
|
||||
char *host;
|
||||
unsigned short port;
|
||||
char *path;
|
||||
char *username;
|
||||
char *password;
|
||||
};
|
||||
|
||||
#define CREDENTIAL_INIT { NULL, NULL, 0, NULL, NULL, NULL }
|
||||
|
||||
typedef int (*credential_op_cb)(struct credential *);
|
||||
|
||||
struct credential_operation {
|
||||
char *name;
|
||||
credential_op_cb op;
|
||||
};
|
||||
|
||||
#define CREDENTIAL_OP_END { NULL, NULL }
|
||||
|
||||
/* ----------------- GNOME Keyring functions ----------------- */
|
||||
|
||||
/* create a special keyring option string, if path is given */
|
||||
static char *keyring_object(struct credential *c)
|
||||
{
|
||||
if (!c->path)
|
||||
return NULL;
|
||||
|
||||
if (c->port)
|
||||
return g_strdup_printf("%s:%hd/%s", c->host, c->port, c->path);
|
||||
|
||||
return g_strdup_printf("%s/%s", c->host, c->path);
|
||||
}
|
||||
|
||||
static int keyring_get(struct credential *c)
|
||||
{
|
||||
char *object = NULL;
|
||||
GList *entries;
|
||||
GnomeKeyringNetworkPasswordData *password_data;
|
||||
GnomeKeyringResult result;
|
||||
|
||||
if (!c->protocol || !(c->host || c->path))
|
||||
return EXIT_FAILURE;
|
||||
|
||||
object = keyring_object(c);
|
||||
|
||||
result = gnome_keyring_find_network_password_sync(
|
||||
c->username,
|
||||
NULL /* domain */,
|
||||
c->host,
|
||||
object,
|
||||
c->protocol,
|
||||
NULL /* authtype */,
|
||||
c->port,
|
||||
&entries);
|
||||
|
||||
g_free(object);
|
||||
|
||||
if (result == GNOME_KEYRING_RESULT_NO_MATCH)
|
||||
return EXIT_SUCCESS;
|
||||
|
||||
if (result == GNOME_KEYRING_RESULT_CANCELLED)
|
||||
return EXIT_SUCCESS;
|
||||
|
||||
if (result != GNOME_KEYRING_RESULT_OK) {
|
||||
g_critical("%s", gnome_keyring_result_to_message(result));
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
/* pick the first one from the list */
|
||||
password_data = (GnomeKeyringNetworkPasswordData *)entries->data;
|
||||
|
||||
gnome_keyring_memory_free(c->password);
|
||||
c->password = gnome_keyring_memory_strdup(password_data->password);
|
||||
|
||||
if (!c->username)
|
||||
c->username = g_strdup(password_data->user);
|
||||
|
||||
gnome_keyring_network_password_list_free(entries);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
static int keyring_store(struct credential *c)
|
||||
{
|
||||
guint32 item_id;
|
||||
char *object = NULL;
|
||||
GnomeKeyringResult result;
|
||||
|
||||
/*
|
||||
* Sanity check that what we are storing is actually sensible.
|
||||
* In particular, we can't make a URL without a protocol field.
|
||||
* Without either a host or pathname (depending on the scheme),
|
||||
* we have no primary key. And without a username and password,
|
||||
* we are not actually storing a credential.
|
||||
*/
|
||||
if (!c->protocol || !(c->host || c->path) ||
|
||||
!c->username || !c->password)
|
||||
return EXIT_FAILURE;
|
||||
|
||||
object = keyring_object(c);
|
||||
|
||||
result = gnome_keyring_set_network_password_sync(
|
||||
GNOME_KEYRING_DEFAULT,
|
||||
c->username,
|
||||
NULL /* domain */,
|
||||
c->host,
|
||||
object,
|
||||
c->protocol,
|
||||
NULL /* authtype */,
|
||||
c->port,
|
||||
c->password,
|
||||
&item_id);
|
||||
|
||||
g_free(object);
|
||||
|
||||
if (result != GNOME_KEYRING_RESULT_OK &&
|
||||
result != GNOME_KEYRING_RESULT_CANCELLED) {
|
||||
g_critical("%s", gnome_keyring_result_to_message(result));
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
static int keyring_erase(struct credential *c)
|
||||
{
|
||||
char *object = NULL;
|
||||
GList *entries;
|
||||
GnomeKeyringNetworkPasswordData *password_data;
|
||||
GnomeKeyringResult result;
|
||||
|
||||
/*
|
||||
* Sanity check that we actually have something to match
|
||||
* against. The input we get is a restrictive pattern,
|
||||
* so technically a blank credential means "erase everything".
|
||||
* But it is too easy to accidentally send this, since it is equivalent
|
||||
* to empty input. So explicitly disallow it, and require that the
|
||||
* pattern have some actual content to match.
|
||||
*/
|
||||
if (!c->protocol && !c->host && !c->path && !c->username)
|
||||
return EXIT_FAILURE;
|
||||
|
||||
object = keyring_object(c);
|
||||
|
||||
result = gnome_keyring_find_network_password_sync(
|
||||
c->username,
|
||||
NULL /* domain */,
|
||||
c->host,
|
||||
object,
|
||||
c->protocol,
|
||||
NULL /* authtype */,
|
||||
c->port,
|
||||
&entries);
|
||||
|
||||
g_free(object);
|
||||
|
||||
if (result == GNOME_KEYRING_RESULT_NO_MATCH)
|
||||
return EXIT_SUCCESS;
|
||||
|
||||
if (result == GNOME_KEYRING_RESULT_CANCELLED)
|
||||
return EXIT_SUCCESS;
|
||||
|
||||
if (result != GNOME_KEYRING_RESULT_OK) {
|
||||
g_critical("%s", gnome_keyring_result_to_message(result));
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
/* pick the first one from the list (delete all matches?) */
|
||||
password_data = (GnomeKeyringNetworkPasswordData *)entries->data;
|
||||
|
||||
result = gnome_keyring_item_delete_sync(
|
||||
password_data->keyring, password_data->item_id);
|
||||
|
||||
gnome_keyring_network_password_list_free(entries);
|
||||
|
||||
if (result != GNOME_KEYRING_RESULT_OK) {
|
||||
g_critical("%s", gnome_keyring_result_to_message(result));
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
* Table with helper operation callbacks, used by generic
|
||||
* credential helper main function.
|
||||
*/
|
||||
static struct credential_operation const credential_helper_ops[] = {
|
||||
{ "get", keyring_get },
|
||||
{ "store", keyring_store },
|
||||
{ "erase", keyring_erase },
|
||||
CREDENTIAL_OP_END
|
||||
};
|
||||
|
||||
/* ------------------ credential functions ------------------ */
|
||||
|
||||
static void credential_init(struct credential *c)
|
||||
{
|
||||
memset(c, 0, sizeof(*c));
|
||||
}
|
||||
|
||||
static void credential_clear(struct credential *c)
|
||||
{
|
||||
g_free(c->protocol);
|
||||
g_free(c->host);
|
||||
g_free(c->path);
|
||||
g_free(c->username);
|
||||
gnome_keyring_memory_free(c->password);
|
||||
|
||||
credential_init(c);
|
||||
}
|
||||
|
||||
static int credential_read(struct credential *c)
|
||||
{
|
||||
char *buf;
|
||||
size_t line_len;
|
||||
char *key;
|
||||
char *value;
|
||||
|
||||
key = buf = gnome_keyring_memory_alloc(1024);
|
||||
|
||||
while (fgets(buf, 1024, stdin)) {
|
||||
line_len = strlen(buf);
|
||||
|
||||
if (line_len && buf[line_len-1] == '\n')
|
||||
buf[--line_len] = '\0';
|
||||
|
||||
if (!line_len)
|
||||
break;
|
||||
|
||||
value = strchr(buf, '=');
|
||||
if (!value) {
|
||||
g_warning("invalid credential line: %s", key);
|
||||
gnome_keyring_memory_free(buf);
|
||||
return -1;
|
||||
}
|
||||
*value++ = '\0';
|
||||
|
||||
if (!strcmp(key, "protocol")) {
|
||||
g_free(c->protocol);
|
||||
c->protocol = g_strdup(value);
|
||||
} else if (!strcmp(key, "host")) {
|
||||
g_free(c->host);
|
||||
c->host = g_strdup(value);
|
||||
value = strrchr(c->host, ':');
|
||||
if (value) {
|
||||
*value++ = '\0';
|
||||
c->port = atoi(value);
|
||||
}
|
||||
} else if (!strcmp(key, "path")) {
|
||||
g_free(c->path);
|
||||
c->path = g_strdup(value);
|
||||
} else if (!strcmp(key, "username")) {
|
||||
g_free(c->username);
|
||||
c->username = g_strdup(value);
|
||||
} else if (!strcmp(key, "password")) {
|
||||
gnome_keyring_memory_free(c->password);
|
||||
c->password = gnome_keyring_memory_strdup(value);
|
||||
while (*value)
|
||||
*value++ = '\0';
|
||||
}
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
}
|
||||
|
||||
gnome_keyring_memory_free(buf);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void credential_write_item(FILE *fp, const char *key, const char *value)
|
||||
{
|
||||
if (!value)
|
||||
return;
|
||||
fprintf(fp, "%s=%s\n", key, value);
|
||||
}
|
||||
|
||||
static void credential_write(const struct credential *c)
|
||||
{
|
||||
/* only write username/password, if set */
|
||||
credential_write_item(stdout, "username", c->username);
|
||||
credential_write_item(stdout, "password", c->password);
|
||||
}
|
||||
|
||||
static void usage(const char *name)
|
||||
{
|
||||
struct credential_operation const *try_op = credential_helper_ops;
|
||||
const char *basename = strrchr(name, '/');
|
||||
|
||||
basename = (basename) ? basename + 1 : name;
|
||||
fprintf(stderr, "usage: %s <", basename);
|
||||
while (try_op->name) {
|
||||
fprintf(stderr, "%s", (try_op++)->name);
|
||||
if (try_op->name)
|
||||
fprintf(stderr, "%s", "|");
|
||||
}
|
||||
fprintf(stderr, "%s", ">\n");
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int ret = EXIT_SUCCESS;
|
||||
|
||||
struct credential_operation const *try_op = credential_helper_ops;
|
||||
struct credential cred = CREDENTIAL_INIT;
|
||||
|
||||
if (!argv[1]) {
|
||||
usage(argv[0]);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
g_set_application_name("Git Credential Helper");
|
||||
|
||||
/* lookup operation callback */
|
||||
while (try_op->name && strcmp(argv[1], try_op->name))
|
||||
try_op++;
|
||||
|
||||
/* unsupported operation given -- ignore silently */
|
||||
if (!try_op->name || !try_op->op)
|
||||
goto out;
|
||||
|
||||
ret = credential_read(&cred);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
/* perform credential operation */
|
||||
ret = (*try_op->op)(&cred);
|
||||
|
||||
credential_write(&cred);
|
||||
|
||||
out:
|
||||
credential_clear(&cred);
|
||||
return ret;
|
||||
}
|
25
third_party/git/contrib/credential/libsecret/Makefile
vendored
Normal file
25
third_party/git/contrib/credential/libsecret/Makefile
vendored
Normal file
|
@ -0,0 +1,25 @@
|
|||
MAIN:=git-credential-libsecret
|
||||
all:: $(MAIN)
|
||||
|
||||
CC = gcc
|
||||
RM = rm -f
|
||||
CFLAGS = -g -O2 -Wall
|
||||
PKG_CONFIG = pkg-config
|
||||
|
||||
-include ../../../config.mak.autogen
|
||||
-include ../../../config.mak
|
||||
|
||||
INCS:=$(shell $(PKG_CONFIG) --cflags libsecret-1 glib-2.0)
|
||||
LIBS:=$(shell $(PKG_CONFIG) --libs libsecret-1 glib-2.0)
|
||||
|
||||
SRCS:=$(MAIN).c
|
||||
OBJS:=$(SRCS:.c=.o)
|
||||
|
||||
%.o: %.c
|
||||
$(CC) $(CFLAGS) $(CPPFLAGS) $(INCS) -o $@ -c $<
|
||||
|
||||
$(MAIN): $(OBJS)
|
||||
$(CC) -o $@ $(LDFLAGS) $^ $(LIBS)
|
||||
|
||||
clean:
|
||||
@$(RM) $(MAIN) $(OBJS)
|
369
third_party/git/contrib/credential/libsecret/git-credential-libsecret.c
vendored
Normal file
369
third_party/git/contrib/credential/libsecret/git-credential-libsecret.c
vendored
Normal file
|
@ -0,0 +1,369 @@
|
|||
/*
|
||||
* Copyright (C) 2011 John Szakmeister <john@szakmeister.net>
|
||||
* 2012 Philipp A. Hartmann <pah@qo.cx>
|
||||
* 2016 Mantas Mikulėnas <grawity@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Credits:
|
||||
* - GNOME Keyring API handling originally written by John Szakmeister
|
||||
* - ported to credential helper API by Philipp A. Hartmann
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <glib.h>
|
||||
#include <libsecret/secret.h>
|
||||
|
||||
/*
|
||||
* This credential struct and API is simplified from git's credential.{h,c}
|
||||
*/
|
||||
struct credential {
|
||||
char *protocol;
|
||||
char *host;
|
||||
unsigned short port;
|
||||
char *path;
|
||||
char *username;
|
||||
char *password;
|
||||
};
|
||||
|
||||
#define CREDENTIAL_INIT { NULL, NULL, 0, NULL, NULL, NULL }
|
||||
|
||||
typedef int (*credential_op_cb)(struct credential *);
|
||||
|
||||
struct credential_operation {
|
||||
char *name;
|
||||
credential_op_cb op;
|
||||
};
|
||||
|
||||
#define CREDENTIAL_OP_END { NULL, NULL }
|
||||
|
||||
/* ----------------- Secret Service functions ----------------- */
|
||||
|
||||
static char *make_label(struct credential *c)
|
||||
{
|
||||
if (c->port)
|
||||
return g_strdup_printf("Git: %s://%s:%hu/%s",
|
||||
c->protocol, c->host, c->port, c->path ? c->path : "");
|
||||
else
|
||||
return g_strdup_printf("Git: %s://%s/%s",
|
||||
c->protocol, c->host, c->path ? c->path : "");
|
||||
}
|
||||
|
||||
static GHashTable *make_attr_list(struct credential *c)
|
||||
{
|
||||
GHashTable *al = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, g_free);
|
||||
|
||||
if (c->username)
|
||||
g_hash_table_insert(al, "user", g_strdup(c->username));
|
||||
if (c->protocol)
|
||||
g_hash_table_insert(al, "protocol", g_strdup(c->protocol));
|
||||
if (c->host)
|
||||
g_hash_table_insert(al, "server", g_strdup(c->host));
|
||||
if (c->port)
|
||||
g_hash_table_insert(al, "port", g_strdup_printf("%hu", c->port));
|
||||
if (c->path)
|
||||
g_hash_table_insert(al, "object", g_strdup(c->path));
|
||||
|
||||
return al;
|
||||
}
|
||||
|
||||
static int keyring_get(struct credential *c)
|
||||
{
|
||||
SecretService *service = NULL;
|
||||
GHashTable *attributes = NULL;
|
||||
GError *error = NULL;
|
||||
GList *items = NULL;
|
||||
|
||||
if (!c->protocol || !(c->host || c->path))
|
||||
return EXIT_FAILURE;
|
||||
|
||||
service = secret_service_get_sync(0, NULL, &error);
|
||||
if (error != NULL) {
|
||||
g_critical("could not connect to Secret Service: %s", error->message);
|
||||
g_error_free(error);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
attributes = make_attr_list(c);
|
||||
items = secret_service_search_sync(service,
|
||||
SECRET_SCHEMA_COMPAT_NETWORK,
|
||||
attributes,
|
||||
SECRET_SEARCH_LOAD_SECRETS | SECRET_SEARCH_UNLOCK,
|
||||
NULL,
|
||||
&error);
|
||||
g_hash_table_unref(attributes);
|
||||
if (error != NULL) {
|
||||
g_critical("lookup failed: %s", error->message);
|
||||
g_error_free(error);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (items != NULL) {
|
||||
SecretItem *item;
|
||||
SecretValue *secret;
|
||||
const char *s;
|
||||
|
||||
item = items->data;
|
||||
secret = secret_item_get_secret(item);
|
||||
attributes = secret_item_get_attributes(item);
|
||||
|
||||
s = g_hash_table_lookup(attributes, "user");
|
||||
if (s) {
|
||||
g_free(c->username);
|
||||
c->username = g_strdup(s);
|
||||
}
|
||||
|
||||
s = secret_value_get_text(secret);
|
||||
if (s) {
|
||||
g_free(c->password);
|
||||
c->password = g_strdup(s);
|
||||
}
|
||||
|
||||
g_hash_table_unref(attributes);
|
||||
secret_value_unref(secret);
|
||||
g_list_free_full(items, g_object_unref);
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
static int keyring_store(struct credential *c)
|
||||
{
|
||||
char *label = NULL;
|
||||
GHashTable *attributes = NULL;
|
||||
GError *error = NULL;
|
||||
|
||||
/*
|
||||
* Sanity check that what we are storing is actually sensible.
|
||||
* In particular, we can't make a URL without a protocol field.
|
||||
* Without either a host or pathname (depending on the scheme),
|
||||
* we have no primary key. And without a username and password,
|
||||
* we are not actually storing a credential.
|
||||
*/
|
||||
if (!c->protocol || !(c->host || c->path) ||
|
||||
!c->username || !c->password)
|
||||
return EXIT_FAILURE;
|
||||
|
||||
label = make_label(c);
|
||||
attributes = make_attr_list(c);
|
||||
secret_password_storev_sync(SECRET_SCHEMA_COMPAT_NETWORK,
|
||||
attributes,
|
||||
NULL,
|
||||
label,
|
||||
c->password,
|
||||
NULL,
|
||||
&error);
|
||||
g_free(label);
|
||||
g_hash_table_unref(attributes);
|
||||
|
||||
if (error != NULL) {
|
||||
g_critical("store failed: %s", error->message);
|
||||
g_error_free(error);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
static int keyring_erase(struct credential *c)
|
||||
{
|
||||
GHashTable *attributes = NULL;
|
||||
GError *error = NULL;
|
||||
|
||||
/*
|
||||
* Sanity check that we actually have something to match
|
||||
* against. The input we get is a restrictive pattern,
|
||||
* so technically a blank credential means "erase everything".
|
||||
* But it is too easy to accidentally send this, since it is equivalent
|
||||
* to empty input. So explicitly disallow it, and require that the
|
||||
* pattern have some actual content to match.
|
||||
*/
|
||||
if (!c->protocol && !c->host && !c->path && !c->username)
|
||||
return EXIT_FAILURE;
|
||||
|
||||
attributes = make_attr_list(c);
|
||||
secret_password_clearv_sync(SECRET_SCHEMA_COMPAT_NETWORK,
|
||||
attributes,
|
||||
NULL,
|
||||
&error);
|
||||
g_hash_table_unref(attributes);
|
||||
|
||||
if (error != NULL) {
|
||||
g_critical("erase failed: %s", error->message);
|
||||
g_error_free(error);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
* Table with helper operation callbacks, used by generic
|
||||
* credential helper main function.
|
||||
*/
|
||||
static struct credential_operation const credential_helper_ops[] = {
|
||||
{ "get", keyring_get },
|
||||
{ "store", keyring_store },
|
||||
{ "erase", keyring_erase },
|
||||
CREDENTIAL_OP_END
|
||||
};
|
||||
|
||||
/* ------------------ credential functions ------------------ */
|
||||
|
||||
static void credential_init(struct credential *c)
|
||||
{
|
||||
memset(c, 0, sizeof(*c));
|
||||
}
|
||||
|
||||
static void credential_clear(struct credential *c)
|
||||
{
|
||||
g_free(c->protocol);
|
||||
g_free(c->host);
|
||||
g_free(c->path);
|
||||
g_free(c->username);
|
||||
g_free(c->password);
|
||||
|
||||
credential_init(c);
|
||||
}
|
||||
|
||||
static int credential_read(struct credential *c)
|
||||
{
|
||||
char *buf;
|
||||
size_t line_len;
|
||||
char *key;
|
||||
char *value;
|
||||
|
||||
key = buf = g_malloc(1024);
|
||||
|
||||
while (fgets(buf, 1024, stdin)) {
|
||||
line_len = strlen(buf);
|
||||
|
||||
if (line_len && buf[line_len-1] == '\n')
|
||||
buf[--line_len] = '\0';
|
||||
|
||||
if (!line_len)
|
||||
break;
|
||||
|
||||
value = strchr(buf, '=');
|
||||
if (!value) {
|
||||
g_warning("invalid credential line: %s", key);
|
||||
g_free(buf);
|
||||
return -1;
|
||||
}
|
||||
*value++ = '\0';
|
||||
|
||||
if (!strcmp(key, "protocol")) {
|
||||
g_free(c->protocol);
|
||||
c->protocol = g_strdup(value);
|
||||
} else if (!strcmp(key, "host")) {
|
||||
g_free(c->host);
|
||||
c->host = g_strdup(value);
|
||||
value = strrchr(c->host, ':');
|
||||
if (value) {
|
||||
*value++ = '\0';
|
||||
c->port = atoi(value);
|
||||
}
|
||||
} else if (!strcmp(key, "path")) {
|
||||
g_free(c->path);
|
||||
c->path = g_strdup(value);
|
||||
} else if (!strcmp(key, "username")) {
|
||||
g_free(c->username);
|
||||
c->username = g_strdup(value);
|
||||
} else if (!strcmp(key, "password")) {
|
||||
g_free(c->password);
|
||||
c->password = g_strdup(value);
|
||||
while (*value)
|
||||
*value++ = '\0';
|
||||
}
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
}
|
||||
|
||||
g_free(buf);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void credential_write_item(FILE *fp, const char *key, const char *value)
|
||||
{
|
||||
if (!value)
|
||||
return;
|
||||
fprintf(fp, "%s=%s\n", key, value);
|
||||
}
|
||||
|
||||
static void credential_write(const struct credential *c)
|
||||
{
|
||||
/* only write username/password, if set */
|
||||
credential_write_item(stdout, "username", c->username);
|
||||
credential_write_item(stdout, "password", c->password);
|
||||
}
|
||||
|
||||
static void usage(const char *name)
|
||||
{
|
||||
struct credential_operation const *try_op = credential_helper_ops;
|
||||
const char *basename = strrchr(name, '/');
|
||||
|
||||
basename = (basename) ? basename + 1 : name;
|
||||
fprintf(stderr, "usage: %s <", basename);
|
||||
while (try_op->name) {
|
||||
fprintf(stderr, "%s", (try_op++)->name);
|
||||
if (try_op->name)
|
||||
fprintf(stderr, "%s", "|");
|
||||
}
|
||||
fprintf(stderr, "%s", ">\n");
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int ret = EXIT_SUCCESS;
|
||||
|
||||
struct credential_operation const *try_op = credential_helper_ops;
|
||||
struct credential cred = CREDENTIAL_INIT;
|
||||
|
||||
if (!argv[1]) {
|
||||
usage(argv[0]);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
g_set_application_name("Git Credential Helper");
|
||||
|
||||
/* lookup operation callback */
|
||||
while (try_op->name && strcmp(argv[1], try_op->name))
|
||||
try_op++;
|
||||
|
||||
/* unsupported operation given -- ignore silently */
|
||||
if (!try_op->name || !try_op->op)
|
||||
goto out;
|
||||
|
||||
ret = credential_read(&cred);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
/* perform credential operation */
|
||||
ret = (*try_op->op)(&cred);
|
||||
|
||||
credential_write(&cred);
|
||||
|
||||
out:
|
||||
credential_clear(&cred);
|
||||
return ret;
|
||||
}
|
8
third_party/git/contrib/credential/netrc/Makefile
vendored
Normal file
8
third_party/git/contrib/credential/netrc/Makefile
vendored
Normal file
|
@ -0,0 +1,8 @@
|
|||
# The default target of this Makefile is...
|
||||
all::
|
||||
|
||||
test:
|
||||
./t-git-credential-netrc.sh
|
||||
|
||||
testverbose:
|
||||
./t-git-credential-netrc.sh -d -v
|
440
third_party/git/contrib/credential/netrc/git-credential-netrc
vendored
Executable file
440
third_party/git/contrib/credential/netrc/git-credential-netrc
vendored
Executable file
|
@ -0,0 +1,440 @@
|
|||
#!/usr/bin/perl
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use Getopt::Long;
|
||||
use File::Basename;
|
||||
use Git;
|
||||
|
||||
my $VERSION = "0.2";
|
||||
|
||||
my %options = (
|
||||
help => 0,
|
||||
debug => 0,
|
||||
verbose => 0,
|
||||
insecure => 0,
|
||||
file => [],
|
||||
|
||||
# identical token maps, e.g. host -> host, will be inserted later
|
||||
tmap => {
|
||||
port => 'protocol',
|
||||
machine => 'host',
|
||||
path => 'path',
|
||||
login => 'username',
|
||||
user => 'username',
|
||||
password => 'password',
|
||||
}
|
||||
);
|
||||
|
||||
# Map each credential protocol token to itself on the netrc side.
|
||||
foreach (values %{$options{tmap}}) {
|
||||
$options{tmap}->{$_} = $_;
|
||||
}
|
||||
|
||||
# Now, $options{tmap} has a mapping from the netrc format to the Git credential
|
||||
# helper protocol.
|
||||
|
||||
# Next, we build the reverse token map.
|
||||
|
||||
# When $rmap{foo} contains 'bar', that means that what the Git credential helper
|
||||
# protocol calls 'bar' is found as 'foo' in the netrc/authinfo file. Keys in
|
||||
# %rmap are what we expect to read from the netrc/authinfo file.
|
||||
|
||||
my %rmap;
|
||||
foreach my $k (keys %{$options{tmap}}) {
|
||||
push @{$rmap{$options{tmap}->{$k}}}, $k;
|
||||
}
|
||||
|
||||
Getopt::Long::Configure("bundling");
|
||||
|
||||
# TODO: maybe allow the token map $options{tmap} to be configurable.
|
||||
GetOptions(\%options,
|
||||
"help|h",
|
||||
"debug|d",
|
||||
"insecure|k",
|
||||
"verbose|v",
|
||||
"file|f=s@",
|
||||
'gpg|g:s',
|
||||
);
|
||||
|
||||
if ($options{help}) {
|
||||
my $shortname = basename($0);
|
||||
$shortname =~ s/git-credential-//;
|
||||
|
||||
print <<EOHIPPUS;
|
||||
|
||||
$0 [(-f <authfile>)...] [-g <program>] [-d] [-v] [-k] get
|
||||
|
||||
Version $VERSION by tzz\@lifelogs.com. License: BSD.
|
||||
|
||||
Options:
|
||||
|
||||
-f|--file <authfile>: specify netrc-style files. Files with the .gpg
|
||||
extension will be decrypted by GPG before parsing.
|
||||
Multiple -f arguments are OK. They are processed in
|
||||
order, and the first matching entry found is returned
|
||||
via the credential helper protocol (see below).
|
||||
|
||||
When no -f option is given, .authinfo.gpg, .netrc.gpg,
|
||||
.authinfo, and .netrc files in your home directory are
|
||||
used in this order.
|
||||
|
||||
-g|--gpg <program> : specify the program for GPG. By default, this is the
|
||||
value of gpg.program in the git repository or global
|
||||
option or gpg.
|
||||
|
||||
-k|--insecure : ignore bad file ownership or permissions
|
||||
|
||||
-d|--debug : turn on debugging (developer info)
|
||||
|
||||
-v|--verbose : be more verbose (show files and information found)
|
||||
|
||||
To enable this credential helper:
|
||||
|
||||
git config credential.helper '$shortname -f AUTHFILE1 -f AUTHFILE2'
|
||||
|
||||
(Note that Git will prepend "git-credential-" to the helper name and look for it
|
||||
in the path.)
|
||||
|
||||
...and if you want lots of debugging info:
|
||||
|
||||
git config credential.helper '$shortname -f AUTHFILE -d'
|
||||
|
||||
...or to see the files opened and data found:
|
||||
|
||||
git config credential.helper '$shortname -f AUTHFILE -v'
|
||||
|
||||
Only "get" mode is supported by this credential helper. It opens every
|
||||
<authfile> and looks for the first entry that matches the requested search
|
||||
criteria:
|
||||
|
||||
'port|protocol':
|
||||
The protocol that will be used (e.g., https). (protocol=X)
|
||||
|
||||
'machine|host':
|
||||
The remote hostname for a network credential. (host=X)
|
||||
|
||||
'path':
|
||||
The path with which the credential will be used. (path=X)
|
||||
|
||||
'login|user|username':
|
||||
The credential’s username, if we already have one. (username=X)
|
||||
|
||||
Thus, when we get this query on STDIN:
|
||||
|
||||
host=github.com
|
||||
protocol=https
|
||||
username=tzz
|
||||
|
||||
this credential helper will look for the first entry in every <authfile> that
|
||||
matches
|
||||
|
||||
machine github.com port https login tzz
|
||||
|
||||
OR
|
||||
|
||||
machine github.com protocol https login tzz
|
||||
|
||||
OR... etc. acceptable tokens as listed above. Any unknown tokens are
|
||||
simply ignored.
|
||||
|
||||
Then, the helper will print out whatever tokens it got from the entry, including
|
||||
"password" tokens, mapping back to Git's helper protocol; e.g. "port" is mapped
|
||||
back to "protocol". Any redundant entry tokens (part of the original query) are
|
||||
skipped.
|
||||
|
||||
Again, note that only the first matching entry from all the <authfile>s,
|
||||
processed in the sequence given on the command line, is used.
|
||||
|
||||
Netrc/authinfo tokens can be quoted as 'STRING' or "STRING".
|
||||
|
||||
No caching is performed by this credential helper.
|
||||
|
||||
EOHIPPUS
|
||||
|
||||
exit 0;
|
||||
}
|
||||
|
||||
my $mode = shift @ARGV;
|
||||
|
||||
# Credentials must get a parameter, so die if it's missing.
|
||||
die "Syntax: $0 [(-f <authfile>)...] [-d] get" unless defined $mode;
|
||||
|
||||
# Only support 'get' mode; with any other unsupported ones we just exit.
|
||||
exit 0 unless $mode eq 'get';
|
||||
|
||||
my $files = $options{file};
|
||||
|
||||
# if no files were given, use a predefined list.
|
||||
# note that .gpg files come first
|
||||
unless (scalar @$files) {
|
||||
my @candidates = qw[
|
||||
~/.authinfo.gpg
|
||||
~/.netrc.gpg
|
||||
~/.authinfo
|
||||
~/.netrc
|
||||
];
|
||||
|
||||
$files = $options{file} = [ map { glob $_ } @candidates ];
|
||||
}
|
||||
|
||||
load_config(\%options);
|
||||
|
||||
my $query = read_credential_data_from_stdin();
|
||||
|
||||
FILE:
|
||||
foreach my $file (@$files) {
|
||||
my $gpgmode = $file =~ m/\.gpg$/;
|
||||
unless (-r $file) {
|
||||
log_verbose("Unable to read $file; skipping it");
|
||||
next FILE;
|
||||
}
|
||||
|
||||
# the following check is copied from Net::Netrc, for non-GPG files
|
||||
# OS/2 and Win32 do not handle stat in a way compatible with this check :-(
|
||||
unless ($gpgmode || $options{insecure} ||
|
||||
$^O eq 'os2'
|
||||
|| $^O eq 'MSWin32'
|
||||
|| $^O eq 'MacOS'
|
||||
|| $^O =~ /^cygwin/) {
|
||||
my @stat = stat($file);
|
||||
|
||||
if (@stat) {
|
||||
if ($stat[2] & 077) {
|
||||
log_verbose("Insecure $file (mode=%04o); skipping it",
|
||||
$stat[2] & 07777);
|
||||
next FILE;
|
||||
}
|
||||
|
||||
if ($stat[4] != $<) {
|
||||
log_verbose("Not owner of $file; skipping it");
|
||||
next FILE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
my @entries = load_netrc($file, $gpgmode);
|
||||
|
||||
unless (scalar @entries) {
|
||||
if ($!) {
|
||||
log_verbose("Unable to open $file: $!");
|
||||
} else {
|
||||
log_verbose("No netrc entries found in $file");
|
||||
}
|
||||
|
||||
next FILE;
|
||||
}
|
||||
|
||||
my $entry = find_netrc_entry($query, @entries);
|
||||
if ($entry) {
|
||||
print_credential_data($entry, $query);
|
||||
# we're done!
|
||||
last FILE;
|
||||
}
|
||||
}
|
||||
|
||||
exit 0;
|
||||
|
||||
sub load_netrc {
|
||||
my $file = shift @_;
|
||||
my $gpgmode = shift @_;
|
||||
|
||||
my $io;
|
||||
if ($gpgmode) {
|
||||
my @cmd = ($options{'gpg'}, qw(--decrypt), $file);
|
||||
log_verbose("Using GPG to open $file: [@cmd]");
|
||||
open $io, "-|", @cmd;
|
||||
} else {
|
||||
log_verbose("Opening $file...");
|
||||
open $io, '<', $file;
|
||||
}
|
||||
|
||||
# nothing to do if the open failed (we log the error later)
|
||||
return unless $io;
|
||||
|
||||
# Net::Netrc does this, but the functionality is merged with the file
|
||||
# detection logic, so we have to extract just the part we need
|
||||
my @netrc_entries = net_netrc_loader($io);
|
||||
|
||||
# these entries will use the credential helper protocol token names
|
||||
my @entries;
|
||||
|
||||
foreach my $nentry (@netrc_entries) {
|
||||
my %entry;
|
||||
my $num_port;
|
||||
|
||||
if (!defined $nentry->{machine}) {
|
||||
next;
|
||||
}
|
||||
if (defined $nentry->{port} && $nentry->{port} =~ m/^\d+$/) {
|
||||
$num_port = $nentry->{port};
|
||||
delete $nentry->{port};
|
||||
}
|
||||
|
||||
# create the new entry for the credential helper protocol
|
||||
$entry{$options{tmap}->{$_}} = $nentry->{$_} foreach keys %$nentry;
|
||||
|
||||
# for "host X port Y" where Y is an integer (captured by
|
||||
# $num_port above), set the host to "X:Y"
|
||||
if (defined $entry{host} && defined $num_port) {
|
||||
$entry{host} = join(':', $entry{host}, $num_port);
|
||||
}
|
||||
|
||||
push @entries, \%entry;
|
||||
}
|
||||
|
||||
return @entries;
|
||||
}
|
||||
|
||||
sub net_netrc_loader {
|
||||
my $fh = shift @_;
|
||||
my @entries;
|
||||
my ($mach, $macdef, $tok, @tok);
|
||||
|
||||
LINE:
|
||||
while (<$fh>) {
|
||||
undef $macdef if /\A\n\Z/;
|
||||
|
||||
if ($macdef) {
|
||||
next LINE;
|
||||
}
|
||||
|
||||
s/^\s*//;
|
||||
chomp;
|
||||
|
||||
while (length && s/^("((?:[^"]+|\\.)*)"|((?:[^\\\s]+|\\.)*))\s*//) {
|
||||
(my $tok = $+) =~ s/\\(.)/$1/g;
|
||||
push(@tok, $tok);
|
||||
}
|
||||
|
||||
TOKEN:
|
||||
while (@tok) {
|
||||
if ($tok[0] eq "default") {
|
||||
shift(@tok);
|
||||
$mach = { machine => undef };
|
||||
next TOKEN;
|
||||
}
|
||||
|
||||
$tok = shift(@tok);
|
||||
|
||||
if ($tok eq "machine") {
|
||||
my $host = shift @tok;
|
||||
$mach = { machine => $host };
|
||||
push @entries, $mach;
|
||||
} elsif (exists $options{tmap}->{$tok}) {
|
||||
unless ($mach) {
|
||||
log_debug("Skipping token $tok because no machine was given");
|
||||
next TOKEN;
|
||||
}
|
||||
|
||||
my $value = shift @tok;
|
||||
unless (defined $value) {
|
||||
log_debug("Token $tok had no value, skipping it.");
|
||||
next TOKEN;
|
||||
}
|
||||
|
||||
# Following line added by rmerrell to remove '/' escape char in .netrc
|
||||
$value =~ s/\/\\/\\/g;
|
||||
$mach->{$tok} = $value;
|
||||
} elsif ($tok eq "macdef") { # we ignore macros
|
||||
next TOKEN unless $mach;
|
||||
my $value = shift @tok;
|
||||
$macdef = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return @entries;
|
||||
}
|
||||
|
||||
sub read_credential_data_from_stdin {
|
||||
# the query: start with every token with no value
|
||||
my %q = map { $_ => undef } values(%{$options{tmap}});
|
||||
|
||||
while (<STDIN>) {
|
||||
next unless m/^([^=]+)=(.+)/;
|
||||
|
||||
my ($token, $value) = ($1, $2);
|
||||
die "Unknown search token $token" unless exists $q{$token};
|
||||
$q{$token} = $value;
|
||||
log_debug("We were given search token $token and value $value");
|
||||
}
|
||||
|
||||
foreach (sort keys %q) {
|
||||
log_debug("Searching for %s = %s", $_, $q{$_} || '(any value)');
|
||||
}
|
||||
|
||||
return \%q;
|
||||
}
|
||||
|
||||
# takes the search tokens and then a list of entries
|
||||
# each entry is a hash reference
|
||||
sub find_netrc_entry {
|
||||
my $query = shift @_;
|
||||
|
||||
ENTRY:
|
||||
foreach my $entry (@_)
|
||||
{
|
||||
my $entry_text = join ', ', map { "$_=$entry->{$_}" } keys %$entry;
|
||||
foreach my $check (sort keys %$query) {
|
||||
if (!defined $entry->{$check}) {
|
||||
log_debug("OK: entry has no $check token, so any value satisfies check $check");
|
||||
} elsif (defined $query->{$check}) {
|
||||
log_debug("compare %s [%s] to [%s] (entry: %s)",
|
||||
$check,
|
||||
$entry->{$check},
|
||||
$query->{$check},
|
||||
$entry_text);
|
||||
unless ($query->{$check} eq $entry->{$check}) {
|
||||
next ENTRY;
|
||||
}
|
||||
} else {
|
||||
log_debug("OK: any value satisfies check $check");
|
||||
}
|
||||
}
|
||||
|
||||
return $entry;
|
||||
}
|
||||
|
||||
# nothing was found
|
||||
return;
|
||||
}
|
||||
|
||||
sub print_credential_data {
|
||||
my $entry = shift @_;
|
||||
my $query = shift @_;
|
||||
|
||||
log_debug("entry has passed all the search checks");
|
||||
TOKEN:
|
||||
foreach my $git_token (sort keys %$entry) {
|
||||
log_debug("looking for useful token $git_token");
|
||||
# don't print unknown (to the credential helper protocol) tokens
|
||||
next TOKEN unless exists $query->{$git_token};
|
||||
|
||||
# don't print things asked in the query (the entry matches them)
|
||||
next TOKEN if defined $query->{$git_token};
|
||||
|
||||
log_debug("FOUND: $git_token=$entry->{$git_token}");
|
||||
printf "%s=%s\n", $git_token, $entry->{$git_token};
|
||||
}
|
||||
}
|
||||
sub load_config {
|
||||
# load settings from git config
|
||||
my $options = shift;
|
||||
# set from command argument, gpg.program option, or default to gpg
|
||||
$options->{'gpg'} //= Git->repository()->config('gpg.program')
|
||||
// 'gpg';
|
||||
log_verbose("using $options{'gpg'} for GPG operations");
|
||||
}
|
||||
sub log_verbose {
|
||||
return unless $options{verbose};
|
||||
printf STDERR @_;
|
||||
printf STDERR "\n";
|
||||
}
|
||||
|
||||
sub log_debug {
|
||||
return unless $options{debug};
|
||||
printf STDERR @_;
|
||||
printf STDERR "\n";
|
||||
}
|
32
third_party/git/contrib/credential/netrc/t-git-credential-netrc.sh
vendored
Executable file
32
third_party/git/contrib/credential/netrc/t-git-credential-netrc.sh
vendored
Executable file
|
@ -0,0 +1,32 @@
|
|||
#!/bin/sh
|
||||
(
|
||||
cd ../../../t
|
||||
test_description='git-credential-netrc'
|
||||
. ./test-lib.sh
|
||||
|
||||
if ! test_have_prereq PERL; then
|
||||
skip_all='skipping perl interface tests, perl not available'
|
||||
test_done
|
||||
fi
|
||||
|
||||
perl -MTest::More -e 0 2>/dev/null || {
|
||||
skip_all="Perl Test::More unavailable, skipping test"
|
||||
test_done
|
||||
}
|
||||
|
||||
# set up test repository
|
||||
|
||||
test_expect_success \
|
||||
'set up test repository' \
|
||||
'git config --add gpg.program test.git-config-gpg'
|
||||
|
||||
# The external test will outputs its own plan
|
||||
test_external_has_tap=1
|
||||
|
||||
export PERL5LIB="$GITPERLLIB"
|
||||
test_external \
|
||||
'git-credential-netrc' \
|
||||
perl "$GIT_BUILD_DIR"/contrib/credential/netrc/test.pl
|
||||
|
||||
test_done
|
||||
)
|
2
third_party/git/contrib/credential/netrc/test.command-option-gpg
vendored
Executable file
2
third_party/git/contrib/credential/netrc/test.command-option-gpg
vendored
Executable file
|
@ -0,0 +1,2 @@
|
|||
#!/bin/sh
|
||||
echo machine command-option-gpg login username password password
|
2
third_party/git/contrib/credential/netrc/test.git-config-gpg
vendored
Executable file
2
third_party/git/contrib/credential/netrc/test.git-config-gpg
vendored
Executable file
|
@ -0,0 +1,2 @@
|
|||
#!/bin/sh
|
||||
echo machine git-config-gpg login username password password
|
13
third_party/git/contrib/credential/netrc/test.netrc
vendored
Normal file
13
third_party/git/contrib/credential/netrc/test.netrc
vendored
Normal file
|
@ -0,0 +1,13 @@
|
|||
machine imap login tzz@lifelogs.com port imaps password letmeknow
|
||||
machine imap login bob port imaps password bobwillknow
|
||||
|
||||
# comment test
|
||||
|
||||
machine imap2 login tzz port 1099 password tzzknow
|
||||
machine imap2 login bob password bobwillknow
|
||||
|
||||
# another command
|
||||
|
||||
machine github.com
|
||||
multilinetoken anothervalue
|
||||
login carol password carolknows
|
0
third_party/git/contrib/credential/netrc/test.netrc.gpg
vendored
Normal file
0
third_party/git/contrib/credential/netrc/test.netrc.gpg
vendored
Normal file
139
third_party/git/contrib/credential/netrc/test.pl
vendored
Executable file
139
third_party/git/contrib/credential/netrc/test.pl
vendored
Executable file
|
@ -0,0 +1,139 @@
|
|||
#!/usr/bin/perl
|
||||
|
||||
use warnings;
|
||||
use strict;
|
||||
use Test::More qw(no_plan);
|
||||
use File::Basename;
|
||||
use File::Spec::Functions qw(:DEFAULT rel2abs);
|
||||
use IPC::Open2;
|
||||
|
||||
BEGIN {
|
||||
# t-git-credential-netrc.sh kicks off our testing, so we have to go
|
||||
# from there.
|
||||
Test::More->builder->current_test(1);
|
||||
}
|
||||
|
||||
my @global_credential_args = @ARGV;
|
||||
my $scriptDir = dirname rel2abs $0;
|
||||
my ($netrc, $netrcGpg, $gcNetrc) = map { catfile $scriptDir, $_; }
|
||||
qw(test.netrc
|
||||
test.netrc.gpg
|
||||
git-credential-netrc);
|
||||
local $ENV{PATH} = join ':'
|
||||
, $scriptDir
|
||||
, $ENV{PATH}
|
||||
? $ENV{PATH}
|
||||
: ();
|
||||
|
||||
diag "Testing insecure file, nothing should be found\n";
|
||||
chmod 0644, $netrc;
|
||||
my $cred = run_credential(['-f', $netrc, 'get'],
|
||||
{ host => 'github.com' });
|
||||
|
||||
ok(scalar keys %$cred == 0, "Got 0 keys from insecure file");
|
||||
|
||||
diag "Testing missing file, nothing should be found\n";
|
||||
chmod 0644, $netrc;
|
||||
$cred = run_credential(['-f', '///nosuchfile///', 'get'],
|
||||
{ host => 'github.com' });
|
||||
|
||||
ok(scalar keys %$cred == 0, "Got 0 keys from missing file");
|
||||
|
||||
chmod 0600, $netrc;
|
||||
|
||||
diag "Testing with invalid data\n";
|
||||
$cred = run_credential(['-f', $netrc, 'get'],
|
||||
"bad data");
|
||||
ok(scalar keys %$cred == 4, "Got first found keys with bad data");
|
||||
|
||||
diag "Testing netrc file for a missing corovamilkbar entry\n";
|
||||
$cred = run_credential(['-f', $netrc, 'get'],
|
||||
{ host => 'corovamilkbar' });
|
||||
|
||||
ok(scalar keys %$cred == 0, "Got no corovamilkbar keys");
|
||||
|
||||
diag "Testing netrc file for a github.com entry\n";
|
||||
$cred = run_credential(['-f', $netrc, 'get'],
|
||||
{ host => 'github.com' });
|
||||
|
||||
ok(scalar keys %$cred == 2, "Got 2 Github keys");
|
||||
|
||||
is($cred->{password}, 'carolknows', "Got correct Github password");
|
||||
is($cred->{username}, 'carol', "Got correct Github username");
|
||||
|
||||
diag "Testing netrc file for a username-specific entry\n";
|
||||
$cred = run_credential(['-f', $netrc, 'get'],
|
||||
{ host => 'imap', username => 'bob' });
|
||||
|
||||
ok(scalar keys %$cred == 2, "Got 2 username-specific keys");
|
||||
|
||||
is($cred->{password}, 'bobwillknow', "Got correct user-specific password");
|
||||
is($cred->{protocol}, 'imaps', "Got correct user-specific protocol");
|
||||
|
||||
diag "Testing netrc file for a host:port-specific entry\n";
|
||||
$cred = run_credential(['-f', $netrc, 'get'],
|
||||
{ host => 'imap2:1099' });
|
||||
|
||||
ok(scalar keys %$cred == 2, "Got 2 host:port-specific keys");
|
||||
|
||||
is($cred->{password}, 'tzzknow', "Got correct host:port-specific password");
|
||||
is($cred->{username}, 'tzz', "Got correct host:port-specific username");
|
||||
|
||||
diag "Testing netrc file that 'host:port kills host' entry\n";
|
||||
$cred = run_credential(['-f', $netrc, 'get'],
|
||||
{ host => 'imap2' });
|
||||
|
||||
ok(scalar keys %$cred == 2, "Got 2 'host:port kills host' keys");
|
||||
|
||||
is($cred->{password}, 'bobwillknow', "Got correct 'host:port kills host' password");
|
||||
is($cred->{username}, 'bob', "Got correct 'host:port kills host' username");
|
||||
|
||||
diag 'Testing netrc file decryption by git config gpg.program setting\n';
|
||||
$cred = run_credential( ['-f', $netrcGpg, 'get']
|
||||
, { host => 'git-config-gpg' }
|
||||
);
|
||||
|
||||
ok(scalar keys %$cred == 2, 'Got keys decrypted by git config option');
|
||||
|
||||
diag 'Testing netrc file decryption by gpg option\n';
|
||||
$cred = run_credential( ['-f', $netrcGpg, '-g', 'test.command-option-gpg', 'get']
|
||||
, { host => 'command-option-gpg' }
|
||||
);
|
||||
|
||||
ok(scalar keys %$cred == 2, 'Got keys decrypted by command option');
|
||||
|
||||
my $is_passing = eval { Test::More->is_passing };
|
||||
exit($is_passing ? 0 : 1) unless $@ =~ /Can't locate object method/;
|
||||
|
||||
sub run_credential
|
||||
{
|
||||
my $args = shift @_;
|
||||
my $data = shift @_;
|
||||
my $pid = open2(my $chld_out, my $chld_in,
|
||||
$gcNetrc, @global_credential_args,
|
||||
@$args);
|
||||
|
||||
die "Couldn't open pipe to netrc credential helper: $!" unless $pid;
|
||||
|
||||
if (ref $data eq 'HASH')
|
||||
{
|
||||
print $chld_in "$_=$data->{$_}\n" foreach sort keys %$data;
|
||||
}
|
||||
else
|
||||
{
|
||||
print $chld_in "$data\n";
|
||||
}
|
||||
|
||||
close $chld_in;
|
||||
my %ret;
|
||||
|
||||
while (<$chld_out>)
|
||||
{
|
||||
chomp;
|
||||
next unless m/^([^=]+)=(.+)/;
|
||||
|
||||
$ret{$1} = $2;
|
||||
}
|
||||
|
||||
return \%ret;
|
||||
}
|
1
third_party/git/contrib/credential/osxkeychain/.gitignore
vendored
Normal file
1
third_party/git/contrib/credential/osxkeychain/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
git-credential-osxkeychain
|
17
third_party/git/contrib/credential/osxkeychain/Makefile
vendored
Normal file
17
third_party/git/contrib/credential/osxkeychain/Makefile
vendored
Normal file
|
@ -0,0 +1,17 @@
|
|||
all:: git-credential-osxkeychain
|
||||
|
||||
CC = gcc
|
||||
RM = rm -f
|
||||
CFLAGS = -g -O2 -Wall
|
||||
|
||||
-include ../../../config.mak.autogen
|
||||
-include ../../../config.mak
|
||||
|
||||
git-credential-osxkeychain: git-credential-osxkeychain.o
|
||||
$(CC) $(CFLAGS) -o $@ $< $(LDFLAGS) -Wl,-framework -Wl,Security
|
||||
|
||||
git-credential-osxkeychain.o: git-credential-osxkeychain.c
|
||||
$(CC) -c $(CFLAGS) $<
|
||||
|
||||
clean:
|
||||
$(RM) git-credential-osxkeychain git-credential-osxkeychain.o
|
183
third_party/git/contrib/credential/osxkeychain/git-credential-osxkeychain.c
vendored
Normal file
183
third_party/git/contrib/credential/osxkeychain/git-credential-osxkeychain.c
vendored
Normal file
|
@ -0,0 +1,183 @@
|
|||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <Security/Security.h>
|
||||
|
||||
static SecProtocolType protocol;
|
||||
static char *host;
|
||||
static char *path;
|
||||
static char *username;
|
||||
static char *password;
|
||||
static UInt16 port;
|
||||
|
||||
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 *xstrdup(const char *s1)
|
||||
{
|
||||
void *ret = strdup(s1);
|
||||
if (!ret)
|
||||
die("Out of memory");
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define KEYCHAIN_ITEM(x) (x ? strlen(x) : 0), x
|
||||
#define KEYCHAIN_ARGS \
|
||||
NULL, /* default keychain */ \
|
||||
KEYCHAIN_ITEM(host), \
|
||||
0, NULL, /* account domain */ \
|
||||
KEYCHAIN_ITEM(username), \
|
||||
KEYCHAIN_ITEM(path), \
|
||||
port, \
|
||||
protocol, \
|
||||
kSecAuthenticationTypeDefault
|
||||
|
||||
static void write_item(const char *what, const char *buf, int len)
|
||||
{
|
||||
printf("%s=", what);
|
||||
fwrite(buf, 1, len, stdout);
|
||||
putchar('\n');
|
||||
}
|
||||
|
||||
static void find_username_in_item(SecKeychainItemRef item)
|
||||
{
|
||||
SecKeychainAttributeList list;
|
||||
SecKeychainAttribute attr;
|
||||
|
||||
list.count = 1;
|
||||
list.attr = &attr;
|
||||
attr.tag = kSecAccountItemAttr;
|
||||
|
||||
if (SecKeychainItemCopyContent(item, NULL, &list, NULL, NULL))
|
||||
return;
|
||||
|
||||
write_item("username", attr.data, attr.length);
|
||||
SecKeychainItemFreeContent(&list, NULL);
|
||||
}
|
||||
|
||||
static void find_internet_password(void)
|
||||
{
|
||||
void *buf;
|
||||
UInt32 len;
|
||||
SecKeychainItemRef item;
|
||||
|
||||
if (SecKeychainFindInternetPassword(KEYCHAIN_ARGS, &len, &buf, &item))
|
||||
return;
|
||||
|
||||
write_item("password", buf, len);
|
||||
if (!username)
|
||||
find_username_in_item(item);
|
||||
|
||||
SecKeychainItemFreeContent(NULL, buf);
|
||||
}
|
||||
|
||||
static void delete_internet_password(void)
|
||||
{
|
||||
SecKeychainItemRef item;
|
||||
|
||||
/*
|
||||
* Require at least a protocol and host for removal, which is what git
|
||||
* will give us; if you want to do something more fancy, use the
|
||||
* Keychain manager.
|
||||
*/
|
||||
if (!protocol || !host)
|
||||
return;
|
||||
|
||||
if (SecKeychainFindInternetPassword(KEYCHAIN_ARGS, 0, NULL, &item))
|
||||
return;
|
||||
|
||||
SecKeychainItemDelete(item);
|
||||
}
|
||||
|
||||
static void add_internet_password(void)
|
||||
{
|
||||
/* Only store complete credentials */
|
||||
if (!protocol || !host || !username || !password)
|
||||
return;
|
||||
|
||||
if (SecKeychainAddInternetPassword(
|
||||
KEYCHAIN_ARGS,
|
||||
KEYCHAIN_ITEM(password),
|
||||
NULL))
|
||||
return;
|
||||
}
|
||||
|
||||
static void read_credential(void)
|
||||
{
|
||||
char buf[1024];
|
||||
|
||||
while (fgets(buf, sizeof(buf), stdin)) {
|
||||
char *v;
|
||||
|
||||
if (!strcmp(buf, "\n"))
|
||||
break;
|
||||
buf[strlen(buf)-1] = '\0';
|
||||
|
||||
v = strchr(buf, '=');
|
||||
if (!v)
|
||||
die("bad input: %s", buf);
|
||||
*v++ = '\0';
|
||||
|
||||
if (!strcmp(buf, "protocol")) {
|
||||
if (!strcmp(v, "imap"))
|
||||
protocol = kSecProtocolTypeIMAP;
|
||||
else if (!strcmp(v, "imaps"))
|
||||
protocol = kSecProtocolTypeIMAPS;
|
||||
else if (!strcmp(v, "ftp"))
|
||||
protocol = kSecProtocolTypeFTP;
|
||||
else if (!strcmp(v, "ftps"))
|
||||
protocol = kSecProtocolTypeFTPS;
|
||||
else if (!strcmp(v, "https"))
|
||||
protocol = kSecProtocolTypeHTTPS;
|
||||
else if (!strcmp(v, "http"))
|
||||
protocol = kSecProtocolTypeHTTP;
|
||||
else if (!strcmp(v, "smtp"))
|
||||
protocol = kSecProtocolTypeSMTP;
|
||||
else /* we don't yet handle other protocols */
|
||||
exit(0);
|
||||
}
|
||||
else if (!strcmp(buf, "host")) {
|
||||
char *colon = strchr(v, ':');
|
||||
if (colon) {
|
||||
*colon++ = '\0';
|
||||
port = atoi(colon);
|
||||
}
|
||||
host = xstrdup(v);
|
||||
}
|
||||
else if (!strcmp(buf, "path"))
|
||||
path = xstrdup(v);
|
||||
else if (!strcmp(buf, "username"))
|
||||
username = xstrdup(v);
|
||||
else if (!strcmp(buf, "password"))
|
||||
password = xstrdup(v);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, const char **argv)
|
||||
{
|
||||
const char *usage =
|
||||
"usage: git credential-osxkeychain <get|store|erase>";
|
||||
|
||||
if (!argv[1])
|
||||
die(usage);
|
||||
|
||||
read_credential();
|
||||
|
||||
if (!strcmp(argv[1], "get"))
|
||||
find_internet_password();
|
||||
else if (!strcmp(argv[1], "store"))
|
||||
add_internet_password();
|
||||
else if (!strcmp(argv[1], "erase"))
|
||||
delete_internet_password();
|
||||
/* otherwise, ignore unknown action */
|
||||
|
||||
return 0;
|
||||
}
|
22
third_party/git/contrib/credential/wincred/Makefile
vendored
Normal file
22
third_party/git/contrib/credential/wincred/Makefile
vendored
Normal file
|
@ -0,0 +1,22 @@
|
|||
all: git-credential-wincred.exe
|
||||
|
||||
-include ../../../config.mak.autogen
|
||||
-include ../../../config.mak
|
||||
|
||||
CC ?= gcc
|
||||
RM ?= rm -f
|
||||
CFLAGS ?= -O2 -Wall
|
||||
|
||||
prefix ?= /usr/local
|
||||
libexecdir ?= $(prefix)/libexec/git-core
|
||||
|
||||
INSTALL ?= install
|
||||
|
||||
git-credential-wincred.exe : git-credential-wincred.c
|
||||
$(LINK.c) $^ $(LOADLIBES) $(LDLIBS) -o $@
|
||||
|
||||
install: git-credential-wincred.exe
|
||||
$(INSTALL) -m 755 $^ $(libexecdir)
|
||||
|
||||
clean:
|
||||
$(RM) git-credential-wincred.exe
|
327
third_party/git/contrib/credential/wincred/git-credential-wincred.c
vendored
Normal file
327
third_party/git/contrib/credential/wincred/git-credential-wincred.c
vendored
Normal file
|
@ -0,0 +1,327 @@
|
|||
/*
|
||||
* A git credential helper that interface with Windows' Credential Manager
|
||||
*
|
||||
*/
|
||||
#include <windows.h>
|
||||
#include <stdio.h>
|
||||
#include <io.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
/* common helpers */
|
||||
|
||||
#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
|
||||
|
||||
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 *xmalloc(size_t size)
|
||||
{
|
||||
void *ret = malloc(size);
|
||||
if (!ret && !size)
|
||||
ret = malloc(1);
|
||||
if (!ret)
|
||||
die("Out of memory");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* MinGW doesn't have wincred.h, so we need to define stuff */
|
||||
|
||||
typedef struct _CREDENTIAL_ATTRIBUTEW {
|
||||
LPWSTR Keyword;
|
||||
DWORD Flags;
|
||||
DWORD ValueSize;
|
||||
LPBYTE Value;
|
||||
} CREDENTIAL_ATTRIBUTEW, *PCREDENTIAL_ATTRIBUTEW;
|
||||
|
||||
typedef struct _CREDENTIALW {
|
||||
DWORD Flags;
|
||||
DWORD Type;
|
||||
LPWSTR TargetName;
|
||||
LPWSTR Comment;
|
||||
FILETIME LastWritten;
|
||||
DWORD CredentialBlobSize;
|
||||
LPBYTE CredentialBlob;
|
||||
DWORD Persist;
|
||||
DWORD AttributeCount;
|
||||
PCREDENTIAL_ATTRIBUTEW Attributes;
|
||||
LPWSTR TargetAlias;
|
||||
LPWSTR UserName;
|
||||
} CREDENTIALW, *PCREDENTIALW;
|
||||
|
||||
#define CRED_TYPE_GENERIC 1
|
||||
#define CRED_PERSIST_LOCAL_MACHINE 2
|
||||
#define CRED_MAX_ATTRIBUTES 64
|
||||
|
||||
typedef BOOL (WINAPI *CredWriteWT)(PCREDENTIALW, DWORD);
|
||||
typedef BOOL (WINAPI *CredEnumerateWT)(LPCWSTR, DWORD, DWORD *,
|
||||
PCREDENTIALW **);
|
||||
typedef VOID (WINAPI *CredFreeT)(PVOID);
|
||||
typedef BOOL (WINAPI *CredDeleteWT)(LPCWSTR, DWORD, DWORD);
|
||||
|
||||
static HMODULE advapi;
|
||||
static CredWriteWT CredWriteW;
|
||||
static CredEnumerateWT CredEnumerateW;
|
||||
static CredFreeT CredFree;
|
||||
static CredDeleteWT CredDeleteW;
|
||||
|
||||
static void load_cred_funcs(void)
|
||||
{
|
||||
/* load DLLs */
|
||||
advapi = LoadLibraryExA("advapi32.dll", NULL,
|
||||
LOAD_LIBRARY_SEARCH_SYSTEM32);
|
||||
if (!advapi)
|
||||
die("failed to load advapi32.dll");
|
||||
|
||||
/* get function pointers */
|
||||
CredWriteW = (CredWriteWT)GetProcAddress(advapi, "CredWriteW");
|
||||
CredEnumerateW = (CredEnumerateWT)GetProcAddress(advapi,
|
||||
"CredEnumerateW");
|
||||
CredFree = (CredFreeT)GetProcAddress(advapi, "CredFree");
|
||||
CredDeleteW = (CredDeleteWT)GetProcAddress(advapi, "CredDeleteW");
|
||||
if (!CredWriteW || !CredEnumerateW || !CredFree || !CredDeleteW)
|
||||
die("failed to load functions");
|
||||
}
|
||||
|
||||
static WCHAR *wusername, *password, *protocol, *host, *path, target[1024];
|
||||
|
||||
static void write_item(const char *what, LPCWSTR wbuf, int wlen)
|
||||
{
|
||||
char *buf;
|
||||
|
||||
if (!wbuf || !wlen) {
|
||||
printf("%s=\n", what);
|
||||
return;
|
||||
}
|
||||
|
||||
int len = WideCharToMultiByte(CP_UTF8, 0, wbuf, wlen, NULL, 0, NULL,
|
||||
FALSE);
|
||||
buf = xmalloc(len);
|
||||
|
||||
if (!WideCharToMultiByte(CP_UTF8, 0, wbuf, wlen, buf, len, NULL, FALSE))
|
||||
die("WideCharToMultiByte failed!");
|
||||
|
||||
printf("%s=", what);
|
||||
fwrite(buf, 1, len, stdout);
|
||||
putchar('\n');
|
||||
free(buf);
|
||||
}
|
||||
|
||||
/*
|
||||
* Match an (optional) expected string and a delimiter in the target string,
|
||||
* consuming the matched text by updating the target pointer.
|
||||
*/
|
||||
|
||||
static LPCWSTR wcsstr_last(LPCWSTR str, LPCWSTR find)
|
||||
{
|
||||
LPCWSTR res = NULL, pos;
|
||||
for (pos = wcsstr(str, find); pos; pos = wcsstr(pos + 1, find))
|
||||
res = pos;
|
||||
return res;
|
||||
}
|
||||
|
||||
static int match_part_with_last(LPCWSTR *ptarget, LPCWSTR want, LPCWSTR delim, int last)
|
||||
{
|
||||
LPCWSTR delim_pos, start = *ptarget;
|
||||
int len;
|
||||
|
||||
/* find start of delimiter (or end-of-string if delim is empty) */
|
||||
if (*delim)
|
||||
delim_pos = last ? wcsstr_last(start, delim) : wcsstr(start, delim);
|
||||
else
|
||||
delim_pos = start + wcslen(start);
|
||||
|
||||
/*
|
||||
* match text up to delimiter, or end of string (e.g. the '/' after
|
||||
* host is optional if not followed by a path)
|
||||
*/
|
||||
if (delim_pos)
|
||||
len = delim_pos - start;
|
||||
else
|
||||
len = wcslen(start);
|
||||
|
||||
/* update ptarget if we either found a delimiter or need a match */
|
||||
if (delim_pos || want)
|
||||
*ptarget = delim_pos ? delim_pos + wcslen(delim) : start + len;
|
||||
|
||||
return !want || (!wcsncmp(want, start, len) && !want[len]);
|
||||
}
|
||||
|
||||
static int match_part(LPCWSTR *ptarget, LPCWSTR want, LPCWSTR delim)
|
||||
{
|
||||
return match_part_with_last(ptarget, want, delim, 0);
|
||||
}
|
||||
|
||||
static int match_part_last(LPCWSTR *ptarget, LPCWSTR want, LPCWSTR delim)
|
||||
{
|
||||
return match_part_with_last(ptarget, want, delim, 1);
|
||||
}
|
||||
|
||||
static int match_cred(const CREDENTIALW *cred)
|
||||
{
|
||||
LPCWSTR target = cred->TargetName;
|
||||
if (wusername && wcscmp(wusername, cred->UserName ? cred->UserName : L""))
|
||||
return 0;
|
||||
|
||||
return match_part(&target, L"git", L":") &&
|
||||
match_part(&target, protocol, L"://") &&
|
||||
match_part_last(&target, wusername, L"@") &&
|
||||
match_part(&target, host, L"/") &&
|
||||
match_part(&target, path, L"");
|
||||
}
|
||||
|
||||
static void get_credential(void)
|
||||
{
|
||||
CREDENTIALW **creds;
|
||||
DWORD num_creds;
|
||||
int i;
|
||||
|
||||
if (!CredEnumerateW(L"git:*", 0, &num_creds, &creds))
|
||||
return;
|
||||
|
||||
/* search for the first credential that matches username */
|
||||
for (i = 0; i < num_creds; ++i)
|
||||
if (match_cred(creds[i])) {
|
||||
write_item("username", creds[i]->UserName,
|
||||
creds[i]->UserName ? wcslen(creds[i]->UserName) : 0);
|
||||
write_item("password",
|
||||
(LPCWSTR)creds[i]->CredentialBlob,
|
||||
creds[i]->CredentialBlobSize / sizeof(WCHAR));
|
||||
break;
|
||||
}
|
||||
|
||||
CredFree(creds);
|
||||
}
|
||||
|
||||
static void store_credential(void)
|
||||
{
|
||||
CREDENTIALW cred;
|
||||
|
||||
if (!wusername || !password)
|
||||
return;
|
||||
|
||||
cred.Flags = 0;
|
||||
cred.Type = CRED_TYPE_GENERIC;
|
||||
cred.TargetName = target;
|
||||
cred.Comment = L"saved by git-credential-wincred";
|
||||
cred.CredentialBlobSize = (wcslen(password)) * sizeof(WCHAR);
|
||||
cred.CredentialBlob = (LPVOID)password;
|
||||
cred.Persist = CRED_PERSIST_LOCAL_MACHINE;
|
||||
cred.AttributeCount = 0;
|
||||
cred.Attributes = NULL;
|
||||
cred.TargetAlias = NULL;
|
||||
cred.UserName = wusername;
|
||||
|
||||
if (!CredWriteW(&cred, 0))
|
||||
die("CredWrite failed");
|
||||
}
|
||||
|
||||
static void erase_credential(void)
|
||||
{
|
||||
CREDENTIALW **creds;
|
||||
DWORD num_creds;
|
||||
int i;
|
||||
|
||||
if (!CredEnumerateW(L"git:*", 0, &num_creds, &creds))
|
||||
return;
|
||||
|
||||
for (i = 0; i < num_creds; ++i) {
|
||||
if (match_cred(creds[i]))
|
||||
CredDeleteW(creds[i]->TargetName, creds[i]->Type, 0);
|
||||
}
|
||||
|
||||
CredFree(creds);
|
||||
}
|
||||
|
||||
static WCHAR *utf8_to_utf16_dup(const char *str)
|
||||
{
|
||||
int wlen = MultiByteToWideChar(CP_UTF8, 0, str, -1, NULL, 0);
|
||||
WCHAR *wstr = xmalloc(sizeof(WCHAR) * wlen);
|
||||
MultiByteToWideChar(CP_UTF8, 0, str, -1, wstr, wlen);
|
||||
return wstr;
|
||||
}
|
||||
|
||||
static void read_credential(void)
|
||||
{
|
||||
char buf[1024];
|
||||
|
||||
while (fgets(buf, sizeof(buf), stdin)) {
|
||||
char *v;
|
||||
int len = strlen(buf);
|
||||
/* strip trailing CR / LF */
|
||||
while (len && strchr("\r\n", buf[len - 1]))
|
||||
buf[--len] = 0;
|
||||
|
||||
if (!*buf)
|
||||
break;
|
||||
|
||||
v = strchr(buf, '=');
|
||||
if (!v)
|
||||
die("bad input: %s", buf);
|
||||
*v++ = '\0';
|
||||
|
||||
if (!strcmp(buf, "protocol"))
|
||||
protocol = utf8_to_utf16_dup(v);
|
||||
else if (!strcmp(buf, "host"))
|
||||
host = utf8_to_utf16_dup(v);
|
||||
else if (!strcmp(buf, "path"))
|
||||
path = utf8_to_utf16_dup(v);
|
||||
else if (!strcmp(buf, "username")) {
|
||||
wusername = utf8_to_utf16_dup(v);
|
||||
} else if (!strcmp(buf, "password"))
|
||||
password = utf8_to_utf16_dup(v);
|
||||
else
|
||||
die("unrecognized input");
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
const char *usage =
|
||||
"usage: git credential-wincred <get|store|erase>\n";
|
||||
|
||||
if (!argv[1])
|
||||
die(usage);
|
||||
|
||||
/* git use binary pipes to avoid CRLF-issues */
|
||||
_setmode(_fileno(stdin), _O_BINARY);
|
||||
_setmode(_fileno(stdout), _O_BINARY);
|
||||
|
||||
read_credential();
|
||||
|
||||
load_cred_funcs();
|
||||
|
||||
if (!protocol || !(host || path))
|
||||
return 0;
|
||||
|
||||
/* prepare 'target', the unique key for the credential */
|
||||
wcscpy(target, L"git:");
|
||||
wcsncat(target, protocol, ARRAY_SIZE(target));
|
||||
wcsncat(target, L"://", ARRAY_SIZE(target));
|
||||
if (wusername) {
|
||||
wcsncat(target, wusername, ARRAY_SIZE(target));
|
||||
wcsncat(target, L"@", ARRAY_SIZE(target));
|
||||
}
|
||||
if (host)
|
||||
wcsncat(target, host, ARRAY_SIZE(target));
|
||||
if (path) {
|
||||
wcsncat(target, L"/", ARRAY_SIZE(target));
|
||||
wcsncat(target, path, ARRAY_SIZE(target));
|
||||
}
|
||||
|
||||
if (!strcmp(argv[1], "get"))
|
||||
get_credential();
|
||||
else if (!strcmp(argv[1], "store"))
|
||||
store_credential();
|
||||
else if (!strcmp(argv[1], "erase"))
|
||||
erase_credential();
|
||||
/* otherwise, ignore unknown action */
|
||||
return 0;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue