src/key_reset.c
author Krista 'DarthMama' Bennett <krista@pep.foundation>
Sat, 11 Jan 2020 14:07:06 +0100
branchkrista-local-7-Jan-2020
changeset 4322 76806e5aebb2
parent 4321 795490894153
child 4327 efeb9e634e14
permissions -rw-r--r--
Fixed misuse of set_own_key
krista@2947
     1
// This file is under GNU General Public License 3.0
krista@2947
     2
// see LICENSE.txt
krista@2947
     3
krista@2947
     4
#include "pEp_internal.h"
krista@2947
     5
#include "dynamic_api.h"
krista@2947
     6
#include "message_api.h"
krista@3967
     7
#include "key_reset.h"
vb@4257
     8
#include "distribution_codec.h"
vb@4257
     9
#include "map_asn1.h"
krista@4312
    10
#include "keymanagement.h"
krista@4312
    11
#include "baseprotocol.h"
vb@4257
    12
#include "../asn.1/Distribution.h"
krista@4312
    13
#include "Sync_impl.h" // this seems... bad
krista@2947
    14
krista@2947
    15
#include <string.h>
krista@2947
    16
#include <stdlib.h>
krista@2947
    17
vb@4275
    18
// FIXME: these should be taken from sync/Distribution.fsm
vb@4275
    19
vb@4275
    20
#define KEY_RESET_MAJOR_VERSION 1L
vb@4275
    21
#define KEY_RESET_MINOR_VERSION 0L
vb@4275
    22
krista@4312
    23
static PEP_STATUS _generate_keyreset_command_message(PEP_SESSION session,
krista@4312
    24
                                                     const pEp_identity* from_ident,
krista@4312
    25
                                                     const pEp_identity* to_ident,
krista@4312
    26
                                                     const char* old_fpr,
krista@4312
    27
                                                     const char* new_fpr,
krista@4312
    28
                                                     bool is_private,
krista@4312
    29
                                                     message** dst) {
krista@4312
    30
                                                                                                                  
krista@4312
    31
    if (!session || !from_ident || !old_fpr || !new_fpr || !dst)
krista@4312
    32
        return PEP_ILLEGAL_VALUE;
krista@4312
    33
krista@4312
    34
    // safe cast
krista@4312
    35
    if (!is_me(session, (pEp_identity*)from_ident))
krista@4312
    36
        return PEP_ILLEGAL_VALUE;
krista@4312
    37
krista@4312
    38
    PEP_STATUS status = PEP_STATUS_OK;
krista@4312
    39
        
krista@4312
    40
    *dst = NULL;
krista@4321
    41
        
krista@4315
    42
    message* msg = NULL;
krista@4315
    43
    
krista@4312
    44
    // Ok, generate payload here...
krista@4312
    45
    pEp_identity* outgoing_ident = identity_dup(from_ident);
krista@4312
    46
    if (!outgoing_ident)
krista@4312
    47
        return PEP_OUT_OF_MEMORY;
krista@4312
    48
    free(outgoing_ident->fpr);
krista@4312
    49
    outgoing_ident->fpr = strdup(old_fpr);
krista@4312
    50
    if (!outgoing_ident->fpr)
krista@4312
    51
        return PEP_OUT_OF_MEMORY;
krista@4312
    52
        
krista@4312
    53
    // Need payload now
krista@4312
    54
    keyreset_command* kr_command = new_keyreset_command(outgoing_ident, new_fpr);
krista@4312
    55
    keyreset_command_list* kr_list = new_keyreset_command_list(kr_command);
krista@4312
    56
    char* payload = NULL;
krista@4312
    57
    size_t size = 0;
krista@4312
    58
    status = key_reset_commands_to_PER(kr_list, &payload, &size);
krista@4312
    59
    status = base_prepare_message(session, outgoing_ident, to_ident,
krista@4312
    60
                                  BASE_KEYRESET, payload, size, NULL,
krista@4312
    61
                                  &msg);
krista@4312
    62
    if (status) {
krista@4312
    63
        free(msg);
krista@4312
    64
        return status;
krista@4312
    65
    }    
krista@4312
    66
    if (!msg)
krista@4312
    67
        return PEP_OUT_OF_MEMORY;
krista@4312
    68
    if (!msg->attachments)
krista@4312
    69
        return PEP_UNKNOWN_ERROR;
krista@4312
    70
    
krista@4321
    71
    char* key_material_old = NULL;
krista@4321
    72
    char* key_material = NULL;    
krista@4312
    73
    size_t datasize = 0;
krista@4321
    74
    
krista@4321
    75
    status = export_key(session, old_fpr, &key_material_old, &datasize);
krista@4321
    76
    key_material = key_material_old;
krista@4321
    77
    if (datasize > 0 && key_material) {         
krista@4321
    78
        if (status)
krista@4321
    79
            return status;
krista@4321
    80
krista@4321
    81
        if (!msg->attachments)
krista@4321
    82
            msg->attachments = new_bloblist(key_material, datasize, 
krista@4321
    83
                                            "application/pgp-keys",
krista@4321
    84
                                            "file://pEpkey_old.asc");
krista@4321
    85
        else                                    
krista@4321
    86
            bloblist_add(msg->attachments, key_material, datasize, "application/pgp-keys",
krista@4321
    87
                                                                   "file://pEpkey_old.asc");
krista@4321
    88
    }
krista@4321
    89
    datasize = 0;
krista@4321
    90
    key_material = NULL;
krista@4321
    91
                                                                      
krista@4312
    92
    status = export_key(session, new_fpr, &key_material, &datasize);
krista@4312
    93
krista@4321
    94
    if (datasize > 0 && key_material) {         
krista@4321
    95
        if (status)
krista@4312
    96
            return status;
krista@4321
    97
krista@4321
    98
        if (!msg->attachments)
krista@4321
    99
            msg->attachments = new_bloblist(key_material, datasize, 
krista@4321
   100
                                            "application/pgp-keys",
krista@4321
   101
                                            "file://pEpkey_pub.asc");
krista@4321
   102
        else                                    
krista@4321
   103
            bloblist_add(msg->attachments, key_material, datasize, "application/pgp-keys",
krista@4321
   104
                                                                   "file://pEpkey_pub.asc");
krista@4312
   105
                        
krista@4312
   106
        key_material = NULL;
krista@4312
   107
        datasize = 0;    
krista@4312
   108
        if (is_private) {
krista@4312
   109
            status = export_secret_key(session, new_fpr, &key_material, &datasize);    
krista@4321
   110
            if (status)
krista@4312
   111
                return status;
krista@4321
   112
            if (datasize > 0 && key_material) {
krista@4321
   113
                bloblist_add(msg->attachments, key_material, datasize, "application/pgp-keys",
krista@4321
   114
                                                                       "file://pEpkey_priv.asc");
krista@4321
   115
            }                                                      
krista@4312
   116
        }    
krista@4312
   117
    }    
krista@4315
   118
    if (msg)
krista@4315
   119
        *dst = msg;
krista@4312
   120
    return status;
krista@4312
   121
}
krista@4312
   122
krista@2947
   123
PEP_STATUS has_key_reset_been_sent(
krista@2947
   124
        PEP_SESSION session, 
krista@4312
   125
        const char* from_addr,
krista@2947
   126
        const char* user_id, 
krista@2947
   127
        const char* revoked_fpr,
krista@2947
   128
        bool* contacted)
krista@2947
   129
{
krista@2947
   130
    assert(session);
krista@2947
   131
    assert(contacted);
krista@2947
   132
    assert(user_id);
krista@2947
   133
    assert(revoked_fpr);
krista@2947
   134
    assert(!EMPTYSTR(user_id));
krista@2947
   135
krista@4312
   136
    if (!session || !contacted || EMPTYSTR(from_addr) || EMPTYSTR(revoked_fpr) || EMPTYSTR(user_id))
krista@2947
   137
        return PEP_ILLEGAL_VALUE;
krista@2947
   138
    
krista@2947
   139
    *contacted = false;
krista@2947
   140
                    
krista@2947
   141
    char* alias_default = NULL;
krista@2947
   142
    
krista@2947
   143
    PEP_STATUS status = get_userid_alias_default(session, user_id, &alias_default);
krista@2947
   144
    
krista@2947
   145
    if (status == PEP_CANNOT_FIND_ALIAS || EMPTYSTR(alias_default)) {
krista@2947
   146
        free(alias_default);
krista@2947
   147
        alias_default = strdup(user_id);
krista@2947
   148
    }
krista@2947
   149
    
krista@2947
   150
    sqlite3_reset(session->was_id_for_revoke_contacted);
krista@2947
   151
    sqlite3_bind_text(session->was_id_for_revoke_contacted, 1, revoked_fpr, -1,
krista@2947
   152
            SQLITE_STATIC);
krista@4312
   153
    sqlite3_bind_text(session->was_id_for_revoke_contacted, 2, from_addr, -1,
krista@4312
   154
            SQLITE_STATIC);        
krista@4312
   155
    sqlite3_bind_text(session->was_id_for_revoke_contacted, 3, user_id, -1,
krista@2947
   156
            SQLITE_STATIC);        
krista@4142
   157
    int result = sqlite3_step(session->was_id_for_revoke_contacted);
krista@2947
   158
    switch (result) {
krista@2947
   159
        case SQLITE_ROW: {
krista@2947
   160
            *contacted = (sqlite3_column_int(session->was_id_for_revoke_contacted, 0) != 0);
krista@2947
   161
            break;
krista@2947
   162
        }
krista@2947
   163
        default:
krista@2947
   164
            sqlite3_reset(session->was_id_for_revoke_contacted);
krista@2947
   165
            free(alias_default);
krista@2947
   166
            return PEP_UNKNOWN_DB_ERROR;
krista@2947
   167
    }
krista@2947
   168
krista@2947
   169
    sqlite3_reset(session->was_id_for_revoke_contacted);
krista@2947
   170
    return PEP_STATUS_OK;
krista@2947
   171
}
krista@2947
   172
krista@2947
   173
PEP_STATUS set_reset_contact_notified(
krista@2947
   174
        PEP_SESSION session,
krista@4312
   175
        const char* own_address,
krista@2947
   176
        const char* revoke_fpr,
krista@2947
   177
        const char* contact_id
krista@2947
   178
    )
krista@2947
   179
{
krista@2947
   180
    PEP_STATUS status = PEP_STATUS_OK;
krista@2947
   181
    
krista@4312
   182
    assert(session && !EMPTYSTR(own_address) && !EMPTYSTR(revoke_fpr) && !EMPTYSTR(contact_id));
krista@2947
   183
    
krista@4312
   184
    if (!session || EMPTYSTR(own_address) || EMPTYSTR(revoke_fpr) || EMPTYSTR(contact_id))
krista@2947
   185
        return PEP_ILLEGAL_VALUE;
krista@2947
   186
    
krista@2947
   187
    sqlite3_reset(session->set_revoke_contact_as_notified);
krista@2947
   188
    sqlite3_bind_text(session->set_revoke_contact_as_notified, 1, revoke_fpr, -1, 
krista@2947
   189
            SQLITE_STATIC);
krista@4312
   190
    sqlite3_bind_text(session->set_revoke_contact_as_notified, 2, own_address, -1, 
krista@4312
   191
            SQLITE_STATIC);            
krista@4312
   192
    sqlite3_bind_text(session->set_revoke_contact_as_notified, 3, contact_id, -1,
krista@2947
   193
            SQLITE_STATIC);
krista@2947
   194
krista@2947
   195
    int result;
krista@2947
   196
    
krista@4142
   197
    result = sqlite3_step(session->set_revoke_contact_as_notified);
krista@2947
   198
    switch (result) {
krista@2947
   199
        case SQLITE_DONE:
krista@2947
   200
            status = PEP_STATUS_OK;
krista@2947
   201
            break;
krista@2947
   202
            
krista@2947
   203
        default:
krista@2947
   204
            status = PEP_UNKNOWN_DB_ERROR;
krista@2947
   205
    }
krista@2947
   206
    
krista@2947
   207
    sqlite3_reset(session->set_revoke_contact_as_notified);
krista@2947
   208
    return status;    
krista@2947
   209
}
krista@2947
   210
krista@4312
   211
// FIXME: fpr ownership
krista@2947
   212
