/* * ox.c * vim: expandtab:ts=4:sts=4:sw=4 * * Copyright (C) 2020 Stefan Kropp * * This file is part of Profanity. * * Profanity is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Profanity is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Profanity. If not, see . * * In addition, as a special exception, the copyright holders give permission to * link the code of portions of this program with the OpenSSL library under * certain conditions as described in each individual source file, and * distribute linked combinations including the two. * * You must obey the GNU General Public License in all respects for all of the * code used other than OpenSSL. If you modify file(s) with this exception, you * may extend this exception to your version of the file(s), but you are not * obligated to do so. If you do not wish to do so, delete this exception * statement from your version. If you delete this exception statement from all * source files in the program, then also delete it here. * */ #include "config.h" #include #include #include "log.h" #include "ui/ui.h" #include "xmpp/connection.h" #include "xmpp/stanza.h" #include "xmpp/iq.h" #include "pgp/gpg.h" #ifdef HAVE_LIBGPGME #define KEYID_LENGTH 40 static void _ox_metadata_node__public_key(const char* const fingerprint); static int _ox_metadata_result(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_stanza_t* const stanza, void* const userdata); /* Return Current Date and Time. * * XEP-0082: XMPP Date and Time Profiles * https://xmpp.org/extensions/xep-0082.html * * According to ISO8601 * YYYY-MM-DDThh:mm:ssZ */ static char* _gettimestamp(); /*! *

  
    
      
        
           
             BASE64_OPENPGP_PUBLIC_KEY
           
        
      
    
  

* */ gboolean ox_announce_public_key(const char* const filename) { assert(filename); cons_show("Announce OpenPGP Key for OX %s ...", filename); log_info("[OX] Announce OpenPGP Key of OX: %s", filename); // key the key and the fingerprint via GnuPG from file char* key = NULL; char* fp = NULL; p_ox_gpg_readkey(filename, &key, &fp); if (!(key && fp)) { cons_show("Error during OpenPGP OX announce. See log file for more information"); return FALSE; } log_info("[OX] Announce OpenPGP Key for Fingerprint: %s", fp); xmpp_ctx_t* const ctx = connection_get_ctx(); char* id = connection_create_stanza_id(); xmpp_stanza_t* iq = xmpp_iq_new(ctx, STANZA_TYPE_SET, id); xmpp_stanza_set_from(iq, xmpp_conn_get_jid(connection_get_conn())); xmpp_stanza_t* pubsub = xmpp_stanza_new(ctx); xmpp_stanza_set_name(pubsub, STANZA_NAME_PUBSUB); xmpp_stanza_set_ns(pubsub, XMPP_FEATURE_PUBSUB); GString* node_name = g_string_new(STANZA_NS_OPENPGP_0_PUBLIC_KEYS); g_string_append(node_name, ":"); g_string_append(node_name, fp); xmpp_stanza_t* publish = xmpp_stanza_new(ctx); xmpp_stanza_set_name(publish, STANZA_NAME_PUBLISH); xmpp_stanza_set_attribute(publish, STANZA_ATTR_NODE, node_name->str); xmpp_stanza_t* item = xmpp_stanza_new(ctx); xmpp_stanza_set_name(item, STANZA_NAME_ITEM); char* timestamp = _gettimestamp(); xmpp_stanza_set_attribute(item, STANZA_ATTR_ID, timestamp); free(timestamp); xmpp_stanza_t* pubkey = xmpp_stanza_new(ctx); xmpp_stanza_set_name(pubkey, STANZA_NAME_PUPKEY); xmpp_stanza_set_ns(pubkey, STANZA_NS_OPENPGP_0); xmpp_stanza_t* data = xmpp_stanza_new(ctx); xmpp_stanza_set_name(data, STANZA_NAME_DATA); xmpp_stanza_t* keydata = xmpp_stanza_new(ctx); xmpp_stanza_set_text(keydata, key); xmpp_stanza_add_child(data, keydata); xmpp_stanza_add_child(pubkey, data); xmpp_stanza_add_child(item, pubkey); xmpp_stanza_add_child(publish, item); xmpp_stanza_add_child(pubsub, publish); xmpp_stanza_add_child(iq, pubsub); if (connection_supports(XMPP_FEATURE_PUBSUB_PUBLISH_OPTIONS)) { stanza_attach_publish_options(ctx, iq, "pubsub#access_model", "open"); } else { log_debug("[OX] Cannot publish public key: no PUBSUB feature announced"); } iq_send_stanza(iq); xmpp_stanza_release(iq); _ox_metadata_node__public_key(fp); return TRUE; } /*! *


  
    
  


 * 
* */ void ox_discover_public_key(const char* const jid) { assert(jid && strlen(jid) > 0); log_info("[OX] 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 = connection_create_stanza_id(); 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); iq_id_handler_add(xmpp_stanza_get_id(iq), _ox_metadata_result, NULL, NULL); iq_send_stanza(iq); xmpp_stanza_release(iq); } void ox_request_public_key(const char* const jid, const char* const fingerprint) { _ox_request_public_key(jid, fingerprint); } /*! * * *

  
    
      
        
          
          
        
      
    
  

* */ void _ox_metadata_node__public_key(const char* const fingerprint) { log_info("Announce OpenPGP metadata: %s", fingerprint); assert(fingerprint); assert(strlen(fingerprint) == KEYID_LENGTH); // iq xmpp_ctx_t* const ctx = connection_get_ctx(); char* id = connection_create_stanza_id(); xmpp_stanza_t* iq = xmpp_iq_new(ctx, STANZA_TYPE_SET, id); xmpp_stanza_set_from(iq, xmpp_conn_get_jid(connection_get_conn())); // 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); // publish xmpp_stanza_t* publish = xmpp_stanza_new(ctx); xmpp_stanza_set_name(publish, STANZA_NAME_PUBLISH); xmpp_stanza_set_attribute(publish, STANZA_ATTR_NODE, STANZA_NS_OPENPGP_0_PUBLIC_KEYS); // item xmpp_stanza_t* item = xmpp_stanza_new(ctx); xmpp_stanza_set_name(item, STANZA_NAME_ITEM); // public-keys-list xmpp_stanza_t* publickeyslist = xmpp_stanza_new(ctx); xmpp_stanza_set_name(publickeyslist, STANZA_NAME_PUBLIC_KEYS_LIST); xmpp_stanza_set_ns(publickeyslist, STANZA_NS_OPENPGP_0); // pubkey-metadata xmpp_stanza_t* pubkeymetadata = xmpp_stanza_new(ctx); xmpp_stanza_set_name(pubkeymetadata, STANZA_NAME_PUBKEY_METADATA); xmpp_stanza_set_attribute(pubkeymetadata, STANZA_ATTR_V4_FINGERPRINT, fingerprint); char* timestamp = _gettimestamp(); xmpp_stanza_set_attribute(pubkeymetadata, STANZA_ATTR_DATE, timestamp); free(timestamp); xmpp_stanza_add_child(publickeyslist, pubkeymetadata); xmpp_stanza_add_child(item, publickeyslist); xmpp_stanza_add_child(publish, item); xmpp_stanza_add_child(pubsub, publish); xmpp_stanza_add_child(iq, pubsub); if (connection_supports(XMPP_FEATURE_PUBSUB_PUBLISH_OPTIONS)) { stanza_attach_publish_options(ctx, iq, "pubsub#access_model", "open"); } else { log_debug("[OX] Cannot publish public key: no PUBSUB feature announced"); } iq_send_stanza(iq); xmpp_stanza_release(iq); } static int _ox_metadata_result(xmpp_stanza_t* const stanza, void* const userdata) { log_debug("[OX] Processing result %s's metadata.", (char*)userdata); if (g_strcmp0(xmpp_stanza_get_type(stanza), "result") != 0) { log_debug("[OX] Error: Unable to load metadata of user %s - Not a stanza result type", (char*)userdata); return FALSE; } // pubsub xmpp_stanza_t* pubsub = xmpp_stanza_get_child_by_name_and_ns(stanza, STANZA_NAME_PUBSUB, XMPP_FEATURE_PUBSUB); if (!pubsub) { cons_show("[OX] Error: No pubsub"); return FALSE; } xmpp_stanza_t* items = xmpp_stanza_get_child_by_name(pubsub, STANZA_NAME_ITEMS); if (!items) { cons_show("[OX] Error: No items"); return FALSE; } xmpp_stanza_t* item = xmpp_stanza_get_child_by_name(items, STANZA_NAME_ITEM); if (!item) { cons_show("[OX] Error: No item"); return FALSE; } xmpp_stanza_t* publickeyslist = xmpp_stanza_get_child_by_name_and_ns(item, STANZA_NAME_PUBLIC_KEYS_LIST, STANZA_NS_OPENPGP_0); if (!publickeyslist) { cons_show("[OX] Error: No 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); if (fingerprint) { if (strlen(fingerprint) == KEYID_LENGTH) { cons_show(fingerprint); } else { cons_show("OX: Wrong char size of public key"); log_error("[OX] Wrong chat size of public key %s", 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); if (strlen(fingerprint) != KEYID_LENGTH) { cons_show_error("Invalid fingerprint length %s for %s", fingerprint, jid); return; } 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 = connection_create_stanza_id(); 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); iq_id_handler_add(xmpp_stanza_get_id(iq), _ox_public_key_result, NULL, NULL); iq_send_stanza(iq); xmpp_stanza_release(iq); } /*! * *


  
    
      
        
          
            BASE64_OPENPGP_PUBLIC_KEY
          
        
      
    
  


 * 
*/ int _ox_public_key_result(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 = xmpp_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 = xmpp_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); if (!data) { log_error("[OX] No data"); } char* base64_data = xmpp_stanza_get_text(data); if (base64_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."); } free(base64_data); } return FALSE; } // Date and Time (XEP-0082) char* _gettimestamp() { GDateTime* dt = g_date_time_new_now_local(); gchar* datestr = g_date_time_format(dt, "%FT%TZ"); g_date_time_unref(dt); return datestr; } #endif // HAVE_LIBGPGME