diff options
Diffstat (limited to 'src/pgp')
-rw-r--r-- | src/pgp/gpg.c | 361 | ||||
-rw-r--r-- | src/pgp/gpg.h | 16 |
2 files changed, 364 insertions, 13 deletions
diff --git a/src/pgp/gpg.c b/src/pgp/gpg.c index 528b772e..d0834923 100644 --- a/src/pgp/gpg.c +++ b/src/pgp/gpg.c @@ -72,6 +72,9 @@ static char* _remove_header_footer(char *str, const char *const footer); static char* _add_header_footer(const char *const str, const char *const header, const char *const footer); static void _save_pubkeys(void); +static gpgme_key_t _ox_key_lookup(const char *const barejid, gboolean secret_only); +static gboolean _ox_key_is_usable(gpgme_key_t key, const char *const barejid, gboolean secret); + void _p_gpg_free_pubkeyid(ProfPGPPubKeyId *pubkeyid) { @@ -158,30 +161,26 @@ p_gpg_close(void) void p_gpg_on_connect(const char *const barejid) { - char *pgpdir = files_get_data_path(DIR_PGP); - GString *pubsfile = g_string_new(pgpdir); - free(pgpdir); - gchar *account_dir = str_replace(barejid, "@", "_at_"); - g_string_append(pubsfile, "/"); - g_string_append(pubsfile, account_dir); - free(account_dir); + gchar* pubsfile = files_get_account_data_path(DIR_PGP, barejid); // mkdir if doesn't exist for account errno = 0; - int res = g_mkdir_with_parents(pubsfile->str, S_IRWXU); + int res = g_mkdir_with_parents(pubsfile, S_IRWXU); if (res == -1) { char *errmsg = strerror(errno); if (errmsg) { - log_error("Error creating directory: %s, %s", pubsfile->str, errmsg); + log_error("Error creating directory: %s, %s", pubsfile, errmsg); } else { - log_error("Error creating directory: %s", pubsfile->str); + log_error("Error creating directory: %s", pubsfile); } } // create or read publickeys - g_string_append(pubsfile, "/pubkeys"); - pubsloc = pubsfile->str; - g_string_free(pubsfile, FALSE); + GString *pubtmp = g_string_new(pubsfile); + g_string_append(pubtmp, "/pubkeys"); + pubsloc = pubtmp->str; + g_string_free(pubtmp, FALSE); + g_free(pubsfile); if (g_file_test(pubsloc, G_FILE_TEST_EXISTS)) { g_chmod(pubsloc, S_IRUSR | S_IWUSR); @@ -787,6 +786,219 @@ p_gpg_format_fp_str(char *fp) return result; } +/*! + * \brief Public keys with XMPP-URI. + * + * This function will look for all public key with a XMPP-URI as UID. + * + */ +GHashTable* ox_gpg_public_keys(void){ + gpgme_error_t error; + GHashTable *result = g_hash_table_new_full(g_str_hash, g_str_equal, free, (GDestroyNotify)_p_gpg_free_key); + + gpgme_ctx_t ctx; + error = gpgme_new(&ctx); + + if (error) { + log_error("OX - gpgme_new failed: %s %s", gpgme_strsource(error), gpgme_strerror(error)); + return NULL; + } + + error = gpgme_op_keylist_start(ctx, NULL, 0); // all public keys + if (error == GPG_ERR_NO_ERROR) { + gpgme_key_t key; + error = gpgme_op_keylist_next(ctx, &key); + if ( error != GPG_ERR_EOF && error != GPG_ERR_NO_ERROR) { + log_error("OX: gpgme_op_keylist_next %s %s", gpgme_strsource(error), gpgme_strerror(error)); + g_hash_table_destroy(result); + return NULL; + } + while (!error) { + // Looking for XMPP URI UID + gpgme_user_id_t uid = key->uids; + gpgme_user_id_t xmppid = NULL; + while (!xmppid && uid) { + if( uid->name && strlen(uid->name) >= 10 ) { + if( strstr(uid->name, "xmpp:") == uid->name ) { + xmppid = uid; + } + } + uid = uid->next; + } + + if(xmppid) { + // Build Key information about all subkey + gpgme_subkey_t sub = key->subkeys; + + ProfPGPKey *p_pgpkey = _p_gpg_key_new(); + p_pgpkey->id = strdup(sub->keyid); + p_pgpkey->name = strdup(xmppid->uid); + p_pgpkey->fp = strdup(sub->fpr); + if (sub->can_encrypt) p_pgpkey->encrypt = TRUE; + if (sub->can_authenticate) p_pgpkey->authenticate = TRUE; + if (sub->can_certify) p_pgpkey->certify = TRUE; + if (sub->can_sign) p_pgpkey->sign = TRUE; + + sub = sub->next; + while (sub) { + if (sub->can_encrypt) p_pgpkey->encrypt = TRUE; + if (sub->can_authenticate) p_pgpkey->authenticate = TRUE; + if (sub->can_certify) p_pgpkey->certify = TRUE; + if (sub->can_sign) p_pgpkey->sign = TRUE; + + sub = sub->next; + } + + g_hash_table_insert(result, strdup(p_pgpkey->name), p_pgpkey); + + } + gpgme_key_unref(key); + error = gpgme_op_keylist_next(ctx, &key); + } + } + gpgme_release(ctx); + + //autocomplete_clear(key_ac); + // GList *ids = g_hash_table_get_keys(result); + // GList *curr = ids; + // while (curr) { + // ProfPGPKey *key = g_hash_table_lookup(result, curr->data); + // autocomplete_add(key_ac, key->id); + // curr = curr->next; + // } + // g_list_free(ids); + + return result; + +} + +char* p_ox_gpg_signcrypt(const char* const sender_barejid, const char* const recipient_barejid , const char* const message) { + 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 ) { + printf("gpgme_new: %d\n", error); + return NULL; + } + error = gpgme_set_protocol(ctx, GPGME_PROTOCOL_OPENPGP); + if(error != 0) { + log_error("GpgME Error: %s", gpgme_strerror(error)); + } + gpgme_set_armor(ctx,0); + gpgme_set_textmode(ctx,0); + gpgme_set_offline(ctx,1); + gpgme_set_keylist_mode(ctx, GPGME_KEYLIST_MODE_LOCAL); + if(error != 0) { + log_error("GpgME Error: %s", gpgme_strerror(error)); + } + + gpgme_key_t recp[3]; + recp[0] = NULL, + recp[1] = NULL; + + char* xmpp_jid_me = alloca( (strlen(sender_barejid)+6) * sizeof(char) ); + char* xmpp_jid_recipient = alloca( (strlen(recipient_barejid)+6) * sizeof(char) ); + + strcpy(xmpp_jid_me, "xmpp:"); + strcpy(xmpp_jid_recipient, "xmpp:"); + strcat(xmpp_jid_me, sender_barejid); + strcat(xmpp_jid_recipient,recipient_barejid); + + gpgme_signers_clear(ctx); + + // lookup own key + recp[0] = _ox_key_lookup(sender_barejid, TRUE); + if(error != 0) { + log_error("Key not found for %s. GpgME Error: %s", xmpp_jid_me, gpgme_strerror(error)); + return NULL; + } + + error = gpgme_signers_add(ctx,recp[0]); + if(error != 0) { + log_error("gpgme_signers_add %s. GpgME Error: %s", xmpp_jid_me, gpgme_strerror(error)); + return NULL; + } + + + // lookup key of recipient + recp[1] = _ox_key_lookup(recipient_barejid, FALSE); + if(error != 0) { + log_error("Key not found for %s. GpgME Error: %s", xmpp_jid_recipient, gpgme_strerror(error)); + return NULL; + } + recp[2] = NULL; + log_debug("%s <%s>", recp[0]->uids->name, recp[0]->uids->email); + log_debug("%s <%s>", recp[1]->uids->name, recp[1]->uids->email); + + gpgme_encrypt_flags_t flags = 0; + + gpgme_data_t plain; + gpgme_data_t cipher; + + error = gpgme_data_new (&plain); + if(error != 0) { + log_error("GpgME Error: %s", gpgme_strerror(error)); + return NULL; + } + + error = gpgme_data_new_from_mem(&plain, message, strlen(message),0); + if(error != 0) { + log_error("GpgME Error: %s", gpgme_strerror(error)); + return NULL; + } + error = gpgme_data_new (&cipher); + if(error != 0) { + log_error("GpgME Error: %s", gpgme_strerror(error)); + return NULL; + } + + error = gpgme_op_encrypt_sign ( ctx, recp, flags, plain, cipher); + if(error != 0) { + log_error("GpgME Error: %s", gpgme_strerror(error)); + return NULL; + } + + size_t len; + char *cipher_str = gpgme_data_release_and_get_mem(cipher, &len); + char* result = g_base64_encode( (unsigned char*) cipher_str,len); + gpgme_key_release (recp[0]); + gpgme_key_release (recp[1]); + gpgme_release (ctx); + return result; + +} + +gboolean ox_is_private_key_available(const char *const barejid) { + g_assert(barejid); + gboolean result = FALSE; + + gpgme_key_t key = _ox_key_lookup(barejid, TRUE); + if(key) { + if (_ox_key_is_usable(key, barejid, TRUE) ) { + result = TRUE; + } + gpgme_key_unref(key); + } + + return result; +} + +gboolean ox_is_public_key_available(const char *const barejid) { + g_assert(barejid); + gboolean result = FALSE; + gpgme_key_t key = _ox_key_lookup(barejid, FALSE); + if(key) { + if (_ox_key_is_usable(key, barejid, FALSE) ) { + result = TRUE; + } + gpgme_key_unref(key); + } + return result; +} + + static char* _remove_header_footer(char *str, const char *const footer) { @@ -837,3 +1049,126 @@ _save_pubkeys(void) g_chmod(pubsloc, S_IRUSR | S_IWUSR); g_free(g_pubkeys_data); } + +static gpgme_key_t _ox_key_lookup(const char *const barejid, gboolean secret_only) { + g_assert(barejid); + log_debug("Looking for %s key: %s", secret_only == TRUE ? "Private" : "Public", barejid); + gpgme_key_t key = NULL; + gpgme_error_t error; + + gpgme_ctx_t ctx; + error = gpgme_new(&ctx); + + if (error) { + log_error("OX - gpgme_new failed: %s %s", gpgme_strsource(error), gpgme_strerror(error)); + return NULL; + } + + error = gpgme_op_keylist_start(ctx, NULL, secret_only); + if (error == GPG_ERR_NO_ERROR) { + error = gpgme_op_keylist_next(ctx, &key); + if ( error != GPG_ERR_EOF && error != GPG_ERR_NO_ERROR) { + log_error("OX: gpgme_op_keylist_next %s %s", gpgme_strsource(error), gpgme_strerror(error)); + return NULL; + } + + GString* xmppuri = g_string_new("xmpp:"); + g_string_append(xmppuri,barejid); + + while (!error) { + // Looking for XMPP URI UID + gpgme_user_id_t uid = key->uids; + + while ( uid ) { + if( uid->name && strlen(uid->name) >= 10 ) { + if( g_strcmp0(uid->name, xmppuri->str) == 0 ) { + gpgme_release(ctx); + return key; + } + } + uid = uid->next; + } + gpgme_key_unref(key); + error = gpgme_op_keylist_next(ctx, &key); + } + } + gpgme_release(ctx); + + return key; +} + +static gboolean _ox_key_is_usable(gpgme_key_t key, const char *const barejid, gboolean secret) { + gboolean result = TRUE; + + if(key->revoked) result = FALSE; + if(key->expired) result = FALSE; + if(key->disabled) result = FALSE; + + return result; +} + +/*! + * @brief XMPP-OX: Decrypt OX Message. + * + * + * + * @param base64 base64_encode OpenPGP message. + * + * @result decrypt XMPP OX Message NULL terminated C-String + */ +char* p_ox_gpg_decrypt(char* base64) { + 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 ) { + printf("gpgme_new: %d\n", error); + return NULL; + } + error = gpgme_set_protocol(ctx, GPGME_PROTOCOL_OPENPGP); + if(error != 0) { + log_error("GpgME Error: %s", gpgme_strerror(error)); + } + gpgme_set_armor(ctx,0); + gpgme_set_textmode(ctx,0); + gpgme_set_offline(ctx,1); + gpgme_set_keylist_mode(ctx, GPGME_KEYLIST_MODE_LOCAL); + if(error != 0) { + log_error("GpgME Error: %s", gpgme_strerror(error)); + } + + gpgme_data_t plain = NULL; + gpgme_data_t cipher = NULL; + + gsize s; + guchar* encypted = g_base64_decode(base64, &s); + error = gpgme_data_new_from_mem(&cipher, (char*)encypted, s,0); + if(error != 0) { + log_error("GpgME Error gpgme_data_new_from_mem: %s", gpgme_strerror(error)); + return NULL; + } + + error = gpgme_data_new (&plain); + if(error != 0) { + log_error("GpgME Error: %s", gpgme_strerror(error)); + return NULL; + } + + error = gpgme_op_decrypt_verify(ctx, cipher, plain); + if(error != 0) { + log_error("GpgME Error gpgme_op_decrypt: %s", gpgme_strerror(error)); + error = gpgme_op_decrypt(ctx, cipher, plain); + if ( error != 0 ) { + return NULL; + } + } + size_t len; + char *plain_str = gpgme_data_release_and_get_mem(plain, &len); + char* result = malloc(len+1); + strcpy(result, plain_str); + result[len] = '\0'; + return result; +} + + diff --git a/src/pgp/gpg.h b/src/pgp/gpg.h index 0417c8a5..3eae6032 100644 --- a/src/pgp/gpg.h +++ b/src/pgp/gpg.h @@ -72,4 +72,20 @@ char* p_gpg_autocomplete_key(const char *const search_str, gboolean previous, vo void p_gpg_autocomplete_key_reset(void); char* p_gpg_format_fp_str(char *fp); +char* p_ox_gpg_signcrypt(const char* const sender_barejid, const char* const recipient_barejid , const char* const message); + +char* p_ox_gpg_decrypt(char* base64); + +/*! + * \brief List of public keys with xmpp-URI. + * + * @returns GHashTable* with GString* xmpp-uri and ProfPGPKey* value. Empty + * hash, if there is no key. NULL in case of error. + * + */ +GHashTable* ox_gpg_public_keys(void); + +gboolean ox_is_private_key_available(const char *const barejid); +gboolean ox_is_public_key_available(const char *const barejid); + #endif |