PEP_STATUS receive_key_reset(PEP_SESSION session,
krista@2947
   213
                             message* reset_msg) {
krista@2947
   214
krista@4312
   215
    if (!session || !reset_msg || !reset_msg->_sender_fpr)
krista@2947
   216
        return PEP_ILLEGAL_VALUE;
krista@2947
   217
krista@4312
   218
    PEP_STATUS status = PEP_STATUS_OK;
krista@4312
   219
krista@4312
   220
    stringlist_t* keylist = NULL;
krista@4312
   221
    
krista@4312
   222
    char* sender_fpr = reset_msg->_sender_fpr;
krista@4312
   223
krista@4312
   224
    bool revoked = false;
krista@4312
   225
krista@4315
   226
    // Check to see if sender fpr is revoked already - if this was 
krista@4315
   227
    // from us, we won't have done it yet for obvious reasons (i.e. 
krista@4315
   228
    // we need to verify it's from us before we accept someone telling
krista@4315
   229
    // us to reset our private key), and if this was from someone else,
krista@4315
   230
    // a key reset message will be signed by their new key, because 
krista@4315
   231
    // we presume the old one was compromised (and we remove trust from 
krista@4315
   232
    // the replacement key until verified)
krista@4312
   233
    status = key_revoked(session, sender_fpr, &revoked); 
krista@4312
   234
    
krista@4312
   235
    if (status != PEP_STATUS_OK)
krista@4312
   236
        return status;
krista@4312
   237
krista@4312
   238
    // Bail if revoked
krista@4312
   239
    if (revoked) {
krista@4312
   240
        return PEP_ILLEGAL_VALUE; // could be an attack            
krista@4312
   241
    }
krista@4312
   242
    // Otherwise, bail
krista@4312
   243
    else {
krista@4312
   244
        bool mistrusted = false;
krista@4312
   245
        status = is_mistrusted_key(session, sender_fpr, &mistrusted);
krista@4312
   246
        
krista@4312
   247
        if (status != PEP_STATUS_OK)
krista@4312
   248
            return status;
krista@4312
   249
        
krista@4312
   250
        if (mistrusted)
krista@4312
   251
            return PEP_ILLEGAL_VALUE;
krista@4312
   252
    }
krista@4312
   253
krista@4312
   254
    
krista@4312
   255
    // Parse reset message
krista@4312
   256
    
krista@2947
   257
    pEp_identity* sender_id = reset_msg->from;
krista@4312
   258
                            
krista@2947
   259
    if (!sender_id)
krista@2947
   260
        return PEP_MALFORMED_KEY_RESET_MSG;
krista@4312
   261
krista@4312
   262
    if (is_me(session, sender_id)) {
krista@4312
   263
        // first off, we need to make sure we're up-to-date
krista@4312
   264
        status = myself(session, sender_id);        
krista@4312
   265
    }
krista@4312
   266
    else {    
krista@4312
   267
        status = update_identity(session, sender_id);
krista@4312
   268
        if (!sender_id->user_id)
krista@4312
   269
            return PEP_UNKNOWN_ERROR;
krista@4312
   270
    }
krista@4312
   271
    
krista@4312
   272
    bool sender_own_key = false;
krista@4315
   273
    bool from_me = is_me(session, sender_id);
krista@4312
   274
    
krista@4312
   275
    if (is_me(session, sender_id)) {
krista@4312
   276
        // Do own-reset-checks
krista@4312
   277
        status = is_own_key(session, sender_fpr, &sender_own_key);
krista@2947
   278
        
krista@4312
   279
        if (status != PEP_STATUS_OK)
krista@4312
   280
            return status;
krista@4312
   281
        
krista@4312
   282
        // Should we mistrust the sender_fpr here??
krista@4312
   283
        if (!sender_own_key) 
krista@4312
   284
            return PEP_ILLEGAL_VALUE; // actually, this is an attack                
krista@2947
   285
        
krista@4312
   286
        // Make sure it's a TRUSTED own key
krista@4312
   287
        char* keyholder = sender_id->fpr;
krista@4312
   288
        
krista@4312
   289
        sender_id->fpr = sender_fpr;                     
krista@4312
   290
        status = get_trust(session, sender_id);
krista@4312
   291
        sender_id->fpr = keyholder;
krista@4312
   292
            
krista@4312
   293
        if (sender_id->comm_type < PEP_ct_pEp)
krista@4312
   294
            return PEP_ILLEGAL_VALUE;
krista@4312
   295
    }
krista@4312
   296
        
krista@2947
   297
    status = PEP_STATUS_OK;
krista@2947
   298
    char* old_fpr = NULL;
krista@2947
   299
    char* new_fpr = NULL;
krista@2947
   300
    
krista@4312
   301
    size_t size = 0;
krista@4312
   302
    const char* payload = NULL;
krista@4312
   303
krista@4315
   304
    char* not_used_fpr = NULL;
krista@4312
   305
    status = base_extract_message(session,
krista@4312
   306
                                  reset_msg,
krista@4312
   307
                                  BASE_KEYRESET,
krista@4312
   308
                                  &size,
krista@4312
   309
                                  &payload,
krista@4315
   310
                                  &not_used_fpr);
krista@4312
   311
                                  
krista@4312
   312
    if (status != PEP_STATUS_OK)
krista@4312
   313
        return status;
krista@4312
   314
        
krista@4312
   315
    if (!payload || size == 0)
krista@4312
   316
        return PEP_MALFORMED_KEY_RESET_MSG;
krista@4312
   317
        
krista@4312
   318
    keyreset_command_list* resets = NULL; 
krista@4312
   319
    
krista@4312
   320
    status = PER_to_key_reset_commands(payload, size, &resets);
krista@4312
   321
krista@4312
   322
    if (status != PEP_STATUS_OK)
krista@4312
   323
        return status;
krista@4312
   324
        
krista@4312
   325
    if (!resets)
krista@4312
   326
        return PEP_MALFORMED_KEY_RESET_MSG;
krista@4312
   327
krista@4312
   328
    keyreset_command_list* curr_cl = resets;
krista@4315
   329
krista@4315
   330
    // Ok, go through the list of reset commands. Right now, this 
krista@4315
   331
    // is actually only one, but could be more later.
krista@4312
   332
    for ( ; curr_cl && curr_cl->command; curr_cl = curr_cl->next) {    
krista@4312
   333
        keyreset_command* curr_cmd = curr_cl->command;
krista@4312
   334
        if (!curr_cmd || !curr_cmd->ident || !curr_cmd->ident->fpr ||
krista@4312
   335
            !curr_cmd->ident->address) {
krista@4312
   336
            return PEP_MALFORMED_KEY_RESET_MSG;        
krista@4312
   337
        }
krista@4315
   338
        pEp_identity* curr_ident = curr_cmd->ident;
krista@4315
   339
        
krista@4315
   340
        old_fpr = curr_ident->fpr;
krista@4321
   341
        new_fpr = strdup(curr_cmd->new_key);
krista@4321
   342
        
krista@4321
   343
        bool is_old_own = false;
krista@4321
   344
        // if it's our key and the old one is revoked, we skip it.
krista@4321
   345
        // Sorry, them's the rules/
krista@4321
   346
        if (sender_own_key) {
krista@4321
   347
            status = is_own_key(session, old_fpr, &is_old_own);
krista@4321
   348
            if (is_old_own) {
krista@4321
   349
                bool old_revoked = false;
krista@4321
   350
                status = key_revoked(session, old_fpr, &old_revoked);
krista@4321
   351
                if (old_revoked)
krista@4321
   352
                    continue;
krista@4321
   353
            }
krista@4321
   354
        }
krista@4315
   355
krista@4312
   356
        // Make sure that this key is at least one we associate 
krista@4312
   357
        // with the sender. FIXME: check key election interaction
krista@4312
   358
        // N.B. If we ever allow ourselves to send resets to ourselves
krista@4312
   359
        // for not-own stuff, this will have to be revised
krista@4312
   360
        
krista@4312
   361
        status = find_keys(session, new_fpr, &keylist);
krista@4312
   362
        if (status != PEP_STATUS_OK)
krista@4312
   363
            goto pEp_free;
krista@4312
   364
        if (!keylist) {
krista@4312
   365
            status = PEP_MALFORMED_KEY_RESET_MSG;
krista@4312
   366
            goto pEp_free;
krista@4312
   367
        }
krista@4312
   368
        
krista@4312
   369
        // We need to update the identity to get the user_id
krista@4312
   370
        curr_ident->fpr = NULL; // ensure old_fpr is preserved
krista@4315
   371
        free(curr_ident->user_id);
krista@4315
   372
        curr_ident->user_id = NULL;
krista@4312
   373
        status = update_identity(session, curr_ident);
krista@4312
   374
        
krista@4315
   375
        // Ok, now check the old fpr to see if we have an entry for it
krista@4312
   376
        // temp fpr set for function call
krista@4315
   377
        curr_ident->fpr = old_fpr;
krista@4312
   378
        status = get_trust(session, curr_ident);
krista@4315
   379
        if (status != PEP_STATUS_OK)
krista@4315
   380
            return status;
krista@4312
   381
        
krista@4312
   382
        PEP_comm_type ct_result = curr_ident->comm_type;
krista@4312
   383
krista@4312
   384
        // Basically, see if fpr is even in the database
krista@4312
   385
        // for this user - we'll get PEP_ct_unknown if it isn't
krista@4315
   386
        if (ct_result == PEP_ct_unknown)
krista@4312
   387
            return PEP_KEY_NOT_RESET;
krista@4315
   388
        
krista@4315
   389
        // Alright, so we have a key to reset. Good.
krista@4315
   390
        
krista@4315
   391
        // If this is a non-own user, for NOW, we presume key reset 
krista@4315
   392
        // by email for non-own keys is ONLY in case of revoke-and-replace. 
krista@4315
   393
        // This means we have, at a *minimum*, an object that once 
krista@4315
   394
        // required the initial private key in order to replace that key 
krista@4315
   395
        // with another.
krista@4315
   396
        //
krista@4315
   397
        // The limitations on what this guarantees are known - this does 
krista@4315
   398
        // not prevent, for example, replay attacks from someone with 
krista@4315
   399
        // access to the original revocation cert are possible if they 
krista@4315
   400
        // get to us before we receive this object from the original sender.
krista@4315
   401
        // The best we can do in this case is to NOT trust the new key.
krista@4315
   402
        // It will be used by default, but if the original was trusted,
krista@4315
   403
        // the rating will visibly change for the sender, and even if it was 
krista@4315
   404
        // not, if we do use it, the sender can report unreadable mails to us 
krista@4315
   405
        // and detect it that way. FIXME: We may need to have some kind 
krista@4315
   406
        // of even alert the user when such a change occurs for their contacts
krista@4315
   407
        //
krista@4315
   408
        // If this is from US, we already made sure that the sender_fpr 
krista@4315
   409
        // was a valid own key, so we don't consider it here.
krista@4315
   410
        if (!from_me) {
krista@4315
   411
            revoked = false;
krista@4315
   412
            status = key_revoked(session, old_fpr, &revoked); 
krista@4315
   413
krista@4315
   414
            if (!revoked)
krista@4315
   415
                return PEP_KEY_NOT_RESET;            
krista@4315
   416
krista@4315
   417
            // Also don't let someone change the replacement fpr 
krista@4315
   418
            // if the replacement fpr was also revoked - we really need 
krista@4315
   419
            // to detect that something fishy is going on at this point
krista@4315
   420
            // FIXME: ensure that PEP_KEY_NOT_RESET responses to 
krista@4315
   421
            // automated key reset functions are propagated upward - 
krista@4315
   422
            // app should be made aware if someone is trying to reset someone 
krista@4315
   423
            // else's key and it's failing for some reason.
krista@4315
   424
            revoked = false;
krista@4315
   425
            status = key_revoked(session, new_fpr, &revoked); 
krista@4315
   426
krista@4315
   427
            if (revoked)
krista@4315
   428
                return PEP_KEY_NOT_RESET;                        
krista@4315
   429
        }
krista@4315
   430
        
krista@4315
   431
        // temp fpr set for function call
krista@4315
   432
        // curr_ident->fpr = (char*)sender_fpr;
krista@4315
   433
        // status = get_trust(session, curr_ident);
krista@4315
   434
        // 
krista@4315
   435
        // PEP_comm_type ct_result = curr_ident->comm_type;
krista@4315
   436
        // 
krista@4315
   437
        // // Basically, see if fpr is even in the database
krista@4315
   438
        // // for this user - we'll get PEP_ct_unknown if it isn't
krista@4315
   439
        // if (ct_result < PEP_ct_strong_but_unconfirmed)
krista@4315
   440
        //     return PEP_KEY_NOT_RESET;            
krista@4312
   441
        // Now check the fpr we're trying to change (old_fpr), which we reset a few lines above -
krista@4312
   442
        // again, if it doesn't belong to the user, we won't use it.
krista@4315
   443
        // status = get_trust(session, curr_ident);
krista@4315
   444
        // 
krista@4315
   445
        // if (status != PEP_STATUS_OK)
krista@4315
   446
        //     return status;            
krista@4315
   447
        // 
krista@4315
   448
        // if (curr_ident->comm_type < PEP_ct_strong_but_unconfirmed)
krista@4315
   449
        //     return PEP_KEY_NOT_RESET;
krista@4312
   450
            
krista@4312
   451
        // Hooray! We apparently now are dealing with keys 
krista@4315
   452
        // belonging to the user from a message at least marginally
krista@4315
   453
        // from the user
krista@4312
   454
        if (!sender_own_key) {
krista@4315
   455
            // Clear all info (ALSO REMOVES OLD KEY RIGHT NOW!!!)            
krista@4312
   456
            status = key_reset(session, old_fpr, curr_ident);
krista@4312
   457
            if (status != PEP_STATUS_OK)
krista@4312
   458
                return status;
krista@4312
   459
                                
krista@4312
   460
            // Make new key the default    
krista@4312
   461
            curr_ident->fpr = new_fpr;
krista@2947
   462
    
krista@4312
   463
            // This only sets as the default, does NOT TRUST IN ANY WAY
krista@4315
   464
            PEP_comm_type new_key_rating = PEP_ct_unknown;
krista@4315
   465
            
krista@4315
   466
            // No key is ever returned as "confirmed" from here - it's based on raw key
krista@4315
   467
            status = get_key_rating(session, new_fpr, &new_key_rating);
krista@4315
   468
            if (status != PEP_STATUS_OK)
krista@4315
   469
                return status;
krista@4315
   470
krista@4315
   471
            if (new_key_rating >= PEP_ct_strong_but_unconfirmed) {
krista@4315
   472
                bool is_pEp = false;
krista@4315
   473
                status = is_pEp_user(session, curr_ident, &is_pEp);
krista@4315
   474
                if (is_pEp)
krista@4315
   475
                    curr_ident->comm_type = PEP_ct_pEp_unconfirmed;
krista@4315
   476
                else    
krista@4315
   477
                    curr_ident->comm_type = new_key_rating & (~PEP_ct_confirmed);
krista@4316
   478
            }
krista@4316
   479
            else
krista@4316
   480
                curr_ident->comm_type = new_key_rating;
krista@4316
   481
                
krista@4312
   482
            status = set_identity(session, curr_ident);  
krista@4312
   483
            if (status != PEP_STATUS_OK)
krista@4312
   484
                goto pEp_free; 
krista@4312
   485
        }    
krista@4312
   486
        else {
krista@4312
   487
            // set new key as the default for this identity
krista@4312
   488
            // N.B. If for some reason this is only a pubkey,
krista@4312
   489
            // then so be it - but we need to double-check to 
krista@4312
   490
            // ensure that in this case, we end up with a private one,
krista@4312
   491
            // so talk to vb about this.
krista@4312
   492
            // Make new key the default    
krista@4322
   493
            
krista@4322
   494
            // This is REQUIRED for set_own_key (see doc)
krista@4322
   495
            curr_ident->fpr = NULL;
krista@4322
   496
            
krista@4312
   497
            status = set_own_key(session, curr_ident, new_fpr);
krista@2947
   498
            
krista@4312
   499
            if (status != PEP_STATUS_OK)
krista@4312
   500
                return status;
krista@4321
   501
            
krista@4321
   502
            status = myself(session, curr_ident);
krista@4312
   503
                
krista@4312
   504
            // key reset on old key
krista@4321
   505
            status = revoke_key(session, old_fpr, NULL);
krista@4321
   506
krista@4312
   507
            if (status != PEP_STATUS_OK)
krista@4321
   508
                goto pEp_free;
krista@4321
   509
        
krista@4321
   510
            // N.B. This sort of sucks because we overwrite this every time.
krista@4321
   511
            // But this case is infrequent and we don't rely on the binding.
krista@4321
   512
krista@4321
   513
            if (status == PEP_STATUS_OK) 
krista@4321
   514
                status = set_revoked(session, old_fpr, curr_ident->fpr, time(NULL));            
krista@4321
   515
krista@4321
   516
            if (status != PEP_STATUS_OK)
krista@4321
   517
                goto pEp_free;
krista@4321
   518
        }    
krista@4312
   519
        old_fpr = NULL;
krista@4321
   520
        free(new_fpr);
krista@4312
   521
        new_fpr = NULL;    
krista@2947
   522
    }
krista@2947
   523
krista@2956
   524
pEp_free:    
krista@2947
   525
    free_stringlist(keylist);    
krista@2947
   526
    free(old_fpr);
krista@2947
   527
    free(new_fpr);
krista@2947
   528
    return status;
krista@2947
   529
}
krista@2947
   530
