about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorDebXWoody <stefan@debxwoody.de>2020-07-10 13:39:34 +0200
committerMichael Vetter <jubalh@iodoru.org>2020-07-10 13:40:00 +0200
commit0552e50c781d9710965c1dab8cd66d37d61ad97e (patch)
treee0adb7822397fa1c2a28273a58b8212aced520dd
parentec6b61c2bbaf6634b1f9dc89892ede82b5c74c01 (diff)
downloadprofani-tty-0552e50c781d9710965c1dab8cd66d37d61ad97e.tar.gz
OX: XEP-0373: OpenPGP for XMPP - Version 0.5.0 (2020-06-19)
Discovering Public Keys via PEP

* 4.3 Discovering Public Keys of a User
* 4.4 Requesting Public Keys

* Import Public Keys into GnuPG's local keyring.

Issue: #1331
-rw-r--r--src/command/cmd_ac.c10
-rw-r--r--src/command/cmd_funcs.c12
-rw-r--r--src/pgp/gpg.c44
-rw-r--r--src/pgp/gpg.h1
-rw-r--r--src/xmpp/ox.c232
-rw-r--r--src/xmpp/ox.h12
-rw-r--r--tests/unittests/xmpp/stub_ox.c10
7 files changed, 308 insertions, 13 deletions
diff --git a/src/command/cmd_ac.c b/src/command/cmd_ac.c
index 13c06832..25e48e88 100644
--- a/src/command/cmd_ac.c
+++ b/src/command/cmd_ac.c
@@ -867,16 +867,6 @@ cmd_ac_init(void)
     autocomplete_add(ox_ac, "announce");
     autocomplete_add(ox_ac, "discover");
     autocomplete_add(ox_ac, "request");
-
-    pgp_log_ac = autocomplete_new();
-    autocomplete_add(ox_log_ac, "on");
-    autocomplete_add(ox_log_ac, "off");
-    autocomplete_add(ox_log_ac, "redact");
-
-    pgp_sendfile_ac = autocomplete_new();
-    autocomplete_add(ox_sendfile_ac, "on");
-    autocomplete_add(ox_sendfile_ac, "off");
-
 #endif
 
     tls_ac = autocomplete_new();
diff --git a/src/command/cmd_funcs.c b/src/command/cmd_funcs.c
index d7488cff..cc52cf42 100644
--- a/src/command/cmd_funcs.c
+++ b/src/command/cmd_funcs.c
@@ -7545,6 +7545,18 @@ cmd_ox(ProfWin* window, const char* const command, gchar** args)
         } else {
             cons_show("Filename is required");
         }
+    } else if (g_strcmp0(args[0], "discover") == 0) {
+        if (args[1]) {
+            ox_discover_public_key(args[1]);
+        } else {
+            cons_show("JID is required");
+        }
+    } else if (g_strcmp0(args[0], "request") == 0) {
+        if (args[1] && args[2]) {
+            ox_request_public_key(args[1], args[2]);
+        } else {
+            cons_show("JID and Fingerprint is required");
+        }
     } else {
         cons_show("OX not implemented");
     }
diff --git a/src/pgp/gpg.c b/src/pgp/gpg.c
index ca70367a..7be1e5c9 100644
--- a/src/pgp/gpg.c
+++ b/src/pgp/gpg.c
@@ -1304,3 +1304,47 @@ p_ox_gpg_readkey(const char* const filename, char** key, char** fp)
         log_error("Read OpenPGP key from file: Unable to read file: %s", error->message);
     }
 }
