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