@@ -537,7 +537,8 @@ runNtfClientTransport th@THandle {params} = do
537537 s <- asks subscriber
538538 ps <- asks pushServer
539539 expCfg <- asks $ inactiveClientExpiration . config
540- raceAny_ ([liftIO $ send th c, client c s ps, receive th c] <> disconnectThread_ c expCfg)
540+ st <- asks store
541+ raceAny_ ([liftIO $ send th c, client c s ps, liftIO $ receive st th c] <> disconnectThread_ c expCfg)
541542 `finally` liftIO (clientDisconnected c)
542543 where
543544 disconnectThread_ c (Just expCfg) = [liftIO $ disconnectTransport th (rcvActiveAt c) (sndActiveAt c) expCfg (pure True )]
@@ -546,10 +547,10 @@ runNtfClientTransport th@THandle {params} = do
546547clientDisconnected :: NtfServerClient -> IO ()
547548clientDisconnected NtfServerClient {connected} = atomically $ writeTVar connected False
548549
549- receive :: Transport c => THandleNTF c 'TServer -> NtfServerClient -> M ()
550- receive th@ THandle {params = THandleParams {thAuth}} NtfServerClient {rcvQ, sndQ, rcvActiveAt} = forever $ do
551- ts <- L. toList <$> liftIO ( tGet th)
552- atomically . (writeTVar rcvActiveAt $! ) =<< liftIO getSystemTime
550+ receive :: Transport c => NtfPostgresStore -> THandleNTF c 'TServer -> NtfServerClient -> IO ()
551+ receive st th@ THandle {params = THandleParams {thAuth}} NtfServerClient {rcvQ, sndQ, rcvActiveAt} = forever $ do
552+ ts <- L. toList <$> tGet th
553+ atomically . (writeTVar rcvActiveAt $! ) =<< getSystemTime
553554 (errs, cmds) <- partitionEithers <$> mapM cmdAction ts
554555 write sndQ errs
555556 write rcvQ cmds
@@ -560,70 +561,60 @@ receive th@THandle {params = THandleParams {thAuth}} NtfServerClient {rcvQ, sndQ
560561 logError $ " invalid client request: " <> tshow e
561562 pure $ Left (corrId, entId, NRErr e)
562563 Right cmd ->
563- verified =<< verifyNtfTransmission ((,C. cbNonce (SMP. bs corrId)) <$> thAuth) t cmd
564+ verified =<< verifyNtfTransmission st ((,C. cbNonce (SMP. bs corrId)) <$> thAuth) t cmd
564565 where
565566 verified = \ case
566567 VRVerified req -> pure $ Right req
567- VRFailed -> do
568+ VRFailed e -> do
568569 logError " unauthorized client request"
569- pure $ Left (corrId, entId, NRErr AUTH )
570+ pure $ Left (corrId, entId, NRErr e )
570571 write q = mapM_ (atomically . writeTBQueue q) . L. nonEmpty
571572
572573send :: Transport c => THandleNTF c 'TServer -> NtfServerClient -> IO ()
573574send h@ THandle {params} NtfServerClient {sndQ, sndActiveAt} = forever $ do
574575 ts <- atomically $ readTBQueue sndQ
575- void . liftIO $ tPut h $ L. map (\ t -> Right (Nothing , encodeTransmission params t)) ts
576- atomically . (writeTVar sndActiveAt $! ) =<< liftIO getSystemTime
577-
578- data VerificationResult = VRVerified NtfRequest | VRFailed
579-
580- verifyNtfTransmission :: Maybe (THandleAuth 'TServer, C. CbNonce ) -> SignedTransmission ErrorType NtfCmd -> NtfCmd -> M VerificationResult
581- verifyNtfTransmission auth_ (tAuth, authorized, (corrId, entId, _)) cmd = do
582- st <- asks store
583- case cmd of
584- -- TODO [ntfdb] this looks suspicious, as if it can prevent repeated registrations
585- NtfCmd SToken c@ (TNEW tkn@ (NewNtfTkn _ k _)) -> do
586- r_ <- liftIO $ getNtfTokenRegistration st tkn
587- pure $
588- if verifyCmdAuthorization auth_ tAuth authorized k
589- then case r_ of
590- Right t@ NtfTknRec {tknVerifyKey}
591- -- keys will be the same because of condition in `getNtfTokenRegistration`
592- | k == tknVerifyKey -> VRVerified $ tknCmd t c
593- | otherwise -> VRFailed
594- Left _ -> VRVerified (NtfReqNew corrId (ANE SToken tkn))
595- else VRFailed
596- NtfCmd SToken c -> do
597- t_ <- liftIO $ getNtfToken st entId
598- verifyToken_' t_ (`tknCmd` c)
599- NtfCmd SSubscription c@ (SNEW sub@ (NewNtfSub tknId smpQueue _)) ->
600- liftIO $ verify <$> findNtfSubscription st tknId smpQueue
601- where
602- verify = \ case
603- Right (t, s_) -> verifyToken t $ case s_ of
604- Nothing -> NtfReqNew corrId (ANE SSubscription sub)
605- Just s -> subCmd s c
606- -- TODO [ntfdb] it should simply return error if it is not AUTH
607- Left _ -> maybe False (dummyVerifyCmd auth_ authorized) tAuth `seq` VRFailed
608- NtfCmd SSubscription PING -> pure $ VRVerified $ NtfReqPing corrId entId
609- NtfCmd SSubscription c -> liftIO $ verify <$> getNtfSubscription st entId
610- where
611- verify = \ case
612- Right (t, s) -> verifyToken t $ subCmd s c
613- -- TODO [ntfdb] it should simply return error if it is not AUTH
614- Left _ -> maybe False (dummyVerifyCmd auth_ authorized) tAuth `seq` VRFailed
576+ void $ tPut h $ L. map (\ t -> Right (Nothing , encodeTransmission params t)) ts
577+ atomically . (writeTVar sndActiveAt $! ) =<< getSystemTime
578+
579+ data VerificationResult = VRVerified NtfRequest | VRFailed ErrorType
580+
581+ verifyNtfTransmission :: NtfPostgresStore -> Maybe (THandleAuth 'TServer, C. CbNonce ) -> SignedTransmission ErrorType NtfCmd -> NtfCmd -> IO VerificationResult
582+ verifyNtfTransmission st auth_ (tAuth, authorized, (corrId, entId, _)) = \ case
583+ NtfCmd SToken c@ (TNEW tkn@ (NewNtfTkn _ k _))
584+ | verifyCmdAuthorization auth_ tAuth authorized k ->
585+ result <$> findNtfTokenRegistration st tkn
586+ | otherwise -> pure $ VRFailed AUTH
587+ where
588+ result = \ case
589+ Right (Just t@ NtfTknRec {tknVerifyKey})
590+ -- keys will be the same because of condition in `findNtfTokenRegistration`
591+ | k == tknVerifyKey -> VRVerified $ tknCmd t c
592+ | otherwise -> VRFailed AUTH
593+ Right Nothing -> VRVerified (NtfReqNew corrId (ANE SToken tkn))
594+ Left e -> VRFailed e
595+ NtfCmd SToken c -> either err verify <$> getNtfToken st entId
596+ where
597+ verify t = verifyToken t $ tknCmd t c
598+ NtfCmd SSubscription c@ (SNEW sub@ (NewNtfSub tknId smpQueue _)) ->
599+ either err verify <$> findNtfSubscription st tknId smpQueue
600+ where
601+ verify (t, s_) = verifyToken t $ case s_ of
602+ Nothing -> NtfReqNew corrId (ANE SSubscription sub)
603+ Just s -> subCmd s c
604+ NtfCmd SSubscription PING -> pure $ VRVerified $ NtfReqPing corrId entId
605+ NtfCmd SSubscription c -> either err verify <$> getNtfSubscription st entId
606+ where
607+ verify (t, s) = verifyToken t $ subCmd s c
615608 where
616609 tknCmd t c = NtfReqCmd SToken (NtfTkn t) (corrId, entId, c)
617610 subCmd s c = NtfReqCmd SSubscription (NtfSub s) (corrId, entId, c)
618- verifyToken_' :: Either ErrorType NtfTknRec -> (NtfTknRec -> NtfRequest ) -> M VerificationResult
619- verifyToken_' t_ result = pure $ case t_ of
620- Right t -> verifyToken t $ result t
621- -- TODO [ntfdb] it should simply return error if it is not AUTH
622- Left _ -> maybe False (dummyVerifyCmd auth_ authorized) tAuth `seq` VRFailed
623611 verifyToken :: NtfTknRec -> NtfRequest -> VerificationResult
624612 verifyToken NtfTknRec {tknVerifyKey} r
625613 | verifyCmdAuthorization auth_ tAuth authorized tknVerifyKey = VRVerified r
626- | otherwise = VRFailed
614+ | otherwise = VRFailed AUTH
615+ err = \ case -- signature verification for AUTH errors mitigates timing attacks for existence checks
616+ AUTH -> maybe False (dummyVerifyCmd auth_ authorized) tAuth `seq` VRFailed AUTH
617+ e -> VRFailed e
627618
628619client :: NtfServerClient -> NtfSubscriber -> NtfPushServer -> M ()
629620client NtfServerClient {rcvQ, sndQ} NtfSubscriber {newSubQ, smpAgent = ca} NtfPushServer {pushQ, intervalNotifiers} =
@@ -669,9 +660,6 @@ client NtfServerClient {rcvQ, sndQ} NtfSubscriber {newSubQ, smpAgent = ca} NtfPu
669660 pure NROk
670661 | otherwise -> do
671662 logDebug " TVFY - incorrect code or token status"
672- liftIO $ print tkn
673- let NtfRegCode c = code
674- liftIO $ print $ B64. encode c
675663 pure $ NRErr AUTH
676664 TCHK -> do
677665 logDebug " TCHK"
@@ -732,17 +720,15 @@ client NtfServerClient {rcvQ, sndQ} NtfSubscriber {newSubQ, smpAgent = ca} NtfPu
732720 atomically $ writeTBQueue newSubQ (srv, [sub])
733721 incNtfStat subCreated
734722 pure $ NRSubId subId
735- -- TODO [ntfdb] we must allow repeated inserts that don't change credentials
736723 False -> pure $ NRErr AUTH
737724 pure (corrId, NoEntity , resp)
738- NtfReqCmd SSubscription (NtfSub NtfSubRec {smpQueue = SMPQueueNtf {smpServer, notifierId}, notifierKey = registeredNKey, subStatus}) (corrId, subId, cmd) -> do
725+ NtfReqCmd SSubscription (NtfSub NtfSubRec {ntfSubId, smpQueue = SMPQueueNtf {smpServer, notifierId}, notifierKey = registeredNKey, subStatus}) (corrId, subId, cmd) -> do
739726 (corrId,subId,) <$> case cmd of
740727 SNEW (NewNtfSub _ _ notifierKey) -> do
741728 logDebug " SNEW - existing subscription"
742- -- possible improvement: retry if subscription failed, if pending or AUTH do nothing
743729 pure $
744730 if notifierKey == registeredNKey
745- then NRSubId subId
731+ then NRSubId ntfSubId
746732 else NRErr AUTH
747733 SCHK -> do
748734 logDebug " SCHK"
0 commit comments