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