14ce14b34d38a3ce917e2a88f199f6710bc913d9
[platform/upstream/libsecret.git] / tool / secret-tool.c
1 /* libsecret - GLib wrapper for Secret Service
2  *
3  * Copyright 2012 Red Hat Inc.
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU Lesser General Public License as published
7  * by the Free Software Foundation; either version 2.1 of the licence or (at
8  * your option) any later version.
9  *
10  * See the included COPYING file for more information.
11  *
12  * Author: Stef Walter <stefw@gnome.org>
13  */
14
15 #include "config.h"
16
17 #include "libsecret/secret-item.h"
18 #include "libsecret/secret-password.h"
19 #include "libsecret/secret-service.h"
20 #include "libsecret/secret-value.h"
21
22 #include <glib/gi18n.h>
23
24 #include <errno.h>
25 #include <locale.h>
26 #include <limits.h>
27 #include <stdlib.h>
28 #include <string.h>
29
30 #define SECRET_ALIAS_PREFIX "/org/freedesktop/secrets/aliases/"
31
32 static gchar **attribute_args = NULL;
33 static gchar *store_label = NULL;
34 static gchar *store_collection = NULL;
35
36 /* secret-tool store --label="blah" --collection="xxxx" name:xxxx name:yyyy */
37 static const GOptionEntry STORE_OPTIONS[] = {
38         { "label", 'l', 0, G_OPTION_ARG_STRING, &store_label,
39           N_("the label for the new stored item"), NULL },
40         { "collection", 'c', 0, G_OPTION_ARG_STRING, &store_collection,
41           N_("the collection in which to place the stored item"), NULL },
42         { G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_STRING_ARRAY, &attribute_args,
43           N_("attribute value pairs of item to lookup"), NULL },
44         { NULL }
45 };
46
47 /* secret-tool lookup name:xxxx yyyy:zzzz */
48 static const GOptionEntry LOOKUP_OPTIONS[] = {
49         { G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_STRING_ARRAY, &attribute_args,
50           N_("attribute value pairs of item to lookup"), NULL },
51         { NULL }
52 };
53
54 /* secret-tool clear name:xxxx yyyy:zzzz */
55 static const GOptionEntry CLEAR_OPTIONS[] = {
56         { G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_STRING_ARRAY, &attribute_args,
57           N_("attribute value pairs which match items to clear"), NULL },
58         { NULL }
59 };
60
61 typedef int       (* SecretToolAction)          (int argc, char *argv[]);
62
63 static void       usage                         (void) G_GNUC_NORETURN;
64
65 static void
66 usage (void)
67 {
68         g_printerr ("usage: secret-tool store --label='label' attribute value ...\n");
69         g_printerr ("       secret-tool lookup attribute value ...\n");
70         g_printerr ("       secret-tool clear attribute value ...\n");
71         g_printerr ("       secret-tool search [--all] [--details] attribute value ...\n");
72         exit (2);
73 }
74
75 static gboolean
76 is_password_value (SecretValue *value)
77 {
78         const gchar *content_type;
79         const gchar *data;
80         gsize length;
81
82         content_type = secret_value_get_content_type (value);
83         if (content_type && g_str_equal (content_type, "text/plain"))
84                 return TRUE;
85
86         data = secret_value_get (value, &length);
87         /* gnome-keyring-daemon used to return passwords like this, so support this, but validate */
88         if (!content_type || g_str_equal (content_type, "application/octet-stream"))
89                 return g_utf8_validate (data, length, NULL);
90
91         return FALSE;
92 }
93
94 static GHashTable *
95 attributes_from_arguments (gchar **args)
96 {
97         GHashTable *attributes;
98
99         if (args == NULL || args[0] == NULL) {
100                 g_printerr ("%s: must specfy attribute and value pairs\n", g_get_prgname ());
101                 usage ();
102         }
103
104         attributes = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
105
106         while (args[0] != NULL) {
107                 if (args[1] == NULL) {
108                         g_printerr ("%s: must specfy attributes and values in pairs\n", g_get_prgname ());
109                         usage ();
110                 }
111
112                 g_hash_table_insert (attributes, g_strdup (args[0]), g_strdup (args[1]));
113                 args += 2;
114         }
115
116         return attributes;
117 }
118
119 static int
120 secret_tool_action_clear (int argc,
121                           char *argv[])
122 {
123         GError *error = NULL;
124         GOptionContext *context;
125         SecretService *service;
126         GHashTable *attributes;
127
128         context = g_option_context_new ("attribute value ...");
129         g_option_context_add_main_entries (context, CLEAR_OPTIONS, GETTEXT_PACKAGE);
130         if (!g_option_context_parse (context, &argc, &argv, &error)) {
131                 g_printerr ("%s\n", error->message);
132                 usage();
133         }
134
135         g_option_context_free (context);
136
137         attributes = attributes_from_arguments (attribute_args);
138         g_strfreev (attribute_args);
139
140         service = secret_service_get_sync (SECRET_SERVICE_NONE, NULL, &error);
141         if (error == NULL)
142                 secret_service_clear_sync (service, NULL, attributes, NULL, &error);
143
144         g_object_unref (service);
145         g_hash_table_unref (attributes);
146
147         if (error != NULL) {
148                 g_printerr ("%s: %s\n", g_get_prgname (), error->message);
149                 return 1;
150         }
151
152         return 0;
153 }
154
155 static void
156 write_password_data (SecretValue *value)
157 {
158         const gchar *at;
159         gsize length;
160         int r;
161
162         at = secret_value_get (value, &length);
163
164         while (length > 0) {
165                 r = write (1, at, length);
166                 if (r == -1) {
167                         if (errno != EAGAIN && errno != EINTR) {
168                                 g_printerr ("%s: couldn't write password: %s\n",
169                                             g_get_prgname (), g_strerror (errno));
170                                 exit (1);
171                         }
172                 } else {
173                         at += r;
174                         length -= r;
175                 }
176         }
177 }
178
179 static void
180 write_password_stdout (SecretValue *value)
181 {
182         if (!is_password_value (value)) {
183                 g_printerr ("%s: secret does not contain a textual password\n", g_get_prgname ());
184                 exit (1);
185         }
186
187         write_password_data (value);
188
189         /* Add a new line if we're writing out to a tty */
190         if (isatty (1))
191                 write (1, "\n", 1);
192 }
193
194 static int
195 secret_tool_action_lookup (int argc,
196                            char *argv[])
197 {
198         GError *error = NULL;
199         GOptionContext *context;
200         SecretService *service;
201         GHashTable *attributes;
202         SecretValue *value = NULL;
203
204         context = g_option_context_new ("attribute value ...");
205         g_option_context_add_main_entries (context, LOOKUP_OPTIONS, GETTEXT_PACKAGE);
206         if (!g_option_context_parse (context, &argc, &argv, &error)) {
207                 g_printerr ("%s\n", error->message);
208                 usage();
209         }
210
211         g_option_context_free (context);
212
213         attributes = attributes_from_arguments (attribute_args);
214         g_strfreev (attribute_args);
215
216         service = secret_service_get_sync (SECRET_SERVICE_NONE, NULL, &error);
217         if (error == NULL)
218                 value = secret_service_lookup_sync (service, NULL, attributes, NULL, &error);
219
220         g_object_unref (service);
221         g_hash_table_unref (attributes);
222
223         if (error != NULL) {
224                 g_printerr ("%s: %s\n", g_get_prgname (), error->message);
225                 return 1;
226         }
227
228         if (value == NULL)
229                 return 1;
230
231         write_password_stdout (value);
232         secret_value_unref (value);
233         return 0;
234 }
235
236 static SecretValue *
237 read_password_stdin (void)
238 {
239         gchar *password;
240         gchar *at;
241         gsize length = 0;
242         gsize remaining = 8192;
243         int r;
244
245         at = password = g_malloc0 (remaining + 1);
246
247         for (;;) {
248                 r = read (0, at, remaining);
249                 if (r == 0) {
250                         break;
251                 } else if (r < 0) {
252                         if (errno != EAGAIN && errno != EINTR) {
253                                 g_printerr ("%s: couldn't read password: %s\n",
254                                             g_get_prgname (), g_strerror (errno));
255                                 exit (1);
256                         }
257                 } else {
258                         /* TODO: This restriction is due purely to laziness. */
259                         if (r == remaining)
260                                 g_printerr ("%s: password is too long\n", g_get_prgname ());
261                         at += r;
262                         remaining -= r;
263                         length += r;
264                 }
265         }
266
267         /* TODO: Verify that the password really is utf-8 text. */
268         return secret_value_new_full (password, length, "text/plain",
269                                       (GDestroyNotify)secret_password_free);
270 }
271
272 static SecretValue *
273 read_password_tty (void)
274 {
275         gchar *password;
276
277         password = getpass ("Password: ");
278         return secret_value_new_full (password, -1, "text/plain",
279                                       (GDestroyNotify)secret_password_wipe);
280 }
281
282 static int
283 secret_tool_action_store (int argc,
284                           char *argv[])
285 {
286         GError *error = NULL;
287         GOptionContext *context;
288         SecretService *service;
289         GHashTable *attributes;
290         SecretValue *value;
291         gchar *collection = NULL;
292
293         context = g_option_context_new ("attribute value ...");
294         g_option_context_add_main_entries (context, STORE_OPTIONS, GETTEXT_PACKAGE);
295         if (!g_option_context_parse (context, &argc, &argv, &error)) {
296                 g_printerr ("%s\n", error->message);
297                 usage();
298         }
299
300         g_option_context_free (context);
301
302         if (store_label == NULL) {
303                 g_printerr ("%s: must specify a label for the new item\n", g_get_prgname ());
304                 usage ();
305         }
306
307         attributes = attributes_from_arguments (attribute_args);
308         g_strfreev (attribute_args);
309
310         if (store_collection) {
311                 /* TODO: Verify that the collection is a valid path or path element */
312                 if (g_str_has_prefix (store_collection, "/"))
313                         collection = g_strdup (store_collection);
314                 else
315                         collection = g_strconcat (SECRET_ALIAS_PREFIX, store_collection, NULL);
316         }
317
318         service = secret_service_get_sync (SECRET_SERVICE_NONE, NULL, &error);
319         if (error == NULL) {
320                 if (isatty (0))
321                         value = read_password_tty ();
322                 else
323                         value = read_password_stdin ();
324
325                 secret_service_store_sync (service, NULL, attributes, collection, store_label, value, NULL, &error);
326                 secret_value_unref (value);
327         }
328
329         g_object_unref (service);
330         g_hash_table_unref (attributes);
331         g_free (store_label);
332         g_free (store_collection);
333         g_free (collection);
334
335         if (error != NULL) {
336                 g_printerr ("%s: %s\n", g_get_prgname (), error->message);
337                 return 1;
338         }
339
340         return 0;
341 }
342
343 static void
344 print_item_when (const char *field,
345                  guint64 when)
346 {
347         GDateTime *dt;
348         gchar *value;
349
350         if (!when) {
351                 value = g_strdup ("");
352         } else {
353                 dt = g_date_time_new_from_unix_utc (when);
354                 value = g_date_time_format (dt, "%Y-%m-%d %H:%M:%S");
355                 g_date_time_unref (dt);
356         }
357
358         g_print ("%s = %s\n", field, value);
359         g_free (value);
360 }
361
362 static void
363 print_item_details (SecretItem *item)
364 {
365         SecretValue *secret;
366         GHashTableIter iter;
367         GHashTable *attributes;
368         gchar *value, *key;
369         guint64 when;
370         const gchar *part;
371         const gchar *path;
372
373         path = g_dbus_proxy_get_object_path (G_DBUS_PROXY (item));
374         g_return_if_fail (path != NULL);
375
376         /* The item identifier */
377         part = strrchr (path, '/');
378         if (part == NULL)
379                 part = path;
380         g_print ("[%s]\n", path);
381
382         /* The label */
383         value = secret_item_get_label (item);
384         g_print ("label = %s\n", value);
385         g_free (value);
386
387         /* The secret value */
388         secret = secret_item_get_secret (item);
389         g_print ("secret = ");
390         if (secret != NULL) {
391                 write_password_data (secret);
392                 secret_value_unref (secret);
393         }
394         g_print ("\n");
395
396         /* The dates */
397         when = secret_item_get_created (item);
398         print_item_when ("created", when);
399         when = secret_item_get_modified (item);
400         print_item_when ("modified", when);
401
402         /* The schema */
403         value = secret_item_get_schema_name (item);
404         g_print ("schema = %s\n", value);
405         g_free (value);
406
407         /* The attributes */
408         attributes = secret_item_get_attributes (item);
409         g_hash_table_iter_init (&iter, attributes);
410         while (g_hash_table_iter_next (&iter, (void **)&key, (void **)&value)) {
411                 if (strcmp (key, "xdg:schema") != 0)
412                         g_printerr ("attribute.%s = %s\n", key, value);
413         }
414         g_hash_table_unref (attributes);
415 }
416
417 static int
418 secret_tool_action_search (int argc,
419                            char *argv[])
420 {
421         GError *error = NULL;
422         GOptionContext *context;
423         SecretService *service;
424         GHashTable *attributes;
425         SecretSearchFlags flags;
426         gboolean flag_all = FALSE;
427         gboolean flag_unlock = FALSE;
428         GList *items, *l;
429
430         /* secret-tool lookup name xxxx yyyy zzzz */
431         const GOptionEntry lookup_options[] = {
432                 { "all", 'a', 0, G_OPTION_ARG_NONE, &flag_all,
433                   N_("return all results, instead of just first one"), NULL },
434                 { "unlock", 'a', 0, G_OPTION_ARG_NONE, &flag_unlock,
435                   N_("unlock item results if necessary"), NULL },
436                 { G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_STRING_ARRAY, &attribute_args,
437                   N_("attribute value pairs of item to lookup"), NULL },
438                 { NULL }
439         };
440
441         context = g_option_context_new ("attribute value ...");
442         g_option_context_add_main_entries (context, lookup_options, GETTEXT_PACKAGE);
443         if (!g_option_context_parse (context, &argc, &argv, &error)) {
444                 g_printerr ("%s\n", error->message);
445                 usage();
446         }
447
448         g_option_context_free (context);
449
450         attributes = attributes_from_arguments (attribute_args);
451         g_strfreev (attribute_args);
452
453         service = secret_service_get_sync (SECRET_SERVICE_NONE, NULL, &error);
454         if (error == NULL) {
455                 flags = SECRET_SEARCH_LOAD_SECRETS;
456                 if (flag_all)
457                         flags |= SECRET_SEARCH_ALL;
458                 if (flag_unlock)
459                         flags |= SECRET_SEARCH_UNLOCK;
460                 items = secret_service_search_sync (service, NULL, attributes, flags, NULL, &error);
461                 if (error == NULL) {
462                         for (l = items; l != NULL; l = g_list_next (l))
463                                 print_item_details (l->data);
464                         g_list_free_full (items, g_object_unref);
465                 }
466
467                 g_object_unref (service);
468         }
469
470         g_hash_table_unref (attributes);
471
472         if (error != NULL) {
473                 g_printerr ("%s: %s\n", g_get_prgname (), error->message);
474                 return 1;
475         }
476
477         return 0;
478 }
479
480 int
481 main (int argc,
482       char *argv[])
483 {
484         SecretToolAction action;
485
486         setlocale (LC_ALL, "");
487
488 #ifdef ENABLE_NLS
489         bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
490         bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
491         textdomain (GETTEXT_PACKAGE);
492 #endif
493
494 #if !GLIB_CHECK_VERSION(2,35,0)
495         g_type_init ();
496 #endif
497
498         if (argc < 2)
499                 usage();
500
501         if (g_str_equal (argv[1], "store")) {
502                 action = secret_tool_action_store;
503         } else if (g_str_equal (argv[1], "lookup")) {
504                 action = secret_tool_action_lookup;
505         } else if (g_str_equal (argv[1], "clear")) {
506                 action = secret_tool_action_clear;
507         } else if (g_str_equal (argv[1], "search")) {
508                 action = secret_tool_action_search;
509         } else {
510                 usage ();
511         }
512
513         argv[1] = argv[0];
514         return (action) (argc - 1, argv + 1);
515 }