krista@2947
   531
PEP_STATUS create_standalone_key_reset_message(PEP_SESSION session,
krista@2947
   532
                                               message** dst, 
krista@4312
   533
                                               pEp_identity* own_identity,
krista@2947
   534
                                               pEp_identity* recip,
krista@2947
   535
                                               const char* old_fpr,
krista@2947
   536
                                               const char* new_fpr) {
krista@2947
   537
                                                   
krista@4312
   538
    if (!dst || !own_identity || EMPTYSTR(own_identity->address) 
krista@4312
   539
             || !recip || EMPTYSTR(recip->user_id) 
krista@4312
   540
             || EMPTYSTR(recip->address))
krista@2947
   541
        return PEP_ILLEGAL_VALUE;
krista@2947
   542
krista@4312
   543
    if (EMPTYSTR(old_fpr) || EMPTYSTR(new_fpr))
krista@2947
   544
        return PEP_ILLEGAL_VALUE;
krista@2947
   545
        
krista@2947
   546
    *dst = NULL;
krista@2947
   547
    
krista@4312
   548
    message* reset_msg = NULL;
krista@2947
   549
    
krista@4312
   550
    PEP_STATUS status = _generate_keyreset_command_message(session, own_identity,
krista@4312
   551
                                                           recip,
krista@4312
   552
                                                           old_fpr, new_fpr, false,
krista@4312
   553
                                                           &reset_msg);
krista@4312
   554
                            
krista@4312
   555
    if (status != PEP_STATUS_OK)
krista@4312
   556
        goto pEp_free;
krista@4312
   557
    
krista@4312
   558
    if (!reset_msg)
krista@4312
   559
        return PEP_ILLEGAL_VALUE;
krista@4312
   560
                                                                         
krista@4312
   561
    if (!reset_msg->attachments)
krista@4312
   562
        return PEP_UNKNOWN_ERROR;
krista@2947
   563
    
krista@2947
   564
    message* output_msg = NULL;
krista@2947
   565
    
krista@4312
   566
    status = encrypt_message(session, reset_msg, NULL,
krista@2947
   567
                             &output_msg, PEP_enc_PGP_MIME,
krista@2947
   568
                             PEP_encrypt_flag_key_reset_only);
krista@2947
   569
krista@2947
   570
    if (status == PEP_STATUS_OK)
krista@2947
   571
        *dst = output_msg;
krista@2947
   572
        
krista@4312
   573
pEp_free:
krista@4312
   574
        
krista@4312
   575
    free_message(reset_msg);    
krista@2947
   576
    return status;
krista@2947
   577
}
krista@2947
   578
krista@2947
   579
PEP_STATUS send_key_reset_to_recents(PEP_SESSION session,
krista@4312
   580
                                     pEp_identity* from_ident,
krista@2947
   581
                                     const char* old_fpr, 
krista@2947
   582
                                     const char* new_fpr) {
krista@2947
   583
    assert(old_fpr);
krista@2947
   584
    assert(new_fpr);
krista@2947
   585
    assert(session);
krista@2956
   586
    assert(session->messageToSend);
krista@2947
   587
    
krista@2947
   588
    if (!session || !old_fpr || !new_fpr)
krista@2947
   589
        return PEP_ILLEGAL_VALUE;
krista@2947
   590
krista@2956
   591
    messageToSend_t send_cb = session->messageToSend;
krista@2947
   592
    if (!send_cb)
krista@2947
   593
        return PEP_SYNC_NO_MESSAGE_SEND_CALLBACK;
krista@2947
   594
        
krista@2947
   595
    identity_list* recent_contacts = NULL;
krista@2947
   596
    message* reset_msg = NULL;
krista@2947
   597
krista@2947
   598
    PEP_STATUS status = get_last_contacted(session, &recent_contacts);
krista@2947
   599
    
krista@2947
   600
    if (status != PEP_STATUS_OK)
krista@2956
   601
        goto pEp_free;
krista@2947
   602
                    
krista@2947
   603
    identity_list* curr_id_ptr = recent_contacts;
krista@2947
   604
krista@2947
   605
    for (curr_id_ptr = recent_contacts; curr_id_ptr; curr_id_ptr = curr_id_ptr->next) {
krista@2947
   606
        pEp_identity* curr_id = curr_id_ptr->ident;
krista@2947
   607
        
krista@2947
   608
        if (!curr_id)
krista@2947
   609
            break;
krista@2947
   610
    
krista@2947
   611
        const char* user_id = curr_id->user_id;
krista@2947
   612
        
krista@2947
   613
        // Should be impossible, but?
krista@2947
   614
        if (!user_id)
krista@2947
   615
            continue;
krista@2947
   616
        
krista@2947
   617
        // Check if it's us - if so, pointless...
krista@2947
   618
        if (is_me(session, curr_id))
krista@2947
   619
            continue;
krista@2947
   620
            
krista@2947
   621
        // Check if they've already been told - this shouldn't be the case, but...
krista@2947
   622
        bool contacted = false;
krista@4312
   623
        status = has_key_reset_been_sent(session, from_ident->address, user_id, old_fpr, &contacted);
krista@2947
   624
        if (status != PEP_STATUS_OK)
krista@2956
   625
            goto pEp_free;
krista@2947
   626
    
krista@2947
   627
        if (contacted)
krista@2947
   628
            continue;
krista@2947
   629
            
krista@4312
   630
        // Make sure they've ever *contacted* this address    
krista@4312
   631
        bool in_contact_w_this_address = false;
krista@4315
   632
        status = has_partner_contacted_address(session, curr_id->user_id, from_ident->address,  
krista@4312
   633
                                               &in_contact_w_this_address);
krista@4312
   634
        
krista@4312
   635
        if (!in_contact_w_this_address)
krista@4312
   636
            continue;
krista@4312
   637
            
krista@2947
   638
        // if not, make em a message    
krista@2947
   639
        reset_msg = NULL;
krista@2947
   640
        
krista@2947
   641
        status = create_standalone_key_reset_message(session,
krista@2947
   642
                                                     &reset_msg,
krista@4312
   643
                                                     from_ident,
krista@2947
   644
                                                     curr_id,
krista@2947
   645
                                                     old_fpr,
krista@2947
   646
                                                     new_fpr);
krista@2947
   647
krista@2947
   648
        if (status == PEP_CANNOT_FIND_IDENTITY) { // this is ok, just means we never mailed them 
krista@2947
   649
            status = PEP_STATUS_OK;
krista@2947
   650
            continue; 
krista@2947
   651
        }
krista@2947
   652
            
krista@2947
   653
        if (status != PEP_STATUS_OK) {
krista@2947
   654
            free(reset_msg);
krista@2956
   655
            goto pEp_free;
krista@2947
   656
        }
krista@2947
   657
        
krista@2947
   658
        // insert into queue
krista@2956
   659
        status = send_cb(reset_msg);
krista@2947
   660
krista@2947
   661
        if (status != PEP_STATUS_OK) {
krista@2947
   662
            free(reset_msg);
krista@2956
   663
            goto pEp_free;            
krista@2947
   664
        }
krista@2947
   665
            
krista@2947
   666
        // Put into notified DB
krista@4312
   667
        status = set_reset_contact_notified(session, from_ident->address, old_fpr, user_id);
krista@2947
   668
        if (status != PEP_STATUS_OK)
krista@2956
   669
            goto pEp_free;            
krista@2947
   670
    }
krista@2947
   671
    
krista@2956
   672
pEp_free:
krista@2947
   673
    free_identity_list(recent_contacts);
krista@2947
   674
    return status;
krista@2947
   675
}
krista@2947
   676
krista@3495
   677
