Browse Source

A metric buttload of code that I finally need to stop shelving and unshelving - new key reset, with bugs.

add_key_notification
parent
commit
46b0697aa2
8 changed files with 776 additions and 207 deletions
  1. +464
    -149
      src/key_reset.c
  2. +8
    -10
      src/key_reset.h
  3. +0
    -1
      src/message.h
  4. +123
    -33
      src/message_api.c
  5. +171
    -7
      src/pEpEngine.c
  6. +2
    -0
      src/pEpEngine.h
  7. +1
    -0
      src/pEp_internal.h
  8. +7
    -7
      test/src/KeyResetMessageTest.cc

+ 464
- 149
src/key_reset.c View File

@ -7,7 +7,10 @@
#include "key_reset.h"
#include "distribution_codec.h"
#include "map_asn1.h"
#include "keymanagement.h"
#include "baseprotocol.h"
#include "../asn.1/Distribution.h"
#include "Sync_impl.h" // this seems... bad
#include <string.h>
#include <stdlib.h>
@ -17,8 +20,90 @@
#define KEY_RESET_MAJOR_VERSION 1L
#define KEY_RESET_MINOR_VERSION 0L
static PEP_STATUS _generate_keyreset_command_message(PEP_SESSION session,
const pEp_identity* from_ident,
const pEp_identity* to_ident,
const char* old_fpr,
const char* new_fpr,
bool is_private,
message** dst) {
if (!session || !from_ident || !old_fpr || !new_fpr || !dst)
return PEP_ILLEGAL_VALUE;
// safe cast
if (!is_me(session, (pEp_identity*)from_ident))
return PEP_ILLEGAL_VALUE;
PEP_STATUS status = PEP_STATUS_OK;
*dst = NULL;
stringlist_t* keydata = NULL;
// Ok, generate payload here...
pEp_identity* outgoing_ident = identity_dup(from_ident);
if (!outgoing_ident)
return PEP_OUT_OF_MEMORY;
free(outgoing_ident->fpr);
outgoing_ident->fpr = strdup(old_fpr);
if (!outgoing_ident->fpr)
return PEP_OUT_OF_MEMORY;
// Need payload now
keyreset_command* kr_command = new_keyreset_command(outgoing_ident, new_fpr);
keyreset_command_list* kr_list = new_keyreset_command_list(kr_command);
char* payload = NULL;
size_t size = 0;
status = key_reset_commands_to_PER(kr_list, &payload, &size);
message* msg = NULL;
status = base_prepare_message(session, outgoing_ident, to_ident,
BASE_KEYRESET, payload, size, NULL,
&msg);
if (status) {
free(msg);
return status;
}
if (!msg)
return PEP_OUT_OF_MEMORY;
if (!msg->attachments)
return PEP_UNKNOWN_ERROR;
char* key_material = NULL;
size_t datasize = 0;
status = export_key(session, new_fpr, &key_material, &datasize);
if (datasize > 0 && key_material) {
if (status) {
free_stringlist(keydata);
return status;
}
stringlist_add(keydata, key_material);
key_material = NULL;
datasize = 0;
if (is_private) {
status = export_secret_key(session, new_fpr, &key_material, &datasize);
if (status) {
free_stringlist(keydata);
return status;
}
if (datasize > 0 && key_material)
stringlist_add(keydata, key_material);
}
}
// Attach to message
stringlist_t* curr_key = keydata;
for ( ; curr_key && curr_key->value; curr_key = curr_key->next) {
bloblist_add(msg->attachments, curr_key->value, size, "application/pgp-keys",
"file://pEpkey.asc");
}
return status;
}
PEP_STATUS has_key_reset_been_sent(
PEP_SESSION session,
const char* from_addr,
const char* user_id,
const char* revoked_fpr,
bool* contacted)
@ -29,7 +114,7 @@ PEP_STATUS has_key_reset_been_sent(
assert(revoked_fpr);
assert(!EMPTYSTR(user_id));
if (!session || !contacted || EMPTYSTR(revoked_fpr) || EMPTYSTR(user_id))
if (!session || !contacted || EMPTYSTR(from_addr) || EMPTYSTR(revoked_fpr) || EMPTYSTR(user_id))
return PEP_ILLEGAL_VALUE;
*contacted = false;
@ -46,7 +131,9 @@ PEP_STATUS has_key_reset_been_sent(
sqlite3_reset(session->was_id_for_revoke_contacted);
sqlite3_bind_text(session->was_id_for_revoke_contacted, 1, revoked_fpr, -1,
SQLITE_STATIC);
sqlite3_bind_text(session->was_id_for_revoke_contacted, 2, user_id, -1,
sqlite3_bind_text(session->was_id_for_revoke_contacted, 2, from_addr, -1,
SQLITE_STATIC);
sqlite3_bind_text(session->was_id_for_revoke_contacted, 3, user_id, -1,
SQLITE_STATIC);
int result = sqlite3_step(session->was_id_for_revoke_contacted);
switch (result) {
@ -64,26 +151,26 @@ PEP_STATUS has_key_reset_been_sent(
return PEP_STATUS_OK;
}
//static const char *sql_set_revoke_contact_as_notified =
// "insert or replace into revocation_contact_list(fpr, contact_id) values (?1, ?2) ;";
PEP_STATUS set_reset_contact_notified(
PEP_SESSION session,
const char* own_address,
const char* revoke_fpr,
const char* contact_id
)
{
PEP_STATUS status = PEP_STATUS_OK;
assert(session && !EMPTYSTR(revoke_fpr) && !EMPTYSTR(contact_id));
assert(session && !EMPTYSTR(own_address) && !EMPTYSTR(revoke_fpr) && !EMPTYSTR(contact_id));
if (!session || EMPTYSTR(revoke_fpr) || EMPTYSTR(contact_id))
if (!session || EMPTYSTR(own_address) || EMPTYSTR(revoke_fpr) || EMPTYSTR(contact_id))
return PEP_ILLEGAL_VALUE;
sqlite3_reset(session->set_revoke_contact_as_notified);
sqlite3_bind_text(session->set_revoke_contact_as_notified, 1, revoke_fpr, -1,
SQLITE_STATIC);
sqlite3_bind_text(session->set_revoke_contact_as_notified, 2, contact_id, -1,
sqlite3_bind_text(session->set_revoke_contact_as_notified, 2, own_address, -1,
SQLITE_STATIC);
sqlite3_bind_text(session->set_revoke_contact_as_notified, 3, contact_id, -1,
SQLITE_STATIC);
int result;
@ -102,149 +189,266 @@ PEP_STATUS set_reset_contact_notified(
return status;
}
// FIXME: fpr ownership
PEP_STATUS receive_key_reset(PEP_SESSION session,
message* reset_msg) {
if (!session || !reset_msg)
if (!session || !reset_msg || !reset_msg->_sender_fpr)
return PEP_ILLEGAL_VALUE;
pEp_identity* sender_id = reset_msg->from;
if (!sender_id)
return PEP_MALFORMED_KEY_RESET_MSG;
PEP_STATUS status = update_identity(session, sender_id);
if (!sender_id->user_id)
return PEP_UNKNOWN_ERROR;
if (is_me(session, sender_id))
return PEP_ILLEGAL_VALUE;
if (!reset_msg->longmsg || strncmp(reset_msg->longmsg, "OLD: ", 5) != 0)
return PEP_MALFORMED_KEY_RESET_MSG;
PEP_STATUS status = PEP_STATUS_OK;
status = PEP_STATUS_OK;
char* old_fpr = NULL;
char* new_fpr = NULL;
stringlist_t* keylist = NULL;
pEp_identity* temp_ident = identity_dup(sender_id);
if (!temp_ident) {
status = PEP_OUT_OF_MEMORY;
goto pEp_free;
}
char* rest = NULL;
char* p = strtok_r(reset_msg->longmsg, "\r\n", &rest);
if (!EMPTYSTR(p + 5))
old_fpr = strdup(p + 5);
char* sender_fpr = reset_msg->_sender_fpr;
bool revoked = false;
status = key_revoked(session, sender_fpr, &revoked);
if (status != PEP_STATUS_OK)
return status;
// Bail if revoked
if (revoked) {
return PEP_ILLEGAL_VALUE; // could be an attack
}
// Otherwise, bail
else {
status = PEP_MALFORMED_KEY_RESET_MSG;
goto pEp_free;
bool mistrusted = false;
status = is_mistrusted_key(session, sender_fpr, &mistrusted);
if (status != PEP_STATUS_OK)
return status;
if (mistrusted)
return PEP_ILLEGAL_VALUE;
}
bool own_key = false;
status = is_own_key(session, old_fpr, &own_key);
// Parse reset message
if (own_key) {
// Nope, no one can make us our own default. If we want to do that,
// that's keysync, NOT key reset.
status = PEP_ILLEGAL_VALUE;
goto pEp_free;
pEp_identity* sender_id = reset_msg->from;
if (!sender_id)
return PEP_MALFORMED_KEY_RESET_MSG;
if (is_me(session, sender_id)) {
// first off, we need to make sure we're up-to-date
status = myself(session, sender_id);
}
else {
status = update_identity(session, sender_id);
if (!sender_id->user_id)
return PEP_UNKNOWN_ERROR;
}
bool sender_own_key = false;
if (is_me(session, sender_id)) {
// Do own-reset-checks
status = is_own_key(session, sender_fpr, &sender_own_key);
if (status != PEP_STATUS_OK)
return status;
// Should we mistrust the sender_fpr here??
if (!sender_own_key)
return PEP_ILLEGAL_VALUE; // actually, this is an attack
// Make sure it's a TRUSTED own key
char* keyholder = sender_id->fpr;
sender_id->fpr = sender_fpr;
status = get_trust(session, sender_id);
sender_id->fpr = keyholder;
p = strtok_r(NULL, "\r\n", &rest);
if (strncmp(p, "NEW: ", 5) != 0 || EMPTYSTR(p + 5)) {
status = PEP_MALFORMED_KEY_RESET_MSG;
goto pEp_free;
if (sender_id->comm_type < PEP_ct_pEp)
return PEP_ILLEGAL_VALUE;
}
new_fpr = strdup(p + 5);
// Reset the original key
status = key_reset(session, old_fpr, temp_ident, NULL, NULL);
status = PEP_STATUS_OK;
char* old_fpr = NULL;
char* new_fpr = NULL;
size_t size = 0;
const char* payload = NULL;
status = base_extract_message(session,
reset_msg,
BASE_KEYRESET,
&size,
&payload,
NULL);
if (status != PEP_STATUS_OK)
goto pEp_free;
return status;
status = find_keys(session, new_fpr, &keylist);
if (!payload || size == 0)
return PEP_MALFORMED_KEY_RESET_MSG;
keyreset_command_list* resets = NULL;
status = PER_to_key_reset_commands(payload, size, &resets);
if (status != PEP_STATUS_OK)
goto pEp_free;
return status;
if (!keylist) {
status = PEP_KEY_NOT_FOUND;
goto pEp_free;
}
if (!resets)
return PEP_MALFORMED_KEY_RESET_MSG;
// alright, we've checked as best we can. Let's set that baby.
sender_id->fpr = new_fpr;
keyreset_command_list* curr_cl = resets;
// This only sets as the default, does NOT TRUST IN ANY WAY
sender_id->comm_type = sender_id->comm_type & (~PEP_ct_confirmed);
status = set_identity(session, sender_id);
for ( ; curr_cl && curr_cl->command; curr_cl = curr_cl->next) {
keyreset_command* curr_cmd = curr_cl->command;
if (!curr_cmd || !curr_cmd->ident || !curr_cmd->ident->fpr ||
!curr_cmd->ident->address) {
return PEP_MALFORMED_KEY_RESET_MSG;
}
// Make sure that this key is at least one we associate
// with the sender. FIXME: check key election interaction
// N.B. If we ever allow ourselves to send resets to ourselves
// for not-own stuff, this will have to be revised
pEp_identity* curr_ident = curr_cmd->ident;
old_fpr = curr_ident->fpr;
new_fpr = curr_cmd->new_key;
status = find_keys(session, new_fpr, &keylist);
if (status != PEP_STATUS_OK)
goto pEp_free;
if (!keylist) {
status = PEP_MALFORMED_KEY_RESET_MSG;
goto pEp_free;
}
// We need to update the identity to get the user_id
curr_ident->fpr = NULL; // ensure old_fpr is preserved
status = update_identity(session, curr_ident);
// temp fpr set for function call
curr_ident->fpr = (char*)sender_fpr;
status = get_trust(session, curr_ident);
PEP_comm_type ct_result = curr_ident->comm_type;
// Reset fpr on ident struct in case we bail to fulfill ownership contract
curr_ident->fpr = old_fpr;
if (status != PEP_STATUS_OK)
return status;
// Basically, see if fpr is even in the database
// for this user - we'll get PEP_ct_unknown if it isn't
if (ct_result < PEP_ct_strong_but_unconfirmed)
return PEP_KEY_NOT_RESET;
// Now check the fpr we're trying to change (old_fpr), which we reset a few lines above -
// again, if it doesn't belong to the user, we won't use it.
status = get_trust(session, curr_ident);
if (status != PEP_STATUS_OK)
return status;
if (curr_ident->comm_type < PEP_ct_strong_but_unconfirmed)
return PEP_KEY_NOT_RESET;
// Hooray! We apparently now are dealing with keys
// belonging to the user from a message signed by
// the user.
if (!sender_own_key) {
status = key_reset(session, old_fpr, curr_ident);
if (status != PEP_STATUS_OK)
return status;
// Make new key the default
curr_ident->fpr = new_fpr;
sender_id->fpr = NULL; // ownership for free
// This only sets as the default, does NOT TRUST IN ANY WAY
curr_ident->comm_type = curr_ident->comm_type & (~PEP_ct_confirmed);
status = set_identity(session, curr_ident);
if (status != PEP_STATUS_OK)
goto pEp_free;
}
else {
// set new key as the default for this identity
// N.B. If for some reason this is only a pubkey,
// then so be it - but we need to double-check to
// ensure that in this case, we end up with a private one,
// so talk to vb about this.
// Make new key the default
curr_ident->fpr = new_fpr;
status = set_own_key(session, curr_ident, new_fpr);
if (status != PEP_STATUS_OK)
return status;
// key reset on old key
status = key_reset(session, old_fpr, curr_ident);
if (status != PEP_STATUS_OK)
return status;
}
old_fpr = NULL;
new_fpr = NULL;
}
pEp_free:
free_stringlist(keylist);
free(old_fpr);
free(new_fpr);
free_identity(temp_ident);
return status;
}
PEP_STATUS create_standalone_key_reset_message(PEP_SESSION session,
message** dst,
pEp_identity* own_identity,
pEp_identity* recip,
const char* old_fpr,
const char* new_fpr) {
if (!dst || !recip->user_id || !recip->address)
if (!dst || !own_identity || EMPTYSTR(own_identity->address)
|| !recip || EMPTYSTR(recip->user_id)
|| EMPTYSTR(recip->address))
return PEP_ILLEGAL_VALUE;
if (!old_fpr || !new_fpr)
if (EMPTYSTR(old_fpr) || EMPTYSTR(new_fpr))
return PEP_ILLEGAL_VALUE;
*dst = NULL;
// Get own identity user has corresponded with
pEp_identity* own_identity = NULL;
PEP_STATUS status = get_own_ident_for_contact_id(session,
recip,
&own_identity);
message* reset_msg = NULL;
PEP_STATUS status = _generate_keyreset_command_message(session, own_identity,
recip,
old_fpr, new_fpr, false,
&reset_msg);
if (status != PEP_STATUS_OK)
return status;
message* reset_message = new_message(PEP_dir_outgoing);
reset_message->from = own_identity;
reset_message->to = new_identity_list(identity_dup(recip)); // ?
const char* oldtag = "OLD: ";
const char* newtag = "\nNEW: ";
const size_t taglens = 11;
size_t full_len = taglens + strlen(old_fpr) + strlen(new_fpr) + 2; // \n and \0
char* longmsg = calloc(full_len, 1);
strlcpy(longmsg, oldtag, full_len);
strlcat(longmsg, old_fpr, full_len);
strlcat(longmsg, newtag, full_len);
strlcat(longmsg, new_fpr, full_len);
strlcat(longmsg, "\n", full_len);
reset_message->longmsg = longmsg;
reset_message->shortmsg = strdup("Key reset");
goto pEp_free;
if (!reset_msg)
return PEP_ILLEGAL_VALUE;
if (!reset_msg->attachments)
return PEP_UNKNOWN_ERROR;
message* output_msg = NULL;
status = encrypt_message(session, reset_message, NULL,
status = encrypt_message(session, reset_msg, NULL,
&output_msg, PEP_enc_PGP_MIME,
PEP_encrypt_flag_key_reset_only);
if (status == PEP_STATUS_OK)
*dst = output_msg;
free_message(reset_message);
pEp_free:
free_message(reset_msg);
return status;
}
PEP_STATUS send_key_reset_to_recents(PEP_SESSION session,
pEp_identity* from_ident,
const char* old_fpr,
const char* new_fpr) {
assert(old_fpr);
@ -287,18 +491,27 @@ PEP_STATUS send_key_reset_to_recents(PEP_SESSION session,
// Check if they've already been told - this shouldn't be the case, but...
bool contacted = false;
status = has_key_reset_been_sent(session, user_id, old_fpr, &contacted);
status = has_key_reset_been_sent(session, from_ident->address, user_id, old_fpr, &contacted);
if (status != PEP_STATUS_OK)
goto pEp_free;
if (contacted)
continue;
// Make sure they've ever *contacted* this address
bool in_contact_w_this_address = false;
status = has_partner_contacted_address(session, from_ident->address, curr_id->user_id,
&in_contact_w_this_address);
if (!in_contact_w_this_address)
continue;
// if not, make em a message
reset_msg = NULL;
status = create_standalone_key_reset_message(session,
&reset_msg,
from_ident,
curr_id,
old_fpr,
new_fpr);
@ -322,7 +535,7 @@ PEP_STATUS send_key_reset_to_recents(PEP_SESSION session,
}
// Put into notified DB
status = set_reset_contact_notified(session, old_fpr, user_id);
status = set_reset_contact_notified(session, from_ident->address, old_fpr, user_id);
if (status != PEP_STATUS_OK)
goto pEp_free;
}
@ -341,7 +554,7 @@ DYNAMIC_API PEP_STATUS key_reset_identity(
if (!session || !ident || (ident && (EMPTYSTR(ident->user_id) || EMPTYSTR(ident->address))))
return PEP_ILLEGAL_VALUE;
return key_reset(session, fpr, ident, NULL, NULL);
return key_reset(session, fpr, ident);
}
DYNAMIC_API PEP_STATUS key_reset_user(
@ -360,13 +573,121 @@ DYNAMIC_API PEP_STATUS key_reset_user(
if (is_me(session, input_ident) && EMPTYSTR(fpr))
return PEP_ILLEGAL_VALUE;
PEP_STATUS status = key_reset(session, fpr, input_ident, NULL, NULL);
PEP_STATUS status = key_reset(session, fpr, input_ident);
free_identity(input_ident);
return status;
}
DYNAMIC_API PEP_STATUS key_reset_all_own_keys(PEP_SESSION session) {
return key_reset(session, NULL, NULL, NULL, NULL);
return key_reset(session, NULL, NULL);
}
static PEP_STATUS _key_reset_device_group_for_shared_key(PEP_SESSION session,
identity_list* key_idents,
const char* old_key) {
assert(session);
assert(key_idents);
assert(old_key);
if (!session || !key_idents || EMPTYSTR(old_key))
return PEP_ILLEGAL_VALUE;
messageToSend_t send_cb = session->messageToSend;
if (!send_cb)
return PEP_SYNC_NO_MESSAGE_SEND_CALLBACK;
PEP_STATUS status = PEP_STATUS_OK;
// each of these has the same key and needs a new one.
identity_list* curr_ident;
for (curr_ident = key_idents; curr_ident && curr_ident->ident; curr_ident = curr_ident->next) {
if (curr_ident->ident->flags & PEP_idf_devicegroup) {
pEp_identity* ident = curr_ident->ident;
free(ident->fpr);
ident->fpr = NULL;
status = generate_keypair(session, ident);
if (status != PEP_STATUS_OK)
return status;
}
else {
status = key_reset(session, old_key, curr_ident->ident);
}
}
// Ok, everyone's got a new keypair. Hoorah!
// generate, sign, and push messages into queue
for (curr_ident = key_idents; curr_ident && curr_ident->ident; curr_ident = curr_ident->next) {
if (curr_ident->ident->flags & PEP_idf_devicegroup) {
message* outmsg = NULL;
// FIXME - do we have to? Maybe check IN the funct
pEp_identity* to_me = identity_dup(curr_ident->ident);
if (!to_me)
return PEP_OUT_OF_MEMORY;
status = _generate_keyreset_command_message(session, curr_ident->ident, to_me,
old_key, curr_ident->ident->fpr,
true, &outmsg);
message* enc_msg = NULL;
// encrypt this baby and get out
// extra keys???
status = encrypt_message(session, outmsg, NULL, &enc_msg, PEP_enc_PGP_MIME, 0);
if (status != PEP_STATUS_OK) {
goto pEp_free;
}
// insert into queue
status = send_cb(enc_msg);
if (status != PEP_STATUS_OK) {
free(enc_msg);
goto pEp_free;
}
// Does there need to be some kind of signal here for sync?
}
}
// Ok, we've signed everything we need to with the old key,
// Revoke that baby.
status = revoke_key(session, old_key, NULL);
if (!status)
goto pEp_free;
for (curr_ident = key_idents; curr_ident && curr_ident->ident; curr_ident = curr_ident->next) {
if (curr_ident->ident->flags & PEP_idf_devicegroup) {
pEp_identity* ident = curr_ident->ident;
// N.B. This sort of sucks because we overwrite this every time.
// But this case is infrequent and we don't rely on the binding.
if (status == PEP_STATUS_OK)
status = set_revoked(session, old_key, curr_ident->ident->fpr, time(NULL));
if (!status)
goto pEp_free;
pEp_identity* tmp_ident = identity_dup(ident);
if (!tmp_ident) {
status = PEP_OUT_OF_MEMORY;
goto pEp_free;
}
free(tmp_ident->fpr);
// for all active communication partners:
// active_send revocation
tmp_ident->fpr = strdup(old_key); // freed in free_identity
if (status == PEP_STATUS_OK)
status = send_key_reset_to_recents(session, tmp_ident, old_key, ident->fpr);
free_identity(tmp_ident);
}
}
pEp_free:
return status;
}
// Notes to integrate into header:
@ -374,9 +695,7 @@ DYNAMIC_API PEP_STATUS key_reset_all_own_keys(PEP_SESSION session) {
PEP_STATUS key_reset(
PEP_SESSION session,
const char* key_id,
pEp_identity* ident,
identity_list** own_identities,
stringlist_t** own_revoked_fprs
pEp_identity* ident
)
{
if (!session || (ident && EMPTYSTR(ident->user_id)))
@ -426,7 +745,7 @@ PEP_STATUS key_reset(
for (curr_key = keys; curr_key && curr_key->value; curr_key = curr_key->next) {
// FIXME: Is the ident really necessary?
status = key_reset(session, curr_key->value, tmp_ident, own_identities, own_revoked_fprs);
status = key_reset(session, curr_key->value, tmp_ident);
if (status != PEP_STATUS_OK)
break;
}
@ -500,20 +819,21 @@ PEP_STATUS key_reset(
if (status == PEP_STATUS_OK) {
// now have ident list, or should
identity_list* curr_ident;
for (curr_ident = key_idents; curr_ident && curr_ident->ident;
curr_ident = curr_ident->next) {
pEp_identity* this_identity = curr_ident->ident;
// Do the full reset on this identity
status = key_reset(session, fpr_copy, this_identity, own_identities, own_revoked_fprs);
// Ident list gets freed below, do not free here!
if (status != PEP_STATUS_OK)
break;
bool is_grouped = false;
if (!deviceGrouped(session, &is_grouped)) {
for (curr_ident = key_idents; curr_ident && curr_ident->ident;
curr_ident = curr_ident->next) {
pEp_identity* this_identity = curr_ident->ident;
// Do the full reset on this identity
status = key_reset(session, fpr_copy, this_identity);
// Ident list gets freed below, do not free here!
}
}
else {
status = _key_reset_device_group_for_shared_key(session, key_idents, fpr_copy); }
}
// Ok, we've either now reset for each own identity with this key, or
// we got an error and want to bail anyway.
@ -521,8 +841,14 @@ PEP_STATUS key_reset(
}
}
// Base case for is_own_private starts here
if (EMPTYSTR(tmp_ident->address)) {
return PEP_UNKNOWN_ERROR; // FIXME - what case IS this?
}
// Ok, first we generate a new key.
// Base case for is_own_private starts here
// tmp ident is an actual identity now (not just a skeleton?)
status = revoke_key(session, fpr_copy, NULL);
// If we have a full identity, we have some cleanup and generation tasks here
@ -532,23 +858,9 @@ PEP_STATUS key_reset(
tmp_ident->fpr = NULL;
status = myself(session, tmp_ident);
}
if (status == PEP_STATUS_OK && tmp_ident->fpr && strcmp(fpr_copy, tmp_ident->fpr) != 0) {
if (status == PEP_STATUS_OK && tmp_ident->fpr && strcmp(fpr_copy, tmp_ident->fpr) != 0)
new_key = strdup(tmp_ident->fpr);
// status = set_own_key(session, tmp_ident, new_key);
}
if (own_revoked_fprs) {
// We can dedup this later
if (!(*own_revoked_fprs))
*own_revoked_fprs = new_stringlist(NULL);
char* revkey = strdup(fpr_copy);
if (!revkey) {
status = PEP_OUT_OF_MEMORY;
goto pEp_free;
}
stringlist_add(*own_revoked_fprs, revkey);
}
// Error handling?
// mistrust fpr from trust
tmp_ident->fpr = fpr_copy;
@ -562,18 +874,6 @@ PEP_STATUS key_reset(
// Update fpr for outgoing
status = myself(session, tmp_ident);
}
if (status == PEP_STATUS_OK && own_identities) {
if (!(*own_identities))
*own_identities = new_identity_list(NULL);
pEp_identity* new_ident = identity_dup(tmp_ident);
if (!new_ident) {
status = PEP_OUT_OF_MEMORY;
goto pEp_free;
}
identity_list_add(*own_identities, new_ident);
}
}
if (status == PEP_STATUS_OK)
@ -594,8 +894,11 @@ PEP_STATUS key_reset(
status = set_revoked(session, fpr_copy, new_key, time(NULL));
// for all active communication partners:
// active_send revocation
tmp_ident->fpr = fpr_copy;
if (status == PEP_STATUS_OK)
status = send_key_reset_to_recents(session, fpr_copy, new_key);
status = send_key_reset_to_recents(session, tmp_ident, fpr_copy, new_key);
tmp_ident->fpr = NULL;
}
} // end is_own_private
else {
@ -642,6 +945,7 @@ pEp_free:
return status;
}
/*
static stringlist_t* collect_key_material(PEP_SESSION session, stringlist_t* fprs) {
stringlist_t* keydata = NULL;
stringlist_t* curr_fpr = fprs;
@ -702,6 +1006,17 @@ PEP_STATUS key_reset_own_and_deliver_revocations(PEP_SESSION session,
}
if (datasize > 0 && key_material)
stringlist_add(keydata, key_material);
key_material = NULL;
datasize = 0;
status = export_private_keys(session, curr_ident->ident->fpr, &key_material, &datasize);
if (status) {
free_stringlist(keydata);
return status;
}
if (datasize > 0 && key_material)
stringlist_add(keydata, key_material);
}
curr_ident = curr_ident->next;
}
@ -713,7 +1028,7 @@ PEP_STATUS key_reset_own_and_deliver_revocations(PEP_SESSION session,
free(revoked_fprs);
return PEP_STATUS_OK;
}
*/
Distribution_t *Distribution_from_keyreset_command_list(
const keyreset_command_list *command_list,
Distribution_t *dist
@ -778,6 +1093,7 @@ enomem:
return NULL;
}
PEP_STATUS key_reset_commands_to_PER(const keyreset_command_list *command_list, char **cmds, size_t *size)
{
PEP_STATUS status = PEP_STATUS_OK;
@ -908,4 +1224,3 @@ the_end:
ASN_STRUCT_FREE(asn_DEF_Distribution, dist);
return status;
}

+ 8
- 10
src/key_reset.h View File

@ -114,11 +114,6 @@ DYNAMIC_API PEP_STATUS key_reset_all_own_keys(PEP_SESSION session);
// if NULL and fpr is non-NULL, we'll reset the key for all
// associated identities. If both ident and fpr are NULL, see
// the fpr arg documentation.
// own_identities (out) IF this is resetting own identities, passing in a pointer here
// will cause key_reset to return a list of own_identities featuring
// the new keys
// own_revoked_fprs (out) IF this is resetting own identities, passing in a pointer here
// will cause key_reset to send a list of the revoked fprs
//
// Note: ident->fpr is always ignored
//
@ -128,26 +123,26 @@ DYNAMIC_API PEP_STATUS key_reset_all_own_keys(PEP_SESSION session);
PEP_STATUS key_reset(
PEP_SESSION session,
const char* fpr,
pEp_identity* ident,
identity_list** own_identities,
stringlist_t** own_revoked_fprs
pEp_identity* ident
);
/*
PEP_STATUS key_reset_own_and_deliver_revocations(PEP_SESSION session,
identity_list** own_identities,
stringlist_t** revocations,
stringlist_t** keys);
*/
PEP_STATUS has_key_reset_been_sent(
PEP_SESSION session,
const char* from_addr,
const char* user_id,
const char* revoked_fpr,
bool* contacted);
PEP_STATUS set_reset_contact_notified(
PEP_SESSION session,
const char* own_address,
const char* revoke_fpr,
const char* contact_id
);
@ -157,11 +152,14 @@ PEP_STATUS receive_key_reset(PEP_SESSION session,
PEP_STATUS create_standalone_key_reset_message(PEP_SESSION session,
message** dst,
pEp_identity* own_identity,
pEp_identity* recip,
const char* old_fpr,
const char* new_fpr);
PEP_STATUS send_key_reset_to_recents(PEP_SESSION session,
pEp_identity* from_ident,
const char* old_fpr,
const char* new_fpr);


+ 0
- 1
src/message.h View File

@ -80,7 +80,6 @@ typedef struct _message_ref_list {
struct _message_ref_list *next;
} message_ref_list;
// new_message() - allocate new message
//
// parameters:


+ 123
- 33
src/message_api.c View File

@ -3395,6 +3395,65 @@ static bool _have_extrakeys(stringlist_t *keylist)
&& keylist->value[0];
}
// practically speaking, only useful to get user_id/address intersection
// we presume no dups in the first list if you're looking for
// a unique result.
static PEP_STATUS ident_list_intersect(identity_list* list_a,
identity_list* list_b,
identity_list** intersection) {
if (!intersection)
return PEP_ILLEGAL_VALUE;
*intersection = NULL;
if (!list_a || !list_b || !list_a->ident || !list_b->ident)
return PEP_STATUS_OK;
*intersection = NULL;
identity_list* isect = NULL;
identity_list* curr_a = list_a;
for ( ; curr_a && curr_a->ident; curr_a = curr_a->next) {
pEp_identity* id_a = curr_a->ident;
if (EMPTYSTR(id_a->user_id) || EMPTYSTR(id_a->address))
continue;
identity_list* curr_b = list_b;
for ( ; curr_b && curr_b->ident; curr_b = curr_b->next) {
pEp_identity* id_b = curr_b->ident;
if (EMPTYSTR(id_b->user_id) || EMPTYSTR(id_b->address))
continue;
if (strcmp(id_a->user_id, id_b->user_id) == 0 &&
strcmp(id_a->address, id_b->address) == 0) {
pEp_identity* result_id = identity_dup(id_b);
if (!id_b)
goto enomem;
if (!isect) {
isect = new_identity_list(result_id);
if (!isect)
goto enomem;
}
else {
if (!identity_list_add(isect, result_id))
goto enomem;
}
break;
}
}
}
*intersection = isect;
return PEP_STATUS_OK;
enomem:
free_identity_list(isect);
return PEP_OUT_OF_MEMORY;
}
static PEP_STATUS _decrypt_message(
PEP_SESSION session,
message *src,
@ -3924,42 +3983,73 @@ static PEP_STATUS _decrypt_message(
continue; // Again, shouldn't occur
if (curr_pair->key && curr_pair->value) {
status = create_standalone_key_reset_message(session,
&reset_msg,
msg->from,
curr_pair->key,
curr_pair->value);
// If we can't find the identity, this is someone we've never mailed, so we just
// go on letting them use the wrong key until we mail them ourselves. (Spammers, etc)
if (status != PEP_CANNOT_FIND_IDENTITY) {
/* Figure out which address(es) this came to so we know who to reply from */
identity_list* my_rev_ids = NULL;
/* check by replacement ID for identities which used this key? */
status = get_identities_by_main_key_id(session, curr_pair->value,
&my_rev_ids);
if (status == PEP_STATUS_OK && my_rev_ids) {
// get identities in this list the message was to/cc'd to (not for bcc)
identity_list* used_ids_for_key = NULL;
status = ident_list_intersect(my_rev_ids, msg->to, &used_ids_for_key);
if (status != PEP_STATUS_OK)
goto pEp_error;
goto pEp_error; // out of memory
if (!reset_msg) {
status = PEP_OUT_OF_MEMORY;
goto pEp_error;
}
// insert into queue
if (session->messageToSend)
status = session->messageToSend(reset_msg);
else
status = PEP_SYNC_NO_MESSAGE_SEND_CALLBACK;
if (status == PEP_STATUS_OK) {
// Put into notified DB
status = set_reset_contact_notified(session, curr_pair->key, msg->from->user_id);
if (status != PEP_STATUS_OK) // It's ok to barf because it's a DB problem??
goto pEp_error;
}
else {
// According to Volker, this would only be a fatal error, so...
free_message(reset_msg); // ??
reset_msg = NULL; // ??
identity_list* used_cc_ids = NULL;
status = ident_list_intersect(my_rev_ids, msg->to, &used_cc_ids);
if (status != PEP_STATUS_OK)
goto pEp_error;
}
}
used_ids_for_key = identity_list_join(used_ids_for_key, used_cc_ids);
identity_list* curr_recip = used_ids_for_key;
for ( ; curr_recip && curr_recip->ident; curr_recip = curr_recip->next) {
if (!is_me(session, curr_recip->ident))
continue;
status = create_standalone_key_reset_message(session,
&reset_msg,
curr_recip->ident,
msg->from,
curr_pair->key,
curr_pair->value);
// If we can't find the identity, this is someone we've never mailed, so we just
// go on letting them use the wrong key until we mail them ourselves. (Spammers, etc)
if (status != PEP_CANNOT_FIND_IDENTITY) {
if (status != PEP_STATUS_OK)
goto pEp_error;
if (!reset_msg) {
status = PEP_OUT_OF_MEMORY;
goto pEp_error;
}
// insert into queue
if (session->messageToSend)
status = session->messageToSend(reset_msg);
else
status = PEP_SYNC_NO_MESSAGE_SEND_CALLBACK;
if (status == PEP_STATUS_OK) {
// Put into notified DB
status = set_reset_contact_notified(session, curr_recip->ident->address, curr_pair->key, msg->from->user_id);
if (status != PEP_STATUS_OK) // It's ok to barf because it's a DB problem??
goto pEp_error;
}
else {
// According to Volker, this would only be a fatal error, so...
free_message(reset_msg); // ??
reset_msg = NULL; // ??
goto pEp_error;
}
}
}
} // else we couldn't find an ident for replacement key
}
}
}


+ 171
- 7
src/pEpEngine.c View File

@ -503,19 +503,22 @@ static const char *sql_get_own_address_binding_from_contact =
"select own_address from social_graph where own_userid = ?1 and contact_userid = ?2 ;";
static const char *sql_set_revoke_contact_as_notified =
"insert or replace into revocation_contact_list(fpr, contact_id) values (?1, ?2) ;";
"insert or replace into revocation_contact_list(fpr, own_address, contact_id) values (?1, ?2, ?3) ;";
static const char *sql_get_contacted_ids_from_revoke_fpr =
"select * from revocation_contact_list where fpr = ?1 ;";
static const char *sql_was_id_for_revoke_contacted =
"select count(*) from revocation_contact_list where fpr = ?1 and contact_id = ?2 ;";
"select count(*) from revocation_contact_list where fpr = ?1 and own_address = ?2 and contact_id = ?3 ;";
static const char *sql_has_id_contacted_address =
"select count(*) from social_graph where own_address = ?1 and contact_userid = ?2 ;";
// We only need user_id and address, since in the main usage, we'll call update_identity
// on this anyway when sending out messages.
static const char *sql_get_last_contacted =
"select user_id, address from identity where datetime('now') < datetime(timestamp, '+14 days') ; ";
static int user_version(void *_version, int count, char **text, char **name)
{
assert(_version);
@ -806,6 +809,112 @@ void errorLogCallback(void *pArg, int iErrCode, const char *zMsg){
fprintf(stderr, "(%d) %s\n", iErrCode, zMsg);
}
static PEP_STATUS upgrade_revoc_contact_to_13(PEP_SESSION session) {
// I HATE SQLITE.
PEP_STATUS status = PEP_STATUS_OK;
int int_result = 0;
// Ok, first we ADD the column so we can USE it.
// We will end up propagating the "error" this first time
// (one-to-one revoke-replace relationships), but since key reset
// hasn't been used in production, this is not a customer-facing
// issue.
int_result = sqlite3_exec(
session->db,
"alter table revocation_contact_list\n"
" add column own_address text\n",
NULL,
NULL,
NULL
);
assert(int_result == SQLITE_OK);
sqlite3_stmt* update_revoked_w_addr_stmt = NULL;
const char* sql_query = "update revocation_contact_list set own_address = ?1 where fpr = ?2;";
sqlite3_prepare_v2(session->db, sql_query, -1, &update_revoked_w_addr_stmt, NULL);
// the best we can do here is search per address, since these
// are no longer associated with an identity. For now, if we find
// something we can't add an address to, we'll delete the record.
// this should not, in the current environment, ever happen, but
// since we need to make the address part of the primary key, it's
// the right thing to do. sqlite does support null fields in a primary
// key for a weird version compatibility reason, but that doesn't
// mean we should use it, and we should be *safe*, not relying
// on an implementation-specific quirk which might be sanely removed
// in a future sqlite version.
stringpair_t* revoked_key_to_own_address = NULL;
identity_list* id_list = NULL;
status = own_identities_retrieve(session, &id_list);
if (!status || !id_list)
return PEP_STATUS_OK; // it's empty AFAIK (FIXME)
identity_list* curr_own = id_list;
// Ok, go through and find any keys associated with this address
for ( ; curr_own && curr_own->ident; curr_own = curr_own->next) {
if (EMPTYSTR(curr_own->ident->address)) // shouldn't happen
continue;
stringlist_t* keylist = NULL;
status = find_keys(session, curr_own->ident->address, &keylist);
stringlist_t* curr_key = keylist;
for ( ; curr_key && curr_key->value; curr_key = curr_key->next) {
if (EMPTYSTR(curr_key->value))
continue;
// We just do this lazily - if this isn't a revoked key, it
// won't do anything.
sqlite3_bind_text(update_revoked_w_addr_stmt, 1, curr_own->ident->address, -1,
SQLITE_STATIC);
sqlite3_bind_text(update_revoked_w_addr_stmt, 2, curr_key->value, -1,
SQLITE_STATIC);
int_result = sqlite3_step(update_revoked_w_addr_stmt);
assert(int_result == SQLITE_DONE);
sqlite3_reset(update_revoked_w_addr_stmt);
}
}
sqlite3_finalize(update_revoked_w_addr_stmt);
int_result = sqlite3_exec(
session->db,
"delete from revocation_contact_list where own_address is NULL;\n"
"PRAGMA foreign_keys=off;\n"
"BEGIN TRANSACTION;\n"
"create table if not exists _revocation_contact_list_new (\n"
" fpr text not null references pgp_keypair (fpr)\n"
" on delete cascade,\n"
" own_address text,\n"
" contact_id text not null references person (id)\n"
" on delete cascade on update cascade,\n"
" timestamp integer default (datetime('now')),\n"
" PRIMARY KEY(fpr, own_address, contact_id)\n"
");\n"
"INSERT INTO _revocation_contact_list_new (fpr, "
" own_address, "
" contact_id) "
" SELECT revocation_contact_list.fpr, "
" revocation_contact_list.own_address, "
" revocation_contact_list.contact_id "
" FROM revocation_contact_list "
" WHERE 1;\n"
"DROP TABLE revocation_contact_list;\n"
"ALTER TABLE _revocation_contact_list_new RENAME TO revocation_contact_list;\n"
"COMMIT;\n"
"\n"
"PRAGMA foreign_keys=on;\n"
,
NULL,
NULL,
NULL
);
assert(int_result == SQLITE_OK);
return status;
}
#ifdef USE_GPG
PEP_STATUS pgp_import_ultimately_trusted_keypairs(PEP_SESSION session);
#endif // USE_GPG
@ -934,7 +1043,7 @@ DYNAMIC_API PEP_STATUS init(
sqlite3_busy_timeout(_session->system_db, 1000);
// increment this when patching DDL
#define _DDL_USER_VERSION "12"
#define _DDL_USER_VERSION "13"
if (in_first) {
@ -1052,10 +1161,11 @@ DYNAMIC_API PEP_STATUS init(
"create table if not exists revocation_contact_list (\n"
" fpr text not null references pgp_keypair (fpr)\n"
" on delete cascade,\n"
" own_address text,\n"
" contact_id text not null references person (id)\n"
" on delete cascade on update cascade,\n"
" timestamp integer default (datetime('now')),\n"
" PRIMARY KEY(fpr, contact_id)\n"
" PRIMARY KEY(fpr, own_address, contact_id)\n"
");\n"
,
NULL,
@ -1103,7 +1213,10 @@ DYNAMIC_API PEP_STATUS init(
// Sometimes the user_version wasn't set correctly.
if (version == 1) {
bool version_changed = true;
if (table_contains_column(_session, "identity", "pEp_version_major")) {
if (table_contains_column(_session, "revocation_contact_list", "own_address")) {
version = 13;
}
else if (table_contains_column(_session, "identity", "pEp_version_major")) {
version = 12;
}
else if (db_contains_table(_session, "social_graph") > 0) {
@ -1555,6 +1668,12 @@ DYNAMIC_API PEP_STATUS init(
if (status != PEP_STATUS_OK)
return status;
}
if (version < 13) {
status = upgrade_revoc_contact_to_13(_session);
assert(status == PEP_STATUS_OK);
if (status != PEP_STATUS_OK)
return status;
}
}
else {
// Version from DB was 0, it means this is initial setup.
@ -1714,6 +1833,12 @@ DYNAMIC_API PEP_STATUS init(
&_session->was_id_for_revoke_contacted, NULL);
assert(int_result == SQLITE_OK);
int_result = sqlite3_prepare_v2(_session->db,
sql_has_id_contacted_address,
(int)strlen(sql_has_id_contacted_address),
&_session->has_id_contacted_address, NULL);
assert(int_result == SQLITE_OK);
int_result = sqlite3_prepare_v2(_session->db,
sql_get_last_contacted,
(int)strlen(sql_get_last_contacted),
@ -2022,7 +2147,9 @@ DYNAMIC_API void release(PEP_SESSION session)
if (session->get_contacted_ids_from_revoke_fpr)
sqlite3_finalize(session->get_contacted_ids_from_revoke_fpr);
if (session->was_id_for_revoke_contacted)
sqlite3_finalize(session->was_id_for_revoke_contacted);
sqlite3_finalize(session->was_id_for_revoke_contacted);
if (session->has_id_contacted_address)
sqlite3_finalize(session->has_id_contacted_address);
if (session->get_last_contacted)
sqlite3_finalize(session->get_last_contacted);
if (session->set_pgp_keypair)
@ -3567,6 +3694,43 @@ PEP_STATUS bind_own_ident_with_contact_ident(PEP_SESSION session,
return PEP_STATUS_OK;
}
// FIXME: should be more like is there a communications relationship,
// since this could be either way
PEP_STATUS has_partner_contacted_address(PEP_SESSION session, const char* partner_id,
const char* own_address, bool* was_contacted) {
assert(session);
assert(!EMPTYSTR(partner_id));
assert(!EMPTYSTR(own_address));
assert(was_contacted);
if (!session || !was_contacted || EMPTYSTR(partner_id) || EMPTYSTR(own_address))
return PEP_ILLEGAL_VALUE;
*was_contacted = false;
PEP_STATUS status = PEP_STATUS_OK;
sqlite3_reset(session->has_id_contacted_address);
sqlite3_bind_text(session->has_id_contacted_address, 1, partner_id, -1,
SQLITE_STATIC);
int result = sqlite3_step(session->has_id_contacted_address);
switch (result) {
case SQLITE_ROW: {
// yeah yeah, I know, we could be lazy here, but it looks bad.
*was_contacted = (sqlite3_column_int(session->has_id_contacted_address, 0) != 0);
status = PEP_STATUS_OK;
break;
}
default:
status = PEP_UNKNOWN_DB_ERROR;
}
sqlite3_reset(session->has_id_contacted_address);
return status;
}
// FIXME: problematic - can be multiple and this now matters
PEP_STATUS get_own_ident_for_contact_id(PEP_SESSION session,
const pEp_identity* contact,
pEp_identity** own_ident) {


+ 2
- 0
src/pEpEngine.h View File

@ -1430,6 +1430,8 @@ PEP_STATUS sign_only(PEP_SESSION session,
PEP_STATUS set_all_userids_to_own(PEP_SESSION session,
identity_list* id_list);
PEP_STATUS has_partner_contacted_address(PEP_SESSION session, const char* partner_id,
const char* own_address, bool* was_contacted);
#ifdef __cplusplus
}
#endif

+ 1
- 0
src/pEp_internal.h View File

@ -191,6 +191,7 @@ struct _pEpSession {
sqlite3_stmt *set_revoke_contact_as_notified;
sqlite3_stmt *get_contacted_ids_from_revoke_fpr;
sqlite3_stmt *was_id_for_revoke_contacted;
sqlite3_stmt *has_id_contacted_address;
sqlite3_stmt *get_last_contacted;
// sqlite3_stmt *set_device_group;
// sqlite3_stmt *get_device_group;


+ 7
- 7
test/src/KeyResetMessageTest.cc View File

@ -307,7 +307,7 @@ TEST_F(KeyResetMessageTest, check_reset_key_and_notify) {
);
ASSERT_EQ(int_result , SQLITE_OK);
status = key_reset(session, alice_fpr, from_ident, NULL, NULL);
status = key_reset(session, alice_fpr, from_ident);
ASSERT_EQ(status , PEP_STATUS_OK);
ASSERT_GT(m_queue.size(), 0);
status = myself(session, from_ident);
@ -460,7 +460,7 @@ TEST_F(KeyResetMessageTest, check_receive_message_to_revoked_key_from_unknown) {
ASSERT_STRCASEEQ(from_ident->fpr, alice_fpr);
ASSERT_TRUE(from_ident->me);
status = key_reset(session, alice_fpr, from_ident, NULL, NULL);
status = key_reset(session, alice_fpr, from_ident);
ASSERT_EQ(status , PEP_STATUS_OK);
m_queue.clear();
@ -524,7 +524,7 @@ TEST_F(KeyResetMessageTest, check_receive_message_to_revoked_key_from_contact) {
// FIXME: longer term we need to fix the test, but the key attached to the message below has expired, so for now, we give her a new key
slurp_and_import_key(session, "test_keys/pub/pep-test-gabrielle-0xE203586C_pub.asc");
status = key_reset(session, alice_fpr, from_ident, NULL, NULL);
status = key_reset(session, alice_fpr, from_ident);
ASSERT_EQ(status , PEP_STATUS_OK);
ASSERT_EQ(m_queue.size() , 0);
m_queue.clear();
@ -570,10 +570,10 @@ TEST_F(KeyResetMessageTest, check_multiple_resets_single_key) {
ASSERT_STRCASEEQ(from_ident->fpr, alice_fpr);
ASSERT_TRUE(from_ident->me);
status = key_reset(session, NULL, NULL, NULL, NULL);
status = key_reset(session, NULL, NULL);
ASSERT_EQ(status , PEP_STATUS_OK);
status = key_reset(session, NULL, NULL, NULL, NULL);
status = key_reset(session, NULL, NULL);
ASSERT_EQ(status , PEP_STATUS_OK);
status = myself(session, from_ident);
@ -1308,7 +1308,7 @@ TEST_F(KeyResetMessageTest, check_reset_mistrust_next_msg_have_not_mailed) {
ASSERT_EQ(rating, PEP_rating_reliable);
}
/*
TEST_F(KeyResetMessageTest, check_reset_own_with_revocations) {
pEp_identity* id1 = new_identity("krista-not-real@darthmama.org", NULL, PEP_OWN_USERID, "Krista at Home");
PEP_STATUS status = myself(session, id1);
@ -1437,7 +1437,7 @@ TEST_F(KeyResetMessageTest, check_reset_own_with_revocations) {
ASSERT_EQ(status, PEP_STATUS_OK);
}
}
*/
TEST_F(KeyResetMessageTest, codec_test) {
// create input values


Loading…
Cancel
Save