lio 0.10.0.0 → 0.11.0.0
raw patch · 21 files changed
+1534/−1530 lines, 21 filesdep +hashablePVP ok
version bump matches the API change (PVP)
Dependencies added: hashable
API changes (from Hackage documentation)
- LIO.Core: CanFlowToViolation :: MonitorFailure
- LIO.Core: ClearanceViolation :: MonitorFailure
- LIO.Core: CurrentLabelViolation :: MonitorFailure
- LIO.Core: ResultExceedsLabel :: MonitorFailure
- LIO.Core: VMonitorFailure :: MonitorFailure -> String -> VMonitorFailure
- LIO.Core: data MonitorFailure
- LIO.Core: data VMonitorFailure
- LIO.Core: instance Exception MonitorFailure
- LIO.Core: instance Exception VMonitorFailure
- LIO.Core: instance Show MonitorFailure
- LIO.Core: instance Show VMonitorFailure
- LIO.Core: instance Typeable MonitorFailure
- LIO.Core: instance Typeable VMonitorFailure
- LIO.Core: monitorFailure :: VMonitorFailure -> MonitorFailure
- LIO.Core: monitorMessage :: VMonitorFailure -> String
- LIO.DCLabel: Principal :: ByteString -> Principal
- LIO.DCLabel: clause :: Set Principal -> Clause
- LIO.DCLabel: data Clause
- LIO.DCLabel: data Component
- LIO.DCLabel: dcBottom :: DCLabel
- LIO.DCLabel: dcFalse :: Component
- LIO.DCLabel: dcFormula :: Set Clause -> Component
- LIO.DCLabel: dcLabel :: Component -> Component -> DCLabel
- LIO.DCLabel: dcPub :: DCLabel
- LIO.DCLabel: dcTop :: DCLabel
- LIO.DCLabel: dcTrue :: Component
- LIO.DCLabel: defaultState :: DCState
- LIO.DCLabel: isFalse :: Component -> Bool
- LIO.DCLabel: isTrue :: Component -> Bool
- LIO.DCLabel: newtype Principal
- LIO.DCLabel: runDC :: DC a -> IO (a, DCState)
- LIO.DCLabel: type DCGate = Gate DCPrivDesc
- LIO.DCLabel: type DCRef a = LIORef DCLabel a
- LIO.DCLabel: type DCState = LIOState DCLabel
- LIO.DCLabel: type MonadDC m = MonadLIO DCLabel m
- LIO.DCLabel.Core: Clause :: Set Principal -> Clause
- LIO.DCLabel.Core: DCFalse :: Component
- LIO.DCLabel.Core: DCFormula :: !(Set Clause) -> Component
- LIO.DCLabel.Core: DCLabel :: !Component -> !Component -> DCLabel
- LIO.DCLabel.Core: Principal :: ByteString -> Principal
- LIO.DCLabel.Core: clause :: Set Principal -> Clause
- LIO.DCLabel.Core: data Component
- LIO.DCLabel.Core: data DCLabel
- LIO.DCLabel.Core: dcAnd :: Component -> Component -> Component
- LIO.DCLabel.Core: dcBottom :: DCLabel
- LIO.DCLabel.Core: dcFalse :: Component
- LIO.DCLabel.Core: dcFormula :: Set Clause -> Component
- LIO.DCLabel.Core: dcImplies :: Component -> Component -> Bool
- LIO.DCLabel.Core: dcIntegrity :: DCLabel -> !Component
- LIO.DCLabel.Core: dcLabel :: Component -> Component -> DCLabel
- LIO.DCLabel.Core: dcLabelNoReduce :: Component -> Component -> DCLabel
- LIO.DCLabel.Core: dcOr :: Component -> Component -> Component
- LIO.DCLabel.Core: dcPub :: DCLabel
- LIO.DCLabel.Core: dcReduce :: Component -> Component
- LIO.DCLabel.Core: dcSecrecy :: DCLabel -> !Component
- LIO.DCLabel.Core: dcTop :: DCLabel
- LIO.DCLabel.Core: dcTrue :: Component
- LIO.DCLabel.Core: instance [safe] Bounded DCLabel
- LIO.DCLabel.Core: instance [safe] Eq Clause
- LIO.DCLabel.Core: instance [safe] Eq Component
- LIO.DCLabel.Core: instance [safe] Eq DCLabel
- LIO.DCLabel.Core: instance [safe] Eq Principal
- LIO.DCLabel.Core: instance [safe] Label DCLabel
- LIO.DCLabel.Core: instance [safe] Monoid Component
- LIO.DCLabel.Core: instance [safe] Ord Clause
- LIO.DCLabel.Core: instance [safe] Ord Principal
- LIO.DCLabel.Core: instance [safe] PrivDesc DCLabel Component
- LIO.DCLabel.Core: instance [safe] Show Clause
- LIO.DCLabel.Core: instance [safe] Show Component
- LIO.DCLabel.Core: instance [safe] Show DCLabel
- LIO.DCLabel.Core: instance [safe] Show Principal
- LIO.DCLabel.Core: instance [safe] Typeable Clause
- LIO.DCLabel.Core: instance [safe] Typeable Component
- LIO.DCLabel.Core: instance [safe] Typeable DCLabel
- LIO.DCLabel.Core: instance [safe] Typeable Principal
- LIO.DCLabel.Core: isFalse :: Component -> Bool
- LIO.DCLabel.Core: isTrue :: Component -> Bool
- LIO.DCLabel.Core: newtype Clause
- LIO.DCLabel.Core: newtype Principal
- LIO.DCLabel.Core: principal :: String -> Principal
- LIO.DCLabel.Core: principalName :: Principal -> ByteString
- LIO.DCLabel.Core: unClause :: Clause -> Set Principal
- LIO.DCLabel.Core: unDCFormula :: Component -> !(Set Clause)
- LIO.DCLabel.DSL: (%%) :: (ToComponent a, ToComponent b) => a -> b -> DCLabel
- LIO.DCLabel.DSL: (/\) :: (ToComponent a, ToComponent b) => a -> b -> Component
- LIO.DCLabel.DSL: (\/) :: (ToComponent a, ToComponent b) => a -> b -> Component
- LIO.DCLabel.DSL: class ToComponent a
- LIO.DCLabel.DSL: fromList :: [[Principal]] -> Component
- LIO.DCLabel.DSL: impossible :: Component
- LIO.DCLabel.DSL: instance [safe] ToComponent (Priv Component)
- LIO.DCLabel.DSL: instance [safe] ToComponent Bool
- LIO.DCLabel.DSL: instance [safe] ToComponent Clause
- LIO.DCLabel.DSL: instance [safe] ToComponent Component
- LIO.DCLabel.DSL: instance [safe] ToComponent Principal
- LIO.DCLabel.DSL: instance [safe] ToComponent String
- LIO.DCLabel.DSL: toComponent :: ToComponent a => a -> Component
- LIO.DCLabel.DSL: toList :: Component -> [[Principal]]
- LIO.DCLabel.DSL: unrestricted :: Component
- LIO.DCLabel.Privs: dcDelegatePriv :: DCPriv -> DCPrivDesc -> Maybe DCPriv
- LIO.DCLabel.Privs: dcOwns :: DCPrivDesc -> Clause -> Bool
- LIO.DCLabel.Privs: noPriv :: DCPriv
- LIO.DCLabel.Privs: type DCPriv = Priv DCPrivDesc
- LIO.DCLabel.Privs: type DCPrivDesc = Component
- LIO.Label: class LabelOf t
- LIO.Label: labelOf :: LabelOf t => t l a -> l
- LIO.Labeled: untaintLabeledP :: PrivDesc l p => Priv p -> l -> Labeled l a -> LIO l (Labeled l a)
- LIO.Privs: callGate :: Gate p a -> Priv p -> a
- LIO.Privs: canFlowToP :: PrivDesc l p => Priv p -> l -> l -> Bool
- LIO.Privs: canFlowToPrivDesc :: PrivDesc l p => p -> l -> l -> Bool
- LIO.Privs: class Label l => PrivDesc l p where canFlowToPrivDesc p a b = partDowngradePrivDesc p a b `canFlowTo` b
- LIO.Privs: data Gate d a
- LIO.Privs: data NoPrivs
- LIO.Privs: data Priv a
- LIO.Privs: gate :: (d -> a) -> Gate d a
- LIO.Privs: instance Label l => PrivDesc l NoPrivs
- LIO.Privs: instance Monoid NoPrivs
- LIO.Privs: instance Read NoPrivs
- LIO.Privs: instance Show NoPrivs
- LIO.Privs: noPrivs :: Priv NoPrivs
- LIO.Privs: partDowngradeP :: PrivDesc l p => Priv p -> l -> l -> l
- LIO.Privs: partDowngradePrivDesc :: PrivDesc l p => p -> l -> l -> l
- LIO.Privs: privDesc :: Priv a -> a
- LIO.TCB: class ReadTCB a where readTCB str = check $ readsPrecTCB minPrec str where check [] = error "readTCB: no parse" check [(x, rst)] | all (== ' ') rst = x | otherwise = error "readTCB: no parse" check _ = error "readTCB: ambiguous parse"
- LIO.TCB: instance (Label l, Read l, Read a) => ReadTCB (Labeled l a)
- LIO.TCB: instance (Label l, Show a) => ShowTCB (Labeled l a)
- LIO.TCB: readTCB :: ReadTCB a => String -> a
- LIO.TCB: readsPrecTCB :: ReadTCB a => Int -> ReadS a
- LIO.TCB: unLIOTCB :: LIO l a -> IORef (LIOState l) -> IO a
- LIO.TCB: updateLIOStateTCB :: Label l => (LIOState l -> LIOState l) -> LIO l ()
- LIO.TCB.Concurrent: LResEmpty :: LResStatus l a
- LIO.TCB.Concurrent: LResLabelTooHigh :: !l -> LResStatus l a
- LIO.TCB.Concurrent: LResResult :: a -> LResStatus l a
- LIO.TCB.Concurrent: LabeledResultTCB :: !ThreadId -> !l -> !(MVar ()) -> !(IORef (LResStatus l a)) -> LabeledResult l a
- LIO.TCB.Concurrent: data LResStatus l a
- LIO.TCB.Concurrent: data LabeledResult l a
- LIO.TCB.Concurrent: instance (Show l, Show a) => Show (LResStatus l a)
- LIO.TCB.Concurrent: instance LabelOf LabeledResult
- LIO.TCB.Concurrent: lresBlockTCB :: LabeledResult l a -> !(MVar ())
- LIO.TCB.Concurrent: lresLabelTCB :: LabeledResult l a -> !l
- LIO.TCB.Concurrent: lresStatusTCB :: LabeledResult l a -> !(IORef (LResStatus l a))
- LIO.TCB.Concurrent: lresThreadIdTCB :: LabeledResult l a -> !ThreadId
- LIO.TCB.DCLabel: allPrivTCB :: DCPriv
+ LIO.Concurrent: ResultExceedsLabel :: [String] -> String -> l -> Maybe l -> ResultExceedsLabel l
+ LIO.Concurrent: data ResultExceedsLabel l
+ LIO.Concurrent: relActualLabel :: ResultExceedsLabel l -> Maybe l
+ LIO.Concurrent: relContext :: ResultExceedsLabel l -> [String]
+ LIO.Concurrent: relDeclaredLabel :: ResultExceedsLabel l -> l
+ LIO.Concurrent: relLocation :: ResultExceedsLabel l -> String
+ LIO.Core: AnyLabelError :: e -> AnyLabelError
+ LIO.Core: LabelError :: [String] -> String -> l -> l -> [GenericPrivDesc l] -> [l] -> LabelError l
+ LIO.Core: data AnyLabelError
+ LIO.Core: data InsufficientPrivs
+ LIO.Core: data LabelError l
+ LIO.Core: inspContext :: InsufficientPrivs -> [String]
+ LIO.Core: inspFailure :: InsufficientPrivs -> String
+ LIO.Core: inspNeeded :: InsufficientPrivs -> p
+ LIO.Core: inspSupplied :: InsufficientPrivs -> p
+ LIO.Core: lerrContext :: LabelError l -> [String]
+ LIO.Core: lerrCurClearance :: LabelError l -> l
+ LIO.Core: lerrCurLabel :: LabelError l -> l
+ LIO.Core: lerrFailure :: LabelError l -> String
+ LIO.Core: lerrLabels :: LabelError l -> [l]
+ LIO.Core: lerrPrivs :: LabelError l -> [GenericPrivDesc l]
+ LIO.DCLabel: (%%) :: (ToCNF a, ToCNF b) => a -> b -> DCLabel
+ LIO.DCLabel: (/\) :: (ToCNF a, ToCNF b) => a -> b -> CNF
+ LIO.DCLabel: (\/) :: (ToCNF a, ToCNF b) => a -> b -> CNF
+ LIO.DCLabel: DCLabel :: !CNF -> !CNF -> DCLabel
+ LIO.DCLabel: cFalse :: CNF
+ LIO.DCLabel: cFromList :: [Disjunction] -> CNF
+ LIO.DCLabel: cToSet :: CNF -> Set Disjunction
+ LIO.DCLabel: cTrue :: CNF
+ LIO.DCLabel: class ToCNF c
+ LIO.DCLabel: dFromList :: [Principal] -> Disjunction
+ LIO.DCLabel: dToSet :: Disjunction -> Set Principal
+ LIO.DCLabel: data CNF
+ LIO.DCLabel: data Disjunction
+ LIO.DCLabel: data Principal
+ LIO.DCLabel: dcDefaultState :: LIOState DCLabel
+ LIO.DCLabel: dcPublic :: DCLabel
+ LIO.DCLabel: instance Eq CNF
+ LIO.DCLabel: instance Eq DCLabel
+ LIO.DCLabel: instance Eq Disjunction
+ LIO.DCLabel: instance Eq Principal
+ LIO.DCLabel: instance Label DCLabel
+ LIO.DCLabel: instance Monoid CNF
+ LIO.DCLabel: instance Monoid Disjunction
+ LIO.DCLabel: instance Ord CNF
+ LIO.DCLabel: instance Ord DCLabel
+ LIO.DCLabel: instance Ord Disjunction
+ LIO.DCLabel: instance Ord Principal
+ LIO.DCLabel: instance PrivDesc DCLabel CNF
+ LIO.DCLabel: instance Read CNF
+ LIO.DCLabel: instance Read DCLabel
+ LIO.DCLabel: instance Read Disjunction
+ LIO.DCLabel: instance Read Principal
+ LIO.DCLabel: instance Show CNF
+ LIO.DCLabel: instance Show DCLabel
+ LIO.DCLabel: instance Show Disjunction
+ LIO.DCLabel: instance Show Principal
+ LIO.DCLabel: instance SpeaksFor CNF
+ LIO.DCLabel: instance ToCNF (Priv CNF)
+ LIO.DCLabel: instance ToCNF Bool
+ LIO.DCLabel: instance ToCNF CNF
+ LIO.DCLabel: instance ToCNF Disjunction
+ LIO.DCLabel: instance ToCNF Principal
+ LIO.DCLabel: instance ToCNF [Char]
+ LIO.DCLabel: instance Typeable CNF
+ LIO.DCLabel: instance Typeable DCLabel
+ LIO.DCLabel: instance Typeable Disjunction
+ LIO.DCLabel: instance Typeable Principal
+ LIO.DCLabel: principalBS :: ByteString -> Principal
+ LIO.DCLabel: toCNF :: ToCNF c => c -> CNF
+ LIO.DCLabel: type DCPriv = Priv CNF
+ LIO.Delegate: callGate :: Gate p a -> Priv p -> a
+ LIO.Delegate: data Gate p a
+ LIO.Delegate: delegate :: SpeaksFor p => Priv p -> p -> Priv p
+ LIO.Delegate: gate :: (p -> a) -> Gate p a
+ LIO.Delegate: instance Typeable2 Gate
+ LIO.Error: AnyLabelError :: e -> AnyLabelError
+ LIO.Error: GenericPrivDesc :: p -> GenericPrivDesc l
+ LIO.Error: InsufficientPrivs :: [String] -> String -> p -> p -> InsufficientPrivs
+ LIO.Error: LabelError :: [String] -> String -> l -> l -> [GenericPrivDesc l] -> [l] -> LabelError l
+ LIO.Error: ResultExceedsLabel :: [String] -> String -> l -> Maybe l -> ResultExceedsLabel l
+ LIO.Error: annotate :: Annotatable e => String -> e -> e
+ LIO.Error: class Annotatable e
+ LIO.Error: data AnyLabelError
+ LIO.Error: data GenericPrivDesc l
+ LIO.Error: data InsufficientPrivs
+ LIO.Error: data LabelError l
+ LIO.Error: data ResultExceedsLabel l
+ LIO.Error: inspContext :: InsufficientPrivs -> [String]
+ LIO.Error: inspFailure :: InsufficientPrivs -> String
+ LIO.Error: inspNeeded :: InsufficientPrivs -> p
+ LIO.Error: inspSupplied :: InsufficientPrivs -> p
+ LIO.Error: instance Annotatable (LabelError l)
+ LIO.Error: instance Annotatable (ResultExceedsLabel l)
+ LIO.Error: instance Annotatable AnyLabelError
+ LIO.Error: instance Annotatable InsufficientPrivs
+ LIO.Error: instance Exception AnyLabelError
+ LIO.Error: instance Exception InsufficientPrivs
+ LIO.Error: instance Label l => Exception (LabelError l)
+ LIO.Error: instance Label l => Exception (ResultExceedsLabel l)
+ LIO.Error: instance Show (GenericPrivDesc l)
+ LIO.Error: instance Show AnyLabelError
+ LIO.Error: instance Show InsufficientPrivs
+ LIO.Error: instance Show l => Show (LabelError l)
+ LIO.Error: instance Show l => Show (ResultExceedsLabel l)
+ LIO.Error: instance Typeable AnyLabelError
+ LIO.Error: instance Typeable InsufficientPrivs
+ LIO.Error: instance Typeable1 LabelError
+ LIO.Error: instance Typeable1 ResultExceedsLabel
+ LIO.Error: insufficientPrivs :: SpeaksFor p => String -> Priv p -> p -> a
+ LIO.Error: labelError :: Label l => String -> [l] -> LIO l a
+ LIO.Error: labelErrorP :: (Label l, PrivDesc l p) => String -> Priv p -> [l] -> LIO l a
+ LIO.Error: lerrContext :: LabelError l -> [String]
+ LIO.Error: lerrCurClearance :: LabelError l -> l
+ LIO.Error: lerrCurLabel :: LabelError l -> l
+ LIO.Error: lerrFailure :: LabelError l -> String
+ LIO.Error: lerrFromException :: Exception e => SomeException -> Maybe e
+ LIO.Error: lerrLabels :: LabelError l -> [l]
+ LIO.Error: lerrPrivs :: LabelError l -> [GenericPrivDesc l]
+ LIO.Error: lerrToException :: (Exception e, Annotatable e) => e -> SomeException
+ LIO.Error: relActualLabel :: ResultExceedsLabel l -> Maybe l
+ LIO.Error: relContext :: ResultExceedsLabel l -> [String]
+ LIO.Error: relDeclaredLabel :: ResultExceedsLabel l -> l
+ LIO.Error: relLocation :: ResultExceedsLabel l -> String
+ LIO.Error: withContext :: String -> LIO l a -> LIO l a
+ LIO.Label: NoPrivs :: NoPrivs
+ LIO.Label: canFlowToP :: PrivDesc l p => p -> l -> l -> Bool
+ LIO.Label: class (Label l, SpeaksFor p) => PrivDesc l p where canFlowToP p l1 l2 = downgradeP p l1 `canFlowTo` l2
+ LIO.Label: class (Typeable p, Show p) => SpeaksFor p
+ LIO.Label: data NoPrivs
+ LIO.Label: data Priv a
+ LIO.Label: downgradeP :: PrivDesc l p => p -> l -> l
+ LIO.Label: instance Label l => PrivDesc l NoPrivs
+ LIO.Label: instance Monoid NoPrivs
+ LIO.Label: instance PrivDesc l p => PrivDesc l (Priv p)
+ LIO.Label: instance Read NoPrivs
+ LIO.Label: instance Show NoPrivs
+ LIO.Label: instance SpeaksFor NoPrivs
+ LIO.Label: instance SpeaksFor p => SpeaksFor (Priv p)
+ LIO.Label: instance Typeable NoPrivs
+ LIO.Label: noPrivs :: Priv NoPrivs
+ LIO.Label: privDesc :: Priv a -> a
+ LIO.Label: speaksFor :: SpeaksFor p => p -> p -> Bool
+ LIO.Labeled: class LabelOf t
+ LIO.Labeled: labelOf :: LabelOf t => t l a -> l
+ LIO.Run: privInit :: SpeaksFor p => p -> IO (Priv p)
+ LIO.Run: tryLIO :: LIO l a -> LIOState l -> IO (Either SomeException a, LIOState l)
+ LIO.TCB: LResEmpty :: LResStatus l a
+ LIO.TCB: LResLabelTooHigh :: !l -> LResStatus l a
+ LIO.TCB: LResResult :: a -> LResStatus l a
+ LIO.TCB: LabeledResultTCB :: !ThreadId -> !l -> !(MVar ()) -> !(IORef (LResStatus l a)) -> LabeledResult l a
+ LIO.TCB: class LabelOf t
+ LIO.TCB: data LResStatus l a
+ LIO.TCB: data LabeledResult l a
+ LIO.TCB: instance (Show l, Show a) => Show (LResStatus l a)
+ LIO.TCB: instance (Show l, Show a) => ShowTCB (Labeled l a)
+ LIO.TCB: instance LabelOf LabeledResult
+ LIO.TCB: labelOf :: LabelOf t => t l a -> l
+ LIO.TCB: lresBlockTCB :: LabeledResult l a -> !(MVar ())
+ LIO.TCB: lresLabelTCB :: LabeledResult l a -> !l
+ LIO.TCB: lresStatusTCB :: LabeledResult l a -> !(IORef (LResStatus l a))
+ LIO.TCB: lresThreadIdTCB :: LabeledResult l a -> !ThreadId
- LIO.Core: InsufficientPrivs :: MonitorFailure
+ LIO.Core: InsufficientPrivs :: [String] -> String -> p -> p -> InsufficientPrivs
- LIO.Core: class (Monad m, Label l) => MonadLIO l m | m -> l
+ LIO.Core: class (Label l, Monad m) => MonadLIO l m | m -> l
- LIO.DCLabel: dcIntegrity :: DCLabel -> Component
+ LIO.DCLabel: dcIntegrity :: DCLabel -> !CNF
- LIO.DCLabel: dcSecrecy :: DCLabel -> Component
+ LIO.DCLabel: dcSecrecy :: DCLabel -> !CNF
- LIO.DCLabel: tryDC :: DC a -> IO (Either SomeException a, DCState)
+ LIO.DCLabel: tryDC :: DC a -> IO (Either SomeException a, LIOState DCLabel)
- LIO.Label: class (Eq l, Show l) => Label l
+ LIO.Label: class (Eq l, Show l, Typeable l) => Label l
- LIO.TCB: modifyLIOStateTCB :: Label l => (LIOState l -> LIOState l) -> LIO l ()
+ LIO.TCB: modifyLIOStateTCB :: (LIOState l -> LIOState l) -> LIO l ()
- LIO.TCB.LObj: blessPTCB :: (GuardIO l io lio, PrivDesc l p) => (a -> io) -> Priv p -> (LObj l a) -> lio
+ LIO.TCB.LObj: blessPTCB :: (GuardIO l io lio, PrivDesc l p) => String -> (a -> io) -> Priv p -> (LObj l a) -> lio
- LIO.TCB.LObj: blessTCB :: (GuardIO l io lio, Label l) => (a -> io) -> (LObj l a) -> lio
+ LIO.TCB.LObj: blessTCB :: (GuardIO l io lio, Label l) => String -> (a -> io) -> (LObj l a) -> lio
Files
- LIO.hs +15/−20
- LIO/Concurrent.hs +69/−53
- LIO/Concurrent/LMVar.hs +29/−27
- LIO/Core.hs +146/−183
- LIO/DCLabel.hs +469/−81
- LIO/DCLabel/Core.hs +0/−318
- LIO/DCLabel/DSL.hs +0/−182
- LIO/DCLabel/Privs.hs +0/−78
- LIO/Delegate.hs +130/−0
- LIO/Error.hs +173/−0
- LIO/Exception.hs +8/−7
- LIO/LIORef.hs +32/−15
- LIO/Label.hs +240/−117
- LIO/Labeled.hs +23/−28
- LIO/Privs.hs +0/−213
- LIO/Run.hs +35/−4
- LIO/TCB.hs +111/−100
- LIO/TCB/Concurrent.hs +0/−44
- LIO/TCB/DCLabel.hs +0/−22
- LIO/TCB/LObj.hs +20/−12
- lio.cabal +34/−26
LIO.hs view
@@ -3,41 +3,36 @@ This is the main module to be included by code using the Labeled IO (LIO) library. This module exports the core library (documented in-"LIO.Core"), with support for labeled values (documented in-"LIO.Labeled") and privileges (documented in "LIO.Privs").+"LIO.Core"), with support for labels and privileges (documented in+"LIO.Label") and labeled values (documented in "LIO.Labeled"). -Certain symbols in the lio library, particularly those in+Certain symbols in the LIO library, particularly those in "LIO.Exception", use the same names as their 'IO' equivalents in the-system libraries. Hence main modules that mostly include 'IO' code-and only need to invoke 'LIO' code should import "LIO.Run" (or+system libraries. Hence main modules consisting mostly of 'IO' code+that simply need to run 'LIO' code should import "LIO.Run" (or "LIO.DCLabel") to avoid polluting their namespaces. Most code will need to use a particular label format, which needs to-be imported separately. For instance:+be imported separately. Hence, a typical set of imports for an+untrusted LIO module is: @ import "LIO"- -- Import your favorite label format: import "LIO.DCLabel" @ -WARNING: For security, untrusted code must always be compiled with-the @-XSafe@ and @-fpackage-trust@ /SafeHaskell/ flags. See-<http://hackage.haskell.org/trac/ghc/wiki/SafeHaskell> for more-details on the guarantees provided by SafeHaskell.- -} module LIO ( - module LIO.Label+ module LIO.Core+ , module LIO.Delegate , module LIO.Exception- , module LIO.Core+ , module LIO.Label , module LIO.Labeled- , module LIO.Privs ) where -import LIO.Core-import LIO.Exception-import LIO.Label-import LIO.Labeled-import LIO.Privs+import safe LIO.Core+import safe LIO.Delegate+import safe LIO.Exception+import safe LIO.Label+import safe LIO.Labeled
LIO/Concurrent.hs view
@@ -1,52 +1,54 @@ {-# LANGUAGE Trustworthy #-} {- | -This module exposes useful concurrency abstrations for 'LIO'. This-module is, in part, analogous to "Control.Concurrent". Specifically,-LIO provides a means for spawning 'LIO' computations in a new thread-with 'forkLIO'. LIO relies on the lightweight threads managed by-Haskell's runtime system; we do not provide a way to fork OS-level-threads.+This module provides concurrency abstractions for 'LIO'. The most+basic function, 'forkLIO', spawns a computation in a new light-weight+thread (analogous to 'forkIO'). -In addition to this, LIO also provides 'lFork' and 'lWait' which allow-forking of a computation that is restricted from reading data more-sensitive than a given upper bound. This limit is different from-clearance in that it allows the computation to spawn additional-threads with an upper bound above said upper bound label, but below-the clearance. The 'lFork' function should be used whenever an LIO-computation wishes to execute a sub-computation that may raise the-current label (up to the supplied upper bound). To this end, the-current label only needs to be raised when the computation is-interested in reading the result of the sub-computation. The role of-'lWait' is precisely this: raise the current label and return the-result of such a sub-computation.+'lFork' spawns a forked thread that returns a result other threads can+wait for (using 'lWait'). The label of such a thread's result must be+specified at the time the thread is spawned with 'lFork'. Should the+'lFork'ed thread terminate with its current label be above the+specified result label, 'lWait' will throw an exception of type+'ResultExceedsLabel' in any thread waiting for the result. +Learing that a spawned thread has terminated by catching a+'ResultExceedsLabel' may cause the label of the waiting thread to+rise, possibly above the current clearance (in which case the+exception cannot be caught). As an alternative, 'timedlWait'+unconditionally kills a spawned thread if it has not terminated at an+observable label within a certain time period. 'timedlWait' is+guaranteed both to terminate and not to throw exceptions that cannot+be caught at the current label.+ -} module LIO.Concurrent (- LabeledResult- -- * Forking new threads- , lForkP, lFork, forkLIO+ -- * Forking simple threads+ forkLIO+ -- * Forking threads that return results+ , LabeledResult+ , lFork, lForkP -- * Waiting on threads- , lWaitP, lWait- , trylWaitP, trylWait- , timedlWaitP, timedlWait+ , ResultExceedsLabel(..)+ , lWait, lWaitP+ , trylWait, trylWaitP+ , timedlWait, timedlWaitP -- * Labeled MVars , module LIO.Concurrent.LMVar ) where -import qualified Control.Concurrent as IO-import qualified Control.Exception as IO-import Control.Monad-import Data.IORef+import safe qualified Control.Concurrent as IO+import safe qualified Control.Exception as IO+import safe Control.Monad+import safe Data.IORef -import LIO.Concurrent.LMVar-import LIO.Core-import LIO.Exception-import LIO.Label-import LIO.Privs+import safe LIO.Concurrent.LMVar+import safe LIO.Core+import safe LIO.Exception+import safe LIO.Error+import safe LIO.Label import LIO.TCB-import LIO.TCB.Concurrent --@@ -73,12 +75,7 @@ -- label and clearance as enforced by a call to 'guardAlloc'. -- Moreover, the supplied computation must not terminate with its -- label above the result label; doing so will result in an exception--- (whose label will reflect this observation) being thrown in the--- thread reading the result.------ If an exception is thrown in the inner computation, the exception--- label will be raised to the join of the result label and original--- exception label.+-- being thrown in the thread reading the result. -- -- Note that @lFork@ immediately returns a 'LabeledResult', which is -- essentially a \"future\", or \"promise\". This prevents@@ -97,14 +94,14 @@ -- for when performing label comparisons. lForkP :: PrivDesc l p => Priv p -> l -> LIO l a -> LIO l (LabeledResult l a)-lForkP p l lio = do- guardAllocP p l+lForkP p l (LIOTCB action) = do+ withContext "lForkP" $ guardAllocP p l mv <- ioTCB IO.newEmptyMVar st <- ioTCB $ newIORef LResEmpty s0 <- getLIOStateTCB tid <- ioTCB $ IO.mask $ \unmask -> IO.forkIO $ do sp <- newIORef s0- ea <- IO.try $ unmask $ unLIOTCB lio sp+ ea <- IO.try $ unmask $ action sp LIOState lEnd _ <- readIORef sp writeIORef st $ case ea of _ | not (lEnd `canFlowTo` l) -> LResLabelTooHigh lEnd@@ -124,7 +121,7 @@ -- the result must be above the current label and below the current -- clearance. Moreover, before block-reading, @lWait@ raises the current -- label to the join of the current label and label of result. An--- exception is thrown by the underlying 'guardWrite' if this is not the+-- exception is thrown by the underlying 'taint' if this is not the -- case. Additionally, if the thread terminates with an exception (for -- example if it violates clearance), the exception is rethrown by -- @lWait@. Similarly, if the thread reads values above the result label,@@ -134,14 +131,19 @@ -- | Same as 'lWait', but uses priviliges in label checks and raises. lWaitP :: PrivDesc l p => Priv p -> LabeledResult l a -> LIO l a-lWaitP p (LabeledResultTCB _ l mv st) = taintP p l >> go+lWaitP p (LabeledResultTCB _ l mv st) =+ withContext "lWaitP" (taintP p l) >> go where go = ioTCB (readIORef st) >>= check check LResEmpty = ioTCB (IO.readMVar mv) >> go check (LResResult a) = return $! a check (LResLabelTooHigh lnew) = do modifyLIOStateTCB $ \s -> s {- lioLabel = partDowngradeP p lnew (lioLabel s) }- throwLIO ResultExceedsLabel+ lioLabel = downgradeP p lnew `lub` lioLabel s }+ throwLIO ResultExceedsLabel {+ relContext = []+ , relLocation = "lWaitP"+ , relDeclaredLabel = l+ , relActualLabel = Just lnew } -- | Same as 'lWait', but does not block waiting for result.@@ -151,13 +153,17 @@ -- | Same as 'trylWait', but uses priviliges in label checks and raises. trylWaitP :: PrivDesc l p => Priv p -> LabeledResult l a -> LIO l (Maybe a) trylWaitP p (LabeledResultTCB _ rl _ st) =- taintP p rl >> ioTCB (readIORef st) >>= check+ withContext "trylWaitP" (taintP p rl) >> ioTCB (readIORef st) >>= check where check LResEmpty = return Nothing check (LResResult a) = return . Just $! a check (LResLabelTooHigh lnew) = do curl <- getLabel if canFlowToP p lnew curl- then throwLIO ResultExceedsLabel+ then throwLIO ResultExceedsLabel {+ relContext = []+ , relLocation = "trylWaitP"+ , relDeclaredLabel = rl+ , relActualLabel = Just lnew } else return Nothing @@ -165,10 +171,14 @@ -- | Like 'lWait', with two differences. First, a timeout is -- specified and the thread is unconditionally killed after this -- timeout (if it has not yet returned a value). Second, if the--- thread's result exceeds its label @timedWait@ and exceeds what the--- calling thread can observe, consumes the whole timeout and throws a+-- thread's result exceeds what the calling thread can observe,+-- @timedlWait@ consumes the whole timeout and throws a -- 'ResultExceedsLabel' exception you can catch (i.e., it never raises -- the label above the clearance).+--+-- Because this function can alter the result by killing a thread, it+-- requires the label of the 'LabeledResult' to be both readable and+-- writable at the current label. timedlWait :: Label l => LabeledResult l a -> Int -> LIO l a timedlWait = timedlWaitP noPrivs @@ -177,7 +187,9 @@ -- catching any 'ResultExceedsLabel' before the timeout period (if -- possible). timedlWaitP :: PrivDesc l p => Priv p -> LabeledResult l a -> Int -> LIO l a-timedlWaitP p lr@(LabeledResultTCB t _ mvb _) to = trylWaitP p lr >>= go+timedlWaitP p lr@(LabeledResultTCB t rl mvb _) to =+ withContext "timedlWaitP" $ do guardWriteP p rl+ trylWaitP p lr >>= go where go (Just a) = return a go Nothing = do mvk <- ioTCB $ IO.newEmptyMVar@@ -186,5 +198,9 @@ IO.throwTo t (UncatchableTCB IO.ThreadKilled) ioTCB $ IO.readMVar mvb trylWaitP p lr >>= maybe- (ioTCB (IO.takeMVar mvk) >> throwLIO ResultExceedsLabel)+ (ioTCB (IO.takeMVar mvk) >> throwLIO failure) (\a -> ioTCB (IO.killThread tk) >> return a)+ failure = ResultExceedsLabel { relContext = []+ , relLocation = "timedWaitP"+ , relDeclaredLabel = rl+ , relActualLabel = Nothing }
LIO/Concurrent/LMVar.hs view
@@ -7,7 +7,7 @@ "Control.Concurrent.MVar", but the operations take place in the 'LIO' monad. A labeled MVar, of type @'LMVar' l a@, is a mutable location that can be in of of two states; an 'LMVar' can be empty, or it can be-full (with a value of tye @a@). The location is protected by a label+full (with a value of type @a@). The location is protected by a label of type 'l'. As in the case of @LIORef@s (see "LIO.LIORef"), this label is fixed and does not change according to the content placed into the location. Different from @LIORef@s, taking and putting@@ -40,11 +40,11 @@ , isEmptyLMVar, isEmptyLMVarP ) where -import Control.Concurrent.MVar+import safe Control.Concurrent.MVar -import LIO.Label-import LIO.Core-import LIO.Privs+import safe LIO.Core+import safe LIO.Error+import safe LIO.Label import LIO.TCB import LIO.TCB.LObj @@ -63,14 +63,16 @@ newEmptyLMVar :: Label l => l -- ^ Label of @LMVar@ -> LIO l (LMVar l a) -- ^ New mutable location-newEmptyLMVar l = guardAlloc l >> ioTCB (LObjTCB l `fmap` newEmptyMVar)+newEmptyLMVar l = do+ withContext "newEmptyLMVar" $ guardAlloc l+ ioTCB (LObjTCB l `fmap` newEmptyMVar) -- | Same as 'newEmptyLMVar' except it takes a set of privileges which -- are accounted for in comparing the label of the MVar to the current -- label and clearance. newEmptyLMVarP :: PrivDesc l p => Priv p -> l -> LIO l (LMVar l a) newEmptyLMVarP p l = do- guardAllocP p l+ withContext "newEmptyLMVarP" $ guardAllocP p l ioTCB $ LObjTCB l `fmap` newEmptyMVar -- | Create a new labeled MVar, in an filled state with the supplied@@ -80,14 +82,16 @@ => l -- ^ Label of @LMVar@ -> a -- ^ Initial value of @LMVar@ -> LIO l (LMVar l a) -- ^ New mutable location-newLMVar l a = guardAlloc l >> ioTCB (LObjTCB l `fmap` newMVar a)+newLMVar l a = do+ withContext "newLMVar" $ guardAlloc l+ ioTCB (LObjTCB l `fmap` newMVar a) -- | Same as 'newLMVar' except it takes a set of privileges which are -- accounted for in comparing the label of the MVar to the current label -- and clearance. newLMVarP :: PrivDesc l p => Priv p -> l -> a -> LIO l (LMVar l a) newLMVarP p l a = do- guardAllocP p l+ withContext "newLMVarP" $ guardAllocP p l ioTCB $ LObjTCB l `fmap` newMVar a --@@ -102,26 +106,26 @@ -- current label to the join of the current label and the label of the -- MVar and read the contents of the @MVar@. The underlying guard -- 'guardWrite' will throw an exception if any of the IFC checks fail.--- If the Finally, like 'MVars' if the 'LMVar' is empty, @takeLMVar@+-- Finally, like 'MVars' if the 'LMVar' is empty, @takeLMVar@ -- blocks. takeLMVar :: Label l => LMVar l a -> LIO l a-takeLMVar = blessTCB takeMVar+takeLMVar = blessTCB "takeLMVar" takeMVar -- | Same as 'takeLMVar' except @takeLMVarP@ takes a privilege object -- which is used when the current label is raised. takeLMVarP :: PrivDesc l p => Priv p -> LMVar l a -> LIO l a-takeLMVarP = blessPTCB takeMVar+takeLMVarP = blessPTCB "takeLMVarP" takeMVar -- | Non-blocking version of 'takeLMVar'. It returns @Nothing@ if the -- 'LMVar' is empty, otherwise it returns @Just@ value, emptying the -- 'LMVar'. tryTakeLMVar :: Label l => LMVar l a -> LIO l (Maybe a)-tryTakeLMVar = blessTCB tryTakeMVar+tryTakeLMVar = blessTCB "tryTakeLMVar" tryTakeMVar -- | Same as 'tryTakeLMVar', but uses priviliges when raising current -- label. tryTakeLMVarP :: PrivDesc l p => Priv p -> LMVar l a -> LIO l (Maybe a)-tryTakeLMVarP = blessPTCB tryTakeMVar+tryTakeLMVarP = blessPTCB "tryTakeLMVar" tryTakeMVar -- -- Put 'LMVar'@@ -140,21 +144,21 @@ => LMVar l a -- ^ Source 'LMVar' -> a -- ^ New value -> LIO l ()-putLMVar = blessTCB putMVar+putLMVar = blessTCB "putLMVar" putMVar -- | Same as 'putLMVar' except @putLMVarP@ takes a privilege object -- which is used when the current label is raised. putLMVarP :: PrivDesc l p => Priv p -> LMVar l a -> a -> LIO l ()-putLMVarP = blessPTCB putMVar+putLMVarP = blessPTCB "putLMVarP" putMVar -- | Non-blocking version of 'putLMVar'. It returns @True@ if the -- 'LMVar' was empty and the put succeeded, otherwise it returns @False@. tryPutLMVar :: Label l => LMVar l a -> a -> LIO l Bool-tryPutLMVar = blessTCB tryPutMVar+tryPutLMVar = blessTCB "tryPutLMVar" tryPutMVar -- | Same as 'tryPutLMVar', but uses privileges when raising current label. tryPutLMVarP :: PrivDesc l p => Priv p -> LMVar l a -> a -> LIO l Bool-tryPutLMVarP = blessPTCB tryPutMVar+tryPutLMVarP = blessPTCB "tryPutLMVarP" tryPutMVar -- -- Read 'LMVar'@@ -166,12 +170,12 @@ -- a function such as 'putLMVar', 'tryTakeLMVarP', or 'isEmptyLMVar' -- for this 'LMVar'. readLMVar :: Label l => LMVar l a -> LIO l a-readLMVar = blessTCB readMVar+readLMVar = blessTCB "readLMVar" readMVar -- | Same as 'readLMVar' except @readLMVarP@ takes a privilege object -- which is used when the current label is raised. readLMVarP :: PrivDesc l p => Priv p -> LMVar l a -> LIO l a-readLMVarP = blessPTCB readMVar+readLMVarP = blessPTCB "readLMVarP" readMVar -- -- Swap 'LMVar'@@ -188,12 +192,12 @@ => LMVar l a -- ^ Source @LMVar@ -> a -- ^ New value -> LIO l a -- ^ Taken value-swapLMVar = blessTCB swapMVar+swapLMVar = blessTCB "swapLMVar" swapMVar -- | Same as 'swapLMVar' except @swapLMVarP@ takes a privilege object -- which is used when the current label is raised. swapLMVarP :: PrivDesc l p => Priv p -> LMVar l a -> a -> LIO l a-swapLMVarP = blessPTCB swapMVar+swapLMVarP = blessPTCB "swapLMVarP" swapMVar -- -- Check state of 'LMVar'@@ -202,12 +206,10 @@ -- | Check the status of an 'LMVar', i.e., whether it is empty. The -- function succeeds if the label of the 'LMVar' is below the current -- clearance -- the current label is raised to the join of the 'LMVar'--- label and the current label. Note that this function only returns a--- snapshot of the state and does not modify it -- hence the--- underlying guard is 'taint' and not 'guardWrite'.+-- label and the current label. isEmptyLMVar :: Label l => LMVar l a -> LIO l Bool-isEmptyLMVar = blessTCB isEmptyMVar+isEmptyLMVar = blessTCB "isEmptyLMVar" isEmptyMVar -- | Same as 'isEmptyLMVar', but uses privileges when raising current label. isEmptyLMVarP :: PrivDesc l p => Priv p -> LMVar l a -> LIO l Bool-isEmptyLMVarP = blessPTCB isEmptyMVar+isEmptyLMVarP = blessPTCB "isEmptyLMVarP" isEmptyMVar
LIO/Core.hs view
@@ -2,70 +2,60 @@ {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE FunctionalDependencies #-} {-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE DeriveDataTypeable #-} {- | -This module implements the core of the Labeled IO (LIO) library for-information flow control (IFC) in Haskell. It provides a monad,-'LIO', that is intended to be used as a replacement for the 'IO' monad-in untrusted code. The idea is for untrusted code to provide a-computation in the 'LIO' monad, which trusted code can then safely-execute through using 'evalLIO'-like functions. Though, usually a-wrapper function is employed depending on the type of /labels/ used by-an application. For example, with "LIO.DCLabel" trusted code would-'evalDC' to execute an untrusted computation.+This module implements the core of the Labeled IO (LIO) information+flow control (IFC) library. It provides a monad, 'LIO', that is+intended to be used as a replacement for the 'IO' monad in untrusted+code. The idea is for untrusted code to provide a computation in the+'LIO' monad, which trusted code can then safely execute through+'evalLIO' and similar functions (e.g., 'evalDC' in+"LIO.DCLabel#v:evalDC"). -Labels are a way of describing who can observe and modify data. (A-detailed consideration of labels is given in "LIO.Label".) LIO-associates a /current label/ with every 'LIO' computation. The current-label effectively tracks the sensitivity of all the data that the-computation has observed. For example, if the computation has read a-\"secret\" mutable refernce (see "LIO.LIORef") and then the result of-a \"top-secret\" thread (see "LIO.Concurrent") then the current label-will be at least \"top-secret\". The role of the current label is-two-fold. First, the current label protects all the data in scope ---it is the label associated with any /unlabeled/ data. For example, the-current label is the label on constants such as @3@ or @\"tis a-string\"@. More interestingly, consider reading a \"secret\" file:+Unlike 'IO', the 'LIO' monad keeps track of a /current label/+(accessible via the 'getLabel' function) during each computation. The+current label effectively tracks the sensitivity of all the data that+the computation has observed. For example, if the computation has+read a \"secret\" mutable reference (see "LIO.LIORef") and then the+result of a \"top-secret\" thread (see "LIO.Concurrent") then the+current label will be at least \"top-secret\". Labels are described+in more detail in the documentation for "LIO.Label", as well as the+documentation for particular label formats (such as "LIO.DCLabel"). -> bs <- readFile "/secret/file.txt"+The role of the current label is two-fold: First, the current label+protects all pure values currently in scope. For example, the current+label is the label on constants (such as @3@ and @\"tis a string\"@)+as well as function arguments. More interestingly, consider reading a+secret reference: -Though the label in the file store may be \"secret\", @bs@ has type-@ByteString@, which is not explicitly labeled. Hence, to protect the-contents (@bs@) the current label must be at least \"secret\" before-executing @readFile@. More generally, if the current label is-@L_cur@, then it is only permissible to read data labeled @L_r@ if-@L_r ``canFlowTo`` L_cur@. Note that, rather than throw an exception,-reading data will often just increase the current label to ensure that-@L_r ``canFlowTo`` L_cur@ using 'taint'.+> val <- readLIORef secret -Second, the current label prevents inforation leaks into public-channels. Specifically, it is only permissible to modify, or write-to, data labeled @L_w@ when @L_cur``canFlowTo`` L_w@. Thus, it the-following attempt to leak the secret @bs@ would fail:+Though the label of @secret@ may be \"secret\", @val@ is not+explicitly labeled. Hence, to protect the contents of the 'LIORef'+that has been read into @val@, the current label must be at least+\"secret\" before returning from @readLIORef@. More generally, if the+current label is @l_cur@, then it is only permissible to read data+labeled @l_r@ if @l_r ``canFlowTo`` l_cur@. Note that, instead of+throwing an exception, reading data often just increases the current+label to ensure that @l_r ``canFlowTo`` l_cur@. This is acomplished+using a function such as 'taint'. -> writeFile "/public/file.txt" bs+The second purpose of the current label is to prevent inforation leaks+into public channels. Specifically, it is only permissible to modify+or write to data labeled @l_w@ when @l_cur``canFlowTo`` l_w@. Thus,+the following attempt to leak the @val@ after reading it from a secret+'LIORef' would fail: +> writeLIORef public val+ In addition to the current label, the LIO monad keeps a second label,-the current /clearance/ (accessible via the 'getClearance' function).-The clearance can be used to enforce a \"need-to-know\" policy since+the /current clearance/ (accessible via the 'getClearance' function).+The clearance can be used to enforce a \"need-to-know\" policy, since it represents the highest value the current label can be raised to.-In other words, if the current clearance is @L_clear@ then the-computation cannot create, read or write to objects labeled @L@ such-that @L ``canFlowTo`` L_clear@ does not hold.--This module exports the 'LIO' monad, functions to access the internal-state (e.g., 'getLabel' and 'getClearance'), functions for raising and-catching exceptions, and IFC guards. Exceptions are core to LIO since-they provide a way to deal with potentially-misbehaving untrusted-code. Specifically, when a computation is about to violate IFC (as-@writeFile@ above), an exception is raised. Guards provide a useful-abstraction for dealing with labeled objects; they should be used-before performing a read-only, write-only, or read-write operation on-a labeled object. The remaining core, but not all, abstractions are-exported by "LIO".+In other words, if the current clearance is @l_clear@ then the+computation cannot create, read or write to objects labeled @l@ such+that @(l ``canFlowTo`` l_clear) == False@. -} @@ -81,78 +71,89 @@ , getClearance, setClearance, setClearanceP , scopeClearance, withClearance, withClearanceP -- * Exceptions thrown by LIO- -- $lioExceptions- , MonitorFailure(..)- , VMonitorFailure(..)+ , AnyLabelError(..), LabelError(..), InsufficientPrivs(..) -- * Guards -- $guards- -- ** Allocate/write-only++ -- ** Allocate/write-only guards , guardAlloc, guardAllocP- -- ** Read-only+ -- ** Read-only guards , taint, taintP- -- ** Write+ -- ** Read-write guards , guardWrite, guardWriteP ) where -import qualified Control.Exception as IO-import Control.Monad-import Data.IORef-import Data.Typeable+import safe qualified Control.Exception as IO+import safe Control.Monad+import safe Data.IORef -import LIO.Exception+import safe LIO.Error+import safe LIO.Exception+import safe LIO.Label+import safe LIO.Run import LIO.TCB-import LIO.Label-import LIO.Privs-import LIO.Run -- -- Internal state -- --- | Returns the current value of the thread's label.+-- | Returns the value of the thread's current label. getLabel :: Label l => LIO l l getLabel = lioLabel `liftM` getLIOStateTCB --- | Raise the current label to the provided label, which must be+-- | Raises the current label to the provided label, which must be -- between the current label and clearance. See 'taint'. setLabel :: Label l => l -> LIO l ()-setLabel = setLabelP noPrivs+setLabel l = withContext "setLabel" $ do+ guardAlloc l+ modifyLIOStateTCB $ \s -> s { lioLabel = l } -- | If the current label is @oldLabel@ and the current clearance is--- @clearance@, this function allows code to raise the current label to--- any value @newLabel@ such that @oldLabel ``canFlowTo`` newLabel &&--- newLabel ``canFlowTo`` clearance@.+-- @clearance@, this function allows code to raise the current label+-- to any value @newLabel@ such that @'canFlowToP' priv oldLabel+-- newLabel && 'canFlowTo' newLabel clearance@. (Note the privilege+-- argument affects the label check, not the clearance check; call+-- 'setClearanceP' first to raise the clearance.) setLabelP :: PrivDesc l p => Priv p -> l -> LIO l ()-setLabelP p l = do- guardAllocP p l `catch`- \(_ :: MonitorFailure) -> throwLIO InsufficientPrivs+setLabelP p l = withContext "setLabelP" $ do+ guardAllocP p l modifyLIOStateTCB $ \s -> s { lioLabel = l } --- | Returns the current value of the thread's clearance.+-- | Returns the thread's current clearance. getClearance :: Label l => LIO l l getClearance = lioClearance `liftM` getLIOStateTCB --- | Lower the current clearance. The new clerance must be between--- the current label and clerance. One cannot raise the current label--- or create object with labels higher than the current clearance.+-- | Lowers the current clearance. The new clerance must be between+-- the current label and previous current clerance. One cannot raise+-- the current label or create object with labels higher than the+-- current clearance. setClearance :: Label l => l -> LIO l ()-setClearance = setClearanceP noPrivs+setClearance cnew = do+ LIOState { lioLabel = l, lioClearance = c } <- getLIOStateTCB+ unless (canFlowTo l cnew && canFlowTo cnew c) $+ labelError "setClearance" [cnew]+ putLIOStateTCB $ LIOState l cnew --- | Raise the current clearance (undoing the effects of--- 'setClearance') by exercising privileges. If the current label is+-- | Raises the current clearance (undoing the effects of+-- 'setClearance') by exercising privileges. If the current label is -- @l@ and current clearance is @c@, then @setClearanceP p cnew@ -- succeeds only if the new clearance is can flow to the current--- clearance (modulo privileges), i.e., @'canFlowToP' p cnew c@ must--- hold. Additionally, the current label must flow to the new--- clearance, i.e., @l ``canFlowTo`` cnew@ must hold.+-- clearance (modulo privileges), i.e., @'canFlowToP' p cnew c ==+-- True@. Additionally, the current label must flow to the new+-- clearance, i.e., @l ``canFlowTo`` cnew@ == True.+-- +-- Since LIO guards that are used when reading/writing data (e.g.,+-- 'guardAllocP') do not use privileges when comparing labels with the+-- current clearance, code must always raise the current clearance, to+-- read/write data above the current clearance. setClearanceP :: PrivDesc l p => Priv p -> l -> LIO l () setClearanceP p cnew = do- LIOState l c <- getLIOStateTCB- unless (canFlowToP p cnew c) $! throwLIO InsufficientPrivs- unless (l `canFlowTo` cnew) $! throwLIO CurrentLabelViolation+ LIOState { lioLabel = l, lioClearance = c } <- getLIOStateTCB+ unless (canFlowTo l cnew && canFlowToP p cnew c) $+ labelErrorP "setClearanceP" p [cnew] putLIOStateTCB $ LIOState l cnew -- | Runs an 'LIO' action and re-sets the current clearance to its@@ -166,21 +167,26 @@ -- be caught outside a second @scopeClearance@ that restores the -- clearance to higher than the current label. scopeClearance :: Label l => LIO l a -> LIO l a-scopeClearance lio = LIOTCB $ \sp -> do+scopeClearance (LIOTCB action) = LIOTCB $ \sp -> do LIOState _ c <- readIORef sp- ea <- IO.try $ unLIOTCB lio sp+ ea <- IO.try $ action sp LIOState l _ <- readIORef sp writeIORef sp (LIOState l c) if l `canFlowTo` c then either (IO.throwIO :: SomeException -> IO a) return ea- else IO.throwIO ClearanceViolation+ else IO.throwIO LabelError { lerrContext = []+ , lerrFailure = "scopeClearance"+ , lerrCurLabel = l+ , lerrCurClearance = c+ , lerrPrivs = []+ , lerrLabels = [] } --- | Lowers the clearance of a computation, then restores the--- clearance to its previous value (actually, to the upper bound of--- the current label and previous value). Useful to wrap around a--- computation if you want to be sure you can catch exceptions thrown--- by it. The supplied clearance label must be bounded by the current--- label and clearance as enforced by 'guardAlloc'.+-- | Temporarily lowers the clearance for a computation, then restores+-- it. Equivalent to:+--+-- @+-- withClearance c lio = 'scopeClearance' $ 'setClearance' c >> lio+-- @ -- -- Note that if the computation inside @withClearance@ acquires any -- 'Priv's, it may still be able to raise its clearance above the@@ -188,63 +194,17 @@ withClearance :: Label l => l -> LIO l a -> LIO l a withClearance c lio = scopeClearance $ setClearance c >> lio --- | Same as 'withClearance', but uses privileges when applying--- 'guardAllocP' to the supplied label.+-- | A variant of 'withClearance' that takes privileges. Equivalent+-- to:+--+-- @+-- withClearanceP p c lio = 'scopeClearance' $ 'setClearanceP' p c >> lio+-- @ withClearanceP :: PrivDesc l p => Priv p -> l -> LIO l a -> LIO l a withClearanceP p c lio = scopeClearance $ setClearanceP p c >> lio ----- Exceptions thrown by LIO-----{- $lioExceptions--Library functions throw an exceptions before an IFC violation can take-place. 'MonitorFailure' should be used when the reason for failure is-sufficiently described by the type. Otherwise, 'VMonitorFailure'-(i.e., \"Verbose\"-'MonitorFailure') should be used to further-describe the error.---}---- | Exceptions thrown when some IFC restriction is about to be--- violated.-data MonitorFailure = ClearanceViolation- -- ^ Current label would exceed clearance, or- -- object label is above clearance.- | CurrentLabelViolation- -- ^ Clearance would be below current label, or- -- object label is not above current label.- | InsufficientPrivs- -- ^ Insufficient privileges. Thrown when lowering- -- the current label or raising the clearance- -- cannot be accomplished with the supplied- -- privileges.- | CanFlowToViolation- -- ^ Generic can-flow-to failure, use with- -- 'VMonitorFailure'- | ResultExceedsLabel- deriving (Show, Typeable)--instance Exception MonitorFailure---- | Verbose version of 'MonitorFailure' also carrying around a--- detailed message.-data VMonitorFailure = VMonitorFailure { monitorFailure :: MonitorFailure- -- ^ Generic monitor failure.- , monitorMessage :: String- -- ^ Detailed message of failure.- }- deriving Typeable--instance Show VMonitorFailure where- show m = (show $ monitorFailure m) ++ ": " ++ (monitorMessage m)--instance Exception VMonitorFailure----- -- Guards -- @@ -260,9 +220,9 @@ that @ldata ``canFlowTo`` lcurrent@. This check is performed by the 'taint' function, so named because it \"taints\" the current 'LIO' context by raising @lcurrent@ until @ldata ``canFlowTo``- lcurrent@. (Specifically, it does this by computing the- least 'upperBound' of the two labels.) However, this is done- only if the new @lcurrent ``canFlowTo`` ccurrent@.+ lcurrent@. Specifically, it does this by computing the least+ upper bound or 'lub' of the two labels. (Note 'taint' will fail+ if the new label cannot flow to the current clearance.) * When /creating/ or /allocating/ objects, it is permissible for them to be higher than the current label, so long as they are@@ -279,13 +239,12 @@ can flow to the current label, then throws an exception if @lcurrent@ cannot flow back to the target label. - Note that in this case a write /always/ implies a read. Hence,- when writing to an object for which you can observe the result,- you must use 'guardWrite'. However, when performing a write for- which there is no observable side-effects to the writer, i.e.,- you cannot observe the success or failure of the write, then it- is safe to solely use 'guardAlloc'.-+Note that a write /always/ implies a read. Hence, when writing to an+object for which you can observe the result (which is frequently the+case), you must use 'guardWrite'. However, when performing a write+for which there are no observable side-effects to the writer, i.e.,+you cannot observe the success or failure of the write, then it is+safe to use 'guardAlloc'. The 'taintP', 'guardAllocP', and 'guardWriteP' functions are variants of the above that take privilege to be more permissive and raise the@@ -304,22 +263,20 @@ -- Similarly use this guard in any code that writes to an -- object labeled @l@ for which the write has no observable -- side-effects.------ If the label does not flow to clearance 'ClearanceViolation' is--- thrown; if the current label does not flow to the argument label--- 'CurrentLabelViolation' is thrown. guardAlloc :: Label l => l -> LIO l ()-guardAlloc = guardAllocP noPrivs+guardAlloc newl = do+ LIOState { lioLabel = l, lioClearance = c } <- getLIOStateTCB+ unless (canFlowTo l newl && canFlowTo newl c) $+ labelError "guardAllocP" [newl] -- | Like 'guardAlloc', but takes privilege argument to be more -- permissive. Note: privileges are /only/ used when checking that -- the current label can flow to the given label. guardAllocP :: PrivDesc l p => Priv p -> l -> LIO l () guardAllocP p newl = do- c <- getClearance- l <- getLabel- unless (canFlowToP p l newl) $! throwLIO CurrentLabelViolation- unless (newl `canFlowTo` c) $! throwLIO ClearanceViolation+ LIOState { lioLabel = l, lioClearance = c } <- getLIOStateTCB+ unless (canFlowToP p l newl && canFlowTo newl c) $+ labelErrorP "guardAllocP" p [newl] -- -- Read@@ -327,10 +284,15 @@ -- | Use @taint l@ in trusted code before observing an object labeled -- @l@. This will raise the current label to a value @l'@ such that--- @l ``canFlowTo`` l'@, or throw 'ClearanceViolation' if @l'@ would--- have to be higher than the current clearance.+-- @l ``canFlowTo`` l'@, or throw a 'LabelError' exception if @l'@+-- would have to be higher than the current clearance. taint :: Label l => l -> LIO l ()-taint = taintP noPrivs+taint newl = do+ LIOState { lioLabel = l, lioClearance = c } <- getLIOStateTCB+ let l' = l `lub` newl+ unless (l' `canFlowTo` c) $ labelError "taint" [newl]+ modifyLIOStateTCB $ \s -> s { lioLabel = l' }+ -- | Like 'taint', but use privileges to reduce the amount of taint -- required. Note that @taintP@ will never lower the current label.@@ -338,40 +300,41 @@ -- 'taint' would raise it. taintP :: PrivDesc l p => Priv p -> l -> LIO l () taintP p newl = do- c <- getClearance- l <- getLabel- let l' = partDowngradeP p newl l- unless (l' `canFlowTo` c) $! throwLIO ClearanceViolation+ LIOState { lioLabel = l, lioClearance = c } <- getLIOStateTCB+ let l' = l `lub` downgradeP p newl+ unless (l' `canFlowTo` c) $ labelErrorP "taintP" p [newl] modifyLIOStateTCB $ \s -> s { lioLabel = l' } -- | Use @guardWrite l@ in any (trusted) code before modifying an--- object labeled @l@, for which a the modification can be observed,+-- object labeled @l@, for which the modification can be observed, -- i.e., the write implies a read. -- -- The implementation of @guardWrite@ is straight forward: ----- > guardWrite l = taint l >> guardAlloc l+-- > guardWrite l = guardAlloc l >> taint l -- -- This guarantees that @l@ ``canFlowTo`` the current label (and -- clearance), and that the current label ``canFlowTo`` @l@. -- guardWrite :: Label l => l -> LIO l ()-guardWrite = guardWriteP noPrivs+guardWrite newl = withContext "guardWrite" $ do+ guardAlloc newl+ taint newl --- | Like 'guardWrite', but takes privilege argument to be more+-- | Like 'guardWrite', but takes a privilege argument to be more -- permissive. guardWriteP :: PrivDesc l p => Priv p -> l -> LIO l ()-guardWriteP p newl = do- taintP p newl+guardWriteP p newl = withContext "guardWriteP" $ do guardAllocP p newl+ taintP p newl -- -- Monad base -- -- | Synonym for monad in which 'LIO' is the base monad.-class (Monad m, Label l) => MonadLIO l m | m -> l where+class (Label l, Monad m) => MonadLIO l m | m -> l where -- | Lift an 'LIO' computation. liftLIO :: LIO l a -> m a
LIO/DCLabel.hs view
@@ -1,116 +1,504 @@ {-# LANGUAGE Trustworthy #-}-{-# LANGUAGE MultiParamTypeClasses,- ConstraintKinds,- TypeSynonymInstances #-}+{-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE MultiParamTypeClasses #-} {-| /Disjunction Category Labels/ ('DCLabel's) are a label format that-encode secrecy and integrity using propositional logic. This exports-label operators and instances for the "LIO". The label format is-documented in "LIO.DCLabel.Core", privileges are described in-"LIO.DCLabel.Privs", and a domain specific language for constructing-labels is presented in "LIO.DCLabel.DSL".+encodes authority, secrecy restrictions, and integrity properties+using propositional logic. +A 'DCLabel' consists of two boolean formulas over 'Principal's. Each+formula is in conjunctive normal form, represented by type 'CNF'. The+first 'CNF' ('dcSecrecy') specifies what combinations of principals+are allowed to make data public. The second 'CNF' ('dcIntegrity')+specifies which combinations of principals have endorsed the integrity+of the data.++The '%%' operator allows one to construct a 'DCLabel' by joining a+secrecy 'CNF' on the left with an integrity 'CNF' on the right. A+'DCLabel' can also be directly constructed with the constructor+'DCLabel'. However, '%%' has the added convenience of accepting any+argument types that are instances of 'ToCNF'.++For example, the following expresses data that can be exported by the+principal \"Alice\" and may have been written by anybody: @\"Alice\"+'%%' 'True'@. (@'toCNF' 'True'@ indicates a trivially satisfiable+label component, which in this case means a label conveying no+integrity properties.)++A 'CNF' is created using the ('\/') and ('/\') operators. The+disjunction operator ('\/') is used to compute a 'CNF' equivalent to+the disjunciton of two 'Principal's, 'Strings', or 'CNF's. For+example:++ @+p1 = 'principal' \"p1\"+p2 = 'principal' \"p2\"+p3 = 'principal' \"p3\"+e1 = p1 '\/' p2+e2 = e1 '\/' \"p4\"+ @++Similarly, the conjunction operator ('/\') creates 'CNF' as a+conjunction of 'Principal's, 'String's, 'Disjunction's, or 'CNF's.++ @+e3 = p1 '\/' p2+e4 = e1 '/\' \"p4\" '/\' p3+ @++Note that because a 'CNF' formula is stored as a conjunction of+'Disjunction's, it is much more efficient to apply '/\' to the result+of '\/' than vice versa. It would be logical for '/\' to have higher+fixity than '\/'. Unfortunately, this makes formulas harder to read+(given the convention of AND binding more tightly than OR). Hence+'\/' and '/\' have been given the same fixity but different+associativities, preventing the two from being mixed in the same+expression without explicit parentheses.++Consider the following, example:++ @+cnf1 = (\"Alice\" '\/' \"Bob\") '/\' \"Carla\"+cnf2 = \"Alice\" '/\' \"Carla\"+dc1 = cnf1 '%%' cnf2+dc2 = \"Djon\" '%%' \"Alice\"+pr = PrivTCB $ \"Alice\" '/\' \"Carla\"+ @++This will result in the following:++>>> dc1+"Carla" /\ ("Alice" \/ "Bob") %% "Alice" /\ "Carla"+>>> dc2+"Djon" %% "Alice"+>>> canFlowTo dc1 dc2+False+>>> canFlowToP pr dc1 dc2+True+ -} module LIO.DCLabel (- -- ** Principals- Principal(..), principal- -- ** Clauses- , Clause, clause- -- ** Components- , Component, dcTrue, dcFalse, dcFormula- , isTrue, isFalse- -- ** Labels- , DCLabel, dcSecrecy, dcIntegrity, dcLabel- , dcPub, dcTop, dcBottom- -- ** Privileges- , module LIO.DCLabel.Privs- -- ** DSL- , module LIO.DCLabel.DSL- -- * Synonyms for "LIO"- -- $dcMonad- , DCState, defaultState- , DC, evalDC, runDC, tryDC- , MonadDC- -- ** Labeled values- , DCLabeled- -- ** Labeled references- , DCRef- -- ** Gates- , DCGate+ -- * Top-level aliases and functions+ DC, DCPriv, DCLabeled, dcDefaultState, evalDC, tryDC+ -- * Main types and functions+ , Principal, principalBS, principal+ , DCLabel(..), dcPublic, (%%), (/\), (\/)+ , CNF, ToCNF(..)+ -- * Lower-level functions+ , principalName+ , Disjunction, dToSet, dFromList+ , cTrue, cFalse, cToSet, cFromList ) where -import qualified Control.Exception as IO+import safe Control.Applicative+import safe Data.Bits+import safe qualified Data.ByteString as S+import Data.Hashable+import safe Data.List+import safe Data.Monoid+import safe Data.Set (Set)+import safe qualified Data.Set as Set+import safe Data.String+import safe Data.Typeable+import safe Data.Word+import safe Text.Read -import LIO.Core-import LIO.Labeled-import LIO.Privs-import LIO.LIORef-import LIO.DCLabel.Core-import LIO.DCLabel.Privs-import LIO.DCLabel.DSL+import safe LIO.Exception (SomeException)+import safe LIO.Core+import safe LIO.Label+import safe LIO.Labeled+import safe LIO.Run +type SetTag = Word64+ ----- LIO synonyms+-- Principals -- +-- | A @Principal@ is a primitive source of authority, represented as+-- a string. The interpretation of principal strings is up to the+-- application. Reasonable schemes include encoding user names,+-- domain names, and/or URLs in the 'Principal' type.+data Principal = Principal !S.ByteString {-# UNPACK #-} !SetTag+ deriving (Ord, Typeable) --- | DC 'Labeled' values.-type DCLabeled = Labeled DCLabel+instance Show Principal where+ showsPrec _ (Principal n _) = shows n --- | DC Labeled 'LIORef's.-type DCRef a = LIORef DCLabel a+instance Read Principal where+ readsPrec d s = do+ (name, rest) <- readsPrec d s+ return (principalBS name, rest) +instance Eq Principal where+ (Principal n1 t1) == (Principal n2 t2) = t1 == t2 && n1 == n2 --- | DC 'Gate'.-type DCGate = Gate DCPrivDesc+-- | Extract the name of a principal as a strict 'S.ByteString'.+-- (Use 'show' to get it as a regular 'String'.)+principalName :: Principal -> S.ByteString+{-# INLINE principalName #-}+principalName (Principal n _) = n +-- | Create a principal from a strict 'S.ByteString'.+principalBS :: S.ByteString -> Principal+principalBS bs = Principal bs bloom+ where hv = hash bs+ bloom = bit (hv .&. 0x3f)+ .|. (bit $ shiftR hv 6 .&. 0x3f)+ .|. (bit $ shiftR hv 12 .&. 0x3f)++-- | Create a principal from a 'String'. The 'String' is packed into+-- a 'S.ByteString' using 'fromString', which will almost certainly+-- give unexpected results for non-ASCII unicode code points.+principal :: String -> Principal+principal = principalBS . fromString+ ----- DC monad+-- Disjunctive clauses (a.k.a. "disjunction categories") -- -{- $dcMonad+-- | Represents a disjunction of 'Principal's, or one clause of a+-- 'CNF'. There is generally not much need to work directly with+-- @Disjunction@s unless you need to serialize and de-serialize them+-- (by means of 'dToSet' and 'dFromList').+data Disjunction = Disjunction !(Set Principal) {-# UNPACK #-} !SetTag+ deriving (Typeable) -The 'DC' monad is 'LIO' with using 'DCLabel's as the label format.-Most application should be written in terms of this monad, while most-libraries should remain polymorphic in the label type. It is important-that any *real* application set the initial current label and-clearance to values other than 'bottom' and 'top' as set by-'defaultState', respectively. In most cases the initial current label-should be public, i.e., 'dcPub'.+-- | Expose the set of 'Principal's being ORed together in a+-- 'Disjunction'.+dToSet :: Disjunction -> Set Principal+dToSet (Disjunction ps _) = ps --}+instance Eq Disjunction where+ (Disjunction ps1 t1) == (Disjunction ps2 t2) = t1 == t2 && ps1 == ps2 --- | Type synonym for 'MonadLIO'.-type MonadDC m = MonadLIO DCLabel m+instance Ord Disjunction where+ compare (Disjunction ps1 _) (Disjunction ps2 _) =+ case compare (Set.size ps1) (Set.size ps2) of+ EQ -> compare ps1 ps2+ o -> o --- | 'LIOState' with underlying label being 'DCLabel'-type DCState = LIOState DCLabel+instance Show Disjunction where+ showsPrec _ (Disjunction ps _)+ | Set.size ps == 0 = ("False" ++)+ | Set.size ps == 1 = shows $ Set.findMin ps+ | otherwise = showParen True $+ foldr1 (\l r -> l . (" \\/ " ++) . r) $ map shows $ Set.toList ps --- | Default, starting state for a 'DC' computation. The current label--- is public (i.e., 'dcPub') and the current clearance is 'top'.-defaultState :: DCState-defaultState = LIOState { lioLabel = dcPub- , lioClearance = dcTop }+-- | Note that a disjunction containing more than one element /must/+-- be surrounded by parentheses to parse correctly.+instance Read Disjunction where+ readPrec = false <++ clause <++ single+ where false = do False <- readPrec; return dFalse+ single = dSingleton <$> readPrec+ clause = parens $ prec minPrec $ do+ let next = do Symbol "\\/" <- lexP+ liftA2 (:) readPrec next+ <++ pure []+ dFromList <$> liftA2 (:) readPrec next --- | The monad for LIO computations using 'DCLabel' as the label.+instance Monoid Disjunction where+ mempty = dFalse+ mappend = dUnion++dFalse :: Disjunction+dFalse = Disjunction Set.empty 0++dSingleton :: Principal -> Disjunction+dSingleton p@(Principal _ t) = Disjunction (Set.singleton p) t++dUnion :: Disjunction -> Disjunction -> Disjunction+dUnion (Disjunction ps1 t1) (Disjunction ps2 t2) =+ Disjunction (Set.union ps1 ps2) (t1 .|. t2)++-- | Convert a list of 'Principal's into a 'Disjunction'.+dFromList :: [Principal] -> Disjunction+dFromList pl = Disjunction (Set.fromList pl) tres+ where tres = foldl' (\tl (Principal _ tr) -> tl .|. tr) 0 pl++-- | Returns 'True' iff the first disjunction is a subset of the second.+dImplies :: Disjunction -> Disjunction -> Bool+dImplies (Disjunction ps1 t1) (Disjunction ps2 t2)+ | t1 .&. t2 /= t1 = False+ | otherwise = ps1 `Set.isSubsetOf` ps2++--+-- Conjunctive Normal Form (CNF) Formulas+--++-- | A boolean formula in Conjunctive Normal Form. @CNF@ is used to+-- describe 'DCLabel' privileges, as well to provide each of the two+-- halves of a 'DCLabel'.+newtype CNF = CNF (Set Disjunction) deriving (Eq, Ord, Typeable)++-- | Convert a 'CNF' to a 'Set' of 'Disjunction's. Mostly useful if+-- you wish to serialize a 'DCLabel'.+cToSet :: CNF -> Set Disjunction+{-# INLINE cToSet #-}+cToSet (CNF ds) = ds++instance Show CNF where+ showsPrec d (CNF ds)+ | Set.size ds == 0 = ("True" ++)+ | Set.size ds == 1 = shows $ Set.findMin ds+ | otherwise = showParen (d > 7) $+ foldr1 (\l r -> l . (" /\\ " ++) . r) $ map shows $ Set.toList ds++instance Read CNF where+ readPrec = true <++ formula <++ single+ where true = do True <- readPrec; return cTrue+ single = cSingleton <$> readPrec+ formula = parens $ prec 7 $ do+ let next = do Symbol "/\\" <- lexP+ liftA2 (:) readPrec next+ <++ pure []+ cFromList <$> liftA2 (:) readPrec next++instance Monoid CNF where+ mempty = cTrue+ mappend = cUnion+ +-- | A 'CNF' that is always @True@--i.e., trivially satisfiable. When+-- @'dcSecrecy' = cTrue@, it means data is public. When+-- @'dcIntegrity' = cTrue@, it means data carries no integrity+-- guarantees. As a description of privileges, @cTrue@ conveys no+-- privileges; @'canFlowToPrivDesc' cTrue l1 l2@ is equivalent to+-- @'canFlowTo' l1 l2@.+--+-- Note that @'toCNF' 'True' = cTrue@. Hence @'dcPublic' = 'DCLabel'+-- cTrue cTrue@.+cTrue :: CNF+cTrue = CNF $ Set.empty++-- | A 'CNF' that is always @False@. If @'dcSecrecy' = cFalse@, then+-- no combination of principals is powerful enough to make the data+-- public. For that reason, @cFalse@ generally shouldn't appear in a+-- data label. However, it is convenient to include as the+-- 'dcSecrecy' component of 'lioClearance' to indicate a thread may+-- arbitrarily raise its label.+--+-- @'dcIntegrity' = cFalse@ indicates impossibly much integrity--i.e.,+-- data that no combination of principals is powerful enough to modify+-- or have created. Generally this is not a useful concept.+--+-- As a privilege description, @cFalse@ indicates impossibly high+-- privileges (i.e., higher than could be achieved through any+-- combination of 'Principal's). @cFalse ``speaksFor`` p@ for any+-- 'CNF' @p@. This can be a useful concept for bootstrapping+-- privileges within the 'DC' monad itself. For instance, the result+-- of @'privInit' cFalse@ can be passed to fully-trusted 'DC' code,+-- which can in turn use 'delegate' to create arbitrary finite+-- privileges to pass to less privileged code.+cFalse :: CNF+cFalse = CNF $ Set.singleton dFalse++cSingleton :: Disjunction -> CNF+cSingleton = CNF . Set.singleton++setAny :: (a -> Bool) -> Set a -> Bool+setAny prd = Set.foldr' (\a -> (prd a ||)) False++setAll :: (a -> Bool) -> Set a -> Bool+setAll prd = Set.foldr' (\a -> (prd a &&)) True++cInsert :: Disjunction -> CNF -> CNF+cInsert dnew c@(CNF ds)+ | setAny (`dImplies` dnew) ds = c+ | otherwise = CNF $ Set.insert dnew $ Set.filter (not . (dnew `dImplies`)) ds++cUnion :: CNF -> CNF -> CNF+cUnion c (CNF ds) = Set.foldr cInsert c ds++cOr :: CNF -> CNF -> CNF+cOr (CNF ds1) (CNF ds2) =+ cFromList $ [dUnion d1 d2 | d1 <- Set.toList ds1, d2 <- Set.toList ds2]++-- | Convert a list of 'Disjunction's into a 'CNF'. Mostly useful if+-- you wish to de-serialize a 'CNF'.+cFromList :: [Disjunction] -> CNF+cFromList = Set.foldr cInsert cTrue . Set.fromList++cImplies1 :: CNF -> Disjunction -> Bool+cImplies1 (CNF ds) d = setAny (`dImplies` d) ds++cImplies :: CNF -> CNF -> Bool+cImplies c (CNF ds) = setAll (c `cImplies1`) ds++--+-- DCLabel+--++-- | Main DCLabel type. @DCLabel@s use 'CNF' boolean formulas over+-- principals to express authority exercised by a combination of+-- principals. A @DCLabel@ contains two 'CNF's. One, 'dcSecrecy',+-- specifies the minimum authority required to make data with the+-- label completely public. The second, 'dcIntegrity', expresses the+-- minimum authority that was used to endorse data with the label, or,+-- for mutable objects, the minimum authority required to modify the+-- object.+--+-- @DCLabel@s are more conveniently expressed using the '%%' operator,+-- with 'dcSecrecy' on the left and 'dcIntegrity' on the right, i.e.:+-- @(@/dcSecrecyValue/ '%%' /dcIntegrityValue/@)@.+--+-- @DCLabel@s enforce the following relations:+--+-- * If @cnf1@ and @cnf2@ are 'CNF's describing authority, then+-- @cnf1 ``speaksFor`` cnf2@ if and only if @cnf1@ logically implies+-- @cnf2@ (often written @cnf1 ⟹ cnf2@). For example,+-- @(\"A\" '/\' \"B\") ``speaksFor`` 'toCNF' \"A\"@, while @'toCNF'+-- \"A\" ``speaksFor`` (\"A\" '\/' \"C\")@.+--+-- * Given two @DCLabel@s @dc1 = (s1 '%%' i1)@ and @dc2 = (s2 '%%'+-- i2)@, @dc1 ``canFlowTo`` dc2@ (often written @dc1@ ⊑ @dc2@)+-- if and only if @s2 ``speaksFor`` s1 && i1 ``speaksFor`` i2@. In+-- other words, data can flow in the direction of requiring more+-- authority to make it public or removing integrity endorsements.+--+-- * Given two @DCLabel@s @dc1 = (s1 '%%' i1)@ and @dc 2 = (s2 '%%'+-- i2)@, and a @p::'CNF'@ representing privileges,+-- @'canFlowToPrivDesc' p dc1 dc2@ (often written @dc1@+-- ⊑ₚ @dc2@) if and only if @(p '/\' s2) ``speaksFor``+-- s2 && (p '/\' i1) ``speaksFor`` i2@.+data DCLabel = DCLabel { dcSecrecy :: !CNF+ -- ^ Describes the authority required to make+ -- the data public.+ , dcIntegrity :: !CNF+ -- ^ Describes the authority with which+ -- immutable data was endorsed, or the+ -- authority required to modify mutable data.+ } deriving (Eq, Ord, Typeable)++instance Show DCLabel where+ showsPrec d (DCLabel sec int) =+ showParen (d > 5) $ shows sec . (" %% " ++) . shows int++instance Read DCLabel where+ readPrec = parens $ prec 5 $ do+ sec <- readPrec+ Symbol "%%" <- lexP+ int <- readPrec+ return $ DCLabel sec int++-- |+-- > dcPublic = True %% True+--+-- This label corresponds to public data with no integrity guarantees.+-- For instance, an unrestricted Internet socket should be labeled+-- @dcPublic@. The significance of @dcPublic@ is that given data+-- labeled @(s %% i)@, @s@ is the exact minimum authority required to+-- transition the data to @dcPublic@. Conversely, given data labeled+-- @dcPublic@, @i@ is the exact authority required to transition the+-- data to @(s %% i)@ (assuming sufficient clearance).+dcPublic :: DCLabel+dcPublic = True %% True++-- | As a type, a 'CNF' is always a conjunction of 'Disjunction's of+-- 'Principal's. However, mathematically speaking, a single+-- 'Principal' or single 'Disjunction' is also a degenerate example of+-- conjunctive normal form. Class 'ToCNF' abstracts over the+-- differences between these types, promoting them all to 'CNF'.+class ToCNF c where toCNF :: c -> CNF+instance ToCNF CNF where toCNF = id+instance ToCNF (Priv CNF) where toCNF = privDesc+instance ToCNF Disjunction where toCNF = cSingleton+instance ToCNF Principal where toCNF = toCNF . dSingleton+instance ToCNF [Char] where toCNF = toCNF . principal+instance ToCNF Bool where+ toCNF True = cTrue+ toCNF False = cFalse++-- | The primary way of creating a 'DCLabel'. The secrecy component+-- goes on the left, while the integrity component goes on the right,+-- e.g.:+--+-- > label = secrecyCNF %% integrityCNF+--+-- Unlike the 'DCLabel' constructor, the arguments can be any instance+-- of 'ToCNF'. @%%@ has fixity:+--+-- > infix 6 %%+(%%) :: (ToCNF a, ToCNF b) => a -> b -> DCLabel+a %% b = toCNF a `DCLabel` toCNF b+infix 6 %%++-- | Compute a conjunction of two 'CNF's or 'ToCNF' instances.+--+-- Has fixity:+--+-- > infixr 7 /\+(/\) :: (ToCNF a, ToCNF b) => a -> b -> CNF+a /\ b = toCNF a `cUnion` toCNF b+infixr 7 /\++-- | Compute a disjunction of two 'CNF's or 'ToCNF' instances. Note+-- that this can be an expensive operation if the inputs have many+-- conjunctions.+--+-- The fixity is specifically chosen so that @\/@ and '/\'+-- cannot be mixed in the same expression without parentheses:+--+-- > infixl 7 \/+(\/) :: (ToCNF a, ToCNF b) => a -> b -> CNF+a \/ b = toCNF a `cOr` toCNF b+infixl 7 \/++instance Label DCLabel where+ lub (DCLabel s1 i1) (DCLabel s2 i2) = DCLabel (cUnion s1 s2) (cOr i1 i2)+ glb (DCLabel s1 i1) (DCLabel s2 i2) = DCLabel (cOr s1 s2) (cUnion i1 i2)+ canFlowTo (DCLabel s1 i1) (DCLabel s2 i2) = cImplies s2 s1 && cImplies i1 i2++instance SpeaksFor CNF where+ {-# INLINE speaksFor #-}+ speaksFor = cImplies++dcMaxDowngrade :: CNF -> DCLabel -> DCLabel+dcMaxDowngrade p (DCLabel (CNF ds) int) = DCLabel sec (cUnion p int)+ where sec = CNF $ Set.filter (not . cImplies1 p) ds++instance PrivDesc DCLabel CNF where+ downgradeP = dcMaxDowngrade+ canFlowToP p (DCLabel s1 i1) (DCLabel s2 i2) =+ cImplies (cUnion p s2) s1 && cImplies (cUnion p i1) i2++--+-- Type aliases+--++-- | A common default starting state, where @'lioLabel' = 'dcPublic'@+-- and @'lioClearance' = False '%%' True@ (i.e., the highest+-- possible clearance).+dcDefaultState :: LIOState DCLabel+dcDefaultState = LIOState { lioLabel = dcPublic+ , lioClearance = False %% True }++-- | The main monad type alias to use for 'LIO' computations that are+-- specific to 'DCLabel's. type DC = LIO DCLabel +-- | 'DCLabel' privileges are expressed as a 'CNF' of the principals+-- whose authority is being exercised.+type DCPriv = Priv CNF --- | Evaluate computation in the 'DC' monad.-evalDC :: DC a -> IO a-evalDC act = evalLIO act defaultState+-- | An alias for 'Labeled' values labeled with a 'DCLabel'.+type DCLabeled = Labeled DCLabel --- | Evaluate computation in the 'DC' monad.-runDC :: DC a -> IO (a, DCState)-runDC act = runLIO act defaultState+-- | Wrapper function for running @'LIO' 'DCLabel'@ computations.+--+-- @+-- evalDC dc = 'evalLIO' dc 'dcDefaultState'+-- @+evalDC :: DC a -> IO a+evalDC dc = evalLIO dc dcDefaultState --- | Similar to 'evalLIO', but catches any exceptions thrown by--- untrusted code with 'throwLIO'.-tryDC :: DC a -> IO (Either IO.SomeException a, DCState)-tryDC act = runDC act >>= tryit- where tryit (a, s) = do- ea <- IO.try (IO.evaluate a)- return (ea, s)+-- | 'DCLabel' wrapper for 'tryLIO':+--+-- @+-- tryDC dc = 'tryLIO' dc 'dcDefaultState'+-- @+tryDC :: DC a -> IO (Either SomeException a, LIOState DCLabel)+tryDC dc = tryLIO dc dcDefaultState
− LIO/DCLabel/Core.hs
@@ -1,318 +0,0 @@-{-# LANGUAGE Safe #-}-{-# LANGUAGE DeriveDataTypeable #-}-{-# LANGUAGE MultiParamTypeClasses #-}-{-|--This module implements Disjunction Category Labels (DCLabels).-DCLabels is a label format for information flow control (IFC) systems.-This library exports relevant data types and operations that may be-used by dynamic IFC systems such as the "LIO" library.--A 'DCLabel' is a pair of /secrecy/ and /integrity/ 'Component's-(sometimes called category sets). Each 'Component' (or formula) is a-conjunction (implemented in terms of 'Set's) of 'Clause's (or-category) in propositional logic (without negation) specifying a-restriction on the flow of information labeled as such. Alternatively,-a 'Component' can take on the value 'DCFalse' corresponding to-falsehood. Each 'Clause', in turn, is a disjunction of 'Principal's,-, where a 'Principal' is a source of authority of type 'ByteString',-whose meaning is application-specific (e.g., a 'Principal' can be a-user name, a URL, etc.).--A clause imposes an information flow restriction. In the case of-secrecy, a clause restricts who can read, receive, or propagate the-information, while in the case of integrity it restricts who can-modify a piece of data. The principals composing a clause are said to-/own/ the clause or category.--For information to flow from a source labeled @L_1@ to a sink @L_2@, the-restrictions imposed by the clauses of @L_2@ must at least as restrictive as-all the restrictions imposed by the clauses of @L_1@ (hence the conjunction)-in the case of secrecy, and at least as permissive in the case of integrity.-More specifically, for information to flow from @L_1@ to @L_2@, the labels-must satisfy the \"can-flow-to\" relation: @L_1 ⊑ L_2@. The ⊑-label check is implemented by the 'canFlowTo' function. For labels-@L_1=\<S_1, I_1\>@, @L_2=\<S_2, I_2\>@ the can-flow-to relation is satisfied-if the secrecy component @S_2@ /implies/ @S_1@ and @I_1@ /implies/ @I_2@-(recall that a category set is a conjunction of disjunctions of principals).-For example, @\<P_1 ⋁ P_2, True\> ⊑ \<P_1, True\>@ because data-that can be read by @P_1@ is more restricting than that readable by @P_1@-or @P_2@. Conversely, @\<True,P_1\> ⊑ \<True,P_1 ⋁ P_2\>@-because data vouched for by @P_1@ or @P_2@ is more permissive than just @P_1@-(note the same principle holds when writing to sinks with such labeling).---}--module LIO.DCLabel.Core ( - -- * Principals- Principal(..), principal- -- * Clauses- , Clause(..), clause- -- * Components- -- $component- , Component(..)- , dcTrue, dcFalse, dcFormula - , isTrue, isFalse- -- * Labels- , DCLabel(..), dcLabel, dcLabelNoReduce- , dcPub, dcTop, dcBottom- -- * Internal- , dcReduce, dcImplies- , dcAnd, dcOr- ) where--import qualified Data.ByteString.Char8 as S8-import Data.List (intercalate)-import Data.Monoid-import Data.Set (Set)-import qualified Data.Set as Set-import Data.Typeable--import LIO.Label-import LIO.Privs------- Principals------- | A @Principal@ is a simple string representing a source of--- authority. Any piece of code can create principals, regardless of how--- untrusted it is.-newtype Principal = Principal { principalName :: S8.ByteString- -- ^ Get the principal name.- } deriving (Eq, Ord, Typeable)--instance Show Principal where- showsPrec _ = shows . principalName---- | Generate a principal from a 'String'. (To create one from a--- 'S8.ByteString', just use the 'Principal' constructor directly.)-principal :: String -> Principal-principal = Principal . S8.pack------- Category - disjunction clauses------- | A clause or disjunction category is a set of 'Principal's.--- Logically the set corresponds to a disjunction of the principals.-newtype Clause = Clause { unClause :: Set Principal- -- ^ Get underlying principal-set.- } deriving (Eq, Typeable)--instance Ord Clause where- (Clause c1) <= (Clause c2) =- case () of- _ | Set.size c1 == Set.size c2 -> c1 <= c2- _ -> Set.size c1 < Set.size c2--instance Show Clause where- show c@(Clause uc)- | Set.size uc == 1 = show . head . Set.toList $ uc- | otherwise =- let ps = map show . Set.toList $! unClause c- in parens . intercalate " \\/ " $! ps- where parens x = "(" ++ x ++ ")"---- | Clause constructor-clause :: Set Principal -> Clause-clause = Clause---- | A component is a set of clauses, i.e., a formula (conjunction of--- disjunction of 'Principal's). @DCFalse@ corresponds to logical--- @False@, while @DCFormula Set.empty@ corresponds to logical @True@.-data Component = DCFalse- -- ^ Logical @False@- | DCFormula { unDCFormula :: !(Set Clause) - -- ^ Get underlying clause-set.- }- -- ^ Conjunction of disjunction categories- deriving (Eq, Typeable)--instance Show Component where- show c | isFalse c = show False- | isTrue c = show True- | otherwise = let cs = map show . Set.toList $! unDCFormula c- in intercalate " /\\ " $! cs---- | Privileges can be combined using 'mappend'-instance Monoid Component where- mempty = dcTrue- mappend p1 p2 = dcReduce $! p1 `dcAnd` p2--instance PrivDesc DCLabel Component where- canFlowToPrivDesc pd l1 l2- | pd == dcTrue = canFlowTo l1 l2- | otherwise =- let i1 = dcReduce $ dcIntegrity l1 `dcAnd` pd- s2 = dcReduce $ dcSecrecy l2 `dcAnd` pd- in l1 { dcIntegrity = i1 } `canFlowTo` l2 { dcSecrecy = s2 }-- partDowngradePrivDesc pd la lg- | pd == mempty = la `lub` lg- | pd == dcFalse = lg- | otherwise = - let sec_a = dcSecrecy la- int_a = dcIntegrity la- sec_g = dcSecrecy lg- int_g = dcIntegrity lg- sec_a' = dcFormula . Set.filter f $ unDCFormula sec_a- sec_res = if isFalse sec_a- then sec_a- else sec_a' `dcAnd` sec_g- int_res = (pd `dcAnd` int_a) `dcOr` int_g- in dcLabel sec_res int_res- where f c = not $ pd `dcImplies` (dcFormula . Set.singleton $ c)------ | Logical @True@.-dcTrue :: Component-dcTrue = DCFormula Set.empty---- | Logical @False@.-dcFalse :: Component-dcFalse = DCFalse---- | Arbitrary formula from a clause.-dcFormula :: Set Clause -> Component-dcFormula = DCFormula---- | Is the component @True@.-isTrue :: Component -> Bool-isTrue = (== dcTrue)---- | Is the component @False@.-isFalse :: Component -> Bool-isFalse = (== dcFalse)------- Labels-----{- $component- A 'Component' is a conjunction of disjunctions of 'Principal's. A- 'DCLabel' is simply a pair of such 'Component's. Hence, we define- almost all operations in terms of this construct, from which the- 'DCLabel' implementation follows almost trivially.--}---- | A @DCLabel@ is a pair of secrecy and integrity 'Component's.-data DCLabel = DCLabel { dcSecrecy :: !Component- -- ^ Extract secrecy component of a label- , dcIntegrity :: !Component- -- ^ Extract integrity component of a label- } deriving (Eq, Typeable)--instance Show DCLabel where - showsPrec d l = showParen (d > 5) $- let s = dcSecrecy l- i = dcIntegrity l- in showsPrec (d + 1) s . showString " %% " . showsPrec (d + 1) i---- | @dcLabel secrecyComponent integrityComponent@ creates a label,--- reducing each component to CNF.-dcLabel :: Component -> Component -> DCLabel-dcLabel c1 c2 = DCLabel (dcReduce c1) (dcReduce c2)---- | Label contstructor. Note: the components should already be reduced.-dcLabelNoReduce :: Component -> Component -> DCLabel-dcLabelNoReduce = DCLabel------ | Element in the DCLabel lattice corresponding to the most secret--- and least trustworthy data.--- @dcTop = \< False, True \> @.-dcTop :: DCLabel-dcTop = dcLabel dcFalse dcTrue---- | Element in the DCLabel lattice corresponding to the least secret--- and most trustworthy data.--- @dcTop = \< True, False \> @.-dcBottom :: DCLabel-dcBottom = dcLabel dcTrue dcFalse---- | Element in the DCLabel lattice corresponding to public data.--- @dcPub = \< True, True \> @. This corresponds to data that is not--- secret nor trustworthy.-dcPub :: DCLabel-dcPub = DCLabel { dcSecrecy = dcTrue, dcIntegrity = dcTrue }------- Bounded by \< True, False \> and \< False, True \>----instance Bounded DCLabel where- minBound = dcBottom- maxBound = dcTop------- Lattice operations-----instance Label DCLabel where- -- | Partial /can-flow-to/ relation on labels.- canFlowTo l1 l2 = (dcSecrecy l2 `dcImplies` dcSecrecy l1) &&- (dcIntegrity l1 `dcImplies` dcIntegrity l2)--- -- | The least upper bound of two labels, i.e., the join.- lub l1 l2 = DCLabel- { dcSecrecy = dcReduce $ dcSecrecy l1 `dcAnd` dcSecrecy l2- , dcIntegrity = dcReduce $ dcIntegrity l1 `dcOr` dcIntegrity l2 }-- -- | The greatest lower bound of two labels, i.e., the meet.- glb l1 l2 = DCLabel- { dcSecrecy = dcReduce $ dcSecrecy l1 `dcOr` dcSecrecy l2- , dcIntegrity = dcReduce $ dcIntegrity l1 `dcAnd` dcIntegrity l2 }------- Helpers------- | Logical implication.-dcImplies :: Component -> Component -> Bool-dcImplies DCFalse _ = True-dcImplies _ DCFalse = False-dcImplies f1@(DCFormula cs1) f2@(DCFormula cs2)- | isTrue f2 = True- | isTrue f1 = False- | otherwise = Set.foldl' dcImpliesDisj True cs2- where dcImpliesDisj :: Bool -> Clause -> Bool- dcImpliesDisj False _ = False- dcImpliesDisj _ (Clause c2) = Set.foldl' f False cs1- where f True _ = True- f _ c1 = unClause c1 `Set.isSubsetOf` c2 ---- | Logical conjunction-dcAnd :: Component -> Component -> Component -dcAnd x y | isFalse x || isFalse y = dcFalse- | otherwise = DCFormula $! unDCFormula x `Set.union` unDCFormula y---- | Logical disjunction-dcOr :: Component -> Component -> Component -dcOr x y | isTrue x || isTrue y = dcTrue-dcOr x y | isFalse x = y- | isFalse y = x- | otherwise = let cs1 = unDCFormula x- cs2 = unDCFormula y- in DCFormula $! doOr cs1 cs2- where -- | Perform disjunction of two components.- doOr :: Set Clause -> Set Clause -> Set Clause- doOr cs1 cs2 = Set.foldl' disjFunc Set.empty cs2- where disjFunc acc c = acc `Set.union` singleOr c cs1- -- | Given a clause and a formula, perform logical or of- -- clause with every clause in formula.- singleOr :: Clause -> Set Clause -> Set Clause- singleOr (Clause c1) = Set.map (Clause . Set.union c1 . unClause)---- | Reduce component to conjunction normal form by removing clauses--- implied by other.-dcReduce :: Component -> Component-dcReduce f | isFalse f || isTrue f = f- | otherwise = DCFormula . doReduce . unDCFormula $ f- where doReduce cs | Set.null cs = cs- doReduce cs =- let (x@(Clause x'), xs) = Set.deleteFindMin cs - ys = doReduce $ Set.filter (not . Set.isSubsetOf x' . unClause) xs- in Set.singleton x `Set.union` ys
− LIO/DCLabel/DSL.hs
@@ -1,182 +0,0 @@-{-# LANGUAGE Safe #-}-{-# LANGUAGE FlexibleInstances #-}--{-|- This module implements a simple, embedded domain specific language- to create 'Component's, privilage descriptions and labels from- conjunctions of principal disjunctions.-- A 'DCLabel' consists of a secrecy 'Component' and an integrity- 'Component'. The '%%' operator allows one to construct a 'DCLabel'- by joining a secrecy 'Component' (on the left) with an integrity- 'Component' on the right. This is similar to 'dcLabel', except that- the arguments can also be instances of 'ToComponent'. For example,- the following expresses data that can be exported by the principal- \"Alice\" and written by anybody: @\"Alice\" '%%' 'True'@. (The- component 'True' or 'dcTrue' indicates a trivially satisfiable label- component, which in this case means a label with no integrity- guarantees.)-- A 'Component' or 'DCPrivDesc' is created using the ('\/') and ('/\')- operators. The disjunction operator ('\/') is used to create a- 'Clause' from 'Principal's, ByteStrings, or a disjunctive- sub-expression. For example:-- @- p1 = 'principal' \"p1\"- p2 = 'principal' \"p2\"- p3 = 'principal' \"p3\"- e1 = p1 '\/' p2- e2 = e1 '\/' \"p4\"- @-- Similarly, the conjunction operator ('/\') is used to create category-sets- from 'Principal's, ByteStrings, and conjunctive or disjunctive sub-expressions.- For example:-- @- e3 = p1 '\/' p2- e4 = e1 '/\' \"p4\" '/\' p3- @-- /Note/ that because a clause consists of a disjunction of principals, and a- component is composed of the conjunction of categories, ('\/') binds- more tightly than ('/\').-- Given two 'Component's, one for secrecy and one for integrity, you- can create a 'DCLabel' with 'dcLabel'. Given a 'DCPriv' and- 'DCPrivDesc' you can create a new minted privilege with- 'dcDelegatePriv'.- - - Consider the following, example:-- @-l1 = \"Alice\" '\/' \"Bob\" '/\' \"Carla\"-l2 = \"Alice\" '/\' \"Carla\"-dc1 = 'dcLabel' l1 l2-dc2 = 'dcLabel' ('toComponent' \"Djon\") ('toComponent' \"Alice\")-pr = PrivTCB $ toComponent $ \"Alice\" '/\' \"Carla\"- @-- This will result in the following:-->>> dc1-"Carla" /\ ("Alice" \/ "Bob") %% "Alice" /\ "Carla"->>> dc2-"Djon" %% "Alice"->>> canFlowTo dc1 dc2-False->>> canFlowToP pr dc1 dc2-True---}--module LIO.DCLabel.DSL (- -- * Operators- (%%), (\/), (/\), ToComponent(..)- , fromList, toList- -- * Aliases- , impossible, unrestricted- ) where--import qualified Data.Set as Set--import LIO.Privs-import LIO.DCLabel.Core---- | Convert a type (e.g., 'Clause', 'Principal') to a label component.-class ToComponent a where- -- | Convert to 'Component'- toComponent :: a -> Component--infix 5 %%---- | Create a `DCLabel` from a secrecy `ToComponent` and integrity--- `ToComponent`. E.g.:------ @--- \"secrecy\" %% \"integrity\"--- @------ @--- infix 5 %%--- @-(%%) :: (ToComponent a, ToComponent b) => a -> b -> DCLabel-(%%) sec int = dcLabel (toComponent sec) (toComponent int)---- | Identity of 'Component'.-instance ToComponent Component where- {-# INLINE toComponent #-}- toComponent = id-instance ToComponent (Priv Component) where- {-# INLINE toComponent #-}- toComponent = privDesc--- | Convert singleton 'Clause' to 'Component'.-instance ToComponent Clause where- toComponent c = DCFormula $! Set.singleton c--- | Convert singleton 'Principal' to 'Component'.-instance ToComponent Principal where- toComponent p = toComponent . Clause $! Set.singleton p---- | Convert singleton 'Principal' (in the form of a 'String')to 'Component'.-instance ToComponent String where- toComponent = toComponent . principal--instance ToComponent Bool where- {-# INLINE toComponent #-}- toComponent True = DCFormula Set.empty- toComponent False = DCFalse--infixl 7 \/-infixl 6 /\---- | Conjunction of two 'Principal'-based elements.--- --- @--- infixl 6 /\--- @----(/\) :: (ToComponent a, ToComponent b) => a -> b -> Component-a /\ b = dcReduce $! toComponent a `dcAnd` toComponent b---- | Disjunction of two 'Principal'-based elements.--- --- @--- infixl 7 \\/--- @----(\/) :: (ToComponent a, ToComponent b) => a -> b -> Component-a \/ b = dcReduce $! toComponent a `dcOr` toComponent b------- Aliases------- | Logical falsehood can be thought of as the component containing--- every possible principal, hence impossible to express:------ > impossible = dcFalse----impossible :: Component-impossible = dcFalse---- | Logical truth can be thought of as the component containing--- no specific principal, hence imposing no restrictions:------ > unrestricted = dcTrue----unrestricted :: Component-unrestricted = dcTrue---- | Convert a 'Component' to a list of list of 'Principal's if the--- 'Component' does not have the value 'DCFalse'. In the latter case--- the function returns an exception.-toList :: Component -> [[Principal]]-toList DCFalse = error "toList: Invalid use, expected DCFormula"-toList (DCFormula cs) = map (Set.toList . unClause) $! Set.toList cs---- | Convert a list of list of 'Principal's to a 'Component'. Each--- inner list is considered to correspond to a 'Clause'.-fromList :: [[Principal]] -> Component-fromList ps = DCFormula . Set.fromList $! map (Clause . Set.fromList) ps
− LIO/DCLabel/Privs.hs
@@ -1,78 +0,0 @@-{-# LANGUAGE Trustworthy #-}-{- |--Privileges allow a piece of code to bypass certain information flow-restrictions imposed by labels. A privilege is simply a conjunction-of disjunctions of 'Principal's, i.e., a 'Component'. We say that a-piece of code containing a singleton 'Clause' owns the 'Principal'-composing the 'Clause'. However, we allow for the more general notion-of ownership of a clause, or category, as to create a-privilege-hierarchy. Specifically, a piece of code exercising a-privilege @P@ can always exercise privilege @P'@ (instead), if @P' => P@.-(This is similar to the DLM notion of \"can act for\".) Hence, if a-piece of code with certain privileges implies a clause, then it is-said to own the clause. Consequently it can bypass the restrictions of-the clause in any label.--Note that the privileges form a partial order over logicla implication-(@=>@), such that @'allPrivTCB' => P@ and @P => 'noPriv'@ for any-privilege @P@. Hence, a privilege hierarchy which can be concretely-built through delegation, with 'allPrivTCB' corresponding to the-/root/, or all, privileges from which all others may be created. More-specifically, given a privilege @P'@ of type 'DCPriv', and a privilege-description @P@ of type 'DCPrivDesc', any piece of code can use-'delegatePriv' to \"mint\" @P@, assuming @P' => P@.---}--module LIO.DCLabel.Privs (- DCPrivDesc- , DCPriv- -- ** Helpers- , noPriv- , dcDelegatePriv- , dcOwns- ) where--import qualified Data.Set as Set--import LIO-import LIO.DCLabel.Core-import LIO.TCB---- | A privilege description is simply a conjunction of disjunctions.--- Unlike (actually minted) privileges (see 'DCPriv'), privilege--- descriptions may be created by untrusted code.-type DCPrivDesc = Component---- | A privilege is a minted and protected privilege description--- ('DCPrivDesc') that may only be created by trusted code or--- delegated from an existing @DCPriv@.-type DCPriv = Priv DCPrivDesc------- Helpers------- | The empty privilege, or no privileges, corresponds to logical--- @True@.-noPriv :: DCPriv-noPriv = PrivTCB dcTrue---- | Given a privilege and a privilege description turn the privilege--- description into a privilege (i.e., mint). Such delegation succeeds--- only if the supplied privilege implies the privilege description.-dcDelegatePriv :: DCPriv -> DCPrivDesc -> Maybe DCPriv-dcDelegatePriv p pd = let c = privDesc $! p- in if c `dcImplies` pd- then Just $! PrivTCB pd- else Nothing---- | We say a piece of code having a privilege object (of type 'DCPriv')--- owns a clause when the privileges allow code to bypass restrictions--- imposed by the clause. This is the case if and only if the 'DCPriv'--- object contains one of the 'Principal's in the 'Clause'. This--- function can be used to make such checks.-dcOwns :: DCPrivDesc -> Clause -> Bool-dcOwns pd c = pd `dcImplies` dcFormula (Set.singleton c)-
+ LIO/Delegate.hs view
@@ -0,0 +1,130 @@+{-# LANGUAGE Trustworthy #-}+{-# LANGUAGE DeriveDataTypeable #-}++-- | This module provides two functions useful for delegating+-- privileges. The 'delegate' function creates a 'Priv' value less+-- powerful than an existing one. 'Gate's provide a mechanism for+-- authenticating calls to closures that embed privileges.+module LIO.Delegate (+ -- * Delegating privileges+ delegate+ -- * Gates+ -- $gateIntro+ , Gate, gate, callGate+ -- ** Gate example+ -- $gateExample+ ) where++import safe Data.Typeable++import safe LIO.Error+import safe LIO.Label+import LIO.TCB++-- | @delegate@ allows you to create a new privilege object that is+-- less powerful than an existing privilege object. The first+-- argument supplies actual privileges. The second argument is a+-- 'PrivDesc' describing the desired new privileges. The call throws+-- an exception unless the privileges supplied 'speaksFor' the+-- privileges requested.+--+-- Note: If you are looking for a way to create privileges /more/+-- powerful than ones you already have, you can use the 'mappend'+-- function to combine existing privileges.+delegate :: (SpeaksFor p) => Priv p -> p -> Priv p+delegate p1 p2+ | privDesc p1 `speaksFor` p2 = PrivTCB p2+ | otherwise = insufficientPrivs "delegate" p1 p2++{- $gateIntro++LIO provides a basic implementation of /gates/, useful in providing+controlled RPC-like services where the client and service provider are+in mutual distrust. ++A service provider uses 'gate' to create a gate data type @'Gate' p a@+given a computation of type @p -> a@. Here, @p@ is a privilege+description (type variable for an instance of 'PrivDesc'). Gates are+invoked with 'callGate', and as such the service provider has the+guarantee that the client (the caller) owns the privileges+corresponding to the privilege description @p@. In effect, this+allows a client to \"prove\" to the service provider that they own+certain privileges without entrusting the service with its privileges.+The gate computation can analyze this privilege description before+performing the \"actual\" computation. The client and server solely+need to trust the implementation of 'callGate'.++-}+++-- | A Gate is a lambda abstraction from a privilege description to an+-- arbitrary type @a@. Applying the gate is accomplished with 'callGate'+-- which takes a privilege argument that is converted to a description+-- before invoking the gate computation.+newtype Gate p a = GateTCB (p -> a) deriving Typeable+-- Note GateTCB is trusted by convention. Anyone with access to the+-- symbol can call any gate while claiming arbitrary privileges. In+-- the absence of gates, however, GateTCB doesn't provide any+-- particular privileges.++-- | Create a gate given a computation from a privilege description.+-- Note that because of currying type 'a' may itself be a function+-- type and thus gates can take arguments in addition to the privilege+-- descriptoin.+gate :: (p -> a) -- ^ Gate computation+ -> Gate p a+{-# INLINE gate #-}+gate = GateTCB++-- | Given a gate and privilege, execute the gate computation. It is+-- important to note that @callGate@ invokes the gate computation with+-- the privilege description and /NOT/ the privilege itself.+--+-- Note that, in general, code should /not/ provide privileges to+-- functions other than @callGate@ when wishing to call a gate. This+-- function is provided by LIO since it can be easily inspected by+-- both the gate creator and caller to be doing the \"right\" thing:+-- provide the privilege description corresponding to the supplied+-- privilege as \"proof\" without explicitly passing in the privilege.+-- +callGate :: Gate p a -- ^ Gate+ -> Priv p -- ^ Privilege used as proof-of-ownership+ -> a+{-# INLINE callGate #-}+callGate (GateTCB g) = g . privDesc++{- $gateExample++This example uses "LIO.DCLabel" to demonstrate the use of gates. The+service provider provides @addGate@ which adds two integers if the+gate is called by a piece of code that owns the \"Alice\" or \"Bob\"+principals. Otherwise, it simply returns @Nothing@. Though 'privInit'+(in "LIO.Run#v:privInit") allows us to create arbitrary privileges in+the 'IO' monad, the gate restricts access to @addGate@ from within the+'LIO' monad where code cannot create arbitrary privileges.++> import LIO+> import LIO.DCLabel+> import LIO.Run+> +> -- | Add two numbers if the computation is invoked by Alice or Bob.+> addGate :: Gate CNF (Int -> Int -> Maybe Int)+> addGate = gate $ \pd a b ->+> if pd `speaksFor` "Alice" \/ "Bob"+> then Just $ a + b+> else Nothing+> +> main :: IO ()+> main = do+> alice <- privInit $ toCNF $ "Alice"+> bob <- privInit $ toCNF $ "Bob"+> clark <- privInit $ toCNF $ "Clark"+> putStrLn . show $ [+> callGate addGate alice 1 2 -- Just 3+> , callGate addGate bob 3 4 -- Just 7+> , callGate addGate clark 5 6 -- Nothing+> ]+> ++-}+
+ LIO/Error.hs view
@@ -0,0 +1,173 @@+{-# LANGUAGE Trustworthy #-}+{-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE ExistentialQuantification #-}++{- |++This module exports exception types thrown in response to label+failures. In addition, it provides 'withContext', a function that+annotates any exceptions in the 'AnyLabelError' hierarchy that are+thrown within a given scope. These annotations should be used to add+function names to exceptions, so as to make it easier to pinpoint the+cause of a label error.++-}++module LIO.Error (+ Annotatable(..), withContext+ , AnyLabelError(..), lerrToException, lerrFromException+ , GenericPrivDesc(..), LabelError(..), labelError, labelErrorP+ , InsufficientPrivs(..), insufficientPrivs+ , ResultExceedsLabel(..)+ ) where++import safe qualified Control.Exception as IO+import safe Data.Typeable++import safe LIO.Exception+import safe LIO.Label+import LIO.TCB++-- | Class of error messages that can be annotated with context.+class Annotatable e where+ annotate :: String -> e -> e++-- | Parent of all label-related exceptions.+data AnyLabelError = forall e. (Exception e, Annotatable e) =>+ AnyLabelError e deriving Typeable++instance Show AnyLabelError where+ showsPrec d (AnyLabelError e) = showsPrec d e++instance Annotatable AnyLabelError where+ annotate s (AnyLabelError e) = AnyLabelError $ annotate s e++instance Exception AnyLabelError++-- | Executes an action with a context string which will be added to+-- any label exception thrown.+-- +-- Note: this function wraps an action with a 'catch', and thus may+-- incur a small runtime cost (though it is well under 100 ns on+-- machines we benchmarked).+withContext :: String -> LIO l a -> LIO l a+withContext ctx (LIOTCB act) =+ LIOTCB $ \st -> act st `IO.catch` \e ->+ IO.throwIO $ annotate ctx (e :: AnyLabelError)++-- | Definition of 'toException' for children of 'AnyLabelError' in+-- the exception hierarchy.+lerrToException :: (Exception e, Annotatable e) => e -> SomeException+lerrToException = toException . AnyLabelError++-- | Definition of 'fromException' for children of 'AnyLabelError' in+-- the exception hierarchy.+lerrFromException :: (Exception e) => SomeException -> Maybe e+lerrFromException se = do+ AnyLabelError e <- fromException se+ cast e+++-- | A generic privilege description for recording relevant privileges+-- in exceptions.+data GenericPrivDesc l = forall p. (PrivDesc l p) => GenericPrivDesc p+instance Show (GenericPrivDesc l) where+ showsPrec d (GenericPrivDesc p) = showsPrec d p+++-- | Main error type thrown by label failures in the 'LIO' monad.+data LabelError l = LabelError {+ lerrContext :: [String] -- ^ Annotation of where the failure happened.+ , lerrFailure :: String -- ^ Actual function that failed.+ , lerrCurLabel :: l -- ^ Current label at time of error.+ , lerrCurClearance :: l -- ^ Current clearance at time of error.+ , lerrPrivs :: [GenericPrivDesc l] -- ^ Any privileges involved in error.+ , lerrLabels :: [l] -- ^ Any labels involved in error.+ } deriving (Show, Typeable)++instance Annotatable (LabelError l) where+ annotate a e = e { lerrContext = a : lerrContext e }++instance Label l => Exception (LabelError l) where+ toException = lerrToException+ fromException = lerrFromException++-- | Throw a label-error exception.+labelError :: (Label l) => String -- ^ Function that failed.+ -> [l] -- ^ Labels involved in error.+ -> LIO l a+labelError fl ls = do+ st <- getLIOStateTCB+ throwLIO LabelError {+ lerrContext = []+ , lerrFailure = fl+ , lerrCurLabel = lioLabel st+ , lerrCurClearance = lioClearance st+ , lerrPrivs = []+ , lerrLabels = ls+ }++-- | Throw a label-error exception.+labelErrorP :: (Label l, PrivDesc l p) => String -- ^ Function that failed.+ -> Priv p -- ^ Privileges involved.+ -> [l] -- ^ Labels involved.+ -> LIO l a+labelErrorP fl p ls = do+ st <- getLIOStateTCB+ throwLIO LabelError {+ lerrContext = []+ , lerrFailure = fl+ , lerrCurLabel = lioLabel st+ , lerrCurClearance = lioClearance st+ , lerrPrivs = [GenericPrivDesc $ privDesc p]+ , lerrLabels = ls+ }+++-- | Error indicating insufficient privileges (independent of the+-- current label). This exception is thrown by 'delegate', and+-- should also be thrown by gates that receive insufficient privilege+-- descriptions (see "LIO.Delegate").+data InsufficientPrivs = forall p. (SpeaksFor p) => InsufficientPrivs {+ inspContext :: [String]+ , inspFailure :: String+ , inspSupplied :: p+ , inspNeeded :: p+ } deriving (Typeable)++instance Show InsufficientPrivs where+ showsPrec _ (InsufficientPrivs c l s n) =+ ("InsufficientPrivs { inspContext = " ++) . shows c .+ (", inspLocation = " ++) . shows l .+ (", inspSupplied = " ++) . shows s .+ (", inspNeeded = " ++) . shows n .+ (" }" ++)++instance Annotatable InsufficientPrivs where+ annotate a e = e { inspContext = a : inspContext e }++instance Exception InsufficientPrivs where+ toException = lerrToException+ fromException = lerrFromException++-- | Raise 'InsufficientPrivs' error.+insufficientPrivs :: (SpeaksFor p) => String -> Priv p -> p -> a+insufficientPrivs fl supplied needed =+ IO.throw $ InsufficientPrivs [] fl (privDesc supplied) needed+++-- | Error raised when a computation spawned by 'lFork' terminates+-- with its current label above the label of the result.+data ResultExceedsLabel l = ResultExceedsLabel {+ relContext :: [String]+ , relLocation :: String+ , relDeclaredLabel :: l+ , relActualLabel :: Maybe l+ } deriving (Show, Typeable)++instance Annotatable (ResultExceedsLabel l) where+ annotate a e = e { relContext = a : relContext e }++instance (Label l) => Exception (ResultExceedsLabel l) where+ toException = lerrToException+ fromException = lerrFromException
LIO/Exception.hs view
@@ -21,14 +21,15 @@ , onException, finally, bracket, evaluate ) where -import Control.Exception (Exception(..), SomeException(..))-import qualified Control.Exception as IO-import Control.Monad-import Data.Typeable+import safe Control.Exception (Exception(..), SomeException(..))+import safe qualified Control.Exception as IO+import safe Control.Monad+import safe Data.Typeable import LIO.TCB-import LIO.Label+import safe LIO.Label +-- | Throw an exception. throwLIO :: Exception e => e -> LIO l a throwLIO = ioTCB . IO.throwIO @@ -40,8 +41,8 @@ -- exceeding the clarance, and an exception is always thrown at the -- time this happens.) catch :: (Label l, Exception e) => LIO l a -> (e -> LIO l a) -> LIO l a-catch io h =- LIOTCB $ \s -> unLIOTCB io s `IO.catch` \e -> unLIOTCB (safeh e) s+catch (LIOTCB io) h =+ LIOTCB $ \s -> io s `IO.catch` \e -> case safeh e of LIOTCB ioe -> ioe s where uncatchableType = typeOf (undefined :: UncatchableTCB) safeh e@(SomeException einner) = do when (typeOf einner == uncatchableType) $ throwLIO e
LIO/LIORef.hs view
@@ -28,10 +28,11 @@ , atomicModifyLIORef, atomicModifyLIORefP ) where -import Data.IORef-import LIO.Core-import LIO.Label-import LIO.Privs+import safe Data.IORef++import safe LIO.Core+import safe LIO.Error+import safe LIO.Label import LIO.TCB import LIO.TCB.LObj @@ -55,13 +56,17 @@ => l -- ^ Label of reference -> a -- ^ Initial value -> LIO l (LIORef l a) -- ^ Mutable reference-newLIORef = newLIORefP noPrivs+newLIORef l a = do+ withContext "newLIORef" $ guardAlloc l+ ioTCB (LObjTCB l `fmap` newIORef a) -- | Same as 'newLIORef' except @newLIORefP@ takes a set of -- privileges which are accounted for in comparing the label of -- the reference to the current label and clearance. newLIORefP :: PrivDesc l p => Priv p -> l -> a -> LIO l (LIORef l a)-newLIORefP p l a = guardAllocP p l >> ioTCB (LObjTCB l `fmap` newIORef a)+newLIORefP p l a = do+ withContext "newLIORefP" $ guardAllocP p l+ ioTCB (LObjTCB l `fmap` newIORef a) -- -- Read 'LIORef's@@ -71,14 +76,18 @@ -- label of the reference is below the current clearance. Moreover, -- the current label is raised to the join of the current label and -- the reference label. To avoid failures (introduced by the 'taint'----guard) use 'labelOf' to check that a read will succeed.+-- guard) use 'labelOf' to check that a read will succeed. readLIORef :: Label l => LIORef l a -> LIO l a-readLIORef (LObjTCB l r) = taint l >> ioTCB (readIORef r)+readLIORef (LObjTCB l r) = do+ withContext "readLIORef" $ taint l+ ioTCB (readIORef r) -- | Same as 'readLIORef' except @readLIORefP@ takes a privilege object -- which is used when the current label is raised. readLIORefP :: PrivDesc l p => Priv p -> LIORef l a -> LIO l a-readLIORefP p (LObjTCB l r) = taintP p l >> ioTCB (readIORef r)+readLIORefP p (LObjTCB l r) = do+ withContext "readLIORefP" $ taintP p l+ ioTCB (readIORef r) -- -- Write 'LIORef's@@ -89,13 +98,17 @@ -- label of the reference can-flow-to the current clearance. Otherwise, -- an exception is raised by the underlying 'guardAlloc' guard. writeLIORef :: Label l => LIORef l a -> a -> LIO l ()-writeLIORef (LObjTCB l r) a = guardAlloc l >> ioTCB (writeIORef r a)+writeLIORef (LObjTCB l r) a = do+ withContext "writeLIORef" $ guardAlloc l+ ioTCB (writeIORef r a) -- | Same as 'writeLIORef' except @writeLIORefP@ takes a set of -- privileges which are accounted for in comparing the label of -- the reference to the current label and clearance. writeLIORefP :: PrivDesc l p => Priv p -> LIORef l a -> a -> LIO l ()-writeLIORefP p (LObjTCB l r) a = guardAllocP p l >> ioTCB (writeIORef r a)+writeLIORefP p (LObjTCB l r) a = do+ withContext "writeLIORefP" $ guardAllocP p l+ ioTCB (writeIORef r a) -- -- Modify 'LIORef's@@ -113,14 +126,18 @@ => LIORef l a -- ^ Labeled reference -> (a -> a) -- ^ Modifier -> LIO l ()-modifyLIORef (LObjTCB l r) f = guardAlloc l >> ioTCB (modifyIORef r f)+modifyLIORef (LObjTCB l r) f = do+ withContext "modifyLIORef" $ guardAlloc l+ ioTCB (modifyIORef r f) -- | Same as 'modifyLIORef' except @modifyLIORefP@ takes a set of -- privileges which are accounted for in comparing the label of -- the reference to the current label and clearance. modifyLIORefP :: PrivDesc l p => Priv p -> LIORef l a -> (a -> a) -> LIO l ()-modifyLIORefP p (LObjTCB l r) f = guardAllocP p l >> ioTCB (modifyIORef r f)+modifyLIORefP p (LObjTCB l r) f = do+ withContext "modifyLIORefP" $ guardAllocP p l+ ioTCB (modifyIORef r f) -- | Atomically modifies the contents of an 'LIORef'. It is required -- that the label of the reference be above the current label, but@@ -132,11 +149,11 @@ -- which will raise an exception if any of the IFC conditions cannot -- be satisfied. atomicModifyLIORef :: Label l => LIORef l a -> (a -> (a, b)) -> LIO l b-atomicModifyLIORef = blessTCB atomicModifyIORef+atomicModifyLIORef = blessTCB "atomicModifyIORef" atomicModifyIORef -- | Same as 'atomicModifyLIORef' except @atomicModifyLIORefP@ takes -- a set of privileges which are accounted for in label comparisons. atomicModifyLIORefP :: PrivDesc l p => Priv p -> LIORef l a -> (a -> (a, b)) -> LIO l b-atomicModifyLIORefP = blessPTCB atomicModifyIORef+atomicModifyLIORefP = blessPTCB "atomicModifyLIORefP" atomicModifyIORef
LIO/Label.hs view
@@ -1,141 +1,264 @@-{-# LANGUAGE Safe #-}-{- | +{-# LANGUAGE Trustworthy #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE DeriveDataTypeable #-} -Labels are a way of describing who can observe and modify data. There-is a partial order, generally pronounced \"can flow to\" on labels.-In LIO we write this partial order ``canFlowTo`` (in the literature it-is usually written as ⊑). -The idea is that data labeled @L_1@ may affect data labeled @L_2@-only if @L_1@ ``canFlowTo`` @L_2@. The 'LIO' monad (see "LIO.Core")-keeps track of the current label of the executing code (accessible via-the 'getLabel' function). Code may attempt to perform various IO or-memory operations on labeled data. Hence, touching data may change-the current label (or throw an exception if an operation would violate-flow restrictions).--If the current label is @L_cur@, then it is only permissible to read-data labeled @L_r@ if @L_r ``canFlowTo`` L_cur@. This is sometimes-termed \"no read up\" in the literature; however, because the partial-order allows for incomparable labels (i.e., two labels @L_1@ and @L_2@-such that @not (L_1 ``canFlowTo`` L_2) && not (L_2 ``canFlowTo``-L_1)@), a more appropriate phrasing would be \"read only what can flow-to your label\". Note that, rather than throw an exception, reading-data will often just increase the current label to ensure that @L_r-``canFlowTo`` L_cur@. The LIO monad keeps a second label, called the-/clearance/ (accessible via the @getClearance@ function), that-represents the highest value the current thread can raise its label-to. The purpose of clearance is to enforce discretionary access-control: you can set the clearance to a label @L_clear@ so as to prevent-a piece of LIO code from accessing anything above @L_clear@.+module LIO.Label (+ -- * Labels+ -- $Labels+ Label(..)+ -- * Privileges+ -- $Privileges+ , SpeaksFor(..), PrivDesc(..)+ , Priv, privDesc+ -- * Empty privileges+ , NoPrivs(..), noPrivs+ ) where -Conversely, it is only permissible to modify data labeled @L_w@ when-@L_cur``canFlowTo`` L_w@, a property often cited as \"no write down\",-but more accurately characterized as \"write only what you can flow-to\". In practice, there are very few IO abstractions (namely,-mutable references) in which it is possible to do a pure write that-doesn't also involve observing some state. For instance, writing to a-file handle and not getting an exception tells you that the handle is-not closed. Thus, in practice, the requirement for modifying data-labeled @L_w@ is almost always that @L_cur ``canFlowTo`` L_w@ and @L_w-``canFlowTo`` L_cur@, i.e., @L_cur == L_w@.+import safe Data.Monoid+import safe Data.Typeable -Note that higher labels are neither more nor less privileged than-lower ones. Simply, the higher one's label is, the more things one-can read. Conversely, the lower one's label, the more things one can-write. But, because labels are a partial and not a total order, some-data may be completely inaccessible to a particular computation; for-instance, if the current label is @L_cur@, the current clearance is-@C_cur@, and some data is labeled @L_d@, such that @not (L_cur-``canFlowTo`` L_d || L_d ``canFlowTo`` C_cur)@, then the current-thread can neither read nor write the data, at least without invoking-some privilege.+import LIO.TCB -LIO is polymorphic in the label type. It is solely required that every-implementation of a label (usually called a "label format") be an-instance of the 'Label' class. This class provides a generic interface-to labels: they must define the 'canFlowTo' relation, some minimal-element 'bottom', some maximum element 'top', and two binary operators-on how to combine labels: the least upper bound ('lub') and greatest-lower bound ('glb').+{- $Labels -Since LIO associates labels with different data types, it is useful to-be able to access the label of such objects (when the label is solely-protected by the current label). To this end, LIO provides the-'LabelOf' type class for which different labeled objects-implementations provide an instance.+Labels are a way of describing who can observe and modify data.+Labels are governed by a partial order, generally pronounced \"can+flow to.\" In LIO, we write this relation ``canFlowTo``. In the+literature, it is usually written ⊑. --}+At a high level, the purpose of this whole library is to ensure that+data labeled @l1@ may affect data labeled @l2@ only if @l1+``canFlowTo`` l2@. The 'LIO' monad (see "LIO.Core") ensures this by+keeping track of a /current label/ of the executing thread (accessible+via the 'getLabel' function). Code may attempt to perform various IO+or memory operations on labeled data. Touching data may change the+current label and will throw an exception in the event that an+operation would violate information flow restrictions. -module LIO.Label (- -- * Labels- Label(..)- -- * Accessing label of labeled values- , LabelOf(..) - ) where+The specific invariant maintained by 'LIO' is, first, that labels on+all previously observed data must flow to a thread's current label.+Second, the current label must flow to the labels of any future+objects the thread will be allowed to modify. Hence, after a thread+with current label @lcur@ observes data labeled @l1@, it must hold+that @l1 ``canFlowTo`` lcur@. If the thread is later permitted to+modify an object labeled @l2@, it must hold that @lcur ``canFlowTo``+l2@. By transitifity of the ``canFlowTo`` relation, it holds that @l1+``canFlowTo` l2@. +-} --- | This class defines a label format, corresponding to a bounded--- lattice (see <https://en.wikipedia.org/wiki/Bounded_lattice>).--- Specifically, it is necessary to define a bottom element--- 'bottom' (in literature, written as ⊥), a top element 'top' (in--- literature, written as ⊤), a join, or least upper bound, 'lub'--- (in literature, written as ⊔), a meet, or greatest lower bound,--- 'glb' (in literature, written as ⊓), and of course the--- can-flow-to partial-order 'canFlowTo' (in literature, written as--- ⊑).-class (Eq l, Show l) => Label l where- -- | /Least/ upper bound, or join, of two labels. For any two labels- -- @L_1@ and @L_2@, if @L_3 = L_1 \`lub` L_2@, it must be that:+-- | This class defines the operations necessary to make a label into+-- a lattice (see <http://en.wikipedia.org/wiki/Lattice_(order)>).+-- 'canFlowTo' partially orders labels.+-- 'lub' and 'glb' compute the least upper bound and greatest lower+-- bound of two labels, respectively.+class (Eq l, Show l, Typeable l) => Label l where+ -- | Compute the /least upper bound/, or join, of two labels. When+ -- data carrying two different labels is mixed together in a+ -- document, the @lub@ of the two labels is the lowest safe value+ -- with which to label the result. --- -- * @L_1 ``canFlowTo`` L_3@,+ -- More formally, for any two labels @l1@ and @l2@, if @ljoin = l1+ -- \`lub` l2@, it must be that: --- -- * @L_2 ``canFlowTo`` L_3@, and+ -- * @L_1 ``canFlowTo`` ljoin@, --- -- * There is no label @L_4 /= L_3 @ such that- -- @L_1 ``canFlowTo`` L_4@, @L_2 ``canFlowTo`` L_4@, and- -- @L_4 ``canFlowTo`` L_3@. In other words @L_3@ is the least- -- such element.+ -- * @L_2 ``canFlowTo`` ljoin@, and+ --+ -- * There is no label @l /= ljoin@ such that @l1 ``canFlowTo`` l@,+ -- @l2 ``canFlowTo`` l@, and @l ``canFlowTo`` ljoin@. In other+ -- words @ljoin@ is the least element to which both @l1@ and @l2@+ -- can flow.+ --+ -- When used infix, has fixity:+ --+ -- > infixl 5 `lub` lub :: l -> l -> l- -- | /Greatest/ lower bound, or meet, of two labels. For any two labels- -- @L_1@ and @L_2@, if @L_3 = L_1 \`glb` L_2@, it must be that:++ -- | /Greatest lower bound/, or meet, of two labels. For any two+ -- labels @l1@ and @l2@, if @lmeet = l1 \`glb` l2@, it must be+ -- that: --- -- * @L_3 ``canFlowTo`` L_1@,+ -- * @lmeet ``canFlowTo`` l1@, --- -- * @L_3 ``canFlowTo`` L_2@, and+ -- * @lmeet ``canFlowTo`` l2@, and --- -- * There is no label @L_4 /= L_3@ such that- -- @L_4 ``canFlowTo`` L_1@, @L_4 ``canFlowTo`` L_1@, and- -- @L_3 ``canFlowTo`` L_4@. In other words @L_3@ is the greatest- -- such element.+ -- * There is no label @l /= lmeet@ such that @l ``canFlowTo`` l1@,+ -- @l ``canFlowTo`` l2@, and @lmeet ``canFlowTo`` l@. In other+ -- words @lmeet@ is the greatest element flowing to both @l1@ and+ -- @l2@.+ --+ -- When used infix, has fixity:+ --+ -- > infixl 5 `glb` glb :: l -> l -> l- -- | Can-flow-to relation. An entity labeled @L_1@ should be allowed- -- to affect an entity @L_2@ only if @L_1 \`canFlowTo` L_2@. This- -- relation on labels is at least a partial order (see++ -- | /Can-flow-to/ relation (⊑). An entity labeled @l1@ should+ -- be allowed to affect an entity @l2@ only if @l1 \`canFlowTo`+ -- l2@. This relation on labels is at least a partial order (see -- <https://en.wikipedia.org/wiki/Partially_ordered_set>), and must- -- satisfy the following rules:+ -- satisfy the following laws: --- -- * Reflexivity: @L_1 \`canFlowTo` L_1@ for any @L_1@.+ -- * Reflexivity: @l1 \`canFlowTo` l1@ for any @l1@. --- -- * Antisymmetry: If @L_1 \`canFlowTo` L_2@ and- -- @L_2 \`canFlowTo` L_1@ then @L_1 = L_2@.+ -- * Antisymmetry: If @l1 \`canFlowTo` l2@ and+ -- @l2 \`canFlowTo` l1@ then @l1 = l2@. --- -- * Transitivity: If @L_1 \`canFlowTo` L_2@ and- -- @L_2 \`canFlowTo` L_3@ then @L_1 \`canFlowTo` L_3@.+ -- * Transitivity: If @l1 \`canFlowTo` l2@ and+ -- @l2 \`canFlowTo` l3@ then @l1 \`canFlowTo` l3@.+ --+ -- When used infix, has fixity:+ --+ -- > infix 4 `canFlowTo` canFlowTo :: l -> l -> Bool --- | Generic class used to get the type of labeled objects. For,--- instance, if you wish to associate a label with a pure value (as in--- "LIO.Labeled"), you may create a data type:--- --- > newtype LVal l a = LValTCB (l, a)--- --- Then, you may wish to allow untrusted code to read the label of any--- @LVal@s but not necessarily the actual value. To do so, simply--- provide an instance for @LabelOf@:--- --- > instance LabelOf LVal where--- > labelOf (LValTCB lv) = fst lv-class LabelOf t where- -- | Get the label of a type kinded @* -> *@- labelOf :: t l a -> l+infixl 5 `lub`, `glb`+infix 4 `canFlowTo`++{- $Privileges ++Privileges are objects the possesion of which allows code to bypass+certain label protections. An instance of class 'PrivDesc' describes+a pre-order (see <http://en.wikipedia.org/wiki/Preorder>) among labels+in which certain unequal labels become equivalent. A 'Priv' object+containing a 'PrivDesc' instance allows code to make those unequal+labels equivalent for the purposes of many library functions.+Effectively, a 'PrivDesc' instance /describes/ privileges, while a+'Priv' object /embodies/ them.++Any code is free to construct 'PrivDesc' values describing arbitrarily+powerful privileges. Security is enforced by preventing safe code+from accessing the constructor for 'Priv' (called 'PrivTCB'). Safe+code can construct arbitrary privileges from the 'IO' monad (using+'privInit' in "LIO.Run#v:privInit"), but cannot do so from the 'LIO'+monad. Starting from existing privileges, safe code can also+'delegate' lesser privileges (see "LIO.Delegate#v:delegate").++Privileges allow you to behave as if @l1 ``canFlowTo`` l2@ even when+that is not the case, but only for certain pairs of labels @l1@ and+@l2@; which pairs depends on the specific privileges. The process of+allowing data labeled @l1@ to infulence data labeled @l2@ when @(l1+``canFlowTo`` l2) == False@ is known as /downgrading/.++The core privilege function is 'canFlowToP', which performs a+more permissive can-flow-to check by exercising particular privileges+(in literature this relation is commonly written @⊑ₚ@ for+privileges @p@). Most core 'LIO' function have variants ending @...P@+that take a privilege argument to act in a more permissive way.++By convention, all 'PrivDesc' instances should also be instances of+'Monoid', allowing privileges to be combined with 'mappend', though+this is not enforced with superclasses.++-}+++-- | Turns privileges into a powerless description of the privileges+-- by unwrapping the 'Priv' newtype.+privDesc :: Priv a -> a+{-# INLINE privDesc #-}+privDesc (PrivTCB a) = a++-- | Every privilege type must be an instance of 'SpeaksFor', which+-- specifies when one privilege value is more powerful than another.+-- If you do not wish to allow delegation, you can simply define+-- @'speaksFor' _ _ = False@.+class (Typeable p, Show p) => SpeaksFor p where+ -- | @speaksFor p1 p2@ returns 'True' iff @p1@ subsumes all the+ -- privileges of @p2@. In other words, it is safe for 'delegate' to+ -- hand out @p2@ to a caller who already has @p1@.+ --+ -- Has fixity:+ --+ -- > infix 4 `speaksFor`+ speaksFor :: p -> p -> Bool++infix 4 `speaksFor`++-- | This class represents privilege descriptions, which define a+-- pre-order on labels in which distinct labels become equivalent.+-- The pre-oder implied by a privilege description is specified by the+-- method 'canFlowToP'. In addition, this this class defines a method+-- 'downgradeP', which is important for finding least labels+-- satisfying a privilege equivalence.+--+-- Minimal complete definition: 'downgradeP'.+--+-- (The 'downgradeP' requirement represents the fact that a generic+-- 'canFlowToP' can be implemented efficiently in terms of+-- 'downgradeP', but not vice-versa.)+class (Label l, SpeaksFor p) => PrivDesc l p where+-- Note: SpeaksFor is a superclass for security reasons. Were it not+-- a superclass, then if a label format ever failed to define+-- SpeaksFor, or defined it in a different module from the PrivDesc+-- instance, then an attacker could produce an vacuous instance that+-- allows all delegation.++ -- | Privileges are described in terms of a pre-order on labels in+ -- which sets of distinct labels become equivalent. @downgradeP p+ -- l@ returns the lowest of all labels equivalent to @l@ under+ -- privilege description @p@.+ --+ -- Less formally, @downgradeP p l@ returns a label representing+ -- the furthest you can downgrade data labeled @l@ given+ -- privileges described by @p@.+ --+ -- Yet another way to view this function is that @downgradeP p l@+ -- returns the greatest lower bound (under 'canFlowTo') of the set+ -- of all labels @l'@ such that @'canFlowToP' p l' l@.+ downgradeP :: p -- ^ Privilege description+ -> l -- ^ Label to downgrade+ -> l -- ^ Lowest label equivelent to input++ -- | @canFlowToP p l1 l2@ determines whether @p@ describes+ -- sufficient privileges to observe data labeled @l1@ and+ -- subsequently write it to an object labeled @l2@. The function+ -- returns 'True' if and only if either @canFlowTo l1 l2@ or @l1+ -- and l2@ are equivalent under @p@.+ --+ -- The default definition is:+ --+ -- > canFlowToP p l1 l2 = downgradeP p l1 `canFlowTo` l2+ -- + -- @canFlowToP@ is a method rather than a function so that it can+ -- be optimized in label-specific ways. However, custom+ -- definitions should behave identically to the default.+ canFlowToP :: p -> l -> l -> Bool+ canFlowToP p l1 l2 = downgradeP p l1 `canFlowTo` l2++instance (SpeaksFor p) => SpeaksFor (Priv p) where+ {-# INLINE speaksFor #-}+ speaksFor p1 p2 = privDesc p1 `speaksFor` privDesc p2++instance (PrivDesc l p) => PrivDesc l (Priv p) where+ {-# INLINE downgradeP #-}+ downgradeP = downgradeP . privDesc+ {-# INLINE canFlowToP #-}+ canFlowToP = canFlowToP . privDesc++--+-- NoPrivs+--++-- | Generic 'PrivDesc' used to denote the lack of privileges. Works+-- with any 'Label' type. This is only a privilege description; a+-- more useful symbol is 'noPrivs', which actually embodies the+-- @NoPrivs@ privilege.+data NoPrivs = NoPrivs deriving (Show, Read, Typeable)++instance SpeaksFor NoPrivs where speaksFor _ _ = True++-- | 'downgradeP' 'NoPrivs' is the identify function. Hence+-- 'canFlowToP' 'NoPrivs' is the same as 'canFlowTo'.+instance Label l => PrivDesc l NoPrivs where downgradeP _ l = l++instance Monoid NoPrivs where+ mempty = NoPrivs+ mappend _ _ = NoPrivs++-- | 'Priv' object corresponding to 'NoPrivs'.+noPrivs :: Priv NoPrivs+noPrivs = PrivTCB NoPrivs+
LIO/Labeled.hs view
@@ -1,7 +1,4 @@ {-# LANGUAGE Trustworthy #-}-{-# LANGUAGE CPP,- ConstraintKinds,- FlexibleContexts #-} {- | @@ -20,31 +17,30 @@ data. This module exports functions for creating labeled values ('label'), using the values protected by 'Labeled' by unlabeling them ('unlabel'), and changing the value of a labeled value without-inspection ('relabelLabeledP', 'taintLabeled', 'untaintLabeled'). A+inspection ('relabelLabeledP', 'taintLabeled'). A 'Functor'-like class ('LabeledFunctor') on 'Labeled' is also defined in this module. -} module LIO.Labeled (- Labeled+ Labeled, LabelOf(..) -- * Label values , label, labelP -- * Unlabel values , unlabel, unlabelP -- * Relabel values , relabelLabeledP- , taintLabeled, taintLabeledP , untaintLabeledP+ , taintLabeled, taintLabeledP , lFmap, lAp ) where -import Control.Monad+import safe Control.Monad -import LIO.Exception+import safe LIO.Error+import safe LIO.Label+import safe LIO.Core import LIO.TCB-import LIO.Label-import LIO.Core-import LIO.Privs -- -- Label values@@ -56,7 +52,9 @@ -- ``canFlowTo`` l && l ``canFlowTo`` ccurrent@. Otherwise an -- exception is thrown (see 'guardAlloc'). label :: Label l => l -> a -> LIO l (Labeled l a)-label = labelP noPrivs+label l a = do+ withContext "label" $ guardAlloc l+ return $ LabeledTCB l a -- | Constructs a 'Labeled' using privilege to allow the `Labeled`'s -- label to be below the current label. If the current label is@@ -68,7 +66,7 @@ -- current clearance. labelP :: PrivDesc l p => Priv p -> l -> a -> LIO l (Labeled l a) labelP p l a = do- guardAllocP p l+ withContext "labelP" $ guardAllocP p l return $ LabeledTCB l a --@@ -87,14 +85,14 @@ -- However, you can use 'labelOf' to check if 'unlabel' will succeed -- without throwing an exception. unlabel :: Label l => Labeled l a -> LIO l a-unlabel = unlabelP noPrivs+unlabel (LabeledTCB l v) = withContext "unlabel" (taint l) >> return v -- | Extracts the value of an 'Labeled' just like 'unlabel', but takes a -- privilege argument to minimize the amount the current label must be -- raised. Function will throw 'ClearanceViolation' under the same -- circumstances as 'unlabel'. unlabelP :: PrivDesc l p => Priv p -> Labeled l a -> LIO l a-unlabelP p (LabeledTCB l v) = taintP p l >> return v+unlabelP p (LabeledTCB l v) = withContext "unlabelP" (taintP p l) >> return v -- -- Relabel values@@ -113,8 +111,9 @@ relabelLabeledP :: PrivDesc l p => Priv p -> l -> Labeled l a -> LIO l (Labeled l a) relabelLabeledP p newl (LabeledTCB oldl v) = do- guardAllocP p newl- unless (canFlowToP p oldl newl) $ throwLIO InsufficientPrivs+ withContext "relabelLabeledP" $ guardAllocP p newl+ unless (canFlowToP p oldl newl) $+ labelErrorP "relabelLabeledP" p [oldl, newl] return $ LabeledTCB newl v -- | Raises the label of a 'Labeled' to the 'upperBound' of it's current@@ -124,7 +123,10 @@ -- the supplied label is not bounded then @taintLabeled@ will throw an -- exception (see 'guardAlloc'). taintLabeled :: Label l => l -> Labeled l a -> LIO l (Labeled l a)-taintLabeled = taintLabeledP noPrivs+taintLabeled l (LabeledTCB lold v) = do+ let lnew = lold `lub` l+ withContext "taintLabeled" $ guardAlloc lnew+ return $ LabeledTCB lnew v -- | Same as 'taintLabeled', but uses privileges when comparing the -- current label to the supplied label. In other words, this function@@ -134,16 +136,9 @@ => Priv p -> l -> Labeled l a -> LIO l (Labeled l a) taintLabeledP p l (LabeledTCB lold v) = do let lnew = lold `lub` l- guardAllocP p lnew+ withContext "taintLabeledP" $ guardAllocP p lnew return $ LabeledTCB lnew v --- | Downgrades a label.-untaintLabeledP :: PrivDesc l p- => Priv p -> l -> Labeled l a -> LIO l (Labeled l a)-untaintLabeledP p target lv =- relabelLabeledP p (partDowngradeP p (labelOf lv) target) lv-- {- $functor Making 'Labeled' an instance of 'Functor' is problematic because:@@ -174,7 +169,7 @@ -- Result label is joined with current label let lnew = lold `lub` l -- `label` checks for clearance violation then labels- label lnew $ f v+ withContext "lFmap" $ label lnew $ f v -- | Similar to 'ap', apply function (wrapped by 'Labeled') to the@@ -185,4 +180,4 @@ lAp (LabeledTCB lf f) (LabeledTCB la a) = do l <- getLabel let lnew = l `lub` lf `lub` la- label lnew $ f a+ withContext "lAp" $ label lnew $ f a
− LIO/Privs.hs
@@ -1,213 +0,0 @@-{-# LANGUAGE Trustworthy #-}-{-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE FlexibleInstances #-}--{- | --Privileges are objects the possesion of which allows code to bypass-some label protections. An in instance of class 'PrivDesc' describes-a pre-order among labels in which certain unequal labels become-equivalent. When wrapped in a 'Priv' type (whose constructor is-private) a 'PrivDesc' allows code to treat those labels as equivalent.--Put another way, privileges represent the ability to bypass the-protection of certain labels. Specifically, privilege allows you to-behave as if @L_1 ``canFlowTo`` L_2@ even when that is not the case.-The process of making data labeled @L_1@ affect data labeled @L_2@-when @not (L_1 ``canFlowTo`` L_2)@ is called /downgrading/.--The basic method of the 'PrivDesc' class is 'canFlowToP', which-performs a more permissive can-flow-to check by exercising particular-privileges (in literature this relation is a pre-order, commonly-written as ⊑ₚ). Almost all 'LIO' operations have variants-ending @...P@ that take a privilege argument to act in a more-permissive way.--By convention, all 'PrivDesc' instances are also be instances of-'Monoid', allowing privileges to be combined with 'mappend'. The-creation of 'PrivDesc' values is specific to the particular label type-in use; the method used is 'mintTCB', but the arguments depend on the-particular label type.---}--module LIO.Privs (- -- * Privilege descriptions- PrivDesc(..), canFlowToP, partDowngradeP- -- * Privileges- , Priv, privDesc- , NoPrivs, noPrivs- -- * Gates- -- $gateIntro- , Gate, gate, callGate- -- ** Gate example- -- $gateExample- ) where--import Data.Monoid-import LIO.Label-import LIO.TCB------- No privileges-----privDesc :: Priv a -> a-privDesc (PrivTCB a) = a---- | This class defines privileges and the more-permissive relation--- ('canFlowToP') on labels using privileges. Additionally, it defines--- 'partDowngradeP' which is used to downgrage a label up to a limit,--- given a set of privilege.-class (Label l) => PrivDesc l p where- -- | The \"can-flow-to given privileges\" pre-order used to compare- -- two labels in the presence of privileges. If @'canFlowToP' p L_1- -- L_2@ holds, then privileges @p@ are sufficient to downgrade data- -- from @L_1@ to @L_2@. Note that @'canFlowTo' L_1 L_2@ implies- -- @'canFlowToP' p L_1 L_2@ for all @p@, but for some labels and- -- privileges, 'canFlowToP' will hold even where 'canFlowTo' does- -- not.- canFlowToPrivDesc :: p -> l -> l -> Bool- canFlowToPrivDesc p a b = partDowngradePrivDesc p a b `canFlowTo` b-- -- | Roughly speaking, @L_r = partDowngradeP p L L_g@ computes how- -- close one can come to downgrading data labeled @L@ to the goal- -- label @L_g@, given privileges @p@. When @p == 'NoPrivs'@, the- -- resulting label @L_r == L ``lub`` L_g@. If @p@ contains /all/- -- possible privileges, then @L_r == L_g@.- --- -- More specifically, @L_r@ is the greatest lower bound of the- -- set of all labels @L_l@ satisfying:- --- -- 1. @ L_g ⊑ L_l@, and- --- -- 2. @ L ⊑ₚ L_l@.- --- -- Operationally, @partDowngradeP@ captures the minimum change required- -- to the current label when viewing data labeled @L_l@. A common- -- pattern is to use the result of 'getLabel' as @L_g@ (i.e., the- -- goal is to use privileges @p@ to avoid changing the label at all),- -- and then compute @L_r@ based on the label of data the code is- -- about to observe. - partDowngradePrivDesc :: p -- ^ Privileges- -> l -- ^ Label from which data must flow- -> l -- ^ Goal label- -> l -- ^ Result---- | TODO(dm): document-canFlowToP :: PrivDesc l p => Priv p -> l -> l -> Bool-canFlowToP priv = canFlowToPrivDesc (privDesc priv)---- | TODO(dm): document-partDowngradeP :: PrivDesc l p => Priv p -> l -> l -> l-partDowngradeP priv = partDowngradePrivDesc (privDesc priv)----- | Generic privilege type used to denote the lack of privileges.-data NoPrivs = NoPrivs deriving (Show, Read)--noPrivs :: Priv NoPrivs-noPrivs = PrivTCB NoPrivs--instance Monoid NoPrivs where- mempty = NoPrivs- mappend _ _ = NoPrivs---- | With lack of privileges, 'canFlowToP' is simply 'canFlowTo', and--- 'partDowngradeP' is the least 'upperBound'.-instance Label l => PrivDesc l NoPrivs where- canFlowToPrivDesc _ l1 l2 = l1 `canFlowTo` l2- partDowngradePrivDesc _ l lg = l `lub` lg---{- $gateIntro--LIO provides a basic implementation of /gates/, useful in providing-controlled RPC-like services where the client and service provider are-in mutual distrust. --A service provider uses 'gate' to create a gate data type @'Gate' d a@-given a computation of type @d -> a@. Here, @d@ is a privilege-description (type variable for an instance of 'PrivDesc'). Gates are-invoked with 'callGate', and as such the service provider has the-guarantee that the client (the caller) owns the privileges-corresponding to the privilege description @d@. In effect, this-allows a client to \"prove\" to the service provider that they own-certain privileges without entrusting the service with its privileges.-The gate computation can analyze this privilege description before-performing the \"actual\" computation. The client and server solely-need to trust the implementation of 'callGate'.---}----- | A Gate is a lambda abstraction from a privilege description to an--- arbitrary type @a@. Applying the gate is accomplished with 'callGate'--- which takes a privilege argument that is converted to a description--- before invoking the gate computation.-newtype Gate d a = GateTCB (d -> a)--- Note GateTCB is trusted by convention. Anyone with access to the--- symbol can call any gate while claiming arbitrary privileges. In--- the absence of gates, however, GateTCB doesn't provide any--- particular privileges.---- | Create a gate given a computation from a privilege description.--- Note that because of currying type 'a' may itself be a function--- type and thus gates can take arguments in addition to the privilege--- descriptoin.-gate :: (d -> a) -- ^ Gate computation- -> Gate d a-{-# INLINE gate #-}-gate = GateTCB---- | Given a gate and privilege, execute the gate computation. It is--- important to note that @callGate@ invokes the gate computation with--- the privilege description and /NOT/ the privilege itself.------ Note that, in general, code should /not/ provide privileges to--- functions other than @callGate@ when wishing to call a gate. This--- function is provided by LIO since it can be easily inspected by--- both the gate creator and caller to be doing the \"right\" thing:--- provide the privilege description corresponding to the supplied--- privilege as \"proof\" without explicitly passing in the privilege.--- -callGate :: Gate p a -- ^ Gate- -> Priv p -- ^ Privilege used as proof-of-ownership- -> a-{-# INLINE callGate #-}-callGate (GateTCB g) = g . privDesc--{- $gateExample--This example uses "LIO.DCLabel" to demonstrate the use of gates. The-service provider provides @addGate@ which adds two integers if the-gate is called by a piece of code that owns the \"Alice\" or \"Bob\"-principals. Otherwise, it simply returns @Nothing@.--> import LIO-> import LIO.DCLabel-> -> import LIO.Privs.TCB (mintTCB)-> -> -> -- | Add two numbers if the computation is invoked by Alice or Bob.-> addGate :: DCGate (Int -> Int -> Maybe Int)-> addGate = gate $ \pd a b ->-> if pd `elem` (dcPrivDesc `map` ["Alice", "Bob"])-> then Just $ a + b-> else Nothing-> -> -> alice, bob, clark :: DCPriv-> alice = PrivTCB . dcPrivDesc $ "Alice"-> bob = PrivTCB . dcPrivDesc $ "Bob"-> clark = PrivTCB . dcPrivDesc $ "Clark"-> -> main = putStrLn . show $ -> [ callGate addGate alice 1 2 -- Just 3-> , callGate addGate bob 3 4 -- Just 7-> , callGate addGate clark 5 6 -- Nothing-> ]----}
LIO/Run.hs view
@@ -9,10 +9,13 @@ -- use in invoking 'LIO' code. The functions are also available via -- "LIO" and "LIO.Core", but those modules will clutter your namespace -- with symbols you don't need in the 'IO' monad.-module LIO.Run (LIOState(..), runLIO, evalLIO) where+module LIO.Run (LIOState(..), runLIO, tryLIO, evalLIO, privInit) where -import Control.Exception-import Data.IORef+import safe Control.Exception+import safe Data.IORef+import safe Data.Typeable++import safe LIO.Label import LIO.TCB -- | Execute an 'LIO' action, returning its result and the final label@@ -28,8 +31,18 @@ s1 <- readIORef sp return (a, s1) +-- | A variant of 'runLIO' that returns results in 'Right' and+-- exceptions in 'Left', much like the standard library 'try'+-- function.+tryLIO :: LIO l a -> LIOState l -> IO (Either SomeException a, LIOState l)+tryLIO lio s0 = runLIO lio s0 >>= tryit+ where tryit (a, s) = do+ ea <- try (evaluate a)+ return (ea, s)++ -- | Given an 'LIO' computation and some initial state, return an IO--- action which when executed will perform the IFC-safe LIO+-- action which, when executed, will perform the IFC-safe LIO -- computation. -- -- Because untrusted code cannot execute 'IO' computations, this function@@ -45,3 +58,21 @@ (a, _) <- runLIO lio s return $! a +-- | Initialize some privileges (within the 'IO' monad) that can be+-- passed to 'LIO' computations run with 'runLIO' or 'evalLIO'. This+-- is a pure function, but the result is encapsulated in 'IO' to+-- make the return value inaccessible from 'LIO' computations.+--+-- Note the same effect can be achieved using the 'PrivTCB'+-- constructor, but 'PrivTCB' is easier to misuse and is only available by+-- importing "LIO.TCB".+privInit :: (SpeaksFor p) => p -> IO (Priv p)+privInit p | isPriv p = fail "privInit called on Priv object"+ | otherwise = return $ PrivTCB p++-- | Uses dynamic typing to return 'True' iff the type of the argument+-- is @'Priv' a@ (for any @a@). Mostly useful to prevent users from+-- accidentally wrapping 'Priv' objects inside other 'Priv' objects.+isPriv :: (Typeable p) => p -> Bool+isPriv p = typeRepTyCon (typeOf p) == privcon+ where privcon = typeRepTyCon $ typeOf noPrivs
LIO/TCB.hs view
@@ -4,21 +4,16 @@ {- | -This module exports --* The definition of the 'LIO' monad and relevant trusted state- access/modifying functions.--* Various other types whose constructors are privileged and must be- hidden from untrusted code.--* Uncatchable exceptions used to pop threads out of the 'LIO' monad- unconditionally.--* Combinators for executing 'IO' actions within the 'LIO' monad.+This module exports symbols that must be accessible only to trusted+code. By convention, the names of such symbols always end+\"@...TCB@\" (short for \"trusted computing base\"). In many cases, a+type is safe to export while its constructor is not. Hence, only the+constructor ends \"@TCB@\", while the type is re-exported to safe code+(without constructors) to from "LIO.Core". -The documentation and external, safe 'LIO' interface is provided in-"LIO.Core".+Security rests on the fact that untrusted code must be compiled with+@-XSafe@. Because this module is flagged unsafe, it cannot be+imported from safe modules. -} @@ -26,26 +21,26 @@ -- * LIO monad LIOState(..), LIO(..) -- ** Accessing internal state- , getLIOStateTCB, putLIOStateTCB, modifyLIOStateTCB, updateLIOStateTCB + , getLIOStateTCB, putLIOStateTCB, modifyLIOStateTCB -- * Executing IO actions , ioTCB -- * Privileged constructors- , Priv(..), Labeled(..)+ , Priv(..), Labeled(..), LabelOf(..) -- * Uncatchable exception type , UncatchableTCB(..), makeCatchable- -- * Trusted 'Show' and 'Read'- , ShowTCB(..), ReadTCB(..)+ -- * Trusted 'Show'+ , ShowTCB(..)+ -- * 'LabeledResult's+ , LabeledResult(..), LResStatus(..) ) where -import Control.Applicative-import Control.Exception (Exception(..), SomeException(..))-import Control.Monad-import Data.Monoid-import Data.IORef-import Data.Typeable-import Text.Read (minPrec)--import LIO.Label+import safe Control.Applicative+import safe Control.Exception (Exception(..), SomeException(..))+import safe qualified Control.Concurrent as IO+import safe Control.Monad+import safe Data.Monoid+import safe Data.IORef+import safe Data.Typeable -- -- LIO Monad@@ -56,31 +51,26 @@ , lioClearance :: !l -- ^ Current clearance. } deriving (Eq, Show, Read) --- | The @LIO@ monad is a state monad, with 'IO' as the underlying monad,--- that carries along a /current label/ ('lioLabel') and /current clearance/--- ('lioClearance'). The current label imposes restrictions on--- what the current computation may read and write (e.g., no writes to--- public channels after reading sensitive data). Since the current--- label can be raised to be permissive in what a computation observes,--- we need a way to prevent certain computations from reading overly--- sensitive data. This is the role of the current clearance: it imposes--- an upper bound on the current label.-newtype LIO l a = LIOTCB {- unLIOTCB :: IORef (LIOState l) -> IO a- } deriving (Typeable)+-- | The @LIO@ monad is a wrapper around 'IO' that keeps track of a+-- /current label/ and /current clearance/. Safe code cannot execute+-- arbitrary 'IO' actions from the 'LIO' monad. However, trusted+-- runtime functions can use 'ioTCB' to perform 'IO' actions (which+-- they should only do after appropriately checking labels).+newtype LIO l a = LIOTCB (IORef (LIOState l) -> IO a) deriving (Typeable) instance Monad (LIO l) where {-# INLINE return #-} return = LIOTCB . const . return {-# INLINE (>>=) #-}- m >>= k = LIOTCB $ \s -> do- a <- unLIOTCB m s- unLIOTCB (k a) s+ (LIOTCB ma) >>= k = LIOTCB $ \s -> do+ a <- ma s+ case k a of LIOTCB mb -> mb s fail = LIOTCB . const . fail instance Functor (LIO l) where- {-# INLINE fmap #-}- fmap f ma = LIOTCB $ \s -> unLIOTCB ma s >>= return . f+ fmap f (LIOTCB a) = LIOTCB $ \s -> a s >>= return . f+-- fmap typically isn't inlined, so we don't inline our definition,+-- but we do define it in terms of >>= and return (which are inlined) instance Applicative (LIO l) where {-# INLINE pure #-}@@ -105,24 +95,19 @@ putLIOStateTCB s = LIOTCB $ \sp -> writeIORef sp $! s -- | Update the internal state given some function.-modifyLIOStateTCB :: Label l => (LIOState l -> LIOState l) -> LIO l ()+modifyLIOStateTCB :: (LIOState l -> LIOState l) -> LIO l () {-# INLINE modifyLIOStateTCB #-} modifyLIOStateTCB f = do s <- getLIOStateTCB putLIOStateTCB (f s) -{-# DEPRECATED updateLIOStateTCB "Use modifyLIOStateTCB instead" #-}-updateLIOStateTCB :: Label l => (LIOState l -> LIOState l) -> LIO l ()-updateLIOStateTCB = modifyLIOStateTCB- -- -- Executing IO actions -- --- | Lifts an 'IO' computation into the 'LIO' monad. Note that--- exceptions thrown within the 'IO' computation cannot directly be--- caught within the 'LIO' computation. Thus, you will generally want to--- use 'rethrowIoTCB'.+-- | Lifts an 'IO' computation into the 'LIO' monad. This function is+-- dangerous and should only be called after appropriate checks ensure+-- the 'IO' computation will not violate IFC policy. ioTCB :: IO a -> LIO l a {-# INLINE ioTCB #-} ioTCB = LIOTCB . const@@ -131,13 +116,14 @@ -- Exception handling -- --- | An uncatchable exception hierarchy use to terminate an untrusted--- thread. Wrap the uncatchable exception in 'UncatchableTCB' before--- throwing it to the thread. 'runLIO' will subsequently unwrap the--- 'UncatchableTCB' constructor.+-- | An uncatchable exception hierarchy is used to terminate an+-- untrusted thread. Wrap the uncatchable exception in+-- 'UncatchableTCB' before throwing it to the thread. 'runLIO' will+-- subsequently unwrap the 'UncatchableTCB' constructor. -- -- Note this can be circumvented by 'IO.mapException', which should be--- made unsafe.+-- made unsafe. In the interim, auditing untrusted code for this is+-- necessary. data UncatchableTCB = forall e. (Exception e) => UncatchableTCB e deriving (Typeable) @@ -159,10 +145,13 @@ -- Privileges -- --- | A newtype wrapper that can be used by trusted code to bless--- privileges. Privilege-related functions are defined in--- "LIO.Privs", but the constructor, 'PrivTCB', allows one to mint--- arbitrary privileges and hence must be located in this file.+-- | A newtype wrapper that can be used by trusted code to transform a+-- powerless description of privileges into actual privileges. The+-- constructor, 'PrivTCB', is dangerous as it allows creation of+-- arbitrary privileges. Hence it is only exported by the unsafe+-- module "LIO.TCB". A safe way to create arbitrary privileges is to+-- call 'privInit' (see "LIO.Run#v:privInit") from the 'IO' monad+-- before running your 'LIO' computation. newtype Priv a = PrivTCB a deriving (Show, Eq, Typeable) instance Monoid p => Monoid (Priv p) where@@ -177,53 +166,75 @@ -- -- | @Labeled l a@ is a value that associates a label of type @l@ with--- a value of type @a@. Labeled values allow users to label data with--- a label other than the current label. In an embedded setting this--- is akin to having first class labeled values. Note that 'Labeled'--- is an instance of 'LabelOf', which effectively means that the label--- of a 'Labeled' value is usually just protected by the current--- label. (Of course if you have a nested labeled value then the label--- on the inner labeled value's label is the outer label.)+-- a pure value of type @a@. Labeled values allow users to label data+-- with a label other than the current label. Note that 'Labeled' is+-- an instance of 'LabelOf', which means that only the /contents/ of a+-- labeled value (the type @t@) is kept secret, not the label. Of+-- course, if you have a @Labeled@ within a @Labeled@, then the label+-- on the inner value will be protected by the outer label. data Labeled l t = LabeledTCB !l t deriving Typeable -- Note: t cannot be strict if we want things like lFmap. +-- | Trusted 'Show' instance.+instance (Show l, Show a) => ShowTCB (Labeled l a) where+ showTCB (LabeledTCB l a) = show a ++ " {" ++ show l ++ "}"++-- | Generic class used to get the type of labeled objects. For,+-- instance, if you wish to associate a label with a pure value (as in+-- "LIO.Labeled"), you may create a data type:+-- +-- > newtype LVal l a = LValTCB (l, a)+-- +-- Then, you may wish to allow untrusted code to read the label of any+-- @LVal@s but not necessarily the actual value. To do so, simply+-- provide an instance for @LabelOf@:+-- +-- > instance LabelOf LVal where+-- > labelOf (LValTCB lv) = fst lv+class LabelOf t where+ -- | Get the label of a labeled value or object. Note the label+ -- must be the second to last type constructor argument.+ labelOf :: t l a -> l+ instance LabelOf Labeled where+ {-# INLINE labelOf #-} labelOf (LabeledTCB l _) = l --- | Trusted 'Show' instance.-instance (Label l, Show a) => ShowTCB (Labeled l a) where- showTCB (LabeledTCB l t) = show t ++ " {" ++ show l ++ "}"+--+-- Trusted 'Show'+-- --- | Trusted 'Read' instance.-instance (Label l, Read l, Read a) => ReadTCB (Labeled l a) where- readsPrecTCB _ str = do (val, str1) <- reads str- ("{", str2) <- lex str1- (lab, str3) <- reads str2- ("}", rest) <- lex str3- return (LabeledTCB lab val, rest)+-- | It would be a security issue to make certain objects members of+-- the 'Show' class. Nonetheless it is useful to be able to examine+-- such objects when debugging. The 'showTCB' method can be used to+-- examine such objects.+class ShowTCB a where+ showTCB :: a -> String + ----- Trusted 'Show' and 'Read'+-- LabeledResult -- --- | It would be a security issue to make certain objects a member of--- the 'Show' class, but nonetheless it is useful to be able to--- examine such objects when debugging. The 'showTCB' method can be used--- to examine such objects.-class ShowTCB a where- showTCB :: a -> String+-- | Status of a 'LabeledResult'.+data LResStatus l a = LResEmpty+ | LResLabelTooHigh !l+ | LResResult a+ deriving (Show) --- | It is useful to have the dual of 'ShowTCB', @ReadTCB@, that allows--- for the reading of strings that were created using 'showTCB'. Only--- @readTCB@ (corresponding to 'read') and @readsPrecTCB@ (corresponding--- to 'readsPrec') are implemented.-class ReadTCB a where- -- | Trusted 'readsPrec'- readsPrecTCB :: Int -> ReadS a- -- | Trusted 'read'- readTCB :: String -> a- readTCB str = check $ readsPrecTCB minPrec str- where check [] = error "readTCB: no parse"- check [(x,rst)] | all (==' ') rst = x- | otherwise = error "readTCB: no parse"- check _ = error "readTCB: ambiguous parse"+-- | A @LabeledResult@ encapsulates a future result from a computation+-- spawned by 'lFork' or 'lForkP'. See "LIO.Concurrent" for a+-- description of the concurrency abstractions of LIO.+data LabeledResult l a = LabeledResultTCB {+ lresThreadIdTCB :: !IO.ThreadId+ -- ^ Thread executing the computation+ , lresLabelTCB :: !l+ -- ^ Label of the tresult+ , lresBlockTCB :: !(IO.MVar ())+ , lresStatusTCB :: !(IORef (LResStatus l a))+ -- ^ Result (when it is ready), or the label at which the thread+ -- terminated, if that label could not flow to 'lresLabelTCB'.+ }++instance LabelOf LabeledResult where+ labelOf = lresLabelTCB
− LIO/TCB/Concurrent.hs
@@ -1,44 +0,0 @@-{-# LANGUAGE Unsafe #-}--{- |--This module exports 'LabeledResult's which are effectively thread exit-results protected by a label. See "LIO.Concurrent" for a description-of the concurrency abstractions of LIO.---}--module LIO.TCB.Concurrent (- LabeledResult(..), LResStatus(..)- ) where--import qualified Control.Concurrent as IO-import Data.IORef--import LIO.Label--data LResStatus l a = LResEmpty- | LResLabelTooHigh !l- | LResResult a- deriving (Show)---- | A LabeledResult encapsulates a future result from a computation running--- in a thread. It holds the 'ThreadId' and an 'LMVar' where the result is--- stored. The thread referenced in 'lresThreadIdTCB' should fill in--- 'lresResultTCB' (either with a value or exception), so waiting on the thread--- should ensure that a result is ready.-data LabeledResult l a = LabeledResultTCB {- lresThreadIdTCB :: !IO.ThreadId- -- ^ Thread executing the computation- , lresLabelTCB :: !l- -- ^ Label of the tresult- , lresBlockTCB :: !(IO.MVar ())- , lresStatusTCB :: !(IORef (LResStatus l a))- -- ^ Result (when it is ready), or the label at which the thread- -- terminated, if that label could not flow to 'lresLabelTCB'.- }--instance LabelOf LabeledResult where- labelOf = lresLabelTCB--
− LIO/TCB/DCLabel.hs
@@ -1,22 +0,0 @@-{-# LANGUAGE Unsafe #-}--{-|--This module implements the trusted compoenet of DCLabel privileges,-documented in "LIO.DCLabel.Privs".-Since privilege objects may be used unsafely, this module is marked-@-XUnsafe@. Untrusted code may access privileges using the interface-provided by "LIO.DCLabel.Privs".---}--module LIO.TCB.DCLabel (allPrivTCB) where--import LIO.DCLabel.Core-import LIO.DCLabel.Privs-import LIO.TCB---- | The all privilege corresponds to logical @False@-allPrivTCB :: DCPriv-allPrivTCB = PrivTCB dcFalse-
LIO/TCB/LObj.hs view
@@ -27,25 +27,26 @@ -- around each 'Handle' using the 'LObjTCB' constructor. module LIO.TCB.LObj (LObj(..), blessTCB, blessPTCB, GuardIO(..)) where -import Data.Typeable+import safe Data.Typeable import safe LIO.Core+import safe LIO.Error import safe LIO.Label-import safe LIO.Privs import LIO.TCB --- | A "@LObj label object@" is a wrapper around an IO abstraction of--- type @object@ (such as a file handle or socket) on which it is safe--- to do @IO@ operations in the 'LIO' monad when the caller can read--- and write a particular label. It is the job of the trusted code--- constructing such a @LObj@ object to ensure both that the same IO--- object is only ever blessed with one label, and that the+-- | A \"@LObj label object@\" is a wrapper around an IO abstraction+-- of type @object@ (such as a file handle or socket) on which it is+-- safe to do @IO@ operations in the 'LIO' monad when the caller can+-- read and write a particular label. It is the job of the trusted+-- code constructing such a @LObj@ object to ensure both that the same+-- IO object is only ever blessed with one label, and that the -- abstraction combined with its blessed IO operations (see -- 'blessTCB') cannot be used to communicate with code running at -- different labels. data LObj label object = LObjTCB !label !object deriving (Typeable) instance LabelOf LObj where+ {-# INLINE labelOf #-} labelOf (LObjTCB l _) = l instance (Label l, Show t) => ShowTCB (LObj l t) where@@ -83,6 +84,7 @@ macro(a1 -> a2 -> a3 -> a4 -> a5 -> a6 -> a7 -> a8 -> a9 -> a10, \ a1 a2 a3 a4 a5 a6 a7 a8 a9 a10) +-- | Class for lifting 'IO' actions. class GuardIO l io lio | l io -> lio where -- | Lifts an 'IO' action in the 'LIO' monad, executing a guard -- before calling the function.@@ -101,17 +103,23 @@ -- one. The 'LIO' version expects a 'LObj' argument, and before -- performing any IO uses 'guardWrite' to check that the current label -- can write the label in the 'LObj' object.+-- +-- The first argument should be the name of the function being defined+-- with @blessTCB@. Its purpose is to enhance error reporting. -- -- Note that @io@ and @lio@ are function types (of up to nine -- arguments), which must be the same in all types except the monad. -- For example, if @io@ is @Int -> String -> IO ()@, then @lio@ must -- be @Int -> String -> LIO l ()@.-blessTCB :: (GuardIO l io lio, Label l) => (a -> io) -> (LObj l a) -> lio+blessTCB :: (GuardIO l io lio, Label l) =>+ String -> (a -> io) -> (LObj l a) -> lio {-# INLINE blessTCB #-}-blessTCB io (LObjTCB l a) = guardIOTCB (guardWrite l) (io a)+blessTCB name io (LObjTCB l a) =+ guardIOTCB (withContext name $ guardWrite l) (io a) -- | A variant of 'blessTCB' that takes a privilege argument. blessPTCB :: (GuardIO l io lio, PrivDesc l p) =>- (a -> io) -> Priv p -> (LObj l a) -> lio+ String -> (a -> io) -> Priv p -> (LObj l a) -> lio {-# INLINE blessPTCB #-}-blessPTCB io p (LObjTCB l a) = guardIOTCB (guardWriteP p l) (io a)+blessPTCB name io p (LObjTCB l a) =+ guardIOTCB (withContext name $ guardWriteP p l) (io a)
lio.cabal view
@@ -1,5 +1,5 @@ Name: lio-Version: 0.10.0.0+Version: 0.11.0.0 Cabal-Version: >= 1.8 Build-type: Simple License: GPL@@ -9,38 +9,49 @@ Synopsis: Labeled IO Information Flow Control Library Category: Security Description:+ The /Labeled IO/ (LIO) library is an information flow control (IFC) library. IFC is a mechanism that enforces security policies by tracking and controlling the flow of information within a system.- Different from discretionary access control (think UNIX file- permissions), with IFC you can execute an untrusted computation on- your secret data and be sure that it does not leak it or overwrite- it.+ Unlike discretionary access control (such as UNIX file permissions),+ IFC permits scenarios in which untrusted computation may have the+ ability to manipulate secret data without having the ability to+ further disclose that data. .+ LIO is an IFC library that can be used to implement such untrusted- computations. LIO provides combinators similar to those of 'IO' for- performing side-effecting computations (e.g., accessing the- filesystem, modifying mutable references, throwing exceptions, etc.)+ computations. LIO provides combinators similar to those of 'IO' for+ performing side-effecting computations (e.g., modifying mutable+ references, forking threads, throwing and catching exceptions, etc.) To track and control the flow of information, LIO associates a- security policy, usually called a /label/, with every piece of data.- A label may, for example, impose a restriction on who can observe,- propagate, or modify the data labeled as such. Different from- standard IO operations, the LIO counterparts usually take an- additional parameter for the label which they inspect before- actually performing the (underlying IO) side-effecting computation.- So, before writing to a file LIO asserts that the write will not- violate any security policies associated with the file or the data- to be written.+ security policy, called a /label/, with every piece of data. A+ label may, for example, impose a restriction on who can observe,+ propagate, or modify the data to which it applies. Unlike standard+ IO operations, the LIO counterparts check the vailidity of labels+ before performing the (underlying IO) side-effecting computation.+ For example, before writing to a labeled variable, LIO asserts that+ the write will not violate any security policies associated with the+ data to be written. . Most code should import module "LIO" and whichever label format the- application is using (e.g., "LIO.DCLabel"). All untrusted code- should have type 'LIO', which trusted code can safely execute with- 'evalLIO'. See "LIO" for a description of the core library API.+ application is using (e.g., "LIO.DCLabel" to use the format that+ ships with the library). Side-effecting code should be specified as+ actions in the 'LIO' monad. See "LIO.Core" for a description of the+ core library API, "LIO.Label" for a discussion of labels, and+ "LIO.Run" for functions allowing one to run an 'LIO' computation+ from the 'IO' monad. + . + WARNING: For security, untrusted code must always be compiled with+ the @-XSafe@ and @-fpackage-trust@ /SafeHaskell/ flags. See+ <http://www.haskell.org/ghc/docs/latest/html/users_guide/safe-haskell.html>+ for more details on the guarantees provided by SafeHaskell.++ Extra-source-files: examples/dclabel.hs examples/gate.hs@@ -56,6 +67,7 @@ base >= 4.5 && < 5.0 ,containers ,bytestring+ ,hashable >= 1.2 GHC-options: -Wall -fno-warn-orphans @@ -67,23 +79,19 @@ LIO.Label -- * Core library LIO.Core+ LIO.Error LIO.Exception -- * Labeled values LIO.Labeled -- * Labeled IORefs LIO.LIORef -- * LIO privileges- LIO.Privs+ LIO.Delegate -- * Concurrency LIO.Concurrent LIO.Concurrent.LMVar -- * DCLabels LIO.DCLabel- LIO.DCLabel.Core- LIO.DCLabel.Privs- LIO.DCLabel.DSL -- * Privileged internals LIO.TCB LIO.TCB.LObj- LIO.TCB.Concurrent- LIO.TCB.DCLabel