DYNAMIC_API PEP_STATUS key_reset_identity(
krista@3495
   678
        PEP_SESSION session,
krista@3503
   679
        pEp_identity* ident,
krista@3503
   680
        const char* fpr        
krista@3495
   681
    )
krista@3495
   682
{
krista@3503
   683
    if (!session || !ident || (ident && (EMPTYSTR(ident->user_id) || EMPTYSTR(ident->address))))
krista@3495
   684
        return PEP_ILLEGAL_VALUE;
krista@3495
   685
    
krista@4312
   686
    return key_reset(session, fpr, ident);    
krista@3495
   687
}
krista@3495
   688
krista@3495
   689
DYNAMIC_API PEP_STATUS key_reset_user(
krista@3495
   690
        PEP_SESSION session,
krista@3503
   691
        const char* user_id,
krista@3503
   692
        const char* fpr        
krista@3495
   693
    )
krista@3495
   694
{
krista@3503
   695
    if (!session || EMPTYSTR(user_id))
krista@3495
   696
        return PEP_ILLEGAL_VALUE;
krista@3495
   697
krista@3503
   698
    pEp_identity* input_ident = new_identity(NULL, NULL, user_id, NULL);
krista@3503
   699
    if (!input_ident)
krista@3503
   700
        return PEP_OUT_OF_MEMORY;
krista@3503
   701
        
krista@3503
   702
    if (is_me(session, input_ident) && EMPTYSTR(fpr))
krista@3503
   703
        return PEP_ILLEGAL_VALUE;
krista@3495
   704
        
krista@4312
   705
    PEP_STATUS status = key_reset(session, fpr, input_ident);
krista@3503
   706
    free_identity(input_ident);
krista@3503
   707
    return status;
krista@3503
   708
}
krista@3495
   709
krista@3503
   710
DYNAMIC_API PEP_STATUS key_reset_all_own_keys(PEP_SESSION session) {
krista@4312
   711
    return key_reset(session, NULL, NULL);
krista@4312
   712
}
krista@4312
   713
krista@4312
   714
krista@4312
   715
static PEP_STATUS _key_reset_device_group_for_shared_key(PEP_SESSION session, 
krista@4312
   716
                                                         identity_list* key_idents, 
krista@4312
   717
                                                         const char* old_key) {
krista@4312
   718
    assert(session);
krista@4312
   719
    assert(key_idents);
krista@4312
   720
    assert(old_key);
krista@4312
   721
    
krista@4312
   722
    if (!session || !key_idents || EMPTYSTR(old_key))
krista@4312
   723
        return PEP_ILLEGAL_VALUE;
krista@4312
   724
        
krista@4312
   725
    messageToSend_t send_cb = session->messageToSend;
krista@4312
   726
    if (!send_cb)
krista@4312
   727
        return PEP_SYNC_NO_MESSAGE_SEND_CALLBACK;
krista@4312
   728
        
krista@4312
   729
    PEP_STATUS status = PEP_STATUS_OK;
krista@4312
   730
        
krista@4312
   731
    // each of these has the same key and needs a new one.
krista@4312
   732
    identity_list* curr_ident;
krista@4312
   733
    for (curr_ident = key_idents; curr_ident && curr_ident->ident; curr_ident = curr_ident->next) {
krista@4312
   734
        if (curr_ident->ident->flags & PEP_idf_devicegroup) {
krista@4312
   735
            pEp_identity* ident = curr_ident->ident;
krista@4312
   736
            free(ident->fpr);
krista@4312
   737
            ident->fpr = NULL;
krista@4312
   738
            status = generate_keypair(session, ident);
krista@4312
   739
            if (status != PEP_STATUS_OK)
krista@4312
   740
                return status;
krista@4312
   741
                
krista@4312
   742
        }
krista@4312
   743
        else {
krista@4312
   744
            status = key_reset(session, old_key, curr_ident->ident); 
krista@4312
   745
        }        
krista@4312
   746
    }
krista@4312
   747
        
krista@4312
   748
    // Ok, everyone's got a new keypair. Hoorah! 
krista@4312
   749
    // generate, sign, and push messages into queue
krista@4312
   750
    for (curr_ident = key_idents; curr_ident && curr_ident->ident; curr_ident = curr_ident->next) {
krista@4312
   751
        if (curr_ident->ident->flags & PEP_idf_devicegroup) {
krista@4312
   752
            message* outmsg = NULL;
krista@4312
   753
            
krista@4312
   754
            // FIXME - do we have to? Maybe check IN the funct
krista@4312
   755
            pEp_identity* to_me = identity_dup(curr_ident->ident);
krista@4312
   756
            if (!to_me)
krista@4312
   757
                return PEP_OUT_OF_MEMORY;
krista@4312
   758
                
krista@4312
   759
            status = _generate_keyreset_command_message(session, curr_ident->ident, to_me,
krista@4312
   760
                                                        old_key, curr_ident->ident->fpr,
krista@4312
   761
                                                        true, &outmsg);
krista@4312
   762
            
krista@4312
   763
            message* enc_msg = NULL;
krista@4312
   764
            
krista@4312
   765
            // encrypt this baby and get out
krista@4312
   766
            // extra keys???
krista@4321
   767
            status = encrypt_message(session, outmsg, NULL, &enc_msg, PEP_enc_PGP_MIME, PEP_encrypt_flag_key_reset_only);
krista@4312
   768
            
krista@4312
   769
            if (status != PEP_STATUS_OK) {
krista@4312
   770
                goto pEp_free;
krista@4312
   771
            }
krista@4312
   772
            
krista@4312
   773
            // insert into queue
krista@4312
   774
            status = send_cb(enc_msg);
krista@4312
   775
krista@4312
   776
            if (status != PEP_STATUS_OK) {
krista@4312
   777
                free(enc_msg);
krista@4312
   778
                goto pEp_free;            
krista@4312
   779
            }                         
krista@4312
   780
            // Does there need to be some kind of signal here for sync?                                               
krista@4312
   781
        }    
krista@4312
   782
    }
krista@4312
   783
    
krista@4312
   784
    // Ok, we've signed everything we need to with the old key,
krista@4312
   785
    // Revoke that baby.
krista@4312
   786
    status = revoke_key(session, old_key, NULL);
krista@4312
   787
krista@4321
   788
    if (status != PEP_STATUS_OK)
krista@4312
   789
        goto pEp_free;
krista@4312
   790
        
krista@4312
   791
    for (curr_ident = key_idents; curr_ident && curr_ident->ident; curr_ident = curr_ident->next) {
krista@4312
   792
        if (curr_ident->ident->flags & PEP_idf_devicegroup) {
krista@4312
   793
            pEp_identity* ident = curr_ident->ident;
krista@4312
   794
            
krista@4312
   795
            // N.B. This sort of sucks because we overwrite this every time.
krista@4312
   796
            // But this case is infrequent and we don't rely on the binding.
krista@4312
   797
            if (status == PEP_STATUS_OK) 
krista@4312
   798
                status = set_revoked(session, old_key, curr_ident->ident->fpr, time(NULL));            
krista@4312
   799
krista@4321
   800
            if (status != PEP_STATUS_OK)
krista@4312
   801
                goto pEp_free;
krista@4312
   802
                
krista@4312
   803
            pEp_identity* tmp_ident = identity_dup(ident);
krista@4312
   804
            if (!tmp_ident) {
krista@4312
   805
                status = PEP_OUT_OF_MEMORY;
krista@4312
   806
                goto pEp_free;
krista@4312
   807
            }    
krista@4312
   808
            free(tmp_ident->fpr);    
krista@4312
   809
            
krista@4312
   810
            // for all active communication partners:
krista@4312
   811
            //      active_send revocation            
krista@4312
   812
            tmp_ident->fpr = strdup(old_key); // freed in free_identity
krista@4312
   813
            if (status == PEP_STATUS_OK)
krista@4312
   814
                status = send_key_reset_to_recents(session, tmp_ident, old_key, ident->fpr);        
krista@4312
   815
            free_identity(tmp_ident);
krista@4312
   816
        }    
krista@4312
   817
    }    
krista@4312
   818
pEp_free:
krista@4312
   819
    return status;
krista@3495
   820
}
krista@3495
   821
krista@3495
   822
// Notes to integrate into header:
krista@3495
   823
// IF there is an ident, it must have a user_id.
krista@3495
   824
PEP_STATUS key_reset(
krista@2947
   825
        PEP_SESSION session,
krista@2947
   826
        const char* key_id,
krista@4312
   827
        pEp_identity* ident
krista@2947
   828
    )
