on master help sign in
Fix safeCompare timing leak, federation ack message loss, gossip hop-0 rejection, federation retry, and gossip source spoofing
5b68b19iko
server.knot+21−33
@@ -137,17 +137,12 @@ verifyFederationAuth = \authorization ->
137
137
138clampTtl = \ttl -> if ttl < 1 then 1 else if ttl > maxPresenceTtl then maxPresenceTtl else ttl
138clampTtl = \ttl -> if ttl < 1 then 1 else if ttl > maxPresenceTtl then maxPresenceTtl else ttl
139
139
140−sameLength = \s1 s2 ->
141− if s1 == "" && s2 == "" then True {}
142− else if s1 == "" || s2 == "" then False {}
143− else sameLength (drop 1 s1) (drop 1 s2)
144−
145safeCompareLoop = \s1 s2 result ->
140safeCompareLoop = \s1 s2 result ->
146 if s1 == "" && s2 == ""
141 if s1 == "" && s2 == ""
147 then result
142 then result
148 else safeCompareLoop (drop 1 s1) (drop 1 s2) (result + (if take 1 s1 == take 1 s2 then 0 else 1))
143 else safeCompareLoop (drop 1 s1) (drop 1 s2) (result + (if take 1 s1 == take 1 s2 then 0 else 1))
149
144
150−safeCompare = \a b -> sameLength a b && safeCompareLoop a b 0 == 0
145+safeCompare = \a b -> safeCompareLoop a b 0 == 0
151
146
152sendToEach = \servers action ->
147sendToEach = \servers action ->
153 if count servers == 0
148 if count servers == 0
@@ -160,7 +155,9 @@ sendToEach = \servers action ->
160 else do
155 else do
161 catch (action server) (\err -> do
156 catch (action server) (\err -> do
162 println ("federation: error reaching " ++ server ++ ": " ++ show err)
157 println ("federation: error reaching " ++ server ++ ": " ++ show err)
158+ catch (action server) (\err2 -> do
159+ println ("federation: retry failed for " ++ server ++ ": " ++ show err2)
163− yield {})
160+ yield {}))
164 sendToEach rest action
161 sendToEach rest action
165
162
166pruneExpired = \t -> do
163pruneExpired = \t -> do
@@ -517,7 +514,7 @@ serve = \req -> case req of
517 t <- now
514 t <- now
518 let authorized = verifyFederationAuth authorization
515 let authorized = verifyFederationAuth authorization
519 let cappedHops = if hopsRemaining > 5 then 5 else hopsRemaining
516 let cappedHops = if hopsRemaining > 5 then 5 else hopsRemaining
520− let validRequest = authorized && cappedHops >= 1 && not (fromServer == "") && not (fromServer == serverHost) && not (tooLong fromServer maxPubkeyLen) && not (containsBadUrlChar fromServer) && count events <= maxGossipEvents
517+ let validRequest = authorized && cappedHops >= 0 && not (fromServer == "") && not (fromServer == serverHost) && not (tooLong fromServer maxPubkeyLen) && not (containsBadUrlChar fromServer) && count events <= maxGossipEvents
521 let onlineEntries = do
518 let onlineEntries = do
522 e <- events
519 e <- events
523 where validRequest
520 where validRequest
@@ -579,8 +576,12 @@ serve = \req -> case req of
579 gossipResult <- atomic do
576 gossipResult <- atomic do
580 pr <- pruneExpired t
577 pr <- pruneExpired t
581 presence <- *presence
578 presence <- *presence
582− let appliedOnline = do
579+ let trustedDeduped = do
583 e <- deduped
580 e <- deduped
581+ where e.server == fromServer || count (do p <- presence; where p.pubkey == e.pubkey; where p.server == e.server; yield p) > 0
582+ yield e
583+ let appliedOnline = do
584+ e <- trustedDeduped
584 where count (do
585 where count (do
585 p <- presence
586 p <- presence
586 where p.pubkey == e.pubkey
587 where p.pubkey == e.pubkey
@@ -601,10 +602,10 @@ serve = \req -> case req of
601 p <- acc
602 p <- acc
602 where not (p.pubkey == e.pubkey && p.server == e.server)
603 where not (p.pubkey == e.pubkey && p.server == e.server)
603 yield p)
604 yield p)
604− [e]) presence deduped
605+ [e]) presence trustedDeduped
605 let offlineOnlyKeys = do
606 let offlineOnlyKeys = do
606 k <- affectedKeys
607 k <- affectedKeys
607− where count (do d <- deduped; where d.pubkey == k.pubkey; where d.server == k.server; yield d) == 0
608+ where count (do d <- trustedDeduped; where d.pubkey == k.pubkey; where d.server == k.server; yield d) == 0
608 yield k
609 yield k
609 let appliedOffline = do
610 let appliedOffline = do
610 k <- offlineOnlyKeys
611 k <- offlineOnlyKeys
@@ -649,7 +650,7 @@ serve = \req -> case req of
649 where not (p.pubkey == e.pubkey && p.server == e.server)
650 where not (p.pubkey == e.pubkey && p.server == e.server)
650 yield p)
651 yield p)
651 [e]
652 [e]
652− else acc) presence deduped
653+ else acc) presence trustedDeduped
653 let updatesOnlyAfterOffline = if withinCapacity then afterOffline
654 let updatesOnlyAfterOffline = if withinCapacity then afterOffline
654 else diff updatesOnlyMerged (do
655 else diff updatesOnlyMerged (do
655 p <- updatesOnlyMerged
656 p <- updatesOnlyMerged
@@ -665,7 +666,7 @@ serve = \req -> case req of
665 yield p)
666 yield p)
666 let appliedOnlineUpdates = if withinCapacity then appliedOnline
667 let appliedOnlineUpdates = if withinCapacity then appliedOnline
667 else do
668 else do
668− e <- deduped
669+ e <- trustedDeduped
669 where count (do p <- presence; where p.pubkey == e.pubkey; where p.server == e.server; yield p) > 0
670 where count (do p <- presence; where p.pubkey == e.pubkey; where p.server == e.server; yield p) > 0
670 where count (do
671 where count (do
671 p <- presence
672 p <- presence
@@ -768,29 +769,16 @@ serve = \req -> case req of
768 let validAckFields = authorized && originServer == serverHost && not (fromServer == "") && not (fromServer == serverHost) && not (tooLong fromServer maxPubkeyLen) && not (containsBadUrlChar fromServer)
769 let validAckFields = authorized && originServer == serverHost && not (fromServer == "") && not (fromServer == serverHost) && not (tooLong fromServer maxPubkeyLen) && not (containsBadUrlChar fromServer)
769 pruneResult <- atomic do
770 pruneResult <- atomic do
770 pr <- pruneExpired t
771 pr <- pruneExpired t
771− messages <- *messages
772− presence <- *presence
773 forwards <- *forwards
772 forwards <- *forwards
774 let validAck = validAckFields
773 let validAck = validAckFields
775− set *messages = if validAck
774+ set *forwards = if validAck
776− then diff messages (do
775+ then diff forwards (do
777− m <- messages
776+ f <- forwards
778 mid <- dedupedIds
777 mid <- dedupedIds
779− where m.messageId == mid
778+ where f.messageId == mid
780− where m.originServer == ""
781− where count (do
782− f <- forwards
783− where f.messageId == mid
784− where f.toServer == fromServer
779+ where f.toServer == fromServer
785− yield f) > 0
780+ yield f)
786− where count (do
781+ else forwards
787− p <- presence
788− where p.pubkey == m.toKey
789− where p.server == serverHost
790− where p.expiresAt > t
791− yield p) == 0
792− yield m)
793− else messages
794 yield {peerServers: pr.peerServers, expiredLocal: pr.expiredLocal, validAck: validAck}
782 yield {peerServers: pr.peerServers, expiredLocal: pr.expiredLocal, validAck: validAck}
795 respond {ok: pruneResult.validAck}
783 respond {ok: pruneResult.validAck}
796 broadcastOfflineGossip t pruneResult.peerServers pruneResult.expiredLocal
784 broadcastOfflineGossip t pruneResult.peerServers pruneResult.expiredLocal