Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Switch to original Hocuri's group membership consistency algo (#3782)(#4624) #4646

Merged
merged 2 commits into from
Sep 9, 2023

Conversation

iequidoo
Copy link
Collaborator

@iequidoo iequidoo commented Aug 31, 2023

  • If we don't know the parent (=In-Reply-To) message, then completely recreate the group member list
    (i.e. use the member list of the incoming message) (because we assume that we missed some messages
    & have a wrong group state).
  • If the message has a "Chat-Group-Member-Removed: member@example.com" header, then remove this
    member.
  • If the message has a "Chat-Group-Member-Added: member@example.com" header, then add this member.

That means:

  • Remove checks for the presense of ContactId::SELF in the group. Thus all recipients of a message
    take the same decision about group membership changes, no matter if they are in the group
    currently. This fixes a situation when a recipient thinks it's not a member because it missed a
    message about its addition before.
    NOTE: But always recreate membership list if SELF has been added. The current/older versions of DC
    don't always set "In-Reply-To" to the latest message they sent (it depends on timings), so we need
    this heuristic currently.
  • Recreate the group member list if we don't know the parent (=In-Reply-To) message, even if the
    sender isn't in the group as per our view, because we missed some messages and our view may be
    stale.

@iequidoo
Copy link
Collaborator Author

iequidoo commented Sep 1, 2023

test_synchronize_member_list_on_group_rejoin() fails. The problem is that the current logic wrt setting In-Reply-To header has a race, if i change the test as follows:

--- a/python/tests/test_0_complex_or_slow.py                                                                                                                                                                         
+++ b/python/tests/test_0_complex_or_slow.py                                                                                                                                                                         
@@ -104,9 +104,11 @@ def test_synchronize_member_list_on_group_rejoin(self, acfactory, lp):                                                                                                                          
                                                                                                                                                                                                                     
         lp.sec("ac2: wait for a message about removal from the chat")                                                                                                                                               
         msg = ac2._evtracker.wait_next_incoming_message()                                                                                                                                                           
+        msg = ac3._evtracker.wait_next_incoming_message()                                                                                                                                                           
                                                                                                                                                                                                                     
         lp.sec("ac1: removing ac3")                                                                                                                                                                                 
         chat.remove_contact(ac3)                                                                                                                                                                                    
+        msg = ac3._evtracker.wait_next_incoming_message()                                                                                                                                                           
                                                                                                                                                                                                                     
         lp.sec("ac1: adding ac2 back")                                                                                                                                                                              
         # Group is promoted, message is sent automatically

, then it passes. Otherwise ac2 receives a message containing in In-Reply-To the message about the removal of itself (not ac3!), i.e. the known message, and doesn't rebuild the group member list. I'll make a workaround for that, otherwise the new algo won't work with the current/older versions of DC.

// Always recreate membership list if SELF has been added. The current/older versions of
// DC don't always set "In-Reply-To" to the latest message they sent (it depends on
// timings), so we have this heuristic here.
|| self_added
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So, i need to bring back this check for SELF-addition, but it's the only place where this heuristic is needed. If In-Reply-To will always point to the latest message, this check could be removed someday.

@iequidoo iequidoo marked this pull request as ready for review September 1, 2023 03:38
@iequidoo iequidoo force-pushed the iequidoo/hocuri-group-consistency-algo branch from 7623e2f to 1ab69a9 Compare September 2, 2023 00:34
@iequidoo
Copy link
Collaborator Author

iequidoo commented Sep 2, 2023

Added 2 tests. Both fail for the previous algo

@iequidoo iequidoo force-pushed the iequidoo/hocuri-group-consistency-algo branch 2 times, most recently from 3583215 to 7beb51a Compare September 2, 2023 01:05
@link2xt
Copy link
Collaborator

link2xt commented Sep 5, 2023

Reference to the algorithm: #3782 (comment)