krista@2947
   829
{
krista@3495
   830
    if (!session || (ident && EMPTYSTR(ident->user_id)))
krista@2947
   831
        return PEP_ILLEGAL_VALUE;
krista@2947
   832
        
krista@2947
   833
    PEP_STATUS status = PEP_STATUS_OK;
krista@2947
   834
        
krista@2947
   835
    char* fpr_copy = NULL;
krista@2947
   836
    char* own_id = NULL;
krista@3495
   837
    char* user_id = NULL;
krista@2947
   838
    char* new_key = NULL;
krista@3495
   839
    pEp_identity* tmp_ident = NULL;
krista@2947
   840
    identity_list* key_idents = NULL;
krista@2947
   841
    stringlist_t* keys = NULL;
krista@2947
   842
    
krista@2947
   843
    if (!EMPTYSTR(key_id)) {
krista@2947
   844
        fpr_copy = strdup(key_id);
krista@2947
   845
        if (!fpr_copy)
krista@2947
   846
            return PEP_OUT_OF_MEMORY;
krista@2947
   847
    }
krista@3495
   848
krista@3495
   849
    // This is true when we don't have a user_id and address and the fpr isn't specified
krista@3495
   850
    bool reset_all_for_user = !fpr_copy && (!ident || EMPTYSTR(ident->address));
krista@3495
   851
krista@3495
   852
    // FIXME: does this need to be done everywhere?> I think not.
krista@3495
   853
    if (ident) {
krista@3495
   854
        user_id = strdup(ident->user_id);
krista@3495
   855
        if (!user_id) {
krista@3495
   856
            status = PEP_OUT_OF_MEMORY;
krista@2956
   857
            goto pEp_free;
krista@3495
   858
        }
krista@3495
   859
    }
krista@3495
   860
    else {
krista@3495
   861
        status = get_default_own_userid(session, &user_id);
krista@3495
   862
        if (status != PEP_STATUS_OK || !user_id)
krista@3495
   863
            goto pEp_free;                    
krista@3495
   864
    }
krista@3495
   865
    
krista@3495
   866
    // FIXME: Make sure this can't result in a double-free in recursive calls
krista@3495
   867
    tmp_ident = (ident ? identity_dup(ident) : new_identity(NULL, NULL, user_id, NULL));
krista@3495
   868
    
krista@3495
   869
    if (reset_all_for_user) {
krista@3495
   870
        status = get_all_keys_for_user(session, user_id, &keys);
krista@3495
   871
        // TODO: free
krista@3495
   872
        if (status == PEP_STATUS_OK) {
krista@3495
   873
            stringlist_t* curr_key;
krista@2947
   874
            
krista@3495
   875
            for (curr_key = keys; curr_key && curr_key->value; curr_key = curr_key->next) {
krista@3495
   876
                // FIXME: Is the ident really necessary?
krista@4312
   877
                status = key_reset(session, curr_key->value, tmp_ident);
krista@4316
   878
                if (status != PEP_STATUS_OK && status != PEP_CANNOT_FIND_IDENTITY)
krista@3495
   879
                    break;
krista@4316
   880
                else 
krista@4316
   881
                    status = PEP_STATUS_OK;
krista@2947
   882
            }
krista@2947
   883
        }
krista@3495
   884
        goto pEp_free;
krista@3495
   885
    }                   
krista@3495
   886
    else {
krista@3495
   887
        // tmp_ident => tmp_ident->user_id (was checked)
krista@3495
   888
        //
krista@3495
   889
        // !(EMPTYSTR(fpr) && (!tmp_ident || EMPTYSTR(tmp_ident->address)))
krista@3495
   890
        // => fpr || (tmp_ident && tmp_ident->address)
krista@3495
   891
        //
krista@3495
   892
        // so: We have an fpr or we have an ident with user_id and address
krista@3495
   893
        //     or both
krista@3495
   894
        if (!fpr_copy) {
krista@3495
   895
            // We are guaranteed to have an ident w/ id + addr here.
krista@3495
   896
            // Get the default key.
krista@3495
   897
            pEp_identity* stored_ident = NULL;
krista@3495
   898
            status = get_identity(session, tmp_ident->address, 
krista@3495
   899
                                  tmp_ident->user_id, &stored_ident);
krista@3495
   900
krista@3495
   901
            // FIXME FIXME FIXME
krista@3495
   902
            if (status == PEP_STATUS_OK) {
krista@3495
   903
                // transfer ownership
krista@3495
   904
                fpr_copy = stored_ident->fpr;
krista@3495
   905
                stored_ident->fpr = NULL;
krista@3495
   906
                free_identity(stored_ident);                
krista@3495
   907
            }
krista@3174
   908
            
krista@3495
   909
            if (!fpr_copy || status == PEP_CANNOT_FIND_IDENTITY) {
krista@3495
   910
                // There's no identity default. Try resetting user default
krista@3495
   911
                status = get_user_default_key(session, tmp_ident->user_id, &fpr_copy);
krista@3495
   912
            }            
krista@4117
   913
                        
krista@3495
   914
            if (!fpr_copy || status != PEP_STATUS_OK) // No default to free. We're done here.
krista@3495
   915
                goto pEp_free;            
krista@3495
   916
        }
krista@3495
   917
        
krista@3495
   918
        // Ok - now we have at least an ident with user_id and an fpr.
krista@3495
   919
        // Now it matters if we're talking about ourselves or a partner.
krista@3495
   920
        bool is_own_private = false;
krista@3495
   921
        if (is_me(session, tmp_ident)) {
krista@3495
   922
            bool own_key = false;            
krista@3495
   923
            status = is_own_key(session, fpr_copy, &own_key);
krista@3495
   924
krista@3495
   925
            if (status != PEP_STATUS_OK)
krista@3495
   926
                goto pEp_free;
krista@3495
   927
            if (!own_key) {
krista@3495
   928
                status = PEP_ILLEGAL_VALUE;
krista@3495
   929
                goto pEp_free;
krista@3495
   930
            }
krista@3495
   931
krista@3495
   932
            status = contains_priv_key(session, fpr_copy, &is_own_private);
krista@3507
   933
            if (status != PEP_STATUS_OK && status != PEP_KEY_NOT_FOUND)
krista@3495
   934
                goto pEp_free;
krista@3495
   935
        }
krista@3495
   936
        
krista@3495
   937
        // Up to this point, we haven't cared about whether or not we 
krista@3495
   938
        // had a full identity. Now we have to deal with that in the 
krista@3495
   939
        // case of own identities with private keys.
krista@3495
   940
        
krista@3495
   941
        if (is_own_private) {
krista@3495
   942
            
krista@4320
   943
            // This is now the "is_own" base case - we don't do this 
krista@4320
   944
            // per-identity, because all identities using this key will 
krista@4320
   945
            // need new ones. That said, this is really only a problem 
krista@4320
   946
            // with manual key management, something which we only support 
krista@4320
   947
            // to a limited extent in any event.
krista@3495
   948
            
krista@4320
   949
            bool is_grouped = false;
krista@4320
   950
            status = deviceGrouped(session, &is_grouped);
krista@4320
   951
             
krista@4320
   952
            // Regardless of the single identity this is for, for own keys, we do this 
krista@4320
   953
            // for all keys associated with the identity.
krista@4320
   954
            status = get_identities_by_main_key_id(session, fpr_copy, &key_idents);
krista@2947
   955
            
krista@4320
   956
            if (status != PEP_CANNOT_FIND_IDENTITY) {
krista@4320
   957
                if (is_grouped) 
krista@4320
   958
                    status = _key_reset_device_group_for_shared_key(session, key_idents, fpr_copy);
krista@4320
   959
                else if (status == PEP_STATUS_OK) {
krista@4320
   960
                    // now have ident list, or should
krista@4320
   961
                    identity_list* curr_ident;
krista@4320
   962
krista@4320
   963
                    for (curr_ident = key_idents; curr_ident && curr_ident->ident; 
krista@4320
   964
                                                    curr_ident = curr_ident->next) {
krista@4320
   965
                        
krista@4320
   966
                        // Do the full reset on this identity        
krista@4320
   967
                        // Base case for is_own_private starts here
krista@4320
   968
                        // tmp ident is an actual identity now (not just a skeleton?)
krista@4320
   969
                        status = revoke_key(session, fpr_copy, NULL);
krista@4320
   970
                        
krista@4320
   971
                        // If we have a full identity, we have some cleanup and generation tasks here
krista@4320
   972
                        if (!EMPTYSTR(tmp_ident->address)) {
krista@4320
   973
                            // generate new key
krista@4320
   974
                            if (status == PEP_STATUS_OK) {
krista@4320
   975
                                tmp_ident->fpr = NULL;
krista@4320
   976
                                status = myself(session, tmp_ident);
krista@4320
   977
                            }
krista@4320
   978
                            if (status == PEP_STATUS_OK && tmp_ident->fpr && strcmp(fpr_copy, tmp_ident->fpr) != 0)
krista@4320
   979
                                new_key = strdup(tmp_ident->fpr);
krista@4320
   980
                            // Error handling?    
krista@4320
   981
                            
krista@4320
   982
                            // mistrust fpr from trust
krista@4320
   983
                            tmp_ident->fpr = fpr_copy;
krista@4320
   984
                                                            
krista@4320
   985
                            tmp_ident->comm_type = PEP_ct_mistrusted;
krista@4320
   986
                            status = set_trust(session, tmp_ident);
krista@4320
   987
                            tmp_ident->fpr = NULL;
krista@4320
   988
                            
krista@4320
   989
                            // Done with old use of ident.
krista@4320
   990
                            if (status == PEP_STATUS_OK) {
krista@4320
   991
                                // Update fpr for outgoing
krista@4320
   992
                                status = myself(session, tmp_ident);
krista@4320
   993
                            }
krista@4320
   994
                        }    
krista@4320
   995
                        
krista@4320
   996
                        if (status == PEP_STATUS_OK)
krista@4320
   997
                            // cascade that mistrust for anyone using this key
krista@4320
   998
                            status = mark_as_compromised(session, fpr_copy);
krista@4320
   999
                            
krista@4320
  1000
                        if (status == PEP_STATUS_OK)
krista@4320
  1001
                            status = remove_fpr_as_default(session, fpr_copy);
krista@4320
  1002
                        if (status == PEP_STATUS_OK)
krista@4320
  1003
                            status = add_mistrusted_key(session, fpr_copy);
krista@4320
  1004
krista@4320
  1005
                        // If there's a new key, do the DB linkage with the revoked one, and 
krista@4320
  1006
                        // send the key reset mail opportunistically to recently contacted
krista@4320
  1007
                        // partners
krista@4320
  1008
                        if (new_key) {
krista@4320
  1009
                            // add to revocation list 
krista@4320
  1010
                            if (status == PEP_STATUS_OK) 
krista@4320
  1011
                                status = set_revoked(session, fpr_copy, new_key, time(NULL));            
krista@4320
  1012
                            // for all active communication partners:
krista@4320
  1013
                            //      active_send revocation
krista@4320
  1014
                            
krista@4320
  1015
                            tmp_ident->fpr = fpr_copy;
krista@4320
  1016
                            if (status == PEP_STATUS_OK)
krista@4320
  1017
                                status = send_key_reset_to_recents(session, tmp_ident, fpr_copy, new_key);        
krista@4320
  1018
                            tmp_ident->fpr = NULL;    
krista@4320
  1019
                        }                    
krista@4320
  1020
                        // Ident list gets freed below, do not free here!
krista@4320
  1021
                    }
krista@3495
  1022
                }
krista@4320
  1023
                // Ok, we've either now reset for each own identity with this key, or 
krista@4320
  1024
                // we got an error and want to bail anyway.
krista@4320
  1025
                goto pEp_free;
krista@4320
  1026
            }
krista@4320
  1027
            else 
krista@4320
  1028
                return PEP_CANNOT_FIND_IDENTITY;
krista@3495
  1029
        } // end is_own_private
krista@3495
  1030
        else {
krista@4120
  1031
            // if it's mistrusted, make it not be so.
krista@4120
  1032
            bool mistrusted_key = false;
krista@4120
  1033
            is_mistrusted_key(session, fpr_copy, &mistrusted_key);
krista@4120
  1034
krista@4120
  1035
            if (mistrusted_key)
krista@4120
  1036
                delete_mistrusted_key(session, fpr_copy);
krista@4120
  1037
            
krista@4121
  1038
            if (tmp_ident->user_id)
krista@4121
  1039
                status = clear_trust_info(session, tmp_ident->user_id, fpr_copy);
krista@4121
  1040
krista@3495
  1041
            // This is a public key (or a private key that isn't ours, which means
krista@3495
  1042
            // we want it gone anyway)
krista@3495
  1043
            //
krista@3495
  1044
            // Delete this key from the keyring.
krista@4245
  1045
            // FIXME: when key election disappears, so should this!
krista@3495
  1046
            status = delete_keypair(session, fpr_copy);
krista@2947
  1047
        }
krista@3495
  1048
krista@3495
  1049
        // REGARDLESS OF WHO OWNS THE KEY, WE NOW NEED TO REMOVE IT AS A DEFAULT.
krista@3495
  1050
        PEP_STATUS cached_status = status;
krista@3495
  1051
        // remove fpr from all identities
krista@3495
  1052
        // remove fpr from all users
krista@3495
  1053
        status = remove_fpr_as_default(session, fpr_copy);
krista@3495
  1054
        // delete key from DB - this does NOT touch the keyring!
krista@3495
  1055
        // Note: for own priv keys, we cannot do this. But we'll never encrypt to/from it.
krista@3495
  1056
        if (status == PEP_STATUS_OK && !is_own_private) {
krista@3495
  1057
            status = remove_key(session, fpr_copy);
krista@2947
  1058
        }
krista@3495
  1059
        if (status == PEP_STATUS_OK)
krista@3495
  1060
            status = cached_status;
krista@3495
  1061
    }           
krista@3495
  1062
        
krista@2956
  1063
pEp_free:
krista@3495
  1064
    if (!ident)
krista@3495
  1065
        free_identity(tmp_ident);
krista@2947
  1066
    free(fpr_copy);
krista@2947
  1067
    free(own_id);
krista@2947
  1068
    free_identity_list(key_idents);
krista@2947
  1069
    free_stringlist(keys);
krista@2947
  1070
    free(new_key);    
krista@2947
  1071
    return status;
krista@2947
  1072
}
krista@4245
  1073
