forked from Telodendria/Telodendria
[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
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:
parent
283455b1a0
commit
d07880b24b
7 changed files with 167 additions and 52 deletions
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -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);
|
{
|
||||||
Log(LOG_DEBUG, "Unlocked configuration.");
|
ConfigUnlock(&tConfig);
|
||||||
|
Log(LOG_DEBUG, "Unlocked configuration.");
|
||||||
|
}
|
||||||
|
|
||||||
DbClose(matrixArgs.db);
|
DbClose(matrixArgs.db);
|
||||||
Log(LOG_DEBUG, "Closed database.");
|
Log(LOG_DEBUG, "Closed database.");
|
||||||
|
|
170
src/Room.c
170
src/Room.c
|
@ -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,37 +1843,41 @@ RoomAddEventV1(Room *room, PduV1 pdu)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
leaves_json = DbJson(room->leaves_ref);
|
/* Only accepted PDUs get to do the news */
|
||||||
leaves_val = JsonValueDuplicate(JsonGet(leaves_json, 1, "leaves"));
|
if (pdu._unsigned.pdu_status == PDUV1_STATUS_ACCEPTED)
|
||||||
leaves = JsonValueAsArray(leaves_val);
|
|
||||||
Free(leaves_val);
|
|
||||||
|
|
||||||
prev_events = pdu.prev_events;
|
|
||||||
for (i = 0; i < ArraySize(prev_events); i++)
|
|
||||||
{
|
{
|
||||||
JsonValue *event_val = ArrayGet(prev_events, i);
|
leaves_json = DbJson(room->leaves_ref);
|
||||||
char *event_id = JsonValueAsString(event_val);
|
leaves_val = JsonValueDuplicate(JsonGet(leaves_json, 1, "leaves"));
|
||||||
size_t j;
|
leaves = JsonValueAsArray(leaves_val);
|
||||||
ssize_t delete_index = -1;
|
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);
|
JsonValue *event_val = ArrayGet(prev_events, i);
|
||||||
HashMap *leaf_object = JsonValueAsObject(leaf_val);
|
char *event_id = JsonValueAsString(event_val);
|
||||||
char *leaf_id =
|
size_t j;
|
||||||
JsonValueAsString(JsonGet(leaf_object, 1, "event_id"));
|
ssize_t delete_index = -1;
|
||||||
|
|
||||||
if (StrEquals(leaf_id, event_id))
|
for (j = 0; j < ArraySize(leaves); j++)
|
||||||
{
|
{
|
||||||
delete_index = j;
|
JsonValue *leaf_val = ArrayGet(leaves, j);
|
||||||
break;
|
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);
|
safe_id = CreateSafeID(pdu.event_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);
|
||||||
|
|
||||||
ArrayAdd(leaves, JsonValueObject(pdu_json));
|
/* Only accepted PDUs get to do the news */
|
||||||
leaves_json = JsonDuplicate(leaves_json);
|
if (pdu._unsigned.pdu_status == PDUV1_STATUS_ACCEPTED)
|
||||||
JsonValueFree(HashMapDelete(leaves_json, "leaves"));
|
{
|
||||||
JsonSet(leaves_json, JsonValueArray(leaves), 1, "leaves");
|
ArrayAdd(leaves, JsonValueObject(pdu_json));
|
||||||
DbJsonSet(room->leaves_ref, leaves_json);
|
leaves_json = JsonDuplicate(leaves_json);
|
||||||
JsonFree(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);
|
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;
|
||||||
|
}
|
||||||
|
|
|
@ -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))
|
||||||
{
|
{
|
||||||
|
|
|
@ -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)
|
||||||
{
|
{
|
||||||
|
|
12
src/User.c
12
src/User.c
|
@ -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
|
||||||
{
|
{
|
||||||
|
|
|
@ -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 */
|
||||||
|
|
Loading…
Reference in a new issue