src/key_reset.c
author Neal H. Walfield <neal@pep.foundation>
Wed, 08 May 2019 12:20:37 +0200
branchemail_comparison
changeset 3649 1dd837adc30b
parent 3507 afa3bcf153fb
child 3739 523fb9710660
permissions -rw-r--r--
Fix public key.
     1 // This file is under GNU General Public License 3.0
     2 // see LICENSE.txt
     3 
     4 #include "pEp_internal.h"
     5 #include "dynamic_api.h"
     6 #include "message_api.h"
     7 
     8 #include <string.h>
     9 #include <stdlib.h>
    10 
    11 PEP_STATUS has_key_reset_been_sent(
    12         PEP_SESSION session, 
    13         const char* user_id, 
    14         const char* revoked_fpr,
    15         bool* contacted)
    16 {
    17     assert(session);
    18     assert(contacted);
    19     assert(user_id);
    20     assert(revoked_fpr);
    21     assert(!EMPTYSTR(user_id));
    22 
    23     if (!session || !contacted || EMPTYSTR(revoked_fpr) || EMPTYSTR(user_id))
    24         return PEP_ILLEGAL_VALUE;
    25     
    26     *contacted = false;
    27                     
    28     char* alias_default = NULL;
    29     
    30     PEP_STATUS status = get_userid_alias_default(session, user_id, &alias_default);
    31     
    32     if (status == PEP_CANNOT_FIND_ALIAS || EMPTYSTR(alias_default)) {
    33         free(alias_default);
    34         alias_default = strdup(user_id);
    35     }
    36     
    37     sqlite3_reset(session->was_id_for_revoke_contacted);
    38     sqlite3_bind_text(session->was_id_for_revoke_contacted, 1, revoked_fpr, -1,
    39             SQLITE_STATIC);
    40     sqlite3_bind_text(session->was_id_for_revoke_contacted, 2, user_id, -1,
    41             SQLITE_STATIC);        
    42     int result = sqlite3_step(session->was_id_for_revoke_contacted);
    43     switch (result) {
    44         case SQLITE_ROW: {
    45             *contacted = (sqlite3_column_int(session->was_id_for_revoke_contacted, 0) != 0);
    46             break;
    47         }
    48         default:
    49             sqlite3_reset(session->was_id_for_revoke_contacted);
    50             free(alias_default);
    51             return PEP_UNKNOWN_DB_ERROR;
    52     }
    53 
    54     sqlite3_reset(session->was_id_for_revoke_contacted);
    55     return PEP_STATUS_OK;
    56 }
    57 
    58 //static const char *sql_set_revoke_contact_as_notified =
    59 //    "insert or replace into revocation_contact_list(fpr, contact_id) values (?1, ?2) ;";
    60 
    61 PEP_STATUS set_reset_contact_notified(
    62         PEP_SESSION session,
    63         const char* revoke_fpr,
    64         const char* contact_id
    65     )
    66 {
    67     PEP_STATUS status = PEP_STATUS_OK;
    68     
    69     assert(session && !EMPTYSTR(revoke_fpr) && !EMPTYSTR(contact_id));
    70     
    71     if (!session || EMPTYSTR(revoke_fpr) || EMPTYSTR(contact_id))
    72         return PEP_ILLEGAL_VALUE;
    73     
    74     sqlite3_reset(session->set_revoke_contact_as_notified);
    75     sqlite3_bind_text(session->set_revoke_contact_as_notified, 1, revoke_fpr, -1, 
    76             SQLITE_STATIC);
    77     sqlite3_bind_text(session->set_revoke_contact_as_notified, 2, contact_id, -1,
    78             SQLITE_STATIC);
    79 
    80     int result;
    81     
    82     result = sqlite3_step(session->set_revoke_contact_as_notified);
    83     switch (result) {
    84         case SQLITE_DONE:
    85             status = PEP_STATUS_OK;
    86             break;
    87             
    88         default:
    89             status = PEP_UNKNOWN_DB_ERROR;
    90     }
    91     
    92     sqlite3_reset(session->set_revoke_contact_as_notified);
    93     return status;    
    94 }
    95 
    96 
    97 PEP_STATUS receive_key_reset(PEP_SESSION session,
    98                              message* reset_msg) {
    99 
   100     if (!session || !reset_msg)
   101         return PEP_ILLEGAL_VALUE;
   102 
   103     pEp_identity* sender_id = reset_msg->from;
   104                 
   105     if (!sender_id)
   106         return PEP_MALFORMED_KEY_RESET_MSG;
   107         
   108     PEP_STATUS status = update_identity(session, sender_id);
   109     if (!sender_id->user_id)
   110         return PEP_UNKNOWN_ERROR;
   111         
   112     if (is_me(session, sender_id))
   113         return PEP_ILLEGAL_VALUE;    
   114         
   115     if (!reset_msg->longmsg || strncmp(reset_msg->longmsg, "OLD: ", 5) != 0) 
   116         return PEP_MALFORMED_KEY_RESET_MSG;
   117 
   118     status = PEP_STATUS_OK;
   119     char* old_fpr = NULL;
   120     char* new_fpr = NULL;
   121     
   122     stringlist_t* keylist = NULL;
   123     pEp_identity* temp_ident = identity_dup(sender_id);
   124     if (!temp_ident) {
   125         status = PEP_OUT_OF_MEMORY;
   126         goto pEp_free;
   127     }        
   128             
   129     char* rest = NULL;
   130     char* p = strtok_r(reset_msg->longmsg, "\r\n", &rest);
   131     if (!EMPTYSTR(p + 5))
   132         old_fpr = strdup(p + 5);
   133     else {
   134         status = PEP_MALFORMED_KEY_RESET_MSG;
   135         goto pEp_free;
   136     }
   137     
   138     bool own_key = false;
   139     status = is_own_key(session, old_fpr, &own_key);
   140     
   141     if (own_key) {
   142         // Nope, no one can make us our own default. If we want to do that,
   143         // that's keysync, NOT key reset.
   144         status = PEP_ILLEGAL_VALUE;
   145         goto pEp_free;
   146     }
   147             
   148     p = strtok_r(NULL, "\r\n", &rest); 
   149     if (strncmp(p, "NEW: ", 5) != 0  || EMPTYSTR(p + 5)) {
   150         status = PEP_MALFORMED_KEY_RESET_MSG;
   151         goto pEp_free;
   152     }
   153 
   154     new_fpr = strdup(p + 5);
   155         
   156     // Reset the original key
   157     status = key_reset(session, old_fpr, temp_ident);
   158     if (status != PEP_STATUS_OK)
   159         goto pEp_free;
   160         
   161     status = find_keys(session, new_fpr, &keylist);
   162     if (status != PEP_STATUS_OK)
   163         goto pEp_free;
   164         
   165     if (!keylist) {
   166         status = PEP_KEY_NOT_FOUND;
   167         goto pEp_free;
   168     }
   169 
   170     // alright, we've checked as best we can. Let's set that baby.
   171     sender_id->fpr = new_fpr;
   172     
   173     // This only sets as the default, does NOT TRUST IN ANY WAY
   174     sender_id->comm_type = sender_id->comm_type & (~PEP_ct_confirmed);
   175     status = set_identity(session, sender_id);
   176     
   177     sender_id->fpr = NULL; // ownership for free
   178 pEp_free:    
   179     free_stringlist(keylist);    
   180     free(old_fpr);
   181     free(new_fpr);
   182     free_identity(temp_ident);
   183     return status;
   184 }
   185 
   186 PEP_STATUS create_standalone_key_reset_message(PEP_SESSION session,
   187                                                message** dst, 
   188                                                pEp_identity* recip,
   189                                                const char* old_fpr,
   190                                                const char* new_fpr) {
   191                                                    
   192     if (!dst || !recip->user_id || !recip->address)
   193         return PEP_ILLEGAL_VALUE;
   194 
   195     if (!old_fpr || !new_fpr)
   196         return PEP_ILLEGAL_VALUE;
   197         
   198     *dst = NULL;
   199     // Get own identity user has corresponded with
   200     pEp_identity* own_identity = NULL;
   201     
   202     PEP_STATUS status = get_own_ident_for_contact_id(session,
   203                                                      recip,
   204                                                      &own_identity);                                                       
   205     if (status != PEP_STATUS_OK)
   206         return status;
   207         
   208     message* reset_message = new_message(PEP_dir_outgoing);
   209     reset_message->from = own_identity;
   210     reset_message->to = new_identity_list(identity_dup(recip)); // ?
   211     
   212     const char* oldtag = "OLD: ";
   213     const char* newtag = "\nNEW: ";
   214     const size_t taglens = 11;
   215     size_t full_len = taglens + strlen(old_fpr) + strlen(new_fpr) + 2; // \n and \0
   216     char* longmsg = calloc(full_len, 1);
   217     strlcpy(longmsg, oldtag, full_len);
   218     strlcat(longmsg, old_fpr, full_len);
   219     strlcat(longmsg, newtag, full_len);
   220     strlcat(longmsg, new_fpr, full_len);
   221     strlcat(longmsg, "\n", full_len);
   222     reset_message->longmsg = longmsg; 
   223     reset_message->shortmsg = strdup("Key reset");    
   224     
   225     message* output_msg = NULL;
   226     
   227     status = encrypt_message(session, reset_message, NULL,
   228                              &output_msg, PEP_enc_PGP_MIME,
   229                              PEP_encrypt_flag_key_reset_only);
   230 
   231     if (status == PEP_STATUS_OK)
   232         *dst = output_msg;
   233         
   234     free_message(reset_message);
   235     return status;
   236 }
   237 
   238 PEP_STATUS send_key_reset_to_recents(PEP_SESSION session,
   239                                      const char* old_fpr, 
   240                                      const char* new_fpr) {
   241     assert(old_fpr);
   242     assert(new_fpr);
   243     assert(session);
   244     assert(session->messageToSend);
   245     
   246     if (!session || !old_fpr || !new_fpr)
   247         return PEP_ILLEGAL_VALUE;
   248 
   249     messageToSend_t send_cb = session->messageToSend;
   250     if (!send_cb)
   251         return PEP_SYNC_NO_MESSAGE_SEND_CALLBACK;
   252         
   253     identity_list* recent_contacts = NULL;
   254     message* reset_msg = NULL;
   255 
   256     PEP_STATUS status = get_last_contacted(session, &recent_contacts);
   257     
   258     if (status != PEP_STATUS_OK)
   259         goto pEp_free;
   260                     
   261     identity_list* curr_id_ptr = recent_contacts;
   262 
   263     for (curr_id_ptr = recent_contacts; curr_id_ptr; curr_id_ptr = curr_id_ptr->next) {
   264         pEp_identity* curr_id = curr_id_ptr->ident;
   265         
   266         if (!curr_id)
   267             break;
   268     
   269         const char* user_id = curr_id->user_id;
   270         
   271         // Should be impossible, but?
   272         if (!user_id)
   273             continue;
   274         
   275         // Check if it's us - if so, pointless...
   276         if (is_me(session, curr_id))
   277             continue;
   278             
   279         // Check if they've already been told - this shouldn't be the case, but...
   280         bool contacted = false;
   281         status = has_key_reset_been_sent(session, user_id, old_fpr, &contacted);
   282         if (status != PEP_STATUS_OK)
   283             goto pEp_free;
   284     
   285         if (contacted)
   286             continue;
   287             
   288         // if not, make em a message    
   289         reset_msg = NULL;
   290         
   291         status = create_standalone_key_reset_message(session,
   292                                                      &reset_msg,
   293                                                      curr_id,
   294                                                      old_fpr,
   295                                                      new_fpr);
   296 
   297         if (status == PEP_CANNOT_FIND_IDENTITY) { // this is ok, just means we never mailed them 
   298             status = PEP_STATUS_OK;
   299             continue; 
   300         }
   301             
   302         if (status != PEP_STATUS_OK) {
   303             free(reset_msg);
   304             goto pEp_free;
   305         }
   306         
   307         // insert into queue
   308         status = send_cb(reset_msg);
   309 
   310         if (status != PEP_STATUS_OK) {
   311             free(reset_msg);
   312             goto pEp_free;            
   313         }
   314             
   315         // Put into notified DB
   316         status = set_reset_contact_notified(session, old_fpr, user_id);
   317         if (status != PEP_STATUS_OK)
   318             goto pEp_free;            
   319     }
   320     
   321 pEp_free:
   322     free_identity_list(recent_contacts);
   323     return status;
   324 }
   325 
   326 DYNAMIC_API PEP_STATUS key_reset_identity(
   327         PEP_SESSION session,
   328         pEp_identity* ident,
   329         const char* fpr        
   330     )
   331 {
   332     if (!session || !ident || (ident && (EMPTYSTR(ident->user_id) || EMPTYSTR(ident->address))))
   333         return PEP_ILLEGAL_VALUE;
   334     
   335     return key_reset(session, fpr, ident);    
   336 }
   337 
   338 DYNAMIC_API PEP_STATUS key_reset_user(
   339         PEP_SESSION session,
   340         const char* user_id,
   341         const char* fpr        
   342     )
   343 {
   344     if (!session || EMPTYSTR(user_id))
   345         return PEP_ILLEGAL_VALUE;
   346 
   347     pEp_identity* input_ident = new_identity(NULL, NULL, user_id, NULL);
   348     if (!input_ident)
   349         return PEP_OUT_OF_MEMORY;
   350         
   351     if (is_me(session, input_ident) && EMPTYSTR(fpr))
   352         return PEP_ILLEGAL_VALUE;
   353         
   354     PEP_STATUS status = key_reset(session, fpr, input_ident);
   355     free_identity(input_ident);
   356     return status;
   357 }
   358 
   359 DYNAMIC_API PEP_STATUS key_reset_all_own_keys(PEP_SESSION session) {
   360     return key_reset(session, NULL, NULL);
   361 }
   362 
   363 // Notes to integrate into header:
   364 // IF there is an ident, it must have a user_id.
   365 PEP_STATUS key_reset(
   366         PEP_SESSION session,
   367         const char* key_id,
   368         pEp_identity* ident
   369     )
   370 {
   371     if (!session || (ident && EMPTYSTR(ident->user_id)))
   372         return PEP_ILLEGAL_VALUE;
   373         
   374     PEP_STATUS status = PEP_STATUS_OK;
   375         
   376     char* fpr_copy = NULL;
   377     char* own_id = NULL;
   378     char* user_id = NULL;
   379     char* new_key = NULL;
   380     pEp_identity* tmp_ident = NULL;
   381     identity_list* key_idents = NULL;
   382     stringlist_t* keys = NULL;
   383     
   384     if (!EMPTYSTR(key_id)) {
   385         fpr_copy = strdup(key_id);
   386         if (!fpr_copy)
   387             return PEP_OUT_OF_MEMORY;
   388     }
   389 
   390     // This is true when we don't have a user_id and address and the fpr isn't specified
   391     bool reset_all_for_user = !fpr_copy && (!ident || EMPTYSTR(ident->address));
   392 
   393     // FIXME: does this need to be done everywhere?> I think not.
   394     if (ident) {
   395         user_id = strdup(ident->user_id);
   396         if (!user_id) {
   397             status = PEP_OUT_OF_MEMORY;
   398             goto pEp_free;
   399         }
   400     }
   401     else {
   402         status = get_default_own_userid(session, &user_id);
   403         if (status != PEP_STATUS_OK || !user_id)
   404             goto pEp_free;                    
   405     }
   406     
   407     // FIXME: Make sure this can't result in a double-free in recursive calls
   408     tmp_ident = (ident ? identity_dup(ident) : new_identity(NULL, NULL, user_id, NULL));
   409     
   410     if (reset_all_for_user) {
   411         status = get_all_keys_for_user(session, user_id, &keys);
   412         // TODO: free
   413         if (status == PEP_STATUS_OK) {
   414             stringlist_t* curr_key;
   415             
   416             for (curr_key = keys; curr_key && curr_key->value; curr_key = curr_key->next) {
   417                 // FIXME: Is the ident really necessary?
   418                 status = key_reset(session, curr_key->value, tmp_ident);
   419                 if (status != PEP_STATUS_OK)
   420                     break;
   421             }
   422         }
   423         goto pEp_free;
   424     }                   
   425     else {
   426         // tmp_ident => tmp_ident->user_id (was checked)
   427         //
   428         // !(EMPTYSTR(fpr) && (!tmp_ident || EMPTYSTR(tmp_ident->address)))
   429         // => fpr || (tmp_ident && tmp_ident->address)
   430         //
   431         // so: We have an fpr or we have an ident with user_id and address
   432         //     or both
   433         if (!fpr_copy) {
   434             // We are guaranteed to have an ident w/ id + addr here.
   435             // Get the default key.
   436             pEp_identity* stored_ident = NULL;
   437             status = get_identity(session, tmp_ident->address, 
   438                                   tmp_ident->user_id, &stored_ident);
   439 
   440             // FIXME FIXME FIXME
   441             if (status == PEP_STATUS_OK) {
   442                 // transfer ownership
   443                 fpr_copy = stored_ident->fpr;
   444                 stored_ident->fpr = NULL;
   445                 free_identity(stored_ident);                
   446             }
   447             
   448             if (!fpr_copy || status == PEP_CANNOT_FIND_IDENTITY) {
   449                 // There's no identity default. Try resetting user default
   450                 status = get_user_default_key(session, tmp_ident->user_id, &fpr_copy);
   451             }            
   452             
   453             if (!fpr_copy || status != PEP_STATUS_OK) // No default to free. We're done here.
   454                 goto pEp_free;            
   455         }
   456         
   457         // Ok - now we have at least an ident with user_id and an fpr.
   458         // Now it matters if we're talking about ourselves or a partner.
   459         bool is_own_private = false;
   460         if (is_me(session, tmp_ident)) {
   461             bool own_key = false;            
   462             status = is_own_key(session, fpr_copy, &own_key);
   463 
   464             if (status != PEP_STATUS_OK)
   465                 goto pEp_free;
   466             if (!own_key) {
   467                 status = PEP_ILLEGAL_VALUE;
   468                 goto pEp_free;
   469             }
   470 
   471             status = contains_priv_key(session, fpr_copy, &is_own_private);
   472             if (status != PEP_STATUS_OK && status != PEP_KEY_NOT_FOUND)
   473                 goto pEp_free;
   474         }
   475         
   476         // Up to this point, we haven't cared about whether or not we 
   477         // had a full identity. Now we have to deal with that in the 
   478         // case of own identities with private keys.
   479         
   480         if (is_own_private) {
   481             
   482             // If there's no address, we want to reset this key for every identity 
   483             // it's a part of. Since this means generating new keys, we have to 
   484             // grab all the identities associated with it.
   485             if (EMPTYSTR(tmp_ident->address)) {
   486                 status = get_identities_by_main_key_id(session, fpr_copy, &key_idents);
   487                 
   488                 if (status != PEP_CANNOT_FIND_IDENTITY) {
   489                     if (status == PEP_STATUS_OK) {
   490                         // now have ident list, or should
   491                         identity_list* curr_ident;
   492                         
   493                         for (curr_ident = key_idents; curr_ident && curr_ident->ident; 
   494                                                         curr_ident = curr_ident->next) {
   495                             
   496                             pEp_identity* this_identity = curr_ident->ident;
   497                             // Do the full reset on this identity        
   498                             status = key_reset(session, fpr_copy, this_identity);
   499                             
   500                             // Ident list gets freed below, do not free here!
   501 
   502                             if (status != PEP_STATUS_OK)
   503                                 break;
   504                             
   505                         }
   506                     }
   507                     // Ok, we've either now reset for each own identity with this key, or 
   508                     // we got an error and want to bail anyway.
   509                     goto pEp_free;
   510                 }    
   511             }
   512             
   513             // Create revocation
   514             status = revoke_key(session, fpr_copy, NULL);
   515             
   516             // If we have a full identity, we have some cleanup and generation tasks here
   517             if (!EMPTYSTR(tmp_ident->address)) {
   518                 // generate new key
   519                 if (status == PEP_STATUS_OK) {
   520                     tmp_ident->fpr = NULL;
   521                     status = generate_keypair(session, tmp_ident);
   522                 }
   523                 if (status == PEP_STATUS_OK) {
   524                     new_key = strdup(tmp_ident->fpr);
   525                     status = set_own_key(session, tmp_ident, new_key);
   526                 }
   527                 // mistrust fpr from trust
   528                 tmp_ident->fpr = fpr_copy;
   529                 
   530                 tmp_ident->comm_type = PEP_ct_mistrusted;
   531                 status = set_trust(session, tmp_ident);
   532                 tmp_ident->fpr = NULL;
   533                 
   534                 // Done with old use of ident.
   535                 if (status == PEP_STATUS_OK) {
   536                     // Update fpr for outgoing
   537                     status = myself(session, tmp_ident);
   538                 }
   539             }    
   540             
   541             if (status == PEP_STATUS_OK)
   542                 // cascade that mistrust for anyone using this key
   543                 status = mark_as_compromised(session, fpr_copy);
   544                 
   545             if (status == PEP_STATUS_OK)
   546                 status = remove_fpr_as_default(session, fpr_copy);
   547             if (status == PEP_STATUS_OK)
   548                 status = add_mistrusted_key(session, fpr_copy);
   549 
   550             // If there's a new key, do the DB linkage with the revoked one, and 
   551             // send the key reset mail opportunistically to recently contacted
   552             // partners
   553             if (new_key) {
   554                 // add to revocation list 
   555                 if (status == PEP_STATUS_OK) 
   556                     status = set_revoked(session, fpr_copy, new_key, time(NULL));            
   557                 // for all active communication partners:
   558                 //      active_send revocation
   559                 if (status == PEP_STATUS_OK)
   560                     status = send_key_reset_to_recents(session, fpr_copy, new_key);        
   561             }        
   562         } // end is_own_private
   563         else {
   564             // This is a public key (or a private key that isn't ours, which means
   565             // we want it gone anyway)
   566             //
   567             // Delete this key from the keyring.
   568             status = delete_keypair(session, fpr_copy);
   569         }
   570 
   571         // REGARDLESS OF WHO OWNS THE KEY, WE NOW NEED TO REMOVE IT AS A DEFAULT.
   572         PEP_STATUS cached_status = status;
   573         // remove fpr from all identities
   574         // remove fpr from all users
   575         status = remove_fpr_as_default(session, fpr_copy);
   576         // delete key from DB - this does NOT touch the keyring!
   577         // Note: for own priv keys, we cannot do this. But we'll never encrypt to/from it.
   578         if (status == PEP_STATUS_OK && !is_own_private) {
   579             status = remove_key(session, fpr_copy);
   580         }
   581         if (status == PEP_STATUS_OK)
   582             status = cached_status;
   583     }           
   584         
   585 pEp_free:
   586     if (!ident)
   587         free_identity(tmp_ident);
   588     free(fpr_copy);
   589     free(own_id);
   590     free_identity_list(key_idents);
   591     free_stringlist(keys);
   592     free(new_key);    
   593     return status;
   594 }