src/keymanagement.c
author Edouard Tisserant
Wed, 29 Jun 2016 17:31:50 +0200
changeset 774 bc90d5bf74d5
parent 755 3bac36e6fb86
child 775 af16c9faedf2
permissions -rw-r--r--
Moved myself() own key election to a separate function
     1 #include "platform.h"
     2 
     3 #include <string.h>
     4 #include <stdio.h>
     5 #include <stdlib.h>
     6 #include <assert.h>
     7 #include <ctype.h>
     8 
     9 #include "pEp_internal.h"
    10 #include "keymanagement.h"
    11 
    12 #ifndef EMPTYSTR
    13 #define EMPTYSTR(STR) ((STR) == NULL || (STR)[0] == '\0')
    14 #endif
    15 
    16 #define KEY_EXPIRE_DELTA (60 * 60 * 24 * 365)
    17 
    18 // Space tolerant and case insensitive fingerprint string compare
    19 static int _same_fpr(
    20         const char* fpra,
    21         size_t fpras,
    22         const char* fprb,
    23         size_t fprbs
    24     )
    25 {
    26     size_t ai = 0;
    27     size_t bi = 0;
    28     
    29     do
    30     {
    31         if(fpra[ai] == 0 || fprb[bi] == 0)
    32         {
    33             return 0;
    34         }
    35         else if(fpra[ai] == ' ')
    36         {
    37             ai++;
    38         }
    39         else if(fprb[bi] == ' ')
    40         {
    41             bi++;
    42         }
    43         else if(toupper(fpra[ai]) == toupper(fprb[bi]))
    44         {
    45             ai++;
    46             bi++;
    47         }
    48         else
    49         {
    50             return 0;
    51         }
    52         
    53     }
    54     while(ai < fpras && bi < fprbs);
    55     
    56     return ai == fpras && bi == fprbs;
    57 }
    58 
    59 PEP_STATUS elect_pubkey(
    60         PEP_SESSION session, pEp_identity * identity
    61     )
    62 {
    63     PEP_STATUS status;
    64     stringlist_t *keylist;
    65     char *_fpr = NULL;
    66     identity->comm_type = PEP_ct_unknown;
    67 
    68     status = find_keys(session, identity->address, &keylist);
    69     assert(status != PEP_OUT_OF_MEMORY);
    70     if (status == PEP_OUT_OF_MEMORY)
    71         return PEP_OUT_OF_MEMORY;
    72 
    73     stringlist_t *_keylist;
    74     for (_keylist = keylist; _keylist && _keylist->value; _keylist = _keylist->next) {
    75         PEP_comm_type _comm_type_key;
    76 
    77         status = get_key_rating(session, _keylist->value, &_comm_type_key);
    78         assert(status != PEP_OUT_OF_MEMORY);
    79         if (status == PEP_OUT_OF_MEMORY) {
    80             free_stringlist(keylist);
    81             return PEP_OUT_OF_MEMORY;
    82         }
    83 
    84         if (_comm_type_key != PEP_ct_compromized &&
    85             _comm_type_key != PEP_ct_unknown)
    86         {
    87             if (identity->comm_type == PEP_ct_unknown ||
    88                 _comm_type_key > identity->comm_type)
    89             {
    90                 identity->comm_type = _comm_type_key;
    91                 _fpr = _keylist->value;
    92             }
    93         }
    94     }
    95 
    96     if (_fpr) {
    97         free(identity->fpr);
    98 
    99         identity->fpr = strdup(_fpr);
   100         if (identity->fpr == NULL) {
   101             free_stringlist(keylist);
   102             return PEP_OUT_OF_MEMORY;
   103         }
   104     }
   105     free_stringlist(keylist);
   106     return PEP_STATUS_OK;
   107 }
   108 
   109 DYNAMIC_API PEP_STATUS update_identity(
   110         PEP_SESSION session, pEp_identity * identity
   111     )
   112 {
   113     pEp_identity *stored_identity;
   114     PEP_STATUS status;
   115 
   116     assert(session);
   117     assert(identity);
   118     assert(!EMPTYSTR(identity->address));
   119 
   120     if (!(session && identity && !EMPTYSTR(identity->address)))
   121         return PEP_ILLEGAL_VALUE;
   122 
   123     int _no_user_id = EMPTYSTR(identity->user_id);
   124 
   125     if (_no_user_id)
   126     {
   127         free(identity->user_id);
   128 
   129         identity->user_id = calloc(1, strlen(identity->address) + 6);
   130         if (!identity->user_id)
   131         {
   132             return PEP_OUT_OF_MEMORY;
   133         }
   134         snprintf(identity->user_id, strlen(identity->address) + 5,
   135                  "TOFU_%s", identity->address);
   136     }
   137     
   138     status = get_identity(session,
   139                           identity->address,
   140                           identity->user_id,
   141                           &stored_identity);
   142     
   143     assert(status != PEP_OUT_OF_MEMORY);
   144     if (status == PEP_OUT_OF_MEMORY)
   145         return PEP_OUT_OF_MEMORY;
   146 
   147     if (stored_identity) {
   148         PEP_comm_type _comm_type_key;
   149         status = get_key_rating(session, stored_identity->fpr, &_comm_type_key);
   150         assert(status != PEP_OUT_OF_MEMORY);
   151         if (status == PEP_OUT_OF_MEMORY)
   152             return PEP_OUT_OF_MEMORY;
   153 
   154         if (EMPTYSTR(identity->username)) {
   155             free(identity->username);
   156             identity->username = strdup(stored_identity->username);
   157             assert(identity->username);
   158             if (identity->username == NULL)
   159                 return PEP_OUT_OF_MEMORY;
   160         }
   161 
   162         if (EMPTYSTR(identity->fpr)) {
   163             identity->fpr = strdup(stored_identity->fpr);
   164             assert(identity->fpr);
   165             if (identity->fpr == NULL)
   166                 return PEP_OUT_OF_MEMORY;
   167             if (_comm_type_key < PEP_ct_unconfirmed_encryption) {
   168                 PEP_STATUS status = elect_pubkey(session, identity);
   169                 if (status != PEP_STATUS_OK)
   170                     return status;
   171             }
   172             else {
   173                 identity->comm_type = stored_identity->comm_type;
   174             }
   175         }
   176         else /* !EMPTYSTR(identity->fpr) */ {
   177             if (_same_fpr(identity->fpr,
   178                           strlen(identity->fpr),
   179                           stored_identity->fpr,
   180                           strlen(stored_identity->fpr))) {
   181                 if (_comm_type_key < PEP_ct_unconfirmed_encryption) {
   182                     identity->comm_type = _comm_type_key;
   183                 }else{
   184                     identity->comm_type = stored_identity->comm_type;
   185                     if (identity->comm_type == PEP_ct_unknown) {
   186                         identity->comm_type = _comm_type_key;
   187                     }
   188                 }
   189             } else {
   190                 status = get_trust(session, identity);
   191                 assert(status != PEP_OUT_OF_MEMORY);
   192                 if (status == PEP_OUT_OF_MEMORY)
   193                     return PEP_OUT_OF_MEMORY;
   194                 if (identity->comm_type < stored_identity->comm_type)
   195                     identity->comm_type = PEP_ct_unknown;
   196             }
   197         }
   198 
   199         if (identity->lang[0] == 0) {
   200             identity->lang[0] = stored_identity->lang[0];
   201             identity->lang[1] = stored_identity->lang[1];
   202             identity->lang[2] = 0;
   203         }
   204     }
   205     else /* stored_identity == NULL */ {
   206         if (!EMPTYSTR(identity->fpr)) {
   207             PEP_comm_type _comm_type_key;
   208 
   209             status = get_key_rating(session, identity->fpr, &_comm_type_key);
   210             assert(status != PEP_OUT_OF_MEMORY);
   211             if (status == PEP_OUT_OF_MEMORY)
   212                 return PEP_OUT_OF_MEMORY;
   213 
   214             identity->comm_type = _comm_type_key;
   215         }
   216         else /* EMPTYSTR(identity->fpr) */ {
   217             PEP_STATUS status = elect_pubkey(session, identity);
   218             if (status != PEP_STATUS_OK)
   219                 return status;
   220         }
   221     }
   222 
   223     status = PEP_STATUS_OK;
   224 
   225     if (identity->comm_type != PEP_ct_unknown && !EMPTYSTR(identity->user_id)) {
   226         assert(!EMPTYSTR(identity->username)); // this should not happen
   227 
   228         if (EMPTYSTR(identity->username)) { // mitigate
   229             free(identity->username);
   230             identity->username = strdup("anonymous");
   231             if (identity->username == NULL)
   232                 return PEP_OUT_OF_MEMORY;
   233         }
   234 
   235         // Identity doesn't get stored if call was just about checking existing
   236         // user by address (i.e. no user id given but already stored)
   237         if (!(_no_user_id && stored_identity))
   238         {
   239             status = set_identity(session, identity);
   240             assert(status == PEP_STATUS_OK);
   241             if (status != PEP_STATUS_OK) {
   242                 return status;
   243             }
   244         }
   245     }
   246 
   247     if (identity->comm_type != PEP_ct_compromized &&
   248             identity->comm_type < PEP_ct_strong_but_unconfirmed)
   249         if (session->examine_identity)
   250             session->examine_identity(identity, session->examine_management);
   251 
   252     return status;
   253 }
   254 
   255 PEP_STATUS elect_ownkey(
   256         PEP_SESSION session, pEp_identity * identity
   257     )
   258 {
   259     PEP_STATUS status;
   260     stringlist_t *keylist = NULL;
   261 
   262     free(identity->fpr);
   263     identity->fpr = NULL;
   264 
   265     status = find_keys(session, identity->address, &keylist);
   266     assert(status != PEP_OUT_OF_MEMORY);
   267     if (status == PEP_OUT_OF_MEMORY)
   268         return PEP_OUT_OF_MEMORY;
   269     
   270     if (keylist != NULL && keylist->value != NULL)
   271     {
   272         char *_fpr = NULL;
   273         identity->comm_type = PEP_ct_unknown;
   274 
   275         stringlist_t *_keylist;
   276         for (_keylist = keylist; _keylist && _keylist->value; _keylist = _keylist->next) {
   277             bool is_own = false;
   278             
   279             if (session->use_only_own_private_keys)
   280             {
   281                 status = own_key_is_listed(session, _keylist->value, &is_own);
   282                 assert(status == PEP_STATUS_OK);
   283                 if (status != PEP_STATUS_OK) {
   284                     free_stringlist(keylist);
   285                     return status;
   286                 }
   287             }
   288 
   289             // TODO : also accept synchronized device group keys ?
   290             
   291             if (!session->use_only_own_private_keys || is_own)
   292             {
   293                 PEP_comm_type _comm_type_key;
   294                 
   295                 status = get_key_rating(session, _keylist->value, &_comm_type_key);
   296                 assert(status != PEP_OUT_OF_MEMORY);
   297                 if (status == PEP_OUT_OF_MEMORY) {
   298                     free_stringlist(keylist);
   299                     return PEP_OUT_OF_MEMORY;
   300                 }
   301                 
   302                 if (_comm_type_key != PEP_ct_compromized &&
   303                     _comm_type_key != PEP_ct_unknown)
   304                 {
   305                     if (identity->comm_type == PEP_ct_unknown ||
   306                         _comm_type_key > identity->comm_type)
   307                     {
   308                         identity->comm_type = _comm_type_key;
   309                         _fpr = _keylist->value;
   310                     }
   311                 }
   312             }
   313         }
   314         
   315         if (_fpr)
   316         {
   317             identity->fpr = strdup(_fpr);
   318             assert(identity->fpr);
   319             if (identity->fpr == NULL)
   320             {
   321                 free_stringlist(keylist);
   322                 return PEP_OUT_OF_MEMORY;
   323             }
   324         }
   325         free_stringlist(keylist);
   326     }
   327     return PEP_STATUS_OK;
   328 }
   329 
   330 DYNAMIC_API PEP_STATUS myself(PEP_SESSION session, pEp_identity * identity)
   331 {
   332     pEp_identity *stored_identity;
   333     PEP_STATUS status;
   334 
   335     assert(session);
   336     assert(identity);
   337     assert(identity->address);
   338     assert(identity->username);
   339     assert(EMPTYSTR(identity->user_id) ||
   340            strcmp(identity->user_id, PEP_OWN_USERID) == 0);
   341 
   342     if (!(session && identity && identity->address && identity->username &&
   343           (EMPTYSTR(identity->user_id) ||
   344            strcmp(identity->user_id, PEP_OWN_USERID) == 0)))
   345         return PEP_ILLEGAL_VALUE;
   346 
   347     identity->comm_type = PEP_ct_pEp;
   348     identity->me = true;
   349     
   350     if(EMPTYSTR(identity->user_id))
   351     {
   352         free(identity->user_id);
   353         identity->user_id = strdup(PEP_OWN_USERID);
   354         assert(identity->user_id);
   355         if (identity->user_id == NULL)
   356         {
   357             return PEP_OUT_OF_MEMORY;
   358         }
   359     }
   360 
   361     DEBUG_LOG("myself", "debug", identity->address);
   362     
   363     status = get_identity(session,
   364                           identity->address,
   365                           identity->user_id,
   366                           &stored_identity);
   367     
   368     assert(status != PEP_OUT_OF_MEMORY);
   369     if (status == PEP_OUT_OF_MEMORY)
   370         return PEP_OUT_OF_MEMORY;
   371     
   372     if (stored_identity)
   373     {
   374         if (EMPTYSTR(identity->fpr)) {
   375             identity->fpr = strdup(stored_identity->fpr);
   376             assert(identity->fpr);
   377             if (identity->fpr == NULL)
   378             {
   379                 return PEP_OUT_OF_MEMORY;
   380             }
   381         }
   382     }
   383     else if (!EMPTYSTR(identity->fpr))
   384     {
   385         // App must have a good reason to give fpr, such as explicit
   386         // import of private key, or similar.
   387 
   388         // Take given fpr as-is.
   389     }
   390     else
   391     {
   392         status = elect_ownkey(session, identity);
   393         assert(status == PEP_STATUS_OK);
   394         if (status != PEP_STATUS_OK) {
   395             return status;
   396         }
   397     }
   398 
   399     bool revoked = false;
   400     char *r_fpr = NULL;
   401     if (!EMPTYSTR(identity->fpr))
   402     {
   403         status = key_revoked(session, identity->fpr, &revoked);
   404         assert(status == PEP_STATUS_OK);
   405         if (status != PEP_STATUS_OK) {
   406             return status;
   407         }
   408     }
   409     
   410     if (EMPTYSTR(identity->fpr) || revoked)
   411     {        
   412         if(revoked)
   413         {
   414             r_fpr = identity->fpr;
   415             identity->fpr = NULL;
   416         }
   417         
   418         DEBUG_LOG("generating key pair", "debug", identity->address);
   419         status = generate_keypair(session, identity);
   420         assert(status != PEP_OUT_OF_MEMORY);
   421         if (status != PEP_STATUS_OK) {
   422             char buf[11];
   423             snprintf(buf, 11, "%d", status);
   424             DEBUG_LOG("generating key pair failed", "debug", buf);
   425             if(revoked && r_fpr)
   426                 free(r_fpr);
   427             return status;
   428         }
   429         
   430         if(revoked)
   431         {
   432             status = set_revoked(session, r_fpr,
   433                                  identity->fpr, time(NULL));
   434             free(r_fpr);
   435             if (status != PEP_STATUS_OK) {
   436                 return status;
   437             }
   438         }
   439     }
   440     else
   441     {
   442         bool expired;
   443         status = key_expired(session, identity->fpr, 
   444                              time(NULL) + (7*24*3600), // In a week
   445                              &expired);
   446 
   447         assert(status == PEP_STATUS_OK);
   448         if (status != PEP_STATUS_OK) {
   449             return status;
   450         }
   451 
   452         if (status == PEP_STATUS_OK && expired) {
   453             timestamp *ts = new_timestamp(time(NULL) + KEY_EXPIRE_DELTA);
   454             renew_key(session, identity->fpr, ts);
   455             free_timestamp(ts);
   456         }
   457     }
   458 
   459     status = set_identity(session, identity);
   460     assert(status == PEP_STATUS_OK);
   461     if (status != PEP_STATUS_OK) {
   462         return status;
   463     }
   464 
   465     return PEP_STATUS_OK;
   466 
   467 }
   468 
   469 DYNAMIC_API PEP_STATUS register_examine_function(
   470         PEP_SESSION session, 
   471         examine_identity_t examine_identity,
   472         void *management
   473     )
   474 {
   475     assert(session);
   476     if (!session)
   477         return PEP_ILLEGAL_VALUE;
   478 
   479     session->examine_management = management;
   480     session->examine_identity = examine_identity;
   481 
   482     return PEP_STATUS_OK;
   483 }
   484 
   485 DYNAMIC_API PEP_STATUS do_keymanagement(
   486         retrieve_next_identity_t retrieve_next_identity,
   487         void *management
   488     )
   489 {
   490     PEP_SESSION session;
   491     pEp_identity *identity;
   492     PEP_STATUS status;
   493 
   494     assert(retrieve_next_identity);
   495     assert(management);
   496 
   497     if (!retrieve_next_identity || !management)
   498         return PEP_ILLEGAL_VALUE;
   499 
   500     status = init(&session);
   501     assert(status == PEP_STATUS_OK);
   502     if (status != PEP_STATUS_OK)
   503         return status;
   504 
   505     log_event(session, "keymanagement thread started", "pEp engine", NULL, NULL);
   506 
   507     while ((identity = retrieve_next_identity(management))) 
   508     {
   509         assert(identity->address);
   510         if(identity->address)
   511         {
   512             DEBUG_LOG("do_keymanagement", "retrieve_next_identity", identity->address);
   513 
   514             if (identity->me) {
   515                 status = myself(session, identity);
   516             } else {
   517                 status = recv_key(session, identity->address);
   518             }
   519 
   520             assert(status != PEP_OUT_OF_MEMORY);
   521             if(status == PEP_OUT_OF_MEMORY)
   522                 return PEP_OUT_OF_MEMORY;
   523         }
   524         free_identity(identity);
   525     }
   526 
   527     log_event(session, "keymanagement thread shutdown", "pEp engine", NULL, NULL);
   528 
   529     release(session);
   530     return PEP_STATUS_OK;
   531 }
   532 
   533 DYNAMIC_API PEP_STATUS key_compromized(
   534         PEP_SESSION session,
   535         pEp_identity *ident
   536     )
   537 {
   538     PEP_STATUS status = PEP_STATUS_OK;
   539 
   540     assert(session);
   541     assert(ident);
   542     assert(!EMPTYSTR(ident->fpr));
   543 
   544     if (!(session && ident && ident->fpr))
   545         return PEP_ILLEGAL_VALUE;
   546 
   547     if (ident->me)
   548     {
   549         revoke_key(session, ident->fpr, NULL);
   550         myself(session, ident);
   551     }
   552     else
   553     {
   554         status = mark_as_compromized(session, ident->fpr);
   555     }
   556 
   557     return status;
   558 }
   559 
   560 DYNAMIC_API PEP_STATUS key_reset_trust(
   561         PEP_SESSION session,
   562         pEp_identity *ident
   563     )
   564 {
   565     PEP_STATUS status = PEP_STATUS_OK;
   566 
   567     assert(session);
   568     assert(ident);
   569     assert(!ident->me);
   570     assert(!EMPTYSTR(ident->fpr));
   571     assert(!EMPTYSTR(ident->address));
   572     assert(!EMPTYSTR(ident->user_id));
   573 
   574     if (!(session && ident && !ident->me && ident->fpr && ident->address &&
   575             ident->user_id))
   576         return PEP_ILLEGAL_VALUE;
   577 
   578     status = update_identity(session, ident);
   579     if (status != PEP_STATUS_OK)
   580         return status;
   581 
   582     if (ident->comm_type == PEP_ct_mistrusted)
   583         ident->comm_type = PEP_ct_unknown;
   584     else
   585         ident->comm_type &= ~PEP_ct_confirmed;
   586 
   587     status = set_identity(session, ident);
   588     if (status != PEP_STATUS_OK)
   589         return status;
   590 
   591     if (ident->comm_type == PEP_ct_unknown)
   592         status = update_identity(session, ident);
   593     return status;
   594 }
   595 
   596 DYNAMIC_API PEP_STATUS trust_personal_key(
   597         PEP_SESSION session,
   598         pEp_identity *ident
   599     )
   600 {
   601     PEP_STATUS status = PEP_STATUS_OK;
   602 
   603     assert(session);
   604     assert(ident);
   605     assert(!EMPTYSTR(ident->address));
   606     assert(!EMPTYSTR(ident->user_id));
   607     assert(!EMPTYSTR(ident->fpr));
   608     assert(!ident->me);
   609 
   610     if (!ident || EMPTYSTR(ident->address) || EMPTYSTR(ident->user_id) ||
   611             EMPTYSTR(ident->fpr) || ident->me)
   612         return PEP_ILLEGAL_VALUE;
   613 
   614     status = update_identity(session, ident);
   615     if (status != PEP_STATUS_OK)
   616         return status;
   617 
   618     if (ident->comm_type > PEP_ct_strong_but_unconfirmed) {
   619         ident->comm_type |= PEP_ct_confirmed;
   620         status = set_identity(session, ident);
   621     }
   622     else {
   623         // MISSING: S/MIME has to be handled depending on trusted CAs
   624         status = PEP_CANNOT_SET_TRUST;
   625     }
   626 
   627     return status;
   628 }
   629 
   630 DYNAMIC_API PEP_STATUS own_key_is_listed(
   631                                            PEP_SESSION session,
   632                                            const char *fpr,
   633                                            bool *listed
   634                                            )
   635 {
   636     PEP_STATUS status = PEP_STATUS_OK;
   637     int count;
   638     
   639     assert(session && fpr && fpr[0] && listed);
   640     
   641     if (!(session && fpr && fpr[0] && listed))
   642         return PEP_ILLEGAL_VALUE;
   643     
   644     *listed = false;
   645     
   646     sqlite3_reset(session->own_key_is_listed);
   647     sqlite3_bind_text(session->own_key_is_listed, 1, fpr, -1, SQLITE_STATIC);
   648     
   649     int result;
   650     
   651     result = sqlite3_step(session->own_key_is_listed);
   652     switch (result) {
   653         case SQLITE_ROW:
   654             count = sqlite3_column_int(session->own_key_is_listed, 0);
   655             *listed = count > 0;
   656             status = PEP_STATUS_OK;
   657             break;
   658             
   659         default:
   660             status = PEP_UNKNOWN_ERROR;
   661     }
   662     
   663     sqlite3_reset(session->own_key_is_listed);
   664     return status;
   665 }
   666 
   667 DYNAMIC_API PEP_STATUS own_key_retrieve(
   668                                           PEP_SESSION session,
   669                                           stringlist_t **own_key
   670                                           )
   671 {
   672     PEP_STATUS status = PEP_STATUS_OK;
   673     
   674     assert(session);
   675     assert(own_key);
   676     
   677     if (!(session && own_key))
   678         return PEP_ILLEGAL_VALUE;
   679     
   680     *own_key = NULL;
   681     stringlist_t *_own_key = new_stringlist(NULL);
   682     if (_own_key == NULL)
   683         goto enomem;
   684     
   685     sqlite3_reset(session->own_key_retrieve);
   686     
   687     int result;
   688     const char *fpr = NULL;
   689     
   690     stringlist_t *_bl = _own_key;
   691     do {
   692         result = sqlite3_step(session->own_key_retrieve);
   693         switch (result) {
   694             case SQLITE_ROW:
   695                 fpr = (const char *) sqlite3_column_text(session->own_key_retrieve, 0);
   696                 
   697                 _bl = stringlist_add(_bl, fpr);
   698                 if (_bl == NULL)
   699                     goto enomem;
   700                 
   701                 break;
   702                 
   703             case SQLITE_DONE:
   704                 break;
   705                 
   706             default:
   707                 status = PEP_UNKNOWN_ERROR;
   708                 result = SQLITE_DONE;
   709         }
   710     } while (result != SQLITE_DONE);
   711     
   712     sqlite3_reset(session->own_key_retrieve);
   713     if (status == PEP_STATUS_OK)
   714         *own_key = _own_key;
   715     else
   716         free_stringlist(_own_key);
   717     
   718     goto the_end;
   719     
   720 enomem:
   721     free_stringlist(_own_key);
   722     status = PEP_OUT_OF_MEMORY;
   723     
   724 the_end:
   725     return status;
   726 }