[MOD] Start softfailing/dropped, and fix leaks
Some checks failed
Compile Telodendria / Compile Telodendria (x86, alpine-v3.19) (push) Has been cancelled
Compile Telodendria / Compile Telodendria (x86, debian-v12.4) (push) Has been cancelled
Compile Telodendria / Compile Telodendria (x86, freebsd-v14.0) (push) Has been cancelled
Compile Telodendria / Compile Telodendria (x86, netbsd-v9.3) (push) Has been cancelled
Compile Telodendria / Compile Telodendria (x86_64, alpine-v3.19) (push) Has been cancelled
Compile Telodendria / Compile Telodendria (x86_64, debian-v12.4) (push) Has been cancelled
Compile Telodendria / Compile Telodendria (x86_64, freebsd-v14.0) (push) Has been cancelled
Compile Telodendria / Compile Telodendria (x86_64, netbsd-v9.3) (push) Has been cancelled
Compile Telodendria / Compile Telodendria (x86_64, openbsd-v7.4) (push) Has been cancelled

This commit is contained in:
LDA 2024-08-22 12:10:37 +02:00
parent 283455b1a0
commit d07880b24b
7 changed files with 167 additions and 52 deletions

View file

@ -2,6 +2,16 @@
"guard": "TELODENDRIA_SCHEMA_PDUV1_H",
"header": "Schema/PduV1.h",
"types": {
"PduV1Status": {
"type": "enum",
"fields": {
"dropped": { "name": "PDUV1_STATUS_DROPPED" },
"softfail": { "name": "PDUV1_STATUS_SOFTFAIL" },
"accepted": { "name": "PDUV1_STATUS_ACCEPTED" },
"rejected": { "name": "PDUV1_STATUS_REJECTED" }
}
},
"PduV1EventHash": {
"type": "struct",
"fields": {
@ -20,6 +30,10 @@
"next_events": {
"type": "[string]",
"required": false
},
"pdu_status": {
"type": "PduV1Status",
"required": false
}
}
},

View file

@ -163,6 +163,7 @@ start:
userInfo = NULL;
groupInfo = NULL;
cron = NULL;
tConfig.ok = false;
token = NULL;
@ -633,9 +634,11 @@ finish:
CronFree(cron);
Log(LOG_DEBUG, "Stopped and freed job scheduler.");
}
ConfigUnlock(&tConfig);
Log(LOG_DEBUG, "Unlocked configuration.");
if (tConfig.ok)
{
ConfigUnlock(&tConfig);
Log(LOG_DEBUG, "Unlocked configuration.");
}
DbClose(matrixArgs.db);
Log(LOG_DEBUG, "Closed database.");

View file

@ -633,6 +633,9 @@ PopulateEventV1(Room * room, HashMap * event, PduV1 * pdu, ServerPart serv)
return true;
}
/* Consider the PDU dropped by default */
pdu->_unsigned.pdu_status = PDUV1_STATUS_DROPPED;
/* TODO: Create a PDU of our own, signed and everything.
* https://telodendria.io/blog/matrix-protocol-overview
* has some ideas on how this could be done(up until stage 5). */
@ -788,7 +791,15 @@ VerifyPDUV1(PduV1 *auth_pdu)
/* TODO:
* https://spec.matrix.org/v1.7/server-server-api/
* #checks-performed-on-receipt-of-a-pdu */
(void) auth_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 */
}
@ -1559,6 +1570,41 @@ EventFitsV1(PduV1 pdu)
JsonFree(hm);
return ret;
}
static PduV1Status
RoomGetEventStatusV1(Room *room, PduV1 *pdu, State *prev, bool client)
{
if (!room || !pdu || !prev)
{
return PDUV1_STATUS_DROPPED;
}
if (!EventFitsV1(*pdu))
{
/* Reject this event as it is too large. */
return PDUV1_STATUS_DROPPED;
}
if (!RoomAuthoriseEventV1(room, *pdu, prev))
{
/* Reject this event as the current state does not allow it.
* TODO: Make the auth check function return a string showing the
* errorr status to the user. */
return PDUV1_STATUS_DROPPED;
}
if (!client)
{
State *current = StateCurrent(room);
if (!RoomAuthoriseEventV1(room, *pdu, current))
{
StateFree(current);
return PDUV1_STATUS_SOFTFAIL;
}
StateFree(current);
}
return PDUV1_STATUS_ACCEPTED;
}
static HashMap *
RoomEventSendV1(Room * room, HashMap * event)
{
@ -1566,6 +1612,7 @@ RoomEventSendV1(Room * room, HashMap * event)
HashMap *pdu_object = NULL;
bool client_event, valid = false;
State *state = NULL;
PduV1Status status;
client_event = !PopulateEventV1(room, event, &pdu, RoomGetCreator(room));
pdu_object = PduV1ToJson(&pdu);
@ -1630,21 +1677,40 @@ RoomEventSendV1(Room * room, HashMap * event)
pdu.hashes.sha256 = RoomHashEventV1(pdu);
#undef AddState
}
/* TODO: It seems like we need to behave differently in terms of
* verifying PDUs from the client/federation.
* - In the client, we just do not care about any events that
* are incorrect. We simply drop them, as if they never existed.
* - In the server on the otherhand, the only place where we can
* possibly drop events as such is if it fails signatures. In
* other cases, we *have* to store it(ableit with flags, to
* restrict what we can do).
* - Rejection: We avoid relaying/linking those to anything.
* They must NOT be used for stateres.
* - Softfail: Essentially almost the same as rejects, except
* that they *are* used for stateres.
* I guess a way to do this may be to add a CheckAuthStatus
* function that also verifies if it is a client event, and returns
* an enum:
* - DROPPED: Do NOT process it _at all_
* - REJECT: Process that event as if it was rejected
* - SOFTFAIL: Process the event as if it was softfailed
* The main issue is storing it in the PDU. A naive approach would be to
* add the status to the unsigned field of the PDU, and add functions to
* return the status. I guess that is possible, but then again, can we
* really abuse the unsigned field for this?
*/
/* TODO: For PDU events, we should verify their hashes. */
if (!EventFitsV1(pdu))
status = RoomGetEventStatusV1(room, &pdu, state, client_event);
Log(LOG_INFO, "status='%s'", PduV1StatusToStr(status));
if (status == PDUV1_STATUS_DROPPED)
{
/* Reject this event as it is too large. */
goto finish;
}
if (!RoomAuthoriseEventV1(room, pdu, state))
{
/* Reject this event as the current state does not allow it.
* TODO: Make the auth check function return a string showing the
* errorr status to the user. */
goto finish;
}
StateFree(state);
state = NULL;
pdu._unsigned.pdu_status = status;
RoomAddEventV1(room, pdu);
valid = true;
@ -1766,8 +1832,8 @@ bool
RoomAddEventV1(Room *room, PduV1 pdu)
{
DbRef *event_ref;
Array *prev_events, *leaves;
HashMap *leaves_json, *pdu_json;
Array *prev_events = NULL, *leaves = NULL;
HashMap *leaves_json = NULL, *pdu_json = NULL;
JsonValue *leaves_val;
char *safe_id;
size_t i;
@ -1777,37 +1843,41 @@ RoomAddEventV1(Room *room, PduV1 pdu)
return false;
}
leaves_json = DbJson(room->leaves_ref);
leaves_val = JsonValueDuplicate(JsonGet(leaves_json, 1, "leaves"));
leaves = JsonValueAsArray(leaves_val);
Free(leaves_val);
prev_events = pdu.prev_events;
for (i = 0; i < ArraySize(prev_events); i++)
/* Only accepted PDUs get to do the news */
if (pdu._unsigned.pdu_status == PDUV1_STATUS_ACCEPTED)
{
JsonValue *event_val = ArrayGet(prev_events, i);
char *event_id = JsonValueAsString(event_val);
size_t j;
ssize_t delete_index = -1;
leaves_json = DbJson(room->leaves_ref);
leaves_val = JsonValueDuplicate(JsonGet(leaves_json, 1, "leaves"));
leaves = JsonValueAsArray(leaves_val);
Free(leaves_val);
for (j = 0; j < ArraySize(leaves); j++)
prev_events = pdu.prev_events;
for (i = 0; i < ArraySize(prev_events); i++)
{
JsonValue *leaf_val = ArrayGet(leaves, j);
HashMap *leaf_object = JsonValueAsObject(leaf_val);
char *leaf_id =
JsonValueAsString(JsonGet(leaf_object, 1, "event_id"));
JsonValue *event_val = ArrayGet(prev_events, i);
char *event_id = JsonValueAsString(event_val);
size_t j;
ssize_t delete_index = -1;
if (StrEquals(leaf_id, event_id))
for (j = 0; j < ArraySize(leaves); j++)
{
delete_index = j;
break;
JsonValue *leaf_val = ArrayGet(leaves, j);
HashMap *leaf_object = JsonValueAsObject(leaf_val);
char *leaf_id =
JsonValueAsString(JsonGet(leaf_object, 1, "event_id"));
if (StrEquals(leaf_id, event_id))
{
delete_index = j;
break;
}
}
if (delete_index == -1)
{
continue;
}
JsonValueFree(ArrayDelete(leaves, delete_index));
}
if (delete_index == -1)
{
continue;
}
JsonValueFree(ArrayDelete(leaves, delete_index));
}
safe_id = CreateSafeID(pdu.event_id);
@ -1817,12 +1887,16 @@ RoomAddEventV1(Room *room, PduV1 pdu)
pdu_json = PduV1ToJson(&pdu);
DbJsonSet(event_ref, pdu_json);
ArrayAdd(leaves, JsonValueObject(pdu_json));
leaves_json = JsonDuplicate(leaves_json);
JsonValueFree(HashMapDelete(leaves_json, "leaves"));
JsonSet(leaves_json, JsonValueArray(leaves), 1, "leaves");
DbJsonSet(room->leaves_ref, leaves_json);
JsonFree(leaves_json);
/* Only accepted PDUs get to do the news */
if (pdu._unsigned.pdu_status == PDUV1_STATUS_ACCEPTED)
{
ArrayAdd(leaves, JsonValueObject(pdu_json));
leaves_json = JsonDuplicate(leaves_json);
JsonValueFree(HashMapDelete(leaves_json, "leaves"));
JsonSet(leaves_json, JsonValueArray(leaves), 1, "leaves");
DbJsonSet(room->leaves_ref, leaves_json);
JsonFree(leaves_json);
}
DbUnlock(room->db, event_ref);
@ -2313,3 +2387,15 @@ failure:
return NULL;
#undef CopyField
}
bool
RoomIsSoftfailedV1(PduV1 pdu)
{
return pdu._unsigned.pdu_status == PDUV1_STATUS_SOFTFAIL;
}
bool
RoomIsRejectedV1(PduV1 pdu)
{
return pdu._unsigned.pdu_status == PDUV1_STATUS_SOFTFAIL;
}

View file

@ -237,6 +237,7 @@ ROUTE_IMPL(RouteKickRoom, path, argp)
id->sigil = '@';
sender = ParserRecomposeCommonID(*id);
Log(LOG_INFO, "sender=%s", sender);
room = RoomLock(db, roomId);
if (!RoomContainsUser(room, sender))
{

View file

@ -233,16 +233,13 @@ ROUTE_IMPL(RouteSendState, path, argp)
goto finish;
}
Log(LOG_INFO, "State event (%s,%s) coming this way", eventType, stateKey);
event = RoomEventCreate(
sender,
eventType, stateKey ? stateKey : "",
JsonDuplicate(request)
);
filled = RoomEventSend(room, event);
Log(LOG_INFO, "State event (%s,%s) coming this way", eventType, stateKey);
JsonFree(event);
Log(LOG_INFO, "And thats freed!");
if (!filled)
{

View file

@ -1040,7 +1040,7 @@ UserAddInvite(User *user, char *roomId)
data = DbJson(user->inviteRef);
JsonFree(HashMapSet(data, roomId, JsonValueNull()));
JsonValueFree(HashMapSet(data, roomId, JsonValueNull()));
}
void
@ -1091,7 +1091,7 @@ UserAddJoin(User *user, char *roomId)
if (!HashMapGet(data, roomId))
{
JsonFree(HashMapSet(data, roomId, JsonValueNull()));
JsonValueFree(HashMapSet(data, roomId, JsonValueNull()));
}
UserNotifyUser(user);
}
@ -1238,7 +1238,9 @@ UserPushJoinSync(User *user, char *roomId)
if (!HashMapGet(join, roomId))
{
JsonFree(HashMapSet(join, roomId, JsonValueObject(joinEntry)));
JsonValueFree(HashMapSet(join,
roomId, JsonValueObject(joinEntry)
));
}
else
{
@ -1370,7 +1372,9 @@ UserFillSyncDiff(User *user, char *batch)
if (!HashMapGet(join, roomId))
{
JsonFree(HashMapSet(join, roomId, JsonValueObject(joinEntry)));
JsonValueFree(HashMapSet(join,
roomId, JsonValueObject(joinEntry)
));
}
else
{

View file

@ -236,4 +236,14 @@ extern bool RoomJoin(Room *, User *);
*/
extern void RoomAddAlias(Db *, char *, char *, char *, char *);
/**
* Checks if a PDU has been qualified as 'soft-failed'.
*/
extern bool RoomIsSoftfailedV1(PduV1);
/**
* Checks if a PDU has been qualified as 'rejected'.
*/
extern bool RoomIsRejectedV1(PduV1);
#endif /* TELODENDRIA_ROOM_H */