static void serve_one_client(FILE *in, FILE *out) { struct credential c = CREDENTIAL_INIT; struct strbuf action = STRBUF_INIT; int timeout = -1; if (read_request(in, &c, &action, &timeout) < 0) /* ignore error */ ; else if (!strcmp(action.buf, "get")) { struct credential_cache_entry *e = lookup_credential(&c); if (e) { fprintf(out, "username=%s\n", e->item.username); fprintf(out, "password=%s\n", e->item.password); } } else if (!strcmp(action.buf, "exit")) exit(0); else if (!strcmp(action.buf, "erase")) remove_credential(&c); else if (!strcmp(action.buf, "store")) { if (timeout < 0) warning("cache client didn't specify a timeout"); else if (!c.username || !c.password) warning("cache client gave us a partial credential"); else { remove_credential(&c); cache_credential(&c, timeout); } } else warning("cache client sent unknown action: %s", action.buf); credential_clear(&c); strbuf_release(&action); }
static void parse_credential_file(const char *fn, struct credential *c, void (*match_cb)(struct credential *), void (*other_cb)(struct strbuf *)) { FILE *fh; struct strbuf line = STRBUF_INIT; struct credential entry = CREDENTIAL_INIT; fh = fopen(fn, "r"); if (!fh) { if (errno != ENOENT) die_errno("unable to open %s", fn); return; } while (strbuf_getline(&line, fh, '\n') != EOF) { credential_from_url(&entry, line.buf); if (entry.username && entry.password && credential_match(c, &entry)) { if (match_cb) { match_cb(&entry); break; } } else if (other_cb) other_cb(&line); } credential_clear(&entry); strbuf_release(&line); fclose(fh); }
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; }
void credential_from_url(struct credential *c, const char *url) { const char *at, *colon, *cp, *slash, *host, *proto_end; credential_clear(c); /* * Match one of: * (1) proto://<host>/... * (2) proto://<user>@<host>/... * (3) proto://<user>:<pass>@<host>/... */ proto_end = strstr(url, "://"); if (!proto_end) return; cp = proto_end + 3; at = strchr(cp, '@'); colon = strchr(cp, ':'); slash = strchrnul(cp, '/'); if (!at || slash <= at) { /* Case (1) */ host = cp; } else if (!colon || at <= colon) { /* Case (2) */ c->username = url_decode_mem(cp, at - cp); host = at + 1; } else { /* Case (3) */ c->username = url_decode_mem(cp, colon - cp); c->password = url_decode_mem(colon + 1, at - (colon + 1)); host = at + 1; } if (proto_end - url > 0) c->protocol = xmemdupz(url, proto_end - url); if (slash - host > 0) c->host = url_decode_mem(host, slash - host); /* Trim leading and trailing slashes from path */ while (*slash == '/') slash++; if (*slash) { char *p; c->path = url_decode(slash); p = c->path + strlen(c->path) - 1; while (p > c->path && *p == '/') *p-- = '\0'; } }
static void serve_one_client(FILE *in, FILE *out) { struct credential c = CREDENTIAL_INIT; struct strbuf action = STRBUF_INIT; int timeout = -1; if (read_request(in, &c, &action, &timeout) < 0) /* ignore error */ ; else if (!strcmp(action.buf, "get")) { struct credential_cache_entry *e = lookup_credential(&c); if (e) { fprintf(out, "username=%s\n", e->item.username); fprintf(out, "password=%s\n", e->item.password); } } else if (!strcmp(action.buf, "exit")) { /* * It's important that we clean up our socket first, and then * signal the client only once we have finished the cleanup. * Calling exit() directly does this, because we clean up in * our atexit() handler, and then signal the client when our * process actually ends, which closes the socket and gives * them EOF. */ exit(0); } else if (!strcmp(action.buf, "erase")) remove_credential(&c); else if (!strcmp(action.buf, "store")) { if (timeout < 0) warning("cache client didn't specify a timeout"); else if (!c.username || !c.password) warning("cache client gave us a partial credential"); else { remove_credential(&c); cache_credential(&c, timeout); } } else warning("cache client sent unknown action: %s", action.buf); credential_clear(&c); strbuf_release(&action); }
static int check_expirations(void) { static unsigned long wait_for_entry_until; int i = 0; unsigned long now = time(NULL); unsigned long next = (unsigned long)-1; /* * Initially give the client 30 seconds to actually contact us * and store a credential before we decide there's no point in * keeping the daemon around. */ if (!wait_for_entry_until) wait_for_entry_until = now + 30; while (i < entries_nr) { if (entries[i].expiration <= now) { entries_nr--; credential_clear(&entries[i].item); if (i != entries_nr) memcpy(&entries[i], &entries[entries_nr], sizeof(*entries)); /* * Stick around 30 seconds in case a new credential * shows up (e.g., because we just removed a failed * one, and we will soon get the correct one). */ wait_for_entry_until = now + 30; } else { if (entries[i].expiration < next) next = entries[i].expiration; i++; } } if (!entries_nr) { if (wait_for_entry_until <= now) return 0; next = wait_for_entry_until; } return next - now; }
static int credential_config_callback(const char *var, const char *value, void *data) { struct credential *c = data; const char *key, *dot; if (!skip_prefix(var, "credential.", &key)) return 0; if (!value) return config_error_nonbool(var); dot = strrchr(key, '.'); if (dot) { struct credential want = CREDENTIAL_INIT; char *url = xmemdupz(key, dot - key); int matched; credential_from_url(&want, url); matched = credential_match(&want, c); credential_clear(&want); free(url); if (!matched) return 0; key = dot + 1; } if (!strcmp(key, "helper")) { if (*value) string_list_append(&c->helpers, value); else string_list_clear(&c->helpers, 0); } else if (!strcmp(key, "username")) { if (!c->username) c->username = xstrdup(value); } else if (!strcmp(key, "usehttppath")) c->use_http_path = git_config_bool(var, value); return 0; }