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