/* GIO - GLib Input, Output and Streaming Library * * Copyright (C) 2008 Carlos Garcia Campos * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General * Public License along with this library; if not, write to the * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. * * Author: Carlos Garcia Campos */ #include #define SECRET_API_SUBJECT_TO_CHANGE 1 #ifdef HAVE_KEYRING #include #endif #include "gvfskeyring.h" gboolean g_vfs_keyring_is_available (void) { #ifdef HAVE_KEYRING return TRUE; #else return FALSE; #endif } #ifdef HAVE_KEYRING static void insert_string (const gchar *key, const gchar *value, GHashTable **attributes) { if (*attributes == NULL) return; if (!g_utf8_validate (value, -1, NULL)) { g_warning ("Non-utf8 value for key %s\n", key); g_hash_table_unref (*attributes); *attributes = NULL; } g_hash_table_insert (*attributes, g_strdup (key), g_strdup (value)); } static void insert_int (const gchar *key, gint value, GHashTable **attributes) { if (*attributes == NULL) return; g_hash_table_insert (*attributes, g_strdup (key), g_strdup_printf ("%d", value)); } static GHashTable * build_network_attributes (const gchar *username, const gchar *host, const gchar *domain, const gchar *protocol, const gchar *object, const gchar *authtype, guint32 port) { GHashTable *attributes; attributes = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); if (username) insert_string ("user", username, &attributes); if (host) insert_string ("server", host, &attributes); if (domain) insert_string ("domain", domain, &attributes); if (protocol) insert_string ("protocol", protocol, &attributes); if (object) insert_string ("object", object, &attributes); if (authtype) insert_string ("authtype", authtype, &attributes); if (port != 0) insert_int ("port", (gint)port, &attributes); return attributes; } static gchar * build_network_label (const gchar *user, const gchar *server, const gchar *object, guint32 port) { GString *s; gchar *name; if (server != NULL) { s = g_string_new (NULL); if (user != NULL) { g_string_append_uri_escaped (s, user, G_URI_RESERVED_CHARS_ALLOWED_IN_USERINFO, TRUE); g_string_append (s, "@"); } g_string_append (s, server); if (port != 0) g_string_append_printf (s, ":%d", port); if (object != NULL) g_string_append_printf (s, "/%s", object); name = g_string_free (s, FALSE); } else { name = g_strdup ("network password"); } return name; } static gint compare_specificity (gconstpointer a, gconstpointer b) { GHashTable *attributes_a, *attributes_b; SecretItem *item_a, *item_b; int res; item_a = SECRET_ITEM (a); attributes_a = secret_item_get_attributes (item_a); item_b = SECRET_ITEM (b); attributes_b = secret_item_get_attributes (item_b); res = g_hash_table_size (attributes_a) - g_hash_table_size (attributes_b); /* Prefer the most recent item if they are equal in specificity. */ if (res == 0) res = secret_item_get_modified (item_b) - secret_item_get_modified (item_a); g_hash_table_unref (attributes_a); g_hash_table_unref (attributes_b); return res; } #endif /* HAVE_KEYRING */ gboolean g_vfs_keyring_lookup_password (const gchar *username, const gchar *host, const gchar *domain, const gchar *protocol, const gchar *object, const gchar *authtype, guint32 port, gchar **username_out, gchar **domain_out, gchar **password_out) { #ifdef HAVE_KEYRING GHashTable *attributes; SecretItem *item; SecretValue *secret; GList *plist; GError *error = NULL; attributes = build_network_attributes (username, host, domain, protocol, object, authtype, port); plist = secret_service_search_sync (NULL, SECRET_SCHEMA_COMPAT_NETWORK, attributes, SECRET_SEARCH_UNLOCK | SECRET_SEARCH_LOAD_SECRETS | SECRET_SEARCH_ALL, NULL, &error); g_hash_table_unref (attributes); if (error != NULL) { g_error_free (error); return FALSE; } if (plist == NULL) return FALSE; /* We want the least specific result, so we sort the return values. For instance, given both items for ftp://host:port and ftp://host in the keyring we always want to use the ftp://host one for i.e. ftp://host/some/path. */ plist = g_list_sort (plist, compare_specificity); item = SECRET_ITEM (plist->data); secret = secret_item_get_secret (item); attributes = secret_item_get_attributes (item); g_list_free_full (plist, g_object_unref); if (secret == NULL) { if (attributes) g_hash_table_unref (attributes); return FALSE; } *password_out = g_strdup (secret_value_get (secret, NULL)); secret_value_unref (secret); if (username_out) *username_out = g_strdup (g_hash_table_lookup (attributes, "user")); if (domain_out) *domain_out = g_strdup (g_hash_table_lookup (attributes, "domain")); g_hash_table_unref (attributes); return TRUE; #else return FALSE; #endif /* HAVE_KEYRING */ } gboolean g_vfs_keyring_save_password (const gchar *username, const gchar *host, const gchar *domain, const gchar *protocol, const gchar *object, const gchar *authtype, guint32 port, const gchar *password, GPasswordSave flags) { #ifdef HAVE_KEYRING const gchar *keyring; GHashTable *attributes; gchar *label; gboolean ret; if (flags == G_PASSWORD_SAVE_NEVER) return FALSE; keyring = (flags == G_PASSWORD_SAVE_FOR_SESSION) ? SECRET_COLLECTION_SESSION : SECRET_COLLECTION_DEFAULT; label = build_network_label (username, host, object, port); attributes = build_network_attributes (username, host, domain, protocol, object, authtype, port); ret = secret_password_storev_sync (SECRET_SCHEMA_COMPAT_NETWORK, attributes, keyring, label, password, NULL, NULL); g_free (label); g_hash_table_unref (attributes); return ret; #else return FALSE; #endif /* HAVE_KEYRING */ }