+
+gboolean
+p_ox_gpg_import(char* base64_public_key)
+{
+    gsize size = -1;
+    guchar* key = g_base64_decode(base64_public_key, &size);
+
+    setlocale(LC_ALL, "");
+    gpgme_check_version(NULL);
+    gpgme_set_locale(NULL, LC_CTYPE, setlocale(LC_CTYPE, NULL));
+    gpgme_ctx_t ctx;
+    gpgme_error_t error = gpgme_new(&ctx);
+
+    if (GPG_ERR_NO_ERROR != error) {
+        log_error("Read OpenPGP key from file: gpgme_new failed: %s", gpgme_strerror(error));
+        return FALSE;
+    }
+
+    error = gpgme_set_protocol(ctx, GPGME_PROTOCOL_OPENPGP);
+    if (error != GPG_ERR_NO_ERROR) {
+        log_error("Read OpenPGP key from file: set GPGME_PROTOCOL_OPENPGP:  %s", gpgme_strerror(error));
+        return FALSE;
+    }
+
+    gpgme_set_armor(ctx, 0);
+    gpgme_set_textmode(ctx, 0);
+    gpgme_set_offline(ctx, 1);
+    gpgme_set_keylist_mode(ctx, GPGME_KEYLIST_MODE_LOCAL);
+
+    gpgme_data_t gpgme_data = NULL;
+    error = gpgme_data_new(&gpgme_data);
+    if (error != GPG_ERR_NO_ERROR) {
+        log_error("Read OpenPGP key from file: gpgme_data_new %s", gpgme_strerror(error));
+        return FALSE;
+    }
+
+    gpgme_data_new_from_mem(&gpgme_data, (gchar*)key, size, 0);
+    error = gpgme_op_import(ctx, gpgme_data);
+    if (error != GPG_ERR_NO_ERROR) {
+        log_error("Failed to import key");
+    }
+
+    return TRUE;
+}
diff --git a/src/pgp/gpg.h b/src/pgp/gpg.h
index c7ca7e8f..090ec83b 100644
--- a/src/pgp/gpg.h
+++ b/src/pgp/gpg.h
@@ -79,6 +79,7 @@ char* p_ox_gpg_signcrypt(const char* const sender_barejid, const char* const rec
 char* p_ox_gpg_decrypt(char* base64);
 
 void p_ox_gpg_readkey(const char* const filename, char** key, char** fp);
+gboolean p_ox_gpg_import(char* base64_public_key);
 
 /*!
  * \brief List of public keys with xmpp-URI.
diff --git a/src/xmpp/ox.c b/src/xmpp/ox.c
index 1727bc34..1f5299bf 100644
--- a/src/xmpp/ox.c
+++ b/src/xmpp/ox.c
@@ -44,6 +44,10 @@
 
 #ifdef HAVE_LIBGPGME
 static void _ox_metadata_node__public_key(const char* const fingerprint);
+static int _ox_metadata_result(xmpp_conn_t* const conn, xmpp_stanza_t* const stanza, void* const userdata);
+
+static void _ox_request_public_key(const char* const jid, const char* const fingerprint);
+static int _ox_public_key_result(xmpp_conn_t* const conn, xmpp_stanza_t* const stanza, void* const userdata);
 
 /*!
  * \brief Current Date and Time.
@@ -62,7 +66,7 @@ static char* _gettimestamp();
 <pre>
 <iq type='set' from='juliet@example.org/balcony' id='publish1'>
   <pubsub xmlns='http://jabber.org/protocol/pubsub'>
-    <publish node='urn:xmpp:openpgp:0:public-keys:1357B01865B2503C18453D208CAC2A9678548E35'>
+    <publish node='urn:xmpp:openpgp:0:public-keys:123456789ABCDEF1234567891238484848484848'>
       <item id='2020-01-21T10:46:21Z'>
         <pubkey xmlns='urn:xmpp:openpgp:0'>
            <data>
@@ -139,6 +143,56 @@ ox_announce_public_key(const char* const filename)
 }
 
 /*!
+ * <pre>
+
+<iq from='romeo@example.org/orchard'
+    to='juliet@example.org'
+    type='get'
+    id='getmeta'>
+  <pubsub xmlns='http://jabber.org/protocol/pubsub'>
+    <items node='urn:xmpp:openpgp:0:public-keys'/>
+  </pubsub>
+</iq>
+
+ * </pre>
+ *
+*/
+
+void
+ox_discover_public_key(const char* const jid)
+{
+    assert(jid);
+    log_info("Discovering Public Key for %s", jid);
+    cons_show("Discovering Public Key for %s", jid);
+    // iq
+    xmpp_ctx_t* const ctx = connection_get_ctx();
+    char* id = xmpp_uuid_gen(ctx);
+    xmpp_stanza_t* iq = xmpp_iq_new(ctx, STANZA_TYPE_GET, id);
+    xmpp_stanza_set_from(iq, xmpp_conn_get_jid(connection_get_conn()));
+    xmpp_stanza_set_to(iq, jid);
+    // pubsub
+    xmpp_stanza_t* pubsub = xmpp_stanza_new(ctx);
+    xmpp_stanza_set_name(pubsub, STANZA_NAME_PUBSUB);
+    xmpp_stanza_set_ns(pubsub, XMPP_FEATURE_PUBSUB);
+    // items
+    xmpp_stanza_t* items = xmpp_stanza_new(ctx);
+    xmpp_stanza_set_name(items, STANZA_NAME_ITEMS);
+    xmpp_stanza_set_attribute(items, STANZA_ATTR_NODE, STANZA_NS_OPENPGP_0_PUBLIC_KEYS);
+
+    xmpp_stanza_add_child(pubsub, items);
+    xmpp_stanza_add_child(iq, pubsub);
+
+    xmpp_id_handler_add(connection_get_conn(), _ox_metadata_result, id, strdup(jid));
+    xmpp_send(connection_get_conn(), iq);
+}
+
+void
+ox_request_public_key(const char* const jid, const char* const fingerprint)
+{
+    _ox_request_public_key(jid, fingerprint);
+}
+
+/*!
  *
  *
  *
@@ -149,11 +203,11 @@ ox_announce_public_key(const char* const filename)
       <item>
         <public-keys-list xmlns='urn:xmpp:openpgp:0'>
           <pubkey-metadata
-            v4-fingerprint='1357B01865B2503C18453D208CAC2A9678548E35'
+            v4-fingerprint='1234512345678122ABCDE2222222222222222222'
             date='2018-03-01T15:26:12Z'
             />
           <pubkey-metadata
-            v4-fingerprint='67819B343B2AB70DED9320872C6464AF2A8E4C02'
+            v4-fingerprint='1234ABCD1234409865ABCD234482728939483472'
             date='1953-05-16T12:00:00Z'
             />
         </public-keys-list>
@@ -205,6 +259,178 @@ _ox_metadata_node__public_key(const char* const fingerprint)
     xmpp_send(connection_get_conn(), iq);
 }
 
+static int
+_ox_metadata_result(xmpp_conn_t* const conn, xmpp_stanza_t* const stanza, void* const userdata)
+{
+    log_debug("OX: Processing result %s's meatadata.", (char*)userdata);
+
+    if (g_strcmp0(xmpp_stanza_get_type(stanza), "result") != 0) {
+        cons_show("OX: Fehler");
+        return FALSE;
+    }
+    // pubsub
+    xmpp_stanza_t* pubsub = stanza_get_child_by_name_and_ns(stanza, STANZA_NAME_PUBSUB, XMPP_FEATURE_PUBSUB);
+    if (!pubsub) {
+        cons_show("OX: Fehler - Kein pubsub");
+        return FALSE;
+    }
+
+    xmpp_stanza_t* items = xmpp_stanza_get_child_by_name(pubsub, STANZA_NAME_ITEMS);
+    if (!items) {
+        cons_show("OX: Fehler - Kein items");
+        return FALSE;
+    }
+
+    xmpp_stanza_t* item = xmpp_stanza_get_child_by_name(items, STANZA_NAME_ITEM);
+    if (!item) {
+        cons_show("OX: Fehler - Kein item");
+        return FALSE;
+    }
+
+    xmpp_stanza_t* publickeyslist = stanza_get_child_by_name_and_ns(item, STANZA_NAME_PUBLIC_KEYS_LIST, STANZA_NS_OPENPGP_0);
+    if (!publickeyslist) {
+        cons_show("OX: Fehler - Kein publickeyslist");
+        return FALSE;
+    }
+
+    xmpp_stanza_t* pubkeymetadata = xmpp_stanza_get_children(publickeyslist);
+
+    while (pubkeymetadata) {
+        const char* fingerprint = xmpp_stanza_get_attribute(pubkeymetadata, STANZA_ATTR_V4_FINGERPRINT);
+        cons_show(fingerprint);
+        pubkeymetadata = xmpp_stanza_get_next(pubkeymetadata);
+    }
+
+    return FALSE;
+}
+
+/*!
+ *
+ * <pre>
+
+<iq from='romeo@example.org/orchard'
+    to='juliet@example.org'
+    type='get'
+    id='getpub'>
+  <pubsub xmlns='http://jabber.org/protocol/pubsub'>
+    <items node='urn:xmpp:openpgp:0:public-keys:1234567890ABCDF12349ABCD1293848292983833'
+           max_items='1'/>
+  </pubsub>
+</iq>
+
+ * </pre>
+ */
+
+void
+_ox_request_public_key(const char* const jid, const char* const fingerprint)
+{
+    assert(jid);
+    assert(fingerprint);
+    assert(strlen(fingerprint) == 40);
+    cons_show("Requesting Public Key %s for %s", fingerprint, jid);
+    log_info("OX: Request %s's public key %s.", jid, fingerprint);
+    // iq
+    xmpp_ctx_t* const ctx = connection_get_ctx();
+    char* id = xmpp_uuid_gen(ctx);
+    xmpp_stanza_t* iq = xmpp_iq_new(ctx, STANZA_TYPE_GET, id);
+    xmpp_stanza_set_from(iq, xmpp_conn_get_jid(connection_get_conn()));
+    xmpp_stanza_set_to(iq, jid);
+    // pubsub
+    xmpp_stanza_t* pubsub = xmpp_stanza_new(ctx);
+    xmpp_stanza_set_name(pubsub, STANZA_NAME_PUBSUB);
+    xmpp_stanza_set_ns(pubsub, XMPP_FEATURE_PUBSUB);
+    // items
+    GString* node_name = g_string_new(STANZA_NS_OPENPGP_0_PUBLIC_KEYS);
+    g_string_append(node_name, ":");
+    g_string_append(node_name, fingerprint);
+
+    xmpp_stanza_t* items = xmpp_stanza_new(ctx);
+    xmpp_stanza_set_name(items, STANZA_NAME_ITEMS);
+    xmpp_stanza_set_attribute(items, STANZA_ATTR_NODE, node_name->str);
+    xmpp_stanza_set_attribute(items, "max_items", "1");
+
+    xmpp_stanza_add_child(pubsub, items);
+    xmpp_stanza_add_child(iq, pubsub);
+
+    xmpp_id_handler_add(connection_get_conn(), _ox_public_key_result, id, NULL);
+
+    xmpp_send(connection_get_conn(), iq);
+}
+
+/*!
+ *
+ * <pre>
+
+<iq from='juliet@example.org'
+    to='romeo@example.org/orchard'
+    type='result'
+    id='getpub'>
+  <pubsub xmlns='http://jabber.org/protocol/pubsub'>
+    <items node='urn:xmpp:openpgp:0:public-keys:123454678819283823ABCDEF1234566789001234'>
+      <item id='2020-01-21T10:46:21Z'>
+        <pubkey xmlns='urn:xmpp:openpgp:0'>
+          <data>
+            BASE64_OPENPGP_PUBLIC_KEY
+          </data>
+        </pubkey>
+      </item>
+    </items>
+  </pubsub>
+</iq>
+
+ * </pre>
+ */
+
+int
+_ox_public_key_result(xmpp_conn_t* const conn, xmpp_stanza_t* const stanza, void* const userdata)
+{
+    log_debug("OX: Processing result public key");
+
+    if (g_strcmp0(xmpp_stanza_get_type(stanza), "result") != 0) {
+        cons_show("Public Key import failed. Check log for details.");
+        log_error("OX: Public Key response type is wrong");
+        return FALSE;
+    }
+    // pubsub
+    xmpp_stanza_t* pubsub = stanza_get_child_by_name_and_ns(stanza, STANZA_NAME_PUBSUB, XMPP_FEATURE_PUBSUB);
+    if (!pubsub) {
+        cons_show("Public Key import failed. Check log for details.");
+        log_error("OX: Public key request response failed: No <pubsub/>");
+        return FALSE;
+    }
+
+    xmpp_stanza_t* items = xmpp_stanza_get_child_by_name(pubsub, STANZA_NAME_ITEMS);
+    if (!items) {
+        cons_show("Public Key import failed. Check log for details.");
+        log_error("OX: Public key request response failed: No <items/>");
+        return FALSE;
+    }
+
+    xmpp_stanza_t* item = xmpp_stanza_get_child_by_name(items, STANZA_NAME_ITEM);
+    if (!item) {
+        cons_show("Public Key import failed. Check log for details.");
+        log_error("OX: Public key request response failed: No <item/>");
+        return FALSE;
+    }
+
+    xmpp_stanza_t* pubkey = stanza_get_child_by_name_and_ns(item, STANZA_NAME_PUPKEY, STANZA_NS_OPENPGP_0);
+    if (!pubkey) {
+        cons_show("Public Key import failed. Check log for details.");
+        log_error("OX: Public key request response failed: No <pubkey/>");
+        return FALSE;
+    }
+    xmpp_stanza_t* data = xmpp_stanza_get_child_by_name(pubkey, STANZA_NAME_DATA);
+    char* base64_data = xmpp_stanza_get_text(data);
+    log_debug("Key data: %s", base64_data);
+    if (p_ox_gpg_import(base64_data)) {
+        cons_show("Public Key imported");
+    } else {
+        cons_show("Public Key import failed. Check log for details.");
+    }
+
+    return FALSE;
+}
+
 // Date and Time (XEP-0082)
 char*
 _gettimestamp()
diff --git a/src/xmpp/ox.h b/src/xmpp/ox.h
index 51435bb5..143cce95 100644
--- a/src/xmpp/ox.h
+++ b/src/xmpp/ox.h
@@ -56,3 +56,15 @@
  */
 
 gboolean ox_announce_public_key(const char* const filename);
+
+/*!
+ * \brief Discovering Public Keys of a User.
+ *
+ * Reads the public key from a JIDs PEP.
+ *
+ * \param jid JID
+ */
+
+void ox_discover_public_key(const char* const jid);
+
+void ox_request_public_key(const char* const jid, const char* const fingerprint);
diff --git a/tests/unittests/xmpp/stub_ox.c b/tests/unittests/xmpp/stub_ox.c
index ce5532ae..ff99c633 100644
--- a/tests/unittests/xmpp/stub_ox.c
+++ b/tests/unittests/xmpp/stub_ox.c
@@ -7,3 +7,13 @@ ox_announce_public_key(const char* const filename)
 {
     return FALSE;
 }
+
+void
+ox_discover_public_key(const char* const jid)
+{
+}
+
+void
+ox_request_public_key(const char* const jid, const char* const fingerprint)
+{
+}