Merge branch 'jk/split-broken-ident'
[git] / contrib / credential / gnome-keyring / git-credential-gnome-keyring.c
1 /*
2  * Copyright (C) 2011 John Szakmeister <john@szakmeister.net>
3  *               2012 Philipp A. Hartmann <pah@qo.cx>
4  *
5  *  This program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2 of the License, or
8  *  (at your option) any later version.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with this program; if not, write to the Free Software
17  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  */
19
20 /*
21  * Credits:
22  * - GNOME Keyring API handling originally written by John Szakmeister
23  * - ported to credential helper API by Philipp A. Hartmann
24  */
25
26 #include <stdio.h>
27 #include <string.h>
28 #include <stdlib.h>
29 #include <glib.h>
30 #include <gnome-keyring.h>
31
32 #ifdef GNOME_KEYRING_DEFAULT
33
34    /* Modern gnome-keyring */
35
36 #include <gnome-keyring-memory.h>
37
38 #else
39
40    /*
41     * Support ancient gnome-keyring, circ. RHEL 5.X.
42     * GNOME_KEYRING_DEFAULT seems to have been introduced with Gnome 2.22,
43     * and the other features roughly around Gnome 2.20, 6 months before.
44     * Ubuntu 8.04 used Gnome 2.22 (I think).  Not sure any distro used 2.20.
45     * So the existence/non-existence of GNOME_KEYRING_DEFAULT seems like
46     * a decent thing to use as an indicator.
47     */
48
49 #define GNOME_KEYRING_DEFAULT NULL
50
51 /*
52  * ancient gnome-keyring returns DENIED when an entry is not found.
53  * Setting NO_MATCH to DENIED will prevent us from reporting DENIED
54  * errors during get and erase operations, but we will still report
55  * DENIED errors during a store.
56  */
57 #define GNOME_KEYRING_RESULT_NO_MATCH GNOME_KEYRING_RESULT_DENIED
58
59 #define gnome_keyring_memory_alloc g_malloc
60 #define gnome_keyring_memory_free gnome_keyring_free_password
61 #define gnome_keyring_memory_strdup g_strdup
62
63 static const char* gnome_keyring_result_to_message(GnomeKeyringResult result)
64 {
65         switch (result) {
66         case GNOME_KEYRING_RESULT_OK:
67                 return "OK";
68         case GNOME_KEYRING_RESULT_DENIED:
69                 return "Denied";
70         case GNOME_KEYRING_RESULT_NO_KEYRING_DAEMON:
71                 return "No Keyring Daemon";
72         case GNOME_KEYRING_RESULT_ALREADY_UNLOCKED:
73                 return "Already UnLocked";
74         case GNOME_KEYRING_RESULT_NO_SUCH_KEYRING:
75                 return "No Such Keyring";
76         case GNOME_KEYRING_RESULT_BAD_ARGUMENTS:
77                 return "Bad Arguments";
78         case GNOME_KEYRING_RESULT_IO_ERROR:
79                 return "IO Error";
80         case GNOME_KEYRING_RESULT_CANCELLED:
81                 return "Cancelled";
82         case GNOME_KEYRING_RESULT_ALREADY_EXISTS:
83                 return "Already Exists";
84         default:
85                 return "Unknown Error";
86         }
87 }
88
89 /*
90  * Support really ancient gnome-keyring, circ. RHEL 4.X.
91  * Just a guess for the Glib version.  Glib 2.8 was roughly Gnome 2.12 ?
92  * Which was released with gnome-keyring 0.4.3 ??
93  */
94 #if GLIB_MAJOR_VERSION == 2 && GLIB_MINOR_VERSION < 8
95
96 static void gnome_keyring_done_cb(GnomeKeyringResult result, gpointer user_data)
97 {
98         gpointer *data = (gpointer*) user_data;
99         int *done = (int*) data[0];
100         GnomeKeyringResult *r = (GnomeKeyringResult*) data[1];
101
102         *r = result;
103         *done = 1;
104 }
105
106 static void wait_for_request_completion(int *done)
107 {
108         GMainContext *mc = g_main_context_default();
109         while (!*done)
110                 g_main_context_iteration(mc, TRUE);
111 }
112
113 static GnomeKeyringResult gnome_keyring_item_delete_sync(const char *keyring, guint32 id)
114 {
115         int done = 0;
116         GnomeKeyringResult result;
117         gpointer data[] = { &done, &result };
118
119         gnome_keyring_item_delete(keyring, id, gnome_keyring_done_cb, data,
120                 NULL);
121
122         wait_for_request_completion(&done);
123
124         return result;
125 }
126
127 #endif
128 #endif
129
130 /*
131  * This credential struct and API is simplified from git's credential.{h,c}
132  */
133 struct credential
134 {
135         char          *protocol;
136         char          *host;
137         unsigned short port;
138         char          *path;
139         char          *username;
140         char          *password;
141 };
142
143 #define CREDENTIAL_INIT \
144   { NULL,NULL,0,NULL,NULL,NULL }
145
146 typedef int (*credential_op_cb)(struct credential*);
147
148 struct credential_operation
149 {
150         char             *name;
151         credential_op_cb op;
152 };
153
154 #define CREDENTIAL_OP_END \
155   { NULL,NULL }
156
157 /* ----------------- GNOME Keyring functions ----------------- */
158
159 /* create a special keyring option string, if path is given */
160 static char* keyring_object(struct credential *c)
161 {
162         if (!c->path)
163                 return NULL;
164
165         if (c->port)
166                 return g_strdup_printf("%s:%hd/%s", c->host, c->port, c->path);
167
168         return g_strdup_printf("%s/%s", c->host, c->path);
169 }
170
171 static int keyring_get(struct credential *c)
172 {
173         char* object = NULL;
174         GList *entries;
175         GnomeKeyringNetworkPasswordData *password_data;
176         GnomeKeyringResult result;
177
178         if (!c->protocol || !(c->host || c->path))
179                 return EXIT_FAILURE;
180
181         object = keyring_object(c);
182
183         result = gnome_keyring_find_network_password_sync(
184                                 c->username,
185                                 NULL /* domain */,
186                                 c->host,
187                                 object,
188                                 c->protocol,
189                                 NULL /* authtype */,
190                                 c->port,
191                                 &entries);
192
193         g_free(object);
194
195         if (result == GNOME_KEYRING_RESULT_NO_MATCH)
196                 return EXIT_SUCCESS;
197
198         if (result == GNOME_KEYRING_RESULT_CANCELLED)
199                 return EXIT_SUCCESS;
200
201         if (result != GNOME_KEYRING_RESULT_OK) {
202                 g_critical("%s", gnome_keyring_result_to_message(result));
203                 return EXIT_FAILURE;
204         }
205
206         /* pick the first one from the list */
207         password_data = (GnomeKeyringNetworkPasswordData *) entries->data;
208
209         gnome_keyring_memory_free(c->password);
210         c->password = gnome_keyring_memory_strdup(password_data->password);
211
212         if (!c->username)
213                 c->username = g_strdup(password_data->user);
214
215         gnome_keyring_network_password_list_free(entries);
216
217         return EXIT_SUCCESS;
218 }
219
220
221 static int keyring_store(struct credential *c)
222 {
223         guint32 item_id;
224         char  *object = NULL;
225         GnomeKeyringResult result;
226
227         /*
228          * Sanity check that what we are storing is actually sensible.
229          * In particular, we can't make a URL without a protocol field.
230          * Without either a host or pathname (depending on the scheme),
231          * we have no primary key. And without a username and password,
232          * we are not actually storing a credential.
233          */
234         if (!c->protocol || !(c->host || c->path) ||
235             !c->username || !c->password)
236                 return EXIT_FAILURE;
237
238         object = keyring_object(c);
239
240         result = gnome_keyring_set_network_password_sync(
241                                 GNOME_KEYRING_DEFAULT,
242                                 c->username,
243                                 NULL /* domain */,
244                                 c->host,
245                                 object,
246                                 c->protocol,
247                                 NULL /* authtype */,
248                                 c->port,
249                                 c->password,
250                                 &item_id);
251
252         g_free(object);
253
254         if (result != GNOME_KEYRING_RESULT_OK &&
255             result != GNOME_KEYRING_RESULT_CANCELLED) {
256                 g_critical("%s", gnome_keyring_result_to_message(result));
257                 return EXIT_FAILURE;
258         }
259
260         return EXIT_SUCCESS;
261 }
262
263 static int keyring_erase(struct credential *c)
264 {
265         char  *object = NULL;
266         GList *entries;
267         GnomeKeyringNetworkPasswordData *password_data;
268         GnomeKeyringResult result;
269
270         /*
271          * Sanity check that we actually have something to match
272          * against. The input we get is a restrictive pattern,
273          * so technically a blank credential means "erase everything".
274          * But it is too easy to accidentally send this, since it is equivalent
275          * to empty input. So explicitly disallow it, and require that the
276          * pattern have some actual content to match.
277          */
278         if (!c->protocol && !c->host && !c->path && !c->username)
279                 return EXIT_FAILURE;
280
281         object = keyring_object(c);
282
283         result = gnome_keyring_find_network_password_sync(
284                                 c->username,
285                                 NULL /* domain */,
286                                 c->host,
287                                 object,
288                                 c->protocol,
289                                 NULL /* authtype */,
290                                 c->port,
291                                 &entries);
292
293         g_free(object);
294
295         if (result == GNOME_KEYRING_RESULT_NO_MATCH)
296                 return EXIT_SUCCESS;
297
298         if (result == GNOME_KEYRING_RESULT_CANCELLED)
299                 return EXIT_SUCCESS;
300
301         if (result != GNOME_KEYRING_RESULT_OK)
302         {
303                 g_critical("%s", gnome_keyring_result_to_message(result));
304                 return EXIT_FAILURE;
305         }
306
307         /* pick the first one from the list (delete all matches?) */
308         password_data = (GnomeKeyringNetworkPasswordData *) entries->data;
309
310         result = gnome_keyring_item_delete_sync(
311                 password_data->keyring, password_data->item_id);
312
313         gnome_keyring_network_password_list_free(entries);
314
315         if (result != GNOME_KEYRING_RESULT_OK)
316         {
317                 g_critical("%s", gnome_keyring_result_to_message(result));
318                 return EXIT_FAILURE;
319         }
320
321         return EXIT_SUCCESS;
322 }
323
324 /*
325  * Table with helper operation callbacks, used by generic
326  * credential helper main function.
327  */
328 static struct credential_operation const credential_helper_ops[] =
329 {
330         { "get",   keyring_get   },
331         { "store", keyring_store },
332         { "erase", keyring_erase },
333         CREDENTIAL_OP_END
334 };
335
336 /* ------------------ credential functions ------------------ */
337
338 static void credential_init(struct credential *c)
339 {
340         memset(c, 0, sizeof(*c));
341 }
342
343 static void credential_clear(struct credential *c)
344 {
345         g_free(c->protocol);
346         g_free(c->host);
347         g_free(c->path);
348         g_free(c->username);
349         gnome_keyring_memory_free(c->password);
350
351         credential_init(c);
352 }
353
354 static int credential_read(struct credential *c)
355 {
356         char    *buf;
357         size_t line_len;
358         char   *key;
359         char   *value;
360
361         key = buf = gnome_keyring_memory_alloc(1024);
362
363         while (fgets(buf, 1024, stdin))
364         {
365                 line_len = strlen(buf);
366
367                 if (line_len && buf[line_len-1] == '\n')
368                         buf[--line_len]='\0';
369
370                 if (!line_len)
371                         break;
372
373                 value = strchr(buf,'=');
374                 if (!value) {
375                         g_warning("invalid credential line: %s", key);
376                         gnome_keyring_memory_free(buf);
377                         return -1;
378                 }
379                 *value++ = '\0';
380
381                 if (!strcmp(key, "protocol")) {
382                         g_free(c->protocol);
383                         c->protocol = g_strdup(value);
384                 } else if (!strcmp(key, "host")) {
385                         g_free(c->host);
386                         c->host = g_strdup(value);
387                         value = strrchr(c->host,':');
388                         if (value) {
389                                 *value++ = '\0';
390                                 c->port = atoi(value);
391                         }
392                 } else if (!strcmp(key, "path")) {
393                         g_free(c->path);
394                         c->path = g_strdup(value);
395                 } else if (!strcmp(key, "username")) {
396                         g_free(c->username);
397                         c->username = g_strdup(value);
398                 } else if (!strcmp(key, "password")) {
399                         gnome_keyring_memory_free(c->password);
400                         c->password = gnome_keyring_memory_strdup(value);
401                         while (*value) *value++ = '\0';
402                 }
403                 /*
404                  * Ignore other lines; we don't know what they mean, but
405                  * this future-proofs us when later versions of git do
406                  * learn new lines, and the helpers are updated to match.
407                  */
408         }
409
410         gnome_keyring_memory_free(buf);
411
412         return 0;
413 }
414
415 static void credential_write_item(FILE *fp, const char *key, const char *value)
416 {
417         if (!value)
418                 return;
419         fprintf(fp, "%s=%s\n", key, value);
420 }
421
422 static void credential_write(const struct credential *c)
423 {
424         /* only write username/password, if set */
425         credential_write_item(stdout, "username", c->username);
426         credential_write_item(stdout, "password", c->password);
427 }
428
429 static void usage(const char *name)
430 {
431         struct credential_operation const *try_op = credential_helper_ops;
432         const char *basename = strrchr(name,'/');
433
434         basename = (basename) ? basename + 1 : name;
435         fprintf(stderr, "usage: %s <", basename);
436         while (try_op->name) {
437                 fprintf(stderr,"%s",(try_op++)->name);
438                 if (try_op->name)
439                         fprintf(stderr,"%s","|");
440         }
441         fprintf(stderr,"%s",">\n");
442 }
443
444 int main(int argc, char *argv[])
445 {
446         int ret = EXIT_SUCCESS;
447
448         struct credential_operation const *try_op = credential_helper_ops;
449         struct credential                  cred   = CREDENTIAL_INIT;
450
451         if (!argv[1]) {
452                 usage(argv[0]);
453                 exit(EXIT_FAILURE);
454         }
455
456         g_set_application_name("Git Credential Helper");
457
458         /* lookup operation callback */
459         while (try_op->name && strcmp(argv[1], try_op->name))
460                 try_op++;
461
462         /* unsupported operation given -- ignore silently */
463         if (!try_op->name || !try_op->op)
464                 goto out;
465
466         ret = credential_read(&cred);
467         if (ret)
468                 goto out;
469
470         /* perform credential operation */
471         ret = (*try_op->op)(&cred);
472
473         credential_write(&cred);
474
475 out:
476         credential_clear(&cred);
477         return ret;
478 }