Skip to content

Commit 5e9b164

Browse files
authored
agent: fail when per-connection transport isolation is used with services (#1670)
1 parent 3ccf854 commit 5e9b164

File tree

11 files changed

+49
-42
lines changed

11 files changed

+49
-42
lines changed

src/Simplex/Messaging/Agent.hs

Lines changed: 33 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,7 @@ import Simplex.Messaging.Agent.Store.Entity
194194
import Simplex.Messaging.Agent.Store.Interface (closeDBStore, execSQL, getCurrentMigrations)
195195
import Simplex.Messaging.Agent.Store.Shared (UpMigration (..), upMigration)
196196
import qualified Simplex.Messaging.Agent.TSessionSubs as SS
197-
import Simplex.Messaging.Client (NetworkRequestMode (..), SMPClientError, ServerTransmission (..), ServerTransmissionBatch, nonBlockingWriteTBQueue, smpErrorClientNotice, temporaryClientError, unexpectedResponse)
197+
import Simplex.Messaging.Client (NetworkRequestMode (..), SMPClientError, ServerTransmission (..), ServerTransmissionBatch, TransportSessionMode (..), nonBlockingWriteTBQueue, smpErrorClientNotice, temporaryClientError, unexpectedResponse)
198198
import qualified Simplex.Messaging.Crypto as C
199199
import Simplex.Messaging.Crypto.File (CryptoFile, CryptoFileArgs)
200200
import Simplex.Messaging.Crypto.Ratchet (PQEncryption, PQSupport (..), pattern PQEncOff, pattern PQEncOn, pattern PQSupportOff, pattern PQSupportOn)
@@ -249,13 +249,15 @@ import UnliftIO.STM
249249
type AE a = ExceptT AgentErrorType IO a
250250

251251
-- | Creates an SMP agent client instance
252-
getSMPAgentClient :: AgentConfig -> InitialAgentServers -> DBStore -> Bool -> IO AgentClient
252+
getSMPAgentClient :: AgentConfig -> InitialAgentServers -> DBStore -> Bool -> AE AgentClient
253253
getSMPAgentClient = getSMPAgentClient_ 1
254254
{-# INLINE getSMPAgentClient #-}
255255

256-
getSMPAgentClient_ :: Int -> AgentConfig -> InitialAgentServers -> DBStore -> Bool -> IO AgentClient
257-
getSMPAgentClient_ clientId cfg initServers@InitialAgentServers {smp, xftp, presetServers} store backgroundMode =
258-
newSMPAgentEnv cfg store >>= runReaderT runAgent
256+
getSMPAgentClient_ :: Int -> AgentConfig -> InitialAgentServers -> DBStore -> Bool -> AE AgentClient
257+
getSMPAgentClient_ clientId cfg initServers@InitialAgentServers {smp, xftp, netCfg, useServices, presetServers} store backgroundMode = do
258+
-- This error should be prevented in the app
259+
when (any id useServices && sessionMode netCfg == TSMEntity) $ throwE $ CMD PROHIBITED "newAgentClient"
260+
liftIO $ newSMPAgentEnv cfg store >>= runReaderT runAgent
259261
where
260262
runAgent = do
261263
liftIO $ checkServers "SMP" smp >> checkServers "XFTP" xftp
@@ -594,18 +596,22 @@ testProtocolServer c nm userId srv = withAgentEnv' c $ case protocolTypeI @p of
594596
SPNTF -> runNTFServerTest c nm userId srv
595597

596598
-- | set SOCKS5 proxy on/off and optionally set TCP timeouts for fast network
597-
-- TODO [certs rcv] should fail if any user is enabled to use services and per-connection isolation is chosen
598-
setNetworkConfig :: AgentClient -> NetworkConfig -> IO ()
599+
setNetworkConfig :: AgentClient -> NetworkConfig -> AE ()
599600
setNetworkConfig c@AgentClient {useNetworkConfig, proxySessTs} cfg' = do
600-
ts <- getCurrentTime
601-
changed <- atomically $ do
602-
(_, cfg) <- readTVar useNetworkConfig
603-
let changed = cfg /= cfg'
604-
!cfgSlow = slowNetworkConfig cfg'
605-
when changed $ writeTVar useNetworkConfig (cfgSlow, cfg')
606-
when (socksProxy cfg /= socksProxy cfg') $ writeTVar proxySessTs ts
607-
pure changed
608-
when changed $ reconnectAllServers c
601+
ts <- liftIO getCurrentTime
602+
(ok, changed) <- atomically $ do
603+
useServices <- readTVar $ useClientServices c
604+
if any id useServices && sessionMode cfg' == TSMEntity
605+
then pure (False, False)
606+
else do
607+
(_, cfg) <- readTVar useNetworkConfig
608+
let changed = cfg /= cfg'
609+
!cfgSlow = slowNetworkConfig cfg'
610+
when changed $ writeTVar useNetworkConfig (cfgSlow, cfg')
611+
when (socksProxy cfg /= socksProxy cfg') $ writeTVar proxySessTs ts
612+
pure (True, changed)
613+
unless ok $ throwE $ CMD PROHIBITED "setNetworkConfig"
614+
when changed $ liftIO $ reconnectAllServers c
609615

610616
setUserNetworkInfo :: AgentClient -> UserNetworkInfo -> IO ()
611617
setUserNetworkInfo c@AgentClient {userNetworkInfo, userNetworkUpdated} ni = withAgentEnv' c $ do
@@ -772,13 +778,19 @@ deleteUser' c@AgentClient {smpServersStats, xftpServersStats} userId delSMPQueue
772778
whenM (withStore' c (`deleteUserWithoutConns` userId)) . atomically $
773779
writeTBQueue (subQ c) ("", "", AEvt SAENone $ DEL_USER userId)
774780

775-
-- TODO [certs rcv] should fail enabling if per-connection isolation is set
776781
setUserService' :: AgentClient -> UserId -> Bool -> AM ()
777782
setUserService' c userId enable = do
778-
wasEnabled <- liftIO $ fromMaybe False <$> TM.lookupIO userId (useClientServices c)
779-
when (enable /= wasEnabled) $ do
780-
atomically $ TM.insert userId enable $ useClientServices c
781-
unless enable $ withStore' c (`deleteClientServices` userId)
783+
(ok, changed) <- atomically $ do
784+
(cfg, _) <- readTVar $ useNetworkConfig c
785+
if enable && sessionMode cfg == TSMEntity
786+
then pure (False, False)
787+
else do
788+
wasEnabled <- fromMaybe False <$> TM.lookup userId (useClientServices c)
789+
let changed = enable /= wasEnabled
790+
when changed $ TM.insert userId enable $ useClientServices c
791+
pure (True, changed)
792+
unless ok $ throwE $ CMD PROHIBITED "setNetworkConfig"
793+
when (changed && not enable) $ withStore' c (`deleteClientServices` userId)
782794

783795
newConnAsync :: ConnectionModeI c => AgentClient -> UserId -> ACorrId -> Bool -> SConnectionMode c -> CR.InitialKeys -> SubscriptionMode -> AM ConnId
784796
newConnAsync c userId corrId enableNtfs cMode pqInitKeys subMode = do

src/Simplex/Messaging/Agent/Client.hs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -500,7 +500,6 @@ data UserNetworkType = UNNone | UNCellular | UNWifi | UNEthernet | UNOther
500500
deriving (Eq, Show)
501501

502502
-- | Creates an SMP agent client instance that receives commands and sends responses via 'TBQueue's.
503-
-- TODO [certs rcv] should fail if both per-connection isolation is set and any users use services
504503
newAgentClient :: Int -> InitialAgentServers -> UTCTime -> Map (Maybe SMPServer) (Maybe SystemSeconds) -> Env -> IO AgentClient
505504
newAgentClient clientId InitialAgentServers {smp, ntf, xftp, netCfg, useServices, presetDomains, presetServers} currentTs notices agentEnv = do
506505
let cfg = config agentEnv
@@ -749,7 +748,7 @@ smpConnectClient c@AgentClient {smpClients, msgQ, proxySessTs, presetDomains} nm
749748
atomically $ SS.setSessionId tSess (sessionId $ thParams smp) $ currentSubs c
750749
updateClientService service smp
751750
pure SMPConnectedClient {connectedClient = smp, proxiedRelays = prs}
752-
-- TODO [certs rcv] this should differentiate between service ID just set and service ID changed, and in the latter case disassociate the queue
751+
-- TODO [certs rcv] this should differentiate between service ID just set and service ID changed, and in the latter case disassociate the queues
753752
updateClientService service smp = case (service, smpClientService smp) of
754753
(Just (_, serviceId_), Just THClientService {serviceId})
755754
| serviceId_ /= Just serviceId -> withStore' c $ \db -> setClientServiceId db userId srv serviceId

src/Simplex/Messaging/Agent/Store/AgentStore.hs

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -487,7 +487,6 @@ createNewConn :: DB.Connection -> TVar ChaChaDRG -> ConnData -> SConnectionMode
487487
createNewConn db gVar cData cMode = do
488488
fst <$$> createConn_ gVar cData (\connId -> createConnRecord db connId cData cMode)
489489

490-
-- TODO [certs rcv] store clientServiceId from NewRcvQueue
491490
updateNewConnRcv :: DB.Connection -> ConnId -> NewRcvQueue -> SubscriptionMode -> IO (Either StoreError RcvQueue)
492491
updateNewConnRcv db connId rq subMode =
493492
getConn db connId $>>= \case
@@ -577,15 +576,13 @@ upgradeRcvConnToDuplex db connId sq =
577576
(SomeConn _ RcvConnection {}) -> Right <$> addConnSndQueue_ db connId sq
578577
(SomeConn c _) -> pure . Left . SEBadConnType "upgradeRcvConnToDuplex" $ connType c
579578

580-
-- TODO [certs rcv] store clientServiceId from NewRcvQueue
581579
upgradeSndConnToDuplex :: DB.Connection -> ConnId -> NewRcvQueue -> SubscriptionMode -> IO (Either StoreError RcvQueue)
582580
upgradeSndConnToDuplex db connId rq subMode =
583581
getConn db connId >>= \case
584582
Right (SomeConn _ SndConnection {}) -> Right <$> addConnRcvQueue_ db connId rq subMode
585583
Right (SomeConn c _) -> pure . Left . SEBadConnType "upgradeSndConnToDuplex" $ connType c
586584
_ -> pure $ Left SEConnNotFound
587585

588-
-- TODO [certs rcv] store clientServiceId from NewRcvQueue
589586
addConnRcvQueue :: DB.Connection -> ConnId -> NewRcvQueue -> SubscriptionMode -> IO (Either StoreError RcvQueue)
590587
addConnRcvQueue db connId rq subMode =
591588
getConn db connId >>= \case
@@ -2500,7 +2497,6 @@ toRcvQueue
25002497
(Just shortLinkId, Just shortLinkKey, Just linkPrivSigKey, Just linkEncFixedData) -> Just ShortLinkCreds {shortLinkId, shortLinkKey, linkPrivSigKey, linkEncFixedData}
25012498
_ -> Nothing
25022499
enableNtfs = maybe True unBI enableNtfs_
2503-
-- TODO [certs rcv] read client service
25042500
in RcvQueue {userId, connId, server, rcvId, rcvPrivateKey, rcvDhSecret, e2ePrivKey, e2eDhSecret, sndId, queueMode, shortLink, rcvServiceAssoc, status, enableNtfs, clientNoticeId, dbQueueId, primary, dbReplaceQueueId, rcvSwchStatus, smpClientVersion, clientNtfCreds, deleteErrors}
25052501

25062502
-- | returns all connection queue credentials, the first queue is the primary one

src/Simplex/Messaging/Client.hs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -781,7 +781,7 @@ temporaryClientError = \case
781781
smpClientServiceError :: SMPClientError -> Bool
782782
smpClientServiceError = \case
783783
PCEServiceUnavailable -> True
784-
PCETransportError (TEHandshake BAD_SERVICE) -> True -- TODO [certs] this error may be temporary, so we should possibly resubscribe.
784+
PCETransportError (TEHandshake BAD_SERVICE) -> True -- TODO [certs rcv] this error may be temporary, so we should possibly resubscribe.
785785
PCEProtocolError SERVICE -> True
786786
PCEProtocolError (PROXY (BROKER NO_SERVICE)) -> True -- for completeness, it cannot happen.
787787
_ -> False

src/Simplex/Messaging/Notifications/Server.hs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -573,7 +573,7 @@ ntfSubscriber NtfSubscriber {smpAgent = ca@SMPClientAgent {msgQ, agentQ}} =
573573
forM_ (L.nonEmpty $ mapMaybe (\(nId, err) -> (nId,) <$> queueSubErrorStatus err) $ L.toList errs) $ \subStatuses -> do
574574
updated <- batchUpdateSrvSubErrors st srv subStatuses
575575
logSubErrors srv subStatuses updated
576-
-- TODO [certs] resubscribe queues with statuses NSErr and NSService
576+
-- TODO [certs rcv] resubscribe queues with statuses NSErr and NSService
577577
CAServiceDisconnected srv serviceSub ->
578578
logNote $ "SMP server service disconnected " <> showService srv serviceSub
579579
CAServiceSubscribed srv serviceSub@(ServiceSub _ expected _) (ServiceSub _ n _) -- TODO [certs rcv] compare hash
@@ -603,7 +603,7 @@ ntfSubscriber NtfSubscriber {smpAgent = ca@SMPClientAgent {msgQ, agentQ}} =
603603
queueSubErrorStatus :: SMPClientError -> Maybe NtfSubStatus
604604
queueSubErrorStatus = \case
605605
PCEProtocolError AUTH -> Just NSAuth
606-
-- TODO [certs] we could allow making individual subscriptions within service session to handle SERVICE error.
606+
-- TODO [certs rcv] we could allow making individual subscriptions within service session to handle SERVICE error.
607607
-- This would require full stack changes in SMP server, SMP client and SMP service agent.
608608
PCEProtocolError SERVICE -> Just NSService
609609
PCEProtocolError e -> updateErr "SMP error " e

src/Simplex/Messaging/Server.hs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -923,7 +923,7 @@ smpServer started cfg@ServerConfig {transports, transportConfig = tCfg, startOpt
923923
putSubscribersInfo protoName ServerSubscribers {queueSubscribers, subClients} showIds = do
924924
activeSubs <- getSubscribedClients queueSubscribers
925925
hPutStrLn h $ protoName <> " subscriptions: " <> show (M.size activeSubs)
926-
-- TODO [certs] service subscriptions
926+
-- TODO [certs rcv] service subscriptions
927927
clnts <- countSubClients activeSubs
928928
hPutStrLn h $ protoName <> " subscribed clients: " <> show (IS.size clnts) <> (if showIds then " " <> show (IS.toList clnts) else "")
929929
clnts' <- readTVarIO subClients

src/Simplex/Messaging/Server/Main.hs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -556,7 +556,7 @@ smpServerCLI_ generateSite serveStaticFiles attachStaticFiles cfgPath logPath =
556556
mkTransportServerConfig
557557
(fromMaybe False $ iniOnOff "TRANSPORT" "log_tls_errors" ini)
558558
(Just $ alpnSupportedSMPHandshakes <> httpALPN)
559-
(fromMaybe True $ iniOnOff "TRANSPORT" "accept_service_credentials" ini), -- TODO [certs] remove this option
559+
(fromMaybe True $ iniOnOff "TRANSPORT" "accept_service_credentials" ini), -- TODO [certs rcv] remove this option
560560
controlPort = eitherToMaybe $ T.unpack <$> lookupValue "TRANSPORT" "control_port" ini,
561561
smpAgentCfg =
562562
defaultSMPClientAgentConfig

src/Simplex/Messaging/Transport.hs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -560,7 +560,7 @@ data SMPClientHandshake = SMPClientHandshake
560560
keyHash :: C.KeyHash,
561561
-- | pub key to agree shared secret for entity ID encryption, shared secret for command authorization is agreed using per-queue keys.
562562
authPubKey :: Maybe C.PublicKeyX25519,
563-
-- TODO [certs] remove proxyServer, as serviceInfo includes it as clientRole
563+
-- TODO [certs rcv] remove proxyServer, as serviceInfo includes it as clientRole
564564
-- | Whether connecting client is a proxy server (send from SMP v12).
565565
-- This property, if True, disables additional transport encrytion inside TLS.
566566
-- (Proxy server connection already has additional encryption, so this layer is not needed there).

tests/AgentTests/FunctionalAPITests.hs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3607,7 +3607,7 @@ testTwoUsers = withAgentClients2 $ \a b -> do
36073607
exchangeGreetings a bId1' b aId1'
36083608
a `hasClients` 1
36093609
b `hasClients` 1
3610-
liftIO $ setNetworkConfig a nc {sessionMode = TSMEntity}
3610+
setNetworkConfig a nc {sessionMode = TSMEntity}
36113611
liftIO $ threadDelay 250000
36123612
("", "", DOWN _ _) <- nGet a
36133613
("", "", UP _ _) <- nGet a
@@ -3617,7 +3617,7 @@ testTwoUsers = withAgentClients2 $ \a b -> do
36173617
exchangeGreetingsMsgId 4 a bId1 b aId1
36183618
exchangeGreetingsMsgId 4 a bId1' b aId1'
36193619
liftIO $ threadDelay 250000
3620-
liftIO $ setNetworkConfig a nc {sessionMode = TSMUser}
3620+
setNetworkConfig a nc {sessionMode = TSMUser}
36213621
liftIO $ threadDelay 250000
36223622
("", "", DOWN _ _) <- nGet a
36233623
("", "", DOWN _ _) <- nGet a
@@ -3632,7 +3632,7 @@ testTwoUsers = withAgentClients2 $ \a b -> do
36323632
exchangeGreetings a bId2' b aId2'
36333633
a `hasClients` 2
36343634
b `hasClients` 1
3635-
liftIO $ setNetworkConfig a nc {sessionMode = TSMEntity}
3635+
setNetworkConfig a nc {sessionMode = TSMEntity}
36363636
liftIO $ threadDelay 250000
36373637
("", "", DOWN _ _) <- nGet a
36383638
("", "", DOWN _ _) <- nGet a
@@ -3646,7 +3646,7 @@ testTwoUsers = withAgentClients2 $ \a b -> do
36463646
exchangeGreetingsMsgId 4 a bId2 b aId2
36473647
exchangeGreetingsMsgId 4 a bId2' b aId2'
36483648
liftIO $ threadDelay 250000
3649-
liftIO $ setNetworkConfig a nc {sessionMode = TSMUser}
3649+
setNetworkConfig a nc {sessionMode = TSMUser}
36503650
liftIO $ threadDelay 250000
36513651
("", "", DOWN _ _) <- nGet a
36523652
("", "", DOWN _ _) <- nGet a
@@ -3695,7 +3695,7 @@ testClientServiceConnection ps = do
36953695
getSMPAgentClient' :: Int -> AgentConfig -> InitialAgentServers -> String -> IO AgentClient
36963696
getSMPAgentClient' clientId cfg' initServers dbPath = do
36973697
Right st <- liftIO $ createStore dbPath
3698-
c <- getSMPAgentClient_ clientId cfg' initServers st False
3698+
Right c <- runExceptT $ getSMPAgentClient_ clientId cfg' initServers st False
36993699
when (dbNew st) $ insertUser st
37003700
pure c
37013701

tests/CoreTests/BatchingTests.hs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -334,7 +334,7 @@ randomSUBv6 = randomSUB_ C.SEd25519 minServerSMPRelayVersion
334334
randomSUB :: ByteString -> IO (Either TransportError (Maybe TAuthorizations, ByteString))
335335
randomSUB = randomSUB_ C.SEd25519 currentClientSMPRelayVersion
336336

337-
-- TODO [certs] test with the additional certificate signature
337+
-- TODO [certs rcv] test with the additional certificate signature
338338
randomSUB_ :: (C.AlgorithmI a, C.AuthAlgorithm a) => C.SAlgorithm a -> VersionSMP -> ByteString -> IO (Either TransportError (Maybe TAuthorizations, ByteString))
339339
randomSUB_ a v sessId = do
340340
g <- C.newRandom

0 commit comments

Comments
 (0)