forked from Telodendria/Telodendria
183 lines
5.7 KiB
C
183 lines
5.7 KiB
C
#include "Room/internal.h"
|
|
#include "Room/V1/Auth/internal.h"
|
|
|
|
#include <Schema/PduV1.h>
|
|
|
|
#include <Cytoplasm/Log.h>
|
|
|
|
#include <Parser.h>
|
|
|
|
static bool
|
|
VerifyPDUV1(PduV1 *auth_pdu)
|
|
{
|
|
/* TODO: The PDU could come from an unknown source, which may lack
|
|
* the tools to verify softfailing(or we may not trust them)*/
|
|
/* TODO:
|
|
* https://spec.matrix.org/v1.7/server-server-api/
|
|
* #checks-performed-on-receipt-of-a-pdu */
|
|
if (RoomIsRejectedV1(*auth_pdu))
|
|
{
|
|
Log(LOG_ERR, "Auth PDU has been rejected.");
|
|
return false;
|
|
}
|
|
if (RoomIsSoftfailedV1(*auth_pdu))
|
|
{
|
|
Log(LOG_ERR, "Auth PDU has been softfailed.");
|
|
}
|
|
return true; /* This only shows whenever an event was rejected, not
|
|
* soft-failed */
|
|
}
|
|
static bool
|
|
ValidAuthEventV1(PduV1 *auth_pdu, PduV1 *pdu)
|
|
{
|
|
if (IsState(auth_pdu, "m.room.create", ""))
|
|
{
|
|
return true;
|
|
}
|
|
if (IsState(auth_pdu, "m.room.power_levels", ""))
|
|
{
|
|
/* TODO: Check if it's the latest in terms of [pdu] */
|
|
return true;
|
|
}
|
|
if (IsState(auth_pdu, "m.room.member", pdu->sender))
|
|
{
|
|
/* TODO: Check if it's the latest in terms of [pdu] */
|
|
return true;
|
|
}
|
|
if (StrEquals(pdu->type, "m.room.member"))
|
|
{
|
|
char *membership =
|
|
JsonValueAsString(JsonGet(pdu->content, 1, "membership"));
|
|
JsonValue *third_pid =
|
|
JsonGet(pdu->content, 1, "third_party_invite");
|
|
|
|
/* The PDU's state_key is the target. So we check if if the
|
|
* auth PDU would count as the target's membership. Was very fun to
|
|
* find that out when I wanted to kick my 'yukari' alt. */
|
|
if (IsState(auth_pdu, "m.room.member", pdu->state_key))
|
|
{
|
|
/* TODO: Check if it's the latest in terms of [pdu] */
|
|
return true;
|
|
}
|
|
if ((StrEquals(membership, "join") ||
|
|
StrEquals(membership, "invite")) &&
|
|
IsState(auth_pdu, "m.room.join_rules",""))
|
|
{
|
|
/* TODO: Check if it's the latest in terms of [pdu] */
|
|
return true;
|
|
}
|
|
if (StrEquals(membership, "invite") && third_pid)
|
|
{
|
|
HashMap *tpid = JsonValueAsObject(third_pid);
|
|
JsonValue *token =
|
|
JsonGet(tpid, 2, "signed", "token");
|
|
char *token_str = JsonValueAsString(token);
|
|
if (IsState(auth_pdu, "m.room.third_party_invite", token_str))
|
|
{
|
|
/* TODO: Check if it is the latest. */
|
|
return true;
|
|
}
|
|
}
|
|
/* V1 simply doesn't have the concept of restricted rooms,
|
|
* so we can safely skip this one for this function. */
|
|
}
|
|
return false;
|
|
}
|
|
bool
|
|
ConsiderAuthEventsV1(Room * room, PduV1 pdu, char **errp)
|
|
{
|
|
char *ignored;
|
|
size_t i;
|
|
bool room_create = false;
|
|
HashMap *state_keytype;
|
|
state_keytype = HashMapCreate();
|
|
for (i = 0; i < ArraySize(pdu.auth_events); i++)
|
|
{
|
|
char *event_id = JsonValueAsString(ArrayGet(pdu.auth_events, i));
|
|
HashMap *event = RoomEventFetchRaw(room, event_id);
|
|
PduV1 auth_pdu = { 0 };
|
|
|
|
char *key_type_id;
|
|
|
|
if (!PduV1FromJson(event, &auth_pdu, &ignored))
|
|
{
|
|
JsonFree(event);
|
|
HashMapFree(state_keytype);
|
|
if (errp)
|
|
{
|
|
*errp = "Couldn't parse an auth event";
|
|
}
|
|
return false; /* Yeah... we aren't doing that. */
|
|
}
|
|
|
|
/* TODO: Find a better way to do this. Using HashMaps as sets
|
|
* *works*, but it's not the best of ideas here. Also, we're using
|
|
* strings to compare things, which yeah. */
|
|
key_type_id = StrConcat(3, auth_pdu.type, ",", auth_pdu.state_key);
|
|
|
|
if (HashMapGet(state_keytype, key_type_id))
|
|
{
|
|
/* Duplicate found! We actually ignore it's actual value. */
|
|
if (errp)
|
|
{
|
|
*errp = "Duplicate auth event was found";
|
|
}
|
|
|
|
JsonFree(event);
|
|
PduV1Free(&auth_pdu);
|
|
|
|
HashMapFree(state_keytype);
|
|
Free(key_type_id);
|
|
return false;
|
|
}
|
|
/* Whenever event is valid or not really doesn't matter, as we're
|
|
* not using it's value anywhere. */
|
|
HashMapSet(state_keytype, key_type_id, event);
|
|
Free(key_type_id);
|
|
|
|
/* Step 2.2: If there are entries whose type and state_key don't
|
|
* match those specified by the auth events selection algorithm
|
|
* described in the server specification, reject. */
|
|
if (!ValidAuthEventV1(&auth_pdu, &pdu))
|
|
{
|
|
if (errp)
|
|
{
|
|
*errp = "Invalid authevent given.";
|
|
}
|
|
JsonFree(event);
|
|
PduV1Free(&auth_pdu);
|
|
HashMapFree(state_keytype);
|
|
return false;
|
|
}
|
|
/* Step 2.3: If there are entries which were themselves rejected
|
|
* under the checks performed on receipt of a PDU, reject.
|
|
* TODO */
|
|
if (!VerifyPDUV1(&auth_pdu))
|
|
{
|
|
if (errp)
|
|
{
|
|
*errp = "Event depends on rejected PDUs";
|
|
}
|
|
PduV1Free(&auth_pdu);
|
|
JsonFree(event);
|
|
HashMapFree(state_keytype);
|
|
return false;
|
|
}
|
|
|
|
/* Step 2.4: If there is no m.room.create event among the entries,
|
|
* reject. */
|
|
if (!room_create && IsState(&auth_pdu, "m.room.create", ""))
|
|
{
|
|
room_create = true; /* Here, we check for the opposite. */
|
|
}
|
|
|
|
JsonFree(event);
|
|
PduV1Free(&auth_pdu);
|
|
}
|
|
HashMapFree(state_keytype);
|
|
if (!room_create && errp)
|
|
{
|
|
*errp = "Room creation event was not in the PDU's auth events";
|
|
}
|
|
return room_create; /* Step 2.4 is actually done here. */
|
|
}
|