diff --git a/Schema/PduV1.json b/Schema/PduV1.json index 6a46feb..6e24bda 100644 --- a/Schema/PduV1.json +++ b/Schema/PduV1.json @@ -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 } } }, diff --git a/src/Main.c b/src/Main.c index 3d90a32..7eed17a 100644 --- a/src/Main.c +++ b/src/Main.c @@ -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."); diff --git a/src/Room.c b/src/Room.c index 856fbb9..e9ab1ce 100644 --- a/src/Room.c +++ b/src/Room.c @@ -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; +} diff --git a/src/Routes/RouteActRoom.c b/src/Routes/RouteActRoom.c index f41f483..a8b1996 100644 --- a/src/Routes/RouteActRoom.c +++ b/src/Routes/RouteActRoom.c @@ -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)) { diff --git a/src/Routes/RouteSendEvent.c b/src/Routes/RouteSendEvent.c index 784f228..807da60 100644 --- a/src/Routes/RouteSendEvent.c +++ b/src/Routes/RouteSendEvent.c @@ -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) { diff --git a/src/User.c b/src/User.c index 025a1fe..2cec874 100644 --- a/src/User.c +++ b/src/User.c @@ -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 { diff --git a/src/include/Room.h b/src/include/Room.h index e9c93e3..f9c6241 100644 --- a/src/include/Room.h +++ b/src/include/Room.h @@ -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 */