on master help sign in
Fix federation ack race, presence capacity, auth case, gossip spoofing, and URL injection
1496a57iko
server.knot+28−25
@@ -101,6 +101,12 @@ tooLong = \s maxLen -> not (drop maxLen s == "")
101
101
102isValidHex = \s -> not (s == "") && bytesToHex (bytesFromHex s) == s
102isValidHex = \s -> not (s == "") && bytesToHex (bytesFromHex s) == s
103
103
104+containsBadUrlChar = \s ->
105+ if s == "" then False {}
106+ else if take 1 s == "/" || take 1 s == "?" || take 1 s == "#" || take 1 s == " " || take 1 s == "@"
107+ then True {}
108+ else containsBadUrlChar (drop 1 s)
109+
104lookupSession = \sessions token t ->
110lookupSession = \sessions token t ->
105 single (do
111 single (do
106 s <- sessions
112 s <- sessions
@@ -109,7 +115,7 @@ lookupSession = \sessions token t ->
109 yield s)
115 yield s)
110
116
111extractToken = \authHeader ->
117extractToken = \authHeader ->
112− if take 7 authHeader == "Bearer "
118+ if take 7 authHeader == "Bearer " || take 7 authHeader == "bearer "
113 then drop 7 authHeader
119 then drop 7 authHeader
114 else ""
120 else ""
115
121
@@ -279,10 +285,15 @@ serve = \req -> case req of
279 p <- presence
285 p <- presence
280 where not (p.pubkey == pubkey && p.server == serverHost)
286 where not (p.pubkey == pubkey && p.server == serverHost)
281 yield p
287 yield p
288+ let oldestPresenceExp = fold (\acc p -> if acc == -1 || p.expiresAt < acc then p.expiresAt else acc) -1 presenceWithout
289+ let presenceEvicted = if count presenceWithout >= maxPresenceEntries && oldestPresenceExp >= 0
290+ then (fold (\acc p ->
291+ if not acc.removed && p.expiresAt == oldestPresenceExp
292+ then {removed: True {}, items: acc.items}
293+ else {removed: acc.removed, items: union acc.items [p]}) {removed: False {}, items: []} presenceWithout).items
294+ else presenceWithout
282 let updatedPresence = if sigValid
295 let updatedPresence = if sigValid
283− then if count presenceWithout < maxPresenceEntries
284− then union presenceWithout [{pubkey: pubkey, server: serverHost, expiresAt: expiry, timestamp: t}]
296+ then union presenceEvicted [{pubkey: pubkey, server: serverHost, expiresAt: expiry, timestamp: t}]
285− else presenceWithout
286 else presence
297 else presence
287 set *presence = updatedPresence
298 set *presence = updatedPresence
288 let peerServers = if sigValid && not (federationSecret == "")
299 let peerServers = if sigValid && not (federationSecret == "")
@@ -492,7 +503,7 @@ serve = \req -> case req of
492 t <- now
503 t <- now
493 let authorized = verifyFederationAuth authorization
504 let authorized = verifyFederationAuth authorization
494 let cappedHops = if hopsRemaining > 5 then 5 else hopsRemaining
505 let cappedHops = if hopsRemaining > 5 then 5 else hopsRemaining
495− let validRequest = authorized && cappedHops >= 1 && not (fromServer == "") && not (fromServer == serverHost) && not (tooLong fromServer maxPubkeyLen) && count events <= maxGossipEvents
506+ let validRequest = authorized && cappedHops >= 1 && not (fromServer == "") && not (fromServer == serverHost) && not (tooLong fromServer maxPubkeyLen) && not (containsBadUrlChar fromServer) && count events <= maxGossipEvents
496 let onlineEntries = do
507 let onlineEntries = do
497 e <- events
508 e <- events
498 where validRequest
509 where validRequest
@@ -501,8 +512,9 @@ serve = \req -> case req of
501 where not (e.pubkey == "")
512 where not (e.pubkey == "")
502 where not (tooLong e.pubkey maxPubkeyLen)
513 where not (tooLong e.pubkey maxPubkeyLen)
503 where not (tooLong e.server maxPubkeyLen)
514 where not (tooLong e.server maxPubkeyLen)
515+ where not (containsBadUrlChar e.server)
504 where isValidHex e.pubkey
516 where isValidHex e.pubkey
505− where cappedHops < 2 || e.server == fromServer
517+ where e.server == fromServer
506 where not (e.server == serverHost)
518 where not (e.server == serverHost)
507 where e.timestamp <= t + clockSkewTolerance
519 where e.timestamp <= t + clockSkewTolerance
508 where e.timestamp + (clampTtl e.ttl) * 1000 > t
520 where e.timestamp + (clampTtl e.ttl) * 1000 > t
@@ -515,8 +527,9 @@ serve = \req -> case req of
515 where not (e.pubkey == "")
527 where not (e.pubkey == "")
516 where not (tooLong e.pubkey maxPubkeyLen)
528 where not (tooLong e.pubkey maxPubkeyLen)
517 where not (tooLong e.server maxPubkeyLen)
529 where not (tooLong e.server maxPubkeyLen)
530+ where not (containsBadUrlChar e.server)
518 where isValidHex e.pubkey
531 where isValidHex e.pubkey
519− where cappedHops < 2 || e.server == fromServer
532+ where e.server == fromServer
520 where not (e.server == serverHost)
533 where not (e.server == serverHost)
521 where e.timestamp <= t + clockSkewTolerance
534 where e.timestamp <= t + clockSkewTolerance
522 where t - e.timestamp < (clampTtl e.ttl) * 1000
535 where t - e.timestamp < (clampTtl e.ttl) * 1000
@@ -666,8 +679,9 @@ serve = \req -> case req of
666 where not (e.pubkey == "")
679 where not (e.pubkey == "")
667 where not (tooLong e.pubkey maxPubkeyLen)
680 where not (tooLong e.pubkey maxPubkeyLen)
668 where not (tooLong e.server maxPubkeyLen)
681 where not (tooLong e.server maxPubkeyLen)
682+ where not (containsBadUrlChar e.server)
669 where isValidHex e.pubkey
683 where isValidHex e.pubkey
670− where cappedHops < 2 || e.server == fromServer
684+ where e.server == fromServer
671 where not (e.server == serverHost)
685 where not (e.server == serverHost)
672 where e.timestamp <= t + clockSkewTolerance
686 where e.timestamp <= t + clockSkewTolerance
673 where e.timestamp + (clampTtl e.ttl) * 1000 > t
687 where e.timestamp + (clampTtl e.ttl) * 1000 > t
@@ -702,19 +716,13 @@ serve = \req -> case req of
702 ForwardMessage {toKey, fromKey, encryptedBlob, timestamp, originServer, messageId, authorization, respond} -> do
716 ForwardMessage {toKey, fromKey, encryptedBlob, timestamp, originServer, messageId, authorization, respond} -> do
703 t <- now
717 t <- now
704 let authorized = verifyFederationAuth authorization
718 let authorized = verifyFederationAuth authorization
705− let validOrigin = not (originServer == "") && not (originServer == serverHost) && not (tooLong originServer maxPubkeyLen)
719+ let validOrigin = not (originServer == "") && not (originServer == serverHost) && not (tooLong originServer maxPubkeyLen) && not (containsBadUrlChar originServer)
706 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
720 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
707 let clampedTs = if timestamp > t then t else if timestamp < t - messageMaxAge then t else timestamp
721 let clampedTs = if timestamp > t then t else if timestamp < t - messageMaxAge then t else timestamp
708 result <- atomic do
722 result <- atomic do
709 pr <- pruneExpired t
723 pr <- pruneExpired t
710− presence <- *presence
711 messages <- *messages
724 messages <- *messages
712 seqCounter <- *seqCounter
725 seqCounter <- *seqCounter
713− let knownPeer = count (do
714− p <- presence
715− where p.server == originServer
716− where p.expiresAt > t
717− yield p) > 0
718 let seqTs = if t > seqCounter then t else seqCounter + 1
726 let seqTs = if t > seqCounter then t else seqCounter + 1
719 let exists = count (do
727 let exists = count (do
720 m <- messages
728 m <- messages
@@ -722,15 +730,15 @@ serve = \req -> case req of
722 where m.toKey == toKey
730 where m.toKey == toKey
723 where m.receivedAt >= t - messageMaxAge
731 where m.receivedAt >= t - messageMaxAge
724 yield m) > 0
732 yield m) > 0
725− let shouldInsert = authorized && validOrigin && validFields && knownPeer && not exists
733+ let shouldInsert = authorized && validOrigin && validFields && not exists
726 set *seqCounter = if shouldInsert then seqTs else seqCounter
734 set *seqCounter = if shouldInsert then seqTs else seqCounter
727 set *messages = if shouldInsert
735 set *messages = if shouldInsert
728 then union messages
736 then union messages
729 [{messageId: messageId, toKey: toKey, fromKey: fromKey,
737 [{messageId: messageId, toKey: toKey, fromKey: fromKey,
730 encryptedBlob: encryptedBlob, timestamp: clampedTs, originServer: originServer, receivedAt: seqTs}]
738 encryptedBlob: encryptedBlob, timestamp: clampedTs, originServer: originServer, receivedAt: seqTs}]
731 else messages
739 else messages
732− yield {peerServers: pr.peerServers, expiredLocal: pr.expiredLocal, knownPeer: knownPeer}
740+ yield {peerServers: pr.peerServers, expiredLocal: pr.expiredLocal}
733− respond {ok: authorized && validOrigin && validFields && result.knownPeer}
741+ respond {ok: authorized && validOrigin && validFields}
734 broadcastOfflineGossip t result.peerServers result.expiredLocal
742 broadcastOfflineGossip t result.peerServers result.expiredLocal
735
743
736 FederationAck {messageIds, originServer, fromServer, authorization, respond} -> do
744 FederationAck {messageIds, originServer, fromServer, authorization, respond} -> do
@@ -743,18 +751,13 @@ serve = \req -> case req of
743 else if not (isValidHex mid) then acc
751 else if not (isValidHex mid) then acc
744 else if count (do a <- acc; where a == mid; yield a) > 0 then acc
752 else if count (do a <- acc; where a == mid; yield a) > 0 then acc
745 else union acc [mid]) [] messageIds
753 else union acc [mid]) [] messageIds
746− let validAckFields = authorized && originServer == serverHost && not (fromServer == "") && not (fromServer == serverHost) && not (tooLong fromServer maxPubkeyLen)
754+ let validAckFields = authorized && originServer == serverHost && not (fromServer == "") && not (fromServer == serverHost) && not (tooLong fromServer maxPubkeyLen) && not (containsBadUrlChar fromServer)
747 pruneResult <- atomic do
755 pruneResult <- atomic do
748 pr <- pruneExpired t
756 pr <- pruneExpired t
749 messages <- *messages
757 messages <- *messages
750 presence <- *presence
758 presence <- *presence
751 forwards <- *forwards
759 forwards <- *forwards
752− let knownPeer = count (do
753− p <- presence
754− where p.server == fromServer
755− where p.expiresAt > t
756− yield p) > 0
757− let validAck = validAckFields && knownPeer
760+ let validAck = validAckFields
758 set *messages = if validAck
761 set *messages = if validAck
759 then diff messages (do
762 then diff messages (do
760 m <- messages
763 m <- messages