[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", "guard": "TELODENDRIA_SCHEMA_PDUV1_H",
"header": "Schema/PduV1.h", "header": "Schema/PduV1.h",
"types": { "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": { "PduV1EventHash": {
"type": "struct", "type": "struct",
"fields": { "fields": {
@ -20,6 +30,10 @@
"next_events": { "next_events": {
"type": "[string]", "type": "[string]",
"required": false "required": false
},
"pdu_status": {
"type": "PduV1Status",
"required": false
} }
} }
}, },

View file

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

View file

@ -633,6 +633,9 @@ PopulateEventV1(Room * room, HashMap * event, PduV1 * pdu, ServerPart serv)
return true; 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. /* TODO: Create a PDU of our own, signed and everything.
* https://telodendria.io/blog/matrix-protocol-overview * https://telodendria.io/blog/matrix-protocol-overview
* has some ideas on how this could be done(up until stage 5). */ * has some ideas on how this could be done(up until stage 5). */
@ -788,7 +791,15 @@ VerifyPDUV1(PduV1 *auth_pdu)
/* TODO: /* TODO:
* https://spec.matrix.org/v1.7/server-server-api/ * https://spec.matrix.org/v1.7/server-server-api/
* #checks-performed-on-receipt-of-a-pdu */ * #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 return true; /* This only shows whenever an event was rejected, not
* soft-failed */ * soft-failed */
} }
@ -1559,6 +1570,41 @@ EventFitsV1(PduV1 pdu)
JsonFree(hm); JsonFree(hm);
return ret; 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 * static HashMap *
RoomEventSendV1(Room * room, HashMap * event) RoomEventSendV1(Room * room, HashMap * event)
{ {
@ -1566,6 +1612,7 @@ RoomEventSendV1(Room * room, HashMap * event)
HashMap *pdu_object = NULL; HashMap *pdu_object = NULL;
bool client_event, valid = false; bool client_event, valid = false;
State *state = NULL; State *state = NULL;
PduV1Status status;
client_event = !PopulateEventV1(room, event, &pdu, RoomGetCreator(room)); client_event = !PopulateEventV1(room, event, &pdu, RoomGetCreator(room));
pdu_object = PduV1ToJson(&pdu); pdu_object = PduV1ToJson(&pdu);
@ -1630,21 +1677,40 @@ RoomEventSendV1(Room * room, HashMap * event)
pdu.hashes.sha256 = RoomHashEventV1(pdu); pdu.hashes.sha256 = RoomHashEventV1(pdu);
#undef AddState #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. */ /* 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; goto finish;
} }
StateFree(state); StateFree(state);
state = NULL; state = NULL;
pdu._unsigned.pdu_status = status;
RoomAddEventV1(room, pdu); RoomAddEventV1(room, pdu);
valid = true; valid = true;
@ -1766,8 +1832,8 @@ bool
RoomAddEventV1(Room *room, PduV1 pdu) RoomAddEventV1(Room *room, PduV1 pdu)
{ {
DbRef *event_ref; DbRef *event_ref;
Array *prev_events, *leaves; Array *prev_events = NULL, *leaves = NULL;
HashMap *leaves_json, *pdu_json; HashMap *leaves_json = NULL, *pdu_json = NULL;
JsonValue *leaves_val; JsonValue *leaves_val;
char *safe_id; char *safe_id;
size_t i; size_t i;
@ -1777,6 +1843,9 @@ RoomAddEventV1(Room *room, PduV1 pdu)
return false; return false;
} }
/* Only accepted PDUs get to do the news */
if (pdu._unsigned.pdu_status == PDUV1_STATUS_ACCEPTED)
{
leaves_json = DbJson(room->leaves_ref); leaves_json = DbJson(room->leaves_ref);
leaves_val = JsonValueDuplicate(JsonGet(leaves_json, 1, "leaves")); leaves_val = JsonValueDuplicate(JsonGet(leaves_json, 1, "leaves"));
leaves = JsonValueAsArray(leaves_val); leaves = JsonValueAsArray(leaves_val);
@ -1809,6 +1878,7 @@ RoomAddEventV1(Room *room, PduV1 pdu)
} }
JsonValueFree(ArrayDelete(leaves, delete_index)); JsonValueFree(ArrayDelete(leaves, delete_index));
} }
}
safe_id = CreateSafeID(pdu.event_id); safe_id = CreateSafeID(pdu.event_id);
event_ref = DbCreate(room->db, 4, "rooms", room->id, "events", safe_id); event_ref = DbCreate(room->db, 4, "rooms", room->id, "events", safe_id);
@ -1817,12 +1887,16 @@ RoomAddEventV1(Room *room, PduV1 pdu)
pdu_json = PduV1ToJson(&pdu); pdu_json = PduV1ToJson(&pdu);
DbJsonSet(event_ref, pdu_json); DbJsonSet(event_ref, pdu_json);
/* Only accepted PDUs get to do the news */
if (pdu._unsigned.pdu_status == PDUV1_STATUS_ACCEPTED)
{
ArrayAdd(leaves, JsonValueObject(pdu_json)); ArrayAdd(leaves, JsonValueObject(pdu_json));
leaves_json = JsonDuplicate(leaves_json); leaves_json = JsonDuplicate(leaves_json);
JsonValueFree(HashMapDelete(leaves_json, "leaves")); JsonValueFree(HashMapDelete(leaves_json, "leaves"));
JsonSet(leaves_json, JsonValueArray(leaves), 1, "leaves"); JsonSet(leaves_json, JsonValueArray(leaves), 1, "leaves");
DbJsonSet(room->leaves_ref, leaves_json); DbJsonSet(room->leaves_ref, leaves_json);
JsonFree(leaves_json); JsonFree(leaves_json);
}
DbUnlock(room->db, event_ref); DbUnlock(room->db, event_ref);
@ -2313,3 +2387,15 @@ failure:
return NULL; return NULL;
#undef CopyField #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 = '@'; id->sigil = '@';
sender = ParserRecomposeCommonID(*id); sender = ParserRecomposeCommonID(*id);
Log(LOG_INFO, "sender=%s", sender);
room = RoomLock(db, roomId); room = RoomLock(db, roomId);
if (!RoomContainsUser(room, sender)) if (!RoomContainsUser(room, sender))
{ {

View file

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

View file

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

View file

@ -236,4 +236,14 @@ extern bool RoomJoin(Room *, User *);
*/ */
extern void RoomAddAlias(Db *, char *, char *, char *, char *); 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 */ #endif /* TELODENDRIA_ROOM_H */