mirror of
https://github.com/oxen-io/session-android.git
synced 2025-06-08 22:28:33 +00:00
Protect against unknown GV2 UUIDs.
This commit is contained in:
parent
981676c7f8
commit
ec8d5defd4
@ -30,7 +30,6 @@ import org.thoughtcrime.securesms.recipients.RecipientId;
|
|||||||
import org.whispersystems.libsignal.util.guava.Optional;
|
import org.whispersystems.libsignal.util.guava.Optional;
|
||||||
import org.whispersystems.signalservice.api.groupsv2.DecryptedGroupUtil;
|
import org.whispersystems.signalservice.api.groupsv2.DecryptedGroupUtil;
|
||||||
import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentPointer;
|
import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentPointer;
|
||||||
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
|
|
||||||
import org.whispersystems.signalservice.api.util.UuidUtil;
|
import org.whispersystems.signalservice.api.util.UuidUtil;
|
||||||
|
|
||||||
import java.io.Closeable;
|
import java.io.Closeable;
|
||||||
@ -41,6 +40,7 @@ import java.util.Collections;
|
|||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
public final class GroupDatabase extends Database {
|
public final class GroupDatabase extends Database {
|
||||||
@ -252,7 +252,7 @@ public final class GroupDatabase extends Database {
|
|||||||
@WorkerThread
|
@WorkerThread
|
||||||
public @NonNull List<Recipient> getGroupMembers(@NonNull GroupId groupId, @NonNull MemberSet memberSet) {
|
public @NonNull List<Recipient> getGroupMembers(@NonNull GroupId groupId, @NonNull MemberSet memberSet) {
|
||||||
if (groupId.isV2()) {
|
if (groupId.isV2()) {
|
||||||
return getGroup(groupId).transform(g -> g.requireV2GroupProperties().getMembers(context, memberSet))
|
return getGroup(groupId).transform(g -> g.requireV2GroupProperties().getMemberRecipients(memberSet))
|
||||||
.or(Collections.emptyList());
|
.or(Collections.emptyList());
|
||||||
} else {
|
} else {
|
||||||
List<RecipientId> currentMembers = getCurrentMembers(groupId);
|
List<RecipientId> currentMembers = getCurrentMembers(groupId);
|
||||||
@ -338,7 +338,7 @@ public final class GroupDatabase extends Database {
|
|||||||
contentValues.put(V2_MASTER_KEY, groupMasterKey.serialize());
|
contentValues.put(V2_MASTER_KEY, groupMasterKey.serialize());
|
||||||
contentValues.put(V2_REVISION, groupState.getVersion());
|
contentValues.put(V2_REVISION, groupState.getVersion());
|
||||||
contentValues.put(V2_DECRYPTED_GROUP, groupState.toByteArray());
|
contentValues.put(V2_DECRYPTED_GROUP, groupState.toByteArray());
|
||||||
contentValues.put(MEMBERS, serializeV2GroupMembers(context, groupState));
|
contentValues.put(MEMBERS, serializeV2GroupMembers(groupState));
|
||||||
} else {
|
} else {
|
||||||
if (groupId.isV2()) {
|
if (groupId.isV2()) {
|
||||||
throw new AssertionError("V2 group id but no master key");
|
throw new AssertionError("V2 group id but no master key");
|
||||||
@ -395,7 +395,7 @@ public final class GroupDatabase extends Database {
|
|||||||
contentValues.put(TITLE, title);
|
contentValues.put(TITLE, title);
|
||||||
contentValues.put(V2_REVISION, decryptedGroup.getVersion());
|
contentValues.put(V2_REVISION, decryptedGroup.getVersion());
|
||||||
contentValues.put(V2_DECRYPTED_GROUP, decryptedGroup.toByteArray());
|
contentValues.put(V2_DECRYPTED_GROUP, decryptedGroup.toByteArray());
|
||||||
contentValues.put(MEMBERS, serializeV2GroupMembers(context, decryptedGroup));
|
contentValues.put(MEMBERS, serializeV2GroupMembers(decryptedGroup));
|
||||||
|
|
||||||
databaseHelper.getWritableDatabase().update(TABLE_NAME, contentValues,
|
databaseHelper.getWritableDatabase().update(TABLE_NAME, contentValues,
|
||||||
GROUP_ID + " = ?",
|
GROUP_ID + " = ?",
|
||||||
@ -517,13 +517,16 @@ public final class GroupDatabase extends Database {
|
|||||||
return getGroup(groupId).transform(g -> g.isPendingMember(recipient)).or(false);
|
return getGroup(groupId).transform(g -> g.isPendingMember(recipient)).or(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String serializeV2GroupMembers(@NonNull Context context, @NonNull DecryptedGroup decryptedGroup) {
|
private static String serializeV2GroupMembers(@NonNull DecryptedGroup decryptedGroup) {
|
||||||
List<RecipientId> groupMembers = new ArrayList<>(decryptedGroup.getMembersCount());
|
List<RecipientId> groupMembers = new ArrayList<>(decryptedGroup.getMembersCount());
|
||||||
|
|
||||||
for (DecryptedMember member : decryptedGroup.getMembersList()) {
|
for (DecryptedMember member : decryptedGroup.getMembersList()) {
|
||||||
Recipient recipient = Recipient.externalPush(context, new SignalServiceAddress(UuidUtil.fromByteString(member.getUuid()), null));
|
UUID uuid = UuidUtil.fromByteString(member.getUuid());
|
||||||
|
if (UuidUtil.UNKNOWN_UUID.equals(uuid)) {
|
||||||
groupMembers.add(recipient.getId());
|
Log.w(TAG, "Seen unknown UUID in members list");
|
||||||
|
} else {
|
||||||
|
groupMembers.add(RecipientId.from(uuid, null));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Collections.sort(groupMembers);
|
Collections.sort(groupMembers);
|
||||||
@ -779,25 +782,41 @@ public final class GroupDatabase extends Database {
|
|||||||
.or(false);
|
.or(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Recipient> getMembers(@NonNull Context context, @NonNull MemberSet memberSet) {
|
public List<Recipient> getMemberRecipients(@NonNull MemberSet memberSet) {
|
||||||
boolean includeSelf = memberSet.includeSelf;
|
return Stream.of(getMemberRecipientIds(memberSet))
|
||||||
DecryptedGroup groupV2 = getDecryptedGroup();
|
.map(Recipient::resolved)
|
||||||
UUID selfUuid = Recipient.self().getUuid().get();
|
.toList();
|
||||||
List<Recipient> recipients = new ArrayList<>(groupV2.getMembersCount() + groupV2.getPendingMembersCount());
|
}
|
||||||
|
|
||||||
|
public List<RecipientId> getMemberRecipientIds(@NonNull MemberSet memberSet) {
|
||||||
|
boolean includeSelf = memberSet.includeSelf;
|
||||||
|
DecryptedGroup groupV2 = getDecryptedGroup();
|
||||||
|
UUID selfUuid = Recipient.self().getUuid().get();
|
||||||
|
List<RecipientId> recipients = new ArrayList<>(groupV2.getMembersCount() + groupV2.getPendingMembersCount());
|
||||||
|
int unknownMembers = 0;
|
||||||
|
int unknownPending = 0;
|
||||||
|
|
||||||
for (UUID uuid : DecryptedGroupUtil.toUuidList(groupV2.getMembersList())) {
|
for (UUID uuid : DecryptedGroupUtil.toUuidList(groupV2.getMembersList())) {
|
||||||
if (includeSelf || !selfUuid.equals(uuid)) {
|
if (UuidUtil.UNKNOWN_UUID.equals(uuid)) {
|
||||||
recipients.add(Recipient.externalPush(context, uuid, null));
|
unknownMembers++;
|
||||||
|
} else if (includeSelf || !selfUuid.equals(uuid)) {
|
||||||
|
recipients.add(RecipientId.from(uuid, null));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (memberSet.includePending) {
|
if (memberSet.includePending) {
|
||||||
for (UUID uuid : DecryptedGroupUtil.pendingToUuidList(groupV2.getPendingMembersList())) {
|
for (UUID uuid : DecryptedGroupUtil.pendingToUuidList(groupV2.getPendingMembersList())) {
|
||||||
if (includeSelf || !selfUuid.equals(uuid)) {
|
if (UuidUtil.UNKNOWN_UUID.equals(uuid)) {
|
||||||
recipients.add(Recipient.externalPush(context, uuid, null));
|
unknownPending++;
|
||||||
|
} else if (includeSelf || !selfUuid.equals(uuid)) {
|
||||||
|
recipients.add(RecipientId.from(uuid, null));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ((unknownMembers + unknownPending) > 0) {
|
||||||
|
Log.w(TAG, String.format(Locale.US, "Group contains %d + %d unknown pending and full members", unknownPending, unknownMembers));
|
||||||
|
}
|
||||||
|
|
||||||
return recipients;
|
return recipients;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1083,9 +1083,7 @@ public class MmsDatabase extends MessagingDatabase {
|
|||||||
if (message.isGroup()) {
|
if (message.isGroup()) {
|
||||||
OutgoingGroupUpdateMessage outgoingGroupUpdateMessage = (OutgoingGroupUpdateMessage) message;
|
OutgoingGroupUpdateMessage outgoingGroupUpdateMessage = (OutgoingGroupUpdateMessage) message;
|
||||||
if (outgoingGroupUpdateMessage.isV2Group()) {
|
if (outgoingGroupUpdateMessage.isV2Group()) {
|
||||||
MessageGroupContext.GroupV2Properties groupV2Properties = outgoingGroupUpdateMessage.requireGroupV2Properties();
|
type |= Types.GROUP_V2_BIT | Types.GROUP_UPDATE_BIT;
|
||||||
type |= Types.GROUP_V2_BIT;
|
|
||||||
if (groupV2Properties.isUpdate()) type |= Types.GROUP_UPDATE_BIT;
|
|
||||||
} else {
|
} else {
|
||||||
MessageGroupContext.GroupV1Properties properties = outgoingGroupUpdateMessage.requireGroupV1Properties();
|
MessageGroupContext.GroupV1Properties properties = outgoingGroupUpdateMessage.requireGroupV1Properties();
|
||||||
if (properties.isUpdate()) type |= Types.GROUP_UPDATE_BIT;
|
if (properties.isUpdate()) type |= Types.GROUP_UPDATE_BIT;
|
||||||
@ -1129,20 +1127,15 @@ public class MmsDatabase extends MessagingDatabase {
|
|||||||
if (message.getRecipient().isGroup()) {
|
if (message.getRecipient().isGroup()) {
|
||||||
OutgoingGroupUpdateMessage outgoingGroupUpdateMessage = (message instanceof OutgoingGroupUpdateMessage) ? (OutgoingGroupUpdateMessage) message : null;
|
OutgoingGroupUpdateMessage outgoingGroupUpdateMessage = (message instanceof OutgoingGroupUpdateMessage) ? (OutgoingGroupUpdateMessage) message : null;
|
||||||
|
|
||||||
GroupReceiptDatabase receiptDatabase = DatabaseFactory.getGroupReceiptDatabase(context);
|
GroupReceiptDatabase receiptDatabase = DatabaseFactory.getGroupReceiptDatabase(context);
|
||||||
RecipientDatabase recipientDatabase = DatabaseFactory.getRecipientDatabase(context);
|
Set<RecipientId> members = new HashSet<>();
|
||||||
Set<RecipientId> members = new HashSet<>();
|
|
||||||
|
|
||||||
if (outgoingGroupUpdateMessage != null && outgoingGroupUpdateMessage.isV2Group()) {
|
if (outgoingGroupUpdateMessage != null && outgoingGroupUpdateMessage.isV2Group()) {
|
||||||
MessageGroupContext.GroupV2Properties groupV2Properties = outgoingGroupUpdateMessage.requireGroupV2Properties();
|
MessageGroupContext.GroupV2Properties groupV2Properties = outgoingGroupUpdateMessage.requireGroupV2Properties();
|
||||||
members.addAll(Stream.of(groupV2Properties.getActiveMembers()).map(recipientDatabase::getOrInsertFromUuid).toList());
|
members.addAll(Stream.of(groupV2Properties.getAllActivePendingAndRemovedMembers())
|
||||||
if (groupV2Properties.isUpdate()) {
|
.distinct()
|
||||||
members.addAll(Stream.concat(Stream.of(groupV2Properties.getPendingMembers()),
|
.map(uuid -> RecipientId.from(uuid, null))
|
||||||
Stream.of(groupV2Properties.getRemovedMembers()))
|
.toList());
|
||||||
.distinct()
|
|
||||||
.map(recipientDatabase::getOrInsertFromUuid)
|
|
||||||
.toList());
|
|
||||||
}
|
|
||||||
members.remove(Recipient.self().getId());
|
members.remove(Recipient.self().getId());
|
||||||
} else {
|
} else {
|
||||||
members.addAll(Stream.of(DatabaseFactory.getGroupDatabase(context).getGroupMembers(message.getRecipient().requireGroupId(), GroupDatabase.MemberSet.FULL_MEMBERS_EXCLUDING_SELF)).map(Recipient::getId).toList());
|
members.addAll(Stream.of(DatabaseFactory.getGroupDatabase(context).getGroupMembers(message.getRecipient().requireGroupId(), GroupDatabase.MemberSet.FULL_MEMBERS_EXCLUDING_SELF)).map(Recipient::getId).toList());
|
||||||
|
@ -36,7 +36,13 @@ public final class ProfileKeySet {
|
|||||||
@Nullable UUID changeSource)
|
@Nullable UUID changeSource)
|
||||||
{
|
{
|
||||||
for (DecryptedMember member : group.getMembersList()) {
|
for (DecryptedMember member : group.getMembersList()) {
|
||||||
UUID memberUuid = UuidUtil.fromByteString(member.getUuid());
|
UUID memberUuid = UuidUtil.fromByteString(member.getUuid());
|
||||||
|
|
||||||
|
if (UuidUtil.UNKNOWN_UUID.equals(memberUuid)) {
|
||||||
|
Log.w(TAG, "Seen unknown member UUID");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
ProfileKey profileKey;
|
ProfileKey profileKey;
|
||||||
try {
|
try {
|
||||||
profileKey = new ProfileKey(member.getProfileKey().toByteArray());
|
profileKey = new ProfileKey(member.getProfileKey().toByteArray());
|
||||||
|
@ -164,21 +164,14 @@ public final class MessageGroupContext {
|
|||||||
return groupMasterKey;
|
return groupMasterKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
public @NonNull List<UUID> getActiveMembers() {
|
public @NonNull List<UUID> getAllActivePendingAndRemovedMembers() {
|
||||||
return DecryptedGroupUtil.membersToUuidList(decryptedGroupV2Context.getGroupState().getMembersList());
|
LinkedList<UUID> memberUuids = new LinkedList<>();
|
||||||
}
|
|
||||||
|
|
||||||
public @NonNull List<UUID> getPendingMembers() {
|
memberUuids.addAll(DecryptedGroupUtil.membersToUuidList(decryptedGroupV2Context.getGroupState().getMembersList()));
|
||||||
return DecryptedGroupUtil.pendingToUuidList(decryptedGroupV2Context.getGroupState().getPendingMembersList());
|
memberUuids.addAll(DecryptedGroupUtil.pendingToUuidList(decryptedGroupV2Context.getGroupState().getPendingMembersList()));
|
||||||
}
|
memberUuids.addAll(DecryptedGroupUtil.removedMembersUuidList(decryptedGroupV2Context.getChange()));
|
||||||
|
|
||||||
public @NonNull List<UUID> getRemovedMembers() {
|
return UuidUtil.filterKnown(memberUuids);
|
||||||
return DecryptedGroupUtil.removedMembersUuidList(decryptedGroupV2Context.getChange());
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isUpdate() {
|
|
||||||
// The group context is only stored on update messages.
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -81,4 +81,19 @@ public final class UuidUtil {
|
|||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Keep only UUIDs that are not the {@link #UNKNOWN_UUID}.
|
||||||
|
*/
|
||||||
|
public static List<UUID> filterKnown(Collection<UUID> uuids) {
|
||||||
|
ArrayList<UUID> result = new ArrayList<>(uuids.size());
|
||||||
|
|
||||||
|
for (UUID uuid : uuids) {
|
||||||
|
if (!UNKNOWN_UUID.equals(uuid)) {
|
||||||
|
result.add(uuid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user