From 0552e50c781d9710965c1dab8cd66d37d61ad97e Mon Sep 17 00:00:00 2001 From: DebXWoody Date: Fri, 10 Jul 2020 13:39:34 +0200 Subject: 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 --- src/command/cmd_ac.c | 10 -- src/command/cmd_funcs.c | 12 +++ src/pgp/gpg.c | 44 ++++++++ src/pgp/gpg.h | 1 + src/xmpp/ox.c | 232 ++++++++++++++++++++++++++++++++++++++++- src/xmpp/ox.h | 12 +++ tests/unittests/xmpp/stub_ox.c | 10 ++ 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();
 
   
-    
+    
       
         
            
@@ -138,6 +142,56 @@ ox_announce_public_key(const char* const filename)
     return TRUE;
 }
 
+/*!
+ * 
+
+
+  
+    
+  
+
+
+ * 
+ * +*/ + +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) @@ -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; +} + +/*! + * + *
+
+
+  
+    
+  
+
+
+ * 
+ */ + +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); +} + +/*! + * + *
+
+
+  
+    
+      
+        
+          
+            BASE64_OPENPGP_PUBLIC_KEY
+          
+        
+      
+    
+  
+
+
+ * 
+ */ + +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 "); + 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 "); + 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 "); + 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 "); + 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) +{ +} -- cgit 1.4.1-2-gfad0