contrib/git-credential-gnome-keyring.c: use secure memory functions for passwds
[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 <stdarg.h>
29 #include <stdlib.h>
30 #include <errno.h>
31 #include <glib.h>
32 #include <gnome-keyring.h>
33 #include <gnome-keyring-memory.h>
34
35 /*
36  * This credential struct and API is simplified from git's credential.{h,c}
37  */
38 struct credential
39 {
40         char          *protocol;
41         char          *host;
42         unsigned short port;
43         char          *path;
44         char          *username;
45         char          *password;
46 };
47
48 #define CREDENTIAL_INIT \
49   { NULL,NULL,0,NULL,NULL,NULL }
50
51 typedef int (*credential_op_cb)(struct credential*);
52
53 struct credential_operation
54 {
55         char             *name;
56         credential_op_cb op;
57 };
58
59 #define CREDENTIAL_OP_END \
60   { NULL,NULL }
61
62 /* ---------------- common helper functions ----------------- */
63
64 static inline void warning(const char *fmt, ...)
65 {
66         va_list ap;
67
68         va_start(ap, fmt);
69         fprintf(stderr, "warning: ");
70         vfprintf(stderr, fmt, ap);
71         fprintf(stderr, "\n" );
72         va_end(ap);
73 }
74
75 static inline void error(const char *fmt, ...)
76 {
77         va_list ap;
78
79         va_start(ap, fmt);
80         fprintf(stderr, "error: ");
81         vfprintf(stderr, fmt, ap);
82         fprintf(stderr, "\n" );
83         va_end(ap);
84 }
85
86 static inline void die_errno(int err)
87 {
88         error("%s", strerror(err));
89         exit(EXIT_FAILURE);
90 }
91
92 static inline char *xstrdup(const char *str)
93 {
94         char *ret = strdup(str);
95         if (!ret)
96                 die_errno(errno);
97
98         return ret;
99 }
100
101 /* ----------------- GNOME Keyring functions ----------------- */
102
103 /* create a special keyring option string, if path is given */
104 static char* keyring_object(struct credential *c)
105 {
106         if (!c->path)
107                 return NULL;
108
109         if (c->port)
110                 return g_strdup_printf("%s:%hd/%s", c->host, c->port, c->path);
111
112         return g_strdup_printf("%s/%s", c->host, c->path);
113 }
114
115 static int keyring_get(struct credential *c)
116 {
117         char* object = NULL;
118         GList *entries;
119         GnomeKeyringNetworkPasswordData *password_data;
120         GnomeKeyringResult result;
121
122         if (!c->protocol || !(c->host || c->path))
123                 return EXIT_FAILURE;
124
125         object = keyring_object(c);
126
127         result = gnome_keyring_find_network_password_sync(
128                                 c->username,
129                                 NULL /* domain */,
130                                 c->host,
131                                 object,
132                                 c->protocol,
133                                 NULL /* authtype */,
134                                 c->port,
135                                 &entries);
136
137         free(object);
138
139         if (result == GNOME_KEYRING_RESULT_NO_MATCH)
140                 return EXIT_SUCCESS;
141
142         if (result == GNOME_KEYRING_RESULT_CANCELLED)
143                 return EXIT_SUCCESS;
144
145         if (result != GNOME_KEYRING_RESULT_OK) {
146                 error("%s",gnome_keyring_result_to_message(result));
147                 return EXIT_FAILURE;
148         }
149
150         /* pick the first one from the list */
151         password_data = (GnomeKeyringNetworkPasswordData *) entries->data;
152
153         gnome_keyring_memory_free(c->password);
154         c->password = gnome_keyring_memory_strdup(password_data->password);
155
156         if (!c->username)
157                 c->username = xstrdup(password_data->user);
158
159         gnome_keyring_network_password_list_free(entries);
160
161         return EXIT_SUCCESS;
162 }
163
164
165 static int keyring_store(struct credential *c)
166 {
167         guint32 item_id;
168         char  *object = NULL;
169
170         /*
171          * Sanity check that what we are storing is actually sensible.
172          * In particular, we can't make a URL without a protocol field.
173          * Without either a host or pathname (depending on the scheme),
174          * we have no primary key. And without a username and password,
175          * we are not actually storing a credential.
176          */
177         if (!c->protocol || !(c->host || c->path) ||
178             !c->username || !c->password)
179                 return EXIT_FAILURE;
180
181         object = keyring_object(c);
182
183         gnome_keyring_set_network_password_sync(
184                                 GNOME_KEYRING_DEFAULT,
185                                 c->username,
186                                 NULL /* domain */,
187                                 c->host,
188                                 object,
189                                 c->protocol,
190                                 NULL /* authtype */,
191                                 c->port,
192                                 c->password,
193                                 &item_id);
194
195         free(object);
196         return EXIT_SUCCESS;
197 }
198
199 static int keyring_erase(struct credential *c)
200 {
201         char  *object = NULL;
202         GList *entries;
203         GnomeKeyringNetworkPasswordData *password_data;
204         GnomeKeyringResult result;
205
206         /*
207          * Sanity check that we actually have something to match
208          * against. The input we get is a restrictive pattern,
209          * so technically a blank credential means "erase everything".
210          * But it is too easy to accidentally send this, since it is equivalent
211          * to empty input. So explicitly disallow it, and require that the
212          * pattern have some actual content to match.
213          */
214         if (!c->protocol && !c->host && !c->path && !c->username)
215                 return EXIT_FAILURE;
216
217         object = keyring_object(c);
218
219         result = gnome_keyring_find_network_password_sync(
220                                 c->username,
221                                 NULL /* domain */,
222                                 c->host,
223                                 object,
224                                 c->protocol,
225                                 NULL /* authtype */,
226                                 c->port,
227                                 &entries);
228
229         free(object);
230
231         if (result == GNOME_KEYRING_RESULT_NO_MATCH)
232                 return EXIT_SUCCESS;
233
234         if (result == GNOME_KEYRING_RESULT_CANCELLED)
235                 return EXIT_SUCCESS;
236
237         if (result != GNOME_KEYRING_RESULT_OK)
238         {
239                 error("%s",gnome_keyring_result_to_message(result));
240                 return EXIT_FAILURE;
241         }
242
243         /* pick the first one from the list (delete all matches?) */
244         password_data = (GnomeKeyringNetworkPasswordData *) entries->data;
245
246         result = gnome_keyring_item_delete_sync(
247                 password_data->keyring, password_data->item_id);
248
249         gnome_keyring_network_password_list_free(entries);
250
251         if (result != GNOME_KEYRING_RESULT_OK)
252         {
253                 error("%s",gnome_keyring_result_to_message(result));
254                 return EXIT_FAILURE;
255         }
256
257         return EXIT_SUCCESS;
258 }
259
260 /*
261  * Table with helper operation callbacks, used by generic
262  * credential helper main function.
263  */
264 static struct credential_operation const credential_helper_ops[] =
265 {
266         { "get",   keyring_get   },
267         { "store", keyring_store },
268         { "erase", keyring_erase },
269         CREDENTIAL_OP_END
270 };
271
272 /* ------------------ credential functions ------------------ */
273
274 static void credential_init(struct credential *c)
275 {
276         memset(c, 0, sizeof(*c));
277 }
278
279 static void credential_clear(struct credential *c)
280 {
281         free(c->protocol);
282         free(c->host);
283         free(c->path);
284         free(c->username);
285         gnome_keyring_memory_free(c->password);
286
287         credential_init(c);
288 }
289
290 static int credential_read(struct credential *c)
291 {
292         char    buf[1024];
293         size_t line_len;
294         char   *key      = buf;
295         char   *value;
296
297         while (fgets(buf, sizeof(buf), stdin))
298         {
299                 line_len = strlen(buf);
300
301                 if (line_len && buf[line_len-1] == '\n')
302                         buf[--line_len]='\0';
303
304                 if (!line_len)
305                         break;
306
307                 value = strchr(buf,'=');
308                 if (!value) {
309                         warning("invalid credential line: %s", key);
310                         return -1;
311                 }
312                 *value++ = '\0';
313
314                 if (!strcmp(key, "protocol")) {
315                         free(c->protocol);
316                         c->protocol = xstrdup(value);
317                 } else if (!strcmp(key, "host")) {
318                         free(c->host);
319                         c->host = xstrdup(value);
320                         value = strrchr(c->host,':');
321                         if (value) {
322                                 *value++ = '\0';
323                                 c->port = atoi(value);
324                         }
325                 } else if (!strcmp(key, "path")) {
326                         free(c->path);
327                         c->path = xstrdup(value);
328                 } else if (!strcmp(key, "username")) {
329                         free(c->username);
330                         c->username = xstrdup(value);
331                 } else if (!strcmp(key, "password")) {
332                         gnome_keyring_memory_free(c->password);
333                         c->password = gnome_keyring_memory_strdup(value);
334                         while (*value) *value++ = '\0';
335                 }
336                 /*
337                  * Ignore other lines; we don't know what they mean, but
338                  * this future-proofs us when later versions of git do
339                  * learn new lines, and the helpers are updated to match.
340                  */
341         }
342         return 0;
343 }
344
345 static void credential_write_item(FILE *fp, const char *key, const char *value)
346 {
347         if (!value)
348                 return;
349         fprintf(fp, "%s=%s\n", key, value);
350 }
351
352 static void credential_write(const struct credential *c)
353 {
354         /* only write username/password, if set */
355         credential_write_item(stdout, "username", c->username);
356         credential_write_item(stdout, "password", c->password);
357 }
358
359 static void usage(const char *name)
360 {
361         struct credential_operation const *try_op = credential_helper_ops;
362         const char *basename = strrchr(name,'/');
363
364         basename = (basename) ? basename + 1 : name;
365         fprintf(stderr, "usage: %s <", basename);
366         while (try_op->name) {
367                 fprintf(stderr,"%s",(try_op++)->name);
368                 if (try_op->name)
369                         fprintf(stderr,"%s","|");
370         }
371         fprintf(stderr,"%s",">\n");
372 }
373
374 int main(int argc, char *argv[])
375 {
376         int ret = EXIT_SUCCESS;
377
378         struct credential_operation const *try_op = credential_helper_ops;
379         struct credential                  cred   = CREDENTIAL_INIT;
380
381         if (!argv[1]) {
382                 usage(argv[0]);
383                 exit(EXIT_FAILURE);
384         }
385
386         g_set_application_name("Git Credential Helper");
387
388         /* lookup operation callback */
389         while (try_op->name && strcmp(argv[1], try_op->name))
390                 try_op++;
391
392         /* unsupported operation given -- ignore silently */
393         if (!try_op->name || !try_op->op)
394                 goto out;
395
396         ret = credential_read(&cred);
397         if (ret)
398                 goto out;
399
400         /* perform credential operation */
401         ret = (*try_op->op)(&cred);
402
403         credential_write(&cred);
404
405 out:
406         credential_clear(&cred);
407         return ret;
408 }