krista@4312
  1074
/*
krista@4245
  1075
static stringlist_t* collect_key_material(PEP_SESSION session, stringlist_t* fprs) {
krista@4245
  1076
    stringlist_t* keydata = NULL;    
krista@4245
  1077
    stringlist_t* curr_fpr = fprs;    
krista@4245
  1078
    while (curr_fpr) {
krista@4245
  1079
        if (curr_fpr->value) {
krista@4245
  1080
            char* key_material = NULL;
krista@4245
  1081
            size_t datasize = 0;
krista@4245
  1082
            PEP_STATUS status = export_key(session, curr_fpr->value, &key_material, &datasize);
krista@4245
  1083
            if (status) {
krista@4245
  1084
                free_stringlist(keydata);
krista@4245
  1085
                return NULL;
krista@4245
  1086
            }
krista@4245
  1087
            if (datasize > 0 && key_material) {
krista@4245
  1088
                if (!(keydata))
krista@4245
  1089
                    keydata = new_stringlist(NULL);
krista@4245
  1090
                    
krista@4245
  1091
                stringlist_add(keydata, key_material);
krista@4245
  1092
            }
krista@4245
  1093
        }
krista@4245
  1094
        curr_fpr = curr_fpr->next;        
krista@4245
  1095
    }   
krista@4245
  1096
    return keydata; 
krista@4245
  1097
}
krista@4245
  1098
krista@4245
  1099
PEP_STATUS key_reset_own_and_deliver_revocations(PEP_SESSION session, 
krista@4245
  1100
                                                 identity_list** own_identities, 
krista@4245
  1101
                                                 stringlist_t** revocations, 
krista@4245
  1102
                                                 stringlist_t** keys) {
krista@4245
  1103
krista@4245
  1104
    if (!(session && own_identities && revocations && keys))
krista@4245
  1105
        return PEP_ILLEGAL_VALUE;
krista@4245
  1106
        
krista@4245
  1107
    stringlist_t* revoked_fprs = NULL;
krista@4245
  1108
    identity_list* affected_idents = NULL;
krista@4245
  1109
        
krista@4245
  1110
    PEP_STATUS status = key_reset(session, NULL, NULL, &affected_idents, &revoked_fprs);                                                 
krista@4245
  1111
krista@4245
  1112
    // FIXME: free things
krista@4245
  1113
    if (status != PEP_STATUS_OK)
krista@4245
  1114
        return status;
krista@4245
  1115
    
krista@4245
  1116
    dedup_stringlist(revoked_fprs);
krista@4245
  1117
krista@4245
  1118
    *revocations = collect_key_material(session, revoked_fprs);
krista@4245
  1119
    stringlist_t* keydata = NULL;
krista@4245
  1120
    
krista@4245
  1121
    if (affected_idents) {
krista@4245
  1122
        keydata = new_stringlist(NULL);
krista@4245
  1123
        identity_list* curr_ident = affected_idents;
krista@4245
  1124
        while (curr_ident) {
krista@4245
  1125
            if (curr_ident->ident && curr_ident->ident->fpr) {
krista@4245
  1126
                char* key_material = NULL;
krista@4245
  1127
                size_t datasize = 0;
krista@4245
  1128
                status = export_key(session, curr_ident->ident->fpr, &key_material, &datasize);
krista@4245
  1129
                if (status) {
krista@4245
  1130
                    free_stringlist(keydata);
krista@4245
  1131
                    return status;
krista@4245
  1132
                }
krista@4245
  1133
                if (datasize > 0 && key_material)
krista@4245
  1134
                    stringlist_add(keydata, key_material);
krista@4312
  1135
                    
krista@4312
  1136
                key_material = NULL;
krista@4312
  1137
                datasize = 0;    
krista@4312
  1138
                status = export_private_keys(session, curr_ident->ident->fpr, &key_material, &datasize);    
krista@4312
  1139
                if (status) {
krista@4312
  1140
                    free_stringlist(keydata);
krista@4312
  1141
                    return status;
krista@4312
  1142
                }
krista@4312
  1143
                if (datasize > 0 && key_material)
krista@4312
  1144
                    stringlist_add(keydata, key_material);
krista@4312
  1145
krista@4245
  1146
            }
krista@4246
  1147
            curr_ident = curr_ident->next;
krista@4245
  1148
        }
krista@4245
  1149
    }
krista@4245
  1150
    
krista@4245
  1151
    *own_identities = affected_idents;
krista@4245
  1152
    *keys = keydata;
krista@4245
  1153
    
krista@4245
  1154
    free(revoked_fprs);
krista@4245
  1155
    return PEP_STATUS_OK;
krista@4245
  1156
}
krista@4312
  1157
*/
vb@4269
  1158
Distribution_t *Distribution_from_keyreset_command_list(
vb@4269
  1159
        const keyreset_command_list *command_list,
vb@4269
  1160
        Distribution_t *dist
vb@4269
  1161
    )
vb@4256
  1162
{
vb@4269
  1163
    bool allocated = !dist;
vb@4257
  1164
vb@4269
  1165
    assert(command_list);
vb@4269
  1166
    if (!command_list)
vb@4269
  1167
        return NULL;
vb@4257
  1168
vb@4269
  1169
    if (allocated)
vb@4269
  1170
        dist = (Distribution_t *) calloc(1, sizeof(Distribution_t));
vb@4261
  1171
vb@4261
  1172
    assert(dist);
vb@4261
  1173
    if (!dist)
vb@4261
  1174
        goto enomem;
vb@4261
  1175
vb@4261
  1176
    dist->present = Distribution_PR_keyreset;
vb@4261
  1177
    dist->choice.keyreset.present = KeyReset_PR_commands;
vb@4261
  1178
vb@4275
  1179
    long *major = malloc(sizeof(long));
vb@4275
  1180
    assert(major);
vb@4275
  1181
    if (!major)
vb@4275
  1182
        goto enomem;
vb@4275
  1183
    *major = KEY_RESET_MAJOR_VERSION;
vb@4275
  1184
    dist->choice.keyreset.choice.commands.version.major = major;
vb@4275
  1185
vb@4275
  1186
    long *minor = malloc(sizeof(long));
vb@4275
  1187
    assert(minor);
vb@4275
  1188
    if (!minor)
vb@4275
  1189
        goto enomem;
vb@4275
  1190
    *minor = KEY_RESET_MINOR_VERSION;
vb@4275
  1191
    dist->choice.keyreset.choice.commands.version.minor = minor;
vb@4275
  1192
vb@4257
  1193
    for (const keyreset_command_list *cl = command_list; cl && cl->command; cl = cl->next) {
vb@4260
  1194
        Command_t *c = (Command_t *) calloc(1, sizeof(Command_t));
vb@4257
  1195
        assert(c);
vb@4257
  1196
        if (!c)
vb@4257
  1197
            goto enomem;
vb@4257
  1198
vb@4257
  1199
        if (!Identity_from_Struct(cl->command->ident, &c->ident)) {
vb@4257
  1200
            free(c);
vb@4257
  1201
            goto enomem;
vb@4257
  1202
        }
vb@4257
  1203
vb@4257
  1204
        if (OCTET_STRING_fromString(&c->newkey, cl->command->new_key)) {
vb@4261
  1205
            ASN_STRUCT_FREE(asn_DEF_Command, c);
vb@4261
  1206
            goto enomem;
vb@4261
  1207
        }
vb@4261
  1208
vb@4261
  1209
        if (ASN_SEQUENCE_ADD(&dist->choice.keyreset.choice.commands.commandlist, c)) {
vb@4261
  1210
            ASN_STRUCT_FREE(asn_DEF_Command, c);
vb@4257
  1211
            goto enomem;
vb@4257
  1212
        }
vb@4257
  1213
    }
vb@4257
  1214
vb@4269
  1215
    return dist;
vb@4269
  1216
vb@4269
  1217
enomem:
vb@4269
  1218
    ASN_STRUCT_FREE(asn_DEF_Distribution, dist);
vb@4269
  1219
    return NULL;
vb@4269
  1220
}
vb@4269
  1221
krista@4312
  1222
vb@4269
  1223
PEP_STATUS key_reset_commands_to_PER(const keyreset_command_list *command_list, char **cmds, size_t *size)
vb@4269
  1224
{
vb@4269
  1225
    PEP_STATUS status = PEP_STATUS_OK;
vb@4269
  1226
vb@4269
  1227
    assert(command_list && cmds);
vb@4269
  1228
    if (!(command_list && cmds))
vb@4269
  1229
        return PEP_ILLEGAL_VALUE;
vb@4269
  1230
vb@4269
  1231
    *cmds = NULL;
vb@4269
  1232
    *size = 0;
vb@4269
  1233
vb@4271
  1234
    // convert from pEp engine struct
vb@4269
  1235
vb@4269
  1236
    Distribution_t *dist = Distribution_from_keyreset_command_list(command_list, NULL);
vb@4269
  1237
    assert(dist);
vb@4269
  1238
    if (!dist)
vb@4269
  1239
        goto enomem;
vb@4269
  1240
vb@4261
  1241
    // encode
vb@4261
  1242
vb@4261
  1243
    char *_cmds;
vb@4261
  1244
    size_t _size;
vb@4261
  1245
    status = encode_Distribution_message(dist, &_cmds, &_size);
vb@4261
  1246
    if (status)
vb@4261
  1247
        goto the_end;
vb@4261
  1248
vb@4266
  1249
    // return result
vb@4266
  1250
vb@4261
  1251
    *cmds = _cmds;
vb@4261
  1252
    *size = _size;
vb@4261
  1253
    goto the_end;
vb@4261
  1254
vb@4257
  1255
enomem:
vb@4257
  1256
    status = PEP_OUT_OF_MEMORY;
vb@4257
  1257
vb@4257
  1258
the_end:
vb@4261
  1259
    ASN_STRUCT_FREE(asn_DEF_Distribution, dist);
vb@4257
  1260
    return status;
vb@4257
  1261
}
vb@4257
  1262
vb@4269
  1263
keyreset_command_list * Distribution_to_keyreset_command_list(
vb@4269
  1264
        Distribution_t *dist,
vb@4269
  1265
        keyreset_command_list *command_list
vb@4269
  1266
    )
vb@4269
  1267
{
vb@4269
  1268
    bool allocated = !command_list;
vb@4269
  1269
vb@4269
  1270
    assert(dist);
vb@4269
  1271
    if (!dist)
vb@4269
  1272
        return NULL;
vb@4269
  1273
vb@4269
  1274
    if (allocated)
vb@4269
  1275
        command_list = new_keyreset_command_list(NULL);
vb@4269
  1276
    if (!command_list)
vb@4269
  1277
        goto enomem;
vb@4269
  1278
vb@4269
  1279
    struct Commands__commandlist *cl = &dist->choice.keyreset.choice.commands.commandlist;
vb@4269
  1280
    keyreset_command_list *_result = command_list;
vb@4269
  1281
    for (int i=0; i<cl->list.count; i++) {
vb@4269
  1282
        pEp_identity *ident = Identity_to_Struct(&cl->list.array[i]->ident, NULL);
vb@4269
  1283
        if (!ident)
vb@4269
  1284
            goto enomem;
vb@4269
  1285
vb@4269
  1286
        const char *new_key = (const char *) cl->list.array[i]->newkey.buf;
vb@4269
  1287
vb@4269
  1288
        keyreset_command *command = new_keyreset_command(ident, new_key);
vb@4269
  1289
        if (!command) {
vb@4269
  1290
            free_identity(ident);
vb@4269
  1291
            goto enomem;
vb@4269
  1292
        }
vb@4269
  1293
vb@4269
  1294
        _result = keyreset_command_list_add(_result, command);
vb@4269
  1295
        free_identity(ident);
vb@4269
  1296
        if (!_result)
vb@4269
  1297
            goto enomem;
vb@4269
  1298
    }
vb@4269
  1299
vb@4269
  1300
    return command_list;
vb@4269
  1301
vb@4269
  1302
enomem:
vb@4269
  1303
    if (allocated)
vb@4269
  1304
        free_keyreset_command_list(command_list);
vb@4269
  1305
    return NULL;
vb@4269
  1306
}
vb@4269
  1307
vb@4261
  1308
PEP_STATUS PER_to_key_reset_commands(const char *cmds, size_t size, keyreset_command_list **command_list)
vb@4257
  1309
{
vb@4257
  1310
    assert(command_list && cmds);
vb@4257
  1311
    if (!(command_list && cmds))
vb@4256
  1312
        return PEP_ILLEGAL_VALUE;
vb@4256
  1313
vb@4262
  1314
    *command_list = NULL;
vb@4262
  1315
vb@4266
  1316
    // decode
vb@4266
  1317
vb@4262
  1318
    Distribution_t *dist = NULL;
vb@4262
  1319
    PEP_STATUS status = decode_Distribution_message(cmds, size, &dist);
vb@4262
  1320
    if (status)
vb@4262
  1321
        goto the_end;
vb@4262
  1322
vb@4266
  1323
    // check if these are key reset commands or not
vb@4266
  1324
vb@4265
  1325
    assert(dist && dist->present == Distribution_PR_keyreset
vb@4265
  1326
            && dist->choice.keyreset.present == KeyReset_PR_commands);
vb@4265
  1327
vb@4262
  1328
    if (!(dist && dist->present == Distribution_PR_keyreset
vb@4262
  1329
            && dist->choice.keyreset.present == KeyReset_PR_commands)) {
vb@4265
  1330
        status = PEP_ILLEGAL_VALUE;
vb@4262
  1331
        goto the_end;
vb@4262
  1332
    }
vb@4262
  1333
vb@4271
  1334
    // convert to pEp engine struct
vb@4266
  1335
vb@4269
  1336
    keyreset_command_list *result = Distribution_to_keyreset_command_list(dist, NULL);
vb@4262
  1337
    if (!result)
vb@4262
  1338
        goto enomem;
vb@4262
  1339
vb@4266
  1340
    // return result
vb@4266
  1341
vb@4263
  1342
    *command_list = result;
vb@4262
  1343
    goto the_end;
vb@4262
  1344
vb@4262
  1345
enomem:
vb@4262
  1346
    status = PEP_OUT_OF_MEMORY;
vb@4264
  1347
    free_keyreset_command_list(result);
vb@4262
  1348
vb@4262
  1349
the_end:
vb@4262
  1350
    ASN_STRUCT_FREE(asn_DEF_Distribution, dist);
vb@4262
  1351
    return status;
vb@4256
  1352
}