packages feed

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 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 &#x27f9; 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@ &#8849; @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@+--   &#8849;&#8346; @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 @&#92;&#47;@ 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 &#8849; L_2@.  The &#8849;-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 &#8897; P_2, True\> &#8849; \<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\> &#8849; \<True,P_1 &#8897; 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 /&#92;--- @----(/\) :: (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 &#8849;). -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 &#8849;. --}+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 &#8869;), a top element 'top' (in--- literature, written as &#8868;), a join, or least upper bound, 'lub'--- (in literature, written as &#8852;), a meet, or greatest lower bound,--- 'glb' (in literature, written as &#8851;), and of course the--- can-flow-to partial-order 'canFlowTo' (in literature, written as--- &#8849;).-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 (&#8849;). 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 @&#8849;&#8346;@ 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 &#8849;&#8346;).  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 &#8849; L_l@, and-    ---    --   2. @ L &#8849;&#8346; 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