1 // This file is under GNU General Public License 3.0
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"
10 #include "sync_impl.h"
11 #include "keymanagement.h"
12 #include "message_api.h"
14 #include "baseprotocol.h"
16 #define SYNC_VERSION_MAJOR 1
17 #define SYNC_VERSION_MINOR 0
19 #define SYNC_INHIBIT_TIME (60*10)
20 #define SYNC_MSG_EXPIRE_TIME (60 * 10)
25 DeviceGroup_Protocol_t *message;
27 DeviceState_event event;
34 static bool _is_own_uuid( PEP_SESSION session, UTF8String_t *uuid)
36 return strncmp(session->sync_session->sync_uuid,
37 (const char*)uuid->buf, uuid->size) == 0;
40 static bool _is_own_group_uuid( PEP_SESSION session, UTF8String_t *uuid, char** our_group)
42 PEP_STATUS status = PEP_STATUS_OK;
45 if(our_group == NULL || *our_group == NULL)
46 status = get_device_group(session, &devgrp);
50 bool res = (status == PEP_STATUS_OK && devgrp && devgrp[0] &&
51 strncmp(devgrp,(const char*)uuid->buf, uuid->size) == 0);
55 else if(*our_group == NULL)
61 PEP_STATUS receive_sync_msg(
69 Identity partner = NULL;
70 DeviceState_event event = DeviceState_event_NONE;
71 assert(session && sync_msg);
72 if (!(session && sync_msg))
73 return PEP_ILLEGAL_VALUE;
76 bool msgIsFromGroup = false;
77 if(sync_msg->is_a_message){
78 DeviceGroup_Protocol_t *msg = sync_msg->u.message;
79 assert(msg && msg->payload.present != DeviceGroup_Protocol__payload_PR_NOTHING);
80 if (!(msg && msg->payload.present != DeviceGroup_Protocol__payload_PR_NOTHING)){
81 status = PEP_OUT_OF_MEMORY;
85 partner = Identity_to_Struct(&msg->header.me, NULL);
87 status = PEP_OUT_OF_MEMORY;
88 ASN_STRUCT_FREE(asn_DEF_DeviceGroup_Protocol, msg);
92 msgIsFromGroup = msg->header.devicegroup;
94 switch (msg->payload.present) {
95 case DeviceGroup_Protocol__payload_PR_beacon:
99 case DeviceGroup_Protocol__payload_PR_handshakeRequest:
101 // re-check uuid in case sync_uuid or group changed while in the queue
102 char *own_group_id = NULL;
103 bool is_for_me = _is_own_uuid(session,
104 msg->payload.choice.handshakeRequest.partner_id);
105 bool is_for_group = !is_for_me && _is_own_group_uuid(session,
106 msg->payload.choice.handshakeRequest.partner_id, &own_group_id);
107 if (!(is_for_me || is_for_group)){
108 status = PEP_MESSAGE_IGNORE;
112 UTF8String_t *guuid = msg->payload.choice.handshakeRequest.group_id;
113 if(msgIsFromGroup && guuid && guuid->buf && guuid->size) {
114 bool is_from_own_group = _is_own_group_uuid(session,
115 guuid, &own_group_id);
117 // Filter handshake requests from own group
118 if(is_from_own_group) {
119 status = PEP_MESSAGE_IGNORE;
123 // if it comes from another group,
124 // we want to communicate with that group
125 // insert group_id given in handshake request
128 free(partner->user_id);
129 partner->user_id = strndup((const char*)guuid->buf, guuid->size);
130 if(partner->user_id == NULL){
131 status = PEP_OUT_OF_MEMORY;
135 // if it comes from another group, and we are grouped,
136 // then this is groupmerge
139 event = HandshakeRequest;
143 case DeviceGroup_Protocol__payload_PR_updateRequest:
144 event = UpdateRequest;
147 case DeviceGroup_Protocol__payload_PR_groupKeys:
149 // re-check uuid in case sync_uuid or group_uuid changed while in the queue
150 char *own_group_id = NULL;
151 UTF8String_t *puuid = msg->payload.choice.groupKeys.partner_id;
152 bool is_for_me = _is_own_uuid(session, puuid);
153 bool is_for_group = !is_for_me &&
154 _is_own_group_uuid(session,
155 puuid, &own_group_id);
156 if (!(is_for_me || is_for_group)){
157 status = PEP_MESSAGE_IGNORE;
161 UTF8String_t *guuid = msg->payload.choice.groupKeys.group_id;
163 // GroupKeys come from groups, no choice
164 if(!(msgIsFromGroup && guuid && guuid->buf && guuid->size)) {
165 status = PEP_SYNC_ILLEGAL_MESSAGE;
169 // Filter groupKeys from own group
170 bool is_from_own_group = _is_own_group_uuid(session,
173 if(is_from_own_group) {
174 // FixMe : protocol shouldn't allow this
175 status = PEP_SYNC_ILLEGAL_MESSAGE;
179 // insert group_id given in groupKeys into partner's id
180 free(partner->user_id);
181 partner->user_id = strndup((const char*)guuid->buf, guuid->size);
182 if(partner->user_id == NULL){
183 status = PEP_OUT_OF_MEMORY;
187 // if it comes from another group, and we are grouped,
188 // then this is groupmerge's groupKeys
190 group_keys_extra_t *group_keys_extra;
191 group_keys_extra = malloc(sizeof(group_keys_extra_t));
192 if(group_keys_extra == NULL){
193 status = PEP_OUT_OF_MEMORY;
194 ASN_STRUCT_FREE(asn_DEF_DeviceGroup_Protocol, msg);
198 char *group_id = strndup((char*)guuid->buf, guuid->size);
201 status = PEP_OUT_OF_MEMORY;
202 free(group_keys_extra);
203 ASN_STRUCT_FREE(asn_DEF_DeviceGroup_Protocol, msg);
206 group_keys_extra->group_id = group_id;
208 identity_list *group_keys = IdentityList_to_identity_list(
209 &msg->payload.choice.groupKeys.ownIdentities,
212 status = PEP_OUT_OF_MEMORY;
214 free(group_keys_extra);
215 ASN_STRUCT_FREE(asn_DEF_DeviceGroup_Protocol, msg);
218 group_keys_extra->group_keys = group_keys;
220 extra = (void *) group_keys_extra;
225 case DeviceGroup_Protocol__payload_PR_groupUpdate:
227 identity_list *group_keys = IdentityList_to_identity_list(
228 &msg->payload.choice.groupUpdate.ownIdentities, NULL);
230 status = PEP_OUT_OF_MEMORY;
231 ASN_STRUCT_FREE(asn_DEF_DeviceGroup_Protocol, msg);
234 extra = (void *) group_keys;
240 status = PEP_SYNC_ILLEGAL_MESSAGE;
241 ASN_STRUCT_FREE(asn_DEF_DeviceGroup_Protocol, msg);
247 partner = sync_msg->u.event.partner;
248 extra = sync_msg->u.event.extra;
249 event = sync_msg->u.event.event;
252 // Event inhibition, to limit mailbox and prevent cycles
256 last = &session->LastCannotDecrypt;
260 last = &session->LastUpdateRequest;
268 time_t now = time(NULL);
269 if(*last != 0 && (*last + SYNC_INHIBIT_TIME) > now ){
270 status = PEP_STATEMACHINE_INHIBITED_EVENT;
276 // partner identity must be explicitely added DB to later
277 // be able to communicate securely with it.
280 status = get_default_own_userid(session, &own_id);
283 own_id = strdup(PEP_OWN_USERID);
285 // protect virtual user IDs
286 if((strncmp("TOFU_", partner->user_id, 6) == 0 &&
287 strlen(partner->user_id) == strlen(partner->address) + 6 &&
288 strcmp(partner->user_id + 6, partner->address)) ||
290 (strcmp(own_id, partner->user_id) == 0)){
291 status = PEP_SYNC_ILLEGAL_MESSAGE;
297 // partner IDs are UUIDs bound to session lifespan
298 // and therefore partner identities are not supposed
299 // to mutate over time, but just not be used anymore.
300 // It should then be safe to accept given identity if not
301 // already pre-existing
302 pEp_identity *stored_identity = NULL;
303 status = get_identity(session,
308 if (!stored_identity) {
309 // make a safe copy of partner, with no flags or comm_type
310 pEp_identity *tmpident = new_identity(partner->address,
314 if (tmpident == NULL){
315 status = PEP_OUT_OF_MEMORY;
319 // finaly add partner to DB
320 status = set_identity(session, tmpident);
321 assert(status == PEP_STATUS_OK);
322 if(status == PEP_STATUS_OK && msgIsFromGroup)
323 status = set_identity_flags(session, tmpident, PEP_idf_devicegroup);
324 free_identity(tmpident);
325 assert(status == PEP_STATUS_OK);
326 if (status != PEP_STATUS_OK) {
330 else if (status == PEP_STATUS_OK) {
331 free_identity(stored_identity);
337 status = fsm_DeviceState_inject(session, event, partner, extra, timeout);
341 free_identity(partner);
346 free_group_keys_extra((group_keys_extra_t*)extra);
351 identity_list *group_keys = (identity_list*) extra;
352 free_identity_list(group_keys);
366 // TODO: DYNAMIC_API was here, but broke the windows build.
367 // We need to check whether it belongs here or it's a bug.
368 /* DYNAMIC_API */ void free_sync_msg(sync_msg_t *sync_msg)
373 if(sync_msg->is_a_message){
374 DeviceGroup_Protocol_t *msg = sync_msg->u.message;
379 ASN_STRUCT_FREE(asn_DEF_DeviceGroup_Protocol, msg);
382 Identity partner = NULL;
383 partner = sync_msg->u.event.partner;
385 free_identity(partner);
394 int call_inject_sync_msg(PEP_SESSION session, void *msg);
396 PEP_STATUS inject_DeviceState_event(
398 DeviceState_event event,
406 return PEP_ILLEGAL_VALUE;
408 sync_msg_t *sync_msg = malloc(sizeof(sync_msg_t));
410 return PEP_OUT_OF_MEMORY;
412 sync_msg->is_a_message = false;
413 sync_msg->u.event.partner = partner;
414 sync_msg->u.event.extra = extra;
415 sync_msg->u.event.event = event;
417 status = call_inject_sync_msg(session, sync_msg);
418 if (status == PEP_SYNC_NO_INJECT_CALLBACK){
425 PEP_STATUS receive_DeviceState_msg(
429 stringlist_t *keylist)
431 assert(session && src);
432 if (!(session && src))
433 return PEP_ILLEGAL_VALUE;
435 bool consume = false;
436 bool discard = false;
437 bool force_keep_msg = false;
440 PEP_STATUS own_id_status = get_default_own_userid(session, &own_id);
442 for (bloblist_t *bl = src->attachments; bl && bl->value; bl = bl->next) {
443 if (bl->mime_type && strcasecmp(bl->mime_type, "application/pEp.sync") == 0
445 DeviceGroup_Protocol_t *msg = NULL;
446 uper_decode_complete(NULL, &asn_DEF_DeviceGroup_Protocol, (void **)
447 &msg, bl->value, bl->size);
450 PEP_STATUS status = PEP_STATUS_OK;
452 char *user_id = strndup((char *) msg->header.me.user_id->buf,
453 msg->header.me.user_id->size);
456 ASN_STRUCT_FREE(asn_DEF_DeviceGroup_Protocol, msg);
457 return PEP_OUT_OF_MEMORY;
460 // detect and mitigate address spoofing
461 Identity check_me = NULL;
462 char* null_terminated_address =
463 strndup((char *) msg->header.me.address->buf,
464 msg->header.me.address->size);
466 if(null_terminated_address){
469 status = get_identity(session,
470 null_terminated_address,
473 free(null_terminated_address);
477 status = own_id_status;
481 status = PEP_OUT_OF_MEMORY;
483 if (status == PEP_OUT_OF_MEMORY)
486 free_identity(check_me);
488 bool not_own_address = status != PEP_STATUS_OK;
489 status = PEP_STATUS_OK;
491 if (not_own_address ||
492 strncmp(src->from->address,
493 (char *) msg->header.me.address->buf,
494 msg->header.me.address->size) != 0 ||
495 strncmp(src->to->ident->address,
496 (char *) msg->header.me.address->buf,
497 msg->header.me.address->size) != 0) {
502 // if encrypted, ensure that header.me.fpr match signer's fpr
503 if (rating >= PEP_rating_reliable && (
505 !_same_fpr((char *) msg->header.me.fpr.buf,
506 msg->header.me.fpr.size,
508 strlen(keylist->value)))) {
513 // check message expiry
515 time_t expiry = timegm(src->recv) + SYNC_MSG_EXPIRE_TIME;
516 time_t now = time(NULL);
517 if(expiry != 0 && now != 0 && expiry < now){
523 int32_t value = (int32_t) msg->header.sequence;
525 status = PEP_SEQUENCE_VIOLATED;
527 status = sequence_value(session, (char *) user_id,
531 if (status == PEP_STATUS_OK) {
532 switch (msg->payload.present) {
533 // HandshakeRequest needs encryption
534 case DeviceGroup_Protocol__payload_PR_handshakeRequest:
536 UTF8String_t *puuid =
537 msg->payload.choice.handshakeRequest.partner_id;
538 bool is_for_me = _is_own_uuid(session, puuid);
539 bool is_for_group = !is_for_me &&
541 session, puuid, NULL);
543 // Reject handshake requests not addressed to us
544 if (rating < PEP_rating_reliable ||
545 !(is_for_me || is_for_group)){
550 // do not consume handshake request for group
552 force_keep_msg = true;
556 // accepting GroupKeys needs encryption and trust of peer device
557 case DeviceGroup_Protocol__payload_PR_groupKeys:
559 UTF8String_t *puuid = msg->payload.choice.groupKeys.partner_id;
560 bool is_for_me = _is_own_uuid(session, puuid);
561 bool is_for_group = !is_for_me &&
562 _is_own_group_uuid(session,
564 if (!keylist || rating < PEP_rating_reliable ||
565 // message is only consumed by instance it is addressed to
566 !(is_for_me || is_for_group)){
571 // do not consume groupKeys for group
573 // This happens in case case of groupmerge
574 force_keep_msg = true;
577 // Trust check disabled here but it still it should be safe.
578 // SameIdentity checks in state machine ensures that we only
579 // store groupkeys signed by device or group that have been
580 // previously accepted in handshake.
582 // // check trust of identity using user_id given in msg.header.me
583 // // to exacly match identity of device, the one trusted in
584 // // case of accepted handshake from a sole device
585 // pEp_identity *_from = new_identity(NULL,
589 // if (_from == NULL){
590 // status = PEP_OUT_OF_MEMORY;
593 // status = get_trust(session, _from);
594 // if (status != PEP_STATUS_OK || _from->comm_type < PEP_ct_strong_encryption) {
596 // // re-try with group_id instead, in case of handshake with pre-existing group
597 // UTF8String_t *guuid = msg->payload.choice.groupKeys.group_id;
598 // free(_from->user_id);
599 // if ((_from->user_id = strndup((const char*)guuid->buf, guuid->size)) == NULL){
600 // free_identity(_from);
601 // status = PEP_OUT_OF_MEMORY;
604 // _from->comm_type = PEP_ct_unknown;
606 // status = get_trust(session, _from);
607 // if (status != PEP_STATUS_OK || _from->comm_type < PEP_ct_strong_encryption) {
608 // status = PEP_STATUS_OK;
609 // free_identity(_from);
614 // free_identity(_from);
617 case DeviceGroup_Protocol__payload_PR_groupUpdate:
618 case DeviceGroup_Protocol__payload_PR_updateRequest:
620 // inject message but don't consume it, so
621 // that other group members can also be updated
622 force_keep_msg = true;
624 if (!keylist || rating < PEP_rating_reliable){
628 // GroupUpdate and UpdateRequests come from group.
629 // check trust relation in between signer key and
630 // own id to be sure.
632 if (status != PEP_STATUS_OK)
635 pEp_identity* _from = NULL;
638 _from = new_identity(NULL,
644 status = own_id_status;
649 status = PEP_OUT_OF_MEMORY;
652 status = get_trust(session, _from);
653 if (status != PEP_STATUS_OK || _from->comm_type < PEP_ct_pEp) {
654 status = PEP_STATUS_OK;
655 free_identity(_from);
659 free_identity(_from);
667 sync_msg_t *sync_msg = malloc(sizeof(sync_msg_t));
668 if(sync_msg == NULL){
669 status = PEP_OUT_OF_MEMORY;
672 sync_msg->is_a_message = true;
673 sync_msg->u.message = msg;
674 status = call_inject_sync_msg(session, sync_msg);
675 if (status != PEP_STATUS_OK){
676 if (status == PEP_SYNC_NO_INJECT_CALLBACK){
681 // don't free message now that it is in the queue
684 else if (status == PEP_OWN_SEQUENCE || status == PEP_SEQUENCE_VIOLATED) {
685 status = PEP_STATUS_OK;
691 ASN_STRUCT_FREE(asn_DEF_DeviceGroup_Protocol, msg);
695 if (status != PEP_STATUS_OK)
701 if (force_keep_msg) {
702 return PEP_MESSAGE_IGNORE;
705 if (consume && !session->keep_sync_msg) {
706 for (stringpair_list_t *spl = src->opt_fields ; spl && spl->value ;
708 if (spl->value->key &&
709 strcasecmp(spl->value->key, "pEp-auto-consume") == 0) {
710 if (spl->value->value &&
711 strcasecmp(spl->value->value, "yes") == 0)
712 return PEP_MESSAGE_CONSUME;
715 return PEP_MESSAGE_IGNORE;
719 return PEP_MESSAGE_IGNORE;
721 if (!session->keep_sync_msg) {
722 bloblist_t *last = NULL;
723 for (bloblist_t *bl = src->attachments; bl && bl->value; ) {
724 if (bl->mime_type && strcasecmp(bl->mime_type, "application/pEp.sync") == 0) {
728 src->attachments = bl;
743 return PEP_STATUS_OK;
746 DeviceGroup_Protocol_t *new_DeviceGroup_Protocol_msg(DeviceGroup_Protocol__payload_PR type)
748 DeviceGroup_Protocol_t *msg = (DeviceGroup_Protocol_t *)
749 calloc(1, sizeof(DeviceGroup_Protocol_t));
753 msg->payload.present = type;
757 void free_DeviceGroup_Protocol_msg(DeviceGroup_Protocol_t *msg)
759 ASN_STRUCT_FREE(asn_DEF_DeviceGroup_Protocol, msg);
764 static int _append(const void *buffer, size_t size, void *appkey)
766 char **dest_ptr = (char **)appkey;
767 size_t osize = strlen(*dest_ptr);
768 size_t nsize = size + osize;
769 *dest_ptr = realloc(*dest_ptr, nsize + 1);
770 if(*dest_ptr == NULL) return -1;
771 memcpy(*dest_ptr + osize, buffer, size);
772 (*dest_ptr)[nsize] = '\0';
777 PEP_STATUS unicast_msg(
779 const Identity partner,
780 DeviceState_state state,
781 DeviceGroup_Protocol_t *msg,
785 PEP_STATUS status = PEP_STATUS_OK;
786 char *payload = NULL;
787 message *_message = NULL;
788 pEp_identity *me = NULL;
789 pEp_identity *_me = NULL;
792 assert(session && partner && state && msg);
793 if (!(session && partner && state && msg))
794 return PEP_ILLEGAL_VALUE;
796 assert(session->messageToSend);
797 if (!session->messageToSend) {
798 status = PEP_SEND_FUNCTION_NOT_REGISTERED;
802 status = get_default_own_userid(session, &own_id);
803 if (status != PEP_STATUS_OK)
806 msg->header.version.major = SYNC_VERSION_MAJOR;
807 msg->header.version.minor = SYNC_VERSION_MINOR;
809 status = get_identity(session, partner->address, own_id, &me);
810 if (status != PEP_STATUS_OK)
815 status = sequence_value(session, session->sync_session->sync_uuid, &seq);
816 if (status != PEP_OWN_SEQUENCE && status != PEP_STATUS_OK)
819 msg->header.sequence = (long) seq;
821 _me = identity_dup(me);
826 _me->user_id = strndup(session->sync_session->sync_uuid, 36);
827 assert(_me->user_id);
831 if (Identity_from_Struct(_me, &msg->header.me) == NULL)
837 msg->header.state = (long) state;
839 bool devicegroup = deviceGrouped(session);
841 msg->header.devicegroup = 1;
843 msg->header.devicegroup = 0;
845 if (asn_check_constraints(&asn_DEF_DeviceGroup_Protocol, msg, NULL, NULL)) {
846 status = PEP_CONTRAINTS_VIOLATED;
850 ssize_t size = uper_encode_to_new_buffer(&asn_DEF_DeviceGroup_Protocol,
851 NULL, msg, (void **) &payload);
853 status = PEP_CANNOT_ENCODE;
857 status = prepare_message(me, partner, payload, size, &_message);
858 if (status != PEP_STATUS_OK)
866 er = xer_encode(&asn_DEF_DeviceGroup_Protocol, msg,
867 XER_F_BASIC, _append, &_message->longmsg);
873 if (msg->payload.present == DeviceGroup_Protocol__payload_PR_groupKeys ||
874 msg->payload.present == DeviceGroup_Protocol__payload_PR_groupUpdate) {
875 PEP_rating rating = PEP_rating_undefined;
876 status = outgoing_message_rating(session, _message, &rating);
877 if (status != PEP_STATUS_OK)
879 if (rating < PEP_rating_trusted) {
880 status = PEP_SYNC_NO_TRUST;
884 stringlist_t *keylist = NULL;
885 status = _own_keys_retrieve(session, &keylist, PEP_idf_not_for_sync);
886 if (status != PEP_STATUS_OK)
889 for (stringlist_t *_keylist=keylist; _keylist!=NULL; _keylist=_keylist->next) {
890 char *fpr = _keylist->value;
891 static char filename[MAX_LINELENGTH];
892 int result = snprintf(filename, MAX_LINELENGTH, "file://%s-sec.asc", fpr);
897 status = export_secret_key(session, fpr, &key, &size);
898 if (status != PEP_STATUS_OK)
900 bloblist_t *bl = bloblist_add(_message->attachments,
901 (char *) key, size, "application/pgp-keys", filename);
904 if (!_message->attachments)
905 _message->attachments = bl;
909 message *_encrypted = NULL;
910 status = encrypt_message(session, _message, NULL, &_encrypted, PEP_enc_PEP, 0);
911 if (status != PEP_STATUS_OK)
913 free_message(_message);
914 _message = _encrypted;
917 attach_own_key(session, _message);
920 status = session->messageToSend(session->sync_obj, _message);
924 status = PEP_OUT_OF_MEMORY;
928 free_message(_message);
934 PEP_STATUS multicast_self_msg(
936 DeviceState_state state,
937 DeviceGroup_Protocol_t *msg,
941 PEP_STATUS status = PEP_STATUS_OK;
943 assert(session && state && msg);
944 if (!(session && state && msg))
945 return PEP_ILLEGAL_VALUE;
947 identity_list *own_identities = NULL;
948 status = _own_identities_retrieve(session, &own_identities, PEP_idf_not_for_sync);
949 if (status != PEP_STATUS_OK)
952 for (identity_list *_i = own_identities; _i && _i->ident; _i = _i->next) {
953 pEp_identity *me = _i->ident;
955 // FIXME: no deep copy for multicast supported yet
956 // DeviceGroup_Protocol_t *_msg = malloc(sizeof(DeviceGroup_Protocol_t));
958 // if (_msg == NULL){
959 // status = PEP_OUT_OF_MEMORY;
962 // memcpy(_msg, msg, sizeof(DeviceGroup_Protocol_t));
963 status = unicast_msg(session, me, state, msg, encrypted);
964 //status = unicast_msg(session, me, state, _msg, encrypted);
965 //free_DeviceGroup_Protocol_msg(_msg);
968 free_identity_list(own_identities);
969 return PEP_STATUS_OK;
972 // free_identity_list(own_identities);
976 void free_group_keys_extra(group_keys_extra_t* group_keys_extra)
978 identity_list *group_keys = group_keys_extra->group_keys;
979 char *group_id = group_keys_extra->group_id;
980 free_identity_list(group_keys);
982 free(group_keys_extra);
985 group_keys_extra_t* group_keys_extra_dup(group_keys_extra_t* group_key_extra_src)
987 group_keys_extra_t *group_key_extra_dst;
988 group_key_extra_dst = calloc(1,sizeof(group_keys_extra_t));
989 if(group_key_extra_dst == NULL){
993 char *group_id = strdup(group_key_extra_src->group_id);
995 if (group_key_extra_dst->group_id && !group_id){
996 free(group_key_extra_dst);
999 group_key_extra_dst->group_id = group_id;
1001 identity_list *group_keys = identity_list_dup(group_key_extra_src->group_keys);;
1004 free(group_key_extra_dst);
1007 group_key_extra_dst->group_keys = group_keys;
1009 return group_key_extra_dst;