src/sync_impl.c
author Edouard Tisserant <edouard@pep-project.org>
Tue, 18 Oct 2016 21:47:38 +0200
changeset 1316 942c9943ae9d
parent 1297 78f5f9894fbd
child 1338 515f4edb52d0
permissions -rw-r--r--
ENGINE-121 #comment keep (i.e. not consume) updateRequests until expiry. added inhibit time on some event to prevent re-uptake of updateRequests and cannotDecrypt, otherwise looping and flooding mailbox
     1 #include "platform.h"
     2 
     3 // it seems pEp_internal.h needs to be the first pEp include due to the 
     4 // #define for the dllimport / dllexport DYNAMIC_API stuff.
     5 #include "pEp_internal.h"
     6 
     7 #include "../asn.1/DeviceGroup-Protocol.h"
     8 #include "sync_impl.h"
     9 #include "keymanagement.h"
    10 #include "message_api.h"
    11 #include "map_asn1.h"
    12 #include "baseprotocol.h"
    13 
    14 #define SYNC_VERSION_MAJOR 1
    15 #define SYNC_VERSION_MINOR 0
    16 
    17 #define SYNC_INHIBIT_TIME (60*10)
    18 #define SYNC_MSG_EXPIRE_TIME (60 * 10)
    19 
    20 struct _sync_msg_t {
    21     bool is_a_message;
    22     union {
    23         DeviceGroup_Protocol_t *message;
    24         struct {
    25             DeviceState_event event;
    26             Identity partner;
    27             void *extra;
    28         } event;
    29     } u;
    30 };
    31 
    32 PEP_STATUS receive_sync_msg(
    33         PEP_SESSION session,
    34         sync_msg_t *sync_msg
    35     )
    36 {
    37     PEP_STATUS status;
    38     void *extra = NULL;
    39     Identity partner = NULL;
    40     DeviceState_event event = DeviceState_event_NONE;
    41     assert(session && sync_msg);
    42     if (!(session && sync_msg))
    43         return PEP_ILLEGAL_VALUE;
    44 
    45     if(sync_msg->is_a_message){
    46         DeviceGroup_Protocol_t *msg = sync_msg->u.message;
    47         assert(msg && msg->payload.present != DeviceGroup_Protocol__payload_PR_NOTHING);
    48         if (!(msg && msg->payload.present != DeviceGroup_Protocol__payload_PR_NOTHING)){
    49             status = PEP_OUT_OF_MEMORY;
    50             goto error;
    51         }
    52 
    53         switch (msg->payload.present) {
    54             case DeviceGroup_Protocol__payload_PR_beacon:
    55                 event = Beacon;
    56                 break;
    57 
    58             case DeviceGroup_Protocol__payload_PR_handshakeRequest:
    59                 event = HandshakeRequest;
    60                 break;
    61 
    62             case DeviceGroup_Protocol__payload_PR_updateRequest:
    63                 event = UpdateRequest;
    64                 break;
    65 
    66             case DeviceGroup_Protocol__payload_PR_groupKeys:
    67             case DeviceGroup_Protocol__payload_PR_groupUpdate:
    68             {
    69                 identity_list *group_keys = IdentityList_to_identity_list(
    70                         msg->payload.present == 
    71                           DeviceGroup_Protocol__payload_PR_groupKeys ?
    72                             &msg->payload.choice.groupKeys.ownIdentities :
    73                             &msg->payload.choice.groupUpdate.ownIdentities,
    74                         NULL);
    75                 if (!group_keys) {
    76                     status = PEP_OUT_OF_MEMORY;
    77                     ASN_STRUCT_FREE(asn_DEF_DeviceGroup_Protocol, msg);
    78                     goto error;
    79                 }
    80                 extra = (void *) group_keys;
    81                 event = msg->payload.present == 
    82                           DeviceGroup_Protocol__payload_PR_groupKeys ?
    83                             GroupKeys : GroupUpdate;
    84                 break;
    85             }
    86 
    87             default:
    88                 status = PEP_SYNC_ILLEGAL_MESSAGE;
    89                 ASN_STRUCT_FREE(asn_DEF_DeviceGroup_Protocol, msg);
    90                 goto error;
    91         }
    92 
    93         partner = Identity_to_Struct(&msg->header.me, NULL);
    94         if (!partner){
    95             status = PEP_OUT_OF_MEMORY;
    96             ASN_STRUCT_FREE(asn_DEF_DeviceGroup_Protocol, msg);
    97             goto error;
    98         }
    99     }
   100     else{
   101         partner = sync_msg->u.event.partner;
   102         extra = sync_msg->u.event.extra;
   103         event = sync_msg->u.event.event;
   104     }
   105 
   106     // Event inhibition, to limit mailbox and prevent cycles
   107     time_t *last = NULL;
   108     switch(event){
   109         case CannotDecrypt:
   110             last = &session->LastCannotDecrypt;
   111             break;
   112 
   113         case UpdateRequest:
   114             last = &session->LastUpdateRequest;
   115             break;
   116 
   117         default:
   118             break;
   119     }
   120     time_t now = time(NULL);
   121     if(last != NULL){
   122         if(*last != 0 && (*last + SYNC_INHIBIT_TIME) > now ){
   123             free_identity(partner);
   124             status = PEP_STATEMACHINE_INHIBITED_EVENT;
   125             goto error;
   126         }
   127         *last = now;
   128     }
   129 
   130     // partner identity must be explicitely added DB to later
   131     // be able to communicate securely with it.
   132     if(partner){
   133         // protect virtual user IDs 
   134         if((strncmp("TOFU_", partner->user_id, 6) == 0 &&
   135            strlen(partner->user_id) == strlen(partner->address) + 6 &&
   136            strcmp(partner->user_id + 6, partner->address)) ||
   137         // protect own ID 
   138            (strcmp(PEP_OWN_USERID, partner->user_id) == 0)){
   139             status = PEP_SYNC_ILLEGAL_MESSAGE;
   140             goto error;
   141         }
   142 
   143         // partner IDs are UUIDs bound to session lifespan
   144         // and therefore partner identities are not supposed
   145         // to mutate over time, but just not be used anymore.
   146         // It should then be safe to accept given identity if not 
   147         // already pre-existing
   148         pEp_identity *stored_identity = NULL;
   149         status = get_identity(session,
   150                               partner->address,
   151                               partner->user_id,
   152                               &stored_identity);
   153 
   154         if (!stored_identity) {
   155             // make a safe copy of partner, with no flags or comm_type
   156             pEp_identity *tmpident = new_identity(partner->address,
   157                                                   partner->fpr,
   158                                                   partner->user_id,
   159                                                   partner->username);
   160             if (tmpident == NULL){
   161                 status = PEP_OUT_OF_MEMORY;
   162                 goto error;
   163             }
   164 
   165             // finaly add partner to DB
   166             status = set_identity(session, tmpident);
   167             free_identity(tmpident);
   168             assert(status == PEP_STATUS_OK);
   169             if (status != PEP_STATUS_OK) {
   170                 goto error;
   171             }
   172         }
   173         else if (status == PEP_STATUS_OK) {
   174             free_identity(stored_identity);
   175         } 
   176         else
   177             goto error;
   178     }
   179 
   180     status = fsm_DeviceState_inject(session, event, partner, extra);
   181 
   182     free_identity(partner);
   183 
   184 error:
   185     free(sync_msg);
   186 
   187     return status;
   188 }
   189 
   190 // TODO: DYNAMIC_API was here, but broke the windows build. 
   191 // We need to check whether it belongs here or it's a bug.
   192 /* DYNAMIC_API */ void free_sync_msg(sync_msg_t *sync_msg)
   193 {
   194     assert(sync_msg);
   195     if (!(sync_msg))
   196         return;
   197 
   198     if(sync_msg->is_a_message){
   199         DeviceGroup_Protocol_t *msg = sync_msg->u.message;
   200         assert(msg);
   201         if (!(msg))
   202             return;
   203 
   204         ASN_STRUCT_FREE(asn_DEF_DeviceGroup_Protocol, msg);
   205     }
   206     else{
   207         Identity partner = NULL;
   208         partner = sync_msg->u.event.partner;
   209         if(partner != NULL)
   210             free_identity(partner);
   211     }
   212 
   213     free(sync_msg);
   214 
   215     return;
   216 }
   217 
   218 // from sync.c
   219 int call_inject_sync_msg(PEP_SESSION session, void *msg);
   220 
   221 PEP_STATUS inject_DeviceState_event(
   222     PEP_SESSION session, 
   223     DeviceState_event event,
   224     Identity partner,
   225     void *extra)
   226 {
   227     PEP_STATUS status;
   228 
   229     assert(session);
   230     if (!(session))
   231         return PEP_ILLEGAL_VALUE;
   232 
   233     sync_msg_t *sync_msg = malloc(sizeof(sync_msg_t));
   234     if(sync_msg == NULL)
   235         return PEP_OUT_OF_MEMORY;
   236 
   237     sync_msg->is_a_message = false;
   238     sync_msg->u.event.partner = partner;
   239     sync_msg->u.event.extra = extra;
   240     sync_msg->u.event.event = event;
   241 
   242     status = call_inject_sync_msg(session, sync_msg);
   243     if (status == PEP_SYNC_NO_INJECT_CALLBACK){
   244         free(sync_msg);
   245     }
   246 
   247     return status;
   248 }
   249 
   250 PEP_STATUS receive_DeviceState_msg(
   251     PEP_SESSION session, 
   252     message *src, 
   253     PEP_rating rating, 
   254     stringlist_t *keylist)
   255 {
   256     assert(session && src);
   257     if (!(session && src))
   258         return PEP_ILLEGAL_VALUE;
   259 
   260     bool found = false;
   261     bool expired = false;
   262     bool discarded = false;
   263     bool force_keep_msg = false;
   264     
   265     bloblist_t *last = NULL;
   266     for (bloblist_t *bl = src->attachments; bl && bl->value; bl = bl->next) {
   267         if (bl->mime_type && strcasecmp(bl->mime_type, "application/pEp.sync") == 0
   268                 && bl->size) {
   269             DeviceGroup_Protocol_t *msg = NULL;
   270             uper_decode_complete(NULL, &asn_DEF_DeviceGroup_Protocol, (void **)
   271                     &msg, bl->value, bl->size);
   272 
   273             if (msg) {
   274                 PEP_STATUS status = PEP_STATUS_OK;
   275 
   276                 char *user_id = strndup((char *) msg->header.me.user_id->buf,
   277                         msg->header.me.user_id->size);
   278                 assert(user_id);
   279                 if (!user_id) {
   280                     ASN_STRUCT_FREE(asn_DEF_DeviceGroup_Protocol, msg);
   281                     return PEP_OUT_OF_MEMORY;
   282                 }
   283 
   284                 // check message expiry 
   285                 if(src->recv) {
   286                     time_t expiry = timegm(src->recv) + SYNC_MSG_EXPIRE_TIME;
   287                     time_t now = time(NULL);
   288                     if(expiry != 0 && now != 0 && expiry < now){
   289                         expired = true;
   290                         goto free_all;
   291                     }
   292                 }
   293 
   294                 int32_t value = (int32_t) msg->header.sequence;
   295                 status = sequence_value(session, (char *) user_id,
   296                         &value);
   297 
   298                 if (status == PEP_STATUS_OK) {
   299                     switch (msg->payload.present) {
   300                         // HandshakeRequest needs encryption
   301                         case DeviceGroup_Protocol__payload_PR_handshakeRequest:
   302                             if (rating < PEP_rating_reliable ||
   303                                 strncmp(session->sync_uuid,
   304                                         (const char *)msg->payload.choice.handshakeRequest.partner.user_id->buf,
   305                                         msg->payload.choice.handshakeRequest.partner.user_id->size) != 0){
   306                                 discarded = true;
   307                                 goto free_all;
   308                             }
   309                             break;
   310                         // accepting GroupKeys needs encryption and trust of peer device
   311                         case DeviceGroup_Protocol__payload_PR_groupKeys:
   312                         {
   313                             if (!keylist || rating < PEP_rating_reliable ||
   314                                 // message is only consumed by instance it is addressed to
   315                                 (strncmp(session->sync_uuid,
   316                                         (const char *)msg->payload.choice.groupKeys.partner.user_id->buf,
   317                                         msg->payload.choice.groupKeys.partner.user_id->size) != 0)){
   318                                 discarded = true;
   319                                 goto free_all;
   320                             }
   321 
   322                             // check trust of identity using user_id given in payload
   323                             // to exacly match identity of device, the one trusted in
   324                             // case of accepted handshake
   325                             pEp_identity *_from = new_identity(NULL, 
   326                                                                keylist->value,
   327                                                                user_id,
   328                                                                NULL);
   329                             if (_from == NULL){
   330                                 status = PEP_OUT_OF_MEMORY;
   331                                 goto free_all;
   332                             }
   333                             status = get_trust(session, _from);
   334                             if (_from->comm_type < PEP_ct_strong_encryption) {
   335                                 free_identity(_from);
   336                                 discarded = true;
   337                                 goto free_all;
   338                             }
   339                             free_identity(_from);
   340                             break;
   341                         }
   342                         case DeviceGroup_Protocol__payload_PR_groupUpdate:
   343                         case DeviceGroup_Protocol__payload_PR_updateRequest:
   344                         {
   345                             // inject message but don't consume it, so 
   346                             // that other group members can also be updated
   347                             force_keep_msg = true;
   348                             
   349                             if (!keylist || rating < PEP_rating_reliable){
   350                                 discarded = true;
   351                                 goto free_all;
   352                             }
   353                             // GroupUpdate and UpdateRequests come from group.
   354                             // check trust relation in between signer key and 
   355                             // own id to be sure.
   356                             pEp_identity *_from = new_identity(NULL, 
   357                                                                keylist->value,
   358                                                                PEP_OWN_USERID,
   359                                                                NULL);
   360                             if (_from == NULL){
   361                                 status = PEP_OUT_OF_MEMORY;
   362                                 goto free_all;
   363                             }
   364                             status = get_trust(session, _from);
   365                             if (_from->comm_type < PEP_ct_pEp) {
   366                                 free_identity(_from);
   367                                 discarded = true;
   368                                 goto free_all;
   369                             }
   370                             free_identity(_from);
   371                         }
   372                         default:
   373                             break;
   374                     }
   375 
   376 
   377                     found = true;
   378                     sync_msg_t *sync_msg = malloc(sizeof(sync_msg_t));
   379                     if(sync_msg == NULL){
   380                         status = PEP_OUT_OF_MEMORY;
   381                         goto free_all;
   382                     }
   383                     sync_msg->is_a_message = true;
   384                     sync_msg->u.message = msg;
   385                     status = call_inject_sync_msg(session, sync_msg);
   386                     if (status != PEP_STATUS_OK){
   387                         if (status == PEP_SYNC_NO_INJECT_CALLBACK){
   388                             free(sync_msg);
   389                         }
   390                         goto free_all;
   391                     }
   392                     // don't message now that it is in the queue
   393                     goto free_userid;
   394                 }
   395                 else if (status == PEP_OWN_SEQUENCE) {
   396                     status = PEP_STATUS_OK;
   397                     discarded = true;
   398                     goto free_all;
   399                 }
   400 
   401             free_all:
   402                 ASN_STRUCT_FREE(asn_DEF_DeviceGroup_Protocol, msg);
   403             free_userid:
   404                 free(user_id);
   405 
   406                 if (status != PEP_STATUS_OK)
   407                     return status;
   408 
   409             }
   410 
   411             if (!session->keep_sync_msg) {
   412                 bloblist_t *blob = bl;
   413                 if (last)
   414                     last->next = bl->next;
   415                 else
   416                     src->attachments = bl->next;
   417 
   418                 blob->next = NULL;
   419                 free_bloblist(blob);
   420             }
   421             else {
   422                 last = bl;
   423             }
   424         }
   425         else {
   426             last = bl;
   427         }
   428     }
   429 
   430     if (force_keep_msg) {
   431         return PEP_MESSAGE_DISCARDED;
   432     }
   433 
   434     if ((expired || found) && !session->keep_sync_msg) {
   435         for (stringpair_list_t *spl = src->opt_fields ; spl && spl->value ;
   436                 spl = spl->next) {
   437             if (spl->value->key &&
   438                     strcasecmp(spl->value->key, "pEp-auto-consume") == 0) {
   439                 if (spl->value->value &&
   440                         strcasecmp(spl->value->value, "yes") == 0)
   441                     return PEP_MESSAGE_CONSUMED;
   442             }
   443         }
   444         return PEP_MESSAGE_DISCARDED;
   445     }
   446 
   447     if(discarded)
   448         return PEP_MESSAGE_DISCARDED;
   449 
   450     return PEP_STATUS_OK;
   451 }
   452 
   453 DeviceGroup_Protocol_t *new_DeviceGroup_Protocol_msg(DeviceGroup_Protocol__payload_PR type)
   454 {
   455     DeviceGroup_Protocol_t *msg = (DeviceGroup_Protocol_t *)
   456             calloc(1, sizeof(DeviceGroup_Protocol_t));
   457     assert(msg);
   458     if (!msg)
   459         return NULL;
   460     msg->payload.present = type;
   461     return msg;
   462 }
   463 
   464 void free_DeviceGroup_Protocol_msg(DeviceGroup_Protocol_t *msg)
   465 {
   466     ASN_STRUCT_FREE(asn_DEF_DeviceGroup_Protocol, msg);
   467 }
   468 
   469 PEP_STATUS unicast_msg(
   470         PEP_SESSION session,
   471         const Identity partner,
   472         DeviceState_state state,
   473         DeviceGroup_Protocol_t *msg,
   474         bool encrypted
   475     )
   476 {
   477     PEP_STATUS status = PEP_STATUS_OK;
   478     char *payload = NULL;
   479     message *_message = NULL;
   480     pEp_identity *me = NULL;
   481     pEp_identity *_me = NULL;
   482 
   483     assert(session && partner && state && msg);
   484     if (!(session && partner && state && msg))
   485         return PEP_ILLEGAL_VALUE;
   486 
   487     assert(session->messageToSend);
   488     if (!session->messageToSend) {
   489         status = PEP_SEND_FUNCTION_NOT_REGISTERED;
   490         goto error;
   491     }
   492 
   493     msg->header.version.major = SYNC_VERSION_MAJOR;
   494     msg->header.version.minor = SYNC_VERSION_MINOR;
   495 
   496     status = get_identity(session, partner->address, PEP_OWN_USERID, &me);
   497     if (status != PEP_STATUS_OK)
   498         goto error;
   499     
   500     int32_t seq = 0;
   501 
   502     status = sequence_value(session, session->sync_uuid, &seq);
   503     if (status != PEP_OWN_SEQUENCE && status != PEP_STATUS_OK)
   504         goto error;
   505 
   506     msg->header.sequence = (long) seq;
   507 
   508     _me = identity_dup(me);
   509     if (!_me)
   510         goto enomem;
   511 
   512     free(_me->user_id);
   513     _me->user_id = strndup(session->sync_uuid, 36);
   514     assert(_me->user_id);
   515     if (!_me->user_id)
   516         goto enomem;
   517 
   518     if (Identity_from_Struct(_me, &msg->header.me) == NULL)
   519         goto enomem;
   520 
   521     free_identity(_me);
   522     _me = NULL;
   523 
   524     msg->header.state = (long) state;
   525 
   526     bool devicegroup = storedGroupKeys(session);
   527     if (devicegroup)
   528         msg->header.devicegroup = 1;
   529     else
   530         msg->header.devicegroup = 0;
   531 
   532     if (asn_check_constraints(&asn_DEF_DeviceGroup_Protocol, msg, NULL, NULL)) {
   533         status = PEP_CONTRAINTS_VIOLATED;
   534         goto error;
   535     }
   536 
   537     ssize_t size = uper_encode_to_new_buffer(&asn_DEF_DeviceGroup_Protocol,
   538             NULL, msg, (void **) &payload);
   539     if (size == -1) {
   540         status = PEP_CANNOT_ENCODE;
   541         goto error;
   542     }
   543 
   544     status = prepare_message(me, partner, payload, size, &_message);
   545     if (status != PEP_STATUS_OK)
   546         goto error;
   547     payload = NULL;
   548     free_identity(me);
   549     me = NULL;
   550 
   551     if (encrypted) {
   552         if (msg->payload.present == DeviceGroup_Protocol__payload_PR_groupKeys || 
   553             msg->payload.present == DeviceGroup_Protocol__payload_PR_groupUpdate) {
   554             PEP_rating rating = PEP_rating_undefined;
   555             status = outgoing_message_rating(session, _message, &rating);
   556             if (status != PEP_STATUS_OK)
   557                 goto error;
   558             if (rating < PEP_rating_trusted) {
   559                 status = PEP_SYNC_NO_TRUST;
   560                 goto error;
   561             }
   562             
   563             IdentityList_t *list = 
   564                 msg->payload.present == 
   565                   DeviceGroup_Protocol__payload_PR_groupKeys ?
   566                   &msg->payload.choice.groupKeys.ownIdentities :
   567                   &msg->payload.choice.groupUpdate.ownIdentities;
   568 
   569             for (int i=0; i<list->list.count; i++) {
   570                 Identity_t *ident = list->list.array[i];
   571                 char *fpr = strndup((const char *)ident->fpr.buf, ident->fpr.size);
   572                 assert(fpr);
   573                 if (!fpr)
   574                     goto enomem;
   575                 static char filename[MAX_LINELENGTH];
   576                 int result = snprintf(filename, MAX_LINELENGTH, "%s-sec.asc", fpr);
   577                 if (result < 0)
   578                     goto enomem;
   579                 char *key = NULL;
   580                 size_t size = 0;
   581                 status = export_secrect_key(session, fpr, &key, &size);
   582                 free(fpr);
   583                 if (status != PEP_STATUS_OK)
   584                     goto error;
   585                 bloblist_t *bl = bloblist_add(_message->attachments,
   586                         (char *) key, size, "application/pgp-keys", filename);
   587                 if (!bl)
   588                     goto enomem;
   589                 if (!_message->attachments)
   590                     _message->attachments = bl;
   591             }
   592         }
   593 
   594         message *_encrypted = NULL;
   595         status = encrypt_message(session, _message, NULL, &_encrypted, PEP_enc_PEP, 0);
   596         if (status != PEP_STATUS_OK)
   597             goto error;
   598         free_message(_message);
   599         _message = _encrypted;
   600     }
   601     else {
   602         attach_own_key(session, _message);
   603     }
   604 
   605     status = session->messageToSend(session->sync_obj, _message);
   606     return status;
   607 
   608 enomem:
   609     status = PEP_OUT_OF_MEMORY;
   610 error:
   611     free_identity(_me);
   612     free(payload);
   613     free_message(_message);
   614     free_identity(me);
   615     return status;
   616 }
   617 
   618 PEP_STATUS multicast_self_msg(
   619         PEP_SESSION session,
   620         DeviceState_state state,
   621         DeviceGroup_Protocol_t *msg,
   622         bool encrypted
   623     )
   624 {
   625     PEP_STATUS status = PEP_STATUS_OK;
   626 
   627     assert(session && state && msg);
   628     if (!(session && state && msg))
   629         return PEP_ILLEGAL_VALUE;
   630 
   631     identity_list *own_identities = NULL;
   632     status = own_identities_retrieve(session, &own_identities);
   633     if (status != PEP_STATUS_OK)
   634         return status;
   635 
   636     // FIXME: exclude previously rejected identities
   637     for (identity_list *_i = own_identities; _i && _i->ident; _i = _i->next) {
   638         pEp_identity *me = _i->ident;
   639 
   640         // FIXME: no deep copy for multicast supported yet
   641         // DeviceGroup_Protocol_t *_msg = malloc(sizeof(DeviceGroup_Protocol_t));
   642         // assert(_msg);
   643         // if (_msg == NULL){
   644         //     status = PEP_OUT_OF_MEMORY;
   645         //     goto error;
   646         // }
   647         // memcpy(_msg, msg, sizeof(DeviceGroup_Protocol_t));
   648         status = unicast_msg(session, me, state, msg, encrypted);
   649         //status = unicast_msg(session, me, state, _msg, encrypted);
   650         //free_DeviceGroup_Protocol_msg(_msg);
   651     }
   652 
   653     free_identity_list(own_identities);
   654     return PEP_STATUS_OK;
   655 
   656 // error:
   657 //     free_identity_list(own_identities);
   658 //     return status;
   659 }
   660