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 "secret-password.h"
18 #include "secret-service.h"
19 #include "secret-value.h"
21 #include <glib/gi18n.h>
27 #define SECRET_ALIAS_PREFIX "/org/freedesktop/secrets/aliases/"
29 static gchar **attribute_args = NULL;
30 static gchar *store_label = NULL;
31 static gchar *store_collection = NULL;
33 /* secret-tool store --label="blah" --collection="xxxx" name:xxxx name:yyyy */
34 static const GOptionEntry STORE_OPTIONS[] = {
35 { "label", 'l', 0, G_OPTION_ARG_STRING, &store_label,
36 N_("the label for the new stored item"), NULL },
37 { "collection", 'c', 0, G_OPTION_ARG_STRING, &store_collection,
38 N_("the collection in which to place the stored item"), NULL },
39 { G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_STRING_ARRAY, &attribute_args,
40 N_("attribute value pairs of item to lookup"), NULL },
44 /* secret-tool lookup name:xxxx yyyy:zzzz */
45 static const GOptionEntry LOOKUP_OPTIONS[] = {
46 { G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_STRING_ARRAY, &attribute_args,
47 N_("attribute value pairs of item to lookup"), NULL },
51 /* secret-tool remove name:xxxx yyyy:zzzz */
52 static const GOptionEntry REMOVE_OPTIONS[] = {
53 { G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_STRING_ARRAY, &attribute_args,
54 N_("attribute value pairs which match item to remove"), NULL },
58 typedef int (* SecretToolAction) (int argc, char *argv[]);
60 static void usage (void) G_GNUC_NORETURN;
65 g_printerr ("usage: secret-tool store --label='label' attribute value ...\n");
66 g_printerr (" secret-tool lookup attribute value ...\n");
67 g_printerr (" secret-tool remove attribute value ...\n");
72 is_password_value (SecretValue *value)
74 const gchar *content_type;
78 content_type = secret_value_get_content_type (value);
79 if (content_type && g_str_equal (content_type, "text/plain"))
82 data = secret_value_get (value, &length);
83 /* gnome-keyring-daemon used to return passwords like this, so support this, but validate */
84 if (!content_type || g_str_equal (content_type, "application/octet-stream"))
85 return g_utf8_validate (data, length, NULL);
91 attributes_from_arguments (gchar **args)
93 GHashTable *attributes;
95 if (args == NULL || args[0] == NULL) {
96 g_printerr ("%s: must specfy attribute and value pairs\n", g_get_prgname ());
100 attributes = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
102 while (args[0] != NULL) {
103 if (args[1] == NULL) {
104 g_printerr ("%s: must specfy attributes and values in pairs\n", g_get_prgname ());
108 g_hash_table_insert (attributes, g_strdup (args[0]), g_strdup (args[1]));
116 secret_tool_action_remove (int argc,
119 GError *error = NULL;
120 GOptionContext *context;
121 SecretService *service;
122 GHashTable *attributes;
124 context = g_option_context_new ("attribute value ...");
125 g_option_context_add_main_entries (context, REMOVE_OPTIONS, GETTEXT_PACKAGE);
126 if (!g_option_context_parse (context, &argc, &argv, &error)) {
127 g_printerr ("%s\n", error->message);
131 g_option_context_free (context);
133 attributes = attributes_from_arguments (attribute_args);
134 g_strfreev (attribute_args);
136 service = secret_service_get_sync (SECRET_SERVICE_NONE, NULL, &error);
138 secret_service_remove_sync (service, NULL, attributes, NULL, &error);
140 g_object_unref (service);
141 g_hash_table_unref (attributes);
144 g_printerr ("%s: %s\n", g_get_prgname (), error->message);
152 write_password_stdout (SecretValue *value)
158 if (!is_password_value (value)) {
159 g_printerr ("%s: secret does not contain a textual password\n", g_get_prgname ());
163 at = secret_value_get (value, &length);
166 r = write (1, at, length);
168 if (errno != EAGAIN && errno != EINTR) {
169 g_printerr ("%s: couldn't write password: %s\n",
170 g_get_prgname (), g_strerror (errno));
179 /* Add a new line if we're writing out to a tty */
185 secret_tool_action_lookup (int argc,
188 GError *error = NULL;
189 GOptionContext *context;
190 SecretService *service;
191 GHashTable *attributes;
192 SecretValue *value = NULL;
194 context = g_option_context_new ("attribute value ...");
195 g_option_context_add_main_entries (context, LOOKUP_OPTIONS, GETTEXT_PACKAGE);
196 if (!g_option_context_parse (context, &argc, &argv, &error)) {
197 g_printerr ("%s\n", error->message);
201 g_option_context_free (context);
203 attributes = attributes_from_arguments (attribute_args);
204 g_strfreev (attribute_args);
206 service = secret_service_get_sync (SECRET_SERVICE_NONE, NULL, &error);
208 value = secret_service_lookup_sync (service, NULL, attributes, NULL, &error);
210 g_object_unref (service);
211 g_hash_table_unref (attributes);
214 g_printerr ("%s: %s\n", g_get_prgname (), error->message);
221 write_password_stdout (value);
222 secret_value_unref (value);
227 read_password_stdin (void)
232 gsize remaining = 8192;
235 at = password = g_malloc0 (remaining + 1);
238 r = read (0, at, remaining);
242 if (errno != EAGAIN && errno != EINTR) {
243 g_printerr ("%s: couldn't read password: %s\n",
244 g_get_prgname (), g_strerror (errno));
248 /* TODO: This restriction is due purely to laziness. */
250 g_printerr ("%s: password is too long\n", g_get_prgname ());
257 /* TODO: Verify that the password really is utf-8 text. */
258 return secret_value_new_full (password, length, "text/plain",
259 (GDestroyNotify)secret_password_free);
263 read_password_tty (void)
267 password = getpass ("Password: ");
268 return secret_value_new_full (password, -1, "text/plain",
269 (GDestroyNotify)secret_password_clear);
273 secret_tool_action_store (int argc,
276 GError *error = NULL;
277 GOptionContext *context;
278 SecretService *service;
279 GHashTable *attributes;
281 gchar *collection = NULL;
283 context = g_option_context_new ("attribute value ...");
284 g_option_context_add_main_entries (context, STORE_OPTIONS, GETTEXT_PACKAGE);
285 if (!g_option_context_parse (context, &argc, &argv, &error)) {
286 g_printerr ("%s\n", error->message);
290 g_option_context_free (context);
292 if (store_label == NULL) {
293 g_printerr ("%s: must specify a label for the new item\n", g_get_prgname ());
297 attributes = attributes_from_arguments (attribute_args);
298 g_strfreev (attribute_args);
300 if (store_collection) {
301 /* TODO: Verify that the collection is a valid path or path element */
302 if (g_str_has_prefix (store_collection, "/"))
303 collection = g_strdup (store_collection);
305 collection = g_strconcat (SECRET_ALIAS_PREFIX, store_collection, NULL);
308 service = secret_service_get_sync (SECRET_SERVICE_NONE, NULL, &error);
311 value = read_password_tty ();
313 value = read_password_stdin ();
315 secret_service_store_sync (service, NULL, attributes, collection, store_label, value, NULL, &error);
316 secret_value_unref (value);
319 g_object_unref (service);
320 g_hash_table_unref (attributes);
321 g_free (store_label);
322 g_free (store_collection);
326 g_printerr ("%s: %s\n", g_get_prgname (), error->message);
337 SecretToolAction action;
344 if (g_str_equal (argv[1], "store")) {
345 action = secret_tool_action_store;
346 } else if (g_str_equal (argv[1], "lookup")) {
347 action = secret_tool_action_lookup;
348 } else if (g_str_equal (argv[1], "remove")) {
349 action = secret_tool_action_remove;
355 return (action) (argc - 1, argv + 1);