src/key_reset.c
branchENGINE-398
changeset 2947 4b525ec0f95c
child 2948 3f66f366dc5f
     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 +}