1 /* libsecret - GLib wrapper for Secret Service
3 * Copyright 2012 Red Hat Inc.
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.
10 * See the included COPYING file for more information.
12 * Author: Stef Walter <stefw@gnome.org>
17 #include "libsecret/secret-item.h"
18 #include "libsecret/secret-password.h"
19 #include "libsecret/secret-service.h"
20 #include "libsecret/secret-value.h"
22 #include <glib/gi18n.h>
30 #define SECRET_ALIAS_PREFIX "/org/freedesktop/secrets/aliases/"
32 static gchar **attribute_args = NULL;
33 static gchar *store_label = NULL;
34 static gchar *store_collection = NULL;
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 },
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 },
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 },
61 typedef int (* SecretToolAction) (int argc, char *argv[]);
63 static void usage (void) G_GNUC_NORETURN;
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");
76 is_password_value (SecretValue *value)
78 const gchar *content_type;
82 content_type = secret_value_get_content_type (value);
83 if (content_type && g_str_equal (content_type, "text/plain"))
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);
95 attributes_from_arguments (gchar **args)
97 GHashTable *attributes;
99 if (args == NULL || args[0] == NULL) {
100 g_printerr ("%s: must specfy attribute and value pairs\n", g_get_prgname ());
104 attributes = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
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 ());
112 g_hash_table_insert (attributes, g_strdup (args[0]), g_strdup (args[1]));
120 secret_tool_action_clear (int argc,
123 GError *error = NULL;
124 GOptionContext *context;
125 SecretService *service;
126 GHashTable *attributes;
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);
135 g_option_context_free (context);
137 attributes = attributes_from_arguments (attribute_args);
138 g_strfreev (attribute_args);
140 service = secret_service_get_sync (SECRET_SERVICE_NONE, NULL, &error);
142 secret_service_clear_sync (service, NULL, attributes, NULL, &error);
144 g_object_unref (service);
145 g_hash_table_unref (attributes);
148 g_printerr ("%s: %s\n", g_get_prgname (), error->message);
156 write_password_data (SecretValue *value)
162 at = secret_value_get (value, &length);
165 r = write (1, at, length);
167 if (errno != EAGAIN && errno != EINTR) {
168 g_printerr ("%s: couldn't write password: %s\n",
169 g_get_prgname (), g_strerror (errno));
180 write_password_stdout (SecretValue *value)
182 if (!is_password_value (value)) {
183 g_printerr ("%s: secret does not contain a textual password\n", g_get_prgname ());
187 write_password_data (value);
189 /* Add a new line if we're writing out to a tty */
195 secret_tool_action_lookup (int argc,
198 GError *error = NULL;
199 GOptionContext *context;
200 SecretService *service;
201 GHashTable *attributes;
202 SecretValue *value = NULL;
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);
211 g_option_context_free (context);
213 attributes = attributes_from_arguments (attribute_args);
214 g_strfreev (attribute_args);
216 service = secret_service_get_sync (SECRET_SERVICE_NONE, NULL, &error);
218 value = secret_service_lookup_sync (service, NULL, attributes, NULL, &error);
220 g_object_unref (service);
221 g_hash_table_unref (attributes);
224 g_printerr ("%s: %s\n", g_get_prgname (), error->message);
231 write_password_stdout (value);
232 secret_value_unref (value);
237 read_password_stdin (void)
242 gsize remaining = 8192;
245 at = password = g_malloc0 (remaining + 1);
248 r = read (0, at, remaining);
252 if (errno != EAGAIN && errno != EINTR) {
253 g_printerr ("%s: couldn't read password: %s\n",
254 g_get_prgname (), g_strerror (errno));
258 /* TODO: This restriction is due purely to laziness. */
260 g_printerr ("%s: password is too long\n", g_get_prgname ());
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);
273 read_password_tty (void)
277 password = getpass ("Password: ");
278 return secret_value_new_full (password, -1, "text/plain",
279 (GDestroyNotify)secret_password_wipe);
283 secret_tool_action_store (int argc,
286 GError *error = NULL;
287 GOptionContext *context;
288 SecretService *service;
289 GHashTable *attributes;
291 gchar *collection = NULL;
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);
300 g_option_context_free (context);
302 if (store_label == NULL) {
303 g_printerr ("%s: must specify a label for the new item\n", g_get_prgname ());
307 attributes = attributes_from_arguments (attribute_args);
308 g_strfreev (attribute_args);
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);
315 collection = g_strconcat (SECRET_ALIAS_PREFIX, store_collection, NULL);
318 service = secret_service_get_sync (SECRET_SERVICE_NONE, NULL, &error);
321 value = read_password_tty ();
323 value = read_password_stdin ();
325 secret_service_store_sync (service, NULL, attributes, collection, store_label, value, NULL, &error);
326 secret_value_unref (value);
329 g_object_unref (service);
330 g_hash_table_unref (attributes);
331 g_free (store_label);
332 g_free (store_collection);
336 g_printerr ("%s: %s\n", g_get_prgname (), error->message);
344 print_item_when (const char *field,
351 value = g_strdup ("");
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);
358 g_print ("%s = %s\n", field, value);
363 print_item_details (SecretItem *item)
367 GHashTable *attributes;
373 path = g_dbus_proxy_get_object_path (G_DBUS_PROXY (item));
374 g_return_if_fail (path != NULL);
376 /* The item identifier */
377 part = strrchr (path, '/');
380 g_print ("[%s]\n", path);
383 value = secret_item_get_label (item);
384 g_print ("label = %s\n", value);
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);
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);
403 value = secret_item_get_schema_name (item);
404 g_print ("schema = %s\n", value);
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);
414 g_hash_table_unref (attributes);
418 secret_tool_action_search (int argc,
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;
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 },
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);
448 g_option_context_free (context);
450 attributes = attributes_from_arguments (attribute_args);
451 g_strfreev (attribute_args);
453 service = secret_service_get_sync (SECRET_SERVICE_NONE, NULL, &error);
455 flags = SECRET_SEARCH_LOAD_SECRETS;
457 flags |= SECRET_SEARCH_ALL;
459 flags |= SECRET_SEARCH_UNLOCK;
460 items = secret_service_search_sync (service, NULL, attributes, flags, NULL, &error);
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);
467 g_object_unref (service);
470 g_hash_table_unref (attributes);
473 g_printerr ("%s: %s\n", g_get_prgname (), error->message);
484 SecretToolAction action;
486 setlocale (LC_ALL, "");
489 bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
490 bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
491 textdomain (GETTEXT_PACKAGE);
494 #if !GLIB_CHECK_VERSION(2,35,0)
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;
514 return (action) (argc - 1, argv + 1);