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