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.

298 lines
6.9KB

  1. #include "cache.h"
  2. #include "config.h"
  3. #include "tempfile.h"
  4. #include "credential.h"
  5. #include "unix-socket.h"
  6. #include "parse-options.h"
  7. struct credential_cache_entry {
  8. struct credential item;
  9. timestamp_t expiration;
  10. };
  11. static struct credential_cache_entry *entries;
  12. static int entries_nr;
  13. static int entries_alloc;
  14. static void cache_credential(struct credential *c, int timeout)
  15. {
  16. struct credential_cache_entry *e;
  17. ALLOC_GROW(entries, entries_nr + 1, entries_alloc);
  18. e = &entries[entries_nr++];
  19. /* take ownership of pointers */
  20. memcpy(&e->item, c, sizeof(*c));
  21. memset(c, 0, sizeof(*c));
  22. e->expiration = time(NULL) + timeout;
  23. }
  24. static struct credential_cache_entry *lookup_credential(const struct credential *c)
  25. {
  26. int i;
  27. for (i = 0; i < entries_nr; i++) {
  28. struct credential *e = &entries[i].item;
  29. if (credential_match(c, e))
  30. return &entries[i];
  31. }
  32. return NULL;
  33. }
  34. static void remove_credential(const struct credential *c)
  35. {
  36. struct credential_cache_entry *e;
  37. e = lookup_credential(c);
  38. if (e)
  39. e->expiration = 0;
  40. }
  41. static timestamp_t check_expirations(void)
  42. {
  43. static timestamp_t wait_for_entry_until;
  44. int i = 0;
  45. timestamp_t now = time(NULL);
  46. timestamp_t next = TIME_MAX;
  47. /*
  48. * Initially give the client 30 seconds to actually contact us
  49. * and store a credential before we decide there's no point in
  50. * keeping the daemon around.
  51. */
  52. if (!wait_for_entry_until)
  53. wait_for_entry_until = now + 30;
  54. while (i < entries_nr) {
  55. if (entries[i].expiration <= now) {
  56. entries_nr--;
  57. credential_clear(&entries[i].item);
  58. if (i != entries_nr)
  59. memcpy(&entries[i], &entries[entries_nr], sizeof(*entries));
  60. /*
  61. * Stick around 30 seconds in case a new credential
  62. * shows up (e.g., because we just removed a failed
  63. * one, and we will soon get the correct one).
  64. */
  65. wait_for_entry_until = now + 30;
  66. }
  67. else {
  68. if (entries[i].expiration < next)
  69. next = entries[i].expiration;
  70. i++;
  71. }
  72. }
  73. if (!entries_nr) {
  74. if (wait_for_entry_until <= now)
  75. return 0;
  76. next = wait_for_entry_until;
  77. }
  78. return next - now;
  79. }
  80. static int read_request(FILE *fh, struct credential *c,
  81. struct strbuf *action, int *timeout)
  82. {
  83. static struct strbuf item = STRBUF_INIT;
  84. const char *p;
  85. strbuf_getline_lf(&item, fh);
  86. if (!skip_prefix(item.buf, "action=", &p))
  87. return error("client sent bogus action line: %s", item.buf);
  88. strbuf_addstr(action, p);
  89. strbuf_getline_lf(&item, fh);
  90. if (!skip_prefix(item.buf, "timeout=", &p))
  91. return error("client sent bogus timeout line: %s", item.buf);
  92. *timeout = atoi(p);
  93. if (credential_read(c, fh) < 0)
  94. return -1;
  95. return 0;
  96. }
  97. static void serve_one_client(FILE *in, FILE *out)
  98. {
  99. struct credential c = CREDENTIAL_INIT;
  100. struct strbuf action = STRBUF_INIT;
  101. int timeout = -1;
  102. if (read_request(in, &c, &action, &timeout) < 0)
  103. /* ignore error */ ;
  104. else if (!strcmp(action.buf, "get")) {
  105. struct credential_cache_entry *e = lookup_credential(&c);
  106. if (e) {
  107. fprintf(out, "username=%s\n", e->item.username);
  108. fprintf(out, "password=%s\n", e->item.password);
  109. }
  110. }
  111. else if (!strcmp(action.buf, "exit")) {
  112. /*
  113. * It's important that we clean up our socket first, and then
  114. * signal the client only once we have finished the cleanup.
  115. * Calling exit() directly does this, because we clean up in
  116. * our atexit() handler, and then signal the client when our
  117. * process actually ends, which closes the socket and gives
  118. * them EOF.
  119. */
  120. exit(0);
  121. }
  122. else if (!strcmp(action.buf, "erase"))
  123. remove_credential(&c);
  124. else if (!strcmp(action.buf, "store")) {
  125. if (timeout < 0)
  126. warning("cache client didn't specify a timeout");
  127. else if (!c.username || !c.password)
  128. warning("cache client gave us a partial credential");
  129. else {
  130. remove_credential(&c);
  131. cache_credential(&c, timeout);
  132. }
  133. }
  134. else
  135. warning("cache client sent unknown action: %s", action.buf);
  136. credential_clear(&c);
  137. strbuf_release(&action);
  138. }
  139. static int serve_cache_loop(int fd)
  140. {
  141. struct pollfd pfd;
  142. timestamp_t wakeup;
  143. wakeup = check_expirations();
  144. if (!wakeup)
  145. return 0;
  146. pfd.fd = fd;
  147. pfd.events = POLLIN;
  148. if (poll(&pfd, 1, 1000 * wakeup) < 0) {
  149. if (errno != EINTR)
  150. die_errno("poll failed");
  151. return 1;
  152. }
  153. if (pfd.revents & POLLIN) {
  154. int client, client2;
  155. FILE *in, *out;
  156. client = accept(fd, NULL, NULL);
  157. if (client < 0) {
  158. warning_errno("accept failed");
  159. return 1;
  160. }
  161. client2 = dup(client);
  162. if (client2 < 0) {
  163. warning_errno("dup failed");
  164. close(client);
  165. return 1;
  166. }
  167. in = xfdopen(client, "r");
  168. out = xfdopen(client2, "w");
  169. serve_one_client(in, out);
  170. fclose(in);
  171. fclose(out);
  172. }
  173. return 1;
  174. }
  175. static void serve_cache(const char *socket_path, int debug)
  176. {
  177. int fd;
  178. fd = unix_stream_listen(socket_path);
  179. if (fd < 0)
  180. die_errno("unable to bind to '%s'", socket_path);
  181. printf("ok\n");
  182. fclose(stdout);
  183. if (!debug) {
  184. if (!freopen("/dev/null", "w", stderr))
  185. die_errno("unable to point stderr to /dev/null");
  186. }
  187. while (serve_cache_loop(fd))
  188. ; /* nothing */
  189. close(fd);
  190. }
  191. static const char permissions_advice[] = N_(
  192. "The permissions on your socket directory are too loose; other\n"
  193. "users may be able to read your cached credentials. Consider running:\n"
  194. "\n"
  195. " chmod 0700 %s");
  196. static void init_socket_directory(const char *path)
  197. {
  198. struct stat st;
  199. char *path_copy = xstrdup(path);
  200. char *dir = dirname(path_copy);
  201. if (!stat(dir, &st)) {
  202. if (st.st_mode & 077)
  203. die(_(permissions_advice), dir);
  204. } else {
  205. /*
  206. * We must be sure to create the directory with the correct mode,
  207. * not just chmod it after the fact; otherwise, there is a race
  208. * condition in which somebody can chdir to it, sleep, then try to open
  209. * our protected socket.
  210. */
  211. if (safe_create_leading_directories_const(dir) < 0)
  212. die_errno("unable to create directories for '%s'", dir);
  213. if (mkdir(dir, 0700) < 0)
  214. die_errno("unable to mkdir '%s'", dir);
  215. }
  216. if (chdir(dir))
  217. /*
  218. * We don't actually care what our cwd is; we chdir here just to
  219. * be a friendly daemon and avoid tying up our original cwd.
  220. * If this fails, it's OK to just continue without that benefit.
  221. */
  222. ;
  223. free(path_copy);
  224. }
  225. int cmd_main(int argc, const char **argv)
  226. {
  227. struct tempfile *socket_file;
  228. const char *socket_path;
  229. int ignore_sighup = 0;
  230. static const char *usage[] = {
  231. "git-credential-cache--daemon [opts] <socket_path>",
  232. NULL
  233. };
  234. int debug = 0;
  235. const struct option options[] = {
  236. OPT_BOOL(0, "debug", &debug,
  237. N_("print debugging messages to stderr")),
  238. OPT_END()
  239. };
  240. git_config_get_bool("credentialcache.ignoresighup", &ignore_sighup);
  241. argc = parse_options(argc, argv, NULL, options, usage, 0);
  242. socket_path = argv[0];
  243. if (!socket_path)
  244. usage_with_options(usage, options);
  245. if (!is_absolute_path(socket_path))
  246. die("socket directory must be an absolute path");
  247. init_socket_directory(socket_path);
  248. socket_file = register_tempfile(socket_path);
  249. if (ignore_sighup)
  250. signal(SIGHUP, SIG_IGN);
  251. serve_cache(socket_path, debug);
  252. delete_tempfile(&socket_file);
  253. return 0;
  254. }