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