src/sync_impl.c
author Krista Bennett <krista@pep-project.org>
Fri, 01 Jun 2018 10:30:19 +0200
branchENGINE-254
changeset 2723 7194b9c8599d
parent 1873 2da96adc534e
child 2287 026ab4dae779
permissions -rw-r--r--
close branch
     1 // This file is under GNU General Public License 3.0
     2 // see LICENSE.txt
     3 
     4 #include "platform.h"
     5 
     6 // it seems pEp_internal.h needs to be the first pEp include due to the 
     7 // #define for the dllimport / dllexport DYNAMIC_API stuff.
     8 #include "pEp_internal.h"
     9 
    10 #include "sync_impl.h"
    11 #include "keymanagement.h"
    12 #include "message_api.h"
    13 #include "map_asn1.h"
    14 #include "baseprotocol.h"
    15 
    16 #define SYNC_VERSION_MAJOR 1
    17 #define SYNC_VERSION_MINOR 0
    18 
    19 #define SYNC_INHIBIT_TIME (60*10)
    20 #define SYNC_MSG_EXPIRE_TIME (60 * 10)
    21 
    22 struct _sync_msg_t {
    23     bool is_a_message;
    24     union {
    25         DeviceGroup_Protocol_t *message;
    26         struct {
    27             DeviceState_event event;
    28             Identity partner;
    29             void *extra;
    30         } event;
    31     } u;
    32 };
    33 
    34 static bool _is_own_uuid( PEP_SESSION session, UTF8String_t *uuid)
    35 {
    36     return strncmp(session->sync_session->sync_uuid,
    37                    (const char*)uuid->buf, uuid->size) == 0;
    38 }
    39 
    40 static bool _is_own_group_uuid( PEP_SESSION session, UTF8String_t *uuid, char** our_group)
    41 {
    42     PEP_STATUS status = PEP_STATUS_OK;
    43     char *devgrp = NULL;
    44 
    45     if(our_group == NULL || *our_group == NULL)
    46         status = get_device_group(session, &devgrp);
    47     else
    48         devgrp = *our_group;
    49 
    50     bool res = (status == PEP_STATUS_OK && devgrp && devgrp[0] &&
    51         strncmp(devgrp,(const char*)uuid->buf, uuid->size) == 0);
    52 
    53     if(our_group == NULL)
    54         free(devgrp);
    55     else if(*our_group == NULL)
    56         *our_group = devgrp;
    57 
    58     return res;
    59 }
    60 
    61 PEP_STATUS receive_sync_msg(
    62         PEP_SESSION session,
    63         sync_msg_t *sync_msg,
    64         time_t *timeout
    65     )
    66 {
    67     PEP_STATUS status;
    68     void *extra = NULL;
    69     Identity partner = NULL;
    70     DeviceState_event event = DeviceState_event_NONE;
    71     assert(session && sync_msg);
    72     if (!(session && sync_msg))
    73         return PEP_ILLEGAL_VALUE;
    74 
    75     bool msgIsFromGroup = false;
    76     if(sync_msg->is_a_message){
    77         DeviceGroup_Protocol_t *msg = sync_msg->u.message;
    78         assert(msg && msg->payload.present != DeviceGroup_Protocol__payload_PR_NOTHING);
    79         if (!(msg && msg->payload.present != DeviceGroup_Protocol__payload_PR_NOTHING)){
    80             status = PEP_OUT_OF_MEMORY;
    81             goto error;
    82         }
    83 
    84         partner = Identity_to_Struct(&msg->header.me, NULL);
    85         if (!partner){
    86             status = PEP_OUT_OF_MEMORY;
    87             ASN_STRUCT_FREE(asn_DEF_DeviceGroup_Protocol, msg);
    88             goto error;
    89         }
    90 
    91         msgIsFromGroup = msg->header.devicegroup;
    92 
    93         switch (msg->payload.present) {
    94             case DeviceGroup_Protocol__payload_PR_beacon:
    95                 event = Beacon;
    96                 break;
    97 
    98             case DeviceGroup_Protocol__payload_PR_handshakeRequest:
    99             {
   100                 // re-check uuid in case sync_uuid or group changed while in the queue
   101                 char *own_group_id = NULL;
   102                 bool is_for_me = _is_own_uuid(session, 
   103                     msg->payload.choice.handshakeRequest.partner_id);
   104                 bool is_for_group = !is_for_me && _is_own_group_uuid(session, 
   105                     msg->payload.choice.handshakeRequest.partner_id, &own_group_id);
   106                 if (!(is_for_me || is_for_group)){
   107                     status = PEP_MESSAGE_IGNORE;
   108                     goto error;
   109                 }
   110 
   111                 UTF8String_t *guuid = msg->payload.choice.handshakeRequest.group_id;
   112                 if(msgIsFromGroup && guuid && guuid->buf && guuid->size) {
   113                     bool is_from_own_group = _is_own_group_uuid(session, 
   114                                                                 guuid, &own_group_id);
   115 
   116                     // Filter handshake requests from own group
   117                     if(is_from_own_group) {
   118                         status = PEP_MESSAGE_IGNORE;
   119                         goto error;
   120                     }
   121 
   122                     // if it comes from another group, 
   123                     // we want to communicate with that group
   124                     // insert group_id given in handshake request 
   125                     // into partner's id
   126 
   127                     free(partner->user_id);
   128                     partner->user_id = strndup((const char*)guuid->buf, guuid->size);
   129                     if(partner->user_id == NULL){
   130                         status = PEP_OUT_OF_MEMORY;
   131                         goto error;
   132                     }
   133 
   134                     // if it comes from another group, and we are grouped,
   135                     // then this is groupmerge
   136                 }
   137 
   138                 event = HandshakeRequest;
   139                 break;
   140 
   141             }
   142             case DeviceGroup_Protocol__payload_PR_updateRequest:
   143                 event = UpdateRequest;
   144                 break;
   145 
   146             case DeviceGroup_Protocol__payload_PR_groupKeys:
   147             {
   148                 // re-check uuid in case sync_uuid or group_uuid changed while in the queue
   149                 char *own_group_id = NULL;
   150                 UTF8String_t *puuid = msg->payload.choice.groupKeys.partner_id;
   151                 bool is_for_me = _is_own_uuid(session, puuid);
   152                 bool is_for_group = !is_for_me &&
   153                                     _is_own_group_uuid(session, 
   154                                         puuid, &own_group_id);
   155                 if (!(is_for_me || is_for_group)){
   156                     status = PEP_MESSAGE_IGNORE;
   157                     goto error;
   158                 }
   159 
   160                 UTF8String_t *guuid = msg->payload.choice.groupKeys.group_id;
   161 
   162                 // GroupKeys come from groups, no choice
   163                 if(!(msgIsFromGroup && guuid && guuid->buf && guuid->size)) {
   164                     status = PEP_SYNC_ILLEGAL_MESSAGE;
   165                     goto error;
   166                 }
   167 
   168                 // Filter groupKeys from own group
   169                 bool is_from_own_group = _is_own_group_uuid(session, 
   170                                                             guuid, 
   171                                                             &own_group_id);
   172                 if(is_from_own_group) {
   173                     // FixMe : protocol shouldn't allow this
   174                     status = PEP_SYNC_ILLEGAL_MESSAGE;
   175                     goto error;
   176                 }
   177 
   178                 // insert group_id given in groupKeys into partner's id
   179                 free(partner->user_id);
   180                 partner->user_id = strndup((const char*)guuid->buf, guuid->size);
   181                 if(partner->user_id == NULL){
   182                     status = PEP_OUT_OF_MEMORY;
   183                     goto error;
   184                 }
   185 
   186                 // if it comes from another group, and we are grouped,
   187                 // then this is groupmerge's groupKeys
   188 
   189                 group_keys_extra_t *group_keys_extra;
   190                 group_keys_extra = malloc(sizeof(group_keys_extra_t));
   191                 if(group_keys_extra == NULL){
   192                     status = PEP_OUT_OF_MEMORY;
   193                     ASN_STRUCT_FREE(asn_DEF_DeviceGroup_Protocol, msg);
   194                     goto error;
   195                 }
   196 
   197                 char *group_id = strndup((char*)guuid->buf, guuid->size);
   198 
   199                 if (!group_id){
   200                     status = PEP_OUT_OF_MEMORY;
   201                     free(group_keys_extra);
   202                     ASN_STRUCT_FREE(asn_DEF_DeviceGroup_Protocol, msg);
   203                     goto error;
   204                 }
   205                 group_keys_extra->group_id = group_id;
   206 
   207                 identity_list *group_keys = IdentityList_to_identity_list(
   208                         &msg->payload.choice.groupKeys.ownIdentities,
   209                         NULL);
   210                 if (!group_keys) {
   211                     status = PEP_OUT_OF_MEMORY;
   212                     free(group_id);
   213                     free(group_keys_extra);
   214                     ASN_STRUCT_FREE(asn_DEF_DeviceGroup_Protocol, msg);
   215                     goto error;
   216                 }
   217                 group_keys_extra->group_keys = group_keys;
   218 
   219                 extra = (void *) group_keys_extra;
   220                 event = GroupKeys;
   221 
   222                 break;
   223             }
   224             case DeviceGroup_Protocol__payload_PR_groupUpdate:
   225             {
   226                 identity_list *group_keys = IdentityList_to_identity_list(
   227                         &msg->payload.choice.groupUpdate.ownIdentities, NULL);
   228                 if (!group_keys) {
   229                     status = PEP_OUT_OF_MEMORY;
   230                     ASN_STRUCT_FREE(asn_DEF_DeviceGroup_Protocol, msg);
   231                     goto error;
   232                 }
   233                 extra = (void *) group_keys;
   234                 event = GroupUpdate;
   235                 break;
   236             }
   237 
   238             default:
   239                 status = PEP_SYNC_ILLEGAL_MESSAGE;
   240                 ASN_STRUCT_FREE(asn_DEF_DeviceGroup_Protocol, msg);
   241                 goto error;
   242         }
   243 
   244     }
   245     else{
   246         partner = sync_msg->u.event.partner;
   247         extra = sync_msg->u.event.extra;
   248         event = sync_msg->u.event.event;
   249     }
   250 
   251     // Event inhibition, to limit mailbox and prevent cycles
   252     time_t *last = NULL;
   253     switch(event){
   254         case CannotDecrypt:
   255             last = &session->LastCannotDecrypt;
   256             break;
   257 
   258         case UpdateRequest:
   259             last = &session->LastUpdateRequest;
   260             break;
   261 
   262         default:
   263             break;
   264     }
   265 
   266     if(last != NULL){
   267         time_t now = time(NULL);
   268         if(*last != 0 && (*last + SYNC_INHIBIT_TIME) > now ){
   269             status = PEP_STATEMACHINE_INHIBITED_EVENT;
   270             goto error;
   271         }
   272         *last = now;
   273     }
   274 
   275     // partner identity must be explicitely added DB to later
   276     // be able to communicate securely with it.
   277     if(partner){
   278         // protect virtual user IDs 
   279         if((strncmp("TOFU_", partner->user_id, 6) == 0 &&
   280            strlen(partner->user_id) == strlen(partner->address) + 6 &&
   281            strcmp(partner->user_id + 6, partner->address)) ||
   282         // protect own ID 
   283            (strcmp(PEP_OWN_USERID, partner->user_id) == 0)){
   284             status = PEP_SYNC_ILLEGAL_MESSAGE;
   285             goto error;
   286         }
   287 
   288         // partner IDs are UUIDs bound to session lifespan
   289         // and therefore partner identities are not supposed
   290         // to mutate over time, but just not be used anymore.
   291         // It should then be safe to accept given identity if not 
   292         // already pre-existing
   293         pEp_identity *stored_identity = NULL;
   294         status = get_identity(session,
   295                               partner->address,
   296                               partner->user_id,
   297                               &stored_identity);
   298 
   299         if (!stored_identity) {
   300             // make a safe copy of partner, with no flags or comm_type
   301             pEp_identity *tmpident = new_identity(partner->address,
   302                                                   partner->fpr,
   303                                                   partner->user_id,
   304                                                   partner->username);
   305             if (tmpident == NULL){
   306                 status = PEP_OUT_OF_MEMORY;
   307                 goto error;
   308             }
   309 
   310             // finaly add partner to DB
   311             status = set_identity(session, tmpident);
   312             assert(status == PEP_STATUS_OK);
   313             if(status == PEP_STATUS_OK && msgIsFromGroup)
   314                 status = set_identity_flags(session, tmpident, PEP_idf_devicegroup);
   315             free_identity(tmpident);
   316             assert(status == PEP_STATUS_OK);
   317             if (status != PEP_STATUS_OK) {
   318                 goto error;
   319             }
   320         }
   321         else if (status == PEP_STATUS_OK) {
   322             free_identity(stored_identity);
   323         } 
   324         else
   325             goto error;
   326     }
   327 
   328     status = fsm_DeviceState_inject(session, event, partner, extra, timeout);
   329 
   330 error:
   331 
   332     free_identity(partner);
   333 
   334     switch(event){
   335         case GroupKeys:
   336         {
   337             free_group_keys_extra((group_keys_extra_t*)extra);
   338             break;
   339         }
   340         case GroupUpdate:
   341         {
   342             identity_list *group_keys = (identity_list*) extra;
   343             free_identity_list(group_keys);
   344             break;
   345         }
   346         default:
   347             assert(extra==NULL);
   348             break;
   349     }
   350 
   351     free(sync_msg);
   352 
   353     return status;
   354 }
   355 
   356 // TODO: DYNAMIC_API was here, but broke the windows build. 
   357 // We need to check whether it belongs here or it's a bug.
   358 /* DYNAMIC_API */ void free_sync_msg(sync_msg_t *sync_msg)
   359 {
   360     if (!sync_msg)
   361         return;
   362 
   363     if(sync_msg->is_a_message){
   364         DeviceGroup_Protocol_t *msg = sync_msg->u.message;
   365         assert(msg);
   366         if (!(msg))
   367             return;
   368 
   369         ASN_STRUCT_FREE(asn_DEF_DeviceGroup_Protocol, msg);
   370     }
   371     else{
   372         Identity partner = NULL;
   373         partner = sync_msg->u.event.partner;
   374         if(partner != NULL)
   375             free_identity(partner);
   376     }
   377 
   378     free(sync_msg);
   379 
   380     return;
   381 }
   382 
   383 // from sync.c
   384 int call_inject_sync_msg(PEP_SESSION session, void *msg);
   385 
   386 PEP_STATUS inject_DeviceState_event(
   387     PEP_SESSION session, 
   388     DeviceState_event event,
   389     Identity partner,
   390     void *extra)
   391 {
   392     PEP_STATUS status;
   393 
   394     assert(session);
   395     if (!(session))
   396         return PEP_ILLEGAL_VALUE;
   397 
   398     sync_msg_t *sync_msg = malloc(sizeof(sync_msg_t));
   399     if(sync_msg == NULL)
   400         return PEP_OUT_OF_MEMORY;
   401 
   402     sync_msg->is_a_message = false;
   403     sync_msg->u.event.partner = partner;
   404     sync_msg->u.event.extra = extra;
   405     sync_msg->u.event.event = event;
   406 
   407     status = call_inject_sync_msg(session, sync_msg);
   408     if (status == PEP_SYNC_NO_INJECT_CALLBACK){
   409         free(sync_msg);
   410     }
   411 
   412     return status;
   413 }
   414 
   415 PEP_STATUS receive_DeviceState_msg(
   416     PEP_SESSION session, 
   417     message *src, 
   418     PEP_rating rating, 
   419     stringlist_t *keylist)
   420 {
   421     assert(session && src);
   422     if (!(session && src))
   423         return PEP_ILLEGAL_VALUE;
   424 
   425     bool consume = false;
   426     bool discard = false;
   427     bool force_keep_msg = false;
   428 
   429     for (bloblist_t *bl = src->attachments; bl && bl->value; bl = bl->next) {
   430         if (bl->mime_type && strcasecmp(bl->mime_type, "application/pEp.sync") == 0
   431                 && bl->size) {
   432             DeviceGroup_Protocol_t *msg = NULL;
   433             uper_decode_complete(NULL, &asn_DEF_DeviceGroup_Protocol, (void **)
   434                     &msg, bl->value, bl->size);
   435 
   436             if (msg) {
   437                 PEP_STATUS status = PEP_STATUS_OK;
   438 
   439                 char *user_id = strndup((char *) msg->header.me.user_id->buf,
   440                         msg->header.me.user_id->size);
   441                 assert(user_id);
   442                 if (!user_id) {
   443                     ASN_STRUCT_FREE(asn_DEF_DeviceGroup_Protocol, msg);
   444                     return PEP_OUT_OF_MEMORY;
   445                 }
   446 
   447                 // detect and mitigate address spoofing
   448                 Identity check_me = NULL;
   449                 char* null_terminated_address = 
   450                     strndup((char *) msg->header.me.address->buf,
   451                             msg->header.me.address->size);
   452 
   453                 if(null_terminated_address){
   454                     status = get_identity(session, 
   455                                           null_terminated_address, 
   456                                           PEP_OWN_USERID, 
   457                                           &check_me);
   458                     free(null_terminated_address);
   459                 } 
   460                 else
   461                     status = PEP_OUT_OF_MEMORY;
   462 
   463                 if (status == PEP_OUT_OF_MEMORY)
   464                     goto free_all;
   465 
   466                 free_identity(check_me);
   467 
   468                 bool not_own_address = status != PEP_STATUS_OK;
   469                 status = PEP_STATUS_OK;
   470 
   471                 if (not_own_address || 
   472                     strncmp(src->from->address,
   473                             (char *) msg->header.me.address->buf,
   474                             msg->header.me.address->size) != 0 ||
   475                     strncmp(src->to->ident->address,
   476                             (char *) msg->header.me.address->buf,
   477                             msg->header.me.address->size) != 0) {
   478                     consume = true;
   479                     goto free_all;
   480                 }
   481 
   482                 // if encrypted, ensure that header.me.fpr match signer's fpr
   483                 if (rating >= PEP_rating_reliable && (
   484                         !keylist ||
   485                         !_same_fpr((char *) msg->header.me.fpr.buf,
   486                                    msg->header.me.fpr.size,
   487                                    keylist->value,
   488                                    strlen(keylist->value)))) {
   489                     consume = true;
   490                     goto free_all;
   491                 }
   492 
   493                 // check message expiry 
   494                 if(src->recv) {
   495                     time_t expiry = timegm(src->recv) + SYNC_MSG_EXPIRE_TIME;
   496                     time_t now = time(NULL);
   497                     if(expiry != 0 && now != 0 && expiry < now){
   498                         consume = true;
   499                         goto free_all;
   500                     }
   501                 }
   502 
   503                 int32_t value = (int32_t) msg->header.sequence;
   504                 if (value < 1) {
   505                     status = PEP_SEQUENCE_VIOLATED;
   506                 } else {
   507                     status = sequence_value(session, (char *) user_id,
   508                             &value);
   509                 }
   510 
   511                 if (status == PEP_STATUS_OK) {
   512                     switch (msg->payload.present) {
   513                         // HandshakeRequest needs encryption
   514                         case DeviceGroup_Protocol__payload_PR_handshakeRequest:
   515                         {
   516                             UTF8String_t *puuid = 
   517                               msg->payload.choice.handshakeRequest.partner_id;
   518                             bool is_for_me = _is_own_uuid(session, puuid);
   519                             bool is_for_group = !is_for_me && 
   520                                                 _is_own_group_uuid(
   521                                                     session, puuid, NULL);
   522 
   523                             // Reject handshake requests not addressed to us
   524                             if (rating < PEP_rating_reliable ||
   525                                 !(is_for_me || is_for_group)){
   526                                 discard = true;
   527                                 goto free_all;
   528                             }
   529 
   530                             // do not consume handshake request for group
   531                             if(is_for_group){ 
   532                                 force_keep_msg = true;
   533                             }
   534                             break;
   535                         }
   536                         // accepting GroupKeys needs encryption and trust of peer device
   537                         case DeviceGroup_Protocol__payload_PR_groupKeys:
   538                         {
   539                             UTF8String_t *puuid = msg->payload.choice.groupKeys.partner_id;
   540                             bool is_for_me = _is_own_uuid(session, puuid);
   541                             bool is_for_group = !is_for_me &&
   542                                                 _is_own_group_uuid(session, 
   543                                                     puuid, NULL);
   544                             if (!keylist || rating < PEP_rating_reliable ||
   545                                 // message is only consumed by instance it is addressed to
   546                                 !(is_for_me || is_for_group)){
   547                                 discard = true;
   548                                 goto free_all;
   549                             }
   550 
   551                             // do not consume groupKeys for group
   552                             if(is_for_group){ 
   553                                 // This happens in case case of groupmerge
   554                                 force_keep_msg = true;
   555                             }
   556 
   557                             // Trust check disabled here but it still it should be safe.
   558                             // SameIdentity checks in state machine ensures that we only
   559                             // store groupkeys signed by device or group that have been 
   560                             // previously accepted in handshake.
   561                             //
   562                             // // check trust of identity using user_id given in msg.header.me
   563                             // // to exacly match identity of device, the one trusted in
   564                             // // case of accepted handshake from a sole device
   565                             // pEp_identity *_from = new_identity(NULL, 
   566                             //                                    keylist->value,
   567                             //                                    user_id,
   568                             //                                    NULL);
   569                             // if (_from == NULL){
   570                             //     status = PEP_OUT_OF_MEMORY;
   571                             //     goto free_all;
   572                             // }
   573                             // status = get_trust(session, _from);
   574                             // if (status != PEP_STATUS_OK || _from->comm_type < PEP_ct_strong_encryption) {
   575 
   576                             //     // re-try with group_id instead, in case of handshake with pre-existing group
   577                             //     UTF8String_t *guuid = msg->payload.choice.groupKeys.group_id;
   578                             //     free(_from->user_id);
   579                             //     if ((_from->user_id = strndup((const char*)guuid->buf, guuid->size)) == NULL){
   580                             //         free_identity(_from);
   581                             //         status = PEP_OUT_OF_MEMORY;
   582                             //         goto free_all;
   583                             //     }
   584                             //     _from->comm_type = PEP_ct_unknown;
   585 
   586                             //     status = get_trust(session, _from);
   587                             //     if (status != PEP_STATUS_OK || _from->comm_type < PEP_ct_strong_encryption) {
   588                             //         status = PEP_STATUS_OK;
   589                             //         free_identity(_from);
   590                             //         discard = true;
   591                             //         goto free_all;
   592                             //     }
   593                             // }
   594                             // free_identity(_from);
   595                             break;
   596                         }
   597                         case DeviceGroup_Protocol__payload_PR_groupUpdate:
   598                         case DeviceGroup_Protocol__payload_PR_updateRequest:
   599                         {
   600                             // inject message but don't consume it, so 
   601                             // that other group members can also be updated
   602                             force_keep_msg = true;
   603                             
   604                             if (!keylist || rating < PEP_rating_reliable){
   605                                 discard = true;
   606                                 goto free_all;
   607                             }
   608                             // GroupUpdate and UpdateRequests come from group.
   609                             // check trust relation in between signer key and 
   610                             // own id to be sure.
   611                             pEp_identity *_from = new_identity(NULL, 
   612                                                                keylist->value,
   613                                                                PEP_OWN_USERID,
   614                                                                NULL);
   615                             if (_from == NULL){
   616                                 status = PEP_OUT_OF_MEMORY;
   617                                 goto free_all;
   618                             }
   619                             status = get_trust(session, _from);
   620                             if (status != PEP_STATUS_OK || _from->comm_type < PEP_ct_pEp) {
   621                                 status = PEP_STATUS_OK;
   622                                 free_identity(_from);
   623                                 discard = true;
   624                                 goto free_all;
   625                             }
   626                             free_identity(_from);
   627                         }
   628                         default:
   629                             break;
   630                     }
   631 
   632 
   633                     consume = true;
   634                     sync_msg_t *sync_msg = malloc(sizeof(sync_msg_t));
   635                     if(sync_msg == NULL){
   636                         status = PEP_OUT_OF_MEMORY;
   637                         goto free_all;
   638                     }
   639                     sync_msg->is_a_message = true;
   640                     sync_msg->u.message = msg;
   641                     status = call_inject_sync_msg(session, sync_msg);
   642                     if (status != PEP_STATUS_OK){
   643                         if (status == PEP_SYNC_NO_INJECT_CALLBACK){
   644                             free(sync_msg);
   645                         }
   646                         goto free_all;
   647                     }
   648                     // don't free message now that it is in the queue
   649                     goto free_userid;
   650                 }
   651                 else if (status == PEP_OWN_SEQUENCE || status == PEP_SEQUENCE_VIOLATED) {
   652                     status = PEP_STATUS_OK;
   653                     discard = true;
   654                     goto free_all;
   655                 }
   656 
   657             free_all:
   658                 ASN_STRUCT_FREE(asn_DEF_DeviceGroup_Protocol, msg);
   659             free_userid:
   660                 free(user_id);
   661 
   662                 if (status != PEP_STATUS_OK)
   663                     return status;
   664             }
   665         }
   666     }
   667 
   668     if (force_keep_msg) {
   669         return PEP_MESSAGE_IGNORE;
   670     }
   671 
   672     if (consume && !session->keep_sync_msg) {
   673         for (stringpair_list_t *spl = src->opt_fields ; spl && spl->value ;
   674                 spl = spl->next) {
   675             if (spl->value->key &&
   676                     strcasecmp(spl->value->key, "pEp-auto-consume") == 0) {
   677                 if (spl->value->value &&
   678                         strcasecmp(spl->value->value, "yes") == 0)
   679                     return PEP_MESSAGE_CONSUME;
   680             }
   681         }
   682         return PEP_MESSAGE_IGNORE;
   683     }
   684 
   685     if(discard)
   686         return PEP_MESSAGE_IGNORE;
   687 
   688     if (!session->keep_sync_msg) {
   689         bloblist_t *last = NULL;
   690         for (bloblist_t *bl = src->attachments; bl && bl->value; ) {
   691             if (bl->mime_type && strcasecmp(bl->mime_type, "application/pEp.sync") == 0) {
   692                 bloblist_t *b = bl;
   693                 bl = bl->next;
   694                 if (!last)
   695                     src->attachments = bl;
   696                 else
   697                     last->next = bl;
   698                 free(b->mime_type);
   699                 free(b->filename);
   700                 free(b->value);
   701                 free(b);
   702             }
   703             else {
   704                 last = bl;
   705                 bl = bl->next;
   706             }
   707         }
   708     }
   709 
   710     return PEP_STATUS_OK;
   711 }
   712 
   713 DeviceGroup_Protocol_t *new_DeviceGroup_Protocol_msg(DeviceGroup_Protocol__payload_PR type)
   714 {
   715     DeviceGroup_Protocol_t *msg = (DeviceGroup_Protocol_t *)
   716             calloc(1, sizeof(DeviceGroup_Protocol_t));
   717     assert(msg);
   718     if (!msg)
   719         return NULL;
   720     msg->payload.present = type;
   721     return msg;
   722 }
   723 
   724 void free_DeviceGroup_Protocol_msg(DeviceGroup_Protocol_t *msg)
   725 {
   726     ASN_STRUCT_FREE(asn_DEF_DeviceGroup_Protocol, msg);
   727 }
   728 
   729 
   730 #ifndef NDEBUG
   731 static int _append(const void *buffer, size_t size, void *appkey)
   732 {
   733     char **dest_ptr = (char **)appkey;
   734     size_t osize = strlen(*dest_ptr);
   735     size_t nsize = size + osize;
   736     *dest_ptr = realloc(*dest_ptr, nsize + 1);
   737     if(*dest_ptr == NULL) return -1;
   738     memcpy(*dest_ptr + osize, buffer, size);
   739     (*dest_ptr)[nsize] = '\0';
   740     return 0;
   741 }
   742 #endif
   743 
   744 PEP_STATUS unicast_msg(
   745         PEP_SESSION session,
   746         const Identity partner,
   747         DeviceState_state state,
   748         DeviceGroup_Protocol_t *msg,
   749         bool encrypted
   750     )
   751 {
   752     PEP_STATUS status = PEP_STATUS_OK;
   753     char *payload = NULL;
   754     message *_message = NULL;
   755     pEp_identity *me = NULL;
   756     pEp_identity *_me = NULL;
   757 
   758     assert(session && partner && state && msg);
   759     if (!(session && partner && state && msg))
   760         return PEP_ILLEGAL_VALUE;
   761 
   762     assert(session->messageToSend);
   763     if (!session->messageToSend) {
   764         status = PEP_SEND_FUNCTION_NOT_REGISTERED;
   765         goto error;
   766     }
   767 
   768     msg->header.version.major = SYNC_VERSION_MAJOR;
   769     msg->header.version.minor = SYNC_VERSION_MINOR;
   770 
   771     status = get_identity(session, partner->address, PEP_OWN_USERID, &me);
   772     if (status != PEP_STATUS_OK)
   773         goto error;
   774     
   775     int32_t seq = 0;
   776 
   777     status = sequence_value(session, session->sync_session->sync_uuid, &seq);
   778     if (status != PEP_OWN_SEQUENCE && status != PEP_STATUS_OK)
   779         goto error;
   780 
   781     msg->header.sequence = (long) seq;
   782 
   783     _me = identity_dup(me);
   784     if (!_me)
   785         goto enomem;
   786 
   787     free(_me->user_id);
   788     _me->user_id = strndup(session->sync_session->sync_uuid, 36);
   789     assert(_me->user_id);
   790     if (!_me->user_id)
   791         goto enomem;
   792 
   793     if (Identity_from_Struct(_me, &msg->header.me) == NULL)
   794         goto enomem;
   795 
   796     free_identity(_me);
   797     _me = NULL;
   798 
   799     msg->header.state = (long) state;
   800 
   801     bool devicegroup = deviceGrouped(session);
   802     if (devicegroup)
   803         msg->header.devicegroup = 1;
   804     else
   805         msg->header.devicegroup = 0;
   806 
   807     if (asn_check_constraints(&asn_DEF_DeviceGroup_Protocol, msg, NULL, NULL)) {
   808         status = PEP_CONTRAINTS_VIOLATED;
   809         goto error;
   810     }
   811 
   812     ssize_t size = uper_encode_to_new_buffer(&asn_DEF_DeviceGroup_Protocol,
   813             NULL, msg, (void **) &payload);
   814     if (size == -1) {
   815         status = PEP_CANNOT_ENCODE;
   816         goto error;
   817     }
   818 
   819     status = prepare_message(me, partner, payload, size, &_message);
   820     if (status != PEP_STATUS_OK)
   821         goto error;
   822     payload = NULL;
   823     free_identity(me);
   824     me = NULL;
   825 
   826 #ifndef NDEBUG
   827     asn_enc_rval_t er;
   828     er = xer_encode(&asn_DEF_DeviceGroup_Protocol, msg, 
   829                     XER_F_BASIC, _append, &_message->longmsg);
   830     if(er.encoded == -1)
   831         goto error;
   832 #endif
   833 
   834     if (encrypted) {
   835         if (msg->payload.present == DeviceGroup_Protocol__payload_PR_groupKeys || 
   836             msg->payload.present == DeviceGroup_Protocol__payload_PR_groupUpdate) {
   837             PEP_rating rating = PEP_rating_undefined;
   838             status = outgoing_message_rating(session, _message, &rating);
   839             if (status != PEP_STATUS_OK)
   840                 goto error;
   841             if (rating < PEP_rating_trusted) {
   842                 status = PEP_SYNC_NO_TRUST;
   843                 goto error;
   844             }
   845             
   846             stringlist_t *keylist = NULL;
   847             status = _own_keys_retrieve(session, &keylist, PEP_idf_not_for_sync);
   848             if (status != PEP_STATUS_OK)
   849                 goto error;
   850 
   851             for (stringlist_t *_keylist=keylist; _keylist!=NULL; _keylist=_keylist->next) {
   852                 char *fpr = _keylist->value;
   853                 static char filename[MAX_LINELENGTH];
   854                 int result = snprintf(filename, MAX_LINELENGTH, "file://%s-sec.asc", fpr);
   855                 if (result < 0)
   856                     goto enomem;
   857                 char *key = NULL;
   858                 size_t size = 0;
   859                 status = export_secrect_key(session, fpr, &key, &size);
   860                 if (status != PEP_STATUS_OK)
   861                     goto error;
   862                 bloblist_t *bl = bloblist_add(_message->attachments,
   863                         (char *) key, size, "application/pgp-keys", filename);
   864                 if (!bl)
   865                     goto enomem;
   866                 if (!_message->attachments)
   867                     _message->attachments = bl;
   868             }
   869         }
   870 
   871         message *_encrypted = NULL;
   872         status = encrypt_message(session, _message, NULL, &_encrypted, PEP_enc_PEP, 0);
   873         if (status != PEP_STATUS_OK)
   874             goto error;
   875         free_message(_message);
   876         _message = _encrypted;
   877     }
   878     else {
   879         attach_own_key(session, _message);
   880     }
   881 
   882     status = session->messageToSend(session->sync_obj, _message);
   883     return status;
   884 
   885 enomem:
   886     status = PEP_OUT_OF_MEMORY;
   887 error:
   888     free_identity(_me);
   889     free(payload);
   890     free_message(_message);
   891     free_identity(me);
   892     return status;
   893 }
   894 
   895 PEP_STATUS multicast_self_msg(
   896         PEP_SESSION session,
   897         DeviceState_state state,
   898         DeviceGroup_Protocol_t *msg,
   899         bool encrypted
   900     )
   901 {
   902     PEP_STATUS status = PEP_STATUS_OK;
   903 
   904     assert(session && state && msg);
   905     if (!(session && state && msg))
   906         return PEP_ILLEGAL_VALUE;
   907 
   908     identity_list *own_identities = NULL;
   909     status = _own_identities_retrieve(session, &own_identities, PEP_idf_not_for_sync);
   910     if (status != PEP_STATUS_OK)
   911         return status;
   912 
   913     for (identity_list *_i = own_identities; _i && _i->ident; _i = _i->next) {
   914         pEp_identity *me = _i->ident;
   915 
   916         // FIXME: no deep copy for multicast supported yet
   917         // DeviceGroup_Protocol_t *_msg = malloc(sizeof(DeviceGroup_Protocol_t));
   918         // assert(_msg);
   919         // if (_msg == NULL){
   920         //     status = PEP_OUT_OF_MEMORY;
   921         //     goto error;
   922         // }
   923         // memcpy(_msg, msg, sizeof(DeviceGroup_Protocol_t));
   924         status = unicast_msg(session, me, state, msg, encrypted);
   925         //status = unicast_msg(session, me, state, _msg, encrypted);
   926         //free_DeviceGroup_Protocol_msg(_msg);
   927     }
   928 
   929     free_identity_list(own_identities);
   930     return PEP_STATUS_OK;
   931 
   932 // error:
   933 //     free_identity_list(own_identities);
   934 //     return status;
   935 }
   936 
   937 void free_group_keys_extra(group_keys_extra_t* group_keys_extra)
   938 {
   939     identity_list *group_keys = group_keys_extra->group_keys;
   940     char *group_id = group_keys_extra->group_id;
   941     free_identity_list(group_keys);
   942     free(group_id);
   943     free(group_keys_extra);
   944 }
   945 
   946 group_keys_extra_t* group_keys_extra_dup(group_keys_extra_t* group_key_extra_src)
   947 {
   948     group_keys_extra_t *group_key_extra_dst;
   949     group_key_extra_dst = calloc(1,sizeof(group_keys_extra_t));
   950     if(group_key_extra_dst == NULL){
   951         return NULL;
   952     }
   953 
   954     char *group_id = strdup(group_key_extra_src->group_id);
   955 
   956     if (group_key_extra_dst->group_id && !group_id){
   957         free(group_key_extra_dst);
   958         return NULL;
   959     }
   960     group_key_extra_dst->group_id = group_id;
   961 
   962     identity_list *group_keys = identity_list_dup(group_key_extra_src->group_keys);;
   963     if (!group_keys) {
   964         free(group_id);
   965         free(group_key_extra_dst);
   966         return NULL;
   967     }
   968     group_key_extra_dst->group_keys = group_keys;
   969 
   970     return group_key_extra_dst;
   971 }