From e5ec4eda9b553121e1ce1262a37939ff3a006731 Mon Sep 17 00:00:00 2001 From: Sam Hartman Date: Mon, 26 Nov 2018 13:08:32 -0500 Subject: [PATCH] Initial implementation of libsecret keystore Split out deserialization of ID cards into code that can be shared between the gnome and libsecret implementations. Use that code for libsecret. --- .gitignore | 4 +- src/moonshot-keyring-store-base.vala | 89 ++++++++++++++++++++- src/moonshot-keyring-store-secret.vala | 136 +++++++++++++++++++++++++++++++++ 3 files changed, 224 insertions(+), 5 deletions(-) create mode 100644 src/moonshot-keyring-store-secret.vala diff --git a/.gitignore b/.gitignore index fbc5605..f8b5d83 100755 --- a/.gitignore +++ b/.gitignore @@ -39,7 +39,9 @@ src/moonshot-identity-request.c src/moonshot-identity-management-view.c src/moonshot-identity-manager-app.c src/moonshot-identities-manager.c -src/moonshot-keyring-store.c +src/moonshot-keyring-store-base.c +src/moonshot-keyring-store-gnome.c +src/moonshot-keyring-store-secret.c src/moonshot-local-flat-file-store.c src/moonshot-logger.c src/moonshot-password-dialog.c diff --git a/src/moonshot-keyring-store-base.vala b/src/moonshot-keyring-store-base.vala index 4aa6494..d23764d 100644 --- a/src/moonshot-keyring-store-base.vala +++ b/src/moonshot-keyring-store-base.vala @@ -32,12 +32,88 @@ using Gee; #if GNOME_KEYRING || LIBSECRET_KEYRING + public abstract class KeyringStoreBase : Object, IIdentityCardStore { protected static MoonshotLogger logger = get_logger("KeyringStore"); protected LinkedList id_card_list; - protected const string keyring_store_attribute = "Moonshot"; - protected const string keyring_store_version = "1.0"; + internal const string keyring_store_attribute = "Moonshot"; + internal const string keyring_store_version = "1.0"; + + /* + * This class is directly useful for the libsecret implementation. + * However, we convert the gnome keyring attributes into a HashTable + * so we can share the serialization code between the two + * implementations. This ends up decreasing complexity even of the + * gnome keyring code + */ + protected class Attributes: GLib.HashTable { + public Attributes() { + base.full(GLib.str_hash, GLib.str_equal, GLib.g_free, GLib.g_free); + } + + } + + protected static Attributes match_attributes; + + protected IdCard deserialize(GLib.HashTable attrs, string? secret) + { + IdCard id_card = new IdCard(); + unowned string store_password = attrs["StorePassword"]; + unowned string ca_cert = attrs["CA-Cert"] ?? ""; + unowned string server_cert = attrs["Server-Cert"] ?? ""; + unowned string subject = attrs["Subject"] ?? ""; + unowned string subject_alt = attrs["Subject-Alt"] ?? ""; + unowned string ta_datetime_added = attrs["TA_DateTime_Added"]; + + id_card.issuer = attrs["Issuer"]; + id_card.username = attrs["Username"]; + id_card.display_name = attrs["DisplayName"]; + unowned string services = attrs["Services"]; + if ((services != null) && services != "") { + id_card.update_services(services.split(";")); + } + var ta = new TrustAnchor(ca_cert, server_cert, subject, subject_alt); + if (ta_datetime_added != null) { + ta.set_datetime_added(ta_datetime_added); + } + id_card.set_trust_anchor_from_store(ta); + + unowned string rules_pattern_all = attrs["Rules-Pattern"]; + unowned string rules_always_confirm_all = attrs["Rules-AlwaysConfirm"]; + if ((rules_pattern_all != null) && (rules_always_confirm_all != null)) { + string[] rules_patterns = rules_pattern_all.split(";"); + string[] rules_always_confirm = rules_always_confirm_all.split(";"); + if (rules_patterns.length == rules_always_confirm.length) { + Rule[] rules = new Rule[rules_patterns.length]; + for (int i = 0; i < rules_patterns.length; i++) { + rules[i].pattern = (owned) rules_patterns[i]; + rules[i].always_confirm = (owned) rules_always_confirm[i]; + } + id_card.rules = rules; + } + } + + if (store_password != null) + id_card.store_password = (store_password == "yes"); + else + id_card.store_password = ((secret != null) && (secret != "")); + + if (id_card.store_password) + id_card.password = secret; + else + id_card.password = null; + + + + + return id_card; + } + + class construct { + match_attributes = new Attributes(); + match_attributes.insert(keyring_store_attribute, keyring_store_version); + } public void add_card(IdCard card) { logger.trace("add_card: Adding card '%s' with services: '%s'" @@ -80,14 +156,19 @@ public abstract class KeyringStoreBase : Object, IIdentityCardStore { } protected abstract void clear_keyring(); - protected abstract void load_id_cards(); + protected abstract void load_id_cards() throws GLib.Error; internal abstract void store_id_cards(); public KeyringStoreBase() { id_card_list = new LinkedList(); - load_id_cards(); + try { + load_id_cards(); + } catch( GLib.Error e) { + stdout.printf("Unable to load ID cards: %s\n", e.message); + } + } } diff --git a/src/moonshot-keyring-store-secret.vala b/src/moonshot-keyring-store-secret.vala new file mode 100644 index 0000000..6836393 --- /dev/null +++ b/src/moonshot-keyring-store-secret.vala @@ -0,0 +1,136 @@ +/* +* Copyright (C) 2018 Sam Hartman +* Copyright (c) 2011-2016, JANET(UK) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of JANET(UK) nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. +*/ + +#if LIBSECRET_KEYRING + +using Secret; + +public class KeyringStore : KeyringStoreBase { + /* + * We choose to remain compatible with the way we stored secrets + * using libgnomekeyring. As a result, we cannot use our own schema + * identifier. Using our own schema might get us a nice icon in + * seahorse, but would not save much code. + */ + private static Schema schema = new Schema("org.freedesktop.Secret.Generic", SchemaFlags.NONE); + private static Collection secret_collection; + + + /* clear all keyring-stored ids (in preparation to store current list) */ + protected override void clear_keyring() { + GLib.List items; + try { + items = secret_collection.search_sync(schema, match_attributes, + SearchFlags.NONE); + } catch (GLib.Error e) { + stdout.printf("Failed to find items to delete: %s\n", e.message); + return; + } + foreach(unowned Item entry in items) { + try { + bool res = entry.delete_sync(); + if (!res) { + stdout.printf("Failed to delete item: %s\n", entry.get_label()); + } + } catch (GLib.Error e) { + stdout.printf("Error deleting item: %s\n", e.message); + } + } + } + + protected override void load_id_cards() throws GLib.Error { + id_card_list.clear(); + + GLib.List items = secret_collection.search_sync( + schema, match_attributes, + SearchFlags.UNLOCK|SearchFlags.LOAD_SECRETS); + foreach(unowned Item entry in items) { + var secret = entry.get_secret(); + string secret_text = null; + if (secret != null) + secret_text = secret.get_text(); + var id_card = deserialize(entry.attributes, secret_text); + id_card_list.add(id_card); + } + } + + internal override void store_id_cards() { + logger.trace("store_id_cards"); + clear_keyring(); + foreach (IdCard id_card in this.id_card_list) { + try { + /* workaround for Centos vala array property bug: use temp array */ + var rules = id_card.rules; + string[] rules_patterns = new string[rules.length]; + string[] rules_always_conf = new string[rules.length]; + + for (int i = 0; i < rules.length; i++) { + rules_patterns[i] = rules[i].pattern; + rules_always_conf[i] = rules[i].always_confirm; + } + string patterns = string.joinv(";", rules_patterns); + string always_conf = string.joinv(";", rules_always_conf); + string services = id_card.get_services_string(";"); + KeyringStoreBase.Attributes attributes = new KeyringStoreBase.Attributes(); + attributes.insert(keyring_store_attribute, keyring_store_version); + attributes.insert("Issuer", id_card.issuer); + attributes.insert("Username", id_card.username); + attributes.insert("DisplayName", id_card.display_name); + attributes.insert("Services", services); + attributes.insert("Rules-Pattern", patterns); + attributes.insert("Rules-AlwaysConfirm", always_conf); + attributes.insert("CA-Cert", id_card.trust_anchor.ca_cert); + attributes.insert("Server-Cert", id_card.trust_anchor.server_cert); + attributes.insert("Subject", id_card.trust_anchor.subject); + attributes.insert("Subject-Alt", id_card.trust_anchor.subject_alt); + attributes.insert("TA_DateTime_Added", id_card.trust_anchor.datetime_added); + attributes.insert("StorePassword", id_card.store_password ? "yes" : "no"); + + password_storev_sync(schema, attributes, null, id_card.display_name, + id_card.store_password?id_card.password: ""); + } catch(GLib.Error e) { + logger.error(@"Unable to store $(id_card.display_name): $(e.message)\n"); + } + + } + try { + load_id_cards(); + } catch (GLib.Error e) { + logger.error(@"Unable to load ID Cards: $(e.message)\n"); + } + + } + +} + +#endif -- 2.1.4