From: Junio C Hamano Date: Tue, 20 Dec 2011 00:05:16 +0000 (-0800) Subject: Merge branch 'jk/credentials' X-Git-Tag: v1.7.9-rc0~54 X-Git-Url: https://git.lorimer.id.au/gitweb.git/diff_plain/367d20ec6b9e8e297537f665fb528346bd0aff27?hp=-c Merge branch 'jk/credentials' * jk/credentials: t: add test harness for external credential helpers credentials: add "store" helper strbuf: add strbuf_add*_urlencode Makefile: unix sockets may not available on some platforms credentials: add "cache" helper docs: end-user documentation for the credential subsystem credential: make relevance of http path configurable credential: add credential.*.username credential: apply helper config http: use credential API to get passwords credential: add function for parsing url components introduce credentials API t5550: fix typo test-lib: add test_config_global variant Conflicts: strbuf.c --- 367d20ec6b9e8e297537f665fb528346bd0aff27 diff --combined Documentation/config.txt index 8a7d2d4cb1,36bcdf2ca8..c6630c73e8 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@@ -677,12 -677,10 +677,12 @@@ branch..mergeoptions: branch..rebase:: When true, rebase the branch on top of the fetched branch, instead of merging the default branch from the default remote when - "git pull" is run. - *NOTE*: this is a possibly dangerous operation; do *not* use - it unless you understand the implications (see linkgit:git-rebase[1] - for details). + "git pull" is run. See "pull.rebase" for doing this in a non + branch-specific manner. ++ +*NOTE*: this is a possibly dangerous operation; do *not* use +it unless you understand the implications (see linkgit:git-rebase[1] +for details). browser..cmd:: Specify the command to invoke the specified browser. The @@@ -834,6 -832,29 +834,29 @@@ commit.template: "{tilde}/" is expanded to the value of `$HOME` and "{tilde}user/" to the specified user's home directory. + credential.helper:: + Specify an external helper to be called when a username or + password credential is needed; the helper may consult external + storage to avoid prompting the user for the credentials. See + linkgit:gitcredentials[7] for details. + + credential.useHttpPath:: + When acquiring credentials, consider the "path" component of an http + or https URL to be important. Defaults to false. See + linkgit:gitcredentials[7] for more information. + + credential.username:: + If no username is set for a network authentication, use this username + by default. See credential..* below, and + linkgit:gitcredentials[7]. + + credential..*:: + Any of the credential.* options above can be applied selectively to + some credentials. For example "credential.https://example.com.username" + would set the default username only for https connections to + example.com. See linkgit:gitcredentials[7] for details on how URLs are + matched. + include::diff-config.txt[] difftool..path:: @@@ -1592,16 -1613,6 +1615,16 @@@ pretty.: Note that an alias with the same name as a built-in format will be silently ignored. +pull.rebase:: + When true, rebase branches on top of the fetched branch, instead + of merging the default branch from the default remote when "git + pull" is run. See "branch..rebase" for setting this on a + per-branch basis. ++ +*NOTE*: this is a possibly dangerous operation; do *not* use +it unless you understand the implications (see linkgit:git-rebase[1] +for details). + pull.octopus:: The default merge strategy to use when pulling multiple branches at once. diff --combined Makefile index 0464e1fc68,2222633a17..2127c1bd15 --- a/Makefile +++ b/Makefile @@@ -57,8 -57,8 +57,8 @@@ all: # # Define NO_STRLCPY if you don't have strlcpy. # -# Define NO_STRTOUMAX if you don't have strtoumax in the C library. -# If your compiler also does not support long long or does not have +# Define NO_STRTOUMAX if you don't have both strtoimax and strtoumax in the +# C library. If your compiler also does not support long long or does not have # strtoull, define NO_STRTOULL. # # Define NO_SETENV if you don't have setenv in the C library. @@@ -143,6 -143,8 +143,8 @@@ # # Define NO_IPV6 if you lack IPv6 support and getaddrinfo(). # + # Define NO_UNIX_SOCKETS if your system does not offer unix sockets. + # # Define NO_SOCKADDR_STORAGE if your platform does not have struct # sockaddr_storage. # @@@ -427,10 -429,12 +429,12 @@@ PROGRAM_OBJS += show-index. PROGRAM_OBJS += upload-pack.o PROGRAM_OBJS += http-backend.o PROGRAM_OBJS += sh-i18n--envsubst.o + PROGRAM_OBJS += credential-store.o PROGRAMS += $(patsubst %.o,git-%$X,$(PROGRAM_OBJS)) TEST_PROGRAMS_NEED_X += test-chmtime + TEST_PROGRAMS_NEED_X += test-credential TEST_PROGRAMS_NEED_X += test-ctype TEST_PROGRAMS_NEED_X += test-date TEST_PROGRAMS_NEED_X += test-delta @@@ -511,7 -515,6 +515,7 @@@ LIB_H += argv-array. LIB_H += attr.h LIB_H += blob.h LIB_H += builtin.h +LIB_H += bulk-checkin.h LIB_H += cache.h LIB_H += cache-tree.h LIB_H += color.h @@@ -526,6 -529,7 +530,7 @@@ LIB_H += compat/win32/poll. LIB_H += compat/win32/dirent.h LIB_H += connected.h LIB_H += convert.h + LIB_H += credential.h LIB_H += csum-file.h LIB_H += decorate.h LIB_H += delta.h @@@ -533,11 -537,9 +538,11 @@@ LIB_H += diffcore. LIB_H += diff.h LIB_H += dir.h LIB_H += exec_cmd.h +LIB_H += fmt-merge-msg.h LIB_H += fsck.h LIB_H += gettext.h LIB_H += git-compat-util.h +LIB_H += gpg-interface.h LIB_H += graph.h LIB_H += grep.h LIB_H += hash.h @@@ -601,7 -603,6 +606,7 @@@ LIB_OBJS += base85. LIB_OBJS += bisect.o LIB_OBJS += blob.o LIB_OBJS += branch.o +LIB_OBJS += bulk-checkin.o LIB_OBJS += bundle.o LIB_OBJS += cache-tree.o LIB_OBJS += color.o @@@ -613,6 -614,7 +618,7 @@@ LIB_OBJS += connect. LIB_OBJS += connected.o LIB_OBJS += convert.o LIB_OBJS += copy.o + LIB_OBJS += credential.o LIB_OBJS += csum-file.o LIB_OBJS += ctype.o LIB_OBJS += date.o @@@ -632,7 -634,6 +638,7 @@@ LIB_OBJS += entry. LIB_OBJS += environment.o LIB_OBJS += exec_cmd.o LIB_OBJS += fsck.o +LIB_OBJS += gpg-interface.o LIB_OBJS += graph.o LIB_OBJS += grep.o LIB_OBJS += hash.o @@@ -1103,6 -1104,7 +1109,7 @@@ ifeq ($(uname_S),Windows NO_SYS_POLL_H = YesPlease NO_SYMLINK_HEAD = YesPlease NO_IPV6 = YesPlease + NO_UNIX_SOCKETS = YesPlease NO_SETENV = YesPlease NO_UNSETENV = YesPlease NO_STRCASESTR = YesPlease @@@ -1196,6 -1198,7 +1203,7 @@@ ifneq (,$(findstring MINGW,$(uname_S)) NO_LIBGEN_H = YesPlease NO_SYS_POLL_H = YesPlease NO_SYMLINK_HEAD = YesPlease + NO_UNIX_SOCKETS = YesPlease NO_SETENV = YesPlease NO_UNSETENV = YesPlease NO_STRCASESTR = YesPlease @@@ -1483,7 -1486,7 +1491,7 @@@ ifdef NO_STRLCP endif ifdef NO_STRTOUMAX COMPAT_CFLAGS += -DNO_STRTOUMAX - COMPAT_OBJS += compat/strtoumax.o + COMPAT_OBJS += compat/strtoumax.o compat/strtoimax.o endif ifdef NO_STRTOULL COMPAT_CFLAGS += -DNO_STRTOULL @@@ -1573,6 -1576,12 +1581,12 @@@ ifdef NO_INET_PTO LIB_OBJS += compat/inet_pton.o BASIC_CFLAGS += -DNO_INET_PTON endif + ifndef NO_UNIX_SOCKETS + LIB_OBJS += unix-socket.o + LIB_H += unix-socket.h + PROGRAM_OBJS += credential-cache.o + PROGRAM_OBJS += credential-cache--daemon.o + endif ifdef NO_ICONV BASIC_CFLAGS += -DNO_ICONV @@@ -2208,6 -2217,7 +2222,7 @@@ ifdef GIT_TEST_CMP_USE_COPIED_CONTEX @echo GIT_TEST_CMP_USE_COPIED_CONTEXT=YesPlease >>$@ endif @echo GETTEXT_POISON=\''$(subst ','\'',$(subst ','\'',$(GETTEXT_POISON)))'\' >>$@ + @echo NO_UNIX_SOCKETS=\''$(subst ','\'',$(subst ','\'',$(NO_UNIX_SOCKETS)))'\' >>$@ ### Detect Tck/Tk interpreter path changes ifndef NO_TCLTK diff --combined git-compat-util.h index 77062ed2a6,5c238bd6dd..8f3972cd32 --- a/git-compat-util.h +++ b/git-compat-util.h @@@ -116,12 -116,7 +116,12 @@@ #else #include #endif -#ifndef __MINGW32__ +#if defined(__MINGW32__) +/* pull in Windows compatibility stuff */ +#include "compat/mingw.h" +#elif defined(_MSC_VER) +#include "compat/msvc.h" +#else #include #include #include @@@ -135,6 -130,7 +135,7 @@@ #include #include #include + #include #ifndef NO_INTTYPES_H #include #else @@@ -150,6 -146,12 +151,6 @@@ #include #define _ALL_SOURCE 1 #endif -#else /* __MINGW32__ */ -/* pull in Windows compatibility stuff */ -#include "compat/mingw.h" -#endif /* __MINGW32__ */ -#ifdef _MSC_VER -#include "compat/msvc.h" #endif #ifndef NO_LIBGEN_H @@@ -350,8 -352,6 +351,8 @@@ extern size_t gitstrlcpy(char *, const #ifdef NO_STRTOUMAX #define strtoumax gitstrtoumax extern uintmax_t gitstrtoumax(const char *, char **, int); +#define strtoimax gitstrtoimax +extern intmax_t gitstrtoimax(const char *, char **, int); #endif #ifdef NO_STRTOK_R diff --combined http.c index 44fcc4d178,917a1ae7d0..8e72664061 --- a/http.c +++ b/http.c @@@ -3,7 -3,9 +3,8 @@@ #include "sideband.h" #include "run-command.h" #include "url.h" + #include "credential.h" -int data_received; int active_requests; int http_is_verbose; size_t http_post_buffer = 16 * LARGE_PACKET_MAX; @@@ -41,7 -43,7 +42,7 @@@ static long curl_low_speed_time = -1 static int curl_ftp_no_epsv; static const char *curl_http_proxy; static const char *curl_cookie_file; - static char *user_name, *user_pass, *description; + static struct credential http_auth = CREDENTIAL_INIT; static const char *user_agent; #if LIBCURL_VERSION_NUM >= 0x071700 @@@ -52,7 -54,7 +53,7 @@@ #define CURLOPT_KEYPASSWD CURLOPT_SSLCERTPASSWD #endif - static char *ssl_cert_password; + static struct credential cert_auth = CREDENTIAL_INIT; static int ssl_cert_password_required; static struct curl_slist *pragma_header; @@@ -98,11 -100,13 +99,11 @@@ size_t fwrite_buffer(char *ptr, size_t struct strbuf *buffer = buffer_; strbuf_add(buffer, ptr, size); - data_received++; return size; } size_t fwrite_null(char *ptr, size_t eltsize, size_t nmemb, void *strbuf) { - data_received++; return eltsize * nmemb; } @@@ -136,27 -140,6 +137,6 @@@ static void process_curl_messages(void } #endif - static char *git_getpass_with_description(const char *what, const char *desc) - { - struct strbuf prompt = STRBUF_INIT; - char *r; - - if (desc) - strbuf_addf(&prompt, "%s for '%s': ", what, desc); - else - strbuf_addf(&prompt, "%s: ", what); - /* - * NEEDSWORK: for usernames, we should do something less magical that - * actually echoes the characters. However, we need to read from - * /dev/tty and not stdio, which is not portable (but getpass will do - * it for us). http.c uses the same workaround. - */ - r = git_getpass(prompt.buf); - - strbuf_release(&prompt); - return xstrdup(r); - } - static int http_options(const char *var, const char *value, void *cb) { if (!strcmp("http.sslverify", var)) { @@@ -229,11 -212,11 +209,11 @@@ static void init_curl_http_auth(CURL *result) { - if (user_name) { + if (http_auth.username) { struct strbuf up = STRBUF_INIT; - if (!user_pass) - user_pass = xstrdup(git_getpass_with_description("Password", description)); - strbuf_addf(&up, "%s:%s", user_name, user_pass); + credential_fill(&http_auth); + strbuf_addf(&up, "%s:%s", + http_auth.username, http_auth.password); curl_easy_setopt(result, CURLOPT_USERPWD, strbuf_detach(&up, NULL)); } @@@ -241,18 -224,14 +221,14 @@@ static int has_cert_password(void) { - if (ssl_cert_password != NULL) - return 1; if (ssl_cert == NULL || ssl_cert_password_required != 1) return 0; - /* Only prompt the user once. */ - ssl_cert_password_required = -1; - ssl_cert_password = git_getpass_with_description("Certificate Password", description); - if (ssl_cert_password != NULL) { - ssl_cert_password = xstrdup(ssl_cert_password); - return 1; - } else - return 0; + if (!cert_auth.password) { + cert_auth.protocol = xstrdup("cert"); + cert_auth.path = xstrdup(ssl_cert); + credential_fill(&cert_auth); + } + return 1; } static CURL *get_curl_handle(void) @@@ -279,7 -258,7 +255,7 @@@ if (ssl_cert != NULL) curl_easy_setopt(result, CURLOPT_SSLCERT, ssl_cert); if (has_cert_password()) - curl_easy_setopt(result, CURLOPT_KEYPASSWD, ssl_cert_password); + curl_easy_setopt(result, CURLOPT_KEYPASSWD, cert_auth.password); #if LIBCURL_VERSION_NUM >= 0x070903 if (ssl_key != NULL) curl_easy_setopt(result, CURLOPT_SSLKEY, ssl_key); @@@ -321,42 -300,6 +297,6 @@@ return result; } - static void http_auth_init(const char *url) - { - const char *at, *colon, *cp, *slash, *host; - - cp = strstr(url, "://"); - if (!cp) - return; - - /* - * Ok, the URL looks like "proto://something". Which one? - * "proto://:@/...", - * "proto://@/...", or just - * "proto:///..."? - */ - cp += 3; - at = strchr(cp, '@'); - colon = strchr(cp, ':'); - slash = strchrnul(cp, '/'); - if (!at || slash <= at) { - /* No credentials, but we may have to ask for some later */ - host = cp; - } - else if (!colon || at <= colon) { - /* Only username */ - user_name = url_decode_mem(cp, at - cp); - user_pass = NULL; - host = at + 1; - } else { - user_name = url_decode_mem(cp, colon - cp); - user_pass = url_decode_mem(colon + 1, at - (colon + 1)); - host = at + 1; - } - - description = url_decode_mem(host, slash - host); - } - static void set_from_env(const char **var, const char *envname) { const char *val = getenv(envname); @@@ -429,7 -372,7 +369,7 @@@ void http_init(struct remote *remote, c curl_ftp_no_epsv = 1; if (url) { - http_auth_init(url); + credential_from_url(&http_auth, url); if (!ssl_cert_password_required && getenv("GIT_SSL_CERT_PASSWORD_PROTECTED") && !prefixcmp(url, "https://")) @@@ -478,10 -421,10 +418,10 @@@ void http_cleanup(void curl_http_proxy = NULL; } - if (ssl_cert_password != NULL) { - memset(ssl_cert_password, 0, strlen(ssl_cert_password)); - free(ssl_cert_password); - ssl_cert_password = NULL; + if (cert_auth.password != NULL) { + memset(cert_auth.password, 0, strlen(cert_auth.password)); + free(cert_auth.password); + cert_auth.password = NULL; } ssl_cert_password_required = 0; } @@@ -533,6 -476,7 +473,6 @@@ struct active_request_slot *get_active_ active_requests++; slot->in_use = 1; - slot->local = NULL; slot->results = NULL; slot->finished = NULL; slot->callback_data = NULL; @@@ -636,6 -580,8 +576,6 @@@ void step_active_slots(void void run_active_slot(struct active_request_slot *slot) { #ifdef USE_CURL_MULTI - long last_pos = 0; - long current_pos; fd_set readfds; fd_set writefds; fd_set excfds; @@@ -645,33 -591,25 +585,33 @@@ slot->finished = &finished; while (!finished) { - data_received = 0; step_active_slots(); - if (!data_received && slot->local != NULL) { - current_pos = ftell(slot->local); - if (current_pos > last_pos) - data_received++; - last_pos = current_pos; - } + if (slot->in_use) { +#if LIBCURL_VERSION_NUM >= 0x070f04 + long curl_timeout; + curl_multi_timeout(curlm, &curl_timeout); + if (curl_timeout == 0) { + continue; + } else if (curl_timeout == -1) { + select_timeout.tv_sec = 0; + select_timeout.tv_usec = 50000; + } else { + select_timeout.tv_sec = curl_timeout / 1000; + select_timeout.tv_usec = (curl_timeout % 1000) * 1000; + } +#else + select_timeout.tv_sec = 0; + select_timeout.tv_usec = 50000; +#endif - if (slot->in_use && !data_received) { - max_fd = 0; + max_fd = -1; FD_ZERO(&readfds); FD_ZERO(&writefds); FD_ZERO(&excfds); - select_timeout.tv_sec = 0; - select_timeout.tv_usec = 50000; - select(max_fd, &readfds, &writefds, - &excfds, &select_timeout); + curl_multi_fdset(curlm, &readfds, &writefds, &excfds, &max_fd); + + select(max_fd+1, &readfds, &writefds, &excfds, &select_timeout); } } #else @@@ -816,6 -754,7 +756,6 @@@ static int http_request(const char *url headers = curl_slist_append(headers, buf.buf); strbuf_reset(&buf); } - slot->local = result; } else curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_buffer); @@@ -837,17 -776,11 +777,11 @@@ else if (missing_target(&results)) ret = HTTP_MISSING_TARGET; else if (results.http_code == 401) { - if (user_name && user_pass) { + if (http_auth.username && http_auth.password) { + credential_reject(&http_auth); ret = HTTP_NOAUTH; } else { - /* - * git_getpass is needed here because its very likely stdin/stdout are - * pipes to our parent process. So we instead need to use /dev/tty, - * but that is non-portable. Using git_getpass() can at least be stubbed - * on other platforms with a different implementation if/when necessary. - */ - if (!user_name) - user_name = xstrdup(git_getpass_with_description("Username", description)); + credential_fill(&http_auth); init_curl_http_auth(slot->curl); ret = HTTP_REAUTH; } @@@ -863,9 -796,13 +797,12 @@@ ret = HTTP_START_FAILED; } - slot->local = NULL; curl_slist_free_all(headers); strbuf_release(&buf); + if (ret == HTTP_OK) + credential_approve(&http_auth); + return ret; } @@@ -1057,6 -994,7 +994,6 @@@ void release_http_pack_request(struct h if (preq->packfile != NULL) { fclose(preq->packfile); preq->packfile = NULL; - preq->slot->local = NULL; } if (preq->range_header != NULL) { curl_slist_free_all(preq->range_header); @@@ -1078,6 -1016,7 +1015,6 @@@ int finish_http_pack_request(struct htt fclose(preq->packfile); preq->packfile = NULL; - preq->slot->local = NULL; lst = preq->lst; while (*lst != p) @@@ -1146,6 -1085,7 +1083,6 @@@ struct http_pack_request *new_http_pack } preq->slot = get_active_slot(); - preq->slot->local = preq->packfile; curl_easy_setopt(preq->slot->curl, CURLOPT_FILE, preq->packfile); curl_easy_setopt(preq->slot->curl, CURLOPT_WRITEFUNCTION, fwrite); curl_easy_setopt(preq->slot->curl, CURLOPT_URL, preq->url); @@@ -1202,6 -1142,7 +1139,6 @@@ static size_t fwrite_sha1_file(char *pt git_SHA1_Update(&freq->c, expn, sizeof(expn) - freq->stream.avail_out); } while (freq->stream.avail_in && freq->zret == Z_OK); - data_received++; return size; } diff --combined strbuf.c index a849705197,60e5e598dd..ff0b96b416 --- a/strbuf.c +++ b/strbuf.c @@@ -398,16 -398,39 +398,53 @@@ int strbuf_read_file(struct strbuf *sb return len; } +void strbuf_add_lines(struct strbuf *out, const char *prefix, + const char *buf, size_t size) +{ + while (size) { + const char *next = memchr(buf, '\n', size); + next = next ? (next + 1) : (buf + size); + strbuf_addstr(out, prefix); + strbuf_add(out, buf, next - buf); + size -= next - buf; + buf = next; + } + strbuf_complete_line(out); +} ++ + static int is_rfc3986_reserved(char ch) + { + switch (ch) { + case '!': case '*': case '\'': case '(': case ')': case ';': + case ':': case '@': case '&': case '=': case '+': case '$': + case ',': case '/': case '?': case '#': case '[': case ']': + return 1; + } + return 0; + } + + static int is_rfc3986_unreserved(char ch) + { + return isalnum(ch) || + ch == '-' || ch == '_' || ch == '.' || ch == '~'; + } + + void strbuf_add_urlencode(struct strbuf *sb, const char *s, size_t len, + int reserved) + { + strbuf_grow(sb, len); + while (len--) { + char ch = *s++; + if (is_rfc3986_unreserved(ch) || + (!reserved && is_rfc3986_reserved(ch))) + strbuf_addch(sb, ch); + else + strbuf_addf(sb, "%%%02x", ch); + } + } + + void strbuf_addstr_urlencode(struct strbuf *sb, const char *s, + int reserved) + { + strbuf_add_urlencode(sb, s, strlen(s), reserved); + } diff --combined strbuf.h index 08fc13d386,cecd48c45a..fbf059f4d3 --- a/strbuf.h +++ b/strbuf.h @@@ -100,14 -100,6 +100,14 @@@ extern void strbuf_addf(struct strbuf * __attribute__((format (printf,2,0))) extern void strbuf_vaddf(struct strbuf *sb, const char *fmt, va_list ap); +extern void strbuf_add_lines(struct strbuf *sb, const char *prefix, const char *buf, size_t size); + +static inline void strbuf_complete_line(struct strbuf *sb) +{ + if (sb->len && sb->buf[sb->len - 1] != '\n') + strbuf_addch(sb, '\n'); +} + extern size_t strbuf_fread(struct strbuf *, size_t, FILE *); /* XXX: if read fails, any partial read is undone */ extern ssize_t strbuf_read(struct strbuf *, int fd, size_t hint); @@@ -123,4 -115,9 +123,9 @@@ extern int launch_editor(const char *pa extern int strbuf_branchname(struct strbuf *sb, const char *name); extern int strbuf_check_branch_ref(struct strbuf *sb, const char *name); + extern void strbuf_add_urlencode(struct strbuf *, const char *, size_t, + int reserved); + extern void strbuf_addstr_urlencode(struct strbuf *, const char *, + int reserved); + #endif /* STRBUF_H */