THIS IS A TEST INSTANCE ONLY! REPOSITORIES CAN BE DELETED AT ANY TIME!

Git Source Code Mirror - This is a publish-only repository and all pull requests are ignored. Please follow Documentation/SubmittingPatches procedure for any of your improvements.
git
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

375 lines
7.9KB

  1. #include "cache.h"
  2. #include "config.h"
  3. #include "credential.h"
  4. #include "string-list.h"
  5. #include "run-command.h"
  6. #include "url.h"
  7. #include "prompt.h"
  8. #include "sigchain.h"
  9. void credential_init(struct credential *c)
  10. {
  11. memset(c, 0, sizeof(*c));
  12. c->helpers.strdup_strings = 1;
  13. }
  14. void credential_clear(struct credential *c)
  15. {
  16. free(c->protocol);
  17. free(c->host);
  18. free(c->path);
  19. free(c->username);
  20. free(c->password);
  21. string_list_clear(&c->helpers, 0);
  22. credential_init(c);
  23. }
  24. int credential_match(const struct credential *want,
  25. const struct credential *have)
  26. {
  27. #define CHECK(x) (!want->x || (have->x && !strcmp(want->x, have->x)))
  28. return CHECK(protocol) &&
  29. CHECK(host) &&
  30. CHECK(path) &&
  31. CHECK(username);
  32. #undef CHECK
  33. }
  34. static int credential_config_callback(const char *var, const char *value,
  35. void *data)
  36. {
  37. struct credential *c = data;
  38. const char *key, *dot;
  39. if (!skip_prefix(var, "credential.", &key))
  40. return 0;
  41. if (!value)
  42. return config_error_nonbool(var);
  43. dot = strrchr(key, '.');
  44. if (dot) {
  45. struct credential want = CREDENTIAL_INIT;
  46. char *url = xmemdupz(key, dot - key);
  47. int matched;
  48. credential_from_url(&want, url);
  49. matched = credential_match(&want, c);
  50. credential_clear(&want);
  51. free(url);
  52. if (!matched)
  53. return 0;
  54. key = dot + 1;
  55. }
  56. if (!strcmp(key, "helper")) {
  57. if (*value)
  58. string_list_append(&c->helpers, value);
  59. else
  60. string_list_clear(&c->helpers, 0);
  61. } else if (!strcmp(key, "username")) {
  62. if (!c->username)
  63. c->username = xstrdup(value);
  64. }
  65. else if (!strcmp(key, "usehttppath"))
  66. c->use_http_path = git_config_bool(var, value);
  67. return 0;
  68. }
  69. static int proto_is_http(const char *s)
  70. {
  71. if (!s)
  72. return 0;
  73. return !strcmp(s, "https") || !strcmp(s, "http");
  74. }
  75. static void credential_apply_config(struct credential *c)
  76. {
  77. if (c->configured)
  78. return;
  79. git_config(credential_config_callback, c);
  80. c->configured = 1;
  81. if (!c->use_http_path && proto_is_http(c->protocol)) {
  82. FREE_AND_NULL(c->path);
  83. }
  84. }
  85. static void credential_describe(struct credential *c, struct strbuf *out)
  86. {
  87. if (!c->protocol)
  88. return;
  89. strbuf_addf(out, "%s://", c->protocol);
  90. if (c->username && *c->username)
  91. strbuf_addf(out, "%s@", c->username);
  92. if (c->host)
  93. strbuf_addstr(out, c->host);
  94. if (c->path)
  95. strbuf_addf(out, "/%s", c->path);
  96. }
  97. static char *credential_ask_one(const char *what, struct credential *c,
  98. int flags)
  99. {
  100. struct strbuf desc = STRBUF_INIT;
  101. struct strbuf prompt = STRBUF_INIT;
  102. char *r;
  103. credential_describe(c, &desc);
  104. if (desc.len)
  105. strbuf_addf(&prompt, "%s for '%s': ", what, desc.buf);
  106. else
  107. strbuf_addf(&prompt, "%s: ", what);
  108. r = git_prompt(prompt.buf, flags);
  109. strbuf_release(&desc);
  110. strbuf_release(&prompt);
  111. return xstrdup(r);
  112. }
  113. static void credential_getpass(struct credential *c)
  114. {
  115. if (!c->username)
  116. c->username = credential_ask_one("Username", c,
  117. PROMPT_ASKPASS|PROMPT_ECHO);
  118. if (!c->password)
  119. c->password = credential_ask_one("Password", c,
  120. PROMPT_ASKPASS);
  121. }
  122. int credential_read(struct credential *c, FILE *fp)
  123. {
  124. struct strbuf line = STRBUF_INIT;
  125. while (strbuf_getline_lf(&line, fp) != EOF) {
  126. char *key = line.buf;
  127. char *value = strchr(key, '=');
  128. if (!line.len)
  129. break;
  130. if (!value) {
  131. warning("invalid credential line: %s", key);
  132. strbuf_release(&line);
  133. return -1;
  134. }
  135. *value++ = '\0';
  136. if (!strcmp(key, "username")) {
  137. free(c->username);
  138. c->username = xstrdup(value);
  139. } else if (!strcmp(key, "password")) {
  140. free(c->password);
  141. c->password = xstrdup(value);
  142. } else if (!strcmp(key, "protocol")) {
  143. free(c->protocol);
  144. c->protocol = xstrdup(value);
  145. } else if (!strcmp(key, "host")) {
  146. free(c->host);
  147. c->host = xstrdup(value);
  148. } else if (!strcmp(key, "path")) {
  149. free(c->path);
  150. c->path = xstrdup(value);
  151. } else if (!strcmp(key, "url")) {
  152. credential_from_url(c, value);
  153. } else if (!strcmp(key, "quit")) {
  154. c->quit = !!git_config_bool("quit", value);
  155. }
  156. /*
  157. * Ignore other lines; we don't know what they mean, but
  158. * this future-proofs us when later versions of git do
  159. * learn new lines, and the helpers are updated to match.
  160. */
  161. }
  162. strbuf_release(&line);
  163. return 0;
  164. }
  165. static void credential_write_item(FILE *fp, const char *key, const char *value)
  166. {
  167. if (!value)
  168. return;
  169. fprintf(fp, "%s=%s\n", key, value);
  170. }
  171. void credential_write(const struct credential *c, FILE *fp)
  172. {
  173. credential_write_item(fp, "protocol", c->protocol);
  174. credential_write_item(fp, "host", c->host);
  175. credential_write_item(fp, "path", c->path);
  176. credential_write_item(fp, "username", c->username);
  177. credential_write_item(fp, "password", c->password);
  178. }
  179. static int run_credential_helper(struct credential *c,
  180. const char *cmd,
  181. int want_output)
  182. {
  183. struct child_process helper = CHILD_PROCESS_INIT;
  184. const char *argv[] = { NULL, NULL };
  185. FILE *fp;
  186. argv[0] = cmd;
  187. helper.argv = argv;
  188. helper.use_shell = 1;
  189. helper.in = -1;
  190. if (want_output)
  191. helper.out = -1;
  192. else
  193. helper.no_stdout = 1;
  194. if (start_command(&helper) < 0)
  195. return -1;
  196. fp = xfdopen(helper.in, "w");
  197. sigchain_push(SIGPIPE, SIG_IGN);
  198. credential_write(c, fp);
  199. fclose(fp);
  200. sigchain_pop(SIGPIPE);
  201. if (want_output) {
  202. int r;
  203. fp = xfdopen(helper.out, "r");
  204. r = credential_read(c, fp);
  205. fclose(fp);
  206. if (r < 0) {
  207. finish_command(&helper);
  208. return -1;
  209. }
  210. }
  211. if (finish_command(&helper))
  212. return -1;
  213. return 0;
  214. }
  215. static int credential_do(struct credential *c, const char *helper,
  216. const char *operation)
  217. {
  218. struct strbuf cmd = STRBUF_INIT;
  219. int r;
  220. if (helper[0] == '!')
  221. strbuf_addstr(&cmd, helper + 1);
  222. else if (is_absolute_path(helper))
  223. strbuf_addstr(&cmd, helper);
  224. else
  225. strbuf_addf(&cmd, "git credential-%s", helper);
  226. strbuf_addf(&cmd, " %s", operation);
  227. r = run_credential_helper(c, cmd.buf, !strcmp(operation, "get"));
  228. strbuf_release(&cmd);
  229. return r;
  230. }
  231. void credential_fill(struct credential *c)
  232. {
  233. int i;
  234. if (c->username && c->password)
  235. return;
  236. credential_apply_config(c);
  237. for (i = 0; i < c->helpers.nr; i++) {
  238. credential_do(c, c->helpers.items[i].string, "get");
  239. if (c->username && c->password)
  240. return;
  241. if (c->quit)
  242. die("credential helper '%s' told us to quit",
  243. c->helpers.items[i].string);
  244. }
  245. credential_getpass(c);
  246. if (!c->username && !c->password)
  247. die("unable to get password from user");
  248. }
  249. void credential_approve(struct credential *c)
  250. {
  251. int i;
  252. if (c->approved)
  253. return;
  254. if (!c->username || !c->password)
  255. return;
  256. credential_apply_config(c);
  257. for (i = 0; i < c->helpers.nr; i++)
  258. credential_do(c, c->helpers.items[i].string, "store");
  259. c->approved = 1;
  260. }
  261. void credential_reject(struct credential *c)
  262. {
  263. int i;
  264. credential_apply_config(c);
  265. for (i = 0; i < c->helpers.nr; i++)
  266. credential_do(c, c->helpers.items[i].string, "erase");
  267. FREE_AND_NULL(c->username);
  268. FREE_AND_NULL(c->password);
  269. c->approved = 0;
  270. }
  271. void credential_from_url(struct credential *c, const char *url)
  272. {
  273. const char *at, *colon, *cp, *slash, *host, *proto_end;
  274. credential_clear(c);
  275. /*
  276. * Match one of:
  277. * (1) proto://<host>/...
  278. * (2) proto://<user>@<host>/...
  279. * (3) proto://<user>:<pass>@<host>/...
  280. */
  281. proto_end = strstr(url, "://");
  282. if (!proto_end)
  283. return;
  284. cp = proto_end + 3;
  285. at = strchr(cp, '@');
  286. colon = strchr(cp, ':');
  287. slash = strchrnul(cp, '/');
  288. if (!at || slash <= at) {
  289. /* Case (1) */
  290. host = cp;
  291. }
  292. else if (!colon || at <= colon) {
  293. /* Case (2) */
  294. c->username = url_decode_mem(cp, at - cp);
  295. host = at + 1;
  296. } else {
  297. /* Case (3) */
  298. c->username = url_decode_mem(cp, colon - cp);
  299. c->password = url_decode_mem(colon + 1, at - (colon + 1));
  300. host = at + 1;
  301. }
  302. if (proto_end - url > 0)
  303. c->protocol = xmemdupz(url, proto_end - url);
  304. if (slash - host > 0)
  305. c->host = url_decode_mem(host, slash - host);
  306. /* Trim leading and trailing slashes from path */
  307. while (*slash == '/')
  308. slash++;
  309. if (*slash) {
  310. char *p;
  311. c->path = url_decode(slash);
  312. p = c->path + strlen(c->path) - 1;
  313. while (p > c->path && *p == '/')
  314. *p-- = '\0';
  315. }
  316. }