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