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