#4624 is already fixed with #4638

} else {
match mime_parser.get_header(HeaderDef::InReplyTo) {
(is_from_in_chat && !mime_parser.has_chat_version())
// Always recreate membership list if SELF has been added. The current/older versions of
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does "current/older versions" means, isn't it the same as "all versions"?

Copy link
Collaborator Author

@iequidoo iequidoo Sep 5, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"current/older" doesn't include "future" versions :) I'm going to debug this. Looks like we're setting In-Reply-To to the Message-Id of the last successfully sent message, and the race occurs while the previous message is still being sent. If so, probably we need to carry in the new message a diff (wrt Chat-Group-Member-Added/Removed headers) to the referenced message, not to the message that is still being sent, i.e. to accumulate changes

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But once it is merged, the comment becomes outdated because it becomes the "current" version.

The bug is the same or related to #4676 I guess, maybe better merge a fix for it first so there could be a reference to exact version which is buggy.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks related, yes, but not sure it's the same. Will try to reproduce #4676 with the fix i added here. It's a couple of LOCs, but i should move it to a separate commit

@@ -1051,8 +1051,11 @@ impl ChatId {
self,
MessageState::OutPreparing,
MessageState::OutDraft,
MessageState::OutPending,
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So, this makes the w/a with self_added check necessary only for the older DC versions. Checked by hand with test_0_complex_or_slow.py::TestGroupStressTests::test_synchronize_member_list_on_group_rejoin

…d messages

The new message for which `parent_query()` is done may assume that it will be received in a context
affected by those messages, e.g. they could add new members to a group and the new message will
contain them in "To:". Anyway recipients must be prepared to orphaned references.
@iequidoo iequidoo force-pushed the iequidoo/hocuri-group-consistency-algo branch from 8086a55 to a012082 Compare September 6, 2023 00:47
…3782)(#4624)

- If we don't know the parent (=In-Reply-To) message, then completely recreate the group member list
  (i.e. use the member list of the incoming message) (because we assume that we missed some messages
  & have a wrong group state).
- If the message has a "Chat-Group-Member-Removed: member@example.com" header, then remove this
  member.
- If the message has a "Chat-Group-Member-Added: member@example.com" header, then add this member.

That means:
- Remove checks for the presense of `ContactId::SELF` in the group. Thus all recipients of a message
  take the same decision about group membership changes, no matter if they are in the group
  currently. This fixes a situation when a recipient thinks it's not a member because it missed a
  message about its addition before.
  NOTE: But always recreate membership list if SELF has been added. The older versions of DC don't
  always set "In-Reply-To" to the latest message they sent, but to the latest delivered message (so
  it's a race), so we need this heuristic currently.
- Recreate the group member list if we don't know the parent (=In-Reply-To) message, even if the
  sender isn't in the group as per our view, because we missed some messages and our view may be
  stale.
@iequidoo iequidoo force-pushed the iequidoo/hocuri-group-consistency-algo branch from a012082 to 73aa56b Compare September 6, 2023 01:28
Copy link
Collaborator

@link2xt link2xt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, especially the tests make sense so there is some progress/improvement.

There is a small comment regarding the possibility of easily adding self to the group from which the user was previously removed, but I suggest merging this PR as is and only trying to fix this in a follow up PR if at all to avoid "security" discussions from blocking this PR and keeping it unmerged.

warn!(
context,
"Contact {from_id} attempts to modify group chat {chat_id} member list without being a member."
"Contact {from_id} modifies group chat {chat_id} member list possibly not being a member."
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Allowing someone who is not a member of the chat to modify it seems a bit dangerous.

This condition (recreate_member_list && !is_from_in_chat) is true if either self_added is true (which simply means that there is a ChatGroupMemberAdded header with our address, can be easily forged) or there is an InReplyTo pointing to unknown Message-ID (could also be easily forged, just put random string into InReplyTo).

Probably not a big deal because user can keep sending messages to all old group members anyway (e.g. from Thunderbird it is easy), the only security implication is that once someone replies to the chat they will leak who was added to the group after the user was removed.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Before this PR it's also possible for non-members of the chat to modify it, but not to recreate the member list completely. F.e. using the Chat-Group-Member-Added header. Was it done intentionally, @Septias, maybe you know? Maybe we should think more and either allow this or prohibit completely

Copy link
Collaborator Author

@iequidoo iequidoo Sep 9, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like it's just a missed check. But we can merge this PR which just makes the behaviour consistent and then open another one prohibiting chat modifications by non-members completely, e.g. just adding an additional check directly to allow_member_list_changes, to continue the discussion

@iequidoo iequidoo merged commit e12e026 into stable Sep 9, 2023
35 checks passed
@iequidoo iequidoo deleted the iequidoo/hocuri-group-consistency-algo branch September 9, 2023 19:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants