First cut at supporting trust anchors
authorDan Breslau <dbreslau@painless-security.com>
Tue, 9 Aug 2016 23:14:50 +0000 (19:14 -0400)
committerDan Breslau <dbreslau@painless-security.com>
Tue, 9 Aug 2016 23:14:50 +0000 (19:14 -0400)
.gitignore
Makefile.am
src/moonshot-id.vala
src/moonshot-idcard-widget.vala
src/moonshot-identity-management-view.vala
src/moonshot-settings.vala
src/moonshot-trust-anchor-dialog.vala [new file with mode: 0644]
webprovisioning/complex-test.msht

index a39248a..b2b436a 100755 (executable)
@@ -47,6 +47,7 @@ src/moonshot-provisioning-common-new.vala
 src/moonshot-provisioning-common.c
 src/moonshot-server.c
 src/moonshot-settings.c
+src/moonshot-trust-anchor-dialog.c
 src/moonshot-utils.c
 src/moonshot-warning-dialog.c
 src/moonshot-webp-parser.c
index 0470762..4f40dd4 100644 (file)
@@ -68,6 +68,7 @@ src_moonshot_SOURCES = \
         src/moonshot-settings.vala \
         src/moonshot-password-dialog.vala \
         src/moonshot-provisioning-common.vala \
+        src/moonshot-trust-anchor-dialog.vala \
         src/moonshot-utils.vala \
         src/moonshot-futils.c \
         src/moonshot-logger.vala \
index ec0866c..77afd4e 100644 (file)
@@ -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
@@ -34,10 +34,60 @@ using Gee;
 
 public class TrustAnchor : Object
 {
-    public string ca_cert {get; set; default = "";}
-    public string subject {get; set; default = "";}
-    public string subject_alt  {get; set; default = "";}
-    public string server_cert  {get; set; default = "";}
+    public static const string TYPE_CA_CERT = _("CA Certificate");
+    public static const string TYPE_ENTERPRISE = _("Enterprise provisioned");
+
+    private string _ca_cert = "";
+    private string _subject = "";
+    private string _subject_alt = "";
+    private string _server_cert = "";
+
+    public string ca_cert {
+        get {
+            return _ca_cert;
+        }
+        set {
+            _ca_cert = (value ?? "");
+        }
+    }
+
+    public string subject {
+        get {
+            return _subject;
+        }
+        set {
+            _subject = (value ?? "");
+        }
+    }
+
+    public string subject_alt  {
+        get {
+            return _subject_alt;
+        }
+        set {
+            _subject_alt = (value ?? "");
+        }
+    }
+
+
+    public string server_cert {
+        get {
+            return _server_cert;
+        }
+        set {
+            _server_cert = (value ?? "");
+        }
+    }
+
+    public bool is_empty() {
+        return ca_cert == "" && subject == "" && subject_alt == "" && server_cert == "";
+    }
+
+    public string get_anchor_type() {
+        return server_cert == "" ? TYPE_CA_CERT : TYPE_ENTERPRISE;
+    }
+
     public int Compare(TrustAnchor other)
     {
         if (this.ca_cert != other.ca_cert)
index 7a34cf2..6cd2b37 100644 (file)
@@ -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
@@ -131,7 +131,9 @@ class IdCardWidget : Box
         {
             label_text += "\nUsername:  " + id_card.username;
             label_text += "\nRealm:  " + id_card.issuer;
-
+            if (!id_card.trust_anchor.is_empty()) {
+                label_text += "\nTrust anchor: " + id_card.trust_anchor.get_anchor_type();
+            }
             services_text += this.id_card.get_services_string(service_spacer);
             label_text += "\n" + services_text;
         }
index cea963b..6620ac0 100644 (file)
@@ -542,14 +542,19 @@ public class IdentityManagerView : Window {
 
     private void send_identity_cb(IdCard id)
     {
-        send_button.set_sensitive(false);
-
-        IdCard identity = id;
         return_if_fail(request_queue.length > 0);
 
-        candidates = null;
+        if (!check_and_confirm_trust_anchor(id)) {
+            // Allow user to pick again
+            return;
+        }
+
         var request = this.request_queue.pop_head();
-        identity = check_add_password(identity, request, identities_manager);
+        var identity = check_add_password(id, request, identities_manager);
+        send_button.set_sensitive(false);
+
+        candidates = null;
+      
         if (this.request_queue.is_empty())
         {
             candidates = null;
@@ -578,6 +583,34 @@ public class IdentityManagerView : Window {
         remember_identity_binding.hide();
     }
 
+    private bool check_and_confirm_trust_anchor(IdCard id)
+    {
+        if (!id.trust_anchor.is_empty() && id.trust_anchor.get_anchor_type() == TrustAnchor.TYPE_ENTERPRISE) {
+            if (get_string_setting("TrustAnchors", id.nai) != id.trust_anchor.server_cert) {
+
+                bool ret = false;
+                int result = ResponseType.CANCEL;
+                var dialog = new TrustAnchorDialog(id, this);
+                while (!dialog.complete)
+                    result = dialog.run();
+
+                switch (result) {
+                case ResponseType.OK:
+                    set_string_setting("TrustAnchors", id.nai, id.trust_anchor.server_cert);
+                    ret = true;
+                    break;
+                default:
+                    break;
+                }
+
+                dialog.destroy();
+                return ret;
+            }
+        }
+        return true;
+    }
+
+
     // private void label_make_bold(Label label)
     // {
     //     var font_desc = new Pango.FontDescription();
index 1549213..8107c05 100644 (file)
@@ -135,3 +135,40 @@ internal bool get_bool_setting(string group_name, string key_name, bool default=
     }
     return default;
 }
+
+
+internal void set_string_setting(string group_name, string key_name, string value)
+{
+    KeyFile key_file = get_keyfile();
+
+    key_file.set_string(group_name, key_name, value);
+    save_keyfile(key_file);
+}
+
+internal string get_string_setting(string group_name, string key_name, string default="")
+{
+    KeyFile key_file = get_keyfile();
+
+    if (key_file == null)
+        return default;
+
+    try {
+        if (!key_file.has_key(group_name, key_name))
+        {
+            logger().info(@"get_string_setting : key file doesn't contain key '$key_name' in group '$group_name'");
+            return default;
+        }
+    }
+    catch(KeyFileError e) {
+        logger().info(@"get_string_setting : KeyFileError checking if key '$key_name' exists in group '$group_name' (maybe ignorable?) : " + e.message);
+    }
+
+    try {
+        // throws KeyFileError if key is not found
+        return key_file.get_string(group_name, key_name);
+    }
+    catch (KeyFileError e) {
+        logger().info("get_string_setting got KeyFileError (may be ignorable) : " + e.message);
+    }
+    return default;
+}
diff --git a/src/moonshot-trust-anchor-dialog.vala b/src/moonshot-trust-anchor-dialog.vala
new file mode 100644 (file)
index 0000000..9fc2213
--- /dev/null
@@ -0,0 +1,142 @@
+/*
+ * 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.
+*/
+using Gtk;
+
+class TrustAnchorDialog : Dialog
+{
+    private static Gdk.Color white = make_color(65535, 65535, 65535);
+
+    private Entry trust_anchor_entry;
+
+    public bool complete = false;
+
+    public TrustAnchorDialog(IdCard idcard, Window parent)
+    {
+        this.set_title(_("Trust Anchor"));
+        this.set_modal(true);
+        this.set_transient_for(parent);
+        this.modify_bg(StateType.NORMAL, white);
+
+        this.add_buttons(_("Connect"), ResponseType.OK,
+                         _("Cancel"), ResponseType.CANCEL);
+
+        this.set_default_response(ResponseType.OK);
+
+        var content_area = this.get_content_area();
+        ((Box) content_area).set_spacing(12);
+        content_area.modify_bg(StateType.NORMAL, white);
+
+        Label dialog_label = new Label("");
+        dialog_label.set_alignment(0, 0);
+
+        string label_markup = _("<span font-weight='heavy'>You are using this identity for the first time with the following trust anchor:</span>");
+
+        dialog_label.set_markup(label_markup);
+        dialog_label.set_line_wrap(true);
+        dialog_label.set_width_chars(60);
+                                                   
+        var user_label = new Label(_("Username: ") + idcard.username);
+        user_label.set_alignment(0, 0.5f);
+
+        var realm_label = new Label(_("Realm: ") + idcard.issuer);
+        realm_label.set_alignment(0, 0.5f);
+
+        var fingerprint_label = new Label(_("SHA-256 fingerprint:"));
+        fingerprint_label.set_alignment(0, 0.5f);
+
+        var footprint = new TextView();
+        //footprint.activates_default = false;
+        // footprint.set_sensitive(false);
+        footprint.set_editable(false);
+        // footprint.set_text(idcard.trust_anchor.server_cert);
+        var buffer = footprint.get_buffer();
+        buffer.set_text(colonize(idcard.trust_anchor.server_cert), -1);
+        footprint.wrap_mode = WrapMode.WORD_CHAR;
+
+        set_atk_relation(fingerprint_label, footprint, Atk.RelationType.LABEL_FOR);
+
+        var footprint_width_constraint = new ScrolledWindow(null, null);
+        footprint_width_constraint.set_policy(PolicyType.NEVER, PolicyType.NEVER);
+        footprint_width_constraint.set_shadow_type(ShadowType.IN);
+        footprint_width_constraint.set_size_request(400, 60);
+        footprint_width_constraint.add_with_viewport(footprint);
+
+        Label confirm_label = new Label(_("Please confirm that this is the correct trust anchor."));
+        confirm_label.set_alignment(0, 0.5f);
+
+        var vbox = new VBox(false, 0);
+        vbox.set_border_width(6);
+        vbox.pack_start(dialog_label, true, true, 12);
+        vbox.pack_start(user_label, true, true, 2);
+        vbox.pack_start(realm_label, true, true, 2);
+        vbox.pack_start(fingerprint_label, true, true, 2);
+        vbox.pack_start(footprint_width_constraint, true, true, 2);
+        vbox.pack_start(confirm_label, true, true, 12);
+
+        ((Container) content_area).add(vbox);
+
+        this.set_border_width(6);
+        this.set_resizable(false);
+
+        this.response.connect(on_response);
+
+        this.show_all();
+    }
+
+    private void on_response(Dialog source, int response_id)
+    {
+        switch (response_id) {
+        case ResponseType.OK:
+            complete = true;
+            break;
+        case ResponseType.CANCEL:
+            complete = true;
+            break;
+        }
+    }
+
+    // Yeah, it doesn't mean "colonize" the way you might think... :-)
+    private static string colonize(string input) {
+        return_if_fail(input.length % 2 == 0);
+
+        string result = "";
+        int i = 0;
+        while (i < input.length) {
+            if (i > 0) {
+                result += ":";
+            }
+            result += input[i : i + 2];
+            i += 2;
+        }
+        return result;
+    }
+}
index 2c11172..b09b1d0 100644 (file)
@@ -3,10 +3,10 @@
     <display-name>Unique Name</display-name>
     <user>user1</user>
     <password></password>
-    <realm>foo.baz</realm>
+    <realm>painless-security.com</realm>
     <services>
-      <service>irc@jabber.project-moonshot.org</service>
-      <service>xmpp@jabber.project-moonshot.org</service>
+      <service>irc/painless-security.com</service>
+      <service>xmpp/painless-security.com</service>
     </services>
     <selection-rules>
       <rule>
         <always-confirm>true</always-confirm>
       </rule>
       <rule>
-        <pattern>imap@*moonshot.org</pattern>
+        <pattern>imap/*moonshot.org</pattern>
         <always-confirm>false</always-confirm>
       </rule>
     </selection-rules>
     <trust-anchor>
-      <ca-cert>ABCDEFGHIJKLMNOPQRSTUVWXYZ123455678910</ca-cert>
-      <subject>Foo</subject>
-      <subject-alt>Bar</subject-alt>
+      <ca-cert>MIIE9jCCA96gAwIBAgIJAJ6SVDCP6o2nMA0GCSqGSIb3DQEBBQUAMIGaMQswCQYD
+VQQGEwJVUzELMAkGA1UECBMCTUExDzANBgNVBAcTBk1hbGRlbjEaMBgGA1UEChMR
+UGFpbmxlc3MgU2VjdXJpdHkxLzAtBgkqhkiG9w0BCQEWIHBvc3RtYXN0ZXJAcGFp
+bmxlc3Mtc2VjdXJpdHkuY29tMSAwHgYDVQQDExdQYWlubGVzcyBTZWN1cml0eSwg
+SW5jLjAeFw0xNjA4MDExNjIxMDVaFw0xOTExMTQxNjIxMDVaMIGaMQswCQYDVQQG
+EwJVUzELMAkGA1UECBMCTUExDzANBgNVBAcTBk1hbGRlbjEaMBgGA1UEChMRUGFp
+bmxlc3MgU2VjdXJpdHkxLzAtBgkqhkiG9w0BCQEWIHBvc3RtYXN0ZXJAcGFpbmxl
+c3Mtc2VjdXJpdHkuY29tMSAwHgYDVQQDExdQYWlubGVzcyBTZWN1cml0eSwgSW5j
+LjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKPiSkw1y6zMJFjnoPjd
+5Bh9EA1NhQcoNxJAtgYEJtpH9a2tfjnXXncXpbIMIfMgv2VKRAxvKb+knCfSCRtU
+PM9i998+ZhJY9o6SSFomlMvdaClauPvBhQvQMmJmp1WINgMUHPpzsGlj04kkl7jw
+iK/oDxp1becikKc10Gr9W03aEJtOaiSqC45zeIgnz9GoQ2tJvz2DDBcddaaT1mSV
+n/lk4ahPC4XaJ08Jn1L6XkVVyDGD38Rwg7r1SFI7ByBFvvQh93Fa48Z7ik0I8s48
+U1euHak2gSJ4zfzLndvGy05qMjhRTlxQu+Rt1g7CS3CLcJqqYzWNrEJWpD8Wn7iA
+MIUCAwEAAaOCATswggE3MB0GA1UdDgQWBBR1qlvY7r2DqhHu5s+sCUPeqBcQuzCB
+zwYDVR0jBIHHMIHEgBR1qlvY7r2DqhHu5s+sCUPeqBcQu6GBoKSBnTCBmjELMAkG
+A1UEBhMCVVMxCzAJBgNVBAgTAk1BMQ8wDQYDVQQHEwZNYWxkZW4xGjAYBgNVBAoT
+EVBhaW5sZXNzIFNlY3VyaXR5MS8wLQYJKoZIhvcNAQkBFiBwb3N0bWFzdGVyQHBh
+aW5sZXNzLXNlY3VyaXR5LmNvbTEgMB4GA1UEAxMXUGFpbmxlc3MgU2VjdXJpdHks
+IEluYy6CCQCeklQwj+qNpzAMBgNVHRMEBTADAQH/MDYGA1UdHwQvMC0wK6ApoCeG
+JWh0dHA6Ly93d3cuZXhhbXBsZS5jb20vZXhhbXBsZV9jYS5jcmwwDQYJKoZIhvcN
+AQEFBQADggEBAB6J5Zxvq96SdIsfEajqU+pANBiA2VTZCpxfIMAKz8KfyzWzFvCM
+8epvYDliyOjw1zR9cYxhQqOcbPHrjLXheVvCePd3jCUOv+tt1Nw2gS2DiMuq37DO
+BZOTlPJ3m2NnvJVO3NjB2I+Pk9v3YlG6mkiVc9dNWgO20SqT2Y+KvHqA5Of8Cb/s
+uIBftctvGpIyEnqSmU7KB0nhIWe65Bsu60hjHHfX1qhJE7qGKbqNaHujssQ/SBXJ
+g7HUhtywv8z3TFoYW0MoBpKGM2Ojc9kQ8f0rYvUKTiD1UfjQoll/Io5xwKy7FXtn
+musuCxXeWkqDtw0clWg6vkf5Tb9v/JQ2PW0=</ca-cert>
+      <subject>Painless Security Server Certificate</subject>
       <!-- Or alternatively -->
-      <server-cert>ABCDEFGHIJKLMNOPQRSTUVWXYZ123455678910</server-cert>
+      <server-cert></server-cert>
     </trust-anchor>
   </identity>
   <identity>
     <display-name>Another Unique Name</display-name>
     <user>user2</user>
     <password></password>
-    <realm>foo.bar</realm>
+    <realm>painless-security.com</realm>
     <services>
-      <service>irc@jabber.project-moonshot.org</service>
-      <service>email@project-moonshot.org</service>
+      <service>irc/painless-security.com</service>
+      <service>email/painless-security.com</service>
     </services>
     <selection-rules>
       <rule>
-        <pattern>*@project-moonshot.org</pattern>
+        <pattern>*/painless-security.com</pattern>
         <always-confirm>true</always-confirm>
       </rule>
     </selection-rules>
     <trust-anchor>
-      <ca-cert>ABCDEFGHIJKLMNOPQRSTUVWXYZ123455678910</ca-cert>
-      <subject>Foo</subject>
-      <subject-alt>Bar</subject-alt>
-      <!-- Or alternatively -->
-      <server-cert>ABCDEFGHIJKLMNOPQRSTUVWXYZ123455678910</server-cert>
+      <ca-cert>MIIE9jCCA96gAwIBAgIJAJ6SVDCP6o2nMA0GCSqGSIb3DQEBBQUAMIGaMQswCQYD
+VQQGEwJVUzELMAkGA1UECBMCTUExDzANBgNVBAcTBk1hbGRlbjEaMBgGA1UEChMR
+UGFpbmxlc3MgU2VjdXJpdHkxLzAtBgkqhkiG9w0BCQEWIHBvc3RtYXN0ZXJAcGFp
+bmxlc3Mtc2VjdXJpdHkuY29tMSAwHgYDVQQDExdQYWlubGVzcyBTZWN1cml0eSwg
+SW5jLjAeFw0xNjA4MDExNjIxMDVaFw0xOTExMTQxNjIxMDVaMIGaMQswCQYDVQQG
+EwJVUzELMAkGA1UECBMCTUExDzANBgNVBAcTBk1hbGRlbjEaMBgGA1UEChMRUGFp
+bmxlc3MgU2VjdXJpdHkxLzAtBgkqhkiG9w0BCQEWIHBvc3RtYXN0ZXJAcGFpbmxl
+c3Mtc2VjdXJpdHkuY29tMSAwHgYDVQQDExdQYWlubGVzcyBTZWN1cml0eSwgSW5j
+LjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKPiSkw1y6zMJFjnoPjd
+5Bh9EA1NhQcoNxJAtgYEJtpH9a2tfjnXXncXpbIMIfMgv2VKRAxvKb+knCfSCRtU
+PM9i998+ZhJY9o6SSFomlMvdaClauPvBhQvQMmJmp1WINgMUHPpzsGlj04kkl7jw
+iK/oDxp1becikKc10Gr9W03aEJtOaiSqC45zeIgnz9GoQ2tJvz2DDBcddaaT1mSV
+n/lk4ahPC4XaJ08Jn1L6XkVVyDGD38Rwg7r1SFI7ByBFvvQh93Fa48Z7ik0I8s48
+U1euHak2gSJ4zfzLndvGy05qMjhRTlxQu+Rt1g7CS3CLcJqqYzWNrEJWpD8Wn7iA
+MIUCAwEAAaOCATswggE3MB0GA1UdDgQWBBR1qlvY7r2DqhHu5s+sCUPeqBcQuzCB
+zwYDVR0jBIHHMIHEgBR1qlvY7r2DqhHu5s+sCUPeqBcQu6GBoKSBnTCBmjELMAkG
+A1UEBhMCVVMxCzAJBgNVBAgTAk1BMQ8wDQYDVQQHEwZNYWxkZW4xGjAYBgNVBAoT
+EVBhaW5sZXNzIFNlY3VyaXR5MS8wLQYJKoZIhvcNAQkBFiBwb3N0bWFzdGVyQHBh
+aW5sZXNzLXNlY3VyaXR5LmNvbTEgMB4GA1UEAxMXUGFpbmxlc3MgU2VjdXJpdHks
+IEluYy6CCQCeklQwj+qNpzAMBgNVHRMEBTADAQH/MDYGA1UdHwQvMC0wK6ApoCeG
+JWh0dHA6Ly93d3cuZXhhbXBsZS5jb20vZXhhbXBsZV9jYS5jcmwwDQYJKoZIhvcN
+AQEFBQADggEBAB6J5Zxvq96SdIsfEajqU+pANBiA2VTZCpxfIMAKz8KfyzWzFvCM
+8epvYDliyOjw1zR9cYxhQqOcbPHrjLXheVvCePd3jCUOv+tt1Nw2gS2DiMuq37DO
+BZOTlPJ3m2NnvJVO3NjB2I+Pk9v3YlG6mkiVc9dNWgO20SqT2Y+KvHqA5Of8Cb/s
+uIBftctvGpIyEnqSmU7KB0nhIWe65Bsu60hjHHfX1qhJE7qGKbqNaHujssQ/SBXJ
+g7HUhtywv8z3TFoYW0MoBpKGM2Ojc9kQ8f0rYvUKTiD1UfjQoll/Io5xwKy7FXtn
+musuCxXeWkqDtw0clWg6vkf5Tb9v/JQ2PW0=</ca-cert>
+      <subject>Painless Security Server Certificate</subject>
     </trust-anchor>
   </identity>
     <identity>
     <display-name>Yet Another Unique Name</display-name>
     <user>user3</user>
     <password></password>
-    <realm>foo.com</realm>
+    <realm>painless-security.com</realm>
     <services>
-      <service>irc@jabber.project-moonshot.org</service>
-      <service>email@project-moonshot.org</service>
+      <service>irc/painless-security.com</service>
+      <service>email/painless-security.com</service>
     </services>
     <trust-anchor>
-      <ca-cert>ABCDEFGHIJKLMNOPQRSTUVWXYZ123455678910</ca-cert>
-      <subject>Foo</subject>
-      <subject-alt>Bar</subject-alt>
-      <!-- Or alternatively -->
-      <server-cert>ABCDEFGHIJKLMNOPQRSTUVWXYZ123455678910</server-cert>
+      <server-cert>3838E17EC9A2A06D7B6030E3C5727E3466EAB4BB4159DCE7CF6297ADAFC8A56F</server-cert>
     </trust-anchor>
   </identity>
 </identities>