From 3483be3d73af11f2bf7ce3318514c4fce50bebf4 Mon Sep 17 00:00:00 2001 From: Dan Breslau Date: Fri, 19 Aug 2016 13:16:24 -0400 Subject: [PATCH] First pass at supporting date/time added for Trust Anchors --- src/moonshot-id.vala | 56 ++++++++++++++++------ src/moonshot-idcard-store.vala | 5 +- src/moonshot-identities-manager.vala | 17 +++++++ src/moonshot-identity-dialog.vala | 3 +- src/moonshot-identity-management-view.vala | 2 +- src/moonshot-keyring-store.vala | 11 ++++- src/moonshot-local-flat-file-store.vala | 34 +++++++++---- src/moonshot-provisioning-common.vala | 76 +++++++++++++----------------- src/moonshot-server.vala | 9 +++- src/moonshot-settings.vala | 47 ++++++++++++++---- 10 files changed, 176 insertions(+), 84 deletions(-) diff --git a/src/moonshot-id.vala b/src/moonshot-id.vala index b1f0860..dcf4bc9 100644 --- a/src/moonshot-id.vala +++ b/src/moonshot-id.vala @@ -51,23 +51,25 @@ public class TrustAnchor : Object private string _subject = ""; private string _subject_alt = ""; private string _server_cert = ""; - + private string _datetime_added = ""; public bool user_verified = false; + private static string fixup (string s) { + return (s == null ? "" : s.strip()); + } + public TrustAnchor(string ca_cert, string server_cert, string subject, string subject_alt, bool user_verified) { - _ca_cert = ca_cert; - _server_cert = server_cert; - _subject = subject; - _subject_alt = subject_alt; + _ca_cert = fixup(ca_cert); + _server_cert = fixup(server_cert); + _subject = fixup(subject); + _subject_alt = fixup(subject_alt); this.user_verified = user_verified; + + // If we're reading from store, this will be overridden (see set_datetime_added) + _datetime_added = ""; } public TrustAnchor.empty() { - _ca_cert = ""; - _server_cert = ""; - _subject = ""; - _subject_alt = ""; - this.user_verified = false; } @@ -96,6 +98,12 @@ public class TrustAnchor : Object } } + public string datetime_added { + get { + return _datetime_added; + } + } + public bool is_empty() { return ca_cert == "" && subject == "" && subject_alt == "" && server_cert == ""; } @@ -104,18 +112,36 @@ public class TrustAnchor : Object return server_cert == "" ? TrustAnchorType.CA_CERT : TrustAnchorType.SERVER_CERT; } + internal void set_datetime_added(string datetime) { + _datetime_added = fixup(datetime); + } + + internal static string format_datetime_now() { + DateTime now = new DateTime.now_utc(); + string dt = now.format("%b %d %T %Y %Z"); + return dt; + } + public int Compare(TrustAnchor other) { - if (this.ca_cert != other.ca_cert) + if (this.ca_cert != other.ca_cert) { return 1; - if (this.subject != other.subject) + } + if (this.subject != other.subject) { return 1; - if (this.subject_alt != other.subject_alt) + } + if (this.subject_alt != other.subject_alt) { return 1; - if (this.server_cert != other.server_cert) + } + if (this.server_cert != other.server_cert) { return 1; - if (this.user_verified != other.user_verified) + } + if (this.user_verified != other.user_verified) { return 1; + } + // if (!is_empty() && this.datetime_added != other.datetime_added) { + // return 1; + // } return 0; } diff --git a/src/moonshot-idcard-store.vala b/src/moonshot-idcard-store.vala index 3d6c863..b9c9368 100644 --- a/src/moonshot-idcard-store.vala +++ b/src/moonshot-idcard-store.vala @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011-2014, JANET(UK) + * Copyright (c) 2011-2016, JANET(UK) * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -42,5 +42,8 @@ public interface IIdentityCardStore : Object { public abstract IdCard? update_card(IdCard card); public abstract StoreType get_store_type(); public abstract LinkedList get_card_list(); + + // Note that (at least right now) store_id_cards() will re-load the cards after saving them. + internal abstract void store_id_cards(); } diff --git a/src/moonshot-identities-manager.vala b/src/moonshot-identities-manager.vala index 8154052..a1cac6b 100644 --- a/src/moonshot-identities-manager.vala +++ b/src/moonshot-identities-manager.vala @@ -244,6 +244,8 @@ public class IdentityManagerModel : Object { return false; } + // The name is misleading: This not only sets the store type, + // it also creates a new store instance, which loads the card data. public void set_store_type(IIdentityCardStore.StoreType type) { if ((store != null) && (store.get_store_type() == type)) return; @@ -258,6 +260,21 @@ public class IdentityManagerModel : Object { store = new LocalFlatFileStore(); break; } + + // Loop through the loaded IDs. If any trust anchors are old enough that we didn't record + // the datetime_added, add it now. + string before_now = _("Before ") + TrustAnchor.format_datetime_now(); + bool save_needed = false; + foreach (IdCard id in this.store.get_card_list()) { + if (!id.trust_anchor.is_empty() && id.trust_anchor.datetime_added == "") { + logger.trace("set_store_type : Set ta_datetime_added for old trust anchor on '%s' to '%s'".printf(id.display_name, before_now)); + id.trust_anchor.set_datetime_added(before_now); + save_needed = true; + } + } + if (save_needed) { + this.store.store_id_cards(); + } } public IIdentityCardStore.StoreType get_store_type() { diff --git a/src/moonshot-identity-dialog.vala b/src/moonshot-identity-dialog.vala index 40f03cf..ccec92c 100644 --- a/src/moonshot-identity-dialog.vala +++ b/src/moonshot-identity-dialog.vala @@ -234,8 +234,7 @@ class IdentityDialog : Dialog ta_table.attach(ta_clear_button, 1, 2, row, row + 1, fill, fill, 0, 0); row++; - //!!TODO - Label added_label = new Label(_("Added on: N/A")); + Label added_label = new Label(_("Added : " + id.trust_anchor.datetime_added)); added_label.set_alignment(0, 0.5f); ta_table.attach(added_label, 0, 1, row, row + 1, opts, opts, 20, 5); row++; diff --git a/src/moonshot-identity-management-view.vala b/src/moonshot-identity-management-view.vala index 2b0a4ba..fae10f0 100644 --- a/src/moonshot-identity-management-view.vala +++ b/src/moonshot-identity-management-view.vala @@ -334,7 +334,7 @@ public class IdentityManagerView : Window { Gtk.MessageDialog dialog; IdCard? prev_id = identities_manager.find_id_card(id_card.nai, force_flat_file_store); logger.trace("add_identity(flat=%s, card='%s'): find_id_card returned %s" - .printf(force_flat_file_store.to_string(), id_card.display_name, (prev_id != null ? "non-null" : "null"))); + .printf(force_flat_file_store.to_string(), id_card.display_name, (prev_id != null ? prev_id.display_name : "null"))); if (prev_id!=null) { int flags = prev_id.Compare(id_card); logger.trace("add_identity: compare returned " + flags.to_string()); diff --git a/src/moonshot-keyring-store.vala b/src/moonshot-keyring-store.vala index e24a6fe..d32fc71 100644 --- a/src/moonshot-keyring-store.vala +++ b/src/moonshot-keyring-store.vala @@ -60,6 +60,8 @@ public class KeyringStore : Object, IIdentityCardStore { return idcard; } } + + logger.error(@"update_card: card '$(card.display_name)' was not found after re-loading!"); return null; } @@ -110,6 +112,7 @@ public class KeyringStore : Object, IIdentityCardStore { string subject = ""; string subject_alt = ""; bool user_verified = false; + string ta_datetime_added = ""; for (i = 0; i < entry.attributes.len; i++) { var attribute = ((GnomeKeyring.Attribute *) entry.attributes.data)[i]; string value = ""; @@ -141,10 +144,15 @@ public class KeyringStore : Object, IIdentityCardStore { store_password = value; } else if (attribute.name == "CACert_User_Verified") { user_verified = (value == "true"); + } else if (attribute.name == "TA_DateTime_Added") { + ta_datetime_added = value; } } var ta = new TrustAnchor(ca_cert, server_cert, subject, subject_alt, user_verified); + if (ta_datetime_added != "") { + ta.set_datetime_added(ta_datetime_added); + } id_card.set_trust_anchor_from_store(ta); if ((rules_always_confirm_index != -1) && (rules_patterns_index != -1)) { @@ -175,7 +183,7 @@ public class KeyringStore : Object, IIdentityCardStore { } } - public void store_id_cards() { + internal void store_id_cards() { logger.trace("store_id_cards"); clear_keyring(); foreach (IdCard id_card in this.id_card_list) { @@ -205,6 +213,7 @@ public class KeyringStore : Object, IIdentityCardStore { attributes.append_string("Subject", id_card.trust_anchor.subject); attributes.append_string("Subject-Alt", id_card.trust_anchor.subject_alt); attributes.append_string("CACert_User_Verified", id_card.trust_anchor.user_verified ? "true" : "false"); + attributes.append_string("TA_DateTime_Added", id_card.trust_anchor.datetime_added); attributes.append_string("StorePassword", id_card.store_password ? "yes" : "no"); GnomeKeyring.Result result = GnomeKeyring.item_create_sync(null, diff --git a/src/moonshot-local-flat-file-store.vala b/src/moonshot-local-flat-file-store.vala index 55c535b..b3784f2 100644 --- a/src/moonshot-local-flat-file-store.vala +++ b/src/moonshot-local-flat-file-store.vala @@ -32,6 +32,8 @@ using Gee; public class LocalFlatFileStore : Object, IIdentityCardStore { + static MoonshotLogger logger = get_logger("LocalFlatFileStore"); + private LinkedList id_card_list; private const string FILE_NAME = "identities.txt"; @@ -44,9 +46,12 @@ public class LocalFlatFileStore : Object, IIdentityCardStore { id_card_list.remove(card); id_card_list.add(card); store_id_cards(); - foreach(IdCard idcard in id_card_list) - if (idcard.display_name == card.display_name) - return idcard; + foreach(IdCard idcard in id_card_list) { + if (idcard.display_name == card.display_name) { + return idcard; + } + } + logger.error(@"update_card: card '$(card.display_name)' was not found after re-loading!"); return null; } @@ -71,6 +76,7 @@ public class LocalFlatFileStore : Object, IIdentityCardStore { var key_file = new KeyFile(); var path = get_data_dir(); var filename = Path.build_filename(path, FILE_NAME); + logger.trace("load_id_cards: attempting to load from " + filename); try { key_file.load_from_file(filename, KeyFileFlags.NONE); @@ -115,8 +121,12 @@ public class LocalFlatFileStore : Object, IIdentityCardStore { string server_cert = key_file.get_string(identity, "ServerCert"); string subject = key_file.get_string(identity, "Subject"); string subject_alt = key_file.get_string(identity, "SubjectAlt"); - bool user_verified = key_file.get_boolean(identity, "CACert_User_Verified"); + bool user_verified = get_bool_setting(identity, "TA_DateTime_Added", false, key_file); var ta = new TrustAnchor(ca_cert, server_cert, subject, subject_alt, user_verified); + string ta_datetime_added = get_string_setting(identity, "TA_DateTime_Added", "", key_file); + if (ta_datetime_added != "") { + ta.set_datetime_added(ta_datetime_added); + } id_card.set_trust_anchor_from_store(ta); id_card_list.add(id_card); } @@ -137,9 +147,11 @@ public class LocalFlatFileStore : Object, IIdentityCardStore { return path; } - public void store_id_cards() { + internal void store_id_cards() { var key_file = new KeyFile(); foreach (IdCard id_card in this.id_card_list) { + logger.trace(@"store_id_cards: Storing '$(id_card.display_name)'"); + /* workaround for Centos vala array property bug: use temp arrays */ var rules = id_card.rules; string[] empty = {}; @@ -175,11 +187,13 @@ public class LocalFlatFileStore : Object, IIdentityCardStore { key_file.set_string(id_card.display_name, "StorePassword", id_card.store_password ? "yes" : "no"); // Trust anchor - key_file.set_string(id_card.display_name, "CA-Cert", id_card.trust_anchor.ca_cert ?? ""); - key_file.set_string(id_card.display_name, "Subject", id_card.trust_anchor.subject ?? ""); - key_file.set_string(id_card.display_name, "SubjectAlt", id_card.trust_anchor.subject_alt ?? ""); - key_file.set_string(id_card.display_name, "ServerCert", id_card.trust_anchor.server_cert ?? ""); + key_file.set_string(id_card.display_name, "CA-Cert", id_card.trust_anchor.ca_cert); + key_file.set_string(id_card.display_name, "Subject", id_card.trust_anchor.subject); + key_file.set_string(id_card.display_name, "SubjectAlt", id_card.trust_anchor.subject_alt); + key_file.set_string(id_card.display_name, "ServerCert", id_card.trust_anchor.server_cert); + key_file.set_string(id_card.display_name, "TA_DateTime_Added", id_card.trust_anchor.datetime_added); key_file.set_boolean(id_card.display_name, "CACert_User_Verified", id_card.trust_anchor.user_verified); + logger.trace(@"store_id_cards: Stored '$(id_card.display_name)'"); } var text = key_file.to_data(null); @@ -187,6 +201,7 @@ public class LocalFlatFileStore : Object, IIdentityCardStore { try { var path = get_data_dir(); var filename = Path.build_filename(path, FILE_NAME); + logger.trace("store_id_cards: attempting to store to " + filename); var file = File.new_for_path(filename); var stream = file.replace(null, false, FileCreateFlags.PRIVATE); #if GIO_VAPI_USES_ARRAYS @@ -197,6 +212,7 @@ public class LocalFlatFileStore : Object, IIdentityCardStore { #endif } catch (Error e) { + logger.error("store_id_cards: Error while saving keyfile: %s\n".printf(e.message)); stdout.printf("Error: %s\n", e.message); } diff --git a/src/moonshot-provisioning-common.vala b/src/moonshot-provisioning-common.vala index cd85f9a..c0b35c4 100644 --- a/src/moonshot-provisioning-common.vala +++ b/src/moonshot-provisioning-common.vala @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011-2014, JANET(UK) + * Copyright (c) 2011-2016, JANET(UK) * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -130,9 +130,13 @@ namespace WebProvisioning { if (element_name == "identity") { - logger.trace("start_element_func (%p): Adding an identity".printf(this)); card = new IdCard(); _cards += card; + + ta_ca_cert = ""; + ta_server_cert = ""; + ta_subject = ""; + ta_subject_alt = ""; } else if (element_name == "rule") { @@ -140,6 +144,22 @@ namespace WebProvisioning } } + private void end_element_func(MarkupParseContext context, + string element_name) throws MarkupError + { + if (element_name == "identity") + { + if (ta_ca_cert != "" || ta_server_cert != "") { + var ta = new TrustAnchor(ta_ca_cert, + ta_server_cert, + ta_subject, + ta_subject_alt, + false); + card.set_trust_anchor_from_store(ta); + } + } + } + private void text_element_func(MarkupParseContext context, string text, @@ -149,8 +169,6 @@ namespace WebProvisioning if (text_len < 1) return; - logger.trace("text_element_func (%p): text='%s'".printf(this, stack.nth_data(0))); - if (stack.nth_data(0) == "display-name" && display_name_handler(stack)) { card.display_name = text; @@ -187,56 +205,26 @@ namespace WebProvisioning card.rules[temp.length - 1].always_confirm = text; } } - // This is ugly, but... we use the TrustAnchor field in the IdCard as a placeholder, - // replacing it with a new one every time we read a new element. - // "user_verified" is always false, since we're reading the TrustAnchor from XML. else if (stack.nth_data(0) == "ca-cert" && ca_cert_handler(stack)) { - string ca_cert = text; - var ta = new TrustAnchor(ca_cert, - card.trust_anchor.server_cert, - card.trust_anchor.subject, - card.trust_anchor.subject_alt, - false); - card.set_trust_anchor_from_store(ta); + ta_ca_cert = text ?? ""; } else if (stack.nth_data(0) == "server-cert" && server_cert_handler(stack)) { - string server_cert = text; - var ta = new TrustAnchor(card.trust_anchor.ca_cert, - server_cert, - card.trust_anchor.subject, - card.trust_anchor.subject_alt, - false); - card.set_trust_anchor_from_store(ta); - + ta_server_cert = text ?? ""; } else if (stack.nth_data(0) == "subject" && subject_handler(stack)) { - string subject = text; - var ta = new TrustAnchor(card.trust_anchor.ca_cert, - card.trust_anchor.server_cert, - subject, - card.trust_anchor.subject_alt, - false); - card.set_trust_anchor_from_store(ta); + ta_subject = text; } else if (stack.nth_data(0) == "subject-alt" && subject_alt_handler(stack)) { - string subject_alt = text; - var ta = new TrustAnchor(card.trust_anchor.ca_cert, - card.trust_anchor.server_cert, - card.trust_anchor.subject, - subject_alt, - false); - card.set_trust_anchor_from_store(ta); + ta_subject_alt = text; } } - - private const MarkupParser parser = { - start_element_func, null, text_element_func, null, null + start_element_func, end_element_func, text_element_func, null, null }; private MarkupParseContext ctx; @@ -244,6 +232,11 @@ namespace WebProvisioning private string text; private string path; + private string ta_ca_cert; + private string ta_server_cert; + private string ta_subject; + private string ta_subject_alt; + private IdCard card; private IdCard[] _cards = {}; @@ -268,8 +261,7 @@ namespace WebProvisioning while ((line = dis.read_line(null)) != null) { text += line; - // Preserve newlines -- important for certificate import. - // (X509 certs can't be parsed without the newlines.) + // Preserve newlines. // // This may add an extra newline at EOF. Maybe use // dis.read_upto("\n", ...) followed by dis.read_byte() instead? @@ -280,8 +272,6 @@ namespace WebProvisioning { error("Could not retreive file size"); } - - logger.trace(@"Parser(): read text to parse; length=$(text.length)"); } public void parse() { diff --git a/src/moonshot-server.vala b/src/moonshot-server.vala index 5f0fa2f..7c95ff7 100644 --- a/src/moonshot-server.vala +++ b/src/moonshot-server.vala @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011-2014, JANET(UK) + * Copyright (c) 2011-2016, JANET(UK) * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -199,7 +199,12 @@ public class MoonshotServer : Object { idcard.issuer = realm; idcard.update_services(services); var ta = new TrustAnchor(ca_cert, server_cert, subject, subject_alt, false); - idcard.set_trust_anchor_from_store(ta); + if (!ta.is_empty()) { + string ta_datetime_added = TrustAnchor.format_datetime_now(); + ta.set_datetime_added(ta_datetime_added); + logger.trace("install_id_card : Set ta_datetime_added for '%s' to '%s'".printf(idcard.display_name, ta_datetime_added)); + idcard.set_trust_anchor_from_store(ta); + } logger.trace("install_id_card: Card '%s' has services: '%s'" .printf(idcard.display_name, idcard.get_services_string("; "))); diff --git a/src/moonshot-settings.vala b/src/moonshot-settings.vala index 8107c05..4dea622 100644 --- a/src/moonshot-settings.vala +++ b/src/moonshot-settings.vala @@ -100,17 +100,31 @@ private void save_keyfile(KeyFile key_file) // streams close automatically } -internal void set_bool_setting(string group_name, string key_name, bool value) +internal void set_bool_setting(string group_name, string key_name, bool value, KeyFile? key_file=null) { - KeyFile key_file = get_keyfile(); + KeyFile tmp_key_file = null; + if (key_file == null) { + // Use tmp_key_file to hold an owned reference (since key_file is unowned) + tmp_key_file = get_keyfile(); + key_file = tmp_key_file; + } key_file.set_boolean(group_name, key_name, value); - save_keyfile(key_file); + + if (tmp_key_file != null) { + // This is a "one-shot" settings update; save it now. + save_keyfile(key_file); + } } -internal bool get_bool_setting(string group_name, string key_name, bool default=false) +internal bool get_bool_setting(string group_name, string key_name, bool default=false, KeyFile? key_file=null) { - KeyFile key_file = get_keyfile(); + KeyFile tmp_key_file = null; + if (key_file == null) { + // Use tmp_key_file to hold an owned reference (since key_file is unowned) + tmp_key_file = get_keyfile(); + key_file = tmp_key_file; + } if (key_file == null) return default; @@ -137,17 +151,30 @@ internal bool get_bool_setting(string group_name, string key_name, bool default= } -internal void set_string_setting(string group_name, string key_name, string value) +internal void set_string_setting(string group_name, string key_name, string value, KeyFile? key_file=null) { - KeyFile key_file = get_keyfile(); + KeyFile tmp_key_file = null; + if (key_file == null) { + // Use tmp_key_file to hold an owned reference (since key_file is unowned) + tmp_key_file = get_keyfile(); + key_file = tmp_key_file; + } key_file.set_string(group_name, key_name, value); - save_keyfile(key_file); + if (tmp_key_file != null) { + // This is a "one-shot" settings update; save it now. + save_keyfile(key_file); + } } -internal string get_string_setting(string group_name, string key_name, string default="") +internal string get_string_setting(string group_name, string key_name, string default="", KeyFile? key_file=null) { - KeyFile key_file = get_keyfile(); + KeyFile tmp_key_file = null; + if (key_file == null) { + // Use tmp_key_file to hold an owned reference (since key_file is unowned) + tmp_key_file = get_keyfile(); + key_file = tmp_key_file; + } if (key_file == null) return default; -- 2.1.4