1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/src/key_reset.c Fri Sep 14 20:18:53 2018 +0200
1.3 @@ -0,0 +1,478 @@
1.4 +// This file is under GNU General Public License 3.0
1.5 +// see LICENSE.txt
1.6 +
1.7 +#include "pEp_internal.h"
1.8 +#include "dynamic_api.h"
1.9 +#include "message_api.h"
1.10 +
1.11 +#include <string.h>
1.12 +#include <stdlib.h>
1.13 +
1.14 +PEP_STATUS has_key_reset_been_sent(
1.15 + PEP_SESSION session,
1.16 + const char* user_id,
1.17 + const char* revoked_fpr,
1.18 + bool* contacted)
1.19 +{
1.20 + assert(session);
1.21 + assert(contacted);
1.22 + assert(user_id);
1.23 + assert(revoked_fpr);
1.24 + assert(!EMPTYSTR(user_id));
1.25 +
1.26 + if (!session || !contacted || EMPTYSTR(revoked_fpr) || EMPTYSTR(user_id))
1.27 + return PEP_ILLEGAL_VALUE;
1.28 +
1.29 + *contacted = false;
1.30 +
1.31 + char* alias_default = NULL;
1.32 +
1.33 + PEP_STATUS status = get_userid_alias_default(session, user_id, &alias_default);
1.34 +
1.35 + if (status == PEP_CANNOT_FIND_ALIAS || EMPTYSTR(alias_default)) {
1.36 + free(alias_default);
1.37 + alias_default = strdup(user_id);
1.38 + }
1.39 +
1.40 + sqlite3_reset(session->was_id_for_revoke_contacted);
1.41 + sqlite3_bind_text(session->was_id_for_revoke_contacted, 1, revoked_fpr, -1,
1.42 + SQLITE_STATIC);
1.43 + sqlite3_bind_text(session->was_id_for_revoke_contacted, 2, user_id, -1,
1.44 + SQLITE_STATIC);
1.45 + int result = sqlite3_step(session->was_id_for_revoke_contacted);
1.46 + switch (result) {
1.47 + case SQLITE_ROW: {
1.48 + *contacted = (sqlite3_column_int(session->was_id_for_revoke_contacted, 0) != 0);
1.49 + break;
1.50 + }
1.51 + default:
1.52 + sqlite3_reset(session->was_id_for_revoke_contacted);
1.53 + free(alias_default);
1.54 + return PEP_UNKNOWN_DB_ERROR;
1.55 + }
1.56 +
1.57 + sqlite3_reset(session->was_id_for_revoke_contacted);
1.58 + return PEP_STATUS_OK;
1.59 +}
1.60 +
1.61 +//static const char *sql_set_revoke_contact_as_notified =
1.62 +// "insert or replace into revocation_contact_list(fpr, contact_id) values (?1, ?2) ;";
1.63 +
1.64 +PEP_STATUS set_reset_contact_notified(
1.65 + PEP_SESSION session,
1.66 + const char* revoke_fpr,
1.67 + const char* contact_id
1.68 + )
1.69 +{
1.70 + PEP_STATUS status = PEP_STATUS_OK;
1.71 +
1.72 + assert(session && !EMPTYSTR(revoke_fpr) && !EMPTYSTR(contact_id));
1.73 +
1.74 + if (!session || EMPTYSTR(revoke_fpr) || EMPTYSTR(contact_id))
1.75 + return PEP_ILLEGAL_VALUE;
1.76 +
1.77 + sqlite3_reset(session->set_revoke_contact_as_notified);
1.78 + sqlite3_bind_text(session->set_revoke_contact_as_notified, 1, revoke_fpr, -1,
1.79 + SQLITE_STATIC);
1.80 + sqlite3_bind_text(session->set_revoke_contact_as_notified, 2, contact_id, -1,
1.81 + SQLITE_STATIC);
1.82 +
1.83 + int result;
1.84 +
1.85 + result = sqlite3_step(session->set_revoke_contact_as_notified);
1.86 + switch (result) {
1.87 + case SQLITE_DONE:
1.88 + status = PEP_STATUS_OK;
1.89 + break;
1.90 +
1.91 + default:
1.92 + status = PEP_UNKNOWN_DB_ERROR;
1.93 + }
1.94 +
1.95 + sqlite3_reset(session->set_revoke_contact_as_notified);
1.96 + return status;
1.97 +}
1.98 +
1.99 +
1.100 +PEP_STATUS receive_key_reset(PEP_SESSION session,
1.101 + message* reset_msg) {
1.102 +
1.103 + if (!session || !reset_msg)
1.104 + return PEP_ILLEGAL_VALUE;
1.105 +
1.106 + pEp_identity* sender_id = reset_msg->from;
1.107 +
1.108 + if (!sender_id)
1.109 + return PEP_MALFORMED_KEY_RESET_MSG;
1.110 +
1.111 + PEP_STATUS status = update_identity(session, sender_id);
1.112 + if (!sender_id->user_id)
1.113 + return PEP_UNKNOWN_ERROR;
1.114 +
1.115 + if (is_me(session, sender_id))
1.116 + return PEP_ILLEGAL_VALUE;
1.117 +
1.118 + if (!reset_msg->longmsg || strncmp(reset_msg->longmsg, "OLD: ", 5) != 0)
1.119 + return PEP_MALFORMED_KEY_RESET_MSG;
1.120 +
1.121 + status = PEP_STATUS_OK;
1.122 + char* old_fpr = NULL;
1.123 + char* new_fpr = NULL;
1.124 +
1.125 + stringlist_t* keylist = NULL;
1.126 + pEp_identity* temp_ident = identity_dup(sender_id);
1.127 + if (!temp_ident) {
1.128 + status = PEP_OUT_OF_MEMORY;
1.129 + goto pep_free;
1.130 + }
1.131 +
1.132 + char* rest = NULL;
1.133 + char* p = strtok_r(reset_msg->longmsg, "\r\n", &rest);
1.134 + if (!EMPTYSTR(p + 5))
1.135 + old_fpr = strdup(p + 5);
1.136 + else {
1.137 + status = PEP_MALFORMED_KEY_RESET_MSG;
1.138 + goto pep_free;
1.139 + }
1.140 +
1.141 + bool own_key = false;
1.142 + status = is_own_key(session, old_fpr, &own_key);
1.143 +
1.144 + if (own_key) {
1.145 + // Nope, no one can make us our own default. If we want to do that,
1.146 + // that's keysync, NOT key reset.
1.147 + status = PEP_ILLEGAL_VALUE;
1.148 + goto pep_free;
1.149 + }
1.150 +
1.151 + p = strtok_r(NULL, "\r\n", &rest);
1.152 + if (strncmp(p, "NEW: ", 5) != 0 || EMPTYSTR(p + 5)) {
1.153 + status = PEP_MALFORMED_KEY_RESET_MSG;
1.154 + goto pep_free;
1.155 + }
1.156 +
1.157 + new_fpr = strdup(p + 5);
1.158 +
1.159 + // Reset the original key
1.160 + status = key_reset(session, old_fpr, temp_ident);
1.161 + if (status != PEP_STATUS_OK)
1.162 + goto pep_free;
1.163 +
1.164 + status = find_keys(session, new_fpr, &keylist);
1.165 + if (status != PEP_STATUS_OK)
1.166 + goto pep_free;
1.167 +
1.168 + if (!keylist) {
1.169 + status = PEP_KEY_NOT_FOUND;
1.170 + goto pep_free;
1.171 + }
1.172 +
1.173 + // alright, we've checked as best we can. Let's set that baby.
1.174 + sender_id->fpr = new_fpr;
1.175 +
1.176 + // This only sets as the default, does NOT TRUST IN ANY WAY
1.177 + sender_id->comm_type = sender_id->comm_type & (~PEP_ct_confirmed);
1.178 + status = set_identity(session, sender_id);
1.179 +
1.180 + sender_id->fpr = NULL; // ownership for free
1.181 +pep_free:
1.182 + free_stringlist(keylist);
1.183 + free(old_fpr);
1.184 + free(new_fpr);
1.185 + free_identity(temp_ident);
1.186 + return status;
1.187 +}
1.188 +
1.189 +PEP_STATUS create_standalone_key_reset_message(PEP_SESSION session,
1.190 + message** dst,
1.191 + pEp_identity* recip,
1.192 + const char* old_fpr,
1.193 + const char* new_fpr) {
1.194 +
1.195 + if (!dst || !recip->user_id || !recip->address)
1.196 + return PEP_ILLEGAL_VALUE;
1.197 +
1.198 + if (!old_fpr || !new_fpr)
1.199 + return PEP_ILLEGAL_VALUE;
1.200 +
1.201 + *dst = NULL;
1.202 + // Get own identity user has corresponded with
1.203 + pEp_identity* own_identity = NULL;
1.204 +
1.205 + PEP_STATUS status = get_own_ident_for_contact_id(session,
1.206 + recip,
1.207 + &own_identity);
1.208 + if (status != PEP_STATUS_OK)
1.209 + return status;
1.210 +
1.211 + message* reset_message = new_message(PEP_dir_outgoing);
1.212 + reset_message->from = own_identity;
1.213 + reset_message->to = new_identity_list(identity_dup(recip)); // ?
1.214 +
1.215 + const char* oldtag = "OLD: ";
1.216 + const char* newtag = "\nNEW: ";
1.217 + const size_t taglens = 11;
1.218 + size_t full_len = taglens + strlen(old_fpr) + strlen(new_fpr) + 2; // \n and \0
1.219 + char* longmsg = calloc(full_len, 1);
1.220 + strlcpy(longmsg, oldtag, full_len);
1.221 + strlcat(longmsg, old_fpr, full_len);
1.222 + strlcat(longmsg, newtag, full_len);
1.223 + strlcat(longmsg, new_fpr, full_len);
1.224 + strlcat(longmsg, "\n", full_len);
1.225 + reset_message->longmsg = longmsg;
1.226 + reset_message->shortmsg = strdup("Key reset");
1.227 +
1.228 + message* output_msg = NULL;
1.229 +
1.230 + status = encrypt_message(session, reset_message, NULL,
1.231 + &output_msg, PEP_enc_PGP_MIME,
1.232 + PEP_encrypt_flag_key_reset_only);
1.233 +
1.234 + if (status == PEP_STATUS_OK)
1.235 + *dst = output_msg;
1.236 +
1.237 + free_message(reset_message);
1.238 + return status;
1.239 +}
1.240 +
1.241 +PEP_STATUS send_key_reset_to_recents(PEP_SESSION session,
1.242 + const char* old_fpr,
1.243 + const char* new_fpr) {
1.244 + assert(old_fpr);
1.245 + assert(new_fpr);
1.246 + assert(session);
1.247 + assert(session->messageToSend || session->sync_session->messageToSend);
1.248 +
1.249 + if (!session || !old_fpr || !new_fpr)
1.250 + return PEP_ILLEGAL_VALUE;
1.251 +
1.252 + messageToSend_t send_cb = send_cb = session->messageToSend;
1.253 + void* sync_obj = session->sync_obj;
1.254 + if (!send_cb) {
1.255 + send_cb = session->sync_session->messageToSend;
1.256 + sync_obj = session->sync_session->sync_obj;
1.257 + }
1.258 + if (!send_cb)
1.259 + return PEP_SYNC_NO_MESSAGE_SEND_CALLBACK;
1.260 +
1.261 + identity_list* recent_contacts = NULL;
1.262 + message* reset_msg = NULL;
1.263 +
1.264 + PEP_STATUS status = get_last_contacted(session, &recent_contacts);
1.265 +
1.266 + if (status != PEP_STATUS_OK)
1.267 + goto pep_free;
1.268 +
1.269 + identity_list* curr_id_ptr = recent_contacts;
1.270 +
1.271 + for (curr_id_ptr = recent_contacts; curr_id_ptr; curr_id_ptr = curr_id_ptr->next) {
1.272 + pEp_identity* curr_id = curr_id_ptr->ident;
1.273 +
1.274 + if (!curr_id)
1.275 + break;
1.276 +
1.277 + const char* user_id = curr_id->user_id;
1.278 +
1.279 + // Should be impossible, but?
1.280 + if (!user_id)
1.281 + continue;
1.282 +
1.283 + // Check if it's us - if so, pointless...
1.284 + if (is_me(session, curr_id))
1.285 + continue;
1.286 +
1.287 + // Check if they've already been told - this shouldn't be the case, but...
1.288 + bool contacted = false;
1.289 + status = has_key_reset_been_sent(session, user_id, old_fpr, &contacted);
1.290 + if (status != PEP_STATUS_OK)
1.291 + goto pep_free;
1.292 +
1.293 + if (contacted)
1.294 + continue;
1.295 +
1.296 + // if not, make em a message
1.297 + reset_msg = NULL;
1.298 +
1.299 + status = create_standalone_key_reset_message(session,
1.300 + &reset_msg,
1.301 + curr_id,
1.302 + old_fpr,
1.303 + new_fpr);
1.304 +
1.305 + if (status == PEP_CANNOT_FIND_IDENTITY) { // this is ok, just means we never mailed them
1.306 + status = PEP_STATUS_OK;
1.307 + continue;
1.308 + }
1.309 +
1.310 + if (status != PEP_STATUS_OK) {
1.311 + free(reset_msg);
1.312 + goto pep_free;
1.313 + }
1.314 +
1.315 + // insert into queue
1.316 + status = send_cb(sync_obj, reset_msg);
1.317 +
1.318 + if (status != PEP_STATUS_OK) {
1.319 + free(reset_msg);
1.320 + goto pep_free;
1.321 + }
1.322 +
1.323 + // Put into notified DB
1.324 + status = set_reset_contact_notified(session, old_fpr, user_id);
1.325 + if (status != PEP_STATUS_OK)
1.326 + goto pep_free;
1.327 + }
1.328 +
1.329 +pep_free:
1.330 + free_identity_list(recent_contacts);
1.331 + return status;
1.332 +}
1.333 +
1.334 +DYNAMIC_API PEP_STATUS key_reset(
1.335 + PEP_SESSION session,
1.336 + const char* key_id,
1.337 + pEp_identity* ident
1.338 + )
1.339 +{
1.340 + if (!session)
1.341 + return PEP_ILLEGAL_VALUE;
1.342 +
1.343 + PEP_STATUS status = PEP_STATUS_OK;
1.344 +
1.345 + char* fpr_copy = NULL;
1.346 + char* own_id = NULL;
1.347 + char* new_key = NULL;
1.348 + identity_list* key_idents = NULL;
1.349 + stringlist_t* keys = NULL;
1.350 +
1.351 + if (!EMPTYSTR(key_id)) {
1.352 + fpr_copy = strdup(key_id);
1.353 + if (!fpr_copy)
1.354 + return PEP_OUT_OF_MEMORY;
1.355 + }
1.356 +
1.357 + if (!ident) {
1.358 + // Get list of own identities
1.359 + status = get_default_own_userid(session, &own_id);
1.360 + if (status != PEP_STATUS_OK)
1.361 + goto pep_free;
1.362 +
1.363 + if (EMPTYSTR(fpr_copy)) {
1.364 + status = get_all_keys_for_user(session, own_id, &keys);
1.365 + if (status == PEP_STATUS_OK) {
1.366 + stringlist_t* curr_key;
1.367 + for (curr_key = keys; curr_key && curr_key->value; curr_key = curr_key->next) {
1.368 + status = key_reset(session, curr_key->value, NULL);
1.369 + if (status != PEP_STATUS_OK)
1.370 + break;
1.371 + }
1.372 + }
1.373 + goto pep_free;
1.374 + } // otherwise, we have a specific fpr to process
1.375 +
1.376 + // fpr_copy exists, so... let's go.
1.377 + // Process own identities with this fpr
1.378 + status = get_identities_by_main_key_id(session, fpr_copy, &key_idents);
1.379 +
1.380 + if (status == PEP_STATUS_OK) {
1.381 + // have ident list, or should
1.382 + identity_list* curr_ident;
1.383 + for (curr_ident = key_idents; curr_ident && curr_ident->ident;
1.384 + curr_ident = curr_ident->next) {
1.385 + pEp_identity* this_identity = curr_ident->ident;
1.386 + status = key_reset(session, fpr_copy, this_identity);
1.387 + if (status != PEP_STATUS_OK)
1.388 + break;
1.389 + }
1.390 + }
1.391 + goto pep_free;
1.392 + }
1.393 + else { // an identity was specified.
1.394 + if (is_me(session, ident)) {
1.395 + // FIXME: make sure this IS our fpr?
1.396 +
1.397 + // If it got sent in with an empty fpr...
1.398 + if (EMPTYSTR(fpr_copy)) {
1.399 + //
1.400 + // if (!EMPTYSTR(ident->fpr))
1.401 + // fpr_copy = strdup(ident->fpr);
1.402 + status = _myself(session, ident, false, true);
1.403 + if (status == PEP_STATUS_OK && ident->fpr)
1.404 + fpr_copy = strdup(ident->fpr);
1.405 + else {
1.406 + // last resort?
1.407 + // Get list of own identities
1.408 + char* own_id = NULL;
1.409 + status = get_default_own_userid(session, &own_id);
1.410 + if (status == PEP_STATUS_OK)
1.411 + status = get_user_default_key(session, own_id, &fpr_copy);
1.412 + if (status != PEP_STATUS_OK || EMPTYSTR(fpr_copy)) {
1.413 + free(own_id);
1.414 + return (status == PEP_STATUS_OK ? PEP_KEY_NOT_FOUND : status);
1.415 + }
1.416 + }
1.417 + }
1.418 +
1.419 + free(ident->fpr);
1.420 + ident->fpr = fpr_copy;
1.421 + // Create revocation
1.422 + status = revoke_key(session, fpr_copy, NULL);
1.423 + // generate new key
1.424 + if (status == PEP_STATUS_OK) {
1.425 + ident->fpr = NULL;
1.426 + status = generate_keypair(session, ident);
1.427 + }
1.428 + if (status == PEP_STATUS_OK) {
1.429 + new_key = strdup(ident->fpr);
1.430 + status = set_own_key(session, ident, new_key);
1.431 + }
1.432 + // mistrust fpr from trust
1.433 + ident->fpr = fpr_copy;
1.434 +
1.435 + ident->comm_type = PEP_ct_mistrusted;
1.436 + status = set_trust(session, ident);
1.437 + ident->fpr = NULL;
1.438 +
1.439 + // Done with old use of ident.
1.440 + if (status == PEP_STATUS_OK) {
1.441 + // Update fpr for outgoing
1.442 + status = myself(session, ident);
1.443 + }
1.444 +
1.445 + if (status == PEP_STATUS_OK)
1.446 + // cascade that mistrust for anyone using this key
1.447 + status = mark_as_compromised(session, fpr_copy);
1.448 + if (status == PEP_STATUS_OK)
1.449 + status = remove_fpr_as_default(session, fpr_copy);
1.450 + if (status == PEP_STATUS_OK)
1.451 + status = add_mistrusted_key(session, fpr_copy);
1.452 + // add to revocation list
1.453 + if (status == PEP_STATUS_OK)
1.454 + status = set_revoked(session, fpr_copy, new_key, time(NULL));
1.455 + // for all active communication partners:
1.456 + // active_send revocation
1.457 + if (status == PEP_STATUS_OK)
1.458 + status = send_key_reset_to_recents(session, fpr_copy, new_key);
1.459 +
1.460 + }
1.461 + else { // not is_me
1.462 + // remove fpr from all identities
1.463 + // remove fpr from all users
1.464 + if (status == PEP_STATUS_OK)
1.465 + status = remove_fpr_as_default(session, fpr_copy);
1.466 + // delete key from DB
1.467 + if (status == PEP_STATUS_OK) {};
1.468 +// status = delete_keypair(session, fpr_copy);
1.469 + // N.B. If this key is being replaced by something else, it
1.470 + // is done outside of this function.
1.471 + }
1.472 + }
1.473 +
1.474 +pep_free:
1.475 + free(fpr_copy);
1.476 + free(own_id);
1.477 + free_identity_list(key_idents);
1.478 + free_stringlist(keys);
1.479 + free(new_key);
1.480 + return status;
1.481 +}