Raw
1 #define USE_THE_REPOSITORY_VARIABLE
2 #include "builtin.h"
3 #include "config.h"
4 #include "gettext.h"
5 #include "lockfile.h"
6 #include "credential.h"
7 #include "path.h"
8 #include "string-list.h"
9 #include "parse-options.h"
10 #include "url.h"
11 #include "write-or-die.h"
12
13 static struct lock_file credential_lock;
14
15 static int parse_credential_file(const char *fn,
16 struct credential *c,
17 void (*match_cb)(struct credential *),
18 void (*other_cb)(struct strbuf *),
19 int match_password)
20 {
21 FILE *fh;
22 struct strbuf line = STRBUF_INIT;
23 struct credential entry = CREDENTIAL_INIT;
24 int found_credential = 0;
25
26 fh = fopen(fn, "r");
27 if (!fh) {
28 if (errno != ENOENT && errno != EACCES)
29 die_errno("unable to open %s", fn);
30 return found_credential;
31 }
32
33 while (strbuf_getline_lf(&line, fh) != EOF) {
34 if (!credential_from_url_gently(&entry, line.buf, 1) &&
35 entry.username && entry.password &&
36 credential_match(c, &entry, match_password)) {
37 found_credential = 1;
38 if (match_cb) {
39 match_cb(&entry);
40 break;
41 }
42 }
43 else if (other_cb)
44 other_cb(&line);
45 }
46
47 credential_clear(&entry);
48 strbuf_release(&line);
49 fclose(fh);
50 return found_credential;
51 }
52
53 static void print_entry(struct credential *c)
54 {
55 printf("username=%s\n", c->username);
56 printf("password=%s\n", c->password);
57 }
58
59 static void print_line(struct strbuf *buf)
60 {
61 strbuf_addch(buf, '\n');
62 write_or_die(get_lock_file_fd(&credential_lock), buf->buf, buf->len);
63 }
64
65 static void rewrite_credential_file(const char *fn, struct credential *c,
66 struct strbuf *extra, int match_password)
67 {
68 int timeout_ms = 1000;
69
70 repo_config_get_int(the_repository, "credentialstore.locktimeoutms", &timeout_ms);
71 if (hold_lock_file_for_update_timeout(&credential_lock, fn, 0, timeout_ms) < 0)
72 die_errno(_("unable to get credential storage lock in %d ms"), timeout_ms);
73 if (extra)
74 print_line(extra);
75 parse_credential_file(fn, c, NULL, print_line, match_password);
76 if (commit_lock_file(&credential_lock) < 0)
77 die_errno("unable to write credential store");
78 }
79
80 static int is_rfc3986_reserved_or_unreserved(char ch)
81 {
82 if (is_rfc3986_unreserved(ch))
83 return 1;
84 switch (ch) {
85 case '!': case '*': case '\'': case '(': case ')': case ';':
86 case ':': case '@': case '&': case '=': case '+': case '$':
87 case ',': case '/': case '?': case '#': case '[': case ']':
88 return 1;
89 }
90 return 0;
91 }
92
93 static void store_credential_file(const char *fn, struct credential *c)
94 {
95 struct strbuf buf = STRBUF_INIT;
96
97 strbuf_addf(&buf, "%s://", c->protocol);
98 strbuf_addstr_urlencode(&buf, c->username, is_rfc3986_unreserved);
99 strbuf_addch(&buf, ':');
100 strbuf_addstr_urlencode(&buf, c->password, is_rfc3986_unreserved);
101 strbuf_addch(&buf, '@');
102 if (c->host)
103 strbuf_addstr_urlencode(&buf, c->host, is_rfc3986_unreserved);
104 if (c->path) {
105 strbuf_addch(&buf, '/');
106 strbuf_addstr_urlencode(&buf, c->path,
107 is_rfc3986_reserved_or_unreserved);
108 }
109
110 rewrite_credential_file(fn, c, &buf, 0);
111 strbuf_release(&buf);
112 }
113
114 static void store_credential(const struct string_list *fns, struct credential *c)
115 {
116 struct string_list_item *fn;
117
118 /*
119 * Sanity check that what we are storing is actually sensible.
120 * In particular, we can't make a URL without a protocol field.
121 * Without either a host or pathname (depending on the scheme),
122 * we have no primary key. And without a username and password,
123 * we are not actually storing a credential.
124 */
125 if (!c->protocol || !(c->host || c->path) || !c->username || !c->password)
126 return;
127
128 for_each_string_list_item(fn, fns)
129 if (!access(fn->string, F_OK)) {
130 store_credential_file(fn->string, c);
131 return;
132 }
133 /*
134 * Write credential to the filename specified by fns->items[0], thus
135 * creating it
136 */
137 if (fns->nr)
138 store_credential_file(fns->items[0].string, c);
139 }
140
141 static void remove_credential(const struct string_list *fns, struct credential *c)
142 {
143 struct string_list_item *fn;
144
145 /*
146 * Sanity check that we actually have something to match
147 * against. The input we get is a restrictive pattern,
148 * so technically a blank credential means "erase everything".
149 * But it is too easy to accidentally send this, since it is equivalent
150 * to empty input. So explicitly disallow it, and require that the
151 * pattern have some actual content to match.
152 */
153 if (!c->protocol && !c->host && !c->path && !c->username)
154 return;
155 for_each_string_list_item(fn, fns)
156 if (!access(fn->string, F_OK))
157 rewrite_credential_file(fn->string, c, NULL, 1);
158 }
159
160 static void lookup_credential(const struct string_list *fns, struct credential *c)
161 {
162 struct string_list_item *fn;
163
164 for_each_string_list_item(fn, fns)
165 if (parse_credential_file(fn->string, c, print_entry, NULL, 0))
166 return; /* Found credential */
167 }
168
169 int cmd_credential_store(int argc,
170 const char **argv,
171 const char *prefix,
172 struct repository *repo UNUSED)
173 {
174 const char * const usage[] = {
175 "git credential-store [<options>] <action>",
176 NULL
177 };
178 const char *op;
179 struct credential c = CREDENTIAL_INIT;
180 struct string_list fns = STRING_LIST_INIT_DUP;
181 char *file = NULL;
182 struct option options[] = {
183 OPT_STRING(0, "file", &file, "path",
184 "fetch and store credentials in <path>"),
185 OPT_END()
186 };
187
188 umask(077);
189
190 argc = parse_options(argc, (const char **)argv, prefix, options, usage, 0);
191 if (argc != 1)
192 usage_with_options(usage, options);
193 op = argv[0];
194
195 if (file) {
196 string_list_append(&fns, file);
197 } else {
198 if ((file = interpolate_path("~/.git-credentials", 0)))
199 string_list_append_nodup(&fns, file);
200 file = xdg_config_home("credentials");
201 if (file)
202 string_list_append_nodup(&fns, file);
203 }
204 if (!fns.nr)
205 die("unable to set up default path; use --file");
206
207 if (credential_read(&c, stdin, CREDENTIAL_OP_HELPER) < 0)
208 die("unable to read credential");
209
210 if (!strcmp(op, "get"))
211 lookup_credential(&fns, &c);
212 else if (!strcmp(op, "erase"))
213 remove_credential(&fns, &c);
214 else if (!strcmp(op, "store"))
215 store_credential(&fns, &c);
216 else
217 ; /* Ignore unknown operation. */
218
219 string_list_clear(&fns, 0);
220 credential_clear(&c);
221 return 0;
222 }