@@ -22,6 +22,10 @@ maxPresenceEntries = 10000
2222maxMessagesPerRecipient = 500
2323clockSkewTolerance = 5000 -- 5 seconds tolerance for clock skew between federated servers
2424maxForwardsEntries = 10000
25+maxServerNameLen = 128
26+maxMessageIdLen = 128
27+maxTokenLen = 256
28+maxChallenges = 10000
2529
2630-- ============================================
2731-- Types
@@ -174,8 +178,43 @@ containsDoubleColon = \s ->
174178 else if take 1 s == ":" && take 1 (drop 1 s) == ":" then True {}
175179 else containsDoubleColon (drop 1 s)
176180
181+containsColon = \s ->
182+ if s == "" then False {}
183+ else if take 1 s == ":" then True {}
184+ else containsColon (drop 1 s)
185+
186+containsMultipleColons = \s ->
187+ if s == "" then False {}
188+ else if take 1 s == ":" then containsColon (drop 1 s)
189+ else containsMultipleColons (drop 1 s)
190+
191+hasOctalOctet = \s ->
192+ if take 1 s == "0" && isDigitChar (take 1 (drop 1 s)) then True {}
193+ else hasOctalOctetAfterDot s
194+
195+hasOctalOctetAfterDot = \s ->
196+ if s == "" then False {}
197+ else if take 1 s == "." && take 1 (drop 1 s) == "0" && isDigitChar (take 1 (drop 2 s)) then True {}
198+ else hasOctalOctetAfterDot (drop 1 s)
199+
200+hasHexPrefix = \s ->
201+ if take 2 s == "0x" || take 2 s == "0X" then True {}
202+ else hasHexPrefixAfterDot s
203+
204+hasHexPrefixAfterDot = \s ->
205+ if s == "" then False {}
206+ else if take 1 s == "." && (take 2 (drop 1 s) == "0x" || take 2 (drop 1 s) == "0X") then True {}
207+ else hasHexPrefixAfterDot (drop 1 s)
208+
209+isAllDigitsUntilColonOrEnd = \s ->
210+ if s == "" then True {}
211+ else if take 1 s == ":" then True {}
212+ else if isDigitChar (take 1 s) then isAllDigitsUntilColonOrEnd (drop 1 s)
213+ else False {}
214+
177215isBadServerName = \s ->
178− containsBadUrlChar s
216+ s == ""
217+ || containsBadUrlChar s
179218 || s == "localhost" || take 10 s == "localhost:"
180219 || take 4 s == "127." || take 3 s == "10."
181220 || take 8 s == "192.168." || take 7 s == "169.254."
@@ -187,7 +226,10 @@ isBadServerName = \s ->
187226 || take 7 s == "198.18." || take 7 s == "198.19."
188227 || take 2 s == "::"
189228 || isCgnatRange s
190− || containsDoubleColon s
229+ || containsMultipleColons s
230+ || hasOctalOctet s
231+ || hasHexPrefix s
232+ || (isDigitChar (take 1 s) && isAllDigitsUntilColonOrEnd s)
191233
192234lookupSession = \sessions token t ->
193235 single (do
@@ -204,6 +246,7 @@ extractToken = \authHeader ->
204246 && (take 1 (drop 4 authHeader) == "e" || take 1 (drop 4 authHeader) == "E")
205247 && (take 1 (drop 5 authHeader) == "r" || take 1 (drop 5 authHeader) == "R")
206248 && take 1 (drop 6 authHeader) == " "
249+ && not (tooLong (drop 7 authHeader) maxTokenLen)
207250 then drop 7 authHeader
208251 else ""
209252
@@ -234,8 +277,10 @@ sendToEach = \servers action ->
234277 else do
235278 catch (action server) (\err -> do
236279 println ("federation: error reaching " ++ server ++ ", retrying (1/2): " ++ show err)
280+ sleep 1000
237281 catch (action server) (\err2 -> do
238282 println ("federation: retry 1 failed for " ++ server ++ ", retrying (2/2): " ++ show err2)
283+ sleep 2000
239284 catch (action server) (\err3 -> do
240285 println ("federation: all retries exhausted for " ++ server ++ ": " ++ show err3)
241286 yield {})))
@@ -336,8 +381,17 @@ serve = \req -> case req of
336381 then {removed: True {}, items: acc.items}
337382 else {removed: acc.removed, items: union acc.items [c]}) {removed: False {}, items: []} challenges
338383 else {removed: False {}, items: challenges}
384+ let globalOldestExpiry = if count cappedChallenges.items >= maxChallenges
385+ then fold (\acc c -> if acc == -1 || c.expiresAt < acc then c.expiresAt else acc) -1 cappedChallenges.items
386+ else -1
387+ let globalCapped = if globalOldestExpiry >= 0
388+ then (fold (\acc c ->
389+ if not acc.removed && c.expiresAt == globalOldestExpiry
390+ then {removed: True {}, items: acc.items}
391+ else {removed: acc.removed, items: union acc.items [c]}) {removed: False {}, items: []} cappedChallenges.items).items
392+ else cappedChallenges.items
339393 set *challenges = union
340− cappedChallenges.items
394+ globalCapped
341395 [{pubkey: pubkey, challenge: challenge, expiresAt: t + 60000}]
342396 yield pr
343397 respond {challenge: challenge}
@@ -546,7 +600,7 @@ serve = \req -> case req of
546600 let dedupedIds = fold (\acc mid ->
547601 if mid == "" then acc
548602 else if count acc >= maxMessageIds then acc
549− else if tooLong mid maxPubkeyLen then acc
603+ else if tooLong mid maxMessageIdLen then acc
550604 else if not (isValidHex mid) then acc
551605 else if count (do a <- acc; where a == normalizeHex mid; yield a) > 0 then acc
552606 else union acc [normalizeHex mid]) [] messageIds
@@ -566,10 +620,15 @@ serve = \req -> case req of
566620 set *messages = case session of
567621 Nothing {} -> messages
568622 Just {value: _} -> diff messages ackedMessages
623+ presence <- *presence
624+ let knownPeers = fold (\acc p ->
625+ if p.server == serverHost then acc
626+ else if count (do a <- acc; where a == p.server; yield a) > 0 then acc
627+ else union acc [p.server]) [] presence
569628 let ok = case session of
570629 Nothing {} -> False {}
571630 _ -> True {}
572− yield {ok: ok, ackedMessages: ackedMessages, expiredLocal: pr.expiredLocal, peerServers: pr.peerServers}
631+ yield {ok: ok, ackedMessages: ackedMessages, expiredLocal: pr.expiredLocal, peerServers: pr.peerServers, knownPeers: knownPeers}
573632 let fedMessages = do
574633 m <- result.ackedMessages
575634 where not (m.originServer == "")
@@ -577,6 +636,7 @@ serve = \req -> case req of
577636 yield m
578637 let originServers = fold (\acc m ->
579638 if count (do a <- acc; where a == m.originServer; yield a) > 0 then acc
639+ else if count (do k <- result.knownPeers; where k == m.originServer; yield k) == 0 then acc
580640 else union acc [m.originServer]) [] fedMessages
581641 respond {ok: result.ok}
582642 if result.ok && count originServers > 0 && not (federationSecret == "")
@@ -624,7 +684,7 @@ serve = \req -> case req of
624684 t <- now
625685 let authorized = verifyFederationAuth authorization
626686 let cappedHops = if hopsRemaining > 5 then 5 else hopsRemaining
627− let validRequest = authorized && cappedHops >= 1 && not (fromServer == "") && not (fromServer == serverHost) && not (tooLong fromServer maxPubkeyLen) && not (isBadServerName fromServer) && count events <= maxGossipEvents
687+ let validRequest = authorized && cappedHops >= 1 && not (fromServer == "") && not (fromServer == serverHost) && not (tooLong fromServer maxServerNameLen) && not (isBadServerName fromServer) && count events <= maxGossipEvents
628688 let onlineEntries = do
629689 e <- events
630690 where validRequest
@@ -632,7 +692,7 @@ serve = \req -> case req of
632692 where not (e.server == "")
633693 where not (e.pubkey == "")
634694 where not (tooLong e.pubkey maxPubkeyLen)
635− where not (tooLong e.server maxPubkeyLen)
695+ where not (tooLong e.server maxServerNameLen)
636696 where not (isBadServerName e.server)
637697 where isValidHex e.pubkey
638698 where not (e.server == serverHost)
@@ -646,7 +706,7 @@ serve = \req -> case req of
646706 where not (e.server == "")
647707 where not (e.pubkey == "")
648708 where not (tooLong e.pubkey maxPubkeyLen)
649− where not (tooLong e.server maxPubkeyLen)
709+ where not (tooLong e.server maxServerNameLen)
650710 where not (isBadServerName e.server)
651711 where isValidHex e.pubkey
652712 where not (e.server == serverHost)
@@ -823,7 +883,7 @@ serve = \req -> case req of
823883 where not (e.server == "")
824884 where not (e.pubkey == "")
825885 where not (tooLong e.pubkey maxPubkeyLen)
826− where not (tooLong e.server maxPubkeyLen)
886+ where not (tooLong e.server maxServerNameLen)
827887 where not (isBadServerName e.server)
828888 where isValidHex e.pubkey
829889 where not (e.server == serverHost)
@@ -861,14 +921,14 @@ serve = \req -> case req of
861921 broadcastOfflineGossip t gossipResult.peerServers gossipResult.expiredLocal
862922
863923 ForwardMessage {toKey, fromKey, encryptedBlob, timestamp, originServer, messageId, authorization, respond} -> do
864− let validFields = not (toKey == "") && not (fromKey == "") && not (encryptedBlob == "") && not (messageId == "") && not (tooLong toKey maxPubkeyLen) && not (tooLong fromKey maxPubkeyLen) && not (tooLong encryptedBlob maxBlobLen) && not (tooLong messageId maxPubkeyLen) && isValidHex toKey && isValidHex fromKey && isValidHex encryptedBlob && isValidHex messageId
924+ let validFields = not (toKey == "") && not (fromKey == "") && not (encryptedBlob == "") && not (messageId == "") && not (tooLong toKey maxPubkeyLen) && not (tooLong fromKey maxPubkeyLen) && not (tooLong encryptedBlob maxBlobLen) && not (tooLong messageId maxMessageIdLen) && isValidHex toKey && isValidHex fromKey && isValidHex encryptedBlob && isValidHex messageId
865925 let toKey = if isValidHex toKey then normalizeHex toKey else toKey
866926 let fromKey = if isValidHex fromKey then normalizeHex fromKey else fromKey
867927 let encryptedBlob = if isValidHex encryptedBlob then normalizeHex encryptedBlob else encryptedBlob
868928 let messageId = if isValidHex messageId then normalizeHex messageId else messageId
869929 t <- now
870930 let authorized = verifyFederationAuth authorization
871− let validOrigin = not (originServer == "") && not (originServer == serverHost) && not (tooLong originServer maxPubkeyLen) && not (isBadServerName originServer)
931+ let validOrigin = not (originServer == "") && not (originServer == serverHost) && not (tooLong originServer maxServerNameLen) && not (isBadServerName originServer)
872932 let clampedTs = if timestamp > t then t else if timestamp < t - messageMaxAge then t else timestamp
873933 result <- atomic do
874934 pr <- pruneExpired t
@@ -903,11 +963,11 @@ serve = \req -> case req of
903963 let dedupedIds = fold (\acc mid ->
904964 if mid == "" then acc
905965 else if count acc >= maxMessageIds then acc
906− else if tooLong mid maxPubkeyLen then acc
966+ else if tooLong mid maxMessageIdLen then acc
907967 else if not (isValidHex mid) then acc
908968 else if count (do a <- acc; where a == normalizeHex mid; yield a) > 0 then acc
909969 else union acc [normalizeHex mid]) [] messageIds
910− let validAckFields = authorized && originServer == serverHost && not (fromServer == "") && not (fromServer == serverHost) && not (tooLong fromServer maxPubkeyLen) && not (isBadServerName fromServer)
970+ let validAckFields = authorized && originServer == serverHost && not (fromServer == "") && not (fromServer == serverHost) && not (tooLong fromServer maxServerNameLen) && not (isBadServerName fromServer)
911971 pruneResult <- atomic do
912972 pr <- pruneExpired t
913973 forwards <- *forwards