lio 0.1.3 → 0.9.0.0
raw patch · 48 files changed
+3951/−7804 lines, 48 filesdep +transformersdep +xattrdep +zlibdep −QuickCheckdep −arraydep −base64-bytestringdep ~SHAdep ~basedep ~bytestringPVP ok
version bump matches the API change (PVP)
Dependencies added: transformers, xattr, zlib
Dependencies removed: QuickCheck, array, base64-bytestring, dclabel, lio, mtl, old-time, test-framework, test-framework-quickcheck2, unix
Dependency ranges changed: SHA, base, bytestring, cereal, containers, directory, filepath, time
API changes (from Hackage documentation)
- LIO.Concurrent.LMVar.TCB: isEmptyLMVar :: LabelState l p s => LMVar l a -> LIO l p s Bool
- LIO.Concurrent.LMVar.TCB: isEmptyLMVarP :: LabelState l p s => p -> LMVar l a -> LIO l p s Bool
- LIO.Concurrent.LMVar.TCB: newEmptyLMVar :: LabelState l p s => l -> LIO l p s (LMVar l a)
- LIO.Concurrent.LMVar.TCB: newEmptyLMVarP :: LabelState l p s => p -> l -> LIO l p s (LMVar l a)
- LIO.Concurrent.LMVar.TCB: newLMVar :: LabelState l p s => l -> a -> LIO l p s (LMVar l a)
- LIO.Concurrent.LMVar.TCB: newLMVarP :: LabelState l p s => p -> l -> a -> LIO l p s (LMVar l a)
- LIO.Concurrent.LMVar.TCB: putLMVar :: LabelState l p s => LMVar l a -> a -> LIO l p s ()
- LIO.Concurrent.LMVar.TCB: putLMVarP :: LabelState l p s => p -> LMVar l a -> a -> LIO l p s ()
- LIO.Concurrent.LMVar.TCB: readLMVar :: LabelState l p s => LMVar l a -> LIO l p s a
- LIO.Concurrent.LMVar.TCB: readLMVarP :: LabelState l p s => p -> LMVar l a -> LIO l p s a
- LIO.Concurrent.LMVar.TCB: swapLMVar :: LabelState l p s => LMVar l a -> a -> LIO l p s a
- LIO.Concurrent.LMVar.TCB: swapLMVarP :: LabelState l p s => p -> LMVar l a -> a -> LIO l p s a
- LIO.Concurrent.LMVar.TCB: takeLMVar :: LabelState l p s => LMVar l a -> LIO l p s a
- LIO.Concurrent.LMVar.TCB: takeLMVarP :: LabelState l p s => p -> LMVar l a -> LIO l p s a
- LIO.Concurrent.LMVar.TCB: tryPutLMVar :: LabelState l p s => LMVar l a -> a -> LIO l p s Bool
- LIO.Concurrent.LMVar.TCB: tryPutLMVarP :: LabelState l p s => p -> LMVar l a -> a -> LIO l p s Bool
- LIO.Concurrent.LMVar.TCB: tryTakeLMVar :: LabelState l p s => LMVar l a -> LIO l p s (Maybe a)
- LIO.Concurrent.LMVar.TCB: tryTakeLMVarP :: LabelState l p s => p -> LMVar l a -> LIO l p s (Maybe a)
- LIO.Concurrent.LMVar.TCB: withLMVar :: LabelState l p s => LMVar l a -> (a -> LIO l p s b) -> LIO l p s b
- LIO.Concurrent.LMVar.TCB: withLMVarP :: LabelState l p s => p -> LMVar l a -> (a -> LIO l p s b) -> LIO l p s b
- LIO.Concurrent.LMVar.TCB: withLMVarTCB :: LabelState l p s => LMVar l a -> (a -> LIO l p s b) -> LIO l p s b
- LIO.DCLabel: evalDCWithRoot :: FilePath -> Maybe DCLabel -> DC a -> IO (a, DCLabel)
- LIO.DCLabel: instance Label DCLabel
- LIO.DCLabel: instance LabelState DCLabel DCPrivTCB ()
- LIO.DCLabel: instance MintTCB TCBPriv Principal
- LIO.DCLabel: instance MintTCB TCBPriv Priv
- LIO.DCLabel: instance Priv DCLabel TCBPriv
- LIO.DCLabel: instance PrivDesc DCPrivTCB DCPriv
- LIO.DCLabel: instance PrivTCB TCBPriv
- LIO.DCLabel: instance Typeable Component
- LIO.DCLabel: instance Typeable Conj
- LIO.DCLabel: instance Typeable DCLabel
- LIO.DCLabel: instance Typeable Disj
- LIO.DCLabel: lpub :: DCLabel
- LIO.DCLabel: type DCCatSet = Component
- LIO.DCLabel: type DCPriv = Priv
- LIO.DCLabel: type DCPrivTCB = TCBPriv
- LIO.FS: cleanUpPath :: LabelState l p s => FilePath -> LIO l p s FilePath
- LIO.FS: createDirectoryTCB :: (Serialize l, Label l) => l -> FilePath -> IO ()
- LIO.FS: createFileTCB :: (Serialize l, Label l) => l -> FilePath -> IOMode -> IO Handle
- LIO.FS: data LFilePath l
- LIO.FS: evalWithRoot :: (Serialize l, LabelState l p s) => FilePath -> Maybe l -> LIO l p s a -> s -> IO (a, l)
- LIO.FS: getObjLabelTCB :: (Serialize l, LabelState l p s) => FilePath -> LIO l p s l
- LIO.FS: instance Eq NewObject
- LIO.FS: instance Eq Object
- LIO.FS: instance Exception FSErr
- LIO.FS: instance Show FSErr
- LIO.FS: instance Show NewObject
- LIO.FS: instance Show Object
- LIO.FS: instance Typeable FSErr
- LIO.FS: labelOfFilePath :: Label l => LFilePath l -> l
- LIO.FS: lookupObjPath :: (LabelState l p s, Serialize l) => FilePath -> LIO l p s (LFilePath l)
- LIO.FS: lookupObjPathP :: (LabelState l p s, Serialize l) => p -> FilePath -> LIO l p s (LFilePath l)
- LIO.FS: stripSlash :: FilePath -> FilePath
- LIO.FS: unlabelFilePath :: LabelState l p s => LFilePath l -> LIO l p s FilePath
- LIO.FS: unlabelFilePathP :: LabelState l p s => p -> LFilePath l -> LIO l p s FilePath
- LIO.FS: unlabelFilePathTCB :: Label l => LFilePath l -> FilePath
- LIO.Handle: class Monad m => CloseOps h m
- LIO.Handle: class Monad m => DirectoryOps h m | m -> h
- LIO.Handle: data LHandle l
- LIO.Handle: evalWithRoot :: (Serialize l, LabelState l p s) => FilePath -> Maybe l -> LIO l p s a -> s -> IO (a, l)
- LIO.Handle: instance (LabelState l p s, CloseOps (LHandle l) (LIO l p s), HandleOps Handle b IO) => HandleOps (LHandle l) b (LIO l p s)
- LIO.Handle: instance (Serialize l, LabelState l p s) => DirectoryOps (LHandle l) (LIO l p s)
- LIO.Handle: instance CloseOps Handle IO
- LIO.Handle: instance DirectoryOps Handle IO
- LIO.Handle: instance LabelState l p s => CloseOps (LHandle l) (LIO l p s)
- LIO.Handle: labelOfHandle :: Label l => LHandle l -> l
- LIO.Handle: writeFileL :: (HandleOps Handle b IO, LabelState l p s, Serialize l) => l -> FilePath -> b -> LIO l p s ()
- LIO.Handle: writeFileLP :: (HandleOps Handle b IO, LabelState l p s, Serialize l) => p -> l -> FilePath -> b -> LIO l p s ()
- LIO.LIORef.TCB: atomicModifyLIORef :: LabelState l p s => LIORef l a -> (a -> (a, b)) -> LIO l p s b
- LIO.LIORef.TCB: atomicModifyLIORefP :: LabelState l p s => p -> LIORef l a -> (a -> (a, b)) -> LIO l p s b
- LIO.LIORef.TCB: modifyLIORef :: LabelState l p s => LIORef l a -> (a -> a) -> LIO l p s ()
- LIO.LIORef.TCB: modifyLIORefP :: LabelState l p s => p -> LIORef l a -> (a -> a) -> LIO l p s ()
- LIO.LIORef.TCB: newLIORef :: LabelState l p s => l -> a -> LIO l p s (LIORef l a)
- LIO.LIORef.TCB: newLIORefP :: LabelState l p s => p -> l -> a -> LIO l p s (LIORef l a)
- LIO.LIORef.TCB: readLIORef :: LabelState l p s => LIORef l a -> LIO l p s a
- LIO.LIORef.TCB: readLIORefP :: LabelState l p s => p -> LIORef l a -> LIO l p s a
- LIO.LIORef.TCB: writeLIORef :: LabelState l p s => LIORef l a -> a -> LIO l p s ()
- LIO.LIORef.TCB: writeLIORefP :: LabelState l p s => p -> LIORef l a -> a -> LIO l p s ()
- LIO.MonadCatch: bracket :: MonadCatch m => m b -> (b -> m c) -> (b -> m a) -> m a
- LIO.MonadCatch: bracket_ :: MonadCatch m => m a -> m b -> m c -> m c
- LIO.MonadCatch: catch :: (MonadCatch m, Exception e) => m a -> (e -> m a) -> m a
- LIO.MonadCatch: class Monad m => MonadCatch m where mask_ io = mask $ \ _ -> io handle = flip catch onException io h = io `catch` \ e -> h >> throwIO (e :: SomeException) bracket = genericBracket onException bracket_ a b c = bracket a (const b) (const c) finally a b = mask $ \ restore -> do { r <- restore a `onException` b; _ <- b; return r }
- LIO.MonadCatch: finally :: MonadCatch m => m a -> m b -> m a
- LIO.MonadCatch: genericBracket :: MonadCatch m => (m b -> m c -> m b) -> m a -> (a -> m c) -> (a -> m b) -> m b
- LIO.MonadCatch: handle :: (MonadCatch m, Exception e) => (e -> m a) -> m a -> m a
- LIO.MonadCatch: instance [safe] MonadCatch IO
- LIO.MonadCatch: mask :: MonadCatch m => ((forall a. m a -> m a) -> m b) -> m b
- LIO.MonadCatch: mask_ :: MonadCatch m => m a -> m a
- LIO.MonadCatch: onException :: MonadCatch m => m a -> m b -> m a
- LIO.MonadCatch: throwIO :: (MonadCatch m, Exception e) => e -> m a
- LIO.MonadLIO: class (Monad m, LabelState l p s) => MonadLIO m l p s | m -> l p s where liftIO = liftLIO
- LIO.MonadLIO: instance [overlap ok] (MonadLIO m l p s, MonadTrans t, Monad (t m)) => MonadLIO (t m) l p s
- LIO.MonadLIO: instance [overlap ok] LabelState l p s => MonadLIO (LIO l p s) l p s
- LIO.MonadLIO: liftIO :: MonadLIO m l p s => LIO l p s a -> m a
- LIO.MonadLIO: liftLIO :: MonadLIO m l p s => LIO l p s a -> m a
- LIO.Safe: LerrClearance :: LabelFault
- LIO.Safe: LerrHigh :: LabelFault
- LIO.Safe: LerrInval :: String -> LabelFault
- LIO.Safe: LerrLow :: LabelFault
- LIO.Safe: LerrPriv :: LabelFault
- LIO.Safe: aguard :: LabelState l p s => l -> LIO l p s ()
- LIO.Safe: aguardP :: LabelState l p s => p -> l -> LIO l p s ()
- LIO.Safe: bracketP :: LabelState l p s => p -> LIO l p s a -> (a -> LIO l p s c) -> (a -> LIO l p s b) -> LIO l p s b
- LIO.Safe: callGate :: (LabelState l p s, PrivDesc p d) => Gate l d a -> p -> LIO l p s a
- LIO.Safe: catchP :: (Exception e, LabelState l p s) => p -> LIO l p s a -> (e -> LIO l p s a) -> LIO l p s a
- LIO.Safe: class (Eq a, Show a, Read a, Typeable a) => Label a
- LIO.Safe: class (Priv l p, Label l) => LabelState l p s | l -> s, l -> p
- LIO.Safe: class (Label l, Monoid p, PrivTCB p) => Priv l p where leqp p a b = lostar p a b `leq` b
- LIO.Safe: class (PrivTCB p, Show d) => PrivDesc p d | p -> d
- LIO.Safe: data Gate l d a
- LIO.Safe: data LIO l p s a
- LIO.Safe: data LabelFault
- LIO.Safe: data Labeled l t
- LIO.Safe: discard :: LabelState l p s => l -> LIO l p s a -> LIO l p s ()
- LIO.Safe: discardP :: LabelState l p s => p -> l -> LIO l p s a -> LIO l p s ()
- LIO.Safe: dropPrivileges :: LabelState l p s => LIO l p s ()
- LIO.Safe: evalLIO :: LabelState l p s => LIO l p s a -> s -> IO (a, l)
- LIO.Safe: evaluate :: LabelState l p s => a -> LIO l p s a
- LIO.Safe: getClearance :: LabelState l p s => LIO l p s l
- LIO.Safe: getLabel :: LabelState l p s => LIO l p s l
- LIO.Safe: getPrivileges :: LabelState l p s => LIO l p s p
- LIO.Safe: glb :: Label a => a -> a -> a
- LIO.Safe: handleP :: (Exception e, LabelState l p s) => p -> (e -> LIO l p s a) -> LIO l p s a -> LIO l p s a
- LIO.Safe: label :: LabelState l p s => l -> a -> LIO l p s (Labeled l a)
- LIO.Safe: labelOf :: Label l => Labeled l a -> l
- LIO.Safe: labelP :: LabelState l p s => p -> l -> a -> LIO l p s (Labeled l a)
- LIO.Safe: lbot :: Label a => a
- LIO.Safe: leq :: Label a => a -> a -> Bool
- LIO.Safe: leqp :: Priv l p => p -> l -> l -> Bool
- LIO.Safe: lostar :: Priv l p => p -> l -> l -> l
- LIO.Safe: lowerClr :: LabelState l p s => l -> LIO l p s ()
- LIO.Safe: lowerClrP :: LabelState l p s => p -> l -> LIO l p s ()
- LIO.Safe: ltop :: Label a => a
- LIO.Safe: lub :: Label a => a -> a -> a
- LIO.Safe: mkGate :: (LabelState l p s, PrivDesc p d) => l -> (d -> a) -> LIO l p s (Gate l d a)
- LIO.Safe: mkGateP :: (LabelState l p s, PrivDesc p d) => p -> l -> (d -> a) -> LIO l p s (Gate l d a)
- LIO.Safe: noPrivs :: Monoid p => p
- LIO.Safe: onExceptionP :: LabelState l p s => p -> LIO l p s a -> LIO l p s b -> LIO l p s a
- LIO.Safe: relabelP :: LabelState l p s => p -> l -> Labeled l a -> LIO l p s (Labeled l a)
- LIO.Safe: setLabelP :: LabelState l p s => p -> l -> LIO l p s ()
- LIO.Safe: taint :: LabelState l p s => l -> LIO l p s ()
- LIO.Safe: taintLabeled :: LabelState l p s => l -> Labeled l a -> LIO l p s (Labeled l a)
- LIO.Safe: taintP :: LabelState l p s => p -> l -> LIO l p s ()
- LIO.Safe: toLabeled :: LabelState l p s => l -> LIO l p s a -> LIO l p s (Labeled l a)
- LIO.Safe: toLabeledP :: LabelState l p s => p -> l -> LIO l p s a -> LIO l p s (Labeled l a)
- LIO.Safe: unlabel :: LabelState l p s => Labeled l a -> LIO l p s a
- LIO.Safe: unlabelP :: LabelState l p s => p -> Labeled l a -> LIO l p s a
- LIO.Safe: untaintLabeled :: LabelState l p s => l -> Labeled l a -> LIO l p s (Labeled l a)
- LIO.Safe: untaintLabeledP :: LabelState l p s => p -> l -> Labeled l a -> LIO l p s (Labeled l a)
- LIO.Safe: wguard :: LabelState l p s => l -> LIO l p s ()
- LIO.Safe: wguardP :: LabelState l p s => p -> l -> LIO l p s ()
- LIO.Safe: withClearance :: LabelState l p s => l -> LIO l p s a -> LIO l p s a
- LIO.Safe: withCombinedPrivs :: LabelState l p s => p -> (p -> LIO l p s a) -> LIO l p s a
- LIO.Safe: withPrivileges :: LabelState l p s => p -> LIO l p s a -> LIO l p s a
- LIO.TCB: Gate :: (Labeled l (d -> a)) -> Gate l d a
- LIO.TCB: LIO :: (StateT (LIOstate l p s) IO a) -> LIO l p s a
- LIO.TCB: LIOstate :: s -> l -> l -> p -> LIOstate l p s
- LIO.TCB: LerrClearance :: LabelFault
- LIO.TCB: LerrHigh :: LabelFault
- LIO.TCB: LerrInval :: String -> LabelFault
- LIO.TCB: LerrLow :: LabelFault
- LIO.TCB: LerrPriv :: LabelFault
- LIO.TCB: aguard :: LabelState l p s => l -> LIO l p s ()
- LIO.TCB: aguardP :: LabelState l p s => p -> l -> LIO l p s ()
- LIO.TCB: bracket :: MonadCatch m => m b -> (b -> m c) -> (b -> m a) -> m a
- LIO.TCB: bracketP :: LabelState l p s => p -> LIO l p s a -> (a -> LIO l p s c) -> (a -> LIO l p s b) -> LIO l p s b
- LIO.TCB: bracketTCB :: OnExceptionTCB m => m a -> (a -> m c) -> (a -> m b) -> m b
- LIO.TCB: bracket_ :: MonadCatch m => m a -> m b -> m c -> m c
- LIO.TCB: callGate :: (LabelState l p s, PrivDesc p d) => Gate l d a -> p -> LIO l p s a
- LIO.TCB: callGateTCB :: (LabelState l p s, PrivDesc p d) => Gate l d a -> p -> a
- LIO.TCB: catch :: (MonadCatch m, Exception e) => m a -> (e -> m a) -> m a
- LIO.TCB: catchP :: (Exception e, LabelState l p s) => p -> LIO l p s a -> (e -> LIO l p s a) -> LIO l p s a
- LIO.TCB: class (Eq a, Show a, Read a, Typeable a) => Label a
- LIO.TCB: class (Priv l p, Label l) => LabelState l p s | l -> s, l -> p
- LIO.TCB: class MintTCB t i
- LIO.TCB: class Monad m => MonadCatch m where mask_ io = mask $ \ _ -> io handle = flip catch onException io h = io `catch` \ e -> h >> throwIO (e :: SomeException) bracket = genericBracket onException bracket_ a b c = bracket a (const b) (const c) finally a b = mask $ \ restore -> do { r <- restore a `onException` b; _ <- b; return r }
- LIO.TCB: class MonadCatch m => OnExceptionTCB m where finallyTCB a b = mask $ \ restore -> do { r <- restore a `onExceptionTCB` b; _ <- b; return r } bracketTCB = genericBracket onExceptionTCB
- LIO.TCB: class (Label l, Monoid p, PrivTCB p) => Priv l p where leqp p a b = lostar p a b `leq` b
- LIO.TCB: class (PrivTCB p, Show d) => PrivDesc p d | p -> d
- LIO.TCB: class PrivTCB t
- LIO.TCB: data LIOstate l p s
- LIO.TCB: data LabelFault
- LIO.TCB: data Labeled l t
- LIO.TCB: discard :: LabelState l p s => l -> LIO l p s a -> LIO l p s ()
- LIO.TCB: discardP :: LabelState l p s => p -> l -> LIO l p s a -> LIO l p s ()
- LIO.TCB: dropPrivileges :: LabelState l p s => LIO l p s ()
- LIO.TCB: evalLIO :: LabelState l p s => LIO l p s a -> s -> IO (a, l)
- LIO.TCB: evaluate :: LabelState l p s => a -> LIO l p s a
- LIO.TCB: finally :: MonadCatch m => m a -> m b -> m a
- LIO.TCB: finallyTCB :: OnExceptionTCB m => m a -> m b -> m a
- LIO.TCB: getClearance :: LabelState l p s => LIO l p s l
- LIO.TCB: getLabel :: LabelState l p s => LIO l p s l
- LIO.TCB: getLabelStateTCB :: LabelState l p s => LIO l p s s
- LIO.TCB: getPrivileges :: LabelState l p s => LIO l p s p
- LIO.TCB: getTCB :: LabelState l p s => LIO l p s (LIOstate l p s)
- LIO.TCB: glb :: Label a => a -> a -> a
- LIO.TCB: handle :: (MonadCatch m, Exception e) => (e -> m a) -> m a -> m a
- LIO.TCB: handleP :: (Exception e, LabelState l p s) => p -> (e -> LIO l p s a) -> LIO l p s a -> LIO l p s a
- 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: instance Applicative (LIO l p s)
- LIO.TCB: instance Exception LabelFault
- LIO.TCB: instance Functor (LIO l p s)
- LIO.TCB: instance Label l => Show (LabeledException l)
- LIO.TCB: instance LabelState l p s => MonadCatch (LIO l p s)
- LIO.TCB: instance LabelState l p s => MonadError IOException (LIO l p s)
- LIO.TCB: instance LabelState l p s => OnExceptionTCB (LIO l p s)
- LIO.TCB: instance Monad (LIO l p s)
- LIO.TCB: instance OnExceptionTCB IO
- LIO.TCB: instance Show LabelFault
- LIO.TCB: instance Typeable LabelFault
- LIO.TCB: instance Typeable2 Labeled
- LIO.TCB: instance Typeable3 Gate
- LIO.TCB: label :: LabelState l p s => l -> a -> LIO l p s (Labeled l a)
- LIO.TCB: labelOf :: Label l => Labeled l a -> l
- LIO.TCB: labelP :: LabelState l p s => p -> l -> a -> LIO l p s (Labeled l a)
- LIO.TCB: labelState :: LIOstate l p s -> s
- LIO.TCB: labelTCB :: Label l => l -> a -> Labeled l a
- LIO.TCB: lbot :: Label a => a
- LIO.TCB: leq :: Label a => a -> a -> Bool
- LIO.TCB: leqp :: Priv l p => p -> l -> l -> Bool
- LIO.TCB: lioC :: LIOstate l p s -> l
- LIO.TCB: lioL :: LIOstate l p s -> l
- LIO.TCB: lioP :: LIOstate l p s -> p
- LIO.TCB: lostar :: Priv l p => p -> l -> l -> l
- LIO.TCB: lowerClr :: LabelState l p s => l -> LIO l p s ()
- LIO.TCB: lowerClrP :: LabelState l p s => p -> l -> LIO l p s ()
- LIO.TCB: lowerClrTCB :: LabelState l p s => l -> LIO l p s ()
- LIO.TCB: ltop :: Label a => a
- LIO.TCB: lub :: Label a => a -> a -> a
- LIO.TCB: mask :: MonadCatch m => ((forall a. m a -> m a) -> m b) -> m b
- LIO.TCB: mask_ :: MonadCatch m => m a -> m a
- LIO.TCB: mintTCB :: MintTCB t i => i -> t
- LIO.TCB: mkGate :: (LabelState l p s, PrivDesc p d) => l -> (d -> a) -> LIO l p s (Gate l d a)
- LIO.TCB: mkGateP :: (LabelState l p s, PrivDesc p d) => p -> l -> (d -> a) -> LIO l p s (Gate l d a)
- LIO.TCB: mkGateTCB :: (LabelState l p s, PrivDesc p d) => l -> (d -> a) -> Gate l d a
- LIO.TCB: newState :: LabelState l p s => s -> LIOstate l p s
- LIO.TCB: newtype Gate l d a
- LIO.TCB: noPrivs :: Monoid p => p
- LIO.TCB: onException :: MonadCatch m => m a -> m b -> m a
- LIO.TCB: onExceptionP :: LabelState l p s => p -> LIO l p s a -> LIO l p s b -> LIO l p s a
- LIO.TCB: onExceptionTCB :: OnExceptionTCB m => m a -> m b -> m a
- LIO.TCB: privDesc :: PrivDesc p d => p -> d
- LIO.TCB: putLabelStateTCB :: LabelState l p s => s -> LIO l p s ()
- LIO.TCB: putTCB :: LabelState l p s => LIOstate l p s -> LIO l p s ()
- LIO.TCB: relabelP :: LabelState l p s => p -> l -> Labeled l a -> LIO l p s (Labeled l a)
- LIO.TCB: rtioTCB :: LabelState l p s => IO a -> LIO l p s a
- LIO.TCB: runLIO :: LabelState l p s => LIO l p s a -> LIOstate l p s -> IO (a, LIOstate l p s)
- LIO.TCB: setLabelP :: LabelState l p s => p -> l -> LIO l p s ()
- LIO.TCB: setLabelTCB :: LabelState l p s => l -> LIO l p s ()
- LIO.TCB: setPrivileges :: LabelState l p s => p -> LIO l p s ()
- LIO.TCB: taint :: LabelState l p s => l -> LIO l p s ()
- LIO.TCB: taintLabeled :: LabelState l p s => l -> Labeled l a -> LIO l p s (Labeled l a)
- LIO.TCB: taintP :: LabelState l p s => p -> l -> LIO l p s ()
- LIO.TCB: throwIO :: (MonadCatch m, Exception e) => e -> m a
- LIO.TCB: toLabeled :: LabelState l p s => l -> LIO l p s a -> LIO l p s (Labeled l a)
- LIO.TCB: toLabeledP :: LabelState l p s => p -> l -> LIO l p s a -> LIO l p s (Labeled l a)
- LIO.TCB: unlabel :: LabelState l p s => Labeled l a -> LIO l p s a
- LIO.TCB: unlabelP :: LabelState l p s => p -> Labeled l a -> LIO l p s a
- LIO.TCB: unlabelTCB :: Label l => Labeled l a -> a
- LIO.TCB: untaintLabeled :: LabelState l p s => l -> Labeled l a -> LIO l p s (Labeled l a)
- LIO.TCB: untaintLabeledP :: LabelState l p s => p -> l -> Labeled l a -> LIO l p s (Labeled l a)
- LIO.TCB: wguard :: LabelState l p s => l -> LIO l p s ()
- LIO.TCB: wguardP :: LabelState l p s => p -> l -> LIO l p s ()
- LIO.TCB: withClearance :: LabelState l p s => l -> LIO l p s a -> LIO l p s a
- LIO.TCB: withCombinedPrivs :: LabelState l p s => p -> (p -> LIO l p s a) -> LIO l p s a
- LIO.TCB: withPrivileges :: LabelState l p s => p -> LIO l p s a -> LIO l p s a
+ LIO.Concurrent: HeapOverflow :: AsyncException
+ LIO.Concurrent: StackOverflow :: AsyncException
+ LIO.Concurrent: ThreadKilled :: AsyncException
+ LIO.Concurrent: UserInterrupt :: AsyncException
+ LIO.Concurrent: data AsyncException :: *
+ LIO.Concurrent: data LabeledResult l a
+ LIO.Concurrent: data ThreadId :: *
+ LIO.Concurrent: forkLIO :: Label l => LIO l () -> LIO l ThreadId
+ LIO.Concurrent: lForce :: MonadLIO l m => LabeledResult l a -> m (Labeled l (Maybe a))
+ LIO.Concurrent: lForceP :: (MonadLIO l m, Priv l p) => p -> LabeledResult l a -> m (Labeled l (Maybe a))
+ LIO.Concurrent: myThreadId :: MonadLIO l m => m ThreadId
+ LIO.Concurrent: threadDelay :: MonadLIO l m => Int -> m ()
+ LIO.Concurrent: trylWait :: MonadLIO l m => LabeledResult l a -> m (Maybe a)
+ LIO.Concurrent: trylWaitP :: (MonadLIO l m, Priv l p) => p -> LabeledResult l a -> m (Maybe a)
+ LIO.Concurrent.LMVar: data LMVar l a
+ LIO.Concurrent.LMVar: isEmptyLMVar :: MonadLIO l m => LMVar l a -> m Bool
+ LIO.Concurrent.LMVar: isEmptyLMVarP :: (MonadLIO l m, Priv l p) => p -> LMVar l a -> m Bool
+ LIO.Concurrent.LMVar: newEmptyLMVar :: MonadLIO l m => l -> m (LMVar l a)
+ LIO.Concurrent.LMVar: newEmptyLMVarP :: (MonadLIO l m, Priv l p) => p -> l -> m (LMVar l a)
+ LIO.Concurrent.LMVar: newLMVar :: Label l => l -> a -> LIO l (LMVar l a)
+ LIO.Concurrent.LMVar: newLMVarP :: (MonadLIO l m, Priv l p) => p -> l -> a -> m (LMVar l a)
+ LIO.Concurrent.LMVar: putLMVar :: Label l => LMVar l a -> a -> LIO l ()
+ LIO.Concurrent.LMVar: putLMVarP :: (MonadLIO l m, Priv l p) => p -> LMVar l a -> a -> m ()
+ LIO.Concurrent.LMVar: readLMVar :: MonadLIO l m => LMVar l a -> m a
+ LIO.Concurrent.LMVar: readLMVarP :: (MonadLIO l m, Priv l p) => p -> LMVar l a -> m a
+ LIO.Concurrent.LMVar: swapLMVar :: Label l => LMVar l a -> a -> LIO l a
+ LIO.Concurrent.LMVar: swapLMVarP :: (MonadLIO l m, Priv l p) => p -> LMVar l a -> a -> m a
+ LIO.Concurrent.LMVar: takeLMVar :: MonadLIO l m => LMVar l a -> m a
+ LIO.Concurrent.LMVar: takeLMVarP :: (MonadLIO l m, Priv l p) => p -> LMVar l a -> m a
+ LIO.Concurrent.LMVar: tryPutLMVar :: MonadLIO l m => LMVar l a -> a -> m Bool
+ LIO.Concurrent.LMVar: tryPutLMVarP :: (MonadLIO l m, Priv l p) => p -> LMVar l a -> a -> m Bool
+ LIO.Concurrent.LMVar: tryTakeLMVar :: MonadLIO l m => LMVar l a -> m (Maybe a)
+ LIO.Concurrent.LMVar: tryTakeLMVarP :: (MonadLIO l m, Priv l p) => p -> LMVar l a -> m (Maybe a)
+ LIO.Concurrent.LMVar.TCB: LMVarTCB :: !l -> MVar a -> LMVar l a
+ LIO.Concurrent.LMVar.TCB: instance LabelOf LMVar
+ LIO.Concurrent.LMVar.TCB: unlabelLMVarTCB :: LMVar l a -> MVar a
+ LIO.Concurrent.TCB: LabeledResultTCB :: ThreadId -> LMVar l (Either (LabeledException l) a) -> LabeledResult l a
+ LIO.Concurrent.TCB: data LabeledResult l a
+ LIO.Concurrent.TCB: data ThreadId :: *
+ LIO.Concurrent.TCB: instance LabelOf LabeledResult
+ LIO.Concurrent.TCB: lresResultTCB :: LabeledResult l a -> LMVar l (Either (LabeledException l) a)
+ LIO.Concurrent.TCB: lresThreadIdTCB :: LabeledResult l a -> ThreadId
+ LIO.Core: CanFlowToViolation :: MonitorFailure
+ LIO.Core: ClearanceViolation :: MonitorFailure
+ LIO.Core: CurrentLabelViolation :: MonitorFailure
+ LIO.Core: InsufficientPrivs :: MonitorFailure
+ LIO.Core: LIOState :: !l -> !l -> LIOState l
+ LIO.Core: VMonitorFailure :: MonitorFailure -> String -> VMonitorFailure
+ LIO.Core: bracket :: Label l => LIO l a -> (a -> LIO l c) -> (a -> LIO l b) -> LIO l b
+ LIO.Core: bracketP :: Priv l p => p -> LIO l a -> (a -> LIO l c) -> (a -> LIO l b) -> LIO l b
+ LIO.Core: catchLIO :: (Exception e, Label l) => LIO l a -> (e -> LIO l a) -> LIO l a
+ LIO.Core: catchLIOP :: (Exception e, Priv l p) => p -> LIO l a -> (e -> LIO l a) -> LIO l a
+ LIO.Core: class (Monad m, Label l) => MonadLIO l m | m -> l
+ LIO.Core: data LIO l a
+ LIO.Core: data LIOState l
+ LIO.Core: data LabeledException l
+ LIO.Core: data MonitorFailure
+ LIO.Core: data VMonitorFailure
+ LIO.Core: evalLIO :: Label l => LIO l a -> LIOState l -> IO a
+ LIO.Core: evaluate :: MonadLIO l m => a -> m a
+ LIO.Core: finally :: Label l => LIO l a -> LIO l b -> LIO l a
+ LIO.Core: finallyP :: Priv l p => p -> LIO l a -> LIO l b -> LIO l a
+ LIO.Core: getClearance :: MonadLIO l m => m l
+ LIO.Core: getLabel :: MonadLIO l m => m l
+ LIO.Core: guardAlloc :: MonadLIO l m => l -> m ()
+ LIO.Core: guardAllocP :: (MonadLIO l m, Priv l p) => p -> l -> m ()
+ LIO.Core: guardWrite :: MonadLIO l m => l -> m ()
+ LIO.Core: guardWriteP :: (MonadLIO l m, Priv l p) => p -> l -> m ()
+ 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: liftLIO :: MonadLIO l m => LIO l a -> m a
+ LIO.Core: lioClearance :: LIOState l -> !l
+ LIO.Core: lioLabel :: LIOState l -> !l
+ LIO.Core: monitorFailure :: VMonitorFailure -> MonitorFailure
+ LIO.Core: monitorMessage :: VMonitorFailure -> String
+ LIO.Core: onException :: Label l => LIO l a -> LIO l b -> LIO l a
+ LIO.Core: onExceptionP :: Priv l p => p -> LIO l a -> LIO l b -> LIO l a
+ LIO.Core: paranoidLIO :: Label l => LIO l a -> LIOState l -> IO (Either SomeException (a, LIOState l))
+ LIO.Core: runLIO :: Label l => LIO l a -> LIOState l -> IO (a, LIOState l)
+ LIO.Core: setClearance :: MonadLIO l m => l -> m ()
+ LIO.Core: setClearanceP :: (MonadLIO l m, Priv l p) => p -> l -> m ()
+ LIO.Core: setLabel :: MonadLIO l m => l -> m ()
+ LIO.Core: setLabelP :: (MonadLIO l m, Priv l p) => p -> l -> m ()
+ LIO.Core: taint :: MonadLIO l m => l -> m ()
+ LIO.Core: taintP :: (MonadLIO l m, Priv l p) => p -> l -> m ()
+ LIO.Core: throwLIO :: (Exception e, MonadLIO l m) => e -> m a
+ LIO.Core: tryLIO :: Label l => LIO l a -> LIOState l -> IO (Either (LabeledException l) a, LIOState l)
+ LIO.Core: withClearance :: Label l => l -> LIO l a -> LIO l a
+ LIO.Core: withClearanceP :: Priv l p => p -> l -> LIO l a -> LIO l a
+ LIO.DCLabel: clause :: Set Principal -> Clause
+ LIO.DCLabel: data Clause
+ LIO.DCLabel: data Component
+ LIO.DCLabel: data DCLabel
+ LIO.DCLabel: data Principal
+ LIO.DCLabel: dcFalse :: Component
+ LIO.DCLabel: dcFormula :: Set Clause -> Component
+ LIO.DCLabel: dcIntegrity :: DCLabel -> Component
+ LIO.DCLabel: dcLabel :: Component -> Component -> DCLabel
+ LIO.DCLabel: dcPub :: DCLabel
+ LIO.DCLabel: dcSecrecy :: DCLabel -> Component
+ LIO.DCLabel: dcTrue :: Component
+ LIO.DCLabel: defaultState :: DCState
+ LIO.DCLabel: instance LabeledFunctor DCLabel
+ LIO.DCLabel: isFalse :: Component -> Bool
+ LIO.DCLabel: isTrue :: Component -> Bool
+ LIO.DCLabel: paranoidDC :: DC a -> IO (Either SomeException (a, DCState))
+ LIO.DCLabel: principal :: S8 -> Principal
+ LIO.DCLabel: runDC :: DC a -> IO (a, DCState)
+ LIO.DCLabel: tryDC :: DC a -> IO (Either DCLabeledException a, DCState)
+ LIO.DCLabel: type DCLabeledException = LabeledException DCLabel
+ LIO.DCLabel: type DCRef = LIORef DCLabel
+ 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 :: S8 -> 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: 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: dcTrue :: Component
+ 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] Ord Clause
+ LIO.DCLabel.Core: instance [safe] Ord Principal
+ 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 :: S8 -> Principal
+ LIO.DCLabel.Core: principalName :: Principal -> S8
+ LIO.DCLabel.Core: unClause :: Clause -> Set Principal
+ LIO.DCLabel.Core: unDCFormula :: Component -> !Set Clause
+ LIO.DCLabel.DSL: (/\) :: (ToComponent a, ToComponent b) => a -> b -> Component
+ LIO.DCLabel.DSL: (\/) :: (ToComponent a, ToComponent b) => a -> b -> Component
+ LIO.DCLabel.DSL: anybody :: Component
+ LIO.DCLabel.DSL: class ToComponent a where dcPrivDesc = toComponent
+ LIO.DCLabel.DSL: dcPrivDesc :: ToComponent a => a -> DCPrivDesc
+ LIO.DCLabel.DSL: everybody :: Component
+ LIO.DCLabel.DSL: fromList :: [[Principal]] -> Component
+ 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 S8
+ LIO.DCLabel.DSL: instance [safe] ToComponent String
+ LIO.DCLabel.DSL: toComponent :: ToComponent a => a -> Component
+ LIO.DCLabel.DSL: toList :: Component -> [[Principal]]
+ LIO.DCLabel.Privs: data DCPriv
+ LIO.DCLabel.Privs: dcDelegatePriv :: DCPriv -> DCPrivDesc -> Maybe DCPriv
+ LIO.DCLabel.Privs: dcOwns :: DCPrivDesc -> Clause -> Bool
+ LIO.DCLabel.Privs: instance Monoid DCPriv
+ LIO.DCLabel.Privs: instance Priv DCLabel DCPriv
+ LIO.DCLabel.Privs: noPriv :: DCPriv
+ LIO.DCLabel.Privs: type DCPrivDesc = Component
+ LIO.DCLabel.Privs.TCB: DCPrivTCB :: DCPrivDesc -> DCPriv
+ LIO.DCLabel.Privs.TCB: allPrivTCB :: DCPriv
+ LIO.DCLabel.Privs.TCB: instance Eq DCPriv
+ LIO.DCLabel.Privs.TCB: instance MintTCB DCPriv DCPrivDesc
+ LIO.DCLabel.Privs.TCB: instance PrivDesc DCPriv DCPrivDesc
+ LIO.DCLabel.Privs.TCB: instance PrivTCB DCPriv
+ LIO.DCLabel.Privs.TCB: instance Show DCPriv
+ LIO.DCLabel.Privs.TCB: instance Typeable DCPriv
+ LIO.DCLabel.Privs.TCB: newtype DCPriv
+ LIO.DCLabel.Privs.TCB: type DCPrivDesc = Component
+ LIO.DCLabel.Privs.TCB: unDCPriv :: DCPriv -> DCPrivDesc
+ LIO.DCLabel.Serialize: instance Serialize Clause
+ LIO.DCLabel.Serialize: instance Serialize Component
+ LIO.DCLabel.Serialize: instance Serialize DCLabel
+ LIO.DCLabel.Serialize: instance Serialize Principal
+ LIO.Data.Time: getCurrentTime :: MonadLIO l m => m UTCTime
+ LIO.Data.Time: getZonedTime :: MonadLIO l m => m ZonedTime
+ LIO.Data.Time: utcToLocalZonedTime :: MonadLIO l m => UTCTime -> m ZonedTime
+ LIO.FS.TCB: FSIllegalFileName :: FSError
+ LIO.FS.TCB: FSLabelCorrupt :: FilePath -> FSError
+ LIO.FS.TCB: FSObjNeedLabel :: FSError
+ LIO.FS.TCB: FSRootCorrupt :: FSError
+ LIO.FS.TCB: FSRootExists :: FSError
+ LIO.FS.TCB: FSRootInvalid :: FSError
+ LIO.FS.TCB: FSRootNeedLabel :: FSError
+ LIO.FS.TCB: FSRootNoExist :: FSError
+ LIO.FS.TCB: LFilePathTCB :: l -> FilePath -> LFilePath l
+ LIO.FS.TCB: createDirectoryTCB :: SLabel l => l -> FilePath -> LIO l ()
+ LIO.FS.TCB: createFileTCB :: SLabel l => l -> FilePath -> IOMode -> LIO l Handle
+ LIO.FS.TCB: data FSError
+ LIO.FS.TCB: data LFilePath l
+ LIO.FS.TCB: decodeLabel :: SLabel l => AttrValue -> Either String l
+ LIO.FS.TCB: encodeLabel :: SLabel l => l -> AttrValue
+ LIO.FS.TCB: getPathLabelTCB :: SLabel l => FilePath -> LIO l l
+ LIO.FS.TCB: getRootDirTCB :: SLabel l => LIO l FilePath
+ LIO.FS.TCB: initFSTCB :: SLabel l => FilePath -> Maybe l -> LIO l ()
+ LIO.FS.TCB: instance Exception FSError
+ LIO.FS.TCB: instance Show FSError
+ LIO.FS.TCB: instance Typeable FSError
+ LIO.FS.TCB: labelOfFilePath :: LFilePath l -> l
+ LIO.FS.TCB: lazyEncodeLabel :: SLabel l => l -> L8
+ LIO.FS.TCB: mkFSTCB :: SLabel l => FilePath -> l -> LIO l ()
+ LIO.FS.TCB: setFSTCB :: SLabel l => FilePath -> LIO l ()
+ LIO.FS.TCB: setPathLabelTCB :: SLabel l => FilePath -> l -> LIO l ()
+ LIO.FS.TCB: type SLabel l = (Label l, Serialize l)
+ LIO.FS.TCB: unlabelFilePathTCB :: LFilePath l -> FilePath
+ LIO.Gate: callGate :: PrivDesc p d => Gate d a -> p -> a
+ LIO.Gate: data Gate d a
+ LIO.Gate: gate :: PrivDesc p d => (d -> a) -> Gate d a
+ LIO.Handle: BlockBuffering :: Maybe Int -> BufferMode
+ LIO.Handle: LineBuffering :: BufferMode
+ LIO.Handle: NoBuffering :: BufferMode
+ LIO.Handle: data BufferMode :: *
+ LIO.Handle: data Handle :: *
+ LIO.Handle: evalWithRootFS :: SLabel l => FilePath -> Maybe l -> LIO l a -> LIOState l -> IO a
+ LIO.Handle: hGetBuffering :: SMonadLIO l m => LabeledHandle l -> m BufferMode
+ LIO.Handle: hGetBufferingP :: (SMonadLIO l m, Priv l p) => p -> LabeledHandle l -> m BufferMode
+ LIO.Handle: hGetLine :: HandleOps h b m => h -> m b
+ LIO.Handle: hGetLineP :: (Priv l p, Serialize l, HandleOps Handle b IO) => p -> LabeledHandle l -> LIO l b
+ LIO.Handle: hIsClosed :: SMonadLIO l m => LabeledHandle l -> m Bool
+ LIO.Handle: hIsClosedP :: (SMonadLIO l m, Priv l p) => p -> LabeledHandle l -> m Bool
+ LIO.Handle: hIsEOF :: SMonadLIO l m => LabeledHandle l -> m Bool
+ LIO.Handle: hIsEOFP :: (SMonadLIO l m, Priv l p) => p -> LabeledHandle l -> m Bool
+ LIO.Handle: hIsOpen :: SMonadLIO l m => LabeledHandle l -> m Bool
+ LIO.Handle: hIsOpenP :: (SMonadLIO l m, Priv l p) => p -> LabeledHandle l -> m Bool
+ LIO.Handle: hIsReadable :: SMonadLIO l m => LabeledHandle l -> m Bool
+ LIO.Handle: hIsReadableP :: (SMonadLIO l m, Priv l p) => p -> LabeledHandle l -> m Bool
+ LIO.Handle: hIsWritable :: SMonadLIO l m => LabeledHandle l -> m Bool
+ LIO.Handle: hIsWritableP :: (SMonadLIO l m, Priv l p) => p -> LabeledHandle l -> m Bool
+ LIO.Handle: hSetBinaryMode :: SMonadLIO l m => LabeledHandle l -> Bool -> m ()
+ LIO.Handle: hSetBinaryModeP :: (SMonadLIO l m, Priv l p) => p -> LabeledHandle l -> Bool -> m ()
+ LIO.Handle: hSetBuffering :: SMonadLIO l m => LabeledHandle l -> BufferMode -> m ()
+ LIO.Handle: hSetBufferingP :: (SMonadLIO l m, Priv l p) => p -> LabeledHandle l -> BufferMode -> m ()
+ LIO.Handle: instance (SLabel l, HandleOps Handle b IO) => HandleOps (LabeledHandle l) b (LIO l)
+ LIO.Handle: type LabeledHandle l = Labeled l Handle
+ LIO.Handle: type SLabel l = (Label l, Serialize l)
+ LIO.Handle: type SMonadLIO l m = (SLabel l, MonadLIO l m)
+ LIO.LIORef: atomicModifyLIORef :: MonadLIO l m => LIORef l a -> (a -> (a, b)) -> m b
+ LIO.LIORef: atomicModifyLIORefP :: (MonadLIO l m, Priv l p) => p -> LIORef l a -> (a -> (a, b)) -> m b
+ LIO.LIORef: data LIORef l a
+ LIO.LIORef: modifyLIORef :: Label l => LIORef l a -> (a -> a) -> LIO l ()
+ LIO.LIORef: modifyLIORefP :: (MonadLIO l m, Priv l p) => p -> LIORef l a -> (a -> a) -> m ()
+ LIO.LIORef: newLIORef :: MonadLIO l m => l -> a -> m (LIORef l a)
+ LIO.LIORef: newLIORefP :: (MonadLIO l m, Priv l p) => p -> l -> a -> m (LIORef l a)
+ LIO.LIORef: readLIORef :: MonadLIO l m => LIORef l a -> m a
+ LIO.LIORef: readLIORefP :: (MonadLIO l m, Priv l p) => p -> LIORef l a -> m a
+ LIO.LIORef: writeLIORef :: MonadLIO l m => LIORef l a -> a -> m ()
+ LIO.LIORef: writeLIORefP :: (MonadLIO l m, Priv l p) => p -> LIORef l a -> a -> m ()
+ LIO.LIORef.TCB: LIORefTCB :: !l -> (IORef a) -> LIORef l a
+ LIO.LIORef.TCB: instance LabelOf LIORef
+ LIO.LIORef.TCB: unlabelLIORefTCB :: LIORef l a -> (IORef a)
+ LIO.Label: bottom :: Label l => l
+ LIO.Label: canFlowTo :: Label l => l -> l -> Bool
+ LIO.Label: class (Eq l, Show l, Typeable l) => Label l
+ LIO.Label: class LabelOf t
+ LIO.Label: glb :: Label l => l -> l -> l
+ LIO.Label: labelOf :: (LabelOf t, Label l) => t l a -> l
+ LIO.Label: lowerBound :: Label l => l -> l -> l
+ LIO.Label: lub :: Label l => l -> l -> l
+ LIO.Label: top :: Label l => l
+ LIO.Label: upperBound :: Label l => l -> l -> l
+ LIO.Labeled: class Label l => LabeledFunctor l
+ LIO.Labeled: data Labeled l t
+ LIO.Labeled: instance LabelOf Labeled
+ LIO.Labeled: lFmap :: (LabeledFunctor l, MonadLIO l m) => Labeled l a -> (a -> b) -> m (Labeled l b)
+ LIO.Labeled: label :: MonadLIO l m => l -> a -> m (Labeled l a)
+ LIO.Labeled: labelP :: (MonadLIO l m, Priv l p) => p -> l -> a -> m (Labeled l a)
+ LIO.Labeled: relabelLabeledP :: (MonadLIO l m, Priv l p) => p -> l -> Labeled l a -> m (Labeled l a)
+ LIO.Labeled: taintLabeled :: MonadLIO l m => l -> Labeled l a -> m (Labeled l a)
+ LIO.Labeled: taintLabeledP :: (MonadLIO l m, Priv l p) => p -> l -> Labeled l a -> m (Labeled l a)
+ LIO.Labeled: unlabel :: MonadLIO l m => Labeled l a -> m a
+ LIO.Labeled: unlabelP :: (MonadLIO l m, Priv l p) => p -> Labeled l a -> m a
+ LIO.Labeled: untaintLabeled :: MonadLIO l m => l -> Labeled l a -> m (Labeled l a)
+ LIO.Labeled: untaintLabeledP :: (MonadLIO l m, Priv l p) => p -> l -> Labeled l a -> m (Labeled l a)
+ LIO.Labeled.TCB: LabeledTCB :: !l -> !t -> Labeled l t
+ LIO.Labeled.TCB: data Labeled l t
+ LIO.Labeled.TCB: instance (Label l, Read l, Read a) => ReadTCB (Labeled l a)
+ LIO.Labeled.TCB: instance (Label l, Show a) => ShowTCB (Labeled l a)
+ LIO.Labeled.TCB: instance Typeable2 Labeled
+ LIO.Labeled.TCB: labelOfLabeled :: Labeled l t -> !l
+ LIO.Labeled.TCB: labelTCB :: Label l => l -> a -> Labeled l a
+ LIO.Labeled.TCB: unlabelTCB :: Labeled l t -> !t
+ LIO.Privs: NoPrivs :: NoPrivs
+ LIO.Privs: canFlowToP :: Priv l p => p -> l -> l -> Bool
+ LIO.Privs: class (Label l, PrivTCB p, Monoid p) => Priv l p where canFlowToP p a b = partDowngradeP p a b `canFlowTo` b
+ LIO.Privs: class (PrivTCB p, Show d) => PrivDesc p d | p -> d, d -> p
+ LIO.Privs: data NoPrivs
+ LIO.Privs: instance Label l => Priv l NoPrivs
+ LIO.Privs: instance MintTCB NoPrivs NoPrivs
+ LIO.Privs: instance Monoid NoPrivs
+ LIO.Privs: instance PrivDesc NoPrivs NoPrivs
+ LIO.Privs: instance PrivTCB NoPrivs
+ LIO.Privs: instance Read NoPrivs
+ LIO.Privs: instance Show NoPrivs
+ LIO.Privs: partDowngradeP :: Priv l p => p -> l -> l -> l
+ LIO.Privs: privDesc :: PrivDesc p d => p -> d
+ LIO.Privs.TCB: class PrivDesc p d => MintTCB p d
+ LIO.Privs.TCB: class (PrivTCB p, Show d) => PrivDesc p d | p -> d, d -> p
+ LIO.Privs.TCB: class PrivTCB p
+ LIO.Privs.TCB: mintTCB :: MintTCB p d => d -> p
+ LIO.Privs.TCB: privDesc :: PrivDesc p d => p -> d
+ LIO.TCB: LIOState :: !l -> !l -> LIOState l
+ LIO.TCB: LIOTCB :: StateT (LIOState l) IO a -> LIO l a
+ LIO.TCB: class (Monad m, Label l) => MonadLIO l m | m -> l
+ LIO.TCB: data LIOState l
+ LIO.TCB: getLIOStateTCB :: Label l => LIO l (LIOState l)
+ LIO.TCB: instance Applicative (LIO l)
+ LIO.TCB: instance Eq l => Eq (LIOState l)
+ LIO.TCB: instance Functor (LIO l)
+ LIO.TCB: instance Label l => MonadLIO l (LIO l)
+ LIO.TCB: instance Monad (LIO l)
+ LIO.TCB: instance Read l => Read (LIOState l)
+ LIO.TCB: instance Show l => Show (LIOState l)
+ LIO.TCB: instance Show l => Show (LabeledException l)
+ LIO.TCB: liftLIO :: MonadLIO l m => LIO l a -> m a
+ LIO.TCB: lioClearance :: LIOState l -> !l
+ LIO.TCB: lioLabel :: LIOState l -> !l
+ LIO.TCB: putLIOStateTCB :: Label l => LIOState l -> LIO l ()
+ LIO.TCB: rethrowIoTCB :: Label l => IO a -> LIO l a
+ LIO.TCB: unLIOTCB :: LIO l a -> StateT (LIOState l) IO a
+ LIO.TCB: unlabeledThrowTCB :: (Exception e, Label l) => e -> LIO l a
+ LIO.TCB: updateLIOStateTCB :: Label l => (LIOState l -> LIOState l) -> LIO l ()
- LIO.Concurrent: lFork :: LabelState l p s => l -> LIO l p s a -> LIO l p s (LRes l a)
+ LIO.Concurrent: lFork :: Label l => l -> LIO l a -> LIO l (LabeledResult l a)
- LIO.Concurrent: lForkP :: LabelState l p s => p -> l -> LIO l p s a -> LIO l p s (LRes l a)
+ LIO.Concurrent: lForkP :: Priv l p => p -> l -> LIO l a -> LIO l (LabeledResult l a)
- LIO.Concurrent: lWait :: LabelState l p s => LRes l a -> LIO l p s a
+ LIO.Concurrent: lWait :: MonadLIO l m => LabeledResult l a -> m a
- LIO.Concurrent: lWaitP :: LabelState l p s => p -> LRes l a -> LIO l p s a
+ LIO.Concurrent: lWaitP :: (MonadLIO l m, Priv l p) => p -> LabeledResult l a -> m a
- LIO.Concurrent.LMVar.TCB: isEmptyLMVarTCB :: LabelState l p s => LMVar l a -> LIO l p s Bool
+ LIO.Concurrent.LMVar.TCB: isEmptyLMVarTCB :: MonadLIO l m => LMVar l a -> m Bool
- LIO.Concurrent.LMVar.TCB: labelOfLMVar :: Label l => LMVar l a -> l
+ LIO.Concurrent.LMVar.TCB: labelOfLMVar :: LMVar l a -> !l
- LIO.Concurrent.LMVar.TCB: newEmptyLMVarTCB :: LabelState l p s => l -> LIO l p s (LMVar l a)
+ LIO.Concurrent.LMVar.TCB: newEmptyLMVarTCB :: MonadLIO l m => l -> m (LMVar l a)
- LIO.Concurrent.LMVar.TCB: newLMVarTCB :: LabelState l p s => l -> a -> LIO l p s (LMVar l a)
+ LIO.Concurrent.LMVar.TCB: newLMVarTCB :: MonadLIO l m => l -> a -> m (LMVar l a)
- LIO.Concurrent.LMVar.TCB: putLMVarTCB :: LabelState l p s => LMVar l a -> a -> LIO l p s ()
+ LIO.Concurrent.LMVar.TCB: putLMVarTCB :: MonadLIO l m => LMVar l a -> a -> m ()
- LIO.Concurrent.LMVar.TCB: readLMVarTCB :: LabelState l p s => LMVar l a -> LIO l p s a
+ LIO.Concurrent.LMVar.TCB: readLMVarTCB :: MonadLIO l m => LMVar l a -> m a
- LIO.Concurrent.LMVar.TCB: swapLMVarTCB :: LabelState l p s => LMVar l a -> a -> LIO l p s a
+ LIO.Concurrent.LMVar.TCB: swapLMVarTCB :: MonadLIO l m => LMVar l a -> a -> m a
- LIO.Concurrent.LMVar.TCB: takeLMVarTCB :: LabelState l p s => LMVar l a -> LIO l p s a
+ LIO.Concurrent.LMVar.TCB: takeLMVarTCB :: MonadLIO l m => LMVar l a -> m a
- LIO.Concurrent.LMVar.TCB: tryPutLMVarTCB :: LabelState l p s => LMVar l a -> a -> LIO l p s Bool
+ LIO.Concurrent.LMVar.TCB: tryPutLMVarTCB :: MonadLIO l m => LMVar l a -> a -> m Bool
- LIO.Concurrent.LMVar.TCB: tryTakeLMVarTCB :: LabelState l p s => LMVar l a -> LIO l p s (Maybe a)
+ LIO.Concurrent.LMVar.TCB: tryTakeLMVarTCB :: MonadLIO l m => LMVar l a -> m (Maybe a)
- LIO.DCLabel: evalDC :: DC a -> IO (a, DCLabel)
+ LIO.DCLabel: evalDC :: DC a -> IO a
- LIO.DCLabel: type DC = LIO DCLabel DCPrivTCB ()
+ LIO.DCLabel: type DC = LIO DCLabel
- LIO.DCLabel: type DCGate = Gate DCLabel DCPriv
+ LIO.DCLabel: type DCGate = Gate DCPrivDesc
- LIO.DCLabel: type DCLabeled a = Labeled DCLabel a
+ LIO.DCLabel: type DCLabeled = Labeled DCLabel
- LIO.Handle: class CloseOps h m => HandleOps h b m where hPutStr = hPut
+ LIO.Handle: class Monad m => HandleOps h b m where hPutStr = hPut
- LIO.Handle: createDirectory :: DirectoryOps h m => FilePath -> m ()
+ LIO.Handle: createDirectory :: SMonadLIO l m => l -> FilePath -> m ()
- LIO.Handle: createDirectoryP :: (LabelState l p s, Serialize l) => p -> l -> FilePath -> LIO l p s ()
+ LIO.Handle: createDirectoryP :: (SMonadLIO l m, Priv l p) => p -> l -> FilePath -> m ()
- LIO.Handle: getDirectoryContents :: DirectoryOps h m => FilePath -> m [FilePath]
+ LIO.Handle: getDirectoryContents :: SMonadLIO l m => FilePath -> m [FilePath]
- LIO.Handle: getDirectoryContentsP :: (LabelState l p s, Serialize l) => p -> FilePath -> LIO l p s [FilePath]
+ LIO.Handle: getDirectoryContentsP :: (SMonadLIO l m, Priv l p) => p -> FilePath -> m [FilePath]
- LIO.Handle: hClose :: CloseOps h m => h -> m ()
+ LIO.Handle: hClose :: SMonadLIO l m => LabeledHandle l -> m ()
- LIO.Handle: hCloseP :: LabelState l p s => p -> LHandle l -> LIO l p s ()
+ LIO.Handle: hCloseP :: (SMonadLIO l m, Priv l p) => p -> LabeledHandle l -> m ()
- LIO.Handle: hFlush :: CloseOps h m => h -> m ()
+ LIO.Handle: hFlush :: SMonadLIO l m => LabeledHandle l -> m ()
- LIO.Handle: hFlushP :: LabelState l p s => p -> LHandle l -> LIO l p s ()
+ LIO.Handle: hFlushP :: (SMonadLIO l m, Priv l p) => p -> LabeledHandle l -> m ()
- LIO.Handle: hGetContentsP :: (LabelState l p s, HandleOps Handle b IO) => p -> LHandle l -> LIO l p s b
+ LIO.Handle: hGetContentsP :: (Priv l p, Serialize l, HandleOps Handle b IO) => p -> LabeledHandle l -> LIO l b
- LIO.Handle: hGetNonBlockingP :: (LabelState l p s, HandleOps Handle b IO) => p -> LHandle l -> Int -> LIO l p s b
+ LIO.Handle: hGetNonBlockingP :: (Priv l p, Serialize l, HandleOps Handle b IO) => p -> LabeledHandle l -> Int -> LIO l b
- LIO.Handle: hGetP :: (LabelState l p s, HandleOps Handle b IO) => p -> LHandle l -> Int -> LIO l p s b
+ LIO.Handle: hGetP :: (Priv l p, Serialize l, HandleOps Handle b IO) => p -> LabeledHandle l -> Int -> LIO l b
- LIO.Handle: hPutP :: (LabelState l p s, HandleOps Handle b IO) => p -> LHandle l -> b -> LIO l p s ()
+ LIO.Handle: hPutP :: (Priv l p, Serialize l, HandleOps Handle b IO) => p -> LabeledHandle l -> b -> LIO l ()
- LIO.Handle: hPutStrLnP :: (LabelState l p s, HandleOps Handle b IO) => p -> LHandle l -> b -> LIO l p s ()
+ LIO.Handle: hPutStrLnP :: (Priv l p, Serialize l, HandleOps Handle b IO) => p -> LabeledHandle l -> b -> LIO l ()
- LIO.Handle: hPutStrP :: (LabelState l p s, HandleOps Handle b IO) => p -> LHandle l -> b -> LIO l p s ()
+ LIO.Handle: hPutStrP :: (Priv l p, Serialize l, HandleOps Handle b IO) => p -> LabeledHandle l -> b -> LIO l ()
- LIO.Handle: openFile :: DirectoryOps h m => FilePath -> IOMode -> m h
+ LIO.Handle: openFile :: SMonadLIO l m => Maybe l -> FilePath -> IOMode -> m (LabeledHandle l)
- LIO.Handle: openFileP :: (LabelState l p s, Serialize l) => p -> Maybe l -> FilePath -> IOMode -> LIO l p s (LHandle l)
+ LIO.Handle: openFileP :: (SMonadLIO l m, Priv l p) => p -> Maybe l -> FilePath -> IOMode -> m (LabeledHandle l)
- LIO.Handle: readFile :: (DirectoryOps h m, HandleOps h b m) => FilePath -> m b
+ LIO.Handle: readFile :: (HandleOps Handle b IO, SLabel l) => FilePath -> LIO l b
- LIO.Handle: readFileP :: (HandleOps Handle b IO, LabelState l p s, Serialize l) => p -> FilePath -> LIO l p s b
+ LIO.Handle: readFileP :: (HandleOps Handle b IO, Priv l p, Serialize l) => p -> FilePath -> LIO l b
- LIO.Handle: writeFile :: (DirectoryOps h m, HandleOps h b m, OnExceptionTCB m) => FilePath -> b -> m ()
+ LIO.Handle: writeFile :: (HandleOps Handle b IO, SLabel l) => l -> FilePath -> b -> LIO l ()
- LIO.Handle: writeFileP :: (HandleOps Handle b IO, LabelState l p s, Serialize l) => p -> FilePath -> b -> LIO l p s ()
+ LIO.Handle: writeFileP :: (HandleOps Handle b IO, Priv l p, Serialize l) => p -> l -> FilePath -> b -> LIO l ()
- LIO.LIORef.TCB: atomicModifyLIORefTCB :: LabelState l p s => LIORef l a -> (a -> (a, b)) -> LIO l p s b
+ LIO.LIORef.TCB: atomicModifyLIORefTCB :: MonadLIO l m => LIORef l a -> (a -> (a, b)) -> m b
- LIO.LIORef.TCB: labelOfLIORef :: Label l => LIORef l a -> l
+ LIO.LIORef.TCB: labelOfLIORef :: LIORef l a -> !l
- LIO.LIORef.TCB: modifyLIORefTCB :: LabelState l p s => LIORef l a -> (a -> a) -> LIO l p s ()
+ LIO.LIORef.TCB: modifyLIORefTCB :: MonadLIO l m => LIORef l a -> (a -> a) -> m ()
- LIO.LIORef.TCB: newLIORefTCB :: LabelState l p s => l -> a -> LIO l p s (LIORef l a)
+ LIO.LIORef.TCB: newLIORefTCB :: MonadLIO l m => l -> a -> m (LIORef l a)
- LIO.LIORef.TCB: readLIORefTCB :: LabelState l p s => LIORef l a -> LIO l p s a
+ LIO.LIORef.TCB: readLIORefTCB :: MonadLIO l m => LIORef l a -> m a
- LIO.LIORef.TCB: writeLIORefTCB :: LabelState l p s => LIORef l a -> a -> LIO l p s ()
+ LIO.LIORef.TCB: writeLIORefTCB :: MonadLIO l m => LIORef l a -> a -> m ()
- LIO.TCB: LabeledExceptionTCB :: l -> SomeException -> LabeledException l
+ LIO.TCB: LabeledExceptionTCB :: !l -> SomeException -> LabeledException l
- LIO.TCB: catchTCB :: LabelState l p s => LIO l p s a -> (LabeledException l -> LIO l p s a) -> LIO l p s a
+ LIO.TCB: catchTCB :: Label l => LIO l a -> (LabeledException l -> LIO l a) -> LIO l a
- LIO.TCB: ioTCB :: LabelState l p s => IO a -> LIO l p s a
+ LIO.TCB: ioTCB :: Label l => IO a -> LIO l a
- LIO.TCB: newtype LIO l p s a
+ LIO.TCB: newtype LIO l a
Files
- LIO.hs +35/−59
- LIO/Concurrent.hs +190/−79
- LIO/Concurrent/LMVar.hs +217/−13
- LIO/Concurrent/LMVar/Safe.hs +0/−22
- LIO/Concurrent/LMVar/TCB.hs +73/−234
- LIO/Concurrent/TCB.hs +31/−0
- LIO/Core.hs +595/−0
- LIO/DCLabel.hs +94/−108
- LIO/DCLabel/Core.hs +275/−0
- LIO/DCLabel/DSL.hs +155/−0
- LIO/DCLabel/Privs.hs +100/−0
- LIO/DCLabel/Privs/TCB.hs +45/−0
- LIO/DCLabel/Serialize.hs +31/−0
- LIO/Data/Time.hs +37/−0
- LIO/FS.hs +0/−652
- LIO/FS/TCB.hs +267/−0
- LIO/Gate.hs +97/−0
- LIO/Handle.hs +469/−262
- LIO/LIORef.hs +138/−12
- LIO/LIORef/Safe.hs +0/−19
- LIO/LIORef/TCB.hs +67/−135
- LIO/Label.hs +160/−0
- LIO/Labeled.hs +187/−0
- LIO/Labeled/TCB.hs +52/−0
- LIO/MonadCatch.hs +0/−88
- LIO/MonadLIO.hs +0/−31
- LIO/Privs.hs +96/−0
- LIO/Privs/TCB.hs +57/−0
- LIO/Safe.hs +0/−60
- LIO/TCB.hs +185/−1201
- System/Posix/Tmp.hsc +0/−112
- cbits/HsTmp.c +0/−19
- configure +0/−3666
- configure.ac +0/−13
- examples/LambdaChair/AliceCode.hs +0/−25
- examples/LambdaChair/BobCode.hs +0/−20
- examples/LambdaChair/LambdaChair.hs +0/−419
- examples/LambdaChair/Main.hs +0/−38
- examples/LambdaChair/Safe.hs +0/−10
- examples/dclabel.hs +47/−0
- examples/fsExample.hs +126/−49
- examples/gate.hs +24/−0
- examples/waitAndCatch.hs +11/−11
- include/HsTmp.h +0/−22
- include/HsTmpConfig.h +0/−29
- include/HsTmpConfig.h.in +0/−28
- lio.cabal +90/−95
- tests/Tests.hs +0/−273
LIO.hs view
@@ -1,67 +1,43 @@-{-# LANGUAGE CPP #-}-#if __GLASGOW_HASKELL__ >= 702 {-# LANGUAGE Safe #-}-#endif--{- | This is the main module to be included by code using the Labeled- IO (LIO) library. The core of the library is documented in the- "LIO.TCB" module. Note, however, that unprivileged code must not- be allowed to import "LIO.TCB"--instead, a module "LIO.Safe"- exports just the safe symbols from "LIO.TCB". This module,- "LIO", re-exports "LIO.Safe" as well as a few other handy- modules. For many modules it should be the only import necessary.-- Certain symbols in the LIO library supersede variants in the- standard Haskell libraries. Thus, depending on the modules- imported and functions used, you may wish to import LIO with- commands like these:-- @- import Prelude hiding ('catch')- import Control.Exception hiding ('throwIO', 'catch', 'handle', 'onException'- , 'bracket', 'block', 'unblock')- import LIO- @-- The LIO variants of the system functions hidden in the above import- commands are designed to work in both the IO and LIO monads, making- it easier to have both types of code in the same module.--- Warning: For security, at a minimum untrusted code must not be- allowed to do any of the following:-- * Import "LIO.TCB",-- * Use any symbols with names ending @...TCB@,-- * Use the @foreign@ keyword,+{- | - * Use functions such as 'unsafePerformIO', 'unsafeInterleaveIO',- 'inlinePerformIO',+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"), privileges (documented in "LIO.Privs"), and gates+(documented in "LIO.Gate"). - * Use language extensions such as Generalized Newtype- Deriving and Stand-alone Deriving to extend LIO types- (such as by deriving an instance of @'Show'@ for 'Lref',- or deriving an instance of the @'MonadTrans'@ class for- 'LIO', which would allow untrusted code to bypass all- security with 'lift'),+Certain symbols in the LIO library supersede variants in the+standard Haskell libraries. Thus, depending on the modules+imported and functions used, you may wish to import LIO with+commands like these: - * Manually define @typeOf@ methods (as this would cause the- supposedly safe 'cast' method to make usafe casts); automatically- deriving @Typeable@ should be safe.+@+ import Control.Exception hiding ( 'onException'+ , 'finally'+ , 'bracket')+ import "LIO"+ -- Import your favorite label format:+ import "LIO.DCLabel"+@ - * Define new 'Ix' instances (which could produce out of bounds- array references).+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. - In general, pragmas and imports should be highly scrutinized.- For example, most of the "Foreign" class of modules are probably- dangerous. With GHC 7.2, we use the SafeHaskell extension to enforce- these restrictions. -}-module LIO ( module LIO.Safe- , module LIO.MonadLIO- ) where -import safe LIO.Safe-import safe LIO.MonadLIO+module LIO ( + module LIO.Label+ , module LIO.Core+ , module LIO.Labeled+ , module LIO.Privs+ , module LIO.Gate+ ) where++import LIO.Label+import LIO.Core+import LIO.Labeled+import LIO.Privs+import LIO.Gate
LIO/Concurrent.hs view
@@ -1,102 +1,213 @@-{-# LANGUAGE CPP #-}-#if __GLASGOW_HASKELL__ >= 702 {-# LANGUAGE Trustworthy #-}-#endif--- |This module exposes the concurrent interface to LIO. Specifically,--- it implements labeled /fork/ ('lFork') and /wait/ ('lWait'). We--- strongly suggest these primitives over 'toLabeled' as they are--- termination-senstivie.-module LIO.Concurrent ( lForkP, lFork- , lWaitP, lWait- ) where+{- | -import LIO.TCB-import LIO.Concurrent.LMVar.TCB-import Control.Concurrent-import Control.Exception (toException)-import Data.Functor+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. --- | An LIO fork.-forkLIO :: (LabelState l p s) => LIO l p s () -> LIO l p s ThreadId-forkLIO m = do- s <- getTCB- ioTCB . forkIO $ runLIO m s >> return ()+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. +-}+module LIO.Concurrent (+ LabeledResult+ , ThreadId, myThreadId+ -- * Forking new threads+ , forkLIO, lForkP, lFork+ -- * Waiting on threads+ , lWaitP, lWait+ , trylWaitP, trylWait+ , threadDelay+ -- * Forcing computations (EXPERIMENTAL)+ , AsyncException(..)+ , lForce, lForceP+ ) where --- | Same as 'lFork', but the supplied set of priviliges are accounted--- for when performing label comparisons.-lForkP :: (LabelState l p s)- => p -> l -> LIO l p s a -> LIO l p s (LRes l a)-lForkP p' l m = withCombinedPrivs p' $ \p -> do- mv <- newEmptyLMVarP p l- _ <- forkLIO $ do- res <- (Right <$> m) `catchTCB` (return . Left . (lubErr l))- lastL <- getLabel- putLMVarTCB mv (if leqp p lastL l- then res- else let l' = lub lastL l- in Left . mkErr . (lub l') $- either getELabel (const l') res- ) - return $ LRes mv- where mkErr le = LabeledExceptionTCB le $ toException LerrLow- lubErr lnew (LabeledExceptionTCB le e) =- LabeledExceptionTCB (le `lub` lnew) e- getELabel (LabeledExceptionTCB le _) = le+import Control.Monad+import Control.Concurrent hiding ( myThreadId, threadDelay )+import qualified Control.Concurrent as C+import Control.Exception ( toException+ , Exception+ , AsyncException(..))+ +import LIO.Label+import LIO.Core+import LIO.Labeled+import LIO.Labeled.TCB+import LIO.Privs+import LIO.TCB+ +import LIO.Concurrent.TCB+import LIO.Concurrent.LMVar+import LIO.Concurrent.LMVar.TCB (putLMVarTCB) --- | Labeled fork. @lFork@ allows one to invoke computations taht++-- | Get the 'ThreadId' of the calling thread.+myThreadId :: MonadLIO l m => m ThreadId+myThreadId = liftLIO $ ioTCB C.myThreadId++--+-- Fork+--++-- | Execute an 'LIO' computation in a new lightweight thread. The+-- 'ThreadId' of the newly created thread is returned.+forkLIO :: Label l => LIO l () -> LIO l ThreadId+forkLIO act = do+ s <- getLIOStateTCB+ ioTCB . forkIO . void $ tryLIO act s+++-- | Labeled fork. @lFork@ allows one to invoke computations that -- would otherwise raise the current label, but without actually -- raising the label. The computation is executed in a separate thread -- and writes its result into a labeled result (whose label is -- supplied). To observe the result of the computation, or if the -- computation has terminated, one will have to call 'lWait' and--- raise the current label. Of couse, as in 'toLabeled', this can be--- postponed until the result is needed.+-- raise the current label. Of couse, this can be postponed until the+-- result is needed. ----- @lFork@ takes a label, which corresponds to the label of the--- result. It is require that this label is above the current label,--- and below the current clearance. Moreover, the supplied computation--- must not read anything more sensitive, i.e., with a label above the--- supplied label --- doing so will result in an exception (whose--- label will reflect this observation) being thrown. +-- @lFork@ takes a label, which corresponds to the label of the result.+-- It is require that this label is above the current label, and below+-- the current clearance as enforced by the underlying 'guardAlloc'.+-- Moreover, the supplied computation must not read anything more+-- sensitive (i.e., with a label above the supplied label) --- doing so+-- will result in an exception (whose label will reflect this+-- observation) being thrown. -- -- 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. -- --- Not that, compared to 'toLabeled', @lFork@ immediately returns a--- labeled result of type 'LRes', which is essentially a \"future\",--- or \"promise\". Moreover, to guarantee that the computation has--- completed, it is important that some thread actually touch the--- future, i.e., perform an 'lWait'.-lFork :: (LabelState l p s)- => l -- ^ Label of result- -> LIO l p s a -- ^ Computation to execute in separate thread- -> LIO l p s (LRes l a) -- ^ Labeled result-lFork = lForkP noPrivs+-- Note that @lFork@ immediately returns a 'LabeledResult', which is+-- essentially a \"future\", or \"promise\". This prevents+-- timing/termination attacks in which the duration of the forked+-- computation affects the duration of the @lFork@.+-- +-- To guarantee that the computation has completed, it is important that+-- some thread actually touch the future, i.e., perform an 'lWait'.+lFork :: Label l+ => l -- ^ Label of result+ -> LIO l a -- ^ Computation to execute in separate thread+ -> LIO l (LabeledResult l a) -- ^ Labeled result+lFork = lForkP NoPrivs --- | A labeled thread result is simply a wrapper for a labeled MVar. A--- thread can observe the result of another thread, only after raising--- its label to the label of the result.-data LRes l a = LRes (LMVar l (Either (LabeledException l) a))+-- | Same as 'lFork', but the supplied set of priviliges are accounted+-- for when performing label comparisons.+lForkP :: Priv l p+ => p -> l -> LIO l a -> LIO l (LabeledResult l a)+lForkP p l act = do+ -- Upperbound is between current label and clearance, asserted by+ -- 'newEmptyLMVarP', otherwise add: guardAllocP p l+ mv <- newEmptyLMVarP p l+ tid <- forkLIO $ do+ res <- (Right `liftM` act) `catchTCB` (return . Left . taintError)+ endLabel <- getLabel+ putLMVarTCB mv $! + let le = endLabel `upperBound` l+ m = "End label does not flow to specified upper bound"+ e = VMonitorFailure { monitorFailure = CanFlowToViolation+ , monitorMessage = m }+ in if canFlowToP p endLabel l+ then res+ else Left $! LabeledExceptionTCB le (toException e)+ return $ LabeledResultTCB { lresThreadIdTCB = tid, lresResultTCB = mv }+ where -- raise the label of the exception to the join of the+ -- exception label and supplied lForkP upper bound+ taintError (LabeledExceptionTCB le e) =+ LabeledExceptionTCB (le `upperBound` l) e --- | Same as 'lWait', but uses priviliges in label checks and raises.-lWaitP :: (LabelState l p s) => p -> LRes l a -> LIO l p s a-lWaitP p' (LRes mv) = withCombinedPrivs p' $ \p -> do- ea <- takeLMVarP p mv- case ea of- Right x -> return x- Left e -> ioTCB $ throwIO e+--+-- Wait+-- --- | Given a labeled result (a future), @lWait@ returns the unwrapped+-- | Given a 'LabeledResult' (a future), @lWait@ returns the unwrapped -- result (blocks, if necessary). For @lWait@ to succeed, the label of -- the result must be above the current label and below the current--- clearnce. Moreover, before block-reading, @lWait@ raises the current--- label to the join of the current label and label of result.--- If the thread @lWait@ is terminates with an exception (for example--- if it violates clearance), the exceptin is rethrown. Similarly, if --- the thread reads values above the result label, an exception is--- thrown in place of the result.-lWait :: (LabelState l p s) => LRes l a -> LIO l p s a-lWait = lWaitP noPrivs+-- 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+-- 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,+-- an exception is thrown in place of the result.+lWait :: MonadLIO l m => LabeledResult l a -> m a+lWait = lWaitP NoPrivs++-- | Same as 'lWait', but uses priviliges in label checks and raises.+lWaitP :: (MonadLIO l m, Priv l p) => p -> LabeledResult l a -> m a+lWaitP p m = do+ v <- readLMVarP p $ lresResultTCB m+ case v of+ Right x -> return x+ Left e -> liftLIO $ unlabeledThrowTCB e++-- | Same as 'lWait', but does not block waiting for result.+trylWait :: MonadLIO l m => LabeledResult l a -> m (Maybe a)+trylWait = trylWaitP NoPrivs++-- | Same as 'trylWait', but uses priviliges in label checks and raises.+trylWaitP :: (MonadLIO l m, Priv l p) => p -> LabeledResult l a -> m (Maybe a)+trylWaitP p m = do+ let mvar = lresResultTCB m+ mv <- tryTakeLMVarP p mvar+ case mv of+ Just v -> do putLMVarP p mvar v+ case v of+ Right x -> return $! Just x+ Left e -> liftLIO $ unlabeledThrowTCB e+ _ -> return Nothing+++-- | Suspend current thread for a given number of microseconds.+threadDelay :: MonadLIO l m => Int -> m ()+threadDelay = liftLIO . ioTCB . C.threadDelay++--+-- Forcing computations+--++-- | Force execution of a thread spawned with 'lFork' or 'lForkP'.+-- If the computation completed without raising an exception, then the+-- result is wrapped in a 'Just', otherwise this function returns+-- 'Nothing'. Note that the current computation must be able to read+-- and write at the security level of the labeled result.+lForce :: MonadLIO l m => LabeledResult l a -> m (Labeled l (Maybe a))+lForce = lForceP NoPrivs++-- | Same as 'lForce', but uses privileges when raising current label+-- to the join of the current label and result label.+lForceP :: (MonadLIO l m, Priv l p)+ => p -> LabeledResult l a -> m (Labeled l (Maybe a))+lForceP p m = do+ let l = labelOf m+ mv = lresResultTCB m+ guardWriteP p l+ -- kill thread:+ liftLIO . ioTCB . killThread . lresThreadIdTCB $ m+ -- check to see if it wrote to MVar:+ v <- tryTakeLMVarP p mv+ return . labelTCB l =<< case v of+ Nothing -> do+ let e = toException ThreadKilled+ putLMVarTCB mv $ Left (LabeledExceptionTCB l e)+ return Nothing+ Just v' -> do+ putLMVarTCB mv v'+ case v' of+ Left _ -> return Nothing+ Right x -> return (Just x)
LIO/Concurrent/LMVar.hs view
@@ -1,14 +1,218 @@-{-# LANGUAGE CPP #-}-#if defined(__GLASGOW_HASKELL__) && (__GLASGOW_HASKELL__ >= 702)-{-# LANGUAGE Safe #-}-#endif--- |This module implements labeled @MVar@s. The interface is analogous--- to "Control.Concurrent.MVar", but the operations take place in--- the LIO monad. Moreover, taking and putting @MVars@ calls a write--- guard @wguard@ as every read implies a write and vice versa. This--- module exports only the safe subset (non-TCB) of the @LMVar@ module--- trusted code can import "LIO.Concurrent.LMVar.TCB".--- The interface is documented in "LIO.Concurrent.LMVar.TCB".-module LIO.Concurrent.LMVar ( module LIO.Concurrent.LMVar.Safe ) where+{-# LANGUAGE Trustworthy #-}+{-# LANGUAGE ConstraintKinds,+ FlexibleContexts #-}+{- | -import LIO.Concurrent.LMVar.Safe+This module implements labeled 'MVar's. The interface is analogous to+"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+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+'LMVars' calls the write guard 'guardWrite' to enforce sound+information flow control.++'LMVar's can be used to build synchronization primitives and+communication channels ('LMVar's themselves are single-place+channels). We refer to "Control.Concurrent.MVar" for the full+documentation on MVars.++-}++module LIO.Concurrent.LMVar (+ LMVar+ -- * Creating labeled 'MVar's+ , newEmptyLMVar, newEmptyLMVarP+ , newLMVar, newLMVarP+ -- * Take 'LMVar'+ , takeLMVar, takeLMVarP+ , tryTakeLMVar, tryTakeLMVarP+ -- * Put 'LMVar'+ , putLMVar, putLMVarP+ -- * Read 'LMVar'+ , tryPutLMVar, tryPutLMVarP+ , readLMVar, readLMVarP+ -- * Swap 'LMVar'+ , swapLMVar, swapLMVarP+ -- * Check state of 'LMVar'+ , isEmptyLMVar, isEmptyLMVarP+ ) where++import LIO.Label+import LIO.Core+import LIO.Privs+import LIO.Concurrent.LMVar.TCB++--+-- Creating labeled 'MVar's+--++-- | Create a new labeled MVar, in an empty state. Note that the supplied+-- label must be above the current label and below the current clearance.+-- An exception will be thrown by the underlying 'guardAlloc' if this is+-- not the case.+newEmptyLMVar :: MonadLIO l m+ => l -- ^ Label of @LMVar@+ -> m (LMVar l a) -- ^ New mutable location+newEmptyLMVar = newEmptyLMVarP NoPrivs++-- | 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 :: (MonadLIO l m, Priv l p) => p -> l -> m (LMVar l a)+newEmptyLMVarP p l = do+ guardAllocP p l+ newEmptyLMVarTCB l++-- | Create a new labeled MVar, in an filled state with the supplied+-- value. Note that the supplied label must be above the current label+-- and below the current clearance.+newLMVar :: Label l+ => l -- ^ Label of @LMVar@+ -> a -- ^ Initial value of @LMVar@+ -> LIO l (LMVar l a) -- ^ New mutable location+newLMVar = newLMVarP NoPrivs++-- | 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 :: (MonadLIO l m, Priv l p) => p -> l -> a -> m (LMVar l a)+newLMVarP p l a = do+ guardAllocP p l+ newLMVarTCB l a++--+-- Take 'LMVar'+--++-- | Return contents of the 'LMVar'. Note that a take consists of a read+-- and a write, since it observes whether or not the 'LMVar' is full, and+-- thus the current label must be the same as that of the 'LMVar' (of+-- course, this is not the case when using privileges). Hence, if the+-- label of the 'LMVar' is below the current clearance, we raise the+-- 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@+-- blocks.+takeLMVar :: MonadLIO l m => LMVar l a -> m a+takeLMVar = takeLMVarP NoPrivs++-- | Same as 'takeLMVar' except @takeLMVarP@ takes a privilege object+-- which is used when the current label is raised.+takeLMVarP :: (MonadLIO l m, Priv l p) => p -> LMVar l a -> m a+takeLMVarP p m = do+ guardWriteP p (labelOf m)+ takeLMVarTCB m++-- | Non-blocking version of 'takeLMVar'. It returns @Nothing@ if the+-- 'LMVar' is empty, otherwise it returns @Just@ value, emptying the+-- 'LMVar'.+tryTakeLMVar :: MonadLIO l m => LMVar l a -> m (Maybe a)+tryTakeLMVar = tryTakeLMVarP NoPrivs++-- | Same as 'tryTakeLMVar', but uses priviliges when raising current+-- label.+tryTakeLMVarP :: (MonadLIO l m, Priv l p) => p -> LMVar l a -> m (Maybe a)+tryTakeLMVarP p m = do+ guardWriteP p (labelOf m)+ tryTakeLMVarTCB m++--+-- Put 'LMVar'+--++-- | Puts a value into an 'LMVar'. Note that a put consists of a read and+-- a write, since it observes whether or not the 'LMVar' is empty, and so+-- the current label must be the same as that of the 'LMVar' (of course,+-- this is not the case when using privileges). As in the 'takeLMVar'+-- case, if the label of the 'LMVar' is below the current clearance, we+-- raise the current label to the join of the current label and the label+-- of the MVar and put the value into the @MVar@. Moreover if these IFC+-- restrictions fail, the underlying 'guardWrite' throws an exception.+-- If the 'LMVar' is full, @putLMVar@ blocks.+putLMVar :: Label l+ => LMVar l a -- ^ Source 'LMVar'+ -> a -- ^ New value+ -> LIO l ()+putLMVar = putLMVarP NoPrivs++-- | Same as 'putLMVar' except @putLMVarP@ takes a privilege object+-- which is used when the current label is raised.+putLMVarP :: (MonadLIO l m, Priv l p) => p -> LMVar l a -> a -> m ()+putLMVarP p m a = do+ guardWriteP p (labelOf m)+ putLMVarTCB m a++-- | Non-blocking version of 'putLMVar'. It returns @True@ if the+-- 'LMVar' was empty and the put succeeded, otherwise it returns @False@.+tryPutLMVar :: MonadLIO l m => LMVar l a -> a -> m Bool+tryPutLMVar = tryPutLMVarP NoPrivs++-- | Same as 'tryPutLMVar', but uses privileges when raising current label.+tryPutLMVarP :: (MonadLIO l m, Priv l p) => p -> LMVar l a -> a -> m Bool+tryPutLMVarP p m x = do+ guardWriteP p (labelOf m)+ tryPutLMVarTCB m x++--+-- Read 'LMVar'+--++-- | Combination of 'takeLMVar' and 'putLMVar'. Read the value, and just+-- put it back. As specified for 'readMVar', this operation is atomic+-- iff there is no other thread calling 'putLMVar' for this 'LMVar'.+readLMVar :: MonadLIO l m => LMVar l a -> m a+readLMVar = readLMVarP NoPrivs++-- | Same as 'readLMVar' except @readLMVarP@ takes a privilege object+-- which is used when the current label is raised.+readLMVarP :: (MonadLIO l m, Priv l p) => p -> LMVar l a -> m a+readLMVarP p m = do+ guardWriteP p (labelOf m)+ readLMVarTCB m++--+-- Swap 'LMVar'+--++-- | Takes a value from an 'LMVar', puts a new value into the 'LMvar',+-- and returns the taken value. Like the other 'LMVar' operations it is+-- required that the label of the 'LMVar' be above the current label and+-- below the current clearance. Moreover, the current label is raised to+-- accommodate for the observation. The underlying 'guardWrite' throws an+-- exception if this cannot be accomplished. This operation is atomic iff+-- there is no other thread calling 'putLMVar' for this 'LMVar'.+swapLMVar :: Label l+ => LMVar l a -- ^ Source @LMVar@+ -> a -- ^ New value+ -> LIO l a -- ^ Taken value+swapLMVar = swapLMVarP NoPrivs++-- | Same as 'swapLMVar' except @swapLMVarP@ takes a privilege object+-- which is used when the current label is raised.+swapLMVarP :: (MonadLIO l m, Priv l p) => p -> LMVar l a -> a -> m a+swapLMVarP p m x = do+ guardWriteP p (labelOf m)+ swapLMVarTCB m x++--+-- Check state of 'LMVar'+--++-- | 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'.+isEmptyLMVar :: MonadLIO l m => LMVar l a -> m Bool+isEmptyLMVar = isEmptyLMVarP NoPrivs++-- | Same as 'isEmptyLMVar', but uses privileges when raising current label.+isEmptyLMVarP :: (MonadLIO l m, Priv l p) => p -> LMVar l a -> m Bool+isEmptyLMVarP p m = do+ taintP p (labelOf m)+ isEmptyLMVarTCB m
− LIO/Concurrent/LMVar/Safe.hs
@@ -1,22 +0,0 @@-{-# LANGUAGE CPP #-}-#if defined(__GLASGOW_HASKELL__) && (__GLASGOW_HASKELL__ >= 702)-{-# LANGUAGE Trustworthy #-}-#endif---- | This module exports a safe subset of the labeled MVar interface.--- See "LIO.Concurrent.LMVar.TCB" for the documentation.-module LIO.Concurrent.LMVar.Safe ( module LIO.Concurrent.LMVar.TCB ) where--import LIO.Concurrent.LMVar.TCB ( LMVar- , labelOfLMVar- , newEmptyLMVar, newEmptyLMVarP- , newLMVar, newLMVarP- , takeLMVar, takeLMVarP- , putLMVar, putLMVarP- , readLMVar, readLMVarP- , swapLMVar, swapLMVarP- , tryTakeLMVar, tryTakeLMVarP- , tryPutLMVar, tryPutLMVarP- , isEmptyLMVar, isEmptyLMVarP- , withLMVar, withLMVarP - )
LIO/Concurrent/LMVar/TCB.hs view
@@ -1,270 +1,109 @@-{-# LANGUAGE CPP #-}-#if defined(__GLASGOW_HASKELL__) && (__GLASGOW_HASKELL__ >= 702) && (__GLASGOW_HASKELL__ < 704)-{-# LANGUAGE SafeImports #-}-#endif-#if defined(__GLASGOW_HASKELL__) && (__GLASGOW_HASKELL__ >= 704) {-# LANGUAGE Unsafe #-}-#endif {-|-This module provides an implementation for labeled MVars. 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 of type-'l'. As in the case of @LIORef@s (see "LIO.LIORef"), this label-is static and does not change according to the content placed into-the location. -'LMVar's can be used to build synchronization primitives and-communication channels ('LMVar's themselves are single-place-channels). We refer to "Control.Concurrent.MVar" for the full-documentation on MVars.+This module implements the core of labeled 'MVars's in the 'LIO ad.+to "Control.Concurrent.MVar", but the operations take place in the+'LIO' monad. The types and functions exported by this module are+strictly TCB and do not perform any information flow checks. The+external, safe interface is provided and documented in+"LIO.Concurrent.LMVar".+ -}-module LIO.Concurrent.LMVar.TCB ( -- * Basic Functions- LMVar- , labelOfLMVar- , newEmptyLMVar, newEmptyLMVarP- , newLMVar, newLMVarP- , takeLMVar, takeLMVarP- , putLMVar, putLMVarP- , readLMVar, readLMVarP- , swapLMVar, swapLMVarP- , tryTakeLMVar, tryTakeLMVarP- , tryPutLMVar, tryPutLMVarP- , isEmptyLMVar, isEmptyLMVarP- , withLMVar, withLMVarP - -- * Unsafe (TCB) Functions- , newEmptyLMVarTCB- , newLMVarTCB- , takeLMVarTCB- , putLMVarTCB- , readLMVarTCB- , swapLMVarTCB- , tryTakeLMVarTCB- , tryPutLMVarTCB- , isEmptyLMVarTCB- , withLMVarTCB- ) where+module LIO.Concurrent.LMVar.TCB (+ LMVar(..)+ -- * Creating labeled 'MVar's+ , newEmptyLMVarTCB, newLMVarTCB+ -- * Take 'LMVar'+ , takeLMVarTCB, tryTakeLMVarTCB+ -- * Put 'LMVar'+ , putLMVarTCB, tryPutLMVarTCB+ -- * Read 'LMVar'+ , readLMVarTCB+ -- * Swap 'LMVar'+ , swapLMVarTCB+ -- * Check state of 'LMVar'+ , isEmptyLMVarTCB+ ) where -import Control.Concurrent.MVar-import LIO.TCB+import Control.Concurrent.MVar+ +import LIO.Label+import LIO.Core+import LIO.TCB -- | An @LMVar@ is a labeled synchronization variable (an 'MVar') that -- can be used by concurrent threads to communicate.-data LMVar l a = LMVarTCB l (MVar a)---- | This function returns the label of a labeled MVar.-labelOfLMVar :: Label l => LMVar l a -> l-labelOfLMVar (LMVarTCB l _) = l+data LMVar l a = LMVarTCB { labelOfLMVar :: !l+ -- ^ Label of MVar.+ , unlabelLMVarTCB :: MVar a+ -- ^ Access the underlying 'MVar', ignoring IFC.+ } --- | 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 :: (LabelState l p s) => p -> l -> LIO l p s (LMVar l a)-newEmptyLMVarP p' l = withCombinedPrivs p' $ \p -> do- aguardP p l- iom <- ioTCB $ newEmptyMVar- return $ LMVarTCB l iom+instance LabelOf LMVar where+ labelOf = labelOfLMVar --- | Create a new labeled MVar, in an empty state. Note that the--- supplied label must be above the current label and below the current--- clearance.-newEmptyLMVar :: (LabelState l p s)- => l -- ^ Label of @LMVar@- -> LIO l p s (LMVar l a) -- ^ New mutable location-newEmptyLMVar l = getPrivileges >>= \p -> newEmptyLMVarP p l+--+-- Creating labeled 'MVar's+-- -- | Trusted function used to create an empty @LMVar@, ignoring IFC.-newEmptyLMVarTCB :: (LabelState l p s) => l -> LIO l p s (LMVar l a)+newEmptyLMVarTCB :: MonadLIO l m => l -> m (LMVar l a) newEmptyLMVarTCB l = do- iom <- ioTCB $ newEmptyMVar- return $ LMVarTCB l iom---- | 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 :: (LabelState l p s) => p -> l -> a -> LIO l p s (LMVar l a)-newLMVarP p' l a = withCombinedPrivs p' $ \p -> do- aguardP p l- m <- ioTCB $ newMVar a+ m <- liftLIO . ioTCB $ newEmptyMVar return $ LMVarTCB l m --- | Create a new labeled MVar, in an filled state with the supplied--- value. Note that the supplied label must be above the current label--- and below the current clearance.-newLMVar :: (LabelState l p s)- => l -- ^ Label of @LMVar@- -> a -- ^ Initial value of @LMVar@- -> LIO l p s (LMVar l a) -- ^ New mutable location-newLMVar l a = getPrivileges >>= \p -> newLMVarP p l a- -- | Trusted function used to create an @LMVar@ with the supplied -- value, ignoring IFC.-newLMVarTCB :: (LabelState l p s) => l -> a -> LIO l p s (LMVar l a)+newLMVarTCB :: MonadLIO l m => l -> a -> m (LMVar l a) newLMVarTCB l a = do- m <- ioTCB $ newMVar a+ m <- liftLIO . ioTCB $ newMVar a return $ LMVarTCB l m --- | Same as 'takeLMVar' except @takeLMVarP@ takes a privilege object--- which is used when the current label is raised.-takeLMVarP :: (LabelState l p s) => p -> LMVar l a -> LIO l p s a-takeLMVarP p' (LMVarTCB l m) = withCombinedPrivs p' $ \p -> do- wguardP p l- ioTCB $ takeMVar m---- | Return contents of the 'LMVar'. Note that a take consists of a read--- and a write, since it observes whether or not the 'LMVar' is full,--- and thus the current label must be the same as that of the--- 'LMVar' (of course, this is not the case when using privileges).--- Hence, if the label of the 'LMVar' is below the current clearance,--- we raise the current label to the join of the current label and the--- label of the MVar and read the contents of the @MVar@. If the--- 'LMVar' is empty, @takeLMVar@ blocks.-takeLMVar :: (LabelState l p s) => LMVar l a -> LIO l p s a-takeLMVar x = getPrivileges >>= \p -> takeLMVarP p x+--+-- Take 'LMVar'+-- -- | Read the contents of an 'LMVar', ignoring IFC.-takeLMVarTCB :: (LabelState l p s) => LMVar l a -> LIO l p s a-takeLMVarTCB (LMVarTCB _ m) = ioTCB $ takeMVar m+takeLMVarTCB :: MonadLIO l m => LMVar l a -> m a+takeLMVarTCB (LMVarTCB _ m) = liftLIO . ioTCB $ takeMVar m --- | Same as 'putLMVar' except @putLMVarP@ takes a privilege object--- which is used when the current label is raised.-putLMVarP :: (LabelState l p s) => p -> LMVar l a -> a -> LIO l p s ()-putLMVarP p' (LMVarTCB l m) a = withCombinedPrivs p' $ \p -> do- wguardP p l- val <- ioTCB $ putMVar m a- return val+-- | Same as 'tryTakeLMVar', but ignorses IFC.+tryTakeLMVarTCB :: MonadLIO l m => LMVar l a -> m (Maybe a)+tryTakeLMVarTCB (LMVarTCB _ m) = liftLIO . ioTCB $ tryTakeMVar m --- | Puts a value into an 'LMVar'. Note that a put consists of a read--- and a write, since it observes whether or not the 'LMVar' is empty,--- and so the current label must be the same as that of the 'LMVar'--- (of course, this is not the case when using privileges). As in the--- 'takeLMVar' case, if the label of the 'LMVar' is below the current--- clearance, we raise the current label to the join of the current--- label and the label of the MVar and put the value into the @MVar@.--- If the 'LMVar' is full, @putLMVar@ blocks.-putLMVar :: (LabelState l p s)- => LMVar l a -- ^ Source 'LMVar'- -> a -- ^ New value- -> LIO l p s ()-putLMVar x a = getPrivileges >>= \p -> putLMVarP p x a+--+-- Put 'LMVar'+-- -- | Put a value into an 'LMVar', ignoring IFC.-putLMVarTCB :: (LabelState l p s) => LMVar l a -> a -> LIO l p s ()-putLMVarTCB (LMVarTCB _ m) a = ioTCB $ putMVar m a+putLMVarTCB :: MonadLIO l m => LMVar l a -> a -> m ()+putLMVarTCB (LMVarTCB _ m) a = liftLIO . ioTCB $ putMVar m a --- | Same as 'readLMVar' except @readLMVarP@ takes a privilege object--- which is used when the current label is raised.-readLMVarP :: (LabelState l p s) => p -> LMVar l a -> LIO l p s a-readLMVarP p' (LMVarTCB l m) = withCombinedPrivs p' $ \p -> do- wguardP p l- ioTCB $ readMVar m +-- | Same as 'tryPutLMVar', but ignorses IFC.+tryPutLMVarTCB :: MonadLIO l m => LMVar l a -> a -> m Bool+tryPutLMVarTCB (LMVarTCB _ m) x = liftLIO . ioTCB $ tryPutMVar m x --- | Combination of 'takeLMVar' and 'putLMVar'. Read the value, and just--- put it back. As specified for 'readMVar', this operation is atomic--- iff there is no other thread calling 'putLMVar' for this 'LMVar'.-readLMVar :: (LabelState l p s) => LMVar l a -> LIO l p s a-readLMVar x = getPrivileges >>= \p -> readLMVarP p x --- | Trusted function used to read (take and put) an 'LMVar', ignoring IFC.-readLMVarTCB :: (LabelState l p s) => LMVar l a -> LIO l p s a-readLMVarTCB (LMVarTCB _ m) = ioTCB $ readMVar m+--+-- Read 'LMVar'+-- --- | Same as 'swapLMVar' except @swapLMVarP@ takes a privilege object--- which is used when the current label is raised.-swapLMVarP :: (LabelState l p s) => p -> LMVar l a -> a -> LIO l p s a-swapLMVarP p' (LMVarTCB l m) x = withCombinedPrivs p' $ \p -> do- wguardP p l- ioTCB $ swapMVar m x+-- | Trusted function used to read (take and put) an 'LMVar', ignoring IFC.+readLMVarTCB :: MonadLIO l m => LMVar l a -> m a+readLMVarTCB (LMVarTCB _ m) = liftLIO . ioTCB $ readMVar m --- | Takes a value from an 'LMVar', puts a new value into the 'LMvar',--- and returns the taken value. Like the other 'LMVar' operations it--- is required that the label of the 'LMVar' be above the current label--- and below the current clearance. Moreover, the current label is raised--- to accommodate for the observation. This operation is atomic iff--- there is no other thread calling 'putLMVar' for this 'LMVar'.-swapLMVar :: LabelState l p s - => LMVar l a -- ^ Source @LMVar@- -> a -- ^ New value- -> LIO l p s a -- ^ Taken value-swapLMVar x a = getPrivileges >>= \p -> swapLMVarP p x a+--+-- Swap 'LMVar'+-- -- | Trusted function that swaps value of 'LMVar', ignoring IFC.-swapLMVarTCB :: LabelState l p s => LMVar l a -> a -> LIO l p s a-swapLMVarTCB (LMVarTCB _ m) x = ioTCB $ swapMVar m x---- | Same as 'tryTakeLMVar', but uses priviliges when raising current label.-tryTakeLMVarP :: (LabelState l p s)- => p -> LMVar l a -> LIO l p s (Maybe a)-tryTakeLMVarP p' (LMVarTCB l m) = withCombinedPrivs p' $ \p -> do- wguardP p l- ioTCB $ tryTakeMVar m---- | Non-blocking version of 'takeLMVar'. It returns @Nothing@ if the--- 'LMVar' is empty, otherwise it returns @Just@ value, emptying the 'LMVar'.-tryTakeLMVar :: LabelState l p s => LMVar l a -> LIO l p s (Maybe a)-tryTakeLMVar x = getPrivileges >>= \p -> tryTakeLMVarP p x---- | Same as 'tryTakeLMVar', but ignorses IFC.-tryTakeLMVarTCB :: LabelState l p s => LMVar l a -> LIO l p s (Maybe a)-tryTakeLMVarTCB (LMVarTCB _ m) = ioTCB $ tryTakeMVar m---- | Same as 'tryPutLMVar', but uses privileges when raising current label.-tryPutLMVarP :: (LabelState l p s)- => p -> LMVar l a -> a -> LIO l p s Bool-tryPutLMVarP p' (LMVarTCB l m) x = withCombinedPrivs p' $ \p -> do- wguardP p l- ioTCB $ tryPutMVar m x---- | Non-blocking version of 'putLMVar'. It returns @True@ if the--- 'LMVar' was empty and the put succeeded, otherwise it returns @False@.-tryPutLMVar :: LabelState l p s => LMVar l a -> a -> LIO l p s Bool-tryPutLMVar x a = getPrivileges >>= \p -> tryPutLMVarP p x a---- | Same as 'tryPutLMVar', but ignorses IFC.-tryPutLMVarTCB :: LabelState l p s => LMVar l a -> a -> LIO l p s Bool-tryPutLMVarTCB (LMVarTCB _ m) x = ioTCB $ tryPutMVar m x----- | Same as 'isEmptyLMVar', but uses privileges when raising current label.-isEmptyLMVarP :: (LabelState l p s) => p -> LMVar l a -> LIO l p s Bool-isEmptyLMVarP p' (LMVarTCB l m) = withCombinedPrivs p' $ \p -> do- taintP p l- ioTCB $ isEmptyMVar m+swapLMVarTCB :: MonadLIO l m => LMVar l a -> a -> m a+swapLMVarTCB (LMVarTCB _ m) x = liftLIO . ioTCB $ swapMVar m x --- | 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.-isEmptyLMVar :: LabelState l p s => LMVar l a -> LIO l p s Bool-isEmptyLMVar x = getPrivileges >>= \p -> isEmptyLMVarP p x+--+-- Check state of 'LMVar'+-- -- | Same as 'isEmptyLMVar', but ignorses IFC.-isEmptyLMVarTCB :: LabelState l p s => LMVar l a -> LIO l p s Bool-isEmptyLMVarTCB (LMVarTCB _ m) = ioTCB $ isEmptyMVar m---- | Same as 'withLMVar', but uses privileges when performing label--- comparisons/raises.-withLMVarP :: (LabelState l p s)- => p -> LMVar l a -> (a -> LIO l p s b) -> LIO l p s b-withLMVarP p' m@(LMVarTCB l _) io = withCombinedPrivs p' $ \p -> do- wguardP p l- withLMVarTCB m io---- | Exception-safe wrapper for working with an 'LMVar'. The original--- contents of the 'LMVar' will be restored if the supplied action throws--- an exception. The function is atomic only if there is no other--- thread that performs a 'putLMVar'.-withLMVar :: LabelState l p s- => LMVar l a -> (a -> LIO l p s b) -> LIO l p s b-withLMVar m io = getPrivileges >>= \p -> withLMVarP p m io---- | Same as 'withLMVar', but ignores IFC.-withLMVarTCB :: LabelState l p s- => LMVar l a -> (a -> LIO l p s b) -> LIO l p s b-withLMVarTCB m io =- mask $ \restore -> do- a <- takeLMVarTCB m- b <- restore (io a) `onExceptionTCB` putLMVarTCB m a- putLMVarTCB m a- return b+isEmptyLMVarTCB :: MonadLIO l m => LMVar l a -> m Bool+isEmptyLMVarTCB (LMVarTCB _ m) = liftLIO . ioTCB $ isEmptyMVar m
+ LIO/Concurrent/TCB.hs view
@@ -0,0 +1,31 @@+{-# 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.Concurrent.TCB (+ LabeledResult(..), ThreadId+ ) where++import LIO.Label+import LIO.Core+import LIO.Concurrent.LMVar+import Control.Concurrent++-- | A labeled thread result is simply a wrapper for a 'LMVar'. A thread+-- can observe the result of another thread, only after raising its label+-- to the label of the result.+data LabeledResult l a = LabeledResultTCB {+ lresThreadIdTCB :: ThreadId + -- ^ Thread executing the computation+ , lresResultTCB :: LMVar l (Either (LabeledException l) a) + -- ^ Plecement of computation result+ }++instance LabelOf LabeledResult where+ labelOf = labelOf . lresResultTCB
+ LIO/Core.hs view
@@ -0,0 +1,595 @@+{-# LANGUAGE Trustworthy #-}+{-# LANGUAGE ScopedTypeVariables,+ 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.++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 contants such as @3@ or @\"tis a+string\"@. More interestingly, consider reading a \"secret\" file:++> bs <- readFile "/secret/file.txt"++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'.++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:++> writeFile "/public/file.txt" bs++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+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".++-}++module LIO.Core (+ -- * LIO monad+ LIO+ , MonadLIO(..)+ -- ** Execute LIO actions+ , evalLIO, runLIO, tryLIO, paranoidLIO+ -- ** Internal state+ , LIOState(..)+ -- *** Current label+ , getLabel, setLabel, setLabelP+ -- *** Current clerance+ , getClearance, setClearance, setClearanceP+ , withClearance, withClearanceP+ -- * Exceptions+ -- $exceptions+ , LabeledException+ -- ** Throwing exceptions+ , throwLIO+ -- ** Catching exceptions+ , catchLIO, catchLIOP+ -- ** Utilities+ -- $utils+ , onException, onExceptionP+ , finally, finallyP+ , bracket, bracketP+ , evaluate+ -- ** Exceptions thrown by LIO+ -- $lioExceptions+ , MonitorFailure(..)+ , VMonitorFailure(..)+ -- * Guards+ -- $guards+ -- ** Allocate/write-only+ , guardAlloc, guardAllocP+ -- ** Read-only+ , taint, taintP+ -- ** Write+ , guardWrite, guardWriteP+ ) where++import Prelude hiding (catch)+import Data.Typeable++import Control.Monad+import Control.Monad.Trans.State.Strict+import qualified Control.Exception as E+import Control.Exception hiding ( finally+ , onException+ , bracket+ , evaluate )++import LIO.TCB+import LIO.Label+import LIO.Privs+++--+-- Execute LIO actions+--++-- | Given an 'LIO' computation and some initial state, return an+-- IO action which when executed will perform the IFC-safe LIO+-- computation.+--+-- Because untrusted code cannot execute 'IO' computations, this function+-- should only be useful within trusted code. No harm is done from+-- exposing the @evalLIO@ symbol to untrusted code. (In general,+-- untrusted code is free to produce 'IO' computations, but it cannot+-- execute them.)+evalLIO :: Label l+ => LIO l a + -- ^ LIO computation+ -> LIOState l+ -- ^ Initial state+ -> IO a+evalLIO act s = fst `liftM` runLIO act s++-- | Execute an 'LIO' action, returning the final state.+-- See 'evalLIO'.+runLIO :: Label l+ => LIO l a+ -- ^ LIO computation that may throw an exception+ -> LIOState l+ -- ^ Initial state+ -> IO (a, LIOState l)+runLIO act s = runStateT (unLIOTCB act) s++-- | Similar to 'evalLIO', but catches all exceptions, including+-- language level exceptions.+paranoidLIO :: Label l+ => LIO l a+ -- ^ LIO computation that may throw an exception+ -> LIOState l+ -- ^ Initial state+ -> IO (Either SomeException (a, LIOState l))+paranoidLIO act s = (Right `liftM` runLIO act s) `catch` (return . Left)++-- | Similar to 'evalLIO', but catches all exceptions exceptions+-- thrown with 'throwLIO'.+tryLIO :: Label l+ => LIO l a+ -- ^ LIO computation that may throw an exception+ -> LIOState l+ -- ^ Initial state+ -> IO (Either (LabeledException l) a, LIOState l)+tryLIO act = runLIO (Right `liftM` act `catchTCB` (return . Left))++--+-- Internal state+--++-- | Returns the current value of the thread's label.+getLabel :: MonadLIO l m => m l+getLabel = liftLIO $ lioLabel `liftM` getLIOStateTCB+++-- | Raise the current label to the provided label, which must be+-- between the current label and clearance. See 'taint'.+setLabel :: MonadLIO l m => l -> m ()+setLabel = setLabelP NoPrivs++-- | 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@.+setLabelP :: (MonadLIO l m, Priv l p) => p -> l -> m ()+setLabelP p l = do+ liftLIO $ guardAllocP p l `catchLIO`+ \(_ :: MonitorFailure) -> throwLIO InsufficientPrivs+ liftLIO . updateLIOStateTCB $ \s -> s { lioLabel = l }++-- | Returns the current value of the thread's clearance.+getClearance :: MonadLIO l m => m l+getClearance = liftLIO $ 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.+setClearance :: MonadLIO l m => l -> m ()+setClearance = setClearanceP NoPrivs++-- | Raise 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.+setClearanceP :: (MonadLIO l m, Priv l p) => p -> l -> m ()+setClearanceP p cnew = do+ l <- getLabel+ c <- getClearance+ unless (canFlowToP p cnew c) $! throwLIO InsufficientPrivs+ unless (l `canFlowTo` cnew) $! throwLIO CurrentLabelViolation+ liftLIO . updateLIOStateTCB $ \s -> s { lioClearance = cnew }++-- | 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'.+-- +-- Note that if the computation inside @withClearance@ acquires any+-- 'Priv's, it may still be able to raise its clearance above the+-- supplied argument using 'setClearanceP'.+withClearance :: Label l => l -> LIO l a -> LIO l a+withClearance = withClearanceP NoPrivs++-- | Same as 'withClearance', but uses privileges when applying+-- 'guardAllocP' to the supplied label.+withClearanceP :: Priv l p => p -> l -> LIO l a -> LIO l a+withClearanceP p l act = do+ guardAllocP p l+ c <- getClearance+ liftLIO . updateLIOStateTCB $ \s -> s { lioClearance = l }+ act `finally` (updateLIOStateTCB $ \s ->+ s { lioClearance = c `lub` lioLabel s })++--+-- Exceptions+--++{- $exceptions++ We must define 'throwIO'- and 'catch'-like functions to work in+ the 'LIO' monad. A complication is that exceptions could+ potentially leak information. For instance, one might examine a+ secret bit, and throw an exception when the bit is 1 but not 0.+ Allowing untrusted code to catch the exception leaks the bit.++ The solution is to wrap exceptions up with a label. The exception+ may be caught, but only if the label of the exception can flow to+ the label at the point the catch statement began execution.+ Arbitrary code can use 'throwLIO' to throw an exception that will+ be labeled with the current label, while 'catchLIO' can be used to+ catch exceptions (whose label flows to the current clearance).+ Wherever possible, code should use the 'catchLIOP' and+ 'onExceptionP' variants that use privileges to downgrade the+ exception. ++ If an exception is uncaught in the 'LIO' monad, the 'evalLIO'+ function will re-throw the exception, so that it is okay to throw+ exceptions from within the 'LIO' monad and catch them within the+ 'IO' monad. Of course, code in the 'IO' monad must be careful not+ to let the 'LIO' code exploit it to exfiltrate information. Hence,+ we recommend the use of 'paranoidLIO' to execute 'LIO' actions as+ to prevent accidental, but unwanted crashes.+++ /Note/: Do not use 'throw' (as opposed to 'throwLIO') within the+ 'LIO' monad. Because 'throw' can be invoked from pure code, it has+ no notion of current label and so cannot assign an appropriate+ label to the exception. As a result, the exception will not be+ catchable within the 'LIO' monad and will propagate all the way out+ to the executing 'IO' layer. Similarly, asynchronous exceptions+ (such as divide by zero or undefined values in lazily evaluated+ expressions) cannot be caught within the 'LIO' monad.++-}++--+-- Throwing exceptions+--++-- | Throw an exception. The label on the exception is the current+-- label.+throwLIO :: (Exception e, MonadLIO l m) => e -> m a+throwLIO e = do+ l <- getLabel+ liftLIO . unlabeledThrowTCB $! LabeledExceptionTCB l (toException e)++--+-- Catching exceptions+--+++-- | Same as 'catchLIO' but does not use privileges when raising the+-- current label to the join of the current label and exception label.+catchLIOP :: (Exception e, Priv l p)+ => p+ -> LIO l a+ -> (e -> LIO l a)+ -> LIO l a+catchLIOP p act handler = do + clr <- getClearance+ act `catchTCB` \se@(LabeledExceptionTCB l seInner) -> + case fromException seInner of+ Just e | l `canFlowTo` clr -> taintP p l >> handler e+ _ -> unlabeledThrowTCB se++-- | Catches an exception, so long as the label at the point where the+-- exception was thrown can flow to the clearance at which @catchLIO@ is+-- invoked. Note that the handler raises the current label to the join+-- ('upperBound') of the current label and exception label.+catchLIO :: (Exception e, Label l)+ => LIO l a+ -> (e -> LIO l a)+ -> LIO l a+catchLIO = catchLIOP NoPrivs ++--+-- Utilities+--++{- $utils++Similar to "Control.Exception" we export 'onException', 'finally' and+'bracket' which should be used by programmers to properly acquire and+release resources in the presence of exceptions. Different from+"Control.Exception" our non-TCB utilities are not implemented in terms+of 'mask', and thus untrusted threads may be killed (and thus garbage+collected) with synchronous exceptions. Of course, only trusted code+may has access to 'throwTo'-like functionality.++-}++-- | Performs an action only if there was an exception raised by the+-- computation. Note that the exception is rethrown after the final+-- computation is executed.+onException :: Label l+ => LIO l a -- ^ The computation to run+ -> LIO l b -- ^ Computation to run on exception+ -> LIO l a -- ^ Result if no exception thrown+onException = onExceptionP NoPrivs++-- | Privileged version of 'onExceptionP'. 'onException' cannot run its+-- handler if the label was raised in the computation that threw the+-- exception. This variant allows privileges to be supplied, so as to+-- catch exceptions thrown with a \"higher\" label.+onExceptionP :: Priv l p+ => p -- ^ Privileges to downgrade exception+ -> LIO l a -- ^ The computation to run+ -> LIO l b -- ^ Computation to run on exception+ -> LIO l a -- ^ Result if no exception thrown+onExceptionP p act1 act2 = + catchLIOP p act1 (\(e :: SomeException) -> do+ void act2+ throwLIO e )++-- | Execute a computation and a finalizer, which is executed even if+-- an exception is raised in the first computation.+finally :: Label l+ => LIO l a -- ^ The computation to run firstly+ -> LIO l b -- ^ Final computation to run (even if exception is thrown)+ -> LIO l a -- ^ Result of first action+finally = finallyP NoPrivs++-- | Version of 'finally' that uses privileges when handling+-- exceptions thrown in the first computation.+finallyP :: Priv l p+ => p -- ^ Privileges to downgrade exception+ -> LIO l a -- ^ The computation to run firstly+ -> LIO l b -- ^ Final computation to run (even if exception is thrown)+ -> LIO l a -- ^ Result of first action+finallyP p act1 act2 = do+ r <- onExceptionP p act1 act2+ void act2+ return r++-- | The @bracket@ function is used in patterns where you acquire a+-- resource, perform a computation on it, and then release the resource.+-- The function releases the resource even if an exception is raised in+-- the computation. An example of its use case is file handling:+--+-- > bracket+-- > (openFile ... {- open file -} )+-- > (\handle -> {- close file -} )+-- > (\handle -> {- computation on handle -})+--+-- Note: @bracket@ does not use 'mask' and thus asynchronous may leave+-- the resource unreleased if the thread is killed in during release.+-- An interface for arbitrarily killing threads is not provided by LIO.+bracket :: Label l+ => LIO l a -- ^ Computation to run first+ -> (a -> LIO l c) -- ^ Computation to run last+ -> (a -> LIO l b) -- ^ Computation to run in-between+ -> LIO l b+bracket = bracketP NoPrivs++-- | Like 'bracket', but uses privileges to downgrade the label of any+-- raised exception.+bracketP :: Priv l p+ => p -- ^ Priviliges used to downgrade+ -> LIO l a -- ^ Computation to run first+ -> (a -> LIO l c) -- ^ Computation to run last+ -> (a -> LIO l b) -- ^ Computation to run in-between+ -> LIO l b+bracketP p first third second = do+ x <- first+ finallyP p (second x) (third x)++-- | Forces its argument to be evaluated to weak head normal form when the+-- resultant LIO action is executed. This is simply a wrapper for +-- "Control.Exception"'s @evaluate@.+evaluate :: MonadLIO l m => a -> m a+evaluate = liftLIO . rethrowIoTCB . E.evaluate++--+-- 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'+ 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+--++{- $guards++ Guards are used by (usually privileged) code to check that the+ invoking, unprivileged code has access to particular data. If the+ current label is @lcurrent@ and the current clearance is+ @ccurrent@, then the following checks should be performed when+ accessing data labeled @ldata@:++ * When /reading/ an object labeled @ldata@, it must be the case+ 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@.++ * When /creating/ or /allocating/ objects, it is permissible for+ them to be higher than the current label, so long as they are+ below the current clearance. In other words, it must be the+ case that @lcurrent ``canFlowTo`` ldata && ldata ``canFlowTo``+ ccurrent@. This is ensured by the 'guardAlloc' function.++ * When /writing/ an object, it should be the case that+ @lcurrent ``canFlowTo`` ldata && ldata ``canFlowTo`` lcurrent@.+ (As stated, this is the same as saying @ldata == lcurrent@, but+ the two are different when using 'canFlowToP' instead of+ 'canFlowTo'.) This is ensured by the 'guardWrite' function, which+ does the equivalent of 'taint' to ensure the target label @ldata@+ 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'.+++The 'taintP', 'guardAllocP', and 'guardWriteP' functions are variants+of the above that take privilege to be more permissive and raise the+current label less. ++-}++--+-- Allocation+--++-- | Ensures the label argument is between the current IO label and+-- current IO clearance. Use this function in code that allocates+-- objects--untrusted code shouldn't be able to create an object+-- labeled @l@ unless @guardAlloc l@ does not throw an exception.+-- 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 :: MonadLIO l m => l -> m ()+guardAlloc = guardAllocP NoPrivs++-- | 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 :: (MonadLIO l m, Priv l p) => p -> l -> m ()+guardAllocP p newl = do+ c <- getClearance+ l <- getLabel+ unless (canFlowToP p l newl) $! throwLIO CurrentLabelViolation+ unless (newl `canFlowTo` c) $! throwLIO ClearanceViolation++--+-- Read+--++-- | 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.+taint :: MonadLIO l m => l -> m ()+taint = taintP NoPrivs++-- | Like 'taint', but use privileges to reduce the amount of taint+-- required. Note that @taintP@ will never lower the current label.+-- It simply uses privileges to avoid raising the label as high as+-- 'taint' would raise it.+taintP :: (MonadLIO l m, Priv l p) => p -> l -> m ()+taintP p newl = do+ c <- getClearance+ l <- getLabel+ let l' = partDowngradeP p newl l+ unless (l' `canFlowTo` c) $! throwLIO ClearanceViolation+ liftLIO . updateLIOStateTCB $ \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,+-- i.e., the write implies a read.+--+-- The implementation of @guardWrite@ is straight forward:+--+-- > guardWrite l = taint l >> guardAlloc l+--+-- This guarantees that @l@ ``canFlowTo`` the current label (and+-- clearance), and that the current label ``canFlowTo`` @l@.+--+guardWrite :: MonadLIO l m => l -> m ()+guardWrite = guardWriteP NoPrivs++-- | Like 'guardWrite', but takes privilege argument to be more+-- permissive.+guardWriteP ::(MonadLIO l m, Priv l p) => p -> l -> m ()+guardWriteP p newl = do+ taintP p newl+ guardAllocP p newl
LIO/DCLabel.hs view
@@ -1,139 +1,125 @@-{-# LANGUAGE CPP #-}-#if __GLASGOW_HASKELL__ >= 702 {-# LANGUAGE Trustworthy #-}-#endif+{-# LANGUAGE MultiParamTypeClasses,+ ConstraintKinds,+ TypeSynonymInstances #-} -{-| This module provides bindings for the @DCLabel@ module, with some -renaming to resolve name clashes. The delegation of privilege and -other trusted code is not exported by this module and code wishing to-use this should import @DCLabel.TCB@.--}+{-| -{-# LANGUAGE StandaloneDeriving #-}-{-# LANGUAGE TypeSynonymInstances #-}-{-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE DeriveDataTypeable #-}-module LIO.DCLabel ( -- * DCLabel export- module DCLabel.Safe- , DCCatSet- -- * Renamed privileges- , DCPriv, DCPrivTCB- -- * Useful aliases for the LIO Monad- , DCLabeled, DC, evalDC, evalDCWithRoot, DCGate- -- * Public label- , lpub- )where+/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". -import LIO.TCB+-} -import LIO.Handle (evalWithRoot)+module LIO.DCLabel (+ -- ** Principals+ Principal, principal+ -- ** Clauses+ , Clause, clause+ -- ** Components+ , Component, dcTrue, dcFalse, dcFormula+ , isTrue, isFalse+ -- ** Labels+ , DCLabel, dcSecrecy, dcIntegrity, dcLabel, dcPub+ -- ** Privileges+ , module LIO.DCLabel.Privs+ -- ** DSL+ , module LIO.DCLabel.DSL+ -- * Synonyms for "LIO"+ -- $dcMonad+ , DCState, defaultState+ , DC, evalDC, runDC, tryDC, paranoidDC+ , MonadDC+ -- ** Exceptions+ , DCLabeledException+ -- ** Labeled values+ , DCLabeled+ -- ** Labeled references+ , DCRef+ -- ** Gates+ , DCGate+ ) where -import Data.Typeable+import Control.Exception -import DCLabel.Safe hiding ( Priv- , bottom- , top- , join- , meet)-import qualified DCLabel.Core as DCL+import LIO+import LIO.LIORef+import LIO.Labeled.TCB +import LIO.DCLabel.Core+import LIO.DCLabel.Privs+import LIO.DCLabel.DSL+import LIO.DCLabel.Serialize () -deriving instance Typeable DCL.Disj-deriving instance Typeable DCL.Conj-deriving instance Typeable DCL.Component-deriving instance Typeable DCL.DCLabel+--+-- LIO synonyms+-- -instance Label DCLabel where- lbot = DCL.bottom- ltop = DCL.top- lub = DCL.join- glb = DCL.meet- leq = DCL.canflowto--instance PrivTCB DCL.TCBPriv+-- | DC Labeled exceptions.+type DCLabeledException = LabeledException DCLabel -instance MintTCB DCL.TCBPriv DCL.Priv where- mintTCB = DCL.createPrivTCB+-- | DC 'Labeled' values.+type DCLabeled = Labeled DCLabel -instance MintTCB DCL.TCBPriv DCL.Principal where- mintTCB p = DCL.createPrivTCB (newPriv p)+instance LabeledFunctor DCLabel where+ lFmap lv f = let l = labelOf lv `upperBound` dcPub+ in label l $ f (unlabelTCB lv) -instance Priv DCLabel DCL.TCBPriv where- leqp = DCL.canflowto_p- {-- The implementation of lostar deserves an explanation. Firstly note- that the properties, for @r = lostar p l g@ that must be satisfied- are [the suffix \'s\' (\'i\')is used for seecrecy (resp. integrity):- 1.) @leq g r : (rs => gs) and (gi => ri)@- 2.) @leqp p l r : (rs /\ p => ls) and (li /\ p => ri)@- Finding the integrity component of @r@ is trivial: it's- simply the least upper bound of @gi@ and @li /\ p@.- Finding the secrecy component is a bit trickier. To do so, we first- find all the categories of @ls@ that are not implied by @p@ (this- gives us @rs'@), such that @rs' /\ p => ls@. Then, we need to find- the remaining categories in @gs@ that are not implied by @rs'@ (this- gives us @rs''@). Directly, @rs = rs' /\ rs''@.- -}- lostar p l g = - let (ls, li) = (DCL.toLNF . secrecy $ l, DCL.toLNF . integrity $ l)- (gs, gi) = (DCL.toLNF . secrecy $ g, DCL.toLNF . integrity $ g)- lp = DCL.toLNF . DCL.priv $ p- rs' = c2l [c | c <- getCats ls- , not (lp `DCL.implies` (c2l [c]))]- rs'' = c2l [c | c <- getCats gs- , not (rs' `DCL.implies` (c2l [c]))]- rs = rs' `DCL.and_component` rs''- ri = (li `DCL.and_component` lp) `DCL.or_component` gi- in DCL.toLNF $ simpleNewLabel p (newDC rs ri)- where getCats = DCL.conj . DCL.component- c2l = DCL.MkComponent . DCL.MkConj- simpleNewLabel pr lr | pr == DCL.rootPrivTCB = g - | pr == DCL.noPriv = l `lub` g- | otherwise = lr+-- | DC Labeled 'LIORef's.+type DCRef = LIORef DCLabel -instance PrivDesc DCPrivTCB DCPriv where- privDesc = priv+-- | DC 'Gate'.+type DCGate = Gate DCPrivDesc ----- Renaming+-- DC monad -- --- | A @DCLabel@ category set.-type DCCatSet = DCL.Component---- | A @DCLabel@ (untrusted) privilege.-type DCPriv = DCL.Priv+{- $dcMonad --- | A @DCLabel@ privilege.-type DCPrivTCB = DCL.TCBPriv+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'. +-} ------ LIO aliases---+-- | Type synonym for 'MonadLIO'.+type MonadDC m = MonadLIO DCLabel m -instance LabelState DCLabel DCPrivTCB () where+-- | 'LIOState' with underlying label being 'DCLabel'+type DCState = LIOState DCLabel --- | The type for 'Labeled' values uinsg 'DCLabel' as the label.-type DCLabeled a = Labeled DCLabel a+-- | 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 = top } -- | The monad for LIO computations using 'DCLabel' as the label.-type DC = LIO DCLabel DCPrivTCB ()+type DC = LIO DCLabel --- | Runs a computation in the LIO Monad, returning both the--- computation's result and the label of the result.-evalDC :: DC a -> IO (a, DCLabel)-evalDC m = evalLIO m () --- | Same as 'evalDC', but with support for filesystem.-evalDCWithRoot :: FilePath -> Maybe DCLabel -> DC a -> IO (a, DCLabel)-evalDCWithRoot path ml act = evalWithRoot path ml act ()+-- | Evaluate computation in the 'DC' monad.+evalDC :: DC a -> IO a+evalDC act = evalLIO act defaultState --- | A DC Label gate-type DCGate = Gate DCLabel DCPriv+-- | Evaluate computation in the 'DC' monad.+runDC :: DC a -> IO (a, DCState)+runDC act = runLIO act defaultState --- | Label corresponding to public data.-lpub :: DCLabel-lpub = newDC (<>) (<>)+-- | Similar to 'evalLIO', but catches any exceptions thrown by+-- untrusted code with 'throwLIO'.+tryDC :: DC a -> IO (Either DCLabeledException a, DCState)+tryDC act = tryLIO act defaultState++-- | Similar to 'evalLIO', but catches all exceptions.+paranoidDC :: DC a -> IO (Either SomeException (a, DCState))+paranoidDC act = paranoidLIO act defaultState
+ LIO/DCLabel/Core.hs view
@@ -0,0 +1,275 @@+{-# LANGUAGE Safe #-}+{-# LANGUAGE DeriveDataTypeable #-}+{-|++This module implements Disjunction Category Labels (DCLabels).+DCLabels is a label format for information flow control (IFC) systems.+This library exports relevant data types and operations that may be+used by dynamic IFC systems such as the "LIO" library.++A 'DCLabel' is a pair of /secrecy/ and /integrity/ 'Component's+(sometimes called category sets). Each 'Component' (or formula) is a+conjunction (implemented in terms of 'Set's) of 'Clause's (or+category) in propositional logic (without negation) specifying a+restriction on the flow of information labeled as such. Alternatively,+a 'Component' can take on the value 'DCFalse' corresponding to+falsehood. Each 'Clause', in turn, is a disjunction of 'Principal's,+, where a 'Principal' is a source of authority of type 'ByteString',+whose meaning is application-specific (e.g., a 'Principal' can be a+user name, a URL, etc.).++A clause imposes an information flow restriction. In the case of+secrecy, a clause restricts who can read, receive, or propagate the+information, while in the case of integrity it restricts who can+modify a piece of data. The principals composing a clause are said to+/own/ the clause or category.++For information to flow from a source labeled @L_1@ to a sink @L_2@, the+restrictions imposed by the clauses of @L_2@ must at least as restrictive as+all the restrictions imposed by the clauses of @L_1@ (hence the conjunction)+in the case of secrecy, and at least as permissive in the case of integrity.+More specifically, for information to flow from @L_1@ to @L_2@, the labels+must satisfy the \"can-flow-to\" relation: @L_1 ⊑ L_2@. The ⊑+label check is implemented by the 'canFlowTo' function. For labels+@L_1=\<S_1, I_1\>@, @L_2=\<S_2, I_2\>@ the can-flow-to relation is satisfied+if the secrecy component @S_2@ /implies/ @S_1@ and @I_1@ /implies/ @I_2@+(recall that a category set is a conjunction of disjunctions of principals).+For example, @\<P_1 ⋁ P_2, True\> ⊑ \<P_1, True\>@ because data+that can be read by @P_1@ is more restricting than that readable by @P_1@+or @P_2@. Conversely, @\<True,P_1\> ⊑ \<True,P_1 ⋁ P_2\>@+because data vouched for by @P_1@ or @P_2@ is more permissive than just @P_1@+(note the same principle holds when writing to sinks with such labeling).++-}++module LIO.DCLabel.Core ( + -- * Principals+ Principal(..), principal+ -- * Clauses+ , Clause(..), clause+ -- * Components+ -- $component+ , Component(..)+ , dcTrue, dcFalse, dcFormula + , isTrue, isFalse+ -- * Labels+ , DCLabel(..), dcLabel, dcLabelNoReduce, dcPub+ -- * Internal+ , dcReduce, dcImplies+ , dcAnd, dcOr+ ) where++import qualified Data.ByteString.Char8 as S8+import Data.Typeable+import Data.Set (Set)+import qualified Data.Set as Set++import Data.List (intercalate)++import LIO.Label++type S8 = S8.ByteString+++--+-- 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 + -- ^ Get the principal name.+ } deriving (Eq, Ord, Typeable)++instance Show Principal where+ show = S8.unpack . principalName++-- | Principal constructor.+principal :: S8 -> Principal+principal = Principal++--+-- 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 = 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 = "|False"+ | isTrue c = "|True"+ | otherwise = let cs = map show . Set.toList $! unDCFormula c+ in parens . intercalate " /\\ " $! cs+ where parens x = "{" ++ x ++ "}"++-- | 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 + show l = let s = dcSecrecy l+ i = dcIntegrity l+ in "< " ++ show s ++ " , " ++ show i ++ " >"++-- | Label constructor. Note that each component is first reduced 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 public data.+-- @dcPub = \< True, True \> @. This corresponds to data that is not+-- secret nor trustworthy.+dcPub :: DCLabel+dcPub = DCLabel { dcSecrecy = dcTrue, dcIntegrity = dcTrue }++--+-- Lattice operations+--++instance Label DCLabel where+ -- | Minimal element of the DCLabel lattice, /bottom/ ⊥, such+ -- that @⊥ ⊑ L@ for any label @L@.+ -- Bottom is defined as: @ ⊤ = \< False, True \> @+ bottom = dcLabel dcTrue dcFalse++ -- | Maximum element of the DCLabel lattice, /top/ ⊤,+ -- such that @L ⊑ ⊤@ for any label @L@.+ -- Top is defined as: @ ⊤ = \< False, True \> @+ top = DCLabel dcFalse dcTrue++ -- | 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 view
@@ -0,0 +1,155 @@+{-# LANGUAGE Safe #-}+{-# LANGUAGE TypeSynonymInstances,+ FlexibleInstances #-}++{-|+ This module implements a ``nano``, very simple, embedded domain+ specific language to create 'Component's and privilage descriptions+ from conjunctions of principal disjunctions.+ + 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 = 'dcPrivTCB' . 'dcPrivDesc' $ \"Alice\" '/\' \"Carla\"+ @++where++ * @ dc1 = \<{[\"Alice\" ⋁ \"Bob\"] ⋀ [\"Carla\"]} , {[\"Alice\"] ⋀ [\"Carla\"]}\>@+ + * @ dc2 = \<{[\"Djon\"]} , {[\"Alice\"]}\>@++ * @ 'canFlowTo' dc1 dc2 = False @++ * @ 'canFlowToP' pr dc1 dc2 = True@++-}++module LIO.DCLabel.DSL (+ -- * Operators+ (\/), (/\), ToComponent(..)+ , fromList, toList+ -- * Aliases+ , everybody, anybody+ ) where++import LIO.DCLabel.Privs+import LIO.DCLabel.Core+import qualified Data.Set as Set+import qualified Data.ByteString.Char8 as S8++type S8 = S8.ByteString++-- | Convert a type (e.g., 'Clause', 'Principal') to a label component.+class ToComponent a where+ -- | Convert to 'Component'+ toComponent :: a -> Component+ -- | Trivial synonym for 'toComponent'. Convert to a 'DCPrivDesc'.+ dcPrivDesc :: a -> DCPrivDesc+ dcPrivDesc = toComponent++-- | Identity of 'Component'.+instance ToComponent Component where+ toComponent = id+-- | 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 @ByteString@)to 'Component'.+instance ToComponent S8 where+ toComponent = toComponent . principal+-- | Convert singleton 'Principal' (in the form of a 'String')to 'Component'.+instance ToComponent String where+ toComponent = toComponent . S8.pack++infixl 7 \/+infixl 6 /\++-- | Conjunction of two 'Principal'-based elements.+-- +-- @+-- infixl 6 /\+-- @+--+(/\) :: (ToComponent a, ToComponent b) => a -> b -> Component+a /\ b = dcReduce $! toComponent a `dcAnd` toComponent b++-- | Disjunction of two 'Principal'-based elements.+-- +-- @+-- infixl 7 \\/+-- @+--+(\/) :: (ToComponent a, ToComponent b) => a -> b -> Component+a \/ b = dcReduce $! toComponent a `dcOr` toComponent b++--+-- Aliases+--++-- | Logical falsehood can be thought of as the component containing+-- every possible principal:+--+-- > everybody = dcFalse+--+everybody :: Component+everybody = dcFalse++-- | Logical truth can be thought of as the component containing+-- no specific principal:+--+-- > anybody = dcTrue+--+anybody :: Component+anybody = 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 'Nothing'.+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 view
@@ -0,0 +1,100 @@+{-# LANGUAGE Trustworthy #-}+{-# LANGUAGE MultiParamTypeClasses,+ TypeSynonymInstances #-}+{- |++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 Data.Monoid+import qualified Data.Set as Set++import LIO+import LIO.Privs.TCB (mintTCB)+import LIO.DCLabel.Core+import LIO.DCLabel.Privs.TCB++-- | Privileges can be combined using 'mappend'+instance Monoid DCPriv where+ mempty = noPriv+ mappend p1 p2 = mintTCB . dcReduce $!+ privDesc p1 `dcAnd` privDesc p2++instance Priv DCLabel DCPriv where+ canFlowToP p 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 }+ where pd = privDesc p+ partDowngradeP p la lg | p == noPriv = la `upperBound` lg+ | p == allPrivTCB = lg+ | otherwise = + let sa = dcSecrecy $ la+ ia = dcIntegrity la+ sg = dcSecrecy lg+ ig = dcIntegrity lg+ pd = privDesc p+ sa' = dcFormula . Set.filter (f pd) $ unDCFormula sa+ sr = if isFalse sa + then sa+ else sa' `dcAnd` sg+ ir = (pd `dcAnd` ia) `dcOr` ig+ in dcLabel sr ir+ where f pd c = not $ pd `dcImplies` (dcFormula . Set.singleton $ c)++--+-- Helpers+--++-- | The empty privilege, or no privileges, corresponds to logical+-- @True@.+noPriv :: DCPriv+noPriv = mintTCB 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 $! mintTCB 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/DCLabel/Privs/TCB.hs view
@@ -0,0 +1,45 @@+{-# LANGUAGE Unsafe #-}+{-# LANGUAGE DeriveDataTypeable,+ MultiParamTypeClasses,+ TypeSynonymInstances #-}++{-|++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.DCLabel.Privs.TCB (+ DCPrivDesc+ , DCPriv(..)+ , allPrivTCB+ ) where++import Data.Typeable+import LIO.DCLabel.Core+import LIO.Privs+import LIO.Privs.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@.+newtype DCPriv = DCPrivTCB { unDCPriv :: DCPrivDesc }+ deriving (Eq, Show, Typeable)++instance PrivTCB DCPriv+instance PrivDesc DCPriv DCPrivDesc where privDesc = unDCPriv+instance MintTCB DCPriv DCPrivDesc where mintTCB = DCPrivTCB++-- | The all privilege corresponds to logical @False@+allPrivTCB :: DCPriv+allPrivTCB = mintTCB dcFalse+
+ LIO/DCLabel/Serialize.hs view
@@ -0,0 +1,31 @@+{-# LANGUAGE Trustworthy #-}+{-# LANGUAGE StandaloneDeriving,+ GeneralizedNewtypeDeriving #-}++{- | This module provides instances for binary serialization of+'DCLabel's. Specifically, we provide insgtances for @cereal@\'s+@Data.Serialize@. -}++module LIO.DCLabel.Serialize () where+++import LIO.DCLabel.Core+import Data.Serialize+import Control.Monad++deriving instance Serialize Principal+deriving instance Serialize Clause++-- | Serialize components by converting them to maybe's+instance Serialize Component where+ put c = put . dcToMaybe $! c+ where dcToMaybe DCFalse = Nothing+ dcToMaybe (DCFormula f) = Just f+ get = dcFromMaybe `liftM` get+ where dcFromMaybe Nothing = dcFalse+ dcFromMaybe (Just f) = dcFormula f++-- | Serialize labels by converting them to pairs of components.+instance Serialize DCLabel where+ put l = put (dcSecrecy l, dcIntegrity l)+ get = uncurry dcLabelNoReduce `liftM` get
+ LIO/Data/Time.hs view
@@ -0,0 +1,37 @@+{-# LANGUAGE Trustworthy #-}+{- |++This module re-exports "Data.Time" wrapped in 'LIO'.++WARNING: The time functions can be used to carry out+/external-timing attacks/ with less effort than using threads and+synchronization. It is therefore advised that computations that+operate on sensitive data take the same amount of time regardless of+the input.++-}+module LIO.Data.Time (+ module Data.Time+ , getCurrentTime+ , getZonedTime+ , utcToLocalZonedTime+ ) where++import qualified Data.Time as T+import Data.Time hiding ( getCurrentTime+ , getZonedTime+ , utcToLocalZonedTime)+import LIO+import LIO.TCB++-- | Get the current UTC time from the system clock.+getCurrentTime :: MonadLIO l m => m UTCTime+getCurrentTime = liftLIO $ rethrowIoTCB T.getCurrentTime++-- | Get the local time together with a TimeZone.+getZonedTime :: MonadLIO l m => m ZonedTime+getZonedTime = liftLIO $ rethrowIoTCB T.getZonedTime++-- | Convert UTC time to local time with TimeZone.+utcToLocalZonedTime :: MonadLIO l m => UTCTime -> m ZonedTime+utcToLocalZonedTime = liftLIO . rethrowIoTCB . T.utcToLocalZonedTime
− LIO/FS.hs
@@ -1,652 +0,0 @@-{-# LANGUAGE CPP #-}-#if __GLASGOW_HASKELL__ >= 702 && __GLASGOW_HASKELL__ < 704-{-# LANGUAGE SafeImports #-}-#endif-{-# LANGUAGE DeriveDataTypeable #-}-{-# LANGUAGE ScopedTypeVariables #-}--{- | This module implements a labeled filesystem as a file store in- which a label is associated with every file and directory. The- module "LIO.Handle" provides an interface for working with labeled- directories and files -- this module provides the underlying- low-level (mostly trusted) interface.-- The file store contains 3 components:-- * @.magic@ file containing a sequence of pre-defined bytes. The- existence of this file and correct contents indicates that- the file store and root of the labeled filesystem were created- before any system crash.-- * @.obj@ directory containing all the objects (files and- directories) in the filesystem, organized according to the- object's label.-- * @ROOT@ symbolic link pointing to a directory object in @.obj@,- representing the root of the filesystem.-- As the details of @.magic@ and @ROOT@ are straight forward, we- now focus on the details of @.obj@.-- Each object is stored in @.obj@ in a directory corresponding- to the label of the file. For example, an object protected by- @bob@'s label will exist as:-- > .obj/oTAUzOt5YoQAyThw89eSgWuig-0=/obj012345-- where @oTAUzOt5YoQAyThw89eSgWuig-0=@ is the base64url encoding of- the (SHA-224 hash of) label @bob@, and @obj012345@ is the object's- name. Each label directory also contains a @.label@ file whose- contents correspond to the serialization of the label. Hence, for- example,-- > .obj/oTAUzOt5YoQAyThw89eSgWuig-0=/.label-- would have @bob@'s label.- - Every object in a label directory is either a file or a directory.- If the object is a directory, its contents will strictly have- symbolic links to objects in the file store. This is more easily- understood using an example.-- Consider the following familiar filesystem layout, containing 4- directories and 2 files:-- >- > /- > |-- bobOralice (type: directory, label: bob \/ alice)- > | |-- alice (type: directory, label: alice)- > | |-- bob (type: directory, label: bob)- > | | '-- secret (type: file , label: bob)- > | '-- messages (type: file , label: bob \/ alice)- > '-- clarice (type: directory, label: clarice)- >-- Suppose we take @/lioFS@ as the location of the file store for our- labeled filesystem. The above files and directories will have the- same layout in our file system, but each file/directory points to- an object in the object store (@.obj@), as shown below. -- >- > /lioFS/ROOT- > |-- bobOralice -> ../../c_EkXYGrmrSit9j_8VjP_-8DgaM=/objXIBabq- > | |-- alice -> ../../12to8q3vDp7ApyKEQk2LQ_2nrvs=/objqZeR5w- > | |-- bob -> ../../oTAUzOt5YoQAyThw89eSgWuig-0=/objtsePnc- > | | '-- secret -> ../../oTAUzOt5YoQAyThw89eSgWuig-0=/objFIQCIm- > | '-- messages -> ../../c_EkXYGrmrSit9j_8VjP_-8DgaM=/objQaiwuA- > '-- clarice -> ../../wordEFmBzWc7Q6qP-TetDgZaG8A=/objOMh0mj- >-- Note that, to a user, the interface (see "LIO.Handle") is- unchanged and they do not need to handle or understand the- underlying file store. We refer to this view as \"friendly\". The- file store for the above layout is:-- >- > /lioFS/- > |-- .magic- > |-- .obj- > | |-- 12to8q3vDp7ApyKEQk2LQ_2nrvs=- > | | |-- .label (=alice's label)- > | | '-- objqZeR5w- > | |-- c_EkXYGrmrSit9j_8VjP_-8DgaM=- > | | |-- .label (=label bob \/ alice)- > | | |-- objQaiwuA- > | | '-- objXIBabq- > | | |-- alice -> ../../12to8q3vDp7ApyKEQk2LQ_2nrvs=/objqZeR5w- > | | |-- bob -> ../../oTAUzOt5YoQAyThw89eSgWuig-0=/objtsePnc- > | | '-- messages -> ../../c_EkXYGrmrSit9j_8VjP_-8DgaM=/objQaiwuA- > | |-- jX7q5Kb8_G4GsjgNpOHB17kypQo=- > | | |-- .label (=label of the filesystem root)- > | | '-- objTj5JZD- > | | |-- bobOralice -> ../../c_EkXYGrmrSit9j_8VjP_-8DgaM=/objXIBabq- > | | '-- clarice -> ../../wordEFmBzWc7Q6qP-TetDgZaG8A=/objOMh0mj- > | |-- oTAUzOt5YoQAyThw89eSgWuig-0=- > | | |-- .label (=bob's label)- > | | |-- objFIQCIm- > | | '-- objtsePnc- > | | '-- secret -> ../../oTAUzOt5YoQAyThw89eSgWuig-0=/objFIQCIm- > | '-- wordEFmBzWc7Q6qP-TetDgZaG8A=- > | |-- .label (=clarice's label)- > | '-- objOMh0mj- > '-- ROOT -> .obj/jX7q5Kb8_G4GsjgNpOHB17kypQo=/objTj5JZD- >-- Observe that every directory object contains symbolic links- pointing to file or directory objects in the store.-- Accessing any file or directory in the \"friendly\" view requires- a lookup ('lookupObjPath') of the corresponding object. Each- path is, however, relative to the root. So, for example,- looking up @\/aliceOrbob\/bob\/@ actually corresponds to looking- up @\/lioFS\/ROOT\/aliceOrbob\/bob\/@. This prevents untrusted code- from accessing objects by guessing filenames and further allows- untrusted code to pick arbitrary filenames (including @.label@,- @.obj@, etc.).-- Several precautions are taken to keep the filesystem in a working- state in case of a crash. The code tries to maintain the invariant- that any inconsistencies will either be:-- 1. temporary files or directories whose names start with the- \"@#@\" character, or-- 2. dangling symbolic links.-- Both of these inconsistencies can be checked and cleaned up- locally without examining the whole file system. The code tries- to fix up these inconsistencies on-the-fly as it encounters them.- However, it could possibly leave some stranded temporary- @.label@ files. ---}---module LIO.FS ( evalWithRoot- -- * Creating and finding (labeled) objects- , lookupObjPath, lookupObjPathP- , getObjLabelTCB- , createFileTCB, createDirectoryTCB- -- * Labeled 'FilePath'- , LFilePath, labelOfFilePath- , unlabelFilePath- , unlabelFilePathP- , unlabelFilePathTCB- -- * Misc helper functions- , cleanUpPath, stripSlash- ) where---import Prelude hiding (catch)-import LIO.TCB -import Control.Monad (unless)-import System.FilePath-import System.Posix.Files-import System.Directory-import Control.Exception (Exception)-import qualified Control.Exception as E-import qualified Data.ByteString as B-import qualified Data.ByteString.Lazy as LB-import qualified Data.ByteString.Char8 as C-import Data.Serialize-import Data.IORef-import Data.Functor-import Data.Tuple (swap)-import Data.Foldable (foldlM)-import Data.Typeable-import Data.List (isPrefixOf)-import System.IO.Unsafe-import System.IO-import qualified System.IO.Error as IOError--import qualified Data.ByteString.Base64.URL as Codec-import qualified Data.Digest.Pure.SHA as SHA--#if __GLASGOW_HASKELL__ >= 706-import System.Posix.Temp -#else--- Patch to unix package will not be up on Hackage until GHC 7.6, so--- we include the implementation with LIO until this is the case-import System.Posix.Tmp -#endif-------- Filesystem root-------- | Root of labeled filesystem.-rootDir :: IORef FilePath-{-# NOINLINE rootDir #-}-rootDir = unsafePerformIO $ newIORef undefined--getRootDir :: IO FilePath-getRootDir = readIORef rootDir---- | Temporary files have the prefix \'#\'.-tmpPrefix :: FilePath-tmpPrefix = "#"---- | Prefix of object filenames.-objPrefix :: FilePath-objPrefix = "obj"---- | Shadow directory containing the actual FS objects.-objDir :: FilePath-objDir = ".obj"---- | We need to serialize the label in each label directory.-labelFile :: FilePath-labelFile = ".label"---- | Root of the filesystem.-rootLink :: FilePath-rootLink = "ROOT"---- | Magic file. Last file created when the the filesystem is--- initially created. If the magic file is invalid, then it is likely--- that a failure occured in the set up process.-magicFile :: FilePath-magicFile = ".magic"---- | Content written to magic file. -magicContent :: B.ByteString-magicContent = B.pack [ 0x7f, 0x45, 0x4c, 0x46, 0x01- , 0x01, 0x01, 0x00, 0x00, 0x00- , 0x00, 0x00, 0x00, 0x00, 0x00- , 0x00, 0xde, 0xad, 0xbe, 0xef]---- | Filesystem errors-data FSErr = FSRootCorrupt -- ^ Root structure is corrupt- | FSRootInvalid -- ^ Root is invalid (must be absolute).- deriving Typeable--instance Exception FSErr--instance Show FSErr where- show FSRootCorrupt = "Root structure is corrupt"- show FSRootInvalid = "Root is invalid (must be absolute)"- ---- | Same as 'evalLIO', but takes two additional parameters--- corresponding to the path of the labeled filesystem store and the--- label of the root. If the labeled filesystem store does not exist,--- it is created at the specified path with the root having the--- supplied label.--- If the filesystem does exist, the supplied label is ignored and thus--- unnecessary. However, if the root label is not provided and the--- filesystem has not been initialized, then 'lbot' is used as the--- root label.-evalWithRoot :: (Serialize l, LabelState l p s)- => FilePath -- ^ Filesystem root- -> Maybe l -- ^ Label of root- -> LIO l p s a -- ^ LIO action- -> s -- ^ Initial state- -> IO (a, l)-evalWithRoot path ml act s = setRootOrInitFS >> evalLIO act s- where setRootOrInitFS = do- unless (isAbsolute path) $ throwIO FSRootInvalid- exists <- doesDirectoryExist path- let l = maybe lbot id ml- if exists- then checkFS path- else initFS l path---- | Initialize the filesystem with a given label and root file path.-initFS :: (Label l, Serialize l)- => l -- ^ Label of root- -> FilePath -- ^ Path to the filesystem root- -> IO ()-initFS l path = do- unless (isAbsolute path) $ throwIO FSRootInvalid- -- Create root of filesystem- createDirectory path- -- Create the shadow object store- createDirectory (path </> objDir)- `onException` (removeDirectory path)- -- Set the root filesystem:- writeIORef rootDir path- rootObj <- newDirObj l- linkRoot rootObj- -- Create magic file:- B.writeFile (path </> magicFile) magicContent---- | Check the filesystem structure.-checkFS :: FilePath -> IO ()-checkFS path = do- -- check that the ROOT link exists- rootStat <- getSymbolicLinkStatus $ path </> rootLink- unless (isSymbolicLink rootStat) $ throwIO FSRootCorrupt- -- find obj dir:- hasObjDir <- doesDirectoryExist $ path </> objDir- unless hasObjDir $ throwIO FSRootCorrupt- -- check magic file:- magicOK <- (B.readFile (path </> magicFile) >>= \c -> return (c==magicContent))- `catchIO` (return False)- unless magicOK $ throwIO FSRootCorrupt- writeIORef rootDir path-------- Objects--- ---- | Encoding of a label into a filepath. We need to take the hash--- of a label (as opposed to e.g. just 64-base encoding of the label)--- in order to keep the filename lengths to a reasonable size.-encodeLabel :: (Serialize l, Label l) => l -> FilePath-encodeLabel l = enc . SHA.bytestringDigest . SHA.sha1 $ encodeLazy l- where enc = C.unpack . Codec.encode . B.concat . LB.toChunks---- | An object can be a directory or a file.-data Object = DirObj FilePath -- ^ Directory object- | FileObj Handle FilePath -- ^ File object- deriving (Eq, Show)---- | A wrapper for a new, not yet commited, object.-newtype NewObject = New Object- deriving (Eq, Show)---- | Create a new directory object with a given label.-newDirObj :: (Serialize l, Label l) => l -> IO NewObject-newDirObj l = newObj l $ \p -> (New . DirObj) <$> mkTmpObjDir p---- | Create a new file object with a given label and 'IOMode'.-newFileObj :: (Serialize l, Label l) => l -> IOMode -> IO NewObject-newFileObj l m = newObj l $ \p -> (New . uncurry FileObj) <$> mkTmpObjFile m p---- | Create a new temporary unique object directory. The function retries if--- there exists an object with the same name but no 'tmpPrefix'.-mkTmpObjDir :: FilePath -> IO FilePath-mkTmpObjDir path = do- dir <- mkdtemp $ path </> (tmpPrefix ++ objPrefix) - exists <- doesDirectoryExist (rmTmpPrefix dir)- if exists- then do ignoreErr $ removeDirectory dir- mkTmpObjDir path- else return dir----- | Create a new temporary unique object file. The function retries if--- there exists an object with the same name but no 'tmpPrefix'.-mkTmpObjFile :: IOMode -> FilePath -> IO (Handle, FilePath)-mkTmpObjFile mode path = do- res@(file, h) <- mkstemp $ path </> (tmpPrefix ++ objPrefix)- exists <- doesFileExist (rmTmpPrefix file)- if exists- then do hClose h - ignoreErr $ removeFile file- mkTmpObjFile mode path- else return . swap $ res---- | Create a new file or directory object (given the make temporary--- object functino). For example, we can create a new file object as:------ > newDirObj l = newObj l $ \p -> (New . DirObj) <$> mkTmpObjDir p------ or a new directory object as------ > newFileObj l m = newObj l $ \p ->--- > (New . uncurry FileObj) <$> mkTmpObjFile m p)--- -newObj :: (Serialize l, Label l)- => l -- ^ Label of object- -> (FilePath -> IO NewObject) -- ^ Object creation function- -> IO NewObject-newObj l mkFunc = getLabelDir l >>= mkFunc---- | Create the label directory (and corresponding 'labelFile') in 'objDir'.-getLabelDir :: (Serialize l, Label l) => l -> IO FilePath-getLabelDir l = do- oRoot <- (</> objDir) <$> getRootDir- let dir = oRoot </> encodeLabel l- createDirectoryIfMissing True dir- createLabelFileIfMissing dir l- return dir---- | This function creates a 'labelFile' for the given label--- directory. It also verifies that the label in the file is the--- same as the given label (as another thread might have created the--- file, or a previous write might have corrupted the file). If the--- file is invalid, it removes the existing label file and writes--- the given label. An assumption made here is that there is a 1-to-1--- correspondence between a label and its encoding (i.e., 'encodeLabel'--- is a bijection).-createLabelFileIfMissing :: (Serialize l, Label l) => FilePath -> l -> IO ()-createLabelFileIfMissing dir l = do- let file = dir </> labelFile- exists <- doesFileExist file- unless exists $ createLabelFile file- -- It's possible for another thread to have created the file, and so- -- we need to make sure that we agree on the label:- valid <- checkLabelFile file- unless valid $ (ignoreErr $ removeFile file) >> createLabelFileIfMissing dir l- where createLabelFile file = do- (h,f) <- mkTmpObjFile WriteMode dir- C.hPutStr h (encode l)- hClose h- rename f file `E.catch` (\e -> - removeFile f >> unless (IOError.isAlreadyExistsError e) (throwIO e))- checkLabelFile file = do- c <- B.readFile file- case decode c of- Left _ -> return False- Right l' -> return (l == l')----- | Link the 'rootLink' to an object directory.-linkRoot :: NewObject -> IO ()-linkRoot newO@(New o) = do- root <- getRootDir- newObjPath <- case o of- DirObj p -> return p- _ -> throwIO . userError $ "Root must be a directory."- let objPath = rmTmpPrefix newObjPath- let name = root </> rootLink- createSymbolicLink (objPath `rmPrefixDir` root) name- `onException` (cleanUpNewObj newO)- rename newObjPath objPath `onException` (do ignoreErr $ removeFile name- cleanUpNewObj newO)---- | Link without returning object.-linkObj_ :: FilePath -> NewObject -> IO ()-linkObj_ f n = linkObj f n >> return ()----- | Link a filepath to an object.-linkObj :: FilePath -> NewObject -> IO Object-linkObj name' newO@(New o) = do- root <- getRootDir- let newObjPath = case o of- DirObj p -> p- FileObj _ p -> p- objPath = rmTmpPrefix newObjPath- let name = root </> rootLink </> name'- relObjPath = (".." </> ".." </> (objPath `rmPrefixDir` (root </> objDir)))- createSymbolicLink relObjPath name `onException` (cleanUpNewObj newO)- rename newObjPath objPath `onException` (do ignoreErr $ removeFile name- cleanUpNewObj newO)- return $ case o of- DirObj _ -> DirObj objPath- FileObj h _ -> FileObj h objPath---- | It's possible that either a program crashed before renaming a--- 'NewObject' into a 'Object', or that another thread is calling--- 'linkObj' and for some reason is being slow between the--- 'createSymbolicLink' and 'rename' calls. Either way it should be--- fine for us just to 'rename' the 'NewObject', because the link to--- the object would not exist if the 'NewObject' were not ready to be--- renamed. This function is primarily used when traversing the--- filesystem tree with 'pathTaintTCB'.-fixObjLink :: FilePath -> IO ()-fixObjLink f = do- exists <- catchIO (getFileStatus f >> return True) (return False)- unless exists $ ignoreErr $ rename (tmpPrefix ++ f) f---- | Clean up a newly created object. If the object is a temporary--- directory, remove it. If it's a file, close the handle, and remove--- the file.-cleanUpNewObj :: NewObject -> IO ()-cleanUpNewObj (New o) = ignoreErr $ case o of - DirObj p -> removeDirectory p- FileObj h p -> hClose h >> removeFile p---- | 'Label' associated with a 'FilePath'.-newtype LFilePath l = LFilePathTCB (Labeled l FilePath)---- | Get the label of a labeled filepath.-labelOfFilePath :: Label l => LFilePath l -> l-labelOfFilePath (LFilePathTCB x) = labelOf x---- | Trusted version of 'unlabelFilePath' that ignores IFC.-unlabelFilePathTCB :: (Label l) => LFilePath l -> FilePath-unlabelFilePathTCB (LFilePathTCB l) = unlabelTCB l---- | Same as 'unlabelFilePath' but uses privileges to unlabel the--- filepath.-unlabelFilePathP :: (LabelState l p s)- => p -> LFilePath l -> LIO l p s FilePath-unlabelFilePathP p' (LFilePathTCB l) = withCombinedPrivs p' $ \p -> unlabelP p l---- | Unlabel a filepath. If the path corresponds to a directory, you--- can now get the contents of the directory; if it's a file, you can--- open the file.-unlabelFilePath :: LabelState l p s- => LFilePath l -> LIO l p s FilePath-unlabelFilePath = unlabelFilePathP noPrivs---- | Given a pathname (forced to be relative to the root of the--- labeled filesystem), find the path to the corresponding object.--- The current label is raised to reflect all the directories traversed.--- Note that if the object does not exist an exception will be thrown;--- the label of the exception will be the join of all the directory labels --- up to the lookup failure.------ Additionally, this function cleans up the--- path before doing the lookup, so e.g., path @/foo/bar/..@ will--- first be rewritten to @/foo@ and thus no traversal to @bar@.--- Note that this is a more permissive behavior than forcing the read--- of @..@ from @bar@.-lookupObjPath :: (LabelState l p s, Serialize l)- => FilePath -- ^ Path to object- -> LIO l p s (LFilePath l)-lookupObjPath = lookupObjPathP noPrivs---- | Same as 'lookupObjPath' but takes an additional privilege object--- that is exercised when raising the current label.-lookupObjPathP :: (LabelState l p s, Serialize l)- => p -- ^ Privilege - -> FilePath -- ^ Path to object- -> LIO l p s (LFilePath l)-lookupObjPathP p' f' = withCombinedPrivs p' $ \p -> do- f <- cleanUpPath f'- root <- ioTCB $ getRootDir- pathTaintTCB p root rootLink- let dirs = splitDirectories f- dir <- foldlM (\a b -> pathTaintTCB p a b >> return (a </> b))- (root </> rootLink)- (safeinit dirs)- let objPath = (dir </> safelast dirs)- l <- getObjLabelTCB objPath- return . LFilePathTCB $ labelTCB l objPath- where safelast [] = ""- safelast x = last x----- | Read the label file of an object. Note that because the format--- of the supplied path is not checked this function is considered to--- be in the @TCB@.-getObjLabelTCB :: (Serialize l, LabelState l p s) => FilePath -> LIO l p s l-getObjLabelTCB objPath = rtioTCB $ do- root <- getRootDir- symLink <- readSymbolicLink objPath- let absObjPath = root </> objDir </>- ((symLink `rmPrefixDir` (".." </> ".."))- `rmPrefixDir` objDir)- lS <- C.readFile $ (takeDirectory absObjPath) </> labelFile- case decode lS of- Left _ -> throwIO . userError $ "Invalid label file."- Right l -> return l ---- | Given a privilege, root-path prefix and a directory/file within--- the prefix, read the 'labelFile' of the root-path prefix, raising--- the current label to reflect this. This function is used to taint --- a process that traverses a filesystem tree. The function is @TCB@--- because we do not check any properties of the root -- it is--- primarily used by 'lookupObjPathP'.-pathTaintTCB :: (Serialize l, LabelState l p s)- => p -> FilePath -> FilePath -> LIO l p s ()-pathTaintTCB p root f' = do- let f = stripSlash f'- rootL <- rtioTCB $ readSymbolicLink (root </> f)- let objPath = root </> rootL- ioTCB $ fixObjLink objPath- lS <- rtioTCB $ C.readFile (objPath </> ".." </> labelFile)- case decode lS of- Left _ -> throwIO . userError $ "Invalid label file."- Right l -> taintP p l---- | Remove any 'pathSeparator's from the front of a file path.-stripSlash :: FilePath -> FilePath -stripSlash [] = []-stripSlash xx@(x:xs) | x == pathSeparator = stripSlash xs- | otherwise = xx---- | Cleanup a file path, if it starts out with a @..@, we consider--- this invalid as it can be used explore parts of the filesystem--- that should otherwise be unaccessible. Similarly, we remove any @.@--- from the path.-cleanUpPath :: LabelState l p s => FilePath -> LIO l p s FilePath -cleanUpPath f = rtioTCB $ doit $ splitDirectories . normalise . stripSlash $ f- where doit [] = return []- doit ("..":[]) = return "/"- doit ("..":_) = throwIO $ IOError.mkIOError IOError.doesNotExistErrorType- "Illegal filename" Nothing (Just ".." )- doit (_:"..":xs) = doit xs- doit (".":xs) = doit xs- doit (x:xs) = (x </>) <$> doit xs- ------- Creating and linking directory and file objects------- | Create a directory object with the given label and link the--- supplied path to the object.-createDirectoryTCB :: (Serialize l, Label l) => l -> FilePath -> IO ()-createDirectoryTCB l p = do- obj <- newDirObj l- linkObj_ p obj---- | Create a file object with the given label and link the--- supplied path to the object. The handle to the file is returned.-createFileTCB :: (Serialize l, Label l) => l -> FilePath -> IOMode -> IO Handle-createFileTCB l p m = do- obj <- newFileObj l m- (FileObj h _) <- linkObj p obj- return h--------- Helper functions------- | Remove the prefix from a list (usually 'FilePath').-rmPrefix :: Eq a => [a] -- ^ String- -> [a] -- ^ Prefix- -> [a]-rmPrefix s pre = if pre `isPrefixOf` s- then drop (length pre) s- else s---- | Remove the 'tmpPrefix' prefix from a file or directory.-rmTmpPrefix :: FilePath -> FilePath-rmTmpPrefix path =- let ds = splitDirectories path- in (joinPath $ safeinit ds) - </> ((last ds) `rmPrefix` tmpPrefix)----- | @rmPrefixDir path prefix@ removes @prefix@ from @path@.-rmPrefixDir :: FilePath -> FilePath -> FilePath-rmPrefixDir path prefix = - if prefix `isPrefixOf` path- then let p = splitDirectories path- x = splitDirectories prefix- in joinPath $ drop (length x) p- else path---- | Ignore 'IOException's.-ignoreErr :: IO () -> IO ()-ignoreErr m = catchIO m (return ())---- | Same as 'catch', but only catches 'IOException's.-catchIO :: IO a -> IO a -> IO a-catchIO a h = E.catch a ((const :: a -> E.IOException -> a) h)---- | Same as 'init', but does not crash on empty list.-safeinit :: [a] -> [a]-safeinit [] = []-safeinit x = init x
+ LIO/FS/TCB.hs view
@@ -0,0 +1,267 @@+{-# LANGUAGE Unsafe #-}+{-# LANGUAGE ConstraintKinds #-}+{-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE ScopedTypeVariables #-}+{- |++This module exports the basic interface for creating and using the+labeled file system, implemented as a file store. Trusted code should+use 'initFSTCB' to set the root of the labeled file system. Moreover,+trusted code should implement all the IO functions in terms of+'createFileTCB', 'createDirectoryTCB', and 'getPathLabelTCB' and+'setPathLabelTCB'.++-}+module LIO.FS.TCB (+ -- * Initializing labeled filesystem+ initFSTCB, mkFSTCB, setFSTCB+ , getRootDirTCB+ -- * Handling path labels+ , setPathLabelTCB+ , getPathLabelTCB+ -- * Creating labeled objects+ , createFileTCB+ , createDirectoryTCB+ -- * Labeled 'FilePath'+ , LFilePath(..)+ -- * Filesystem errors+ , FSError(..)+ -- * Serializable label constraint+ , SLabel+ , lazyEncodeLabel, encodeLabel, decodeLabel+ ) where++import Prelude hiding (catch)++import Data.Serialize+import Data.Typeable+import Data.IORef+import qualified Data.ByteString as S+import qualified Data.ByteString.Char8 as S8+import qualified Data.ByteString.Lazy as L+import qualified Data.ByteString.Lazy.Char8 as L8+import qualified Data.Digest.Pure.SHA as SHA++import Codec.Compression.Zlib hiding (compress)++import Control.Monad+import Control.Exception+ +import System.FilePath+import System.Directory+import System.IO+import System.IO.Unsafe+import System.Xattr++import LIO.Label+import LIO.Core+import LIO.TCB++-- | Synonym for strict ByteString+type S8 = S8.ByteString+ +-- | Synonym for lazy ByteString+type L8 = L8.ByteString++-- | Constraintfor serializable labels+type SLabel l = (Label l, Serialize l)++--+-- Exception thrown by the file store interface+--++-- | Filesystem errors+data FSError = FSRootCorrupt -- ^ Root structure is corrupt.+ | FSRootInvalid -- ^ Root is invalid (must be absolute).+ | FSRootExists -- ^ Root already exists.+ | FSRootNoExist -- ^ Root does not exists.+ | FSRootNeedLabel -- ^ Cannot create root, missing label.+ | FSObjNeedLabel -- ^ FSobjectcannot be created without a label.+ | FSLabelCorrupt FilePath -- ^ Object label is corrupt.+ | FSIllegalFileName -- ^ Supplied file name is illegal.+ deriving Typeable++instance Exception FSError++instance Show FSError where+ show FSRootCorrupt = "Root structure is corrupt."+ show FSRootInvalid = "Root path is invalid, must be absolute."+ show FSRootExists = "Root already exists."+ show FSRootNoExist = "Root directory does not exist."+ show FSRootNeedLabel = "Root cannot be created without a label."+ show (FSLabelCorrupt f) = "Label of " ++ show f ++ " is corrupt/non-existant."+ show FSObjNeedLabel = "FS object cannot be created without a label."+ show FSIllegalFileName = "Supplied file name is illegal."++--+-- Handling root of FS+--++magicAttr :: AttrName+magicAttr = "user._lio_magic"++-- | Content written to magic key. +magicContent :: AttrValue+magicContent = S.pack [ 0x7f, 0x45, 0x4c, 0x46, 0x01+ , 0x01, 0x01, 0x00, 0x00, 0x00+ , 0x00, 0x00, 0x00, 0x00, 0x00+ , 0x00, 0xde, 0xad, 0xbe, 0xef]++-- | Root of labeled filesystem.+rootDir :: IORef FilePath+{-# NOINLINE rootDir #-}+rootDir = unsafePerformIO $ newIORef (error "LIO Filesystem not initialized.")++-- | Get the root directory.+getRootDirTCB :: SLabel l => LIO l FilePath+getRootDirTCB = ioTCB $ readIORef rootDir++-- | Create a the file store (i.e., labeled file system) with a given+-- label and root file path. The path must be an absolute path,+-- otherwise @initFSTCB@ throws 'FSRootInvalid'.+mkFSTCB :: SLabel l+ => FilePath -- ^ Path to the filesystem root+ -> l -- ^ Label of root+ -> LIO l ()+mkFSTCB path l = do+ unless (isAbsolute path) $ ioTCB $ throwIO FSRootInvalid+ -- Create root of filesystem:+ ioTCB $ createDirectory path+ -- Set root label:+ setPathLabelTCB path l+ -- Create magic attribute:+ ioTCB $ lsetxattr path magicAttr magicContent CreateMode+ -- Set the root filesystem:+ ioTCB $ writeIORef rootDir path++-- | Set the given file path as the root of the labeled filesystem. This+-- function throws a 'FSLabelCorrupt' if the directory does not contain a+-- valid label, and a 'FSRootCorrupt' if the 'magicAttr' attribute is+-- missing.+setFSTCB :: SLabel l => FilePath -> LIO l ()+setFSTCB path = do+ -- Path must be absolute+ unless (isAbsolute path) $ ioTCB $ throwIO FSRootInvalid+ -- Path must be a directory+ checkDirExists+ -- Check magic attribute:+ checkMagic+ -- Check that the label of the root is valid+ void $ getPathLabelTCB path+ -- Set the root directory+ ioTCB $ writeIORef rootDir path+ where checkMagic = ioTCB $ do+ magicOK <-(==magicContent) `liftM` + (throwOnFail $ lgetxattr path magicAttr)+ unless magicOK doFail+ checkDirExists = ioTCB $ do+ e <- doesDirectoryExist path+ unless e $ throwIO FSRootNoExist+ doFail = throwIO FSRootCorrupt+ throwOnFail act = act `catch` (\(_:: SomeException) -> doFail)++-- | Initialize filesystem at the given path. The supplied path must be+-- absolute, otherwise @initFSTCB@ throw 'FSRootInvalid'. If the FS has+-- already been created then @initFSTCB@ solely verifies that the root+-- directory is not corrupt (see 'setFSTCB'). Otherwise, a new FS is created+-- with the supplied label (see 'mkFSTCB').+--+-- This function performs several checks that 'setFSTCB' and 'mkFSTCB' perform,+-- so when considering performance they should be called directly.+initFSTCB :: SLabel l => FilePath -> Maybe l -> LIO l ()+initFSTCB path ml = do+ unless (isAbsolute path) $ ioTCB $ throwIO FSRootInvalid+ exists <- ioTCB $ doesDirectoryExist path+ (if exists then setFSTCB else mkFSTCB') path+ where mkFSTCB' p = maybe (ioTCB $ throwIO FSRootNeedLabel) (mkFSTCB p) ml+ ++--+-- Objects+-- ++-- | Label attribute name.+labelAttr :: AttrName+labelAttr = "user._lio_label"++-- | Hash-of-label attribute name.+labelHashAttr :: AttrName+labelHashAttr = "user._lio_label_sha"++-- | Encode a label into an attribute value.+lazyEncodeLabel :: SLabel l => l -> L8+lazyEncodeLabel = compress . encodeLazy++-- | Encode a label into an attribute value.+encodeLabel :: SLabel l => l -> AttrValue+encodeLabel = strictify . lazyEncodeLabel++-- | Descode label from an attribute value.+decodeLabel :: SLabel l => AttrValue -> Either String l+decodeLabel = decodeLazy . decompress . lazyfy++-- | Set the label of a given path. This function sets the 'labelAttr'+-- attribute to the encoded label, and the hash to 'labelHashAttr'.+setPathLabelTCB :: SLabel l => FilePath -> l -> LIO l ()+setPathLabelTCB path l = ioTCB $ do+ lsetxattr path labelAttr (strictify lEnc) RegularMode+ lsetxattr path labelHashAttr lHsh RegularMode+ where lEnc = lazyEncodeLabel l+ lHsh = strictify . SHA.bytestringDigest . SHA.sha1 $ lEnc++-- | Get the label of a given path. If the object does not have an+-- associated label or the hash of the label and stored-hash are not+-- equal, this function throws 'FSLabelCorrupt'.+getPathLabelTCB :: SLabel l => FilePath -> LIO l l+getPathLabelTCB path = rethrowIoTCB $ do+ (b, h) <- throwOnFail $ do b <- lgetxattr path labelAttr+ h <- lgetxattr path labelHashAttr+ return (b, h)+ let b' = lazyfy b+ h' = strictify . SHA.bytestringDigest . SHA.sha1 $ b'+ case decodeLabel b of+ Right l | h == h' -> return l+ _ -> doFail+ where doFail = throwIO $ FSLabelCorrupt path+ throwOnFail act = act `catch` (\(_:: SomeException) -> doFail)+++-- | Create a directory object with the given label.+createDirectoryTCB :: (SLabel l) => l -> FilePath -> LIO l ()+createDirectoryTCB l path = do+ rethrowIoTCB $ createDirectory path+ setPathLabelTCB path l++-- | Create a file object with the given label and return a handle to+-- the new file.+createFileTCB :: (SLabel l) => l -> FilePath -> IOMode -> LIO l Handle+createFileTCB l path mode = do+ h <- rethrowIoTCB $ openFile path mode+ setPathLabelTCB path l+ return h++--+-- Labeled 'FilePath's+--++data LFilePath l = LFilePathTCB { labelOfFilePath :: l+ -- ^ Label of file path+ , unlabelFilePathTCB :: FilePath+ -- ^ Unlabel a filepath, ignoring IFC.+ } ++--+-- Misc helper+--++-- | Convert lazy ByteString to strict ByteString.+strictify :: L8 -> S8+strictify = S8.concat . L.toChunks++-- | Convert strict ByteString to lazy ByteString.+lazyfy :: S8 -> L8+lazyfy x = L8.fromChunks [x]++-- | Compress with zlib (optimized for speed).+compress :: L8 -> L8+compress = compressWith (defaultCompressParams { compressLevel = bestSpeed })
+ LIO/Gate.hs view
@@ -0,0 +1,97 @@+{-# LANGUAGE Safe #-}++{- |++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'.++-}++module LIO.Gate (+ Gate+ , gate, callGate+ -- * Example+ -- $example+ ) where++import LIO.Privs++-- | 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 = Gate { unGate :: d -> a }++-- | 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 :: PrivDesc p d+ => (d -> a) -- ^ Gate computation+ -> Gate d a+gate = Gate++-- | 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 :: PrivDesc p d+ => Gate d a -- ^ Gate+ -> p -- ^ Privilege used as proof-of-ownership+ -> a+callGate g = unGate g . privDesc++{- $example++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 = mintTCB . dcPrivDesc $ "Alice"+> bob = mintTCB . dcPrivDesc $ "Bob"+> clark = mintTCB . 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/Handle.hs view
@@ -1,342 +1,549 @@-{-# LANGUAGE CPP #-}-#if __GLASGOW_HASKELL__ >= 702 {-# LANGUAGE Trustworthy #-}-#endif-{-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE FunctionalDependencies #-}+{-# LANGUAGE ConstraintKinds,+ FlexibleInstances,+ FlexibleContexts,+ TypeSynonymInstances,+ MultiParamTypeClasses #-}+{- | --- | This module abstracts the basic 'FileHandle' methods provided by--- the system library, and provides an 'LHandle' ('Labeled' 'Handle')--- type that can be manipulated from within the 'LIO' Monad.--- (There is no notion of changeable current working directory in--- the 'LIO' Monad, nor symbolic links.)------ The actual storage of labeled files is handled by the "LIO.FS"--- module.------ /IMPORTANT:/ To use a labeled filesystem you must use 'evalWithRoot',--- otherwise any actions built using the combinators of this module will--- crash.------ An example use is shown below: ------ >--- > main = dcEvalWithRoot "/tmp/lioFS" $ do--- > createDirectoryP p lsecrets "secrets"--- > writeFileP p ("secrets" </> "alice" ) "I like Bob!"--- > where p = ...--- > lsecrets = ....--- >------ The file store for the labeled filesystem (see "LIO.FS") will--- be created in @\/tmp\/lioFS@, but this is transparent and the user--- can think of the filesystem as having root @/@.-module LIO.Handle (- -- * LIO with filesystem support- evalWithRoot- -- * Generic Handle operations- , DirectoryOps(..)- , CloseOps(..)- , HandleOps(..)- , readFile, writeFile, writeFileL+This module abstracts the basic file 'Handle' methods provided by the+system library, and provides a 'LabeledHandle' type that can be+manipulated from within the 'LIO' Monad. A 'LabeledHandle' is imply a+file 'Handle' with an associated label that is used to track and+control the information flowing from and to the file. The API exposed+by this module is analogous to "System.IO", and the functions mainly+differ in taking an additional label and enforcing IFC.++The actual storage of labeled files is handled by the "LIO.FS" module.+The filesystem is implemented as a file store in which labels are+associated with files and directories (using extended attributes).++/IMPORTANT:/ To use the labeled filesystem you must use+'evalWithRootFS' (or other initializers from "LIO.FS.TCB"), otherwise+any actions built using the combinators of this module will crash.++An example use case shown below: ++>+> main = dcEvalWithRoot "/tmp/lioFS" $ do+> createDirectoryP p lsecrets "secrets"+> writeFileP p ("secrets" </> "alice" ) "I like Bob!"+> where p = ...+> lsecrets = ....+>++The file store for the labeled filesystem (see "LIO.FS") will+be created in @\/tmp\/lioFS@, but this is transparent and the user+can think of the filesystem as having root @/@. Note that for this to+work the filesystem must be mounted with the @user_xattr@ option.+For example, on GNU/Linux:++> mount -o rw,noauto,user,sync,noexec,user_xattr /dev/sdb1 /tmp/lioFS++In the current version of the filesystem, there is no notion of+changeable current working directory in the 'LIO' Monad, nor symbolic+links.+-}+module LIO.Handle ( evalWithRootFS+ , SLabel+ , SMonadLIO+ -- * LIO Handle+ , LabeledHandle, Handle , IOMode(..)- -- * LIO Handle- , LHandle, labelOfHandle - -- ** Privileged combinators- , getDirectoryContentsP- , createDirectoryP- , openFileP- , hCloseP- , hFlushP + , BufferMode(..)+ -- * File operations+ , openFile, openFileP+ , hClose, hCloseP+ , hFlush, hFlushP+ , HandleOps(..) , hGetP , hGetNonBlockingP , hGetContentsP+ , hGetLineP , hPutP , hPutStrP , hPutStrLnP- , readFileP, writeFileP, writeFileLP+ -- ** Special cases+ , readFile, readFileP+ , writeFile, writeFileP+ -- * Directory operations+ , getDirectoryContents, getDirectoryContentsP+ , createDirectory, createDirectoryP+ -- * Setting/getting handle status/settings+ , hSetBuffering, hSetBufferingP+ , hGetBuffering, hGetBufferingP+ , hSetBinaryMode, hSetBinaryModeP+ , hIsEOF, hIsEOFP+ , hIsOpen, hIsOpenP+ , hIsClosed, hIsClosedP+ , hIsReadable, hIsReadableP+ , hIsWritable, hIsWritableP ) where --#if __GLASGOW_HASKELL__ >= 702-import safe Prelude hiding (catch, readFile, writeFile)-import safe System.IO (IOMode(..))-import safe qualified System.IO as IO-#else import Prelude hiding (catch, readFile, writeFile)-import System.IO (IOMode(..))-import qualified System.IO as IO-#endif -import LIO.TCB-import LIO.FS-import Data.Serialize-import qualified System.Directory as IO-import System.FilePath-import qualified Data.ByteString.Lazy as L-import qualified Data.ByteString.Lazy.Char8 as LC+import Data.Serialize+import qualified Data.ByteString.Char8 as S8+import qualified Data.ByteString.Lazy.Char8 as L8 +import Control.Monad+import Control.Exception (throwIO) --- | Class used to abstract reading and creating directories, and--- opening (possibly creating) files.-class (Monad m) => DirectoryOps h m | m -> h where- -- | Get the contents of a directory.- getDirectoryContents :: FilePath -> m [FilePath]- -- | Create a directory at the supplied path.- -- The LIO instance labels the directory with the current label.- createDirectory :: FilePath -> m ()- -- | Open handle to manage the file at the supplied path.- openFile :: FilePath -> IOMode -> m h --- | Class used to abstract close and flush operations on handles.-class (Monad m) => CloseOps h m where- hClose :: h -> m ()- hFlush :: h -> m ()+import System.IO (IOMode(..), BufferMode(..), Handle)+import qualified System.IO as IO+import qualified System.Directory as IO+import System.FilePath --- | Class used to abstract reading and writing from and to handles,--- respectively.-class (CloseOps h m) => HandleOps h b m where- hGet :: h -> Int -> m b- hGetNonBlocking :: h -> Int -> m b- hGetContents :: h -> m b- hPut :: h -> b -> m ()- hPutStr :: h -> b -> m ()- hPutStr = hPut- hPutStrLn :: h -> b -> m ()+import LIO+import LIO.Labeled.TCB+import LIO.TCB+import LIO.FS.TCB ----- Standard IO Handle Operations+-- LIO related -- -instance DirectoryOps IO.Handle IO where- getDirectoryContents = IO.getDirectoryContents- createDirectory = IO.createDirectory- openFile = IO.openBinaryFile+-- | Serialize 'MonadLIO'+type SMonadLIO l m = (SLabel l, MonadLIO l m) -instance CloseOps IO.Handle IO where- hClose = IO.hClose- hFlush = IO.hFlush -instance HandleOps IO.Handle L.ByteString IO where- hGet = L.hGet- hGetNonBlocking = L.hGetNonBlocking- hGetContents = L.hGetContents- hPut = L.hPut- hPutStrLn = LC.hPutStrLn+-- | Same as 'evalLIO', but takes two additional parameters+-- corresponding to the path of the labeled filesystem store and the+-- label of the root. If the labeled filesystem store does not exist,+-- it is created at the specified path with the root having the+-- supplied label.+-- If the filesystem does exist, the supplied label is ignored and thus+-- unnecessary. However, if the root label is not provided and the+-- filesystem has not been initialized, a 'FSRootNeedLabel' exception+-- will be thrown.+evalWithRootFS :: SLabel l+ => FilePath -- ^ Filesystem root+ -> Maybe l -- ^ Label of root+ -> LIO l a -- ^ LIO action+ -> LIOState l -- ^ Initial state+ -> IO a+evalWithRootFS path ml act = evalLIO (initFSTCB path ml >> act) -- -- LIO Handle Operations -- --- | A labeled handle.-data LHandle l = LHandleTCB l IO.Handle---- | Get the label of a labeled handle.-labelOfHandle :: Label l => LHandle l -> l-labelOfHandle (LHandleTCB l _) = l--instance (Serialize l, LabelState l p s)- => DirectoryOps (LHandle l) (LIO l p s) where- getDirectoryContents = getDirectoryContentsP noPrivs- createDirectory f = do l <- getLabel - createDirectoryP noPrivs l f- openFile f m = do l <- getLabel - openFileP noPrivs (Just l) f m+-- | Get the contents of a directory. The current label is raised to the+-- join of the current label and that of all the directories traversed to+-- the leaf directory. Note that, unlike the standard Haskell+-- 'getDirectoryContents', we first normalise the path by collapsing all+-- the @..@'s. The function uses 'unlabelFilePath' when raising the+-- current label and thus may throw an exception if the clearance is+-- too low.+-- /Note:/ The current LIO filesystem does not support links.+getDirectoryContents :: SMonadLIO l m => FilePath -> m [FilePath]+getDirectoryContents = getDirectoryContentsP NoPrivs --- | Get the contents of a directory. The current label is raised to--- the join of the current label and that of all the directories--- traversed to the leaf directory (of course, using privileges to--- keep the current label unchanged when possible). Note that, unlike--- the standard Haskell 'getDirectoryContents', we first normalise the--- path by collapsing all the @..@'s. (The LIO filesystem does not--- support links.)-getDirectoryContentsP :: (LabelState l p s, Serialize l)+-- | Same as 'getDirectoryContents', but uses privileges when raising+-- the current label.+getDirectoryContentsP :: (SMonadLIO l m, Priv l p) => p -- ^ Privilege -> FilePath -- ^ Directory- -> LIO l p s [FilePath]-getDirectoryContentsP p' dir = withCombinedPrivs p' $ \p -> do- path <- lookupObjPathP p dir >>= unlabelFilePathP p- rtioTCB $ IO.getDirectoryContents path+ -> m [FilePath]+getDirectoryContentsP p dir = do+ path <- taintObjPathP p dir+ liftLIO . rethrowIoTCB $ IO.getDirectoryContents path --- | Create a directory at the supplied path with the given label.--- The current label (after traversing the filesystem to the--- directory path) must flow to the supplied label which in turn must--- flow to the current label (of course, using privileges to bypass--- certain restrictions). If this information flow restriction is--- satisfied, the directory is created.-createDirectoryP :: (LabelState l p s, Serialize l)+-- | Create a directory at the supplied path with the given label. The+-- given label must be bounded by the the current label and clearance, as+-- checked by 'guardAlloc'. The current label (after traversing the+-- filesystem to the directory path) must flow to the supplied label,+-- which must, in turn, flow to the current label as required by+-- 'guardWrite'.+createDirectory :: SMonadLIO l m => l -> FilePath -> m ()+createDirectory = createDirectoryP NoPrivs++-- | Same as 'createDirectory', but uses privileges when raising the+-- current label and checking IFC restrictions.+createDirectoryP :: (SMonadLIO l m, Priv l p) => p -- ^ Privilege -> l -- ^ Label of new directory -> FilePath -- ^ Path of directory- -> LIO l p s ()-createDirectoryP p ldir path' = withCombinedPrivs p $ \priv -> do- path <- cleanUpPath path'- aguardP priv ldir- lcDir <- lookupObjPathP priv (containingDir path)- wguardP priv $ labelOfFilePath lcDir- rtioTCB $ createDirectoryTCB ldir path- where stripLastSlash = (reverse . stripSlash . reverse)- containingDir = takeDirectory . ([pathSeparator] </>)- . stripLastSlash+ -> m ()+createDirectoryP p l dir0 = do+ -- Check that the label is bounded by the current label and clearance:+ guardAllocP p l+ -- Clean up directory:+ dir <- cleanUpPath dir0+ let (containingDir, dName) = breakDir dir+ -- Taint up to containing dir:+ path <- taintObjPathP p containingDir+ -- Get label of containing dir:+ ldir <- liftLIO $ getPathLabelTCB path+ -- Can write to containing dir:+ guardWriteP p ldir+ -- Can still create dir:+ guardAllocP p l+ -- Create actual directory:+ liftLIO $ createDirectoryTCB l $ path </> dName+ where breakDir dir = let ds = splitDirectories dir+ cd' = joinPath $ init ds+ cd = if null cd' then [pathSeparator] else cd'+ in (cd, last ds) +--+-- Files+-- --- | Given a set of privileges, a new (maybe) label of a file, a filepath--- and the handle mode, open (and possibly create) the file. If the file --- exists the supplied label is not necessary; otherwise it must be supplied.--- The current label is raised to reflect all the traversed directories --- (of course, using privileges to minimize the taint). Additionally the--- label of the file (new or existing) must be between the current label--- and clearance. If the file is created, it is further required that the --- current process be able to write to the containing directory.-openFileP :: (LabelState l p s, Serialize l)+-- Synonym for a labeled handle.+type LabeledHandle l = Labeled l Handle++-- | Given a set of privileges, a (maybe) new label of a file, a+-- filepath, and the IO mode, open (and possibly create) the file. If the+-- file exists, the supplied label is not necessary; otherwise it must be+-- supplied. The current label is raised to reflect all the traversed+-- directories. Additionally the label of the file (new or existing)+-- must be between the current label and clearance, as imposed by+-- 'guardAlloc'. If the file is created, it is further required that the+-- current computation be able to write to the containing directory, as+-- imposed by 'guardWrite'.+openFile :: SMonadLIO l m+ => Maybe l -- ^ Label of file if created+ -> FilePath -- ^ File to open+ -> IOMode -- ^ Mode+ -> m (LabeledHandle l)+openFile = openFileP NoPrivs++-- | Same as 'openFile', but uses privileges when traversing+-- directories and performing IFC checks.+openFileP :: (SMonadLIO l m, Priv l p) => p -- ^ Privileges -> Maybe l -- ^ Label of file if created -> FilePath -- ^ File to open- -> IOMode -- ^ Mode of handle- -> LIO l p s (LHandle l)-openFileP p mlfile path' mode = withCombinedPrivs p $ \priv -> do- path <- cleanUpPath path'- let containingDir = takeDirectory path- fileName = takeFileName path- -- check that the supplied label is bounded by current label and clearance:- maybe (return ()) (aguardP priv) mlfile- -- lookup object corresponding to containing dir:- lcDir <- lookupObjPathP priv containingDir - -- unlabel the containing dir object:- actualCDir <- unlabelFilePathP priv lcDir - let objPath = actualCDir </> fileName -- actual object path- exists <- rtioTCB $ IO.doesFileExist objPath+ -> IOMode -- ^ Mode+ -> m (LabeledHandle l)+openFileP p ml file' mode = do+ file <- cleanUpPath file'+ let containingDir = takeDirectory file+ fileName = takeFileName file+ -- Check that the supplied label is bounded by current label and clearance:+ maybe (return ()) (guardAllocP p) ml+ -- Taint up to containing dir:+ path <- taintObjPathP p containingDir+ --Get label of containing dir:+ ldir <- liftLIO $ getPathLabelTCB path+ -- Create actual file path:+ let objPath = path </> fileName+ -- Check if file exists:+ exists <- liftLIO . rethrowIoTCB $ IO.doesFileExist objPath if exists- then do l <- getObjLabelTCB objPath -- label of object- aguardP priv l -- make sure we can actually read the file- -- NOTE: if mode == ReadMode, we might want to instead do- -- aguardP priv (l `lub` currentLabel) to allow opening - -- a handle for an object whose label is below the current- -- label. Some Unix systems still update a file's atime- -- when performing a read and so, for now, a read always- -- implies a write.- h <- rtioTCB $ IO.openFile objPath mode- return $ LHandleTCB l h- else case mlfile of- Nothing -> throwIO $ userError "openFileP: File label missing."+ then do+ -- Get label of file:+ l <- liftLIO $ getPathLabelTCB objPath+ -- Make sure we can create labeled handle:+ guardAllocP p l+ -- NOTE: if mode == ReadMode, we might want to instead do+ -- guardAllocp p (l `lub` currentLabel) to allow opening + -- a handle for an object whose label is below the current+ -- label. Some Unix systems still update a file's atime+ -- when performing a read and so, for now, a read always+ -- implies a write.+ h <- liftLIO . rethrowIoTCB $ IO.openFile objPath mode+ return $ labelTCB l h+ else case ml of+ Nothing -> throwLIO FSObjNeedLabel Just l -> do- wguardP priv (labelOfFilePath lcDir) -- can write to containing dir- aguardP priv l -- make sure we can actually read the file- -- NOTE: the latter is necessary as looking up the containing- -- directory object might have raised the current label.- h <- ioTCB $ createFileTCB l objPath mode- return $ LHandleTCB l h- --instance (LabelState l p s) => CloseOps (LHandle l) (LIO l p s) where- hClose = hCloseP noPrivs- hFlush = hFlushP noPrivs+ -- Can write to containing dir:+ guardWriteP p ldir+ -- Can still create file with this label:+ guardAllocP p l+ h <- liftLIO $ createFileTCB l objPath mode+ return $ labelTCB l h -instance (LabelState l p s, CloseOps (LHandle l) (LIO l p s)- , HandleOps IO.Handle b IO) =>- HandleOps (LHandle l) b (LIO l p s) where- hGet = hGetP noPrivs- hGetNonBlocking = hGetNonBlockingP noPrivs- hGetContents = hGetContentsP noPrivs- hPut = hPutP noPrivs- hPutStrLn = hPutStrLnP noPrivs+-- | Close a file handle. Must be able to write to the the labeled+-- handle, as checkd by 'guardWrite'.+hClose :: SMonadLIO l m => LabeledHandle l -> m ()+hClose = hCloseP NoPrivs -- | Close a labeled file handle.-hCloseP :: (LabelState l p s) => p -> LHandle l -> LIO l p s ()-hCloseP p' (LHandleTCB l h) = withCombinedPrivs p' $ \p ->- wguardP p l >> rtioTCB (hClose h)+hCloseP :: (SMonadLIO l m, Priv l p) => p -> LabeledHandle l -> m ()+hCloseP p lh = do+ guardWriteP p (labelOf lh)+ liftLIO . rethrowIoTCB . IO.hClose $ unlabelTCB lh ++-- | Flush a file handle. Must be able to write to the the labeled+-- handle, as checkd by 'guardWrite'.+hFlush :: SMonadLIO l m => LabeledHandle l -> m ()+hFlush = hFlushP NoPrivs+ -- | Flush a labeled file handle.-hFlushP :: (LabelState l p s) => p -> LHandle l -> LIO l p s ()-hFlushP p' (LHandleTCB l h) = withCombinedPrivs p' $ \p ->- wguardP p l >> rtioTCB (hFlush h)+hFlushP :: (SMonadLIO l m, Priv l p) => p -> LabeledHandle l -> m ()+hFlushP p lh = do+ guardWriteP p (labelOf lh)+ liftLIO . rethrowIoTCB . IO.hFlush $ unlabelTCB lh ++-- | Class used to abstract reading and writing from and to handles,+-- respectively.+class Monad m => HandleOps h b m where+ hGet :: h -> Int -> m b+ hGetNonBlocking :: h -> Int -> m b+ hGetContents :: h -> m b+ hGetLine :: h -> m b+ hPut :: h -> b -> m ()+ hPutStr :: h -> b -> m ()+ hPutStr = hPut+ hPutStrLn :: h -> b -> m ()++instance HandleOps IO.Handle L8.ByteString IO where+ hGet = L8.hGet+ hGetNonBlocking = L8.hGetNonBlocking+ hGetContents = L8.hGetContents+ hGetLine h = (L8.fromChunks . (:[])) `liftM` S8.hGetLine h+ hPut = L8.hPut+ hPutStrLn = L8.hPutStrLn++instance HandleOps IO.Handle S8.ByteString IO where+ hGet = S8.hGet+ hGetNonBlocking = S8.hGetNonBlocking+ hGetContents = S8.hGetContents+ hGetLine = S8.hGetLine+ hPut = S8.hPut+ hPutStrLn = S8.hPutStrLn++instance (SLabel l, HandleOps IO.Handle b IO) =>+ HandleOps (LabeledHandle l) b (LIO l) where+ hGet = hGetP NoPrivs+ hGetNonBlocking = hGetNonBlockingP NoPrivs+ hGetContents = hGetContentsP NoPrivs+ hGetLine = hGetLineP NoPrivs+ hPut = hPutP NoPrivs+ hPutStrLn = hPutStrLnP NoPrivs+ -- | Read @n@ bytes from the labeled handle, using privileges when -- performing label comparisons and tainting.-hGetP :: (LabelState l p s, HandleOps IO.Handle b IO)- => p -- ^ Privileges- -> LHandle l -- ^ Labeled handle- -> Int -- ^ Number of bytes to read- -> LIO l p s b-hGetP p' (LHandleTCB l h) n = withCombinedPrivs p' $ \p ->- wguardP p l >> rtioTCB (hGet h n)+hGetP :: (Priv l p, Serialize l, HandleOps IO.Handle b IO)+ => p -- ^ Privileges+ -> LabeledHandle l -- ^ Labeled handle+ -> Int -- ^ Number of bytes to read+ -> LIO l b+hGetP p lh n = do+ guardWriteP p (labelOf lh)+ liftLIO . rethrowIoTCB $ hGet (unlabelTCB lh) n -- | Same as 'hGetP', but will not block waiting for data to become -- available. Instead, it returns whatever data is available. -- Privileges are used in the label comparisons and when raising -- the current label.-hGetNonBlockingP :: (LabelState l p s, HandleOps IO.Handle b IO)- => p -> LHandle l -> Int -> LIO l p s b-hGetNonBlockingP p' (LHandleTCB l h) n = withCombinedPrivs p' $ \p ->- wguardP p l >> rtioTCB (hGetNonBlocking h n)+hGetNonBlockingP :: (Priv l p, Serialize l, HandleOps IO.Handle b IO)+ => p -> LabeledHandle l -> Int -> LIO l b+hGetNonBlockingP p lh n = do+ guardWriteP p (labelOf lh)+ liftLIO . rethrowIoTCB $ hGetNonBlocking (unlabelTCB lh) n -- | Read the entire labeled handle contents and close handle upon -- reading @EOF@. Privileges are used in the label comparisons -- and when raising the current label.-hGetContentsP :: (LabelState l p s, HandleOps IO.Handle b IO)- => p -> LHandle l -> LIO l p s b-hGetContentsP p' (LHandleTCB l h) = withCombinedPrivs p' $ \p ->- wguardP p l >> rtioTCB (hGetContents h)+hGetContentsP :: (Priv l p, Serialize l, HandleOps IO.Handle b IO)+ => p -> LabeledHandle l -> LIO l b+hGetContentsP p lh = do+ guardWriteP p (labelOf lh)+ liftLIO . rethrowIoTCB $ hGetContents (unlabelTCB lh) +-- | Read the a line from a labeled handle.+hGetLineP :: (Priv l p, Serialize l, HandleOps IO.Handle b IO)+ => p -> LabeledHandle l -> LIO l b+hGetLineP p lh = do+ guardWriteP p (labelOf lh)+ liftLIO . rethrowIoTCB $ hGetLine (unlabelTCB lh)+ -- | Output the given (Byte)String to the specified labeled handle. -- Privileges are used in the label comparisons and when raising -- the current label.-hPutP :: (LabelState l p s, HandleOps IO.Handle b IO)- => p -> LHandle l -> b -> LIO l p s ()-hPutP p' (LHandleTCB l h) s = withCombinedPrivs p' $ \p ->- wguardP p l >> rtioTCB (hPut h s)+hPutP :: (Priv l p, Serialize l, HandleOps IO.Handle b IO)+ => p -> LabeledHandle l -> b -> LIO l ()+hPutP p lh s = do+ guardWriteP p (labelOf lh)+ liftLIO . rethrowIoTCB $ hPut (unlabelTCB lh) s -- | Synonym for 'hPutP'.-hPutStrP :: (LabelState l p s, HandleOps IO.Handle b IO)- => p -> LHandle l -> b -> LIO l p s ()+hPutStrP :: (Priv l p, Serialize l, HandleOps IO.Handle b IO)+ => p -> LabeledHandle l -> b -> LIO l () hPutStrP = hPutP -- | Output the given (Byte)String with an appended newline to the -- specified labeled handle. Privileges are used in the label -- comparisons and when raising the current label.-hPutStrLnP :: (LabelState l p s, HandleOps IO.Handle b IO)- => p -> LHandle l -> b -> LIO l p s ()-hPutStrLnP p' (LHandleTCB l h) s = withCombinedPrivs p' $ \p ->- wguardP p l >> rtioTCB (hPutStrLn h s)+hPutStrLnP :: (Priv l p, Serialize l, HandleOps IO.Handle b IO)+ => p -> LabeledHandle l -> b -> LIO l ()+hPutStrLnP p lh s = do+ guardWriteP p (labelOf lh)+ liftLIO . rethrowIoTCB $ hPutStrLn (unlabelTCB lh) s -- -- Special cases -- --- | Reads a file and returns the contents of the file as a (Byte)String.-readFile :: (DirectoryOps h m, HandleOps h b m) => FilePath -> m b-readFile path = openFile path ReadMode >>= hGetContents---- | Write a (Byte)String to a file.-writeFile :: (DirectoryOps h m, HandleOps h b m, OnExceptionTCB m)- => FilePath -> b -> m ()-writeFile path contents = bracketTCB (openFile path WriteMode) hClose- (flip hPut contents)+-- | Reads a file and returns the contents of the file as a ByteString.+readFile :: (HandleOps Handle b IO, SLabel l)+ => FilePath -> LIO l b+readFile = readFileP NoPrivs -- | Same as 'readFile' but uses privilege in opening the file.-readFileP :: (HandleOps IO.Handle b IO, LabelState l p s, Serialize l) =>- p -> FilePath -> LIO l p s b-readFileP p' path = withCombinedPrivs p' $ \p ->- openFileP p Nothing path ReadMode >>= hGetContentsP p+readFileP :: (HandleOps Handle b IO, Priv l p, Serialize l)+ => p -> FilePath -> LIO l b+readFileP p file = openFileP p Nothing file ReadMode >>= hGetContentsP p --- | Same as 'writeFile' but uses privilege in opening the file.-writeFileP :: (HandleOps IO.Handle b IO, LabelState l p s, Serialize l) =>- p -> FilePath -> b -> LIO l p s ()-writeFileP p' path contents = withCombinedPrivs p' $ \privs -> do- l <- getLabel- bracketTCB (openFileP privs (Just l) path WriteMode) (hCloseP privs)- (flip (hPutP privs) contents)+-- | Write a ByteString to the given filepath with the supplied label.+writeFile :: (HandleOps Handle b IO, SLabel l)+ => l -> FilePath -> b -> LIO l ()+writeFile = writeFileP NoPrivs --- | Same as 'writeFile' but also takes the label of the file.-writeFileL :: (HandleOps IO.Handle b IO, LabelState l p s, Serialize l) =>- l -> FilePath -> b -> LIO l p s ()-writeFileL = writeFileLP noPrivs+-- | Same as 'writeFile' but uses privilege when opening, writing and+-- closing the file.+writeFileP :: (HandleOps Handle b IO, Priv l p, Serialize l)+ => p -> l -> FilePath -> b -> LIO l ()+writeFileP p l file contents = do+ bracket (openFileP p (Just l) file WriteMode) (hCloseP p)+ (flip (hPutP p) contents) --- | Same as 'writeFileL' but uses privilege in opening the file.-writeFileLP :: (HandleOps IO.Handle b IO, LabelState l p s, Serialize l) =>- p -> l -> FilePath -> b -> LIO l p s ()-writeFileLP p' l path contents = withCombinedPrivs p' $ \privs -> do- bracketTCB (openFileP privs (Just l) path WriteMode) (hCloseP privs)- (flip (hPutP privs) contents)+--+-- Setting/getting handle status/setting+--++-- | Set the buffering mode+hSetBuffering :: SMonadLIO l m => LabeledHandle l -> BufferMode -> m ()+hSetBuffering = hSetBufferingP NoPrivs++-- | Set the buffering mode+hSetBufferingP :: (SMonadLIO l m, Priv l p)+ => p -> LabeledHandle l -> BufferMode -> m ()+hSetBufferingP p lh m = do+ guardWriteP p (labelOf lh)+ liftLIO . rethrowIoTCB $ IO.hSetBuffering (unlabelTCB lh) m++-- | Get the buffering mode+hGetBuffering :: SMonadLIO l m => LabeledHandle l -> m BufferMode+hGetBuffering = hGetBufferingP NoPrivs++-- | Get the buffering mode+hGetBufferingP :: (SMonadLIO l m, Priv l p)+ => p -> LabeledHandle l -> m BufferMode+hGetBufferingP p lh = do+ taintP p (labelOf lh)+ liftLIO . rethrowIoTCB $ IO.hGetBuffering (unlabelTCB lh)++-- | Select binary mode ('True') or text mode ('False')+hSetBinaryMode :: SMonadLIO l m => LabeledHandle l -> Bool -> m ()+hSetBinaryMode = hSetBinaryModeP NoPrivs++-- | Select binary mode ('True') or text mode ('False')+hSetBinaryModeP :: (SMonadLIO l m, Priv l p)+ => p -> LabeledHandle l -> Bool -> m ()+hSetBinaryModeP p lh m = do+ guardWriteP p (labelOf lh)+ liftLIO . rethrowIoTCB $ IO.hSetBinaryMode (unlabelTCB lh) m++-- | End of file.+hIsEOF :: SMonadLIO l m => LabeledHandle l -> m Bool+hIsEOF = hIsEOFP NoPrivs++-- | End of file.+hIsEOFP :: (SMonadLIO l m, Priv l p) => p -> LabeledHandle l -> m Bool+hIsEOFP p lh = do+ taintP p (labelOf lh)+ liftLIO . rethrowIoTCB $ IO.hIsEOF (unlabelTCB lh)+ +-- | Is handle open. +hIsOpen :: SMonadLIO l m => LabeledHandle l -> m Bool +hIsOpen = hIsOpenP NoPrivs++-- | Is handle open. +hIsOpenP :: (SMonadLIO l m, Priv l p) => p -> LabeledHandle l -> m Bool +hIsOpenP p lh = do+ taintP p (labelOf lh)+ liftLIO . rethrowIoTCB $ IO.hIsOpen (unlabelTCB lh)+ +-- | Is handle closed. +hIsClosed :: SMonadLIO l m => LabeledHandle l -> m Bool +hIsClosed = hIsClosedP NoPrivs++-- | Is handle closed. +hIsClosedP :: (SMonadLIO l m, Priv l p) => p -> LabeledHandle l -> m Bool+hIsClosedP p lh = do+ taintP p (labelOf lh)+ liftLIO . rethrowIoTCB $ IO.hIsClosed (unlabelTCB lh)+ +-- | Is handle readable. +hIsReadable :: SMonadLIO l m => LabeledHandle l -> m Bool +hIsReadable = hIsReadableP NoPrivs++-- | Is handle readable. +hIsReadableP :: (SMonadLIO l m, Priv l p) => p -> LabeledHandle l -> m Bool+hIsReadableP p lh = do+ taintP p (labelOf lh)+ liftLIO . rethrowIoTCB $ IO.hIsReadable (unlabelTCB lh)+ +-- | Is handle writable. +hIsWritable :: SMonadLIO l m => LabeledHandle l -> m Bool+hIsWritable = hIsWritableP NoPrivs++-- | Is handle writable. +hIsWritableP :: (SMonadLIO l m, Priv l p) => p -> LabeledHandle l -> m Bool+hIsWritableP p lh = do+ taintP p (labelOf lh)+ liftLIO . rethrowIoTCB $ IO.hIsWritable (unlabelTCB lh)++--+-- Internal helpers+--++-- | Given a pathname to a labeled filesystem object, traverse all the+-- directories up to the object, while correspondingly raising the+-- current label. Note that if the object or a parent-directory does not+-- exist, an exception will be thrown; the label of the exception will be+-- the join of all the directory labels up to the lookup failure.+--+-- /Note:/ this function cleans up the path before doing the+-- lookup, so e.g., path @/foo/bar/..@ will first be rewritten to @/foo@+-- and thus no traversal to @bar@. Note that this is a more permissive+-- behavior than forcing the read of @..@ from @bar@.+-- @taintObjPath@ returns this cleaned up path.+taintObjPathP :: (SMonadLIO l m, Priv l p)+ => p -- ^ Privilege + -> FilePath -- ^ Path to object+ -> m FilePath+taintObjPathP p path0 = do+ -- Clean up supplied path:+ path <- cleanUpPath path0+ -- Get root directory:+ root <- liftLIO $ getRootDirTCB+ let dirs = splitDirectories . stripSlash $ path+ -- "Traverse" all directories up to object:+ forM_ ("" : allSubDirs dirs) $ \dir -> do+ l <- liftLIO $ getPathLabelTCB (root </> dir)+ taintP p l+ return $ root </> joinPath dirs++-- | Take a list of directories (e.g., @[\"a\",\"b\",\"c\"]@) and return+-- all the subtrees up to the node (@[\"a\",\"a/b\",\"a/b/c\"]@).+allSubDirs :: [FilePath] -> [FilePath]+allSubDirs dirs = reverse $ allSubDirs' dirs "" []+ where allSubDirs' [] _ acc = acc+ allSubDirs' (dir:[]) pfix acc = (pfix </> dir) : acc+ allSubDirs' (dir:ds) pfix acc = let ndir = pfix </> dir+ in allSubDirs' ds ndir (ndir : acc)++-- | Remove any 'pathSeparator's from the front of a file path.+stripSlash :: FilePath -> FilePath +stripSlash [] = []+stripSlash xx@(x:xs) | x == pathSeparator = stripSlash xs+ | otherwise = xx++-- | Cleanup a file path, if it starts out with a @..@, we consider this+-- invalid as it can be used explore parts of the filesystem that should+-- otherwise be unaccessible. Similarly, we remove any @.@ from the path.+cleanUpPath :: MonadLIO l m => FilePath -> m FilePath +cleanUpPath = liftLIO . rethrowIoTCB . doit . splitDirectories . normalise . stripSlash+ where doit [] = return []+ doit ("..":_) = throwIO FSIllegalFileName+ doit (_:"..":xs) = doit xs+ doit (".":xs) = doit xs+ doit (x:xs) = (x </>) `liftM` doit xs
LIO/LIORef.hs view
@@ -1,13 +1,139 @@-{-# LANGUAGE CPP #-}-#if __GLASGOW_HASKELL__ >= 702-{-# LANGUAGE Safe #-}-#endif--- |This module implements labeled IORefs. The interface is analogous--- to "Data.IORef", but the operations take place in the LIO monad.--- (See "LIO.LIORef.TCB" for documentation.)--- Moreover, reading the LIORef calls taint, while writing it calls--- aguard. This module exports only the safe subset (non TCB) of the--- @LIORef@ module -- trusted code can import "LIO.LIORef.TCB".-module LIO.LIORef ( module LIO.LIORef.Safe) where+{-# LANGUAGE Trustworthy #-}+{-# LANGUAGE ConstraintKinds,+ FlexibleContexts #-} -import LIO.LIORef.Safe+{- |++Mutable reference in the 'LIO' monad. As with other objects in LIO,+mutable references have an associated label that is used to impose+restrictions on its operations. In fact, labeled references+('LIORef's) are solely labeled 'IORef's with read and write access+restricted according to the label. This module is analogous to+"Data.IORef", but the operations take place in the 'LIO' monad.++-}+++module LIO.LIORef (+ LIORef+ -- * Basic Functions+ -- ** Create labeled 'IORef's+ , newLIORef, newLIORefP+ -- ** Read 'LIORef's+ , readLIORef, readLIORefP+ -- ** Write 'LIORef's+ , writeLIORef, writeLIORefP+ -- ** Modify 'LIORef's+ , modifyLIORef, modifyLIORefP+ , atomicModifyLIORef, atomicModifyLIORefP+ ) where++import LIO+import LIO.LIORef.TCB++--+-- Create labeled 'IORef's+--++-- | To create a new reference the label of the reference must be+-- below the thread's current clearance and above the current label.+-- If this is the case, the reference is built. Otherwise an exception+-- will be thrown by the underlying 'guardAlloc' guard.+newLIORef :: MonadLIO l m+ => l -- ^ Label of reference+ -> a -- ^ Initial value+ -> m (LIORef l a) -- ^ Mutable reference+newLIORef = newLIORefP NoPrivs++-- | 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 :: (MonadLIO l m, Priv l p) => p -> l -> a -> m (LIORef l a)+newLIORefP p l a = do+ guardAllocP p l+ newLIORefTCB l a++--+-- Read 'LIORef's+--++-- | Read the value of a labeled reference. A read succeeds only if the+-- 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.+readLIORef :: MonadLIO l m => LIORef l a -> m a+readLIORef = readLIORefP NoPrivs++-- | Same as 'readLIORef' except @readLIORefP@ takes a privilege object+-- which is used when the current label is raised.+readLIORefP :: (MonadLIO l m, Priv l p) => p -> LIORef l a -> m a+readLIORefP p lr = do+ taintP p $! labelOf lr+ readLIORefTCB lr++--+-- Write 'LIORef's+--++-- | Write a new value into a labeled reference. A write succeeds if+-- the current label can-flow-to the label of the reference, and the+-- label of the reference can-flow-to the current clearance. Otherwise,+-- an exception is raised by the underlying 'guardAlloc' guard.+writeLIORef :: MonadLIO l m => LIORef l a -> a -> m ()+writeLIORef = writeLIORefP NoPrivs++-- | 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 :: (MonadLIO l m, Priv l p) => p -> LIORef l a -> a -> m ()+writeLIORefP p lr a = do+ guardAllocP p $! labelOf lr + writeLIORefTCB lr a++--+-- Modify 'LIORef's+--++-- | Mutate the contents of a labeled reference. For the mutation to+-- succeed it must be that the current label can flow to the label of the+-- reference, and the label of the reference can flow to the current+-- clearance. Note that because a modifier is provided, the reference+-- contents are not observable by the outer computation and so it is not+-- required that the current label be raised. It is, however, required+-- that the label of the reference be bounded by the current label and+-- clearance (as checked by the underlying 'guardAlloc' guard).+modifyLIORef :: Label l+ => LIORef l a -- ^ Labeled reference+ -> (a -> a) -- ^ Modifier+ -> LIO l ()+modifyLIORef = modifyLIORefP NoPrivs++-- | 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 :: (MonadLIO l m, Priv l p)+ => p -> LIORef l a -> (a -> a) -> m ()+modifyLIORefP p lr f = do+ guardAllocP p $! labelOf lr + modifyLIORefTCB lr f++-- | Atomically modifies the contents of an 'LIORef'. It is required+-- that the label of the reference be above the current label, but+-- below the current clearance. Moreover, since this function can be+-- used to directly read the value of the stored reference, the+-- computation is \"tainted\" by the reference label (i.e., the+-- current label is raised to the 'join' of the current and reference+-- labels). These checks and label raise are done by 'guardWrite',+-- which will raise an exception if any of the IFC conditions cannot+-- be satisfied.+atomicModifyLIORef :: MonadLIO l m => LIORef l a -> (a -> (a, b)) -> m b+atomicModifyLIORef = atomicModifyLIORefP NoPrivs++-- | Same as 'atomicModifyLIORef' except @atomicModifyLIORefP@ takes+-- a set of privileges which are accounted for in label comparisons.+atomicModifyLIORefP :: (MonadLIO l m, Priv l p)+ => p -> LIORef l a -> (a -> (a, b)) -> m b+atomicModifyLIORefP p lr f = do+ guardWriteP p $! labelOf lr + atomicModifyLIORefTCB lr f
− LIO/LIORef/Safe.hs
@@ -1,19 +0,0 @@-{-# LANGUAGE CPP #-}-#if defined(__GLASGOW_HASKELL__) && (__GLASGOW_HASKELL__ >= 702)-{-# LANGUAGE Trustworthy #-}-#endif---- |This module exports the safe subset of the "LIO.LIORef.TCB" module.--- It is important that untrusted code be limited to this subset; information--- flow can easily be violated if the TCB functions are exported.-module LIO.LIORef.Safe ( module LIO.LIORef.TCB ) where-import LIO.LIORef.TCB ( LIORef- , newLIORef, labelOfLIORef- , readLIORef, writeLIORef- , modifyLIORef- , atomicModifyLIORef- , newLIORefP- , readLIORefP, writeLIORefP- , modifyLIORefP- , atomicModifyLIORefP- )
LIO/LIORef/TCB.hs view
@@ -1,162 +1,94 @@-{-# LANGUAGE CPP #-}-#if defined(__GLASGOW_HASKELL__) && (__GLASGOW_HASKELL__ >= 702) && (__GLASGOW_HASKELL__ < 704)-{-# LANGUAGE SafeImports #-}-#endif-#if defined(__GLASGOW_HASKELL__) && (__GLASGOW_HASKELL__ >= 704) {-# LANGUAGE Unsafe #-}-#endif--- |This module implements labeled IORefs. The interface is analogous--- to "Data.IORef", but the operations take place in the LIO monad.-module LIO.LIORef.TCB (-- * Basic Functions- LIORef- , newLIORef, labelOfLIORef- , readLIORef, writeLIORef- , modifyLIORef- , atomicModifyLIORef- -- * Privileged Functions- , newLIORefP- , readLIORefP, writeLIORefP- , modifyLIORefP- , atomicModifyLIORefP- -- * Unsafe (TCB) Functions- , newLIORefTCB- , readLIORefTCB, writeLIORefTCB- , modifyLIORefTCB- , atomicModifyLIORefTCB- ) where+{- | -import LIO.TCB+This module implements the core of labeled 'IORef's in the 'LIO ad.+to "Data.IORef", but the operations take place in the 'LIO' monad. The+types and functions exported by this module are strictly TCB and do+not perform any information flow checks. The external, safe interface+is provided and documented in "LIO.LIORef". -#if defined(__GLASGOW_HASKELL__) && (__GLASGOW_HASKELL__ >= 702)-import safe Data.IORef-#else-import Data.IORef-#endif +Different from many labeled objects (e.g., files or MVars), references+are uni-directional. This means that reading from a reference can be+done without being able to write to it; and writing to a refernece can+be done without raising the current label, as if also performing a read. --- | An @LIORef@ is an @IORef@ with an associated, static label. --- The restriction of an immutable label come from the fact that it--- is possible to leak information through the label itself.--- Hence, LIO is /flow-insensitive/. Of course, you can create an--- @LIORef@ of 'Labeled' to get a limited form of flow-sensitivity.-data LIORef l a = LIORefTCB l (IORef a)+-}+module LIO.LIORef.TCB (+ LIORef(..)+ -- * Basic Functions+ -- ** Create labeled 'IORef's+ , newLIORefTCB+ -- ** Read 'LIORef's+ , readLIORefTCB+ -- ** Write 'LIORef's+ , writeLIORefTCB+ -- ** Modify 'LIORef's+ , modifyLIORefTCB, atomicModifyLIORefTCB+ ) where +import LIO+import LIO.TCB+import Data.IORef --- | 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 :: (LabelState l p s)- => p -> l -> a -> LIO l p s (LIORef l a)-newLIORefP p' l a = withCombinedPrivs p' $ \p -> do- aguardP p l- ior <- rtioTCB $ newIORef a- return $ LIORefTCB l ior --- | To create a new reference the label of the reference must be--- below the thread's current clearance and above the current label.--- If this is the case, the reference is built.-newLIORef :: (LabelState l p s)- => l -- ^ Label of reference- -> a -- ^ Initial value- -> LIO l p s (LIORef l a) -- ^ Mutable reference-newLIORef l x = getPrivileges >>= \p -> newLIORefP p l x+-- | An @LIORef@ is an @IORef@ with an associated, fixed label. The+-- restriction to an immutable label come from the fact that it is+-- possible to leak information through the label itself, if we wish to+-- allow @LIORef@ to be an instance of 'LabelOf'. Of course, you can+-- create an @LIORef@ of 'Labeled' to get a limited form of+-- flow-sensitivity.+data LIORef l a = LIORefTCB { labelOfLIORef :: !l+ -- ^ Label of the labeled 'IORef'.+ , unlabelLIORefTCB :: (IORef a)+ -- ^ Access the underlying 'IORef', ignoring IFC.+ } --- | Trusted constructor that creates labeled references.-newLIORefTCB :: (LabelState l p s) => l -> a -> LIO l p s (LIORef l a)-newLIORefTCB l a = do- ior <- rtioTCB $ newIORef a- return $ LIORefTCB l ior+-- | Get the label of an 'LIORef'.+instance LabelOf LIORef where+ labelOf = labelOfLIORef --- | Get the label of a reference.-labelOfLIORef :: (Label l) => LIORef l a -> l-labelOfLIORef (LIORefTCB l _) = l+--+-- Create labeled 'IORef's+-- --- | Same as 'readLIORef' except @readLIORefP@ takes a privilege object--- which is used when the current label is raised.-readLIORefP :: (LabelState l p s) => p -> LIORef l a -> LIO l p s a-readLIORefP p' (LIORefTCB l r) = withCombinedPrivs p' $ \p -> do- taintP p l - rtioTCB $ readIORef r+-- | Trusted constructor that creates labeled references with the+-- given label without any IFC checks.+newLIORefTCB :: MonadLIO l m => l -> a -> m (LIORef l a)+newLIORefTCB l a = do+ ior <- liftLIO . ioTCB $! newIORef a+ return $! LIORefTCB l ior --- | Read the value of a labeled refernce. A read succeeds only if the--- 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 use 'labelOfLIORef' to check--- that a read will suceed.-readLIORef :: (LabelState l p s) => LIORef l a -> LIO l p s a-readLIORef x = getPrivileges >>= \p -> readLIORefP p x +--+-- Write 'LIORef's+-- -- | Trusted function used to read the value of a reference without -- raising the current label.-readLIORefTCB :: (LabelState l p s) => LIORef l a -> LIO l p s a-readLIORefTCB (LIORefTCB _ r) = rtioTCB $ readIORef r---- | 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 :: (LabelState l p s)- => p -> LIORef l a -> a -> LIO l p s ()-writeLIORefP p' (LIORefTCB l r) a = withCombinedPrivs p' $ \p -> do- aguardP p l - rtioTCB $ writeIORef r a+readLIORefTCB :: MonadLIO l m => LIORef l a -> m a+readLIORefTCB = liftLIO . ioTCB . readIORef . unlabelLIORefTCB --- | Write a new value into a labeled reference. A write succeeds if--- the current label can-flow-to the label of the reference, and the--- label of the reference can-flow-to the current clearance.-writeLIORef :: (LabelState l p s) => LIORef l a -> a -> LIO l p s ()-writeLIORef x a = getPrivileges >>= \p -> writeLIORefP p x a+--+-- Write 'LIORef's+-- -- | Trusted function used to write a new value into a labeled -- reference, ignoring IFC.-writeLIORefTCB :: (LabelState l p s) => LIORef l a -> a -> LIO l p s ()-writeLIORefTCB (LIORefTCB _ r) a = rtioTCB $ writeIORef r a---- | 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 :: (LabelState l p s)- => p -> LIORef l a -> (a -> a) -> LIO l p s ()-modifyLIORefP p' (LIORefTCB l r) f = withCombinedPrivs p' $ \p -> do- aguardP p l - rtioTCB $ modifyIORef r f+writeLIORefTCB :: MonadLIO l m => LIORef l a -> a -> m ()+writeLIORefTCB lr a = liftLIO . ioTCB $! writeIORef (unlabelLIORefTCB lr) a --- | Mutate the contents of a labeled reference. For the mutation to--- succeed it must be that the current label can-flow-to the label of--- the reference, and the label of the reference can-flow-to the--- current clearance. Note that because a modifer is provided, the--- reference contents are not observable by the outer computation and--- so it is not required that the current label be raised.-modifyLIORef :: (LabelState l p s)- => LIORef l a -- ^ Labeled reference- -> (a -> a) -- ^ Modifier- -> LIO l p s ()-modifyLIORef x f = getPrivileges >>= \p -> modifyLIORefP p x f+--+-- Modify 'LIORef's+-- -- | Trusted function that mutates the contents on an 'LIORef', -- ignoring IFC.-modifyLIORefTCB :: (LabelState l p s)- => LIORef l a -> (a -> a) -> LIO l p s ()-modifyLIORefTCB (LIORefTCB _ r) f = rtioTCB $ modifyIORef r f----- | Same as 'atomicModifyLIORef' except @atomicModifyLIORefP@ takes--- a set of privileges which are accounted for in label comparisons.-atomicModifyLIORefP :: (LabelState l p s) =>- p -> LIORef l a -> (a -> (a, b)) -> LIO l p s b-atomicModifyLIORefP p' (LIORefTCB l r) f = withCombinedPrivs p' $ \p -> do- aguardP p l- rtioTCB $ atomicModifyIORef r f---- | Atomically modifies the contents of an 'LIORef'. It is required--- that the label of the reference be above the current label, but--- below the current clearance. -atomicModifyLIORef :: (LabelState l p s) =>- LIORef l a -> (a -> (a, b)) -> LIO l p s b-atomicModifyLIORef x f = getPrivileges >>= \p -> atomicModifyLIORefP p x f+modifyLIORefTCB :: MonadLIO l m => LIORef l a -> (a -> a) -> m ()+modifyLIORefTCB lr f = liftLIO . ioTCB $! modifyIORef (unlabelLIORefTCB lr) f -- | Trusted function used to atomically modify the contents of a -- labeled reference, ignoring IFC.-atomicModifyLIORefTCB :: (LabelState l p s)- => LIORef l a -> (a -> (a, b)) -> LIO l p s b-atomicModifyLIORefTCB (LIORefTCB _ r) f = rtioTCB $ atomicModifyIORef r f+atomicModifyLIORefTCB :: MonadLIO l m => LIORef l a -> (a -> (a, b)) -> m b+atomicModifyLIORefTCB lr f =+ liftLIO . ioTCB $! atomicModifyIORef (unlabelLIORefTCB lr) f
+ LIO/Label.hs view
@@ -0,0 +1,160 @@+{-# LANGUAGE Safe #-}+{- | ++Labels are a way of describing who can observe and modify data. There+is a partial order, generally pronounced \"can flow to\" on labels.+In LIO we write this partial order ``canFlowTo`` (in the literature it+is usually written as ⊑).++The idea is that data labeled @L_1@ may affect data labeled @L_2@+only if @L_1@ ``canFlowTo`` @L_2@. The 'LIO' monad (see "LIO.Core")+keeps track of the current label of the executing code (accessible via+the 'getLabel' function). Code may attempt to perform various IO or+memory operations on labeled data. Hence, touching data may change+the current label (or throw an exception if an operation would violate+flow restrictions).++If the current label is @L_cur@, then it is only permissible to read+data labeled @L_r@ if @L_r ``canFlowTo`` L_cur@. This is sometimes+termed \"no read up\" in the literature; however, because the partial+order allows for incomparable labels (i.e., two labels @L_1@ and @L_2@+such that @not (L_1 ``canFlowTo`` L_2) && not (L_2 ``canFlowTo``+L_1)@), a more appropriate phrasing would be \"read only what can flow+to your label\". Note that, rather than throw an exception, reading+data will often just increase the current label to ensure that @L_r+``canFlowTo`` L_cur@. The LIO monad keeps a second label, called the+/clearance/ (accessible via the @getClearance@ function), that+represents the highest value the current thread can raise its label+to. The purpose of clearance is to enforce discretionary access+control: you can set the clearance to a label @L_clear@ as to prevent+a piece of LIO code from accessing anything above @L_clear@.++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@.++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.++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').++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.++-}++module LIO.Label (+ -- * Labels+ Label(..), upperBound, lowerBound+ -- * Accessing label of labeled values+ , LabelOf(..) + ) where++import Data.Typeable++-- | This class defines a label format, corresponding to a bounded+-- lattice (see <https://en.wikipedia.org/wiki/Bounded_lattice>).+-- Specifically, it is necessary to define a bottom element+-- 'bottom' (in literature, written as ⊥), a top element 'top' (in+-- literature, written as ⊤), a join, or least upper bound, 'lub'+-- (in literature, written as ⊔), a meet, or greatest lower bound,+-- 'glb' (in literature, written as ⊓), and of course the+-- can-flow-to partial-order 'canFlowTo' (in literature, written as+-- ⊑).+class (Eq l, Show l, Typeable l) => Label l where+ -- | Bottom, or minimum, element. It must be that for any label @L@, + -- @'bottom' ``canFlowTo`` L@.+ bottom :: l+ -- | Top, or maximum, element. It must be that for any label @L@, + -- @L ``canFlowTo`` 'top'@.+ top :: l+ -- | /Least/ upper bound, or join, of two labels. For any two labels+ -- @L_1@ and @L_2@, such that @L_3 = L_1 ``lub`` L_2@, it must be that:+ --+ -- * @L_1 ``canFlowTo`` L_3@,+ --+ -- * @L_2 ``canFlowTo`` L_3@, and+ --+ -- * 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.+ lub :: l -> l -> l+ -- | /Greatest/ lower bound, or meet, of two labels. For any two labels+ -- @L_1@ and @L_2@, such that @L_3 = L_1 ``glb`` L_2@, it must be that:+ --+ -- * @L_3 ``canFlowTo`` L_1@,+ --+ -- * @L_3 ``canFlowTo`` L_2@, 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.+ 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+ -- <https://en.wikipedia.org/wiki/Partially_ordered_set>), and must+ -- satisfy the following rules:+ --+ -- * Reflexivity: @L_1 ``canFlowTo`` L_1@ for any @L_1@.+ --+ -- * Antisymmetry: If @L_1 ``canFlowTo`` L_2@ and+ -- @L_2 ``canFlowTo`` L_1@ then @L_1 = L_2@.+ --+ -- * Transitivity: If @L_1 ``canFlowTo`` L_2@ and+ -- @L_2 ``canFlowTo`` L_3@ then @L_1 ``canFlowTo`` L_3@.+ canFlowTo :: l -> l -> Bool++-- | A more meaningful name for 'lub'. Note that since the name+-- does not imply /least/ upper bound it is not a method of 'Label'.+upperBound :: Label l => l -> l -> l+upperBound = lub++-- | A more meaningful name for 'glb'. Note that since the name+-- does not imply /greatest/ lower bound it is not a method of+-- 'Label'.+lowerBound :: Label l => l -> l -> l+lowerBound = glb++-- | 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 :: Label l => t l a -> l+
+ LIO/Labeled.hs view
@@ -0,0 +1,187 @@+{-# LANGUAGE Trustworthy #-}+{-# LANGUAGE ConstraintKinds,+ FlexibleContexts #-}++{- |++A data type 'Labeled' protects access to pure values (hence, we refer+to values of type @'Label' a@ as /labeled values/). The role of+labeled values is to allow users to associate heterogeneous labels (see+"LIO.Label") with values. Although LIO\'s current label protects all+values in scope with the current label, 'Labeled' values allow for+more fine grained protection. Moreover, trusted code may easily+inspect 'Labeled' values, for instance, when inserting values into a+database.++Without the appropriate privileges, one cannot produce a pure+/unlabeled/ value that depends on a secret 'Labeled' value, or+conversely produce a high-integrity 'Labeled' value based on pure+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+'Functor'-like class ('LabeledFunctor') on 'Labeled' is also defined+in this module.++-}++module LIO.Labeled (+ Labeled+ -- * Label values+ , label, labelP+ -- * Unlabel values+ , unlabel, unlabelP+ -- * Relabel values+ , relabelLabeledP+ , taintLabeled, taintLabeledP , untaintLabeled, untaintLabeledP+ -- * Labeled functor+ -- $functor+ , LabeledFunctor(..)+ ) where++import LIO.Label+import LIO.Core+import LIO.Privs+import LIO.Labeled.TCB+import Control.Monad++-- | Returns label of a 'Labeled' type.+instance LabelOf Labeled where+ labelOf = labelOfLabeled++--+-- Label values+--++-- | Function to construct a 'Labeled' from a label and pure value. If+-- the current label is @lcurrent@ and the current clearance is+-- @ccurrent@, then the label @l@ specified must satisfy @lcurrent+-- ``canFlowTo`` l && l ``canFlowTo`` ccurrent@. Otherwise an+-- exception is thrown (see 'guardAlloc').+label :: MonadLIO l m => l -> a -> m (Labeled l a)+label = labelP NoPrivs++-- | Constructs a 'Labeled' using privilege to allow the `Labeled`'s+-- label to be below the current label. If the current label is+-- @lcurrent@ and the current clearance is @ccurrent@, then the privilege+-- @p@ and label @l@ specified must satisfy @canFlowTo p lcurrent l@ and+-- @l ``canFlowTo`` ccurrent@. Note that privilege is not used to bypass+-- the clearance. You must use 'setClearanceP' to raise the clearance+-- first if you wish to create an 'Labeled' at a higher label than the+-- current clearance.+labelP :: (MonadLIO l m, Priv l p) => p -> l -> a -> m (Labeled l a)+labelP p l a = do+ guardAllocP p l+ return $! labelTCB l a++--+-- Unlabel values+--++-- | Within the 'LIO' monad, this function takes a 'Labeled' and returns+-- the underlying value. Thus, in the 'LIO' monad one can say:+--+-- > x <- unlabel (xv :: Labeled SomeLabelType Int)+--+-- And now it is possible to use the value of @x :: Int@, which is the+-- pure value of what was stored in @xv@. Of course, @unlabel@ also+-- raises the current label. If raising the label would exceed the+-- current clearance, then @unlabel@ throws 'ClearanceViolation'.+-- However, you can use 'labelOf' to check if 'unlabel' will succeed+-- without throwing an exception.+unlabel :: MonadLIO l m => Labeled l a -> m a+unlabel = unlabelP NoPrivs++-- | 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 :: (MonadLIO l m, Priv l p) => p -> Labeled l a -> m a+unlabelP p lv = do+ taintP p $! labelOf lv+ return $! unlabelTCB lv++--+-- Relabel values+--++-- | Relabels a 'Labeled' value to the supplied label if the given+-- privilege privileges permits it. It must be that the original+-- label and new label are equal, modulo the supplied privileges. In+-- other words the label remains in the same congruence class.+--+-- Consequently @relabelP p l lv@ throws an 'InsufficientPrivs'+-- exception if+--+-- @'canFlowToP' p l ('labelOf' lv) && 'canFlowToP' p ('labelOf' lv) l@+--+-- does not hold.+relabelLabeledP :: (MonadLIO l m, Priv l p)+ => p -> l -> Labeled l a -> m (Labeled l a)+relabelLabeledP p newl lv = do+ let origl = labelOf lv+ unless (canFlowToP p newl origl &&+ canFlowToP p origl newl) $ throwLIO InsufficientPrivs+ return . labelTCB newl $! unlabelTCB lv++-- | Raises the label of a 'Labeled' to the 'upperBound' of it's current+-- label and the value supplied. The label supplied must be bounded by+-- the current label and clearance, though the resulting label may not be+-- if the 'Labeled' is already above the current thread's clearance. If+-- the supplied label is not bounded then @taintLabeled@ will throw an+-- exception (see 'guardAlloc').+taintLabeled :: MonadLIO l m => l -> Labeled l a -> m (Labeled l a)+taintLabeled = taintLabeledP NoPrivs++-- | Same as 'taintLabeled', but uses privileges when comparing the+-- current label to the supplied label. In other words, this function+-- can be used to lower the label of the labeled value by leveraging+-- the supplied privileges.+taintLabeledP :: (MonadLIO l m, Priv l p)+ => p -> l -> Labeled l a -> m (Labeled l a)+taintLabeledP p l lv = do+ guardAllocP p l+ return . labelTCB (l `upperBound` labelOf lv) $! unlabelTCB lv++-- | Downgrades the label of a 'Labeled' as much as possible given the+-- current privilege.+untaintLabeled :: MonadLIO l m => l -> Labeled l a -> m (Labeled l a)+untaintLabeled = untaintLabeledP NoPrivs++-- | Same as 'untaintLabeled' but uses the supplied privileges when+-- downgrading the label of the labeled value.+untaintLabeledP :: (MonadLIO l m, Priv l p)+ => p -> l -> Labeled l a -> m (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:++1. 'fmap' would have type @Labeled l a -> (a -> b) -> Labeled b@ and thus + creating /new/ labeled values above the current clearance or below+ the current label would be feasible (given one such value).+2. 'LIO' is polymorphic in the label type and thus 'fmap' would is+ susceptible to /refinement attacks/. Superficially if the label type+ contains an integrity component (see for example "LIO.DCLabel")+ then @fmap (\ -> 3) lv@ would produce a high-integrity labeled @3@+ if @lv@ is a high-integrity labeled value without any any authority+ or /endorsement/.++As a result, we provide a class 'LabeledFunctor' that export 'lFmap'+(labeled 'lFmap') that addressed the above issues. Firstly, each newly+created value is in the 'LIO' monad and secondly each label format+implementation must produce their own definition of 'lFmap' such that+the end label protects the computation result accordingly.+-}++-- | IFC-aware functor instance. Since certain label formats may contain+-- integrity information, this is provided as a class rather than a+-- function. Such label formats will likely wish to drop endorsements in+-- the new labeled valued.+class Label l => LabeledFunctor l where+ -- | 'fmap'-like funciton that is aware of the current label and+ -- clearance.+ lFmap :: MonadLIO l m => Labeled l a -> (a -> b) -> m (Labeled l b)
+ LIO/Labeled/TCB.hs view
@@ -0,0 +1,52 @@+{-# LANGUAGE Unsafe #-}+{-# LANGUAGE DeriveDataTypeable #-}++{- |++This is the TCB-restricted version of "LIO.Labeled", which documents+the implementation of 'Labeled' values and their use. It provides+functions for labeling ('labelTCB') and unlabeling ('unlabelTCB')+labeled values without imposing any information flow restrictions.++-}++module LIO.Labeled.TCB (+ Labeled(..)+ , labelTCB+ ) where++import Data.Typeable+import LIO.Label+import LIO.TCB++-- | @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.)+data Labeled l t = LabeledTCB { labelOfLabeled :: !l+ -- ^ Label of 'Labeled' valued+ , unlabelTCB :: !t + -- ^ Extracts the value from an+ -- 'Labeled', discarding the label and any+ -- protection.+ } deriving Typeable++-- | Trusted constructor that creates labeled values.+labelTCB :: Label l => l -> a -> Labeled l a+labelTCB = LabeledTCB++-- | Trusted 'Show' instance.+instance (Label l, Show a) => ShowTCB (Labeled l a) where+ showTCB (LabeledTCB l t) = show t ++ " {" ++ show l ++ "}"++-- | 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 (labelTCB lab val, rest)
− LIO/MonadCatch.hs
@@ -1,88 +0,0 @@-{-# LANGUAGE CPP #-}-#if __GLASGOW_HASKELL__ >= 702-{-# LANGUAGE Safe #-}-#endif-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE RankNTypes #-}---- | This module generalizes 'throw' and 'catch' (from "Control.Exception")--- to methods that can be defined on multiple monads.-module LIO.MonadCatch (MonadCatch(..), genericBracket) where--import Prelude hiding (catch)-import Control.Exception (Exception, SomeException)-import qualified Control.Exception as E---- | @MonadCatch@ is the class used to generalize the standard IO--- @catch@ and @throwIO@ functions to methods that can be defined in--- multiple monads.--- Minimal definition requires: @mask@, @throwIO@, @catch@, and--- @onException@.-class (Monad m) => MonadCatch m where- -- | Executes a computation with asynchronous exceptions masked.- -- See "Control.Exception" for more details.- mask :: ((forall a. m a -> m a) -> m b) -- ^ Function- -- that takes a mask-restoring function as argument and returns an- -- action to execute.- -> m b- -- | Like 'mask', but does not pass a restore action to the argument.- mask_ :: m a -> m a- mask_ io = mask $ \_ -> io- -- | A variant of @throwIO@ that can be used within the monad.- throwIO :: (Exception e) => e -> m a- -- | Simplest exception-catching function.- catch :: (Exception e) => m a -- ^ Computation to run - -> (e -> m a) -- ^ Handler- -> m a- -- | Version of 'catch' with the arguments swapped around.- handle :: (Exception e) => (e -> m a) -> m a -> m a- handle = flip catch- -- | Performs an action and a subsequent action if an exceptino is raised.- onException :: m a -- ^ Computation to run first- -> m b -- ^ Computation to run after, if- -- an exception was raised.- -> m a- onException io h = io `catch` \e -> h >> throwIO (e :: SomeException)- -- | This function allows you to execute an action with an initial- -- \"acquire resource\" and final \"release resource\" as @bracket@- -- of "Control.Exception".- bracket :: m b -- ^ Computation to run first- -> (b -> m c) -- ^ Computation to run last- -> (b -> m a) -- ^ Computation to run in-between- -> m a- bracket = genericBracket onException- -- | Variant of 'bracket' where the return value from the first- -- computation is not required. - bracket_ :: m a -> m b -> m c -> m c- bracket_ a b c = bracket a (const b) (const c)- -- | Performs an action and a subsequent action.- finally :: m a -- ^ Computation to run first- -> m b -- ^ Computation to run after- -> m a- finally a b = mask $ \restore -> do- r <- restore a `onException` b- _ <- b- return r--instance MonadCatch IO where- mask = E.mask- throwIO = E.throwIO- catch = E.catch- onException = E.onException- bracket = E.bracket---- | Given some general @onException@ function, @genericBracket@--- allows you to execute an action with an initial \"acquire resource\"--- and final \"release resource\" as @bracket@ of "Control.Exception".-genericBracket :: (MonadCatch m) =>- (m b -> m c -> m b) -- ^ On exception function- -> m a -- ^ Action to perform first- -> (a -> m c) -- ^ Action to perform last- -> (a -> m b) -- ^ Action to perform in-between- -> m b -- ^ Result of in-between action-genericBracket myOnException m1 m3 m2 =- mask $ \restore -> do- a <- m1- b <- restore (m2 a) `myOnException` (m3 a)- _ <- m3 a- return b
− LIO/MonadLIO.hs
@@ -1,31 +0,0 @@-{-# LANGUAGE CPP #-}-#if __GLASGOW_HASKELL__ >= 702-{-# LANGUAGE Trustworthy #-}-#endif-{-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE FunctionalDependencies #-}-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE UndecidableInstances #-}-{-# LANGUAGE OverlappingInstances #-}---- | This module provides a function 'liftLIO' for executing 'LIO'--- computations from transformed versions of the 'LIO' monad.--- There is also a method 'liftIO', which is a synonym for 'liftLIO',--- to help with porting code that expects to run in the @IO@ monad.-module LIO.MonadLIO (MonadLIO(..)) where--import LIO.TCB (LIO, LabelState)-import Control.Monad.Trans (MonadTrans(..))---- | MonadIO-like class.-class (Monad m, LabelState l p s) => MonadLIO m l p s | m -> l p s where- liftLIO :: LIO l p s a -> m a- liftIO :: LIO l p s a -> m a- liftIO = liftLIO--instance (LabelState l p s) => MonadLIO (LIO l p s) l p s where- liftLIO = id--instance (MonadLIO m l p s, MonadTrans t, Monad (t m))- => MonadLIO (t m) l p s where- liftLIO = lift . liftLIO
+ LIO/Privs.hs view
@@ -0,0 +1,96 @@+{-# LANGUAGE Trustworthy #-}+{-# LANGUAGE MultiParamTypeClasses,+ FlexibleInstances #-}+{- | ++Privileges are instances of the class called 'Priv'. They 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 'Priv' class is 'canFlowToP', which performs a+more permissive can-flow-to check by exercising particular privileges+(in literature this relation is a pre-order, commonly written as+⊑ₚ). Almost all 'LIO' operations have variants ending+@...P@ that take a privilege argument to act in a more permissive way. ++All 'Priv' types are 'Monoid's, and so privileges can be combined with+'mappend'. The creation of 'Priv' 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(..)+ -- * Privileges+ , Priv(..)+ , NoPrivs(..)+ ) where++import Data.Monoid++import LIO.Label+import LIO.Privs.TCB++-- | 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, PrivTCB p, Monoid p) => Priv 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.+ canFlowToP :: p -> l -> l -> Bool+ canFlowToP p a b = partDowngradeP 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 ``upperBound`` L_g@. If @p@ contains /all/+ -- possible privileges, then @L_r == L_g@.+ --+ -- More specifically, @L_r@ is the greatest lower bound of the+ -- set of all labels @L_l@ satisfying:+ --+ -- 1. @ L_g ⊑ L_l@, and+ --+ -- 2. @ L ⊑ₚ L_l@.+ --+ -- Operationally, @partDowngradeP@ captures the minimum change required+ -- to the current label when viewing data labeled @L_l@. A common+ -- pattern is to use the result of 'getLabel' as @L_g@ (i.e., the+ -- goal is to use privileges @p@ to avoid changing the label at all),+ -- and then compute @L_r@ based on the label of data the code is+ -- about to observe. + partDowngradeP :: p -- ^ Privileges+ -> l -- ^ Label from which data must flow+ -> l -- ^ Goal label+ -> l -- ^ Result++--+-- No privileges+--++-- | Generic privilege type used to denote the lack of privileges.+data NoPrivs = NoPrivs deriving (Show, Read)++instance PrivTCB NoPrivs+instance PrivDesc NoPrivs NoPrivs where privDesc = id+instance MintTCB NoPrivs NoPrivs where mintTCB = id+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 => Priv l NoPrivs where+ canFlowToP _ l1 l2 = l1 `canFlowTo` l2+ partDowngradeP _ l lg = l `upperBound` lg
+ LIO/Privs/TCB.hs view
@@ -0,0 +1,57 @@+{-# LANGUAGE Unsafe #-}+{-# LANGUAGE MultiParamTypeClasses,+ FunctionalDependencies #-}++{- | ++This module exports the class 'PrivTCB' which all privilege types must+be an instance of. This class is in the TCB since privileges can be+used to bypass label restrictions and untrusted code should not be+allowed to do do arbitrarily. See "LIO.Privs" for an additional+description of privileges and their role within "LIO".++In addition to 'PrivTCB' this module exports the class 'PrivDesc'+which provides a function from privileges to /privilege descriptions/.+A privilege description is a meaningful and safe interpretation of a+coresponding privilege (note that the function must be one-to-on).+Privilege descriptions are used in "LIO.Gate" as \"proof\" of+privilege ownership. Additionally, privilege descriptions can be used+by TCB code to mint new privileges using the 'MintTCB' class.++-}++module LIO.Privs.TCB ( + PrivTCB+ , PrivDesc(..)+ , MintTCB(..)+ ) where++-- | Zero-method class that imposes a restriction on what code+-- (namely trusted) can make a \"privilege type\".+class PrivTCB p++-- | Class used to convert a privilege to a privilege description. This+-- is particularly useful when one piece of code wishes to prove+-- ownership of certain privileges without granting the privilege.+-- NOTE: it (almost) always a security violation if the privilege is+-- also the privilege description.+--+-- Although this class is not part of the TCB there are some security+-- implications that should be considered when making a type an+-- instance of this class. Specifically, if the value constructor for+-- the privilege description type @d@ is exported then some+-- trusted code must be used when \"proving\" ownership of a certain+-- privilege. This is generally a good idea even if the constructor is+-- not made available, since code can (usually) cache such privilege +-- descriptions. An alternative is to use phantom types to enforce a+-- linear-type-like behavior.+class (PrivTCB p, Show d) => PrivDesc p d | p -> d, d -> p where+ -- | Retrive privilege description from a privilege.+ privDesc :: p -> d++-- | The dual of 'PrivDesc'. This class provides @mintTCB@ which may+-- be used to convert, or /mint/, a privilege descriptions into a+-- privilege. Of course, @mintTCB@ must be restricted to the TCB.+class (PrivDesc p d) => MintTCB p d where+ -- | Mint a new privilege values given a privilege description.+ mintTCB :: d -> p
− LIO/Safe.hs
@@ -1,60 +0,0 @@-{-# LANGUAGE CPP #-}-#if __GLASGOW_HASKELL__ >= 702-{-# LANGUAGE Trustworthy #-}-#endif---- | This module exports the subset of symbols in the "LIO.TCB" module--- that are safe for untrusted code to access. See the "LIO.TCB"--- module for documentation.--module LIO.Safe ( Label(..)- , Priv(..), noPrivs- , getPrivileges, withPrivileges- , withCombinedPrivs - , dropPrivileges - , LIO, LabelState- , evalLIO- , getLabel, setLabelP- , getClearance, lowerClr, lowerClrP, withClearance- , labelOf- , label, labelP- , unlabel, unlabelP- , taintLabeled- , untaintLabeled, untaintLabeledP- , relabelP- , toLabeled, toLabeledP, discard, discardP- , taint, taintP- , wguard, wguardP, aguard, aguardP- , Labeled- , LabelFault(..)- , catchP, handleP, onExceptionP, bracketP- , evaluate- , PrivDesc- , Gate, mkGate, mkGateP, callGate- ) where--import LIO.TCB ( Label(..)- , Priv(..), noPrivs- , getPrivileges, withPrivileges- , withCombinedPrivs - , dropPrivileges - , LIO, LabelState- , evalLIO- , getLabel, setLabelP- , getClearance, lowerClr, lowerClrP, withClearance- , labelOf- , label, labelP- , unlabel, unlabelP- , taintLabeled- , untaintLabeled, untaintLabeledP- , relabelP- , toLabeled, toLabeledP, discard, discardP- , taint, taintP- , wguard, wguardP, aguard, aguardP- , Labeled- , LabelFault(..)- , catchP, handleP, onExceptionP, bracketP- , evaluate- , PrivDesc- , Gate, mkGate, mkGateP, callGate- )
LIO/TCB.hs view
@@ -1,1201 +1,185 @@-{-# LANGUAGE CPP #-}-#if __GLASGOW_HASKELL__ >= 704-{-# LANGUAGE Unsafe #-}-#endif-{-# LANGUAGE DeriveFunctor #-}-{-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE GeneralizedNewtypeDeriving #-}-{-# LANGUAGE ExistentialQuantification #-}-{-# LANGUAGE DeriveDataTypeable #-}-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE RankNTypes #-}-{-# LANGUAGE FunctionalDependencies #-}---- | This module implements the core of the Labeled IO library for--- information flow control 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 the 'evalLIO' function. (Though usually a wrapper--- function is employed depending on the type of labels used by an--- application. For example, with "LIO.DCLabel", you would use--- 'evalDC' to execute an untrusted computation. There are also--- abbreviations for the 'LIO' monad type of a particular--- label--for instance 'DC'.)------ A data structure 'Labeled' (labeled value) protects access to pure--- values. Without the appropriate privileges, one cannot produce a--- pure value that depends on a secret 'Labeled', or conversely produce a--- high-integrity 'Labeled' based on pure data. The function 'toLabeled'--- allows one to seal off the results of an LIO computation inside an--- 'Labeled' without tainting the current flow of execution. 'unlabel'--- conversely allows one to use the value stored within a 'Labeled'.------ We note that using 'toLabeled' is /not/ safe with respect to--- the termination covert channel. Specifically, LIO with 'toLabeled'--- is only termination-insensitive non-interfering. For a--- termination-sensitive 'toLabeled'-like function, see "LIO.Concurrent".--- --- Any code that imports this module is part of the--- /Trusted Computing Base/ (TCB) of the system. Hence, untrusted--- code must be prevented from importing this module. The exported--- symbols ending @...TCB@ can be used to violate label protections--- even from within pure code or the LIO Monad. A safe subset of--- these symbols is exported by the "LIO.Safe" module, which is how--- untrusted code should access the core label functionality.--- ("LIO.Safe" is also re-exported by "LIO", the main gateway to--- this library.)----module LIO.TCB (-- * Basic Label Functions- -- $labels- Label(..)- -- * Basic Privilige Functions- -- $privs- , Priv(..), noPrivs- , getPrivileges- , setPrivileges- , withPrivileges- , withCombinedPrivs- , dropPrivileges - -- * Labeled IO Monad (LIO)- -- $LIO- , LIO(..), LabelState- , evalLIO, runLIO- , newState- , getLabel, setLabelP- , getClearance, lowerClr, lowerClrP- , withClearance- -- * Labeled Values- , Labeled- , labelOf- , label, labelP- , unlabel, unlabelP- , taintLabeled- , untaintLabeled, untaintLabeledP- , relabelP- , toLabeled, toLabeledP, discard, discardP- -- ** LIO Guards- -- $guards- , taint, taintP- , wguard, wguardP- , aguard, aguardP- -- * Labeled Exceptions- -- $lexception- -- ** Exception type thrown by LIO library- , LabelFault(..)- -- ** Throwing and Catching Labeled Exceptions- -- $throw- , LabeledException(..)- , MonadCatch(..)- , catchP, handleP- , onExceptionP, bracketP- , evaluate- -- * Gates- -- $gates- , PrivDesc(..)- , Gate(..)- , mkGate, mkGateP- , callGate- -- * Unsafe (TCB) Operations- -- ** Basic Label Functions- , PrivTCB, MintTCB(..)- -- ** Labeled IO Monad (LIO)- , LIOstate(..)- , getTCB, putTCB- , getLabelStateTCB, putLabelStateTCB- , setLabelTCB, lowerClrTCB- -- ** Labeled Values- , ShowTCB(..), ReadTCB(..)- , labelTCB, unlabelTCB- -- ** Labeled Exceptions- , catchTCB, OnExceptionTCB(..)- , ioTCB, rtioTCB- -- ** Gates- , mkGateTCB, callGateTCB- ) where--import Prelude hiding (catch)-import Control.Exception hiding (catch, handle, throw, throwIO,- onException, block, unblock,- evaluate, finally, mask)-import qualified Control.Exception as E-import Data.Monoid-import Data.Typeable-import Data.Functor-import Data.IORef-import Control.Applicative-import Text.Read (minPrec)-import LIO.MonadCatch--import Control.Monad.Error-import Control.Monad.State.Lazy hiding (put, get)-------------------------------------------------------------------------- Basic label functions --------------------------------------------------------------------------------------------------------------------{- $labels--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 Haskell we write this partial order ``leq`` (in the literature it-is usually written as ⊑).--The idea is that data labeled @L_1@ should affect data labeled-@L_2@ only if @L_1@ ``leq`` @L_2@, (i.e., @L_1@ /can flow to/-@L_2@). The 'LIO' monad 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.-Touching data may change the current label (or throw an exception-if an operation would violate can-flow-to restrictions).--If the current label is @L_cur@, then it is only permissible to-read data labeled @L_r@ if @L_r ``leq`` 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 ``leq`` L_2) && not (L_2 ``leq`` 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 ``leq`` L_cur@. The LIO monad keeps a second label, called-the /clearance/ (see 'getClearance'), that represents the highest-value the current thread can raise its label to. The purpose of-clearance is to enforce of discretionary access control: you can-set the clearance to a label @L_clear@ as to prevent a piece of-LIO code from reading anything above @L_clear@.--Conversely, it is only permissible to modify data labeled @L_w@-when @L_cur``leq`` 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 == L_w@.--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 @Ld_@, such that @not (L_cur-``leq`` L_d || L_d ``leq`` C_cur)@, then the current thread can-neither read nor write the data, at least without invoking some-privilege.--}--{- | This class defines a label format, corresponding to a bounded-lattice. Specifically, it is necessary to define a bottom element-'lbot' (in literature, written as ⊥), a top element 'ltop'-(in literature, written as ⊤), a join, or least upper bound,-'lub' (in literature, written as ⊔), a meet, or greatest lower-bound, 'glb' (in literature, written as ⊓), and of course the-can-flow-to partial-order 'leq' (in literature, written as ⊑).--}-class (Eq a, Show a, Read a, Typeable a) => Label a where- -- | Bottom- lbot :: a- -- | Top- ltop :: a- -- | Least upper bound (join) of two labels- lub :: a -> a -> a- -- | Greatest lower bound (meet) of two labels- glb :: a -> a -> a- -- | Can-flow-to relation- leq :: a -> a -> Bool-------------------------------------------------------------------------- Basic privilege functions ----------------------------------------------------------------------------------------------------------------{- $privs--Privilege comes from a separate class called 'Priv', representing the-ability to bypass the protection of certain labels. Essentially,-privilege allows you to behave as if @L_1 ``leq`` 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 ``leq`` L_2)@ is called-/downgrading/.--The basic method of the 'Priv' object is 'leqp', which performs the-more permissive can-flow-to check in the presence of particular-privileges (in literature this relation is a pre-order, commonly-written as ⊑ₚ). Many 'LIO' operations have variants-ending @...P@ that take a privilege argument to act in a more-permissive way. It is also possible to execute an 'LIO' action with-a set of privileges (without explicitly using the @...P@ combinators)-using the 'withPrivileges' combinator. Practicing the /principle of-least privilege/, it is recommended that 'withPrivileges' only be-used in small blocks of code in which many operations require the-same privileges. It is safer to use @...P@ operators and privileges-explicitly.--All 'Priv' types are monoids, and so can be combined with 'mappend'.-How to create 'Priv' objects is specific to the particular label-type in use. The method used is 'mintTCB', but the arguments depend-on the particular label type. (Of course, the symbol 'mintTCB'-must not be available to untrusted code.)---}----- | @PrivTCB@ is a method-less class whose only purpose is to be--- unavailable to unprivileged code. Since @(PrivTCB t) =>@ is in the--- context of class 'Priv' and unprivileged code cannot create new--- instances of the @PrivTCB@ class, this ensures unprivileged code--- cannot create new instances of the 'Priv' class either, even though--- the symbol 'Priv' is exported by "LIO.Base" and visible to--- untrusted code.-class PrivTCB t where---- | This class defines privileges and the more-permissive relation--- ('leqp') on labels using privileges. Additionally, it defines--- 'lostar' which is used to compute the smallest difference between--- two labels given a set of privilege.-class (Label l, Monoid p, PrivTCB p) => Priv l p where- -- | The \"can-flow-to given privileges\" pre-order used to- -- compare two labels in the presence of privileges.- -- If @'leqp' p L_1 L_2@ holds, then privileges @p@ are sufficient to- -- downgrade data from @L_1@ to @L_2@. Note that @'leq' L_1 L_2@- -- implies @'leq' p L_1 L_2@ for all @p@, but for some labels and- -- privileges, 'leqp' will hold even where 'leq' does not.- leqp :: p -> l -> l -> Bool- leqp p a b = lostar p a b `leq` b-- -- | Roughly speaking, @L_r = lostar p L L_g@ computes how close- -- one can come to downgrading data labeled @L@ to the goal label- -- @L_g@, given privileges @p@. When @p == 'noPrivs'@, the resulting- -- label @L_r == L ``lub``L_g@. If @p@ contains all possible privileges,- -- then @L_r == L_g@.- --- -- More specifically, @L_r@ is the greatest lower bound of the- -- set of all labels @L_l@ satisfying:- --- -- 1. @ L_g ⊑ L_l@, and- --- -- 2. @ L ⊑ₚ L_l@.- --- -- Operationally, @lostar@ 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. For example, 'taintP' could be- -- implemented as:- --- -- @- -- taintP p l = do lcurrent <- 'getLabel'- -- 'taint' (lostar p l lcurrent)- -- @- lostar :: p -- ^ Privileges- -> l -- ^ Label from which data must flow- -> l -- ^ Goal label- -> l -- ^ Result--class MintTCB t i where- -- |A function that mints new objects (such as instances of- -- 'Priv') in a way that only privileged code should be allowed to- -- do. Because the MintTCB method is only available to- -- priviledged code, other modules imported by unpriviledged code- -- can define instances of mintTCB.- mintTCB :: i -> t---- | Alias for 'mempty'.-noPrivs :: Monoid p => p-noPrivs = mempty-------------------------------------------------------------------------- Labeled IO --------------------------------------------------------------------------------------------------------------------------------- $LIO--- --- The 'LIO' monad is a wrapper around 'IO' that keeps track of the--- current label and clearance. It is possible to raise one's label--- or lower one's clearance without privilege, but moving in the other--- direction requires appropriate privilege. The 'LIO' monad also--- keeps track of a set of privileges, used within a 'withPrivileges'--- block.------ 'LIO' is parameterized by three types. The first is the--- particular label type. The second type is the type of underlying--- privileges. The third type is state specific to and functionally--- determined by the label type. Trusted label implementation code--- can use 'getTCB' and 'putTCB' to get and set the label state.---- | Empty class used to specify the functional dependency between a--- label and it state.-class (Priv l p, Label l) => LabelState l p s | l -> s, l -> p where----- | Internal state of an 'LIO' computation.-data LIOstate l p s = LIOstate { labelState :: s -- ^ label-specific state- , lioL :: l -- ^ current label- , lioC :: l -- ^ current clearance- , lioP :: p -- ^ current privileges- }---- | LIO monad is a State monad transformer with IO as the underlying--- monad.-newtype LIO l p s a = LIO (StateT (LIOstate l p s) IO a)- deriving (Functor, Applicative, Monad)----- | Get internal state.-getTCB :: (LabelState l p s) => LIO l p s (LIOstate l p s)-getTCB = mkLIO $ \s -> return (s, s)---- | Put internal state.-putTCB :: (LabelState l p s) => LIOstate l p s -> LIO l p s ()-putTCB s = mkLIO $ \_ -> return (() , s)---- | Returns label-specific state of the 'LIO' monad. This is the--- data specified as the second argument of 'evalLIO', whose type is--- @s@ in the monad @LIO l s@.-getLabelStateTCB :: (LabelState l p s) => LIO l p s s-getLabelStateTCB = labelState <$> getTCB---- | Sets the label-specific state of the 'LIO' monad. See 'getTCB'.-putLabelStateTCB :: (LabelState l p s) => s -> LIO l p s ()-putLabelStateTCB ls = getTCB >>= putTCB . update- where update s = s { labelState = ls }---- | Generate a fresh state to pass 'runLIO' when invoking it for the--- first time. The current label is set to 'lbot', the current--- clearance is set to 'ltop', and the current privileges are set to--- none.-newState :: (LabelState l p s) => s -> LIOstate l p s-newState s = LIOstate { labelState = s- , lioL = lbot- , lioC = ltop- , lioP = noPrivs }---- | Lift an IO computation into @LIO@.-mkLIO :: (LabelState l p s)- => (LIOstate l p s -> IO (a, LIOstate l p s)) -> LIO l p s a-mkLIO = LIO . StateT---- | Given an LIO computation and state, run it.-unLIO :: (LabelState l p s)- => LIO l p s a -> LIOstate l p s -> IO (a, LIOstate l p s)-unLIO (LIO m) = runStateT m---- | Execute an LIO action. The label on exceptions are removed.--- See 'evalLIO'.-runLIO :: forall l p s a. (LabelState l p s)- => LIO l p s a -> LIOstate l p s -> IO (a, LIOstate l p s)-runLIO m s = unLIO m s `catch` (throwIO . delabel)- where delabel :: LabeledException l -> SomeException- delabel (LabeledExceptionTCB _ e) = e---- | Produces an 'IO' computation that will execute a particular 'LIO'--- computation. Because untrusted code cannot execute 'IO'--- computations, this function should only be useful within trusted--- code. No harm is done from exposing the @evalLIO@ symbol to--- untrusted code. (In general, untrusted code is free to produce--- 'IO' computations--it just can't execute them without access to--- 'ioTCB'.)-evalLIO :: (LabelState l p s) =>- LIO l p s a -- ^ The LIO computation to execute- -> s -- ^ Initial value of label-specific state- -> IO (a, l) -- ^ IO computation that will execute first argument-evalLIO m s = do (a, ls) <- runLIO m (newState s)- return (a, lioL ls)---- | Returns the current value of the thread's label.-getLabel :: (LabelState l p s) => LIO l p s l-getLabel = lioL <$> getTCB---- | Returns the current value of the thread's clearance.-getClearance :: (LabelState l p s) => LIO l p s l-getClearance = lioC <$> getTCB----- | Returns the current privileges.-getPrivileges :: (LabelState l p s) => LIO l p s p-getPrivileges = lioP <$> getTCB---- | Execute an LIO action with a set of underlying privileges. Within--- a @withPrivileges@ block, the supplied privileges are used in every --- even (non @...P@) operation. For instance,------ > unlabelP p x------ can instead be written as:------ > withPrivileges p $ unlabel x------ The original privileges of the thread are restored after--- the action is executed within the @withPrivileges@ block.--- The @withPrivileges@ combinator provides a middle-ground between--- a fully explicit, but safe, privilege use (@...P@ combinators),--- and an implicit, but less safe, interface (provide getter/setter,--- and always use underlying privileges). It allows for the use--- of implicit privileges by explicitly enclosing the code with a--- @withPrivileges@ block.-withPrivileges :: (LabelState l p s) => p -> LIO l p s a -> LIO l p s a-withPrivileges p m = do- p0 <- getPrivileges- setPrivileges p- -- restore privileges even if an exception is thrown- m `finallyTCB` setPrivileges p0---- | Execute an 'LIO' action with the combination of the supplied--- privileges (usually passed to @...P@ functions) and current--- privileges.-withCombinedPrivs :: LabelState l p s => p -> (p -> LIO l p s a) -> LIO l p s a-withCombinedPrivs p0 io = do- p1 <- getPrivileges- io (p0 `mappend` p1)---- | Set the underlying privileges. Although this function is not--- unsafe, it is not exported by "LIO.Safe" as it provides a higher security--- risk. Users are encouraged to use 'withPrivileges'.-setPrivileges :: (LabelState l p s) => p -> LIO l p s ()-setPrivileges p = do s <- getTCB- putTCB $ s { lioP = p }---- | Drop all privileges. It is useful to remove all privileges when--- executing some untrusted code withing a 'withPrivileges' block.-dropPrivileges :: (LabelState l p s) => LIO l p s ()-dropPrivileges = setPrivileges noPrivs---- | If the current label is @oldLabel@ and the current clearance is--- @clearance@, this function allows code to raise the label to--- any value @newLabel@ such that--- @oldLabel ``leq`` newLabel && newLabel ``leq`` clearance@. --- Note that there is no @setLabel@ variant without the @...P@--- because the 'taint' function provides essentially the same--- functionality that @setLabel@ would.-setLabelP :: (LabelState l p s) => p -> l -> LIO l p s ()-setLabelP p' l = withCombinedPrivs p' $ \p -> do- s <- getTCB- if leqp p (lioL s) l- then putTCB s { lioL = l }- else throwIO LerrPriv---- | Set the current label to anything, with no security check.-setLabelTCB :: (LabelState l p s) => l -> LIO l p s ()-setLabelTCB l = do s <- getTCB- if l `leq` lioC s- then putTCB s { lioL = l }- else throwIO LerrClearance---- | Reduce the current clearance. One cannot raise the current label--- or create object with labels higher than the current clearance.-lowerClr :: (LabelState l p s) => l -> LIO l p s ()-lowerClr l = getTCB >>= doit- where doit s | not $ l `leq` lioC s = throwIO LerrClearance- | not $ lioL s `leq` l = throwIO LerrLow- | otherwise = putTCB s { lioC = l }---- | Raise the current clearance (undoing the effects of 'lowerClr').--- This requires privileges.-lowerClrP :: (LabelState l p s) => p -> l -> LIO l p s ()-lowerClrP p' l = withCombinedPrivs p' $ \p -> getTCB >>= doit p- where doit p s | not $ leqp p l $ lioC s = throwIO LerrPriv- | not $ lioL s `leq` l = throwIO LerrLow- | otherwise = putTCB s { lioC = l }---- | Set the current clearance to anything, with no security check.-lowerClrTCB :: (LabelState l p s) => l -> LIO l p s ()-lowerClrTCB l = getTCB >>= doit- where doit s | not $ lioL s `leq` l =- throwIO $ LerrInval ("Cannot lower the current clearance"- ++ " below current label.")- | otherwise = putTCB s { lioC = l }---- | Lowers the clearance of a computation, then restores the--- clearance to its previous value. Useful to wrap around a--- computation if you want to be sure you can catch exceptions thrown--- by it. Also useful to wrap around 'toLabeled' to ensure that the--- computation does not access data exceeding a particular label. If--- @withClearance@ is given a label that can't flow to the current--- clearance, then the clearance is lowered to the greatest lower--- bound of the label supplied and the current clearance.------ Note that if the computation inside @withClearance@ acquires any--- 'Priv's, it may still be able to raise its clearance above the--- supplied argument using 'lowerClrP'.-withClearance :: (LabelState l p s) => l -> LIO l p s a -> LIO l p s a-withClearance l m = do- s <- getTCB- putTCB s { lioC = l `glb` lioC s }- a <- m- putTCB s { lioC = lioC s }- return a---- | 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 'rtioTCB' instead of 'ioTCB'.-ioTCB :: (LabelState l p s) => IO a -> LIO l p s a-ioTCB a = mkLIO $ \s -> do- r <- a- return (r, s)---- | Lifts an 'IO' computation into the 'LIO' monad. If the 'IO'--- computation throws an exception, it labels the exception with the--- current label so that the exception can be caught with 'catch' or--- 'catchP'. This function's name stands for \"re-throw io\".-rtioTCB :: (LabelState l p s) => IO a -> LIO l p s a-rtioTCB io = do- l <- getLabel- ioTCB $ io `catch` (throwIO . LabeledExceptionTCB l)-------------------------------------------------------------------------- Labeled value ------------------------------------------------------------------------------------------------------------------------------ | @Labeled@ is a type representing labeled data. -data Labeled l t = LabeledTCB l t- deriving (Typeable)---- | Returns label of a 'Labeled' type.-labelOf :: Label l => Labeled l a -> l-labelOf (LabeledTCB l _) = l---- | Function to construct a 'Labeled' from a label and pure value. If--- the current label is @lcurrent@ and the current clearance is--- @ccurrent@, then the label @l@ specified must satisfy--- @lcurrent ``leq`` l && l ``leq`` ccurrent@.-label :: (LabelState l p s) => l -> a -> LIO l p s (Labeled l a)-label = labelP noPrivs---- | Constructs a 'Labeled' using privilege to allow the `Labeled`'s label--- to be below the current label. If the current label is @lcurrent@--- and the current clearance is @ccurrent@, then the privilege @p@ and--- label @l@ specified must satisfy--- @(leqp p lcurrent l) && l ``leq`` ccurrent@.--- Note that privilege is not used to bypass the clearance. You must--- use 'lowerClrP' to raise the clearance first if you wish to--- create an 'Labeled' at a higher label than the current clearance.-labelP :: (LabelState l p s) => p -> l -> a -> LIO l p s (Labeled l a)-labelP p' l a = withCombinedPrivs p' $ \p -> getTCB >>= doit p- where doit p s | not $ l `leq` lioC s = throwIO LerrClearance- | not $ leqp p (lioL s) l = throwIO LerrLow- | otherwise = return $ LabeledTCB l a---- | Trusted constructor that creates labeled values.-labelTCB :: Label l => l -> a -> Labeled l a-labelTCB = LabeledTCB---- | Within the 'LIO' monad, this function takes a 'Labeled' and returns--- the value. Thus, in the 'LIO' monad one can say:------ > x <- unlabel (xv :: Labeled SomeLabelType Int)------ And now it is possible to use the value of @x@, which is the pure--- value of what was stored in @xv@. Of course, @unlabel@ also raises--- the current label. If raising the label would exceed the current--- clearance, then @unlabel@ throws 'LerrClearance'.--- However, you can use 'labelOf' to check if 'unlabel' will succeed--- without throwing an exception.-unlabel :: (LabelState l p s) => Labeled l a -> LIO l p s a-unlabel = unlabelP noPrivs---- | Extracts the value of an 'Labeled' just like 'unlabel', but takes a--- privilege argument to minimize the amount the current label must be--- raised. Will still throw 'LerrClearance' under the same--- circumstances as 'unlabel'.-unlabelP :: (LabelState l p s) => p -> Labeled l a -> LIO l p s a-unlabelP p' (LabeledTCB la a) = withCombinedPrivs p' $ \p ->- taintP p la >> return a---- | Extracts the value from an 'Labeled', discarding the label and any--- protection.-unlabelTCB :: Label l => Labeled l a -> a-unlabelTCB (LabeledTCB _ a) = a---- | Raises the label of a 'Labeled' to the 'lub' of it's current label--- and the value supplied. The label supplied must be less than the--- current clearance, though the resulting label may not be if the--- 'Labeled' is already above the current thread's clearance.-taintLabeled :: (LabelState l p s)- => l -> Labeled l a -> LIO l p s (Labeled l a)-taintLabeled l (LabeledTCB la a) = do- aguard l- return $ LabeledTCB (lub l la) a----- | Downgrades the label of a 'Labeled' as much as possible given the--- current privilege.-untaintLabeled :: (LabelState l p s)- => l -> Labeled l a -> LIO l p s (Labeled l a)-untaintLabeled = untaintLabeledP noPrivs---- | Same as 'untaintLabeled' but combines the current privilege with the--- supplied privilege when downgrading the label.-untaintLabeledP :: (LabelState l p s)- => p -> l -> Labeled l a -> LIO l p s (Labeled l a)-untaintLabeledP p target lbld =- relabelP p (lostar p (labelOf lbld) target) lbld---- | Relabeles a @Labeled@ value if the given privilege combined--- with the current privileges permits it.-relabelP :: (LabelState l p s)- => p -> l -> Labeled l a -> LIO l p s (Labeled l a)-relabelP p' lbl (LabeledTCB la a) = withCombinedPrivs p' $ \p ->- if leqp p lbl la && leqp p la lbl- then return $ LabeledTCB lbl a- else throwIO LerrPriv---- | @toLabeled@ is the dual of 'unlabel'. It allows one to invoke--- computations that would raise the current label, but without--- actually raising the label. Instead, the result of the--- computation is packaged into a 'Labeled' with a supplied--- label. (Of couse, the computation executed by @toLabeled@ must--- most observe any data whose label exceeds the supplied label.)------ To get at the result of the computation one will have to call--- 'unlabel' and raise the label, but this can be postponed, or--- done inside some other call to 'toLabeled'. This suggests that--- the provided label must be above the current label and below--- the current clearance.------ Note that @toLabeled@ always restores the clearance to whatever--- it was when it was invoked, regardless of what occurred in the--- computation producing the value of the 'Labeled'. This highlights--- one main use of clearance: to ensure that a @Labeled@ computed--- does not exceed a particular label.------ If an exception is thrown within a @toLabeled@ block, such that--- the outer context is withing a 'catch', which is futher within--- a @toLabeled@ block, infromation can be leaked. Consider the--- following program that uses 'DCLabel's. (Note that 'discard' is--- simply @toLabeled@ that throws throws the result away.)------ > main = evalDC' $ do--- > lRef <- newLIORef lbot ""--- > hRef <- newLIORef ltop "secret" --- > -- brute force:--- > forM_ ["fun", "secret"] $ \guess -> do--- > stash <- readLIORef lRef--- > writeLIORef lRef $ stash ++ "\n" ++ guess ++ ":"--- > discard ltop $ do --- > catch ( discard ltop $ do--- > secret <- readLIORef hRef--- > when (secret == guess) $ throwIO . userError $ "got it!"--- > ) (\(e :: IOError) -> return ())--- > l <- getLabel--- > when (l == lbot) $ do stash <- readLIORef lRef--- > writeLIORef lRef $ stash ++ "no!"--- > readLIORef lRef--- > where evalDC' act = do (r,l) <- evalDC act--- > putStrLn r--- > putStrLn $ "label = " ++ prettyShow l------ The output of the program is:------ > $ ./new--- > --- > fun:no!--- > secret:--- > label = <True , False>------ Note that the current label is 'lbot' (which in DCLabels is--- @<True , False>@), and the secret is leaked. The fundamental issue--- is that the outer 'discard' allows for the current label to remain--- low even though the 'catch' raised the current label when the--- secret was found (and thus exception was throw). As a consequence,--- 'toLabeled' catches all exceptions, and returns a 'Labeled'--- value that may have a labeled exception as wrapped by @throw@.--- All exceptions within the outer computation, including--- IFC violation attempts, are essentially rethrown when performing--- an 'unlabel'.------ DEPRECATED: @toLabeled@ is susceptible to termination attacks.----toLabeled :: (LabelState l p s)- => l -- ^ Label of result and upper bound on- -- inner-computations' observation- -> LIO l p s a -- ^ Inner computation- -> LIO l p s (Labeled l a)-toLabeled = toLabeledP noPrivs-{-# DEPRECATED toLabeled "toLabeled is susceptible to termination attacks" #-}---- | Same as 'toLabeled' but allows one to supply a privilege object--- when comparing the initial and final label of the computation.------ DEPRECATED: @toLabeledP@ is susceptible to termination attacks.----toLabeledP :: (LabelState l p s)- => p -> l -> LIO l p s a -> LIO l p s (Labeled l a)-toLabeledP p' l m = withCombinedPrivs p' $ \p -> do- aguardP p l- save_s <- getTCB- res <- (Right <$> m) `catchTCB` (return . Left . lubErr l)- s <- getTCB- let lastL = lioL s- putTCB s { lioL = lioL save_s, lioC = lioC save_s, lioP = lioP save_s }- return . labelTCB l $- if leqp p lastL l- then either E.throw id res- else let l' = lub lastL l- in E.throw . mkErr . lub l' $ either getELabel (const l') res- where mkErr le = LabeledExceptionTCB le $ toException LerrLow- lubErr lnew (LabeledExceptionTCB le e) =- LabeledExceptionTCB (le `lub` lnew) e- getELabel (LabeledExceptionTCB le _) = le-{-# DEPRECATED toLabeledP "toLabeledP is susceptible to termination attacks" #-}----- | Executes a computation that would raise the current label, but--- discards the result so as to keep the label the same. Used when--- one only cares about the side effects of a computation. For--- instance, if @log_handle@ is an 'LHandle' with a high label, one--- can execute------ @--- discard ltop $ 'hputStrLn' log_handle \"Log message\"--- @------ to create a log message without affecting the current label. ------ DEPRECATED: discard is susceptible to termination attacks.----discard :: (LabelState l p s) => l -> LIO l p s a -> LIO l p s ()-discard = discardP noPrivs-{-# DEPRECATED discard "discard is susceptible to termination attacks" #-}---- | Same as 'discard', but uses privileges when comparing initial and--- final label of the computation.-discardP :: (LabelState l p s) => p -> l -> LIO l p s a -> LIO l p s ()-discardP p l m = void $ toLabeledP p l m-{-# DEPRECATED discardP "discardP is susceptible to termination attacks" #-}------ | 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--instance (Label l, Show a) => ShowTCB (Labeled l a) where- showTCB (LabeledTCB l t) = show t ++ " {" ++ show l ++ "}"---- | It is useful to have the dual of 'ShowTCB', @ReadTCB@, that allows--- for the reading of 'Labeled's that were written using 'showTCB'. Only--- @readTCB@ (corresponding to 'read') and @readsPrecTCB@ (corresponding--- to 'readsPrec') are implemented.-class ReadTCB a where- readsPrecTCB :: Int -> ReadS a- 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"--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 (labelTCB lab val, rest)-------------------------------------------------------------------------- LIO guards -------------------------------------------------------------------------------------------------------------------------------{- $guards-- Guards are used by privileged code to check that the invoking,- unprivileged code has access to particular data. If the current- label is @lcurrent@ and the current clearance is @ccurrent@, then- the following checks should be performed when accessing data- labeled @ldata@:-- * When /reading/ an object labeled @ldata@, it must be the case- that @ldata ``leq`` lcurrent@. This check is performed by the- 'taint' function, so named becuase it \"taints\" the current LIO- context by raising @lcurrent@ until @ldata ``leq`` lcurrent@.- (Specifically, it does this by computing the least upper bound of- the two labels with the 'lub' method of the 'Label' class.)- However, if after doing this it would be the case that- @not (lcurrent ``leq`` ccurrent)@, then 'taint' throws exception- 'LerrClearance' rather than raising the current label.-- * When /writing/ an object, it should be the case that @ldata- ``leq`` lcurrent && lcurrent ``leq`` ldata@. (As stated, this is- the same as saying @ldata == lcurrent@, but the two are different- when using 'leqp' instead of 'leq'.) This is ensured by the- 'wguard' (write guard) function, which does the equivalent of- 'taint' to ensure the target label @ldata@ can flow to the- current label, then throws an exception if @lcurrent@ cannot flow- back to the target label.-- * When /creating/ or /allocating/ objects, it is permissible for- them to be higher than the current label, so long as they are- bellow the current clearance. In other words, it must be the- case that @lcurrent ``leq`` ldata && ldata ``leq`` ccurrent@.- This is ensured by the 'aguard' (allocation guard) function.--The 'taintP', 'wguardP', and 'aguardP' functions are variants of the-above that take privilege to be more permissive and raise the current-label less. ---}---- | General (internal) taint function. Uses @mylub@ instead of--- 'lub', so that privileges can optionally be passed in. Throws--- 'LerrClearance' if raising the current label would exceed the--- current clearance.-gtaint :: (LabelState l p s) =>- (l -> l -> l) -- ^ @mylub@ function- -> l -- ^ @l@ - Label to taint with- -> LIO l p s ()-gtaint mylub l = do- s <- getTCB- let lnew = l `mylub` (lioL s)- if lnew `leq` lioC s- then putTCB s { lioL = lnew }- else ioTCB $ E.throwIO $ LabeledExceptionTCB (lioL s)- (toException LerrClearance)---- |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 ``leq`` l'@, or throw @'LerrClearance'@ if @l'@ would have to be--- higher than the current clearance.-taint :: (LabelState l p s) => l -> LIO l p s ()-taint = gtaint lub---- |Like 'taint', but use privileges to reduce the amount of taint--- required. Note that unlike 'setLabelP', @taintP@ will never lower--- the current label. It simply uses privileges to avoid raising the--- label as high as 'taint' would raise it.-taintP :: (LabelState l p s)- => p -- ^Privileges to invoke- -> l -- ^Label to taint to if no privileges- -> LIO l p s ()-taintP p' l = withCombinedPrivs p' $ \p -> gtaint (lostar p) l---- |Use @wguard l@ in trusted code before modifying an object labeled--- @l@. If @l'@ is the current label, then this function ensures that--- @l' ``leq`` l@ before doing the same thing as @'taint' l@. Throws--- @'LerrHigh'@ if the current label @l'@ is too high.-wguard :: (LabelState l p s) => l -> LIO l p s ()-wguard = wguardP noPrivs---- |Like 'wguard', but takes privilege argument to be more permissive.-wguardP :: (LabelState l p s) => p -> l -> LIO l p s ()-wguardP p' l = withCombinedPrivs p' $ \p -> do- l' <- getLabel- if leqp p l' l- then taintP p l- else throwIO LerrHigh---- |Ensures the label argument is between the current IO label and--- current IO clearance. Use this function in code that allocates--- objects--untrusted code shouldn't be able to create an object--- labeled @l@ unless @aguard l@ does not throw an exception.-aguard :: (LabelState l p s) => l -> LIO l p s ()-aguard = aguardP noPrivs---- | Like 'aguardP', but takes privilege argument to be more permissive.-aguardP :: (LabelState l p s) => p -> l -> LIO l p s ()-aguardP p' newl = withCombinedPrivs p' $ \p -> do- c <- getClearance- l <- getLabel- unless (newl `leq` c) $ throwIO LerrClearance- unless (leqp p l newl) $ throwIO LerrLow--------------------------------------------------------------------------- Labeled Exceptions -----------------------------------------------------------------------------------------------------------------------{- $lexception--LIO throws 'LabelFault' exceptions when an information flow violation-is to occur. In general, such exceptions are handled in the outer,-trusted IO code block that executes untrusted LIO code. However, it is-sometimes desirable for untrusted code to throw exceptions. To this-end we provide an implementation of labeled exceptions, as \"normal\"-exceptions are unsafe and can be used to leak information. We-describe the interface below.---}---- | Violation of information flow conditions, or label checks should--- throw exceptions of type @LabelFault@. The @LerrInval@ constructor--- takes a string parameter -- it is important that trusted code use--- this carefully and aovid leaking information through it.-data LabelFault = LerrLow -- ^ Requested label too low- | LerrHigh -- ^ Current label too high- | LerrClearance -- ^ Label would exceed clearance- | LerrPriv -- ^ Insufficient privileges- | LerrInval String -- ^ Invalid request- deriving Typeable--instance Show LabelFault where- show LerrLow = "LerrLow: Requested label is too low"- show LerrHigh = "LerrHigh: Requested label is too high"- show LerrClearance = "LerrClearance: Label would exceed clearance"- show LerrPriv = "LerrPriv: Insufficient privileges"- show (LerrInval s) = "LerrInval: " ++ s--instance Exception LabelFault---{- $throw-- We must re-define the 'throwIO' and 'catch' functions to work in- the 'LIO' monad. A complication is that exceptions could- potentially leak information. For instance, within a block of code- wrapped by 'discard', one might examine a secret bit, and throw an- exception when the bit is 1 but not 0. Allowing untrusted code to- catch the exception leaks the bit.-- The solution is to wrap exceptions up with a label. The exception- may be caught, but only if the label of the exception can flow to- the label at the point the catch statement began execution. For- compatibility, the 'throwIO', 'catch', and 'onException' functions- are now methods that work in both the 'IO' or 'LIO' monad.-- If an exception is uncaught in the 'LIO' monad, the 'evalLIO'- function will unlabel and re-throw the exception, so that it is- okay to throw exceptions from within the 'LIO' monad and catch them- within the 'IO' monad. (Of course, code in the 'IO' monad must be- careful not to let the 'LIO' code exploit it to exfiltrate- information.)-- Wherever possible, however, code should use the 'catchP' and- 'onExceptionP' variants that use whatever privilege is available to- downgrade the exception. Privileged code that must always run some- cleanup function can use the 'onExceptionTCB' and 'bracketTCB'- functions to run the cleanup code on all exceptions.-- /Note/: Do not use 'throw' (as opposed to 'throwIO') within the- 'LIO' monad. Because 'throw' can be invoked from pure code, it has- no notion of current label and so cannot assign an appropriate- label to the exception. As a result, the exception will not be- catchable within the 'LIO' monad and will propagate all the way out- of the 'evalLIO' function. Similarly, asynchronous exceptions- (such as divide by zero or undefined values in lazily evaluated- expressions) cannot be caught within the 'LIO' monad.---}---- | A labeled exception is simply an exception associated with a label.-data LabeledException l = LabeledExceptionTCB l SomeException- deriving (Typeable)--instance Label l => Show (LabeledException l) where- show (LabeledExceptionTCB l e) = show e ++ " {" ++ show l ++ "}"--instance (Label l) => Exception (LabeledException l)--instance (LabelState l p s) => MonadCatch (LIO l p s) where- mask k = mkLIO $ \s -> E.mask (fun k s)- where fun :: (LabelState l p s)- => ((forall a. LIO l p s a -> LIO l p s a) -> LIO l p s b)- -> LIOstate l p s- -> (forall a. IO a -> IO a) -> IO (b, LIOstate l p s)- -- a little bit of magic:- fun lioMask s = \ioRestore ->- unLIO' s $ lioMask $ \lioRestore -> do- s' <- getTCB- ref <- ioTCB $ newIORef s'- res <- ioTCB $ ioRestore $ do- (a,s'') <- unLIO lioRestore s'- writeIORef ref s''- return a- s'' <- ioTCB $ readIORef ref- putTCB s''- return res- unLIO' s act = unLIO act s- -- | It is not possible to catch pure exceptions from within the 'LIO'- -- monad, but @throwIO@ wraps up an exception with the current label,- -- so that it can be caught with 'catch' or 'catchP'..- throwIO e = do- l <- getLabel- ioTCB $ E.throwIO $ LabeledExceptionTCB l (toException e)-- -- | Basic function for catching labeled exceptions. (The fact that- -- they are labeled is hidden from the handler.)- --- -- > catch = catchP noPrivs- --- catch = catchP noPrivs---- | Catches an exception, so long as the label at the point where the--- exception was thrown can flow to the label at which @catchP@ is--- invoked, modulo the privileges specified. Note that the handler--- raises the current label to the joint of the current label and --- exception label.-catchP :: (Exception e, LabelState l p s)- => p -- ^ Privileges with which to downgrade exception- -> LIO l p s a -- ^ Computation to run- -> (e -> LIO l p s a) -- ^ Exception handler- -> LIO l p s a -- ^ Result of computation or handler-catchP p' io handler = withCombinedPrivs p' $ \p -> do- clr <- getClearance- io `catchTCB` (\e@(LabeledExceptionTCB le se) ->- case fromException se of- Just e' | le `leq` clr -> taintP p le >> handler e'- _ -> throwIO e)---- | Trusted catch functin.-catchTCB :: (LabelState l p s)- => LIO l p s a -- ^ Computation to run- -> (LabeledException l -> LIO l p s a) -- ^ Exception handler- -> LIO l p s a -- ^ Result of computation or handler-catchTCB io handler = do- s <- getTCB- (a, s') <- ioTCB $ do- (unLIO io s) `E.catch` (\e -> unLIO (handler e) s)- putTCB s'- return a---- | Version of 'catchP' with arguments swapped.-handleP :: (Exception e, LabelState l p s)- => p -- ^ Privileges with which to downgrade exception- -> (e -> LIO l p s a) -- ^ Exception handler- -> LIO l p s a -- ^ Computation to run- -> LIO l p s a -- ^ Result of computation or handler-handleP p = flip (catchP p)---- | 'onException' cannot run its handler if the label was raised in--- the computation that threw the exception. This variant allows--- privileges to be supplied, so as to catch exceptions thrown with a--- raised label.-onExceptionP :: (LabelState l p s)- => p -- ^ Privileges to downgrade exception- -> LIO l p s a -- ^ The computation to run- -> LIO l p s b -- ^ Handler to run on exception- -> LIO l p s a -- ^ Result if no exception thrown-onExceptionP p io what = catchP p io- (\e -> what >> throwIO (e :: SomeException))---- | Like standard 'E.bracket', but with privileges to downgrade--- exception.-bracketP :: (LabelState l p s)- => p -- ^ Priviliges used to downgrade- -> LIO l p s a -- ^ Computation to run first- -> (a -> LIO l p s c) -- ^ Computation to run last- -> (a -> LIO l p s b) -- ^ Computation to run in-between- -> LIO l p s b-bracketP p' f l i = withCombinedPrivs p' $ \p ->- genericBracket (onExceptionP p) f l i---- | Forces its argument to be evaluated to weak head normal form when the--- resultant LIO action is executed. This is simply a wrapper for --- "Control.Exception"'s @evaluate@.-evaluate :: (LabelState l p s) => a -> LIO l p s a-evaluate = rtioTCB . E.evaluate---- | For privileged code that needs to catch all exceptions in some--- cleanup function. Note that for the 'LIO' monad, these methods do--- /not/ label the exceptions. It is assumed that you will use--- 'rtioTCB' instead of 'ioTCB' for IO within the computation arguments--- of these methods.-class (MonadCatch m) => OnExceptionTCB m where- onExceptionTCB :: m a -> m b -> m a- finallyTCB :: m a -> m b -> m a- finallyTCB a b = mask $ \restore -> do- r <- restore a `onExceptionTCB` b- _ <- b- return r- bracketTCB :: m a -> (a -> m c) -> (a -> m b) -> m b- bracketTCB = genericBracket onExceptionTCB--instance OnExceptionTCB IO where- onExceptionTCB = E.onException- bracketTCB = E.bracket--instance (LabelState l p s) => OnExceptionTCB (LIO l p s) where- onExceptionTCB io cleanup = io `catchTCB` (\e -> do void cleanup - ioTCB $ E.throwIO e)--instance (LabelState l p s) => MonadError IOException (LIO l p s) where- throwError = throwIO- catchError = catch-------------------------------------------------------------------------- Gates ------------------------------------------------------------------------------------------------------------------------------------{- $gates--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 'mkGate' to create a gate data type 'Gate l d-a'. The type parameter @l@ indicates the 'Label' type used to protect-the gate computation; the label value @ is bounded by the current-label and clearance at the time of creation. The gate computation-itself is of type @d -> a@, where @d@, an instance of 'PrivDesc'.-Since gates are invoked with 'callGate', the service provider has the-guarantee that the client (the callee) 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 when-determining the result.--A client invokes a gate with 'callGate', which unlabels the gate-computation and applies the gate computation to the description of the-supplied privilege.---}---- | Class used to describe privileges in a meaningful manner.-class (PrivTCB p, Show d) => PrivDesc p d | p -> d where- -- | Get privilege description- privDesc :: p -> d---- | A Gate is a wrapper for a 'Labeled' type.-newtype Gate l d a = Gate (Labeled l (d -> a))- deriving (Typeable)---- | Create a gate given a gate label and computation.--- The label of the gate must be bounded by the current label and--- clearance.-mkGate :: (LabelState l p s, PrivDesc p d)- => l -- ^ Label of gate- -> (d -> a) -- ^ Gate omputation- -> LIO l p s (Gate l d a)-mkGate = mkGateP noPrivs---- | Same as 'mkGate', but uses privileges when making the gate.-mkGateP :: (LabelState l p s, PrivDesc p d)- => p -- ^ Privileges- -> l -- ^ Label of gate- -> (d -> a) -- ^ Gate computation- -> LIO l p s (Gate l d a)-mkGateP p l f = Gate <$> labelP p l f---- | Same as 'mkGate', but ignores IFC.-mkGateTCB :: (LabelState l p s, PrivDesc p d)- => l -- ^ Label of gate- -> (d -> a) -- ^ Gate action- -> Gate l d a-mkGateTCB l f = Gate (labelTCB l f)---- | Given a labeled gate and privilege, execute the gate computation.--- The current label is raised to the join of the gate and current--- label, clearance permitting. It is important to note that--- @callGate@ invokes the gate computation with the privilege--- description and /not/ the actual privilege.-callGate :: (LabelState l p s, PrivDesc p d)- => Gate l d a -- ^ Gate- -> p -- ^ Privilege used to unlabel gate action- -> LIO l p s a-callGate (Gate lf) p = withCombinedPrivs p $ \pAmp -> do- f <- unlabelP pAmp lf- return $ f (privDesc p)---- | Same as 'callGate', but does not raise label in accessing the--- gate computation.-callGateTCB :: (LabelState l p s, PrivDesc p d)- => Gate l d a -- ^ Gate- -> p -- ^ Privilege used to unlabel gate action- -> a-callGateTCB (Gate lf) p = unlabelTCB lf $ privDesc p+{-# LANGUAGE Unsafe #-}+{-# LANGUAGE GeneralizedNewtypeDeriving,+ MultiParamTypeClasses,+ FunctionalDependencies,+ FlexibleInstances,+ DeriveDataTypeable #-}++{- | ++This module exports ++* The definition of the 'LIO' monad and relevant trusted state+ access/modifying functions.++* Labeled exceptions that are use throughout 'LIO' and low-level,+ /unsafe/, throw and catch primitives.++* Combinators for executing 'IO' actions.++The documentation and external, safe 'LIO' interface is provided in+"LIO.Core".++-}++module LIO.TCB (+ -- * LIO monad+ LIO(..), MonadLIO(..)+ -- ** Internal state+ , LIOState(..)+ , getLIOStateTCB, putLIOStateTCB, updateLIOStateTCB + -- * Exceptions+ , LabeledException(..)+ -- ** Throw and catch+ , unlabeledThrowTCB, catchTCB+ -- * Executing IO actions+ , ioTCB, rethrowIoTCB+ -- * Trusted 'Show' and 'Read'+ , ShowTCB(..), ReadTCB(..)+ ) where++import Data.Typeable++import Control.Applicative+import Control.Monad+import Control.Monad.Trans.State.Strict+import Control.Monad.Trans.Class (lift)+import Control.Monad.IO.Class (liftIO)+import Control.Exception (Exception, SomeException)+import qualified Control.Exception as E++import Text.Read (minPrec)++import LIO.Label++--+-- LIO Monad+--++-- | Internal state of an 'LIO' computation.+data LIOState l = LIOState { lioLabel :: !l -- ^ Current label.+ , 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 :: StateT (LIOState l) IO a }+ deriving (Functor, Applicative, Monad)++--+-- Monad base+--++-- | Synonym for monad in which 'LIO' is the base monad.+class (Monad m, Label l) => MonadLIO l m | m -> l where+ -- | Lift an 'LIO' computation.+ liftLIO :: LIO l a -> m a++instance Label l => MonadLIO l (LIO l) where+ liftLIO = id++--+-- Internal state+--++-- | Get internal state. This function is not actually unsafe, but+-- to avoid future security bugs we leave all direct access to the+-- internal state to trusted code.+getLIOStateTCB :: Label l => LIO l (LIOState l)+getLIOStateTCB = LIOTCB . StateT $! \s -> return (s, s)++-- | Set internal state.+putLIOStateTCB :: Label l => LIOState l -> LIO l ()+putLIOStateTCB s = LIOTCB . StateT $! \_ -> return ((), s)++-- | Update the internal state given some function.+updateLIOStateTCB :: Label l => (LIOState l -> LIOState l) -> LIO l ()+updateLIOStateTCB f = do+ s <- getLIOStateTCB+ putLIOStateTCB $! f s+++--+-- Exceptions+--++-- | A labeled exception is simply an exception associated with a label.+data LabeledException l = LabeledExceptionTCB !l SomeException+ deriving (Show, Typeable)++instance Label l => Exception (LabeledException l)++-- | Throw an arbitrary exception. Note that the exception being+-- thrown is not labeled.+unlabeledThrowTCB :: (Exception e, Label l) => e -> LIO l a+unlabeledThrowTCB = LIOTCB . liftIO . E.throwIO++-- | Catch an exception. Note that all exceptions thrown by LIO are+-- labeled and thus this trusted function can be used to handle any+-- exception. Note that the label of the exception must be considered+-- in the handler (i.e., handler must raise the current label) to+-- preserve security.+catchTCB :: Label l+ => LIO l a+ -> (LabeledException l -> LIO l a)+ -> LIO l a+catchTCB act handler = do+ s0 <- getLIOStateTCB+ (res, s1) <- ioTCB $! toIO act s0 `E.catch` ioHandler s0+ putLIOStateTCB s1+ return res+ where toIO io = runStateT (unLIOTCB io)+ ioHandler s e = toIO (handler e) s++--+-- 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 'rtioTCB' exported by "LIO.Exception.TCB" instead of 'ioTCB'.+ioTCB :: Label l => IO a -> LIO l a+ioTCB = LIOTCB . lift++-- | Lifts an 'IO' computation into the 'LIO' monad. If the 'IO'+-- computation throws an exception, it labels the exception with the+-- current label so that the exception can be caught with 'catchLIO'.+rethrowIoTCB :: Label l => IO a -> LIO l a+rethrowIoTCB io = do+ l <- lioLabel `liftM` getLIOStateTCB+ ioTCB $ io `E.catch` (E.throwIO . LabeledExceptionTCB l)+++--+-- Trusted 'Show' and 'Read'+--++-- | 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++-- | 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"
− System/Posix/Tmp.hsc
@@ -1,112 +0,0 @@-{-# LANGUAGE ForeignFunctionInterface #-}-#if __GLASGOW_HASKELL__ >= 701-{-# LANGUAGE Trustworthy #-}-#endif--------------------------------------------------------------------------------- |--- Module : System.Posix.Tmp--- Copyright : (c) Volker Stolz <vs@foldr.org>--- Deian Stefan <deian@cs.stanford.edu>--- This is a copy of the /latest/ @System.Posix.Temp@ module. Because--- @Temp@ will not be made available until GHC 7.6, we are including--- the core functions here.-----------------------------------------------------------------------------------module System.Posix.Tmp (- mkstemp, mkstemps, mkdtemp- ) where--#include "HsTmp.h"--import Foreign.C-import System.IO-import System.Posix.IO-import System.Posix.Types--#if __GLASGOW_HASKELL__ > 700-import System.Posix.Internals (withFilePath, peekFilePath)--#elif __GLASGOW_HASKELL__ > 611-import System.Posix.Internals (withFilePath)--peekFilePath :: CString -> IO FilePath-peekFilePath = peekCString--#else-withFilePath :: FilePath -> (CString -> IO a) -> IO a-withFilePath = withCString--peekFilePath :: CString -> IO FilePath-peekFilePath = peekCString-#endif--#if HAVE_MKSTEMP-foreign import ccall unsafe "HsUnix.h __hstmp_mkstemp"- c_mkstemp :: CString -> IO CInt-#endif---- | Make a unique filename and open it for reading\/writing. The returned--- 'FilePath' is the (possibly relative) path of the created file, which is--- padded with 6 random characters. The argument is the desired prefix of the--- filepath of the temporary file to be created.-mkstemp :: String -> IO (FilePath, Handle)-#if HAVE_MKSTEMP-mkstemp template' = do- let template = template' ++ "XXXXXX"- withFilePath template $ \ ptr -> do- fd <- throwErrnoIfMinus1 "mkstemp" (c_mkstemp ptr)- name <- peekFilePath ptr- h <- fdToHandle (Fd fd)- return (name, h)-#else-mkstemp = error "System.Posix.Temp.mkstemp: not supported"-#endif--#if HAVE_MKSTEMPS-foreign import ccall unsafe "HsUnix.h __hstmp_mkstemps"- c_mkstemps :: CString -> CInt -> IO CInt-#endif---- | Make a unique filename with a given prefix and suffix and open it for--- reading\/writing. The returned 'FilePath' is the (possibly relative) path of--- the created file, which contains 6 random characters in between the prefix--- and suffix. The first argument is the desired prefix of the filepath of the--- temporary file to be created. The second argument is the suffix of the--- temporary file to be created.------ If you are using as system that doesn't support the mkstemps glibc function--- (supported in glibc > 2.11) then this function simply throws an error.-mkstemps :: String -> String -> IO (FilePath, Handle)-#if HAVE_MKSTEMPS-mkstemps prefix suffix = do- let template = prefix ++ "XXXXXX" ++ suffix- lenOfsuf = (fromIntegral $ length suffix) :: CInt- withFilePath template $ \ ptr -> do- fd <- throwErrnoIfMinus1 "mkstemps" (c_mkstemps ptr lenOfsuf)- name <- peekFilePath ptr- h <- fdToHandle (Fd fd)- return (name, h)-#else-mkstemps = error "System.Posix.Temp.mkstemps: not supported"-#endif--#if HAVE_MKDTEMP-foreign import ccall unsafe "HsUnix.h __hstmp_mkdtemp"- c_mkdtemp :: CString -> IO CString-#endif---- | Make a unique directory. The returned 'FilePath' is the path of the--- created directory, which is padded with 6 random characters. The argument is--- the desired prefix of the filepath of the temporary directory to be created.-mkdtemp :: String -> IO FilePath-mkdtemp template' = do-#if HAVE_MKDTEMP- let template = template' ++ "XXXXXX"- withFilePath template $ \ ptr -> do- _ <- throwErrnoIfNull "mkdtemp" (c_mkdtemp ptr)- name <- peekFilePath ptr- return name-#else-mkdtemp = error "System.Posix.Temp.mkdtemp: not supported"-#endif
− cbits/HsTmp.c
@@ -1,19 +0,0 @@-#include "HsTmp.h"--#if HAVE_MKSTEMP-int __hstmp_mkstemp(char *filetemplate) {- return (mkstemp(filetemplate));-}-#endif--#if HAVE_MKSTEMPS-int __hstmp_mkstemps(char *filetemplate, int suffixlen) {- return (mkstemps(filetemplate, suffixlen));-}-#endif--#if HAVE_MKDTEMP-char *__hstmp_mkdtemp(char *filetemplate) {- return (mkdtemp(filetemplate));-}-#endif
− configure
@@ -1,3666 +0,0 @@-#! /bin/sh-# Guess values for system-dependent variables and create Makefiles.-# Generated by GNU Autoconf 2.68 for Labeled IO library 0.1.0.-#-# Report bugs to <deian@cs.stanford.edu>.-#-#-# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001,-# 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 Free Software-# Foundation, Inc.-#-#-# This configure script is free software; the Free Software Foundation-# gives unlimited permission to copy, distribute and modify it.-## -------------------- ##-## M4sh Initialization. ##-## -------------------- ##--# Be more Bourne compatible-DUALCASE=1; export DUALCASE # for MKS sh-if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then :- emulate sh- NULLCMD=:- # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which- # is contrary to our usage. Disable this feature.- alias -g '${1+"$@"}'='"$@"'- setopt NO_GLOB_SUBST-else- case `(set -o) 2>/dev/null` in #(- *posix*) :- set -o posix ;; #(- *) :- ;;-esac-fi---as_nl='-'-export as_nl-# Printing a long string crashes Solaris 7 /usr/bin/printf.-as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'-as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo-as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo-# Prefer a ksh shell builtin over an external printf program on Solaris,-# but without wasting forks for bash or zsh.-if test -z "$BASH_VERSION$ZSH_VERSION" \- && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then- as_echo='print -r --'- as_echo_n='print -rn --'-elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then- as_echo='printf %s\n'- as_echo_n='printf %s'-else- if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then- as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"'- as_echo_n='/usr/ucb/echo -n'- else- as_echo_body='eval expr "X$1" : "X\\(.*\\)"'- as_echo_n_body='eval- arg=$1;- case $arg in #(- *"$as_nl"*)- expr "X$arg" : "X\\(.*\\)$as_nl";- arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;;- esac;- expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl"- '- export as_echo_n_body- as_echo_n='sh -c $as_echo_n_body as_echo'- fi- export as_echo_body- as_echo='sh -c $as_echo_body as_echo'-fi--# The user is always right.-if test "${PATH_SEPARATOR+set}" != set; then- PATH_SEPARATOR=:- (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && {- (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 ||- PATH_SEPARATOR=';'- }-fi---# IFS-# We need space, tab and new line, in precisely that order. Quoting is-# there to prevent editors from complaining about space-tab.-# (If _AS_PATH_WALK were called with IFS unset, it would disable word-# splitting by setting IFS to empty value.)-IFS=" "" $as_nl"--# Find who we are. Look in the path if we contain no directory separator.-as_myself=-case $0 in #((- *[\\/]* ) as_myself=$0 ;;- *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR-for as_dir in $PATH-do- IFS=$as_save_IFS- test -z "$as_dir" && as_dir=.- test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break- done-IFS=$as_save_IFS-- ;;-esac-# We did not find ourselves, most probably we were run as `sh COMMAND'-# in which case we are not to be found in the path.-if test "x$as_myself" = x; then- as_myself=$0-fi-if test ! -f "$as_myself"; then- $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2- exit 1-fi--# Unset variables that we do not need and which cause bugs (e.g. in-# pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1"-# suppresses any "Segmentation fault" message there. '((' could-# trigger a bug in pdksh 5.2.14.-for as_var in BASH_ENV ENV MAIL MAILPATH-do eval test x\${$as_var+set} = xset \- && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || :-done-PS1='$ '-PS2='> '-PS4='+ '--# NLS nuisances.-LC_ALL=C-export LC_ALL-LANGUAGE=C-export LANGUAGE--# CDPATH.-(unset CDPATH) >/dev/null 2>&1 && unset CDPATH--if test "x$CONFIG_SHELL" = x; then- as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then :- emulate sh- NULLCMD=:- # Pre-4.2 versions of Zsh do word splitting on \${1+\"\$@\"}, which- # is contrary to our usage. Disable this feature.- alias -g '\${1+\"\$@\"}'='\"\$@\"'- setopt NO_GLOB_SUBST-else- case \`(set -o) 2>/dev/null\` in #(- *posix*) :- set -o posix ;; #(- *) :- ;;-esac-fi-"- as_required="as_fn_return () { (exit \$1); }-as_fn_success () { as_fn_return 0; }-as_fn_failure () { as_fn_return 1; }-as_fn_ret_success () { return 0; }-as_fn_ret_failure () { return 1; }--exitcode=0-as_fn_success || { exitcode=1; echo as_fn_success failed.; }-as_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; }-as_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; }-as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; }-if ( set x; as_fn_ret_success y && test x = \"\$1\" ); then :--else- exitcode=1; echo positional parameters were not saved.-fi-test x\$exitcode = x0 || exit 1"- as_suggested=" as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO- as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO- eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" &&- test \"x\`expr \$as_lineno_1'\$as_run' + 1\`\" = \"x\$as_lineno_2'\$as_run'\"' || exit 1"- if (eval "$as_required") 2>/dev/null; then :- as_have_required=yes-else- as_have_required=no-fi- if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null; then :--else- as_save_IFS=$IFS; IFS=$PATH_SEPARATOR-as_found=false-for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH-do- IFS=$as_save_IFS- test -z "$as_dir" && as_dir=.- as_found=:- case $as_dir in #(- /*)- for as_base in sh bash ksh sh5; do- # Try only shells that exist, to save several forks.- as_shell=$as_dir/$as_base- if { test -f "$as_shell" || test -f "$as_shell.exe"; } &&- { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$as_shell"; } 2>/dev/null; then :- CONFIG_SHELL=$as_shell as_have_required=yes- if { $as_echo "$as_bourne_compatible""$as_suggested" | as_run=a "$as_shell"; } 2>/dev/null; then :- break 2-fi-fi- done;;- esac- as_found=false-done-$as_found || { if { test -f "$SHELL" || test -f "$SHELL.exe"; } &&- { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$SHELL"; } 2>/dev/null; then :- CONFIG_SHELL=$SHELL as_have_required=yes-fi; }-IFS=$as_save_IFS--- if test "x$CONFIG_SHELL" != x; then :- # We cannot yet assume a decent shell, so we have to provide a- # neutralization value for shells without unset; and this also- # works around shells that cannot unset nonexistent variables.- # Preserve -v and -x to the replacement shell.- BASH_ENV=/dev/null- ENV=/dev/null- (unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV- export CONFIG_SHELL- case $- in # ((((- *v*x* | *x*v* ) as_opts=-vx ;;- *v* ) as_opts=-v ;;- *x* ) as_opts=-x ;;- * ) as_opts= ;;- esac- exec "$CONFIG_SHELL" $as_opts "$as_myself" ${1+"$@"}-fi-- if test x$as_have_required = xno; then :- $as_echo "$0: This script requires a shell more modern than all"- $as_echo "$0: the shells that I found on your system."- if test x${ZSH_VERSION+set} = xset ; then- $as_echo "$0: In particular, zsh $ZSH_VERSION has bugs and should"- $as_echo "$0: be upgraded to zsh 4.3.4 or later."- else- $as_echo "$0: Please tell bug-autoconf@gnu.org and-$0: deian@cs.stanford.edu about your system, including any-$0: error possibly output before this message. Then install-$0: a modern shell, or manually run the script under such a-$0: shell if you do have one."- fi- exit 1-fi-fi-fi-SHELL=${CONFIG_SHELL-/bin/sh}-export SHELL-# Unset more variables known to interfere with behavior of common tools.-CLICOLOR_FORCE= GREP_OPTIONS=-unset CLICOLOR_FORCE GREP_OPTIONS--## --------------------- ##-## M4sh Shell Functions. ##-## --------------------- ##-# as_fn_unset VAR-# ----------------# Portably unset VAR.-as_fn_unset ()-{- { eval $1=; unset $1;}-}-as_unset=as_fn_unset--# as_fn_set_status STATUS-# ------------------------# Set $? to STATUS, without forking.-as_fn_set_status ()-{- return $1-} # as_fn_set_status--# as_fn_exit STATUS-# ------------------# Exit the shell with STATUS, even in a "trap 0" or "set -e" context.-as_fn_exit ()-{- set +e- as_fn_set_status $1- exit $1-} # as_fn_exit--# as_fn_mkdir_p-# --------------# Create "$as_dir" as a directory, including parents if necessary.-as_fn_mkdir_p ()-{-- case $as_dir in #(- -*) as_dir=./$as_dir;;- esac- test -d "$as_dir" || eval $as_mkdir_p || {- as_dirs=- while :; do- case $as_dir in #(- *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'(- *) as_qdir=$as_dir;;- esac- as_dirs="'$as_qdir' $as_dirs"- as_dir=`$as_dirname -- "$as_dir" ||-$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \- X"$as_dir" : 'X\(//\)[^/]' \| \- X"$as_dir" : 'X\(//\)$' \| \- X"$as_dir" : 'X\(/\)' \| . 2>/dev/null ||-$as_echo X"$as_dir" |- sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{- s//\1/- q- }- /^X\(\/\/\)[^/].*/{- s//\1/- q- }- /^X\(\/\/\)$/{- s//\1/- q- }- /^X\(\/\).*/{- s//\1/- q- }- s/.*/./; q'`- test -d "$as_dir" && break- done- test -z "$as_dirs" || eval "mkdir $as_dirs"- } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir"---} # as_fn_mkdir_p-# as_fn_append VAR VALUE-# -----------------------# Append the text in VALUE to the end of the definition contained in VAR. Take-# advantage of any shell optimizations that allow amortized linear growth over-# repeated appends, instead of the typical quadratic growth present in naive-# implementations.-if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then :- eval 'as_fn_append ()- {- eval $1+=\$2- }'-else- as_fn_append ()- {- eval $1=\$$1\$2- }-fi # as_fn_append--# as_fn_arith ARG...-# -------------------# Perform arithmetic evaluation on the ARGs, and store the result in the-# global $as_val. Take advantage of shells that can avoid forks. The arguments-# must be portable across $(()) and expr.-if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then :- eval 'as_fn_arith ()- {- as_val=$(( $* ))- }'-else- as_fn_arith ()- {- as_val=`expr "$@" || test $? -eq 1`- }-fi # as_fn_arith---# as_fn_error STATUS ERROR [LINENO LOG_FD]-# -----------------------------------------# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are-# provided, also output the error to LOG_FD, referencing LINENO. Then exit the-# script with STATUS, using 1 if that was 0.-as_fn_error ()-{- as_status=$1; test $as_status -eq 0 && as_status=1- if test "$4"; then- as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack- $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4- fi- $as_echo "$as_me: error: $2" >&2- as_fn_exit $as_status-} # as_fn_error--if expr a : '\(a\)' >/dev/null 2>&1 &&- test "X`expr 00001 : '.*\(...\)'`" = X001; then- as_expr=expr-else- as_expr=false-fi--if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then- as_basename=basename-else- as_basename=false-fi--if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then- as_dirname=dirname-else- as_dirname=false-fi--as_me=`$as_basename -- "$0" ||-$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \- X"$0" : 'X\(//\)$' \| \- X"$0" : 'X\(/\)' \| . 2>/dev/null ||-$as_echo X/"$0" |- sed '/^.*\/\([^/][^/]*\)\/*$/{- s//\1/- q- }- /^X\/\(\/\/\)$/{- s//\1/- q- }- /^X\/\(\/\).*/{- s//\1/- q- }- s/.*/./; q'`--# Avoid depending upon Character Ranges.-as_cr_letters='abcdefghijklmnopqrstuvwxyz'-as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'-as_cr_Letters=$as_cr_letters$as_cr_LETTERS-as_cr_digits='0123456789'-as_cr_alnum=$as_cr_Letters$as_cr_digits--- as_lineno_1=$LINENO as_lineno_1a=$LINENO- as_lineno_2=$LINENO as_lineno_2a=$LINENO- eval 'test "x$as_lineno_1'$as_run'" != "x$as_lineno_2'$as_run'" &&- test "x`expr $as_lineno_1'$as_run' + 1`" = "x$as_lineno_2'$as_run'"' || {- # Blame Lee E. McMahon (1931-1989) for sed's syntax. :-)- sed -n '- p- /[$]LINENO/=- ' <$as_myself |- sed '- s/[$]LINENO.*/&-/- t lineno- b- :lineno- N- :loop- s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/- t loop- s/-\n.*//- ' >$as_me.lineno &&- chmod +x "$as_me.lineno" ||- { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; }-- # Don't try to exec as it changes $[0], causing all sort of problems- # (the dirname of $[0] is not the place where we might find the- # original and so on. Autoconf is especially sensitive to this).- . "./$as_me.lineno"- # Exit status is that of the last command.- exit-}--ECHO_C= ECHO_N= ECHO_T=-case `echo -n x` in #(((((--n*)- case `echo 'xy\c'` in- *c*) ECHO_T=' ';; # ECHO_T is single tab character.- xy) ECHO_C='\c';;- *) echo `echo ksh88 bug on AIX 6.1` > /dev/null- ECHO_T=' ';;- esac;;-*)- ECHO_N='-n';;-esac--rm -f conf$$ conf$$.exe conf$$.file-if test -d conf$$.dir; then- rm -f conf$$.dir/conf$$.file-else- rm -f conf$$.dir- mkdir conf$$.dir 2>/dev/null-fi-if (echo >conf$$.file) 2>/dev/null; then- if ln -s conf$$.file conf$$ 2>/dev/null; then- as_ln_s='ln -s'- # ... but there are two gotchas:- # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail.- # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable.- # In both cases, we have to default to `cp -p'.- ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe ||- as_ln_s='cp -p'- elif ln conf$$.file conf$$ 2>/dev/null; then- as_ln_s=ln- else- as_ln_s='cp -p'- fi-else- as_ln_s='cp -p'-fi-rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file-rmdir conf$$.dir 2>/dev/null--if mkdir -p . 2>/dev/null; then- as_mkdir_p='mkdir -p "$as_dir"'-else- test -d ./-p && rmdir ./-p- as_mkdir_p=false-fi--if test -x / >/dev/null 2>&1; then- as_test_x='test -x'-else- if ls -dL / >/dev/null 2>&1; then- as_ls_L_option=L- else- as_ls_L_option=- fi- as_test_x='- eval sh -c '\''- if test -d "$1"; then- test -d "$1/.";- else- case $1 in #(- -*)set "./$1";;- esac;- case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in #((- ???[sx]*):;;*)false;;esac;fi- '\'' sh- '-fi-as_executable_p=$as_test_x--# Sed expression to map a string onto a valid CPP name.-as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"--# Sed expression to map a string onto a valid variable name.-as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"---test -n "$DJDIR" || exec 7<&0 </dev/null-exec 6>&1--# Name of the host.-# hostname on some systems (SVR3.2, old GNU/Linux) returns a bogus exit status,-# so uname gets run too.-ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q`--#-# Initializations.-#-ac_default_prefix=/usr/local-ac_clean_files=-ac_config_libobj_dir=.-LIBOBJS=-cross_compiling=no-subdirs=-MFLAGS=-MAKEFLAGS=--# Identity of this package.-PACKAGE_NAME='Labeled IO library'-PACKAGE_TARNAME='lio'-PACKAGE_VERSION='0.1.0'-PACKAGE_STRING='Labeled IO library 0.1.0'-PACKAGE_BUGREPORT='deian@cs.stanford.edu'-PACKAGE_URL=''--ac_unique_file="include/HsTmp.h"-ac_subst_vars='LTLIBOBJS-LIBOBJS-OBJEXT-EXEEXT-ac_ct_CC-CPPFLAGS-LDFLAGS-CFLAGS-CC-target_alias-host_alias-build_alias-LIBS-ECHO_T-ECHO_N-ECHO_C-DEFS-mandir-localedir-libdir-psdir-pdfdir-dvidir-htmldir-infodir-docdir-oldincludedir-includedir-localstatedir-sharedstatedir-sysconfdir-datadir-datarootdir-libexecdir-sbindir-bindir-program_transform_name-prefix-exec_prefix-PACKAGE_URL-PACKAGE_BUGREPORT-PACKAGE_STRING-PACKAGE_VERSION-PACKAGE_TARNAME-PACKAGE_NAME-PATH_SEPARATOR-SHELL'-ac_subst_files=''-ac_user_opts='-enable_option_checking-'- ac_precious_vars='build_alias-host_alias-target_alias-CC-CFLAGS-LDFLAGS-LIBS-CPPFLAGS'---# Initialize some variables set by options.-ac_init_help=-ac_init_version=false-ac_unrecognized_opts=-ac_unrecognized_sep=-# The variables have the same names as the options, with-# dashes changed to underlines.-cache_file=/dev/null-exec_prefix=NONE-no_create=-no_recursion=-prefix=NONE-program_prefix=NONE-program_suffix=NONE-program_transform_name=s,x,x,-silent=-site=-srcdir=-verbose=-x_includes=NONE-x_libraries=NONE--# Installation directory options.-# These are left unexpanded so users can "make install exec_prefix=/foo"-# and all the variables that are supposed to be based on exec_prefix-# by default will actually change.-# Use braces instead of parens because sh, perl, etc. also accept them.-# (The list follows the same order as the GNU Coding Standards.)-bindir='${exec_prefix}/bin'-sbindir='${exec_prefix}/sbin'-libexecdir='${exec_prefix}/libexec'-datarootdir='${prefix}/share'-datadir='${datarootdir}'-sysconfdir='${prefix}/etc'-sharedstatedir='${prefix}/com'-localstatedir='${prefix}/var'-includedir='${prefix}/include'-oldincludedir='/usr/include'-docdir='${datarootdir}/doc/${PACKAGE_TARNAME}'-infodir='${datarootdir}/info'-htmldir='${docdir}'-dvidir='${docdir}'-pdfdir='${docdir}'-psdir='${docdir}'-libdir='${exec_prefix}/lib'-localedir='${datarootdir}/locale'-mandir='${datarootdir}/man'--ac_prev=-ac_dashdash=-for ac_option-do- # If the previous option needs an argument, assign it.- if test -n "$ac_prev"; then- eval $ac_prev=\$ac_option- ac_prev=- continue- fi-- case $ac_option in- *=?*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;;- *=) ac_optarg= ;;- *) ac_optarg=yes ;;- esac-- # Accept the important Cygnus configure options, so we can diagnose typos.-- case $ac_dashdash$ac_option in- --)- ac_dashdash=yes ;;-- -bindir | --bindir | --bindi | --bind | --bin | --bi)- ac_prev=bindir ;;- -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*)- bindir=$ac_optarg ;;-- -build | --build | --buil | --bui | --bu)- ac_prev=build_alias ;;- -build=* | --build=* | --buil=* | --bui=* | --bu=*)- build_alias=$ac_optarg ;;-- -cache-file | --cache-file | --cache-fil | --cache-fi \- | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c)- ac_prev=cache_file ;;- -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \- | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*)- cache_file=$ac_optarg ;;-- --config-cache | -C)- cache_file=config.cache ;;-- -datadir | --datadir | --datadi | --datad)- ac_prev=datadir ;;- -datadir=* | --datadir=* | --datadi=* | --datad=*)- datadir=$ac_optarg ;;-- -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \- | --dataroo | --dataro | --datar)- ac_prev=datarootdir ;;- -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \- | --dataroot=* | --dataroo=* | --dataro=* | --datar=*)- datarootdir=$ac_optarg ;;-- -disable-* | --disable-*)- ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'`- # Reject names that are not valid shell variable names.- expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&- as_fn_error $? "invalid feature name: $ac_useropt"- ac_useropt_orig=$ac_useropt- ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`- case $ac_user_opts in- *"-"enable_$ac_useropt"-"*) ;;- *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig"- ac_unrecognized_sep=', ';;- esac- eval enable_$ac_useropt=no ;;-- -docdir | --docdir | --docdi | --doc | --do)- ac_prev=docdir ;;- -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*)- docdir=$ac_optarg ;;-- -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv)- ac_prev=dvidir ;;- -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*)- dvidir=$ac_optarg ;;-- -enable-* | --enable-*)- ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'`- # Reject names that are not valid shell variable names.- expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&- as_fn_error $? "invalid feature name: $ac_useropt"- ac_useropt_orig=$ac_useropt- ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`- case $ac_user_opts in- *"-"enable_$ac_useropt"-"*) ;;- *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig"- ac_unrecognized_sep=', ';;- esac- eval enable_$ac_useropt=\$ac_optarg ;;-- -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \- | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \- | --exec | --exe | --ex)- ac_prev=exec_prefix ;;- -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \- | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \- | --exec=* | --exe=* | --ex=*)- exec_prefix=$ac_optarg ;;-- -gas | --gas | --ga | --g)- # Obsolete; use --with-gas.- with_gas=yes ;;-- -help | --help | --hel | --he | -h)- ac_init_help=long ;;- -help=r* | --help=r* | --hel=r* | --he=r* | -hr*)- ac_init_help=recursive ;;- -help=s* | --help=s* | --hel=s* | --he=s* | -hs*)- ac_init_help=short ;;-- -host | --host | --hos | --ho)- ac_prev=host_alias ;;- -host=* | --host=* | --hos=* | --ho=*)- host_alias=$ac_optarg ;;-- -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht)- ac_prev=htmldir ;;- -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \- | --ht=*)- htmldir=$ac_optarg ;;-- -includedir | --includedir | --includedi | --included | --include \- | --includ | --inclu | --incl | --inc)- ac_prev=includedir ;;- -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \- | --includ=* | --inclu=* | --incl=* | --inc=*)- includedir=$ac_optarg ;;-- -infodir | --infodir | --infodi | --infod | --info | --inf)- ac_prev=infodir ;;- -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*)- infodir=$ac_optarg ;;-- -libdir | --libdir | --libdi | --libd)- ac_prev=libdir ;;- -libdir=* | --libdir=* | --libdi=* | --libd=*)- libdir=$ac_optarg ;;-- -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \- | --libexe | --libex | --libe)- ac_prev=libexecdir ;;- -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \- | --libexe=* | --libex=* | --libe=*)- libexecdir=$ac_optarg ;;-- -localedir | --localedir | --localedi | --localed | --locale)- ac_prev=localedir ;;- -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*)- localedir=$ac_optarg ;;-- -localstatedir | --localstatedir | --localstatedi | --localstated \- | --localstate | --localstat | --localsta | --localst | --locals)- ac_prev=localstatedir ;;- -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \- | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*)- localstatedir=$ac_optarg ;;-- -mandir | --mandir | --mandi | --mand | --man | --ma | --m)- ac_prev=mandir ;;- -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*)- mandir=$ac_optarg ;;-- -nfp | --nfp | --nf)- # Obsolete; use --without-fp.- with_fp=no ;;-- -no-create | --no-create | --no-creat | --no-crea | --no-cre \- | --no-cr | --no-c | -n)- no_create=yes ;;-- -no-recursion | --no-recursion | --no-recursio | --no-recursi \- | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r)- no_recursion=yes ;;-- -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \- | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \- | --oldin | --oldi | --old | --ol | --o)- ac_prev=oldincludedir ;;- -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \- | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \- | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*)- oldincludedir=$ac_optarg ;;-- -prefix | --prefix | --prefi | --pref | --pre | --pr | --p)- ac_prev=prefix ;;- -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*)- prefix=$ac_optarg ;;-- -program-prefix | --program-prefix | --program-prefi | --program-pref \- | --program-pre | --program-pr | --program-p)- ac_prev=program_prefix ;;- -program-prefix=* | --program-prefix=* | --program-prefi=* \- | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*)- program_prefix=$ac_optarg ;;-- -program-suffix | --program-suffix | --program-suffi | --program-suff \- | --program-suf | --program-su | --program-s)- ac_prev=program_suffix ;;- -program-suffix=* | --program-suffix=* | --program-suffi=* \- | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*)- program_suffix=$ac_optarg ;;-- -program-transform-name | --program-transform-name \- | --program-transform-nam | --program-transform-na \- | --program-transform-n | --program-transform- \- | --program-transform | --program-transfor \- | --program-transfo | --program-transf \- | --program-trans | --program-tran \- | --progr-tra | --program-tr | --program-t)- ac_prev=program_transform_name ;;- -program-transform-name=* | --program-transform-name=* \- | --program-transform-nam=* | --program-transform-na=* \- | --program-transform-n=* | --program-transform-=* \- | --program-transform=* | --program-transfor=* \- | --program-transfo=* | --program-transf=* \- | --program-trans=* | --program-tran=* \- | --progr-tra=* | --program-tr=* | --program-t=*)- program_transform_name=$ac_optarg ;;-- -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd)- ac_prev=pdfdir ;;- -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*)- pdfdir=$ac_optarg ;;-- -psdir | --psdir | --psdi | --psd | --ps)- ac_prev=psdir ;;- -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*)- psdir=$ac_optarg ;;-- -q | -quiet | --quiet | --quie | --qui | --qu | --q \- | -silent | --silent | --silen | --sile | --sil)- silent=yes ;;-- -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)- ac_prev=sbindir ;;- -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \- | --sbi=* | --sb=*)- sbindir=$ac_optarg ;;-- -sharedstatedir | --sharedstatedir | --sharedstatedi \- | --sharedstated | --sharedstate | --sharedstat | --sharedsta \- | --sharedst | --shareds | --shared | --share | --shar \- | --sha | --sh)- ac_prev=sharedstatedir ;;- -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \- | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \- | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \- | --sha=* | --sh=*)- sharedstatedir=$ac_optarg ;;-- -site | --site | --sit)- ac_prev=site ;;- -site=* | --site=* | --sit=*)- site=$ac_optarg ;;-- -srcdir | --srcdir | --srcdi | --srcd | --src | --sr)- ac_prev=srcdir ;;- -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*)- srcdir=$ac_optarg ;;-- -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \- | --syscon | --sysco | --sysc | --sys | --sy)- ac_prev=sysconfdir ;;- -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \- | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*)- sysconfdir=$ac_optarg ;;-- -target | --target | --targe | --targ | --tar | --ta | --t)- ac_prev=target_alias ;;- -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*)- target_alias=$ac_optarg ;;-- -v | -verbose | --verbose | --verbos | --verbo | --verb)- verbose=yes ;;-- -version | --version | --versio | --versi | --vers | -V)- ac_init_version=: ;;-- -with-* | --with-*)- ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'`- # Reject names that are not valid shell variable names.- expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&- as_fn_error $? "invalid package name: $ac_useropt"- ac_useropt_orig=$ac_useropt- ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`- case $ac_user_opts in- *"-"with_$ac_useropt"-"*) ;;- *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig"- ac_unrecognized_sep=', ';;- esac- eval with_$ac_useropt=\$ac_optarg ;;-- -without-* | --without-*)- ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'`- # Reject names that are not valid shell variable names.- expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&- as_fn_error $? "invalid package name: $ac_useropt"- ac_useropt_orig=$ac_useropt- ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`- case $ac_user_opts in- *"-"with_$ac_useropt"-"*) ;;- *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig"- ac_unrecognized_sep=', ';;- esac- eval with_$ac_useropt=no ;;-- --x)- # Obsolete; use --with-x.- with_x=yes ;;-- -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \- | --x-incl | --x-inc | --x-in | --x-i)- ac_prev=x_includes ;;- -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \- | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*)- x_includes=$ac_optarg ;;-- -x-libraries | --x-libraries | --x-librarie | --x-librari \- | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l)- ac_prev=x_libraries ;;- -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \- | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*)- x_libraries=$ac_optarg ;;-- -*) as_fn_error $? "unrecognized option: \`$ac_option'-Try \`$0 --help' for more information"- ;;-- *=*)- ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='`- # Reject names that are not valid shell variable names.- case $ac_envvar in #(- '' | [0-9]* | *[!_$as_cr_alnum]* )- as_fn_error $? "invalid variable name: \`$ac_envvar'" ;;- esac- eval $ac_envvar=\$ac_optarg- export $ac_envvar ;;-- *)- # FIXME: should be removed in autoconf 3.0.- $as_echo "$as_me: WARNING: you should use --build, --host, --target" >&2- expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null &&- $as_echo "$as_me: WARNING: invalid host type: $ac_option" >&2- : "${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}"- ;;-- esac-done--if test -n "$ac_prev"; then- ac_option=--`echo $ac_prev | sed 's/_/-/g'`- as_fn_error $? "missing argument to $ac_option"-fi--if test -n "$ac_unrecognized_opts"; then- case $enable_option_checking in- no) ;;- fatal) as_fn_error $? "unrecognized options: $ac_unrecognized_opts" ;;- *) $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;;- esac-fi--# Check all directory arguments for consistency.-for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \- datadir sysconfdir sharedstatedir localstatedir includedir \- oldincludedir docdir infodir htmldir dvidir pdfdir psdir \- libdir localedir mandir-do- eval ac_val=\$$ac_var- # Remove trailing slashes.- case $ac_val in- */ )- ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'`- eval $ac_var=\$ac_val;;- esac- # Be sure to have absolute directory names.- case $ac_val in- [\\/$]* | ?:[\\/]* ) continue;;- NONE | '' ) case $ac_var in *prefix ) continue;; esac;;- esac- as_fn_error $? "expected an absolute directory name for --$ac_var: $ac_val"-done--# There might be people who depend on the old broken behavior: `$host'-# used to hold the argument of --host etc.-# FIXME: To remove some day.-build=$build_alias-host=$host_alias-target=$target_alias--# FIXME: To remove some day.-if test "x$host_alias" != x; then- if test "x$build_alias" = x; then- cross_compiling=maybe- $as_echo "$as_me: WARNING: if you wanted to set the --build type, don't use --host.- If a cross compiler is detected then cross compile mode will be used" >&2- elif test "x$build_alias" != "x$host_alias"; then- cross_compiling=yes- fi-fi--ac_tool_prefix=-test -n "$host_alias" && ac_tool_prefix=$host_alias---test "$silent" = yes && exec 6>/dev/null---ac_pwd=`pwd` && test -n "$ac_pwd" &&-ac_ls_di=`ls -di .` &&-ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` ||- as_fn_error $? "working directory cannot be determined"-test "X$ac_ls_di" = "X$ac_pwd_ls_di" ||- as_fn_error $? "pwd does not report name of working directory"---# Find the source files, if location was not specified.-if test -z "$srcdir"; then- ac_srcdir_defaulted=yes- # Try the directory containing this script, then the parent directory.- ac_confdir=`$as_dirname -- "$as_myself" ||-$as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \- X"$as_myself" : 'X\(//\)[^/]' \| \- X"$as_myself" : 'X\(//\)$' \| \- X"$as_myself" : 'X\(/\)' \| . 2>/dev/null ||-$as_echo X"$as_myself" |- sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{- s//\1/- q- }- /^X\(\/\/\)[^/].*/{- s//\1/- q- }- /^X\(\/\/\)$/{- s//\1/- q- }- /^X\(\/\).*/{- s//\1/- q- }- s/.*/./; q'`- srcdir=$ac_confdir- if test ! -r "$srcdir/$ac_unique_file"; then- srcdir=..- fi-else- ac_srcdir_defaulted=no-fi-if test ! -r "$srcdir/$ac_unique_file"; then- test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .."- as_fn_error $? "cannot find sources ($ac_unique_file) in $srcdir"-fi-ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work"-ac_abs_confdir=`(- cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error $? "$ac_msg"- pwd)`-# When building in place, set srcdir=.-if test "$ac_abs_confdir" = "$ac_pwd"; then- srcdir=.-fi-# Remove unnecessary trailing slashes from srcdir.-# Double slashes in file names in object file debugging info-# mess up M-x gdb in Emacs.-case $srcdir in-*/) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;;-esac-for ac_var in $ac_precious_vars; do- eval ac_env_${ac_var}_set=\${${ac_var}+set}- eval ac_env_${ac_var}_value=\$${ac_var}- eval ac_cv_env_${ac_var}_set=\${${ac_var}+set}- eval ac_cv_env_${ac_var}_value=\$${ac_var}-done--#-# Report the --help message.-#-if test "$ac_init_help" = "long"; then- # Omit some internal or obsolete options to make the list less imposing.- # This message is too long to be a string in the A/UX 3.1 sh.- cat <<_ACEOF-\`configure' configures Labeled IO library 0.1.0 to adapt to many kinds of systems.--Usage: $0 [OPTION]... [VAR=VALUE]...--To assign environment variables (e.g., CC, CFLAGS...), specify them as-VAR=VALUE. See below for descriptions of some of the useful variables.--Defaults for the options are specified in brackets.--Configuration:- -h, --help display this help and exit- --help=short display options specific to this package- --help=recursive display the short help of all the included packages- -V, --version display version information and exit- -q, --quiet, --silent do not print \`checking ...' messages- --cache-file=FILE cache test results in FILE [disabled]- -C, --config-cache alias for \`--cache-file=config.cache'- -n, --no-create do not create output files- --srcdir=DIR find the sources in DIR [configure dir or \`..']--Installation directories:- --prefix=PREFIX install architecture-independent files in PREFIX- [$ac_default_prefix]- --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX- [PREFIX]--By default, \`make install' will install all the files in-\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify-an installation prefix other than \`$ac_default_prefix' using \`--prefix',-for instance \`--prefix=\$HOME'.--For better control, use the options below.--Fine tuning of the installation directories:- --bindir=DIR user executables [EPREFIX/bin]- --sbindir=DIR system admin executables [EPREFIX/sbin]- --libexecdir=DIR program executables [EPREFIX/libexec]- --sysconfdir=DIR read-only single-machine data [PREFIX/etc]- --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com]- --localstatedir=DIR modifiable single-machine data [PREFIX/var]- --libdir=DIR object code libraries [EPREFIX/lib]- --includedir=DIR C header files [PREFIX/include]- --oldincludedir=DIR C header files for non-gcc [/usr/include]- --datarootdir=DIR read-only arch.-independent data root [PREFIX/share]- --datadir=DIR read-only architecture-independent data [DATAROOTDIR]- --infodir=DIR info documentation [DATAROOTDIR/info]- --localedir=DIR locale-dependent data [DATAROOTDIR/locale]- --mandir=DIR man documentation [DATAROOTDIR/man]- --docdir=DIR documentation root [DATAROOTDIR/doc/lio]- --htmldir=DIR html documentation [DOCDIR]- --dvidir=DIR dvi documentation [DOCDIR]- --pdfdir=DIR pdf documentation [DOCDIR]- --psdir=DIR ps documentation [DOCDIR]-_ACEOF-- cat <<\_ACEOF-_ACEOF-fi--if test -n "$ac_init_help"; then- case $ac_init_help in- short | recursive ) echo "Configuration of Labeled IO library 0.1.0:";;- esac- cat <<\_ACEOF--Some influential environment variables:- CC C compiler command- CFLAGS C compiler flags- LDFLAGS linker flags, e.g. -L<lib dir> if you have libraries in a- nonstandard directory <lib dir>- LIBS libraries to pass to the linker, e.g. -l<library>- CPPFLAGS (Objective) C/C++ preprocessor flags, e.g. -I<include dir> if- you have headers in a nonstandard directory <include dir>--Use these variables to override the choices made by `configure' or to help-it to find libraries and programs with nonstandard names/locations.--Report bugs to <deian@cs.stanford.edu>.-_ACEOF-ac_status=$?-fi--if test "$ac_init_help" = "recursive"; then- # If there are subdirs, report their specific --help.- for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue- test -d "$ac_dir" ||- { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } ||- continue- ac_builddir=.--case "$ac_dir" in-.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;;-*)- ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'`- # A ".." for each directory in $ac_dir_suffix.- ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'`- case $ac_top_builddir_sub in- "") ac_top_builddir_sub=. ac_top_build_prefix= ;;- *) ac_top_build_prefix=$ac_top_builddir_sub/ ;;- esac ;;-esac-ac_abs_top_builddir=$ac_pwd-ac_abs_builddir=$ac_pwd$ac_dir_suffix-# for backward compatibility:-ac_top_builddir=$ac_top_build_prefix--case $srcdir in- .) # We are building in place.- ac_srcdir=.- ac_top_srcdir=$ac_top_builddir_sub- ac_abs_top_srcdir=$ac_pwd ;;- [\\/]* | ?:[\\/]* ) # Absolute name.- ac_srcdir=$srcdir$ac_dir_suffix;- ac_top_srcdir=$srcdir- ac_abs_top_srcdir=$srcdir ;;- *) # Relative name.- ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix- ac_top_srcdir=$ac_top_build_prefix$srcdir- ac_abs_top_srcdir=$ac_pwd/$srcdir ;;-esac-ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix-- cd "$ac_dir" || { ac_status=$?; continue; }- # Check for guested configure.- if test -f "$ac_srcdir/configure.gnu"; then- echo &&- $SHELL "$ac_srcdir/configure.gnu" --help=recursive- elif test -f "$ac_srcdir/configure"; then- echo &&- $SHELL "$ac_srcdir/configure" --help=recursive- else- $as_echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2- fi || ac_status=$?- cd "$ac_pwd" || { ac_status=$?; break; }- done-fi--test -n "$ac_init_help" && exit $ac_status-if $ac_init_version; then- cat <<\_ACEOF-Labeled IO library configure 0.1.0-generated by GNU Autoconf 2.68--Copyright (C) 2010 Free Software Foundation, Inc.-This configure script is free software; the Free Software Foundation-gives unlimited permission to copy, distribute and modify it.-_ACEOF- exit-fi--## ------------------------ ##-## Autoconf initialization. ##-## ------------------------ ##--# ac_fn_c_try_compile LINENO-# ---------------------------# Try to compile conftest.$ac_ext, and return whether this succeeded.-ac_fn_c_try_compile ()-{- as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack- rm -f conftest.$ac_objext- if { { ac_try="$ac_compile"-case "(($ac_try" in- *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;- *) ac_try_echo=$ac_try;;-esac-eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""-$as_echo "$ac_try_echo"; } >&5- (eval "$ac_compile") 2>conftest.err- ac_status=$?- if test -s conftest.err; then- grep -v '^ *+' conftest.err >conftest.er1- cat conftest.er1 >&5- mv -f conftest.er1 conftest.err- fi- $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5- test $ac_status = 0; } && {- test -z "$ac_c_werror_flag" ||- test ! -s conftest.err- } && test -s conftest.$ac_objext; then :- ac_retval=0-else- $as_echo "$as_me: failed program was:" >&5-sed 's/^/| /' conftest.$ac_ext >&5-- ac_retval=1-fi- eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno- as_fn_set_status $ac_retval--} # ac_fn_c_try_compile--# ac_fn_c_try_link LINENO-# ------------------------# Try to link conftest.$ac_ext, and return whether this succeeded.-ac_fn_c_try_link ()-{- as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack- rm -f conftest.$ac_objext conftest$ac_exeext- if { { ac_try="$ac_link"-case "(($ac_try" in- *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;- *) ac_try_echo=$ac_try;;-esac-eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""-$as_echo "$ac_try_echo"; } >&5- (eval "$ac_link") 2>conftest.err- ac_status=$?- if test -s conftest.err; then- grep -v '^ *+' conftest.err >conftest.er1- cat conftest.er1 >&5- mv -f conftest.er1 conftest.err- fi- $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5- test $ac_status = 0; } && {- test -z "$ac_c_werror_flag" ||- test ! -s conftest.err- } && test -s conftest$ac_exeext && {- test "$cross_compiling" = yes ||- $as_test_x conftest$ac_exeext- }; then :- ac_retval=0-else- $as_echo "$as_me: failed program was:" >&5-sed 's/^/| /' conftest.$ac_ext >&5-- ac_retval=1-fi- # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information- # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would- # interfere with the next link command; also delete a directory that is- # left behind by Apple's compiler. We do this before executing the actions.- rm -rf conftest.dSYM conftest_ipa8_conftest.oo- eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno- as_fn_set_status $ac_retval--} # ac_fn_c_try_link--# ac_fn_c_check_func LINENO FUNC VAR-# -----------------------------------# Tests whether FUNC exists, setting the cache variable VAR accordingly-ac_fn_c_check_func ()-{- as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack- { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5-$as_echo_n "checking for $2... " >&6; }-if eval \${$3+:} false; then :- $as_echo_n "(cached) " >&6-else- cat confdefs.h - <<_ACEOF >conftest.$ac_ext-/* end confdefs.h. */-/* Define $2 to an innocuous variant, in case <limits.h> declares $2.- For example, HP-UX 11i <limits.h> declares gettimeofday. */-#define $2 innocuous_$2--/* System header to define __stub macros and hopefully few prototypes,- which can conflict with char $2 (); below.- Prefer <limits.h> to <assert.h> if __STDC__ is defined, since- <limits.h> exists even on freestanding compilers. */--#ifdef __STDC__-# include <limits.h>-#else-# include <assert.h>-#endif--#undef $2--/* Override any GCC internal prototype to avoid an error.- Use char because int might match the return type of a GCC- builtin and then its argument prototype would still apply. */-#ifdef __cplusplus-extern "C"-#endif-char $2 ();-/* The GNU C library defines this for functions which it implements- to always fail with ENOSYS. Some functions are actually named- something starting with __ and the normal name is an alias. */-#if defined __stub_$2 || defined __stub___$2-choke me-#endif--int-main ()-{-return $2 ();- ;- return 0;-}-_ACEOF-if ac_fn_c_try_link "$LINENO"; then :- eval "$3=yes"-else- eval "$3=no"-fi-rm -f core conftest.err conftest.$ac_objext \- conftest$ac_exeext conftest.$ac_ext-fi-eval ac_res=\$$3- { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5-$as_echo "$ac_res" >&6; }- eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno--} # ac_fn_c_check_func-cat >config.log <<_ACEOF-This file contains any messages produced by compilers while-running configure, to aid debugging if configure makes a mistake.--It was created by Labeled IO library $as_me 0.1.0, which was-generated by GNU Autoconf 2.68. Invocation command line was-- $ $0 $@--_ACEOF-exec 5>>config.log-{-cat <<_ASUNAME-## --------- ##-## Platform. ##-## --------- ##--hostname = `(hostname || uname -n) 2>/dev/null | sed 1q`-uname -m = `(uname -m) 2>/dev/null || echo unknown`-uname -r = `(uname -r) 2>/dev/null || echo unknown`-uname -s = `(uname -s) 2>/dev/null || echo unknown`-uname -v = `(uname -v) 2>/dev/null || echo unknown`--/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown`-/bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown`--/bin/arch = `(/bin/arch) 2>/dev/null || echo unknown`-/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown`-/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown`-/usr/bin/hostinfo = `(/usr/bin/hostinfo) 2>/dev/null || echo unknown`-/bin/machine = `(/bin/machine) 2>/dev/null || echo unknown`-/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown`-/bin/universe = `(/bin/universe) 2>/dev/null || echo unknown`--_ASUNAME--as_save_IFS=$IFS; IFS=$PATH_SEPARATOR-for as_dir in $PATH-do- IFS=$as_save_IFS- test -z "$as_dir" && as_dir=.- $as_echo "PATH: $as_dir"- done-IFS=$as_save_IFS--} >&5--cat >&5 <<_ACEOF---## ----------- ##-## Core tests. ##-## ----------- ##--_ACEOF---# Keep a trace of the command line.-# Strip out --no-create and --no-recursion so they do not pile up.-# Strip out --silent because we don't want to record it for future runs.-# Also quote any args containing shell meta-characters.-# Make two passes to allow for proper duplicate-argument suppression.-ac_configure_args=-ac_configure_args0=-ac_configure_args1=-ac_must_keep_next=false-for ac_pass in 1 2-do- for ac_arg- do- case $ac_arg in- -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;;- -q | -quiet | --quiet | --quie | --qui | --qu | --q \- | -silent | --silent | --silen | --sile | --sil)- continue ;;- *\'*)- ac_arg=`$as_echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;;- esac- case $ac_pass in- 1) as_fn_append ac_configure_args0 " '$ac_arg'" ;;- 2)- as_fn_append ac_configure_args1 " '$ac_arg'"- if test $ac_must_keep_next = true; then- ac_must_keep_next=false # Got value, back to normal.- else- case $ac_arg in- *=* | --config-cache | -C | -disable-* | --disable-* \- | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \- | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \- | -with-* | --with-* | -without-* | --without-* | --x)- case "$ac_configure_args0 " in- "$ac_configure_args1"*" '$ac_arg' "* ) continue ;;- esac- ;;- -* ) ac_must_keep_next=true ;;- esac- fi- as_fn_append ac_configure_args " '$ac_arg'"- ;;- esac- done-done-{ ac_configure_args0=; unset ac_configure_args0;}-{ ac_configure_args1=; unset ac_configure_args1;}--# When interrupted or exit'd, cleanup temporary files, and complete-# config.log. We remove comments because anyway the quotes in there-# would cause problems or look ugly.-# WARNING: Use '\'' to represent an apostrophe within the trap.-# WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug.-trap 'exit_status=$?- # Save into config.log some information that might help in debugging.- {- echo-- $as_echo "## ---------------- ##-## Cache variables. ##-## ---------------- ##"- echo- # The following way of writing the cache mishandles newlines in values,-(- for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do- eval ac_val=\$$ac_var- case $ac_val in #(- *${as_nl}*)- case $ac_var in #(- *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5-$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;;- esac- case $ac_var in #(- _ | IFS | as_nl) ;; #(- BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #(- *) { eval $ac_var=; unset $ac_var;} ;;- esac ;;- esac- done- (set) 2>&1 |- case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #(- *${as_nl}ac_space=\ *)- sed -n \- "s/'\''/'\''\\\\'\'''\''/g;- s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p"- ;; #(- *)- sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p"- ;;- esac |- sort-)- echo-- $as_echo "## ----------------- ##-## Output variables. ##-## ----------------- ##"- echo- for ac_var in $ac_subst_vars- do- eval ac_val=\$$ac_var- case $ac_val in- *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;;- esac- $as_echo "$ac_var='\''$ac_val'\''"- done | sort- echo-- if test -n "$ac_subst_files"; then- $as_echo "## ------------------- ##-## File substitutions. ##-## ------------------- ##"- echo- for ac_var in $ac_subst_files- do- eval ac_val=\$$ac_var- case $ac_val in- *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;;- esac- $as_echo "$ac_var='\''$ac_val'\''"- done | sort- echo- fi-- if test -s confdefs.h; then- $as_echo "## ----------- ##-## confdefs.h. ##-## ----------- ##"- echo- cat confdefs.h- echo- fi- test "$ac_signal" != 0 &&- $as_echo "$as_me: caught signal $ac_signal"- $as_echo "$as_me: exit $exit_status"- } >&5- rm -f core *.core core.conftest.* &&- rm -f -r conftest* confdefs* conf$$* $ac_clean_files &&- exit $exit_status-' 0-for ac_signal in 1 2 13 15; do- trap 'ac_signal='$ac_signal'; as_fn_exit 1' $ac_signal-done-ac_signal=0--# confdefs.h avoids OS command line length limits that DEFS can exceed.-rm -f -r conftest* confdefs.h--$as_echo "/* confdefs.h */" > confdefs.h--# Predefined preprocessor variables.--cat >>confdefs.h <<_ACEOF-#define PACKAGE_NAME "$PACKAGE_NAME"-_ACEOF--cat >>confdefs.h <<_ACEOF-#define PACKAGE_TARNAME "$PACKAGE_TARNAME"-_ACEOF--cat >>confdefs.h <<_ACEOF-#define PACKAGE_VERSION "$PACKAGE_VERSION"-_ACEOF--cat >>confdefs.h <<_ACEOF-#define PACKAGE_STRING "$PACKAGE_STRING"-_ACEOF--cat >>confdefs.h <<_ACEOF-#define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT"-_ACEOF--cat >>confdefs.h <<_ACEOF-#define PACKAGE_URL "$PACKAGE_URL"-_ACEOF---# Let the site file select an alternate cache file if it wants to.-# Prefer an explicitly selected file to automatically selected ones.-ac_site_file1=NONE-ac_site_file2=NONE-if test -n "$CONFIG_SITE"; then- # We do not want a PATH search for config.site.- case $CONFIG_SITE in #((- -*) ac_site_file1=./$CONFIG_SITE;;- */*) ac_site_file1=$CONFIG_SITE;;- *) ac_site_file1=./$CONFIG_SITE;;- esac-elif test "x$prefix" != xNONE; then- ac_site_file1=$prefix/share/config.site- ac_site_file2=$prefix/etc/config.site-else- ac_site_file1=$ac_default_prefix/share/config.site- ac_site_file2=$ac_default_prefix/etc/config.site-fi-for ac_site_file in "$ac_site_file1" "$ac_site_file2"-do- test "x$ac_site_file" = xNONE && continue- if test /dev/null != "$ac_site_file" && test -r "$ac_site_file"; then- { $as_echo "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5-$as_echo "$as_me: loading site script $ac_site_file" >&6;}- sed 's/^/| /' "$ac_site_file" >&5- . "$ac_site_file" \- || { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5-$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}-as_fn_error $? "failed to load site script $ac_site_file-See \`config.log' for more details" "$LINENO" 5; }- fi-done--if test -r "$cache_file"; then- # Some versions of bash will fail to source /dev/null (special files- # actually), so we avoid doing that. DJGPP emulates it as a regular file.- if test /dev/null != "$cache_file" && test -f "$cache_file"; then- { $as_echo "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5-$as_echo "$as_me: loading cache $cache_file" >&6;}- case $cache_file in- [\\/]* | ?:[\\/]* ) . "$cache_file";;- *) . "./$cache_file";;- esac- fi-else- { $as_echo "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5-$as_echo "$as_me: creating cache $cache_file" >&6;}- >$cache_file-fi--# Check that the precious variables saved in the cache have kept the same-# value.-ac_cache_corrupted=false-for ac_var in $ac_precious_vars; do- eval ac_old_set=\$ac_cv_env_${ac_var}_set- eval ac_new_set=\$ac_env_${ac_var}_set- eval ac_old_val=\$ac_cv_env_${ac_var}_value- eval ac_new_val=\$ac_env_${ac_var}_value- case $ac_old_set,$ac_new_set in- set,)- { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5-$as_echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;}- ac_cache_corrupted=: ;;- ,set)- { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was not set in the previous run" >&5-$as_echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;}- ac_cache_corrupted=: ;;- ,);;- *)- if test "x$ac_old_val" != "x$ac_new_val"; then- # differences in whitespace do not lead to failure.- ac_old_val_w=`echo x $ac_old_val`- ac_new_val_w=`echo x $ac_new_val`- if test "$ac_old_val_w" != "$ac_new_val_w"; then- { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' has changed since the previous run:" >&5-$as_echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;}- ac_cache_corrupted=:- else- { $as_echo "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5-$as_echo "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;}- eval $ac_var=\$ac_old_val- fi- { $as_echo "$as_me:${as_lineno-$LINENO}: former value: \`$ac_old_val'" >&5-$as_echo "$as_me: former value: \`$ac_old_val'" >&2;}- { $as_echo "$as_me:${as_lineno-$LINENO}: current value: \`$ac_new_val'" >&5-$as_echo "$as_me: current value: \`$ac_new_val'" >&2;}- fi;;- esac- # Pass precious variables to config.status.- if test "$ac_new_set" = set; then- case $ac_new_val in- *\'*) ac_arg=$ac_var=`$as_echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;;- *) ac_arg=$ac_var=$ac_new_val ;;- esac- case " $ac_configure_args " in- *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy.- *) as_fn_append ac_configure_args " '$ac_arg'" ;;- esac- fi-done-if $ac_cache_corrupted; then- { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5-$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}- { $as_echo "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5-$as_echo "$as_me: error: changes in the environment can compromise the build" >&2;}- as_fn_error $? "run \`make distclean' and/or \`rm $cache_file' and start over" "$LINENO" 5-fi-## -------------------- ##-## Main body of script. ##-## -------------------- ##--ac_ext=c-ac_cpp='$CPP $CPPFLAGS'-ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'-ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'-ac_compiler_gnu=$ac_cv_c_compiler_gnu----# Safety check: Ensure that we are in the correct source directory.---ac_ext=c-ac_cpp='$CPP $CPPFLAGS'-ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'-ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'-ac_compiler_gnu=$ac_cv_c_compiler_gnu-if test -n "$ac_tool_prefix"; then- # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args.-set dummy ${ac_tool_prefix}gcc; ac_word=$2-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5-$as_echo_n "checking for $ac_word... " >&6; }-if ${ac_cv_prog_CC+:} false; then :- $as_echo_n "(cached) " >&6-else- if test -n "$CC"; then- ac_cv_prog_CC="$CC" # Let the user override the test.-else-as_save_IFS=$IFS; IFS=$PATH_SEPARATOR-for as_dir in $PATH-do- IFS=$as_save_IFS- test -z "$as_dir" && as_dir=.- for ac_exec_ext in '' $ac_executable_extensions; do- if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then- ac_cv_prog_CC="${ac_tool_prefix}gcc"- $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5- break 2- fi-done- done-IFS=$as_save_IFS--fi-fi-CC=$ac_cv_prog_CC-if test -n "$CC"; then- { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5-$as_echo "$CC" >&6; }-else- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5-$as_echo "no" >&6; }-fi---fi-if test -z "$ac_cv_prog_CC"; then- ac_ct_CC=$CC- # Extract the first word of "gcc", so it can be a program name with args.-set dummy gcc; ac_word=$2-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5-$as_echo_n "checking for $ac_word... " >&6; }-if ${ac_cv_prog_ac_ct_CC+:} false; then :- $as_echo_n "(cached) " >&6-else- if test -n "$ac_ct_CC"; then- ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.-else-as_save_IFS=$IFS; IFS=$PATH_SEPARATOR-for as_dir in $PATH-do- IFS=$as_save_IFS- test -z "$as_dir" && as_dir=.- for ac_exec_ext in '' $ac_executable_extensions; do- if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then- ac_cv_prog_ac_ct_CC="gcc"- $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5- break 2- fi-done- done-IFS=$as_save_IFS--fi-fi-ac_ct_CC=$ac_cv_prog_ac_ct_CC-if test -n "$ac_ct_CC"; then- { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5-$as_echo "$ac_ct_CC" >&6; }-else- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5-$as_echo "no" >&6; }-fi-- if test "x$ac_ct_CC" = x; then- CC=""- else- case $cross_compiling:$ac_tool_warned in-yes:)-{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5-$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}-ac_tool_warned=yes ;;-esac- CC=$ac_ct_CC- fi-else- CC="$ac_cv_prog_CC"-fi--if test -z "$CC"; then- if test -n "$ac_tool_prefix"; then- # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args.-set dummy ${ac_tool_prefix}cc; ac_word=$2-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5-$as_echo_n "checking for $ac_word... " >&6; }-if ${ac_cv_prog_CC+:} false; then :- $as_echo_n "(cached) " >&6-else- if test -n "$CC"; then- ac_cv_prog_CC="$CC" # Let the user override the test.-else-as_save_IFS=$IFS; IFS=$PATH_SEPARATOR-for as_dir in $PATH-do- IFS=$as_save_IFS- test -z "$as_dir" && as_dir=.- for ac_exec_ext in '' $ac_executable_extensions; do- if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then- ac_cv_prog_CC="${ac_tool_prefix}cc"- $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5- break 2- fi-done- done-IFS=$as_save_IFS--fi-fi-CC=$ac_cv_prog_CC-if test -n "$CC"; then- { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5-$as_echo "$CC" >&6; }-else- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5-$as_echo "no" >&6; }-fi--- fi-fi-if test -z "$CC"; then- # Extract the first word of "cc", so it can be a program name with args.-set dummy cc; ac_word=$2-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5-$as_echo_n "checking for $ac_word... " >&6; }-if ${ac_cv_prog_CC+:} false; then :- $as_echo_n "(cached) " >&6-else- if test -n "$CC"; then- ac_cv_prog_CC="$CC" # Let the user override the test.-else- ac_prog_rejected=no-as_save_IFS=$IFS; IFS=$PATH_SEPARATOR-for as_dir in $PATH-do- IFS=$as_save_IFS- test -z "$as_dir" && as_dir=.- for ac_exec_ext in '' $ac_executable_extensions; do- if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then- if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then- ac_prog_rejected=yes- continue- fi- ac_cv_prog_CC="cc"- $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5- break 2- fi-done- done-IFS=$as_save_IFS--if test $ac_prog_rejected = yes; then- # We found a bogon in the path, so make sure we never use it.- set dummy $ac_cv_prog_CC- shift- if test $# != 0; then- # We chose a different compiler from the bogus one.- # However, it has the same basename, so the bogon will be chosen- # first if we set CC to just the basename; use the full file name.- shift- ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@"- fi-fi-fi-fi-CC=$ac_cv_prog_CC-if test -n "$CC"; then- { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5-$as_echo "$CC" >&6; }-else- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5-$as_echo "no" >&6; }-fi---fi-if test -z "$CC"; then- if test -n "$ac_tool_prefix"; then- for ac_prog in cl.exe- do- # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args.-set dummy $ac_tool_prefix$ac_prog; ac_word=$2-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5-$as_echo_n "checking for $ac_word... " >&6; }-if ${ac_cv_prog_CC+:} false; then :- $as_echo_n "(cached) " >&6-else- if test -n "$CC"; then- ac_cv_prog_CC="$CC" # Let the user override the test.-else-as_save_IFS=$IFS; IFS=$PATH_SEPARATOR-for as_dir in $PATH-do- IFS=$as_save_IFS- test -z "$as_dir" && as_dir=.- for ac_exec_ext in '' $ac_executable_extensions; do- if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then- ac_cv_prog_CC="$ac_tool_prefix$ac_prog"- $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5- break 2- fi-done- done-IFS=$as_save_IFS--fi-fi-CC=$ac_cv_prog_CC-if test -n "$CC"; then- { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5-$as_echo "$CC" >&6; }-else- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5-$as_echo "no" >&6; }-fi--- test -n "$CC" && break- done-fi-if test -z "$CC"; then- ac_ct_CC=$CC- for ac_prog in cl.exe-do- # Extract the first word of "$ac_prog", so it can be a program name with args.-set dummy $ac_prog; ac_word=$2-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5-$as_echo_n "checking for $ac_word... " >&6; }-if ${ac_cv_prog_ac_ct_CC+:} false; then :- $as_echo_n "(cached) " >&6-else- if test -n "$ac_ct_CC"; then- ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.-else-as_save_IFS=$IFS; IFS=$PATH_SEPARATOR-for as_dir in $PATH-do- IFS=$as_save_IFS- test -z "$as_dir" && as_dir=.- for ac_exec_ext in '' $ac_executable_extensions; do- if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then- ac_cv_prog_ac_ct_CC="$ac_prog"- $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5- break 2- fi-done- done-IFS=$as_save_IFS--fi-fi-ac_ct_CC=$ac_cv_prog_ac_ct_CC-if test -n "$ac_ct_CC"; then- { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5-$as_echo "$ac_ct_CC" >&6; }-else- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5-$as_echo "no" >&6; }-fi--- test -n "$ac_ct_CC" && break-done-- if test "x$ac_ct_CC" = x; then- CC=""- else- case $cross_compiling:$ac_tool_warned in-yes:)-{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5-$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}-ac_tool_warned=yes ;;-esac- CC=$ac_ct_CC- fi-fi--fi---test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5-$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}-as_fn_error $? "no acceptable C compiler found in \$PATH-See \`config.log' for more details" "$LINENO" 5; }--# Provide some information about the compiler.-$as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5-set X $ac_compile-ac_compiler=$2-for ac_option in --version -v -V -qversion; do- { { ac_try="$ac_compiler $ac_option >&5"-case "(($ac_try" in- *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;- *) ac_try_echo=$ac_try;;-esac-eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""-$as_echo "$ac_try_echo"; } >&5- (eval "$ac_compiler $ac_option >&5") 2>conftest.err- ac_status=$?- if test -s conftest.err; then- sed '10a\-... rest of stderr output deleted ...- 10q' conftest.err >conftest.er1- cat conftest.er1 >&5- fi- rm -f conftest.er1 conftest.err- $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5- test $ac_status = 0; }-done--cat confdefs.h - <<_ACEOF >conftest.$ac_ext-/* end confdefs.h. */--int-main ()-{-- ;- return 0;-}-_ACEOF-ac_clean_files_save=$ac_clean_files-ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out"-# Try to create an executable without -o first, disregard a.out.-# It will help us diagnose broken compilers, and finding out an intuition-# of exeext.-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler works" >&5-$as_echo_n "checking whether the C compiler works... " >&6; }-ac_link_default=`$as_echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'`--# The possible output files:-ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*"--ac_rmfiles=-for ac_file in $ac_files-do- case $ac_file in- *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;;- * ) ac_rmfiles="$ac_rmfiles $ac_file";;- esac-done-rm -f $ac_rmfiles--if { { ac_try="$ac_link_default"-case "(($ac_try" in- *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;- *) ac_try_echo=$ac_try;;-esac-eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""-$as_echo "$ac_try_echo"; } >&5- (eval "$ac_link_default") 2>&5- ac_status=$?- $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5- test $ac_status = 0; }; then :- # Autoconf-2.13 could set the ac_cv_exeext variable to `no'.-# So ignore a value of `no', otherwise this would lead to `EXEEXT = no'-# in a Makefile. We should not override ac_cv_exeext if it was cached,-# so that the user can short-circuit this test for compilers unknown to-# Autoconf.-for ac_file in $ac_files ''-do- test -f "$ac_file" || continue- case $ac_file in- *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj )- ;;- [ab].out )- # We found the default executable, but exeext='' is most- # certainly right.- break;;- *.* )- if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no;- then :; else- ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`- fi- # We set ac_cv_exeext here because the later test for it is not- # safe: cross compilers may not add the suffix if given an `-o'- # argument, so we may need to know it at that point already.- # Even if this section looks crufty: it has the advantage of- # actually working.- break;;- * )- break;;- esac-done-test "$ac_cv_exeext" = no && ac_cv_exeext=--else- ac_file=''-fi-if test -z "$ac_file"; then :- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5-$as_echo "no" >&6; }-$as_echo "$as_me: failed program was:" >&5-sed 's/^/| /' conftest.$ac_ext >&5--{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5-$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}-as_fn_error 77 "C compiler cannot create executables-See \`config.log' for more details" "$LINENO" 5; }-else- { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5-$as_echo "yes" >&6; }-fi-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name" >&5-$as_echo_n "checking for C compiler default output file name... " >&6; }-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5-$as_echo "$ac_file" >&6; }-ac_exeext=$ac_cv_exeext--rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out-ac_clean_files=$ac_clean_files_save-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5-$as_echo_n "checking for suffix of executables... " >&6; }-if { { ac_try="$ac_link"-case "(($ac_try" in- *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;- *) ac_try_echo=$ac_try;;-esac-eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""-$as_echo "$ac_try_echo"; } >&5- (eval "$ac_link") 2>&5- ac_status=$?- $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5- test $ac_status = 0; }; then :- # If both `conftest.exe' and `conftest' are `present' (well, observable)-# catch `conftest.exe'. For instance with Cygwin, `ls conftest' will-# work properly (i.e., refer to `conftest.exe'), while it won't with-# `rm'.-for ac_file in conftest.exe conftest conftest.*; do- test -f "$ac_file" || continue- case $ac_file in- *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;;- *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`- break;;- * ) break;;- esac-done-else- { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5-$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}-as_fn_error $? "cannot compute suffix of executables: cannot compile and link-See \`config.log' for more details" "$LINENO" 5; }-fi-rm -f conftest conftest$ac_cv_exeext-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5-$as_echo "$ac_cv_exeext" >&6; }--rm -f conftest.$ac_ext-EXEEXT=$ac_cv_exeext-ac_exeext=$EXEEXT-cat confdefs.h - <<_ACEOF >conftest.$ac_ext-/* end confdefs.h. */-#include <stdio.h>-int-main ()-{-FILE *f = fopen ("conftest.out", "w");- return ferror (f) || fclose (f) != 0;-- ;- return 0;-}-_ACEOF-ac_clean_files="$ac_clean_files conftest.out"-# Check that the compiler produces executables we can run. If not, either-# the compiler is broken, or we cross compile.-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5-$as_echo_n "checking whether we are cross compiling... " >&6; }-if test "$cross_compiling" != yes; then- { { ac_try="$ac_link"-case "(($ac_try" in- *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;- *) ac_try_echo=$ac_try;;-esac-eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""-$as_echo "$ac_try_echo"; } >&5- (eval "$ac_link") 2>&5- ac_status=$?- $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5- test $ac_status = 0; }- if { ac_try='./conftest$ac_cv_exeext'- { { case "(($ac_try" in- *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;- *) ac_try_echo=$ac_try;;-esac-eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""-$as_echo "$ac_try_echo"; } >&5- (eval "$ac_try") 2>&5- ac_status=$?- $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5- test $ac_status = 0; }; }; then- cross_compiling=no- else- if test "$cross_compiling" = maybe; then- cross_compiling=yes- else- { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5-$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}-as_fn_error $? "cannot run C compiled programs.-If you meant to cross compile, use \`--host'.-See \`config.log' for more details" "$LINENO" 5; }- fi- fi-fi-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5-$as_echo "$cross_compiling" >&6; }--rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out-ac_clean_files=$ac_clean_files_save-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5-$as_echo_n "checking for suffix of object files... " >&6; }-if ${ac_cv_objext+:} false; then :- $as_echo_n "(cached) " >&6-else- cat confdefs.h - <<_ACEOF >conftest.$ac_ext-/* end confdefs.h. */--int-main ()-{-- ;- return 0;-}-_ACEOF-rm -f conftest.o conftest.obj-if { { ac_try="$ac_compile"-case "(($ac_try" in- *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;- *) ac_try_echo=$ac_try;;-esac-eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""-$as_echo "$ac_try_echo"; } >&5- (eval "$ac_compile") 2>&5- ac_status=$?- $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5- test $ac_status = 0; }; then :- for ac_file in conftest.o conftest.obj conftest.*; do- test -f "$ac_file" || continue;- case $ac_file in- *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;;- *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'`- break;;- esac-done-else- $as_echo "$as_me: failed program was:" >&5-sed 's/^/| /' conftest.$ac_ext >&5--{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5-$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}-as_fn_error $? "cannot compute suffix of object files: cannot compile-See \`config.log' for more details" "$LINENO" 5; }-fi-rm -f conftest.$ac_cv_objext conftest.$ac_ext-fi-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5-$as_echo "$ac_cv_objext" >&6; }-OBJEXT=$ac_cv_objext-ac_objext=$OBJEXT-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5-$as_echo_n "checking whether we are using the GNU C compiler... " >&6; }-if ${ac_cv_c_compiler_gnu+:} false; then :- $as_echo_n "(cached) " >&6-else- cat confdefs.h - <<_ACEOF >conftest.$ac_ext-/* end confdefs.h. */--int-main ()-{-#ifndef __GNUC__- choke me-#endif-- ;- return 0;-}-_ACEOF-if ac_fn_c_try_compile "$LINENO"; then :- ac_compiler_gnu=yes-else- ac_compiler_gnu=no-fi-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext-ac_cv_c_compiler_gnu=$ac_compiler_gnu--fi-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5-$as_echo "$ac_cv_c_compiler_gnu" >&6; }-if test $ac_compiler_gnu = yes; then- GCC=yes-else- GCC=-fi-ac_test_CFLAGS=${CFLAGS+set}-ac_save_CFLAGS=$CFLAGS-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5-$as_echo_n "checking whether $CC accepts -g... " >&6; }-if ${ac_cv_prog_cc_g+:} false; then :- $as_echo_n "(cached) " >&6-else- ac_save_c_werror_flag=$ac_c_werror_flag- ac_c_werror_flag=yes- ac_cv_prog_cc_g=no- CFLAGS="-g"- cat confdefs.h - <<_ACEOF >conftest.$ac_ext-/* end confdefs.h. */--int-main ()-{-- ;- return 0;-}-_ACEOF-if ac_fn_c_try_compile "$LINENO"; then :- ac_cv_prog_cc_g=yes-else- CFLAGS=""- cat confdefs.h - <<_ACEOF >conftest.$ac_ext-/* end confdefs.h. */--int-main ()-{-- ;- return 0;-}-_ACEOF-if ac_fn_c_try_compile "$LINENO"; then :--else- ac_c_werror_flag=$ac_save_c_werror_flag- CFLAGS="-g"- cat confdefs.h - <<_ACEOF >conftest.$ac_ext-/* end confdefs.h. */--int-main ()-{-- ;- return 0;-}-_ACEOF-if ac_fn_c_try_compile "$LINENO"; then :- ac_cv_prog_cc_g=yes-fi-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext-fi-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext-fi-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext- ac_c_werror_flag=$ac_save_c_werror_flag-fi-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5-$as_echo "$ac_cv_prog_cc_g" >&6; }-if test "$ac_test_CFLAGS" = set; then- CFLAGS=$ac_save_CFLAGS-elif test $ac_cv_prog_cc_g = yes; then- if test "$GCC" = yes; then- CFLAGS="-g -O2"- else- CFLAGS="-g"- fi-else- if test "$GCC" = yes; then- CFLAGS="-O2"- else- CFLAGS=- fi-fi-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5-$as_echo_n "checking for $CC option to accept ISO C89... " >&6; }-if ${ac_cv_prog_cc_c89+:} false; then :- $as_echo_n "(cached) " >&6-else- ac_cv_prog_cc_c89=no-ac_save_CC=$CC-cat confdefs.h - <<_ACEOF >conftest.$ac_ext-/* end confdefs.h. */-#include <stdarg.h>-#include <stdio.h>-#include <sys/types.h>-#include <sys/stat.h>-/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */-struct buf { int x; };-FILE * (*rcsopen) (struct buf *, struct stat *, int);-static char *e (p, i)- char **p;- int i;-{- return p[i];-}-static char *f (char * (*g) (char **, int), char **p, ...)-{- char *s;- va_list v;- va_start (v,p);- s = g (p, va_arg (v,int));- va_end (v);- return s;-}--/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has- function prototypes and stuff, but not '\xHH' hex character constants.- These don't provoke an error unfortunately, instead are silently treated- as 'x'. The following induces an error, until -std is added to get- proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an- array size at least. It's necessary to write '\x00'==0 to get something- that's true only with -std. */-int osf4_cc_array ['\x00' == 0 ? 1 : -1];--/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters- inside strings and character constants. */-#define FOO(x) 'x'-int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1];--int test (int i, double x);-struct s1 {int (*f) (int a);};-struct s2 {int (*f) (double a);};-int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int);-int argc;-char **argv;-int-main ()-{-return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1];- ;- return 0;-}-_ACEOF-for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \- -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__"-do- CC="$ac_save_CC $ac_arg"- if ac_fn_c_try_compile "$LINENO"; then :- ac_cv_prog_cc_c89=$ac_arg-fi-rm -f core conftest.err conftest.$ac_objext- test "x$ac_cv_prog_cc_c89" != "xno" && break-done-rm -f conftest.$ac_ext-CC=$ac_save_CC--fi-# AC_CACHE_VAL-case "x$ac_cv_prog_cc_c89" in- x)- { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5-$as_echo "none needed" >&6; } ;;- xno)- { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5-$as_echo "unsupported" >&6; } ;;- *)- CC="$CC $ac_cv_prog_cc_c89"- { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5-$as_echo "$ac_cv_prog_cc_c89" >&6; } ;;-esac-if test "x$ac_cv_prog_cc_c89" != xno; then :--fi--ac_ext=c-ac_cpp='$CPP $CPPFLAGS'-ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'-ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'-ac_compiler_gnu=$ac_cv_c_compiler_gnu---ac_config_headers="$ac_config_headers include/HsTmpConfig.h"---# Temp functions--for ac_func in mkstemp mkstemps mkdtemp-do :- as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`-ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var"-if eval test \"x\$"$as_ac_var"\" = x"yes"; then :- cat >>confdefs.h <<_ACEOF-#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1-_ACEOF--fi-done---cat >confcache <<\_ACEOF-# This file is a shell script that caches the results of configure-# tests run on this system so they can be shared between configure-# scripts and configure runs, see configure's option --config-cache.-# It is not useful on other systems. If it contains results you don't-# want to keep, you may remove or edit it.-#-# config.status only pays attention to the cache file if you give it-# the --recheck option to rerun configure.-#-# `ac_cv_env_foo' variables (set or unset) will be overridden when-# loading this file, other *unset* `ac_cv_foo' will be assigned the-# following values.--_ACEOF--# The following way of writing the cache mishandles newlines in values,-# but we know of no workaround that is simple, portable, and efficient.-# So, we kill variables containing newlines.-# Ultrix sh set writes to stderr and can't be redirected directly,-# and sets the high bit in the cache file unless we assign to the vars.-(- for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do- eval ac_val=\$$ac_var- case $ac_val in #(- *${as_nl}*)- case $ac_var in #(- *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5-$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;;- esac- case $ac_var in #(- _ | IFS | as_nl) ;; #(- BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #(- *) { eval $ac_var=; unset $ac_var;} ;;- esac ;;- esac- done-- (set) 2>&1 |- case $as_nl`(ac_space=' '; set) 2>&1` in #(- *${as_nl}ac_space=\ *)- # `set' does not quote correctly, so add quotes: double-quote- # substitution turns \\\\ into \\, and sed turns \\ into \.- sed -n \- "s/'/'\\\\''/g;- s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p"- ;; #(- *)- # `set' quotes correctly as required by POSIX, so do not add quotes.- sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p"- ;;- esac |- sort-) |- sed '- /^ac_cv_env_/b end- t clear- :clear- s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/- t end- s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/- :end' >>confcache-if diff "$cache_file" confcache >/dev/null 2>&1; then :; else- if test -w "$cache_file"; then- if test "x$cache_file" != "x/dev/null"; then- { $as_echo "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5-$as_echo "$as_me: updating cache $cache_file" >&6;}- if test ! -f "$cache_file" || test -h "$cache_file"; then- cat confcache >"$cache_file"- else- case $cache_file in #(- */* | ?:*)- mv -f confcache "$cache_file"$$ &&- mv -f "$cache_file"$$ "$cache_file" ;; #(- *)- mv -f confcache "$cache_file" ;;- esac- fi- fi- else- { $as_echo "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5-$as_echo "$as_me: not updating unwritable cache $cache_file" >&6;}- fi-fi-rm -f confcache--test "x$prefix" = xNONE && prefix=$ac_default_prefix-# Let make expand exec_prefix.-test "x$exec_prefix" = xNONE && exec_prefix='${prefix}'--DEFS=-DHAVE_CONFIG_H--ac_libobjs=-ac_ltlibobjs=-U=-for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue- # 1. Remove the extension, and $U if already installed.- ac_script='s/\$U\././;s/\.o$//;s/\.obj$//'- ac_i=`$as_echo "$ac_i" | sed "$ac_script"`- # 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR- # will be set to the directory where LIBOBJS objects are built.- as_fn_append ac_libobjs " \${LIBOBJDIR}$ac_i\$U.$ac_objext"- as_fn_append ac_ltlibobjs " \${LIBOBJDIR}$ac_i"'$U.lo'-done-LIBOBJS=$ac_libobjs--LTLIBOBJS=$ac_ltlibobjs----: "${CONFIG_STATUS=./config.status}"-ac_write_fail=0-ac_clean_files_save=$ac_clean_files-ac_clean_files="$ac_clean_files $CONFIG_STATUS"-{ $as_echo "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5-$as_echo "$as_me: creating $CONFIG_STATUS" >&6;}-as_write_fail=0-cat >$CONFIG_STATUS <<_ASEOF || as_write_fail=1-#! $SHELL-# Generated by $as_me.-# Run this file to recreate the current configuration.-# Compiler output produced by configure, useful for debugging-# configure, is in config.log if it exists.--debug=false-ac_cs_recheck=false-ac_cs_silent=false--SHELL=\${CONFIG_SHELL-$SHELL}-export SHELL-_ASEOF-cat >>$CONFIG_STATUS <<\_ASEOF || as_write_fail=1-## -------------------- ##-## M4sh Initialization. ##-## -------------------- ##--# Be more Bourne compatible-DUALCASE=1; export DUALCASE # for MKS sh-if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then :- emulate sh- NULLCMD=:- # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which- # is contrary to our usage. Disable this feature.- alias -g '${1+"$@"}'='"$@"'- setopt NO_GLOB_SUBST-else- case `(set -o) 2>/dev/null` in #(- *posix*) :- set -o posix ;; #(- *) :- ;;-esac-fi---as_nl='-'-export as_nl-# Printing a long string crashes Solaris 7 /usr/bin/printf.-as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'-as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo-as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo-# Prefer a ksh shell builtin over an external printf program on Solaris,-# but without wasting forks for bash or zsh.-if test -z "$BASH_VERSION$ZSH_VERSION" \- && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then- as_echo='print -r --'- as_echo_n='print -rn --'-elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then- as_echo='printf %s\n'- as_echo_n='printf %s'-else- if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then- as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"'- as_echo_n='/usr/ucb/echo -n'- else- as_echo_body='eval expr "X$1" : "X\\(.*\\)"'- as_echo_n_body='eval- arg=$1;- case $arg in #(- *"$as_nl"*)- expr "X$arg" : "X\\(.*\\)$as_nl";- arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;;- esac;- expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl"- '- export as_echo_n_body- as_echo_n='sh -c $as_echo_n_body as_echo'- fi- export as_echo_body- as_echo='sh -c $as_echo_body as_echo'-fi--# The user is always right.-if test "${PATH_SEPARATOR+set}" != set; then- PATH_SEPARATOR=:- (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && {- (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 ||- PATH_SEPARATOR=';'- }-fi---# IFS-# We need space, tab and new line, in precisely that order. Quoting is-# there to prevent editors from complaining about space-tab.-# (If _AS_PATH_WALK were called with IFS unset, it would disable word-# splitting by setting IFS to empty value.)-IFS=" "" $as_nl"--# Find who we are. Look in the path if we contain no directory separator.-as_myself=-case $0 in #((- *[\\/]* ) as_myself=$0 ;;- *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR-for as_dir in $PATH-do- IFS=$as_save_IFS- test -z "$as_dir" && as_dir=.- test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break- done-IFS=$as_save_IFS-- ;;-esac-# We did not find ourselves, most probably we were run as `sh COMMAND'-# in which case we are not to be found in the path.-if test "x$as_myself" = x; then- as_myself=$0-fi-if test ! -f "$as_myself"; then- $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2- exit 1-fi--# Unset variables that we do not need and which cause bugs (e.g. in-# pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1"-# suppresses any "Segmentation fault" message there. '((' could-# trigger a bug in pdksh 5.2.14.-for as_var in BASH_ENV ENV MAIL MAILPATH-do eval test x\${$as_var+set} = xset \- && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || :-done-PS1='$ '-PS2='> '-PS4='+ '--# NLS nuisances.-LC_ALL=C-export LC_ALL-LANGUAGE=C-export LANGUAGE--# CDPATH.-(unset CDPATH) >/dev/null 2>&1 && unset CDPATH---# as_fn_error STATUS ERROR [LINENO LOG_FD]-# -----------------------------------------# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are-# provided, also output the error to LOG_FD, referencing LINENO. Then exit the-# script with STATUS, using 1 if that was 0.-as_fn_error ()-{- as_status=$1; test $as_status -eq 0 && as_status=1- if test "$4"; then- as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack- $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4- fi- $as_echo "$as_me: error: $2" >&2- as_fn_exit $as_status-} # as_fn_error---# as_fn_set_status STATUS-# ------------------------# Set $? to STATUS, without forking.-as_fn_set_status ()-{- return $1-} # as_fn_set_status--# as_fn_exit STATUS-# ------------------# Exit the shell with STATUS, even in a "trap 0" or "set -e" context.-as_fn_exit ()-{- set +e- as_fn_set_status $1- exit $1-} # as_fn_exit--# as_fn_unset VAR-# ----------------# Portably unset VAR.-as_fn_unset ()-{- { eval $1=; unset $1;}-}-as_unset=as_fn_unset-# as_fn_append VAR VALUE-# -----------------------# Append the text in VALUE to the end of the definition contained in VAR. Take-# advantage of any shell optimizations that allow amortized linear growth over-# repeated appends, instead of the typical quadratic growth present in naive-# implementations.-if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then :- eval 'as_fn_append ()- {- eval $1+=\$2- }'-else- as_fn_append ()- {- eval $1=\$$1\$2- }-fi # as_fn_append--# as_fn_arith ARG...-# -------------------# Perform arithmetic evaluation on the ARGs, and store the result in the-# global $as_val. Take advantage of shells that can avoid forks. The arguments-# must be portable across $(()) and expr.-if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then :- eval 'as_fn_arith ()- {- as_val=$(( $* ))- }'-else- as_fn_arith ()- {- as_val=`expr "$@" || test $? -eq 1`- }-fi # as_fn_arith---if expr a : '\(a\)' >/dev/null 2>&1 &&- test "X`expr 00001 : '.*\(...\)'`" = X001; then- as_expr=expr-else- as_expr=false-fi--if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then- as_basename=basename-else- as_basename=false-fi--if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then- as_dirname=dirname-else- as_dirname=false-fi--as_me=`$as_basename -- "$0" ||-$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \- X"$0" : 'X\(//\)$' \| \- X"$0" : 'X\(/\)' \| . 2>/dev/null ||-$as_echo X/"$0" |- sed '/^.*\/\([^/][^/]*\)\/*$/{- s//\1/- q- }- /^X\/\(\/\/\)$/{- s//\1/- q- }- /^X\/\(\/\).*/{- s//\1/- q- }- s/.*/./; q'`--# Avoid depending upon Character Ranges.-as_cr_letters='abcdefghijklmnopqrstuvwxyz'-as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'-as_cr_Letters=$as_cr_letters$as_cr_LETTERS-as_cr_digits='0123456789'-as_cr_alnum=$as_cr_Letters$as_cr_digits--ECHO_C= ECHO_N= ECHO_T=-case `echo -n x` in #(((((--n*)- case `echo 'xy\c'` in- *c*) ECHO_T=' ';; # ECHO_T is single tab character.- xy) ECHO_C='\c';;- *) echo `echo ksh88 bug on AIX 6.1` > /dev/null- ECHO_T=' ';;- esac;;-*)- ECHO_N='-n';;-esac--rm -f conf$$ conf$$.exe conf$$.file-if test -d conf$$.dir; then- rm -f conf$$.dir/conf$$.file-else- rm -f conf$$.dir- mkdir conf$$.dir 2>/dev/null-fi-if (echo >conf$$.file) 2>/dev/null; then- if ln -s conf$$.file conf$$ 2>/dev/null; then- as_ln_s='ln -s'- # ... but there are two gotchas:- # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail.- # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable.- # In both cases, we have to default to `cp -p'.- ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe ||- as_ln_s='cp -p'- elif ln conf$$.file conf$$ 2>/dev/null; then- as_ln_s=ln- else- as_ln_s='cp -p'- fi-else- as_ln_s='cp -p'-fi-rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file-rmdir conf$$.dir 2>/dev/null---# as_fn_mkdir_p-# --------------# Create "$as_dir" as a directory, including parents if necessary.-as_fn_mkdir_p ()-{-- case $as_dir in #(- -*) as_dir=./$as_dir;;- esac- test -d "$as_dir" || eval $as_mkdir_p || {- as_dirs=- while :; do- case $as_dir in #(- *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'(- *) as_qdir=$as_dir;;- esac- as_dirs="'$as_qdir' $as_dirs"- as_dir=`$as_dirname -- "$as_dir" ||-$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \- X"$as_dir" : 'X\(//\)[^/]' \| \- X"$as_dir" : 'X\(//\)$' \| \- X"$as_dir" : 'X\(/\)' \| . 2>/dev/null ||-$as_echo X"$as_dir" |- sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{- s//\1/- q- }- /^X\(\/\/\)[^/].*/{- s//\1/- q- }- /^X\(\/\/\)$/{- s//\1/- q- }- /^X\(\/\).*/{- s//\1/- q- }- s/.*/./; q'`- test -d "$as_dir" && break- done- test -z "$as_dirs" || eval "mkdir $as_dirs"- } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir"---} # as_fn_mkdir_p-if mkdir -p . 2>/dev/null; then- as_mkdir_p='mkdir -p "$as_dir"'-else- test -d ./-p && rmdir ./-p- as_mkdir_p=false-fi--if test -x / >/dev/null 2>&1; then- as_test_x='test -x'-else- if ls -dL / >/dev/null 2>&1; then- as_ls_L_option=L- else- as_ls_L_option=- fi- as_test_x='- eval sh -c '\''- if test -d "$1"; then- test -d "$1/.";- else- case $1 in #(- -*)set "./$1";;- esac;- case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in #((- ???[sx]*):;;*)false;;esac;fi- '\'' sh- '-fi-as_executable_p=$as_test_x--# Sed expression to map a string onto a valid CPP name.-as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"--# Sed expression to map a string onto a valid variable name.-as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"---exec 6>&1-## ----------------------------------- ##-## Main body of $CONFIG_STATUS script. ##-## ----------------------------------- ##-_ASEOF-test $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1--cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1-# Save the log message, to keep $0 and so on meaningful, and to-# report actual input values of CONFIG_FILES etc. instead of their-# values after options handling.-ac_log="-This file was extended by Labeled IO library $as_me 0.1.0, which was-generated by GNU Autoconf 2.68. Invocation command line was-- CONFIG_FILES = $CONFIG_FILES- CONFIG_HEADERS = $CONFIG_HEADERS- CONFIG_LINKS = $CONFIG_LINKS- CONFIG_COMMANDS = $CONFIG_COMMANDS- $ $0 $@--on `(hostname || uname -n) 2>/dev/null | sed 1q`-"--_ACEOF---case $ac_config_headers in *"-"*) set x $ac_config_headers; shift; ac_config_headers=$*;;-esac---cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1-# Files that config.status was made for.-config_headers="$ac_config_headers"--_ACEOF--cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1-ac_cs_usage="\-\`$as_me' instantiates files and other configuration actions-from templates according to the current configuration. Unless the files-and actions are specified as TAGs, all are instantiated by default.--Usage: $0 [OPTION]... [TAG]...-- -h, --help print this help, then exit- -V, --version print version number and configuration settings, then exit- --config print configuration, then exit- -q, --quiet, --silent- do not print progress messages- -d, --debug don't remove temporary files- --recheck update $as_me by reconfiguring in the same conditions- --header=FILE[:TEMPLATE]- instantiate the configuration header FILE--Configuration headers:-$config_headers--Report bugs to <deian@cs.stanford.edu>."--_ACEOF-cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1-ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"-ac_cs_version="\\-Labeled IO library config.status 0.1.0-configured by $0, generated by GNU Autoconf 2.68,- with options \\"\$ac_cs_config\\"--Copyright (C) 2010 Free Software Foundation, Inc.-This config.status script is free software; the Free Software Foundation-gives unlimited permission to copy, distribute and modify it."--ac_pwd='$ac_pwd'-srcdir='$srcdir'-test -n "\$AWK" || AWK=awk-_ACEOF--cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1-# The default lists apply if the user does not specify any file.-ac_need_defaults=:-while test $# != 0-do- case $1 in- --*=?*)- ac_option=`expr "X$1" : 'X\([^=]*\)='`- ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'`- ac_shift=:- ;;- --*=)- ac_option=`expr "X$1" : 'X\([^=]*\)='`- ac_optarg=- ac_shift=:- ;;- *)- ac_option=$1- ac_optarg=$2- ac_shift=shift- ;;- esac-- case $ac_option in- # Handling of the options.- -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r)- ac_cs_recheck=: ;;- --version | --versio | --versi | --vers | --ver | --ve | --v | -V )- $as_echo "$ac_cs_version"; exit ;;- --config | --confi | --conf | --con | --co | --c )- $as_echo "$ac_cs_config"; exit ;;- --debug | --debu | --deb | --de | --d | -d )- debug=: ;;- --header | --heade | --head | --hea )- $ac_shift- case $ac_optarg in- *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;;- esac- as_fn_append CONFIG_HEADERS " '$ac_optarg'"- ac_need_defaults=false;;- --he | --h)- # Conflict between --help and --header- as_fn_error $? "ambiguous option: \`$1'-Try \`$0 --help' for more information.";;- --help | --hel | -h )- $as_echo "$ac_cs_usage"; exit ;;- -q | -quiet | --quiet | --quie | --qui | --qu | --q \- | -silent | --silent | --silen | --sile | --sil | --si | --s)- ac_cs_silent=: ;;-- # This is an error.- -*) as_fn_error $? "unrecognized option: \`$1'-Try \`$0 --help' for more information." ;;-- *) as_fn_append ac_config_targets " $1"- ac_need_defaults=false ;;-- esac- shift-done--ac_configure_extra_args=--if $ac_cs_silent; then- exec 6>/dev/null- ac_configure_extra_args="$ac_configure_extra_args --silent"-fi--_ACEOF-cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1-if \$ac_cs_recheck; then- set X '$SHELL' '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion- shift- \$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6- CONFIG_SHELL='$SHELL'- export CONFIG_SHELL- exec "\$@"-fi--_ACEOF-cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1-exec 5>>config.log-{- echo- sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX-## Running $as_me. ##-_ASBOX- $as_echo "$ac_log"-} >&5--_ACEOF-cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1-_ACEOF--cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1--# Handling of arguments.-for ac_config_target in $ac_config_targets-do- case $ac_config_target in- "include/HsTmpConfig.h") CONFIG_HEADERS="$CONFIG_HEADERS include/HsTmpConfig.h" ;;-- *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;;- esac-done---# If the user did not use the arguments to specify the items to instantiate,-# then the envvar interface is used. Set only those that are not.-# We use the long form for the default assignment because of an extremely-# bizarre bug on SunOS 4.1.3.-if $ac_need_defaults; then- test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers-fi--# Have a temporary directory for convenience. Make it in the build tree-# simply because there is no reason against having it here, and in addition,-# creating and moving files from /tmp can sometimes cause problems.-# Hook for its removal unless debugging.-# Note that there is a small window in which the directory will not be cleaned:-# after its creation but before its name has been assigned to `$tmp'.-$debug ||-{- tmp= ac_tmp=- trap 'exit_status=$?- : "${ac_tmp:=$tmp}"- { test ! -d "$ac_tmp" || rm -fr "$ac_tmp"; } && exit $exit_status-' 0- trap 'as_fn_exit 1' 1 2 13 15-}-# Create a (secure) tmp directory for tmp files.--{- tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` &&- test -d "$tmp"-} ||-{- tmp=./conf$$-$RANDOM- (umask 077 && mkdir "$tmp")-} || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5-ac_tmp=$tmp--# Set up the scripts for CONFIG_HEADERS section.-# No need to generate them if there are no CONFIG_HEADERS.-# This happens for instance with `./config.status Makefile'.-if test -n "$CONFIG_HEADERS"; then-cat >"$ac_tmp/defines.awk" <<\_ACAWK ||-BEGIN {-_ACEOF--# Transform confdefs.h into an awk script `defines.awk', embedded as-# here-document in config.status, that substitutes the proper values into-# config.h.in to produce config.h.--# Create a delimiter string that does not exist in confdefs.h, to ease-# handling of long lines.-ac_delim='%!_!# '-for ac_last_try in false false :; do- ac_tt=`sed -n "/$ac_delim/p" confdefs.h`- if test -z "$ac_tt"; then- break- elif $ac_last_try; then- as_fn_error $? "could not make $CONFIG_HEADERS" "$LINENO" 5- else- ac_delim="$ac_delim!$ac_delim _$ac_delim!! "- fi-done--# For the awk script, D is an array of macro values keyed by name,-# likewise P contains macro parameters if any. Preserve backslash-# newline sequences.--ac_word_re=[_$as_cr_Letters][_$as_cr_alnum]*-sed -n '-s/.\{148\}/&'"$ac_delim"'/g-t rset-:rset-s/^[ ]*#[ ]*define[ ][ ]*/ /-t def-d-:def-s/\\$//-t bsnl-s/["\\]/\\&/g-s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\-D["\1"]=" \3"/p-s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2"/p-d-:bsnl-s/["\\]/\\&/g-s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\-D["\1"]=" \3\\\\\\n"\\/p-t cont-s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2\\\\\\n"\\/p-t cont-d-:cont-n-s/.\{148\}/&'"$ac_delim"'/g-t clear-:clear-s/\\$//-t bsnlc-s/["\\]/\\&/g; s/^/"/; s/$/"/p-d-:bsnlc-s/["\\]/\\&/g; s/^/"/; s/$/\\\\\\n"\\/p-b cont-' <confdefs.h | sed '-s/'"$ac_delim"'/"\\\-"/g' >>$CONFIG_STATUS || ac_write_fail=1--cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1- for (key in D) D_is_set[key] = 1- FS = ""-}-/^[\t ]*#[\t ]*(define|undef)[\t ]+$ac_word_re([\t (]|\$)/ {- line = \$ 0- split(line, arg, " ")- if (arg[1] == "#") {- defundef = arg[2]- mac1 = arg[3]- } else {- defundef = substr(arg[1], 2)- mac1 = arg[2]- }- split(mac1, mac2, "(") #)- macro = mac2[1]- prefix = substr(line, 1, index(line, defundef) - 1)- if (D_is_set[macro]) {- # Preserve the white space surrounding the "#".- print prefix "define", macro P[macro] D[macro]- next- } else {- # Replace #undef with comments. This is necessary, for example,- # in the case of _POSIX_SOURCE, which is predefined and required- # on some systems where configure will not decide to define it.- if (defundef == "undef") {- print "/*", prefix defundef, macro, "*/"- next- }- }-}-{ print }-_ACAWK-_ACEOF-cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1- as_fn_error $? "could not setup config headers machinery" "$LINENO" 5-fi # test -n "$CONFIG_HEADERS"---eval set X " :H $CONFIG_HEADERS "-shift-for ac_tag-do- case $ac_tag in- :[FHLC]) ac_mode=$ac_tag; continue;;- esac- case $ac_mode$ac_tag in- :[FHL]*:*);;- :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;;- :[FH]-) ac_tag=-:-;;- :[FH]*) ac_tag=$ac_tag:$ac_tag.in;;- esac- ac_save_IFS=$IFS- IFS=:- set x $ac_tag- IFS=$ac_save_IFS- shift- ac_file=$1- shift-- case $ac_mode in- :L) ac_source=$1;;- :[FH])- ac_file_inputs=- for ac_f- do- case $ac_f in- -) ac_f="$ac_tmp/stdin";;- *) # Look for the file first in the build tree, then in the source tree- # (if the path is not absolute). The absolute path cannot be DOS-style,- # because $ac_f cannot contain `:'.- test -f "$ac_f" ||- case $ac_f in- [\\/$]*) false;;- *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";;- esac ||- as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;;- esac- case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac- as_fn_append ac_file_inputs " '$ac_f'"- done-- # Let's still pretend it is `configure' which instantiates (i.e., don't- # use $as_me), people would be surprised to read:- # /* config.h. Generated by config.status. */- configure_input='Generated from '`- $as_echo "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g'- `' by configure.'- if test x"$ac_file" != x-; then- configure_input="$ac_file. $configure_input"- { $as_echo "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5-$as_echo "$as_me: creating $ac_file" >&6;}- fi- # Neutralize special characters interpreted by sed in replacement strings.- case $configure_input in #(- *\&* | *\|* | *\\* )- ac_sed_conf_input=`$as_echo "$configure_input" |- sed 's/[\\\\&|]/\\\\&/g'`;; #(- *) ac_sed_conf_input=$configure_input;;- esac-- case $ac_tag in- *:-:* | *:-) cat >"$ac_tmp/stdin" \- || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;;- esac- ;;- esac-- ac_dir=`$as_dirname -- "$ac_file" ||-$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \- X"$ac_file" : 'X\(//\)[^/]' \| \- X"$ac_file" : 'X\(//\)$' \| \- X"$ac_file" : 'X\(/\)' \| . 2>/dev/null ||-$as_echo X"$ac_file" |- sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{- s//\1/- q- }- /^X\(\/\/\)[^/].*/{- s//\1/- q- }- /^X\(\/\/\)$/{- s//\1/- q- }- /^X\(\/\).*/{- s//\1/- q- }- s/.*/./; q'`- as_dir="$ac_dir"; as_fn_mkdir_p- ac_builddir=.--case "$ac_dir" in-.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;;-*)- ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'`- # A ".." for each directory in $ac_dir_suffix.- ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'`- case $ac_top_builddir_sub in- "") ac_top_builddir_sub=. ac_top_build_prefix= ;;- *) ac_top_build_prefix=$ac_top_builddir_sub/ ;;- esac ;;-esac-ac_abs_top_builddir=$ac_pwd-ac_abs_builddir=$ac_pwd$ac_dir_suffix-# for backward compatibility:-ac_top_builddir=$ac_top_build_prefix--case $srcdir in- .) # We are building in place.- ac_srcdir=.- ac_top_srcdir=$ac_top_builddir_sub- ac_abs_top_srcdir=$ac_pwd ;;- [\\/]* | ?:[\\/]* ) # Absolute name.- ac_srcdir=$srcdir$ac_dir_suffix;- ac_top_srcdir=$srcdir- ac_abs_top_srcdir=$srcdir ;;- *) # Relative name.- ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix- ac_top_srcdir=$ac_top_build_prefix$srcdir- ac_abs_top_srcdir=$ac_pwd/$srcdir ;;-esac-ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix--- case $ac_mode in-- :H)- #- # CONFIG_HEADER- #- if test x"$ac_file" != x-; then- {- $as_echo "/* $configure_input */" \- && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs"- } >"$ac_tmp/config.h" \- || as_fn_error $? "could not create $ac_file" "$LINENO" 5- if diff "$ac_file" "$ac_tmp/config.h" >/dev/null 2>&1; then- { $as_echo "$as_me:${as_lineno-$LINENO}: $ac_file is unchanged" >&5-$as_echo "$as_me: $ac_file is unchanged" >&6;}- else- rm -f "$ac_file"- mv "$ac_tmp/config.h" "$ac_file" \- || as_fn_error $? "could not create $ac_file" "$LINENO" 5- fi- else- $as_echo "/* $configure_input */" \- && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" \- || as_fn_error $? "could not create -" "$LINENO" 5- fi- ;;--- esac--done # for ac_tag---as_fn_exit 0-_ACEOF-ac_clean_files=$ac_clean_files_save--test $ac_write_fail = 0 ||- as_fn_error $? "write failure creating $CONFIG_STATUS" "$LINENO" 5---# configure is writing to config.log, and then calls config.status.-# config.status does its own redirection, appending to config.log.-# Unfortunately, on DOS this fails, as config.log is still kept open-# by configure, so config.status won't be able to write to it; its-# output is simply discarded. So we exec the FD to /dev/null,-# effectively closing config.log, so it can be properly (re)opened and-# appended to by config.status. When coming back to configure, we-# need to make the FD available again.-if test "$no_create" != yes; then- ac_cs_success=:- ac_config_status_args=- test "$silent" = yes &&- ac_config_status_args="$ac_config_status_args --quiet"- exec 5>/dev/null- $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false- exec 5>>config.log- # Use ||, not &&, to avoid exiting from the if with $? = 1, which- # would make configure fail if this is the last instruction.- $ac_cs_success || as_fn_exit 1-fi-if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then- { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5-$as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;}-fi-
− configure.ac
@@ -1,13 +0,0 @@-AC_INIT([Labeled IO library], [0.1.0], [deian@cs.stanford.edu], [lio])--# Safety check: Ensure that we are in the correct source directory.-AC_CONFIG_SRCDIR([include/HsTmp.h])--AC_PROG_CC--AC_CONFIG_HEADERS([include/HsTmpConfig.h])--# Temp functions-AC_CHECK_FUNCS([mkstemp mkstemps mkdtemp])--AC_OUTPUT
− examples/LambdaChair/AliceCode.hs
@@ -1,25 +0,0 @@-{-# LANGUAGE CPP #-}-#if __GLASGOW_HASKELL__ >= 702-{-# LANGUAGE Safe #-}-#endif-module AliceCode ( mainReview ) where--import Safe--findPaper' s = do- ep <- findPaper s- case ep of- Left e -> error $ "Failed with" ++ e- Right p -> return p--mainReview = do- p1 <- findPaper' "Flexible Dynamic"- p2 <- findPaper' "A Static"-- readPaper p1-- appendToReview p1 "Interesting work!"-- readPaper p2- readReview p2- appendToReview p2 "What about adding new users?"
− examples/LambdaChair/BobCode.hs
@@ -1,20 +0,0 @@-{-# LANGUAGE CPP #-}-#if __GLASGOW_HASKELL__ >= 702-{-# LANGUAGE Safe #-}-#endif-module BobCode ( mainReview ) where--import Safe--findPaper' s = do- ep <- findPaper s- case ep of- Left e -> error $ "Failed with" ++ e- Right p -> return p--mainReview = do- p1 <- findPaper' "Flexible Dynamic..."- p2 <- findPaper' "A Static..."- appendToReview p2 "Hmm, IFC.."- readReview p2- readReview p1
− examples/LambdaChair/LambdaChair.hs
@@ -1,419 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE GeneralizedNewtypeDeriving #-}-{-# LANGUAGE ScopedTypeVariables #-}-module LambdaChair ( evalReviewDC- , addUser- , addPaper- , addConflict- , addAssignment- , asUser- ---- , findPaper- , retrievePaper, readPaper- , retrieveReview, readReview- , appendToReview- , reviewDCPutStrLn - -- TCB- , printUsersTCB- , printReviewsTCB- , reviewDCPutStrLnTCB - , dcPutStrLnTCB - ) where--import Prelude hiding (catch)-import Control.Monad-import Control.Exception (SomeException, ErrorCall(..))-import Control.Monad.State-import Data.Maybe-import Data.List--import LIO.TCB-import LIO.LIORef-import LIO.LIORef.TCB (readLIORefTCB)-import LIO.MonadLIO-import LIO.MonadCatch (throwIO)-import LIO.DCLabel hiding (name)-import DCLabel.Safe hiding (name)-import DCLabel.TCB (Disj(..), Conj(..), Component(..))-import DCLabel.PrettyShow--import qualified Data.ByteString.Char8 as C---type ErrorStr = String-type DCLabeled = Labeled DCLabel-type DCRef = LIORef DCLabel---- ^ Clss to with sideffectful show-class DCShowTCB s where- dcShowTCB :: s -> DC String--dcPutStrLnTCB :: String -> DC ()-dcPutStrLnTCB = ioTCB . putStrLn--dcGetLineTCB :: DC String-dcGetLineTCB = ioTCB getLine--type Name = String-type Password = String-type Content = String-type Reviews = String--type Id = Int-data Paper = Paper Content-data Review = Review Content--data User = User { name :: Name- , password :: Password- , conflicts :: [Id]- , assignments :: [Id] }--instance Eq User where- u1 == u2 = name u1 == name u2--instance DCShowTCB User where- dcShowTCB u = do- return $ "Name: " ++ (name u) ++ "\n"- ++ "Password: " ++ (password u) ++ "\n"- ++ "Conflicts: " ++ (show . conflicts $ u) ++ "\n"- ++ "Assignments: " ++ (show . assignments $ u)--data ReviewEnt = ReviewEnt { paperId :: Id- , paper :: DCRef Paper- , review :: DCRef Review }--instance Eq ReviewEnt where- r1 == r2 = paperId r1 == paperId r2--instance DCShowTCB ReviewEnt where- dcShowTCB r = do- (Paper pap) <- readLIORefTCB (paper r)- (Review rev) <- readLIORefTCB (review r)- return $ "ID:" ++ (show . paperId $ r)- ++ "\nPaper:" ++ pap- ++ "\nReviews:" ++ rev----- State related-data ReviewState = ReviewState { users :: [User]- , reviewEntries :: [ReviewEnt]- , curUser :: Maybe Name }--emptyReviewState :: ReviewState-emptyReviewState = ReviewState [] [] Nothing--newtype ReviewDC a = ReviewDC (StateT ReviewState DC a)- deriving (Monad)--liftReviewDC :: DC a -> ReviewDC a-liftReviewDC = ReviewDC . liftLIO--get' :: ReviewDC ReviewState-get' = ReviewDC . StateT $ \s -> return (s,s)--put' :: ReviewState -> ReviewDC ()-put' s = ReviewDC . StateT $ \_ -> return ((),s)--runReviewDC :: ReviewDC a -> ReviewState -> DC (a, ReviewState)-runReviewDC (ReviewDC m) s = runStateT m s--evalReviewDC :: ReviewDC a -> IO (a, DCLabel)-evalReviewDC m = evalDC $ do- (a, s') <- runReviewDC m emptyReviewState- return a------- ^ Get all users-getUsers :: ReviewDC [User]-getUsers = get' >>= return . users---- ^ Get all review entries-getReviews :: ReviewDC [ReviewEnt]-getReviews = get' >>= return . reviewEntries---- ^ Get priviliges-getCurUserName :: ReviewDC (Maybe Name)-getCurUserName = get' >>= return . curUser---- ^ Get curren tuser name-getCurUser :: ReviewDC (Maybe User)-getCurUser = do- n <- getCurUserName- maybe (return Nothing) findUser n---- ^ Get priviliges-getPrivs :: ReviewDC DCPrivTCB-getPrivs = do - u <- getCurUser- return $ maybe noPrivs (genPrivTCB . name) u---- ^ Write new users-putUsers :: [User] -> ReviewDC ()-putUsers us = do- rs <- getReviews- u <- getCurUserName- put' $ ReviewState us rs u---- ^ Write new reviews-putReviews :: [ReviewEnt] -> ReviewDC ()-putReviews rs = do- us <- getUsers- u <- getCurUserName- put' $ ReviewState us rs u---- ^ Write new privs-putCurUserName :: Name -> ReviewDC ()-putCurUserName u = do- us <- getUsers- rs <- getReviews- put' $ ReviewState us rs (Just u)---- ^ Clear user naem-clearCurUserName :: ReviewDC ()-clearCurUserName = do- us <- getUsers- rs <- getReviews- put' $ ReviewState us rs Nothing---- ^ Find review entry by id-findReview :: Id -> ReviewDC (Maybe ReviewEnt)-findReview pId = do- reviews <- getReviews- return $ find (\e -> paperId e == pId) reviews---- ^ Find user by name-findUser :: Name -> ReviewDC (Maybe User)-findUser n = do- users <- getUsers- return $ find (\u -> name u == n) users ---- ^ Add new (fresh) user-addUser :: Name -> Password -> ReviewDC ()-addUser n p = do- u <- findUser n- unless (isJust u) $ do- let newUser = User { name = n- , password = p- , conflicts = []- , assignments = [] }- us <- getUsers- putUsers (newUser:us)---- ^ Add conflicting paper to user-addConflict :: Name -> Id -> ReviewDC ()-addConflict n i = do- usr <- findUser n- pap <- findReview i- case (usr, pap) of- (Just u, Just _) -> - if i `elem` (assignments u)- then return ()- else do let u' = u { conflicts = i : (conflicts u)}- usrs <- getUsers- putUsers $ u' : (filter (/= u) usrs)- _ -> return ()---- ^ Assign a paper for the user to review-addAssignment :: Name -> Id -> ReviewDC ()-addAssignment n i = do- usr <- findUser n- pap <- findReview i- case (usr, pap) of- (Just u, Just _) -> - if i `elem` (conflicts u)- then return ()- else do let u' = u { assignments = i : (assignments u)}- usrs <- getUsers- putUsers $ u' : (filter (/= u) usrs)- _ -> return ()---- ^ Print users-printUsersTCB :: ReviewDC ()-printUsersTCB = do- users <- getUsers- mapM (liftReviewDC . dcShowTCB) users >>=- reviewDCPutStrLnTCB . (intercalate "\n--\n")---- ^ Print papers and reviews-printReviewsTCB :: ReviewDC ()-printReviewsTCB = do- reviews <- getReviews- mapM (liftReviewDC . dcShowTCB) reviews >>=- reviewDCPutStrLnTCB . (intercalate "\n--\n")---- | Generate privilege from a string-genPrivTCB :: NewPriv a => a -> DCPrivTCB-genPrivTCB = mintTCB . newPriv ---- ^ Create new paper given id and content-newReviewEnt :: Id -> Content -> ReviewDC ReviewEnt-newReviewEnt pId content = do- let p1 = "Paper" ++ (show pId)- r1 = "Review" ++ (show pId)- pLabel = newDC (<>) p1 - rLabel = newDC r1 r1 - privs = genPrivTCB (p1 ./\. r1)- liftReviewDC $ do- rPaper <- newLIORefP privs pLabel (Paper content)- rReview <- newLIORefP privs rLabel (Review "")- return $ ReviewEnt pId rPaper rReview---- ^ Adda new paper to be reviewed-addPaper :: Content -> ReviewDC Id-addPaper content = do- reviews <- getReviews- let pId = 1 + (length reviews)- ent <- newReviewEnt pId content- putReviews (ent:reviews)- return pId----- ^ Given a paper number return the paper-retrievePaper :: Id -> ReviewDC (Either ErrorStr Content)-retrievePaper pId = do- mu <- getCurUser- case mu of- Nothing -> return $ Left "Need to be logged in"- Just u -> do- mRev <- findReview pId - case mRev of - Nothing -> return $ Left "Invalid Id"- Just rev -> let as = assignments u- priv = genPrivTCB (listToComponent $ map id2cat as)- in doReadPaper priv rev- where doReadPaper priv rev = liftReviewDC $ do- (Paper lPaper) <- readLIORefP priv (paper rev)- return (Right lPaper)- id2cat i = MkDisj [principal . C.pack $ "Review"++(show i)]---- ^ Given a paper number print the paper--- NOTE: in the paper, the functionality of @readPaper@ corresponds to--- that of @retrievePaper@; here, we print out the content.-readPaper :: Id -> ReviewDC ()-readPaper i = retrievePaper i >>= \r -> reviewDCPutStrLn $ show r----- ^ Given a paper/review number return the review, if the entry exists-retrieveReview :: Id -> ReviewDC (Either ErrorStr Content)-retrieveReview pId = do- mRev <- findReview pId - case mRev of - Nothing -> return $ Left "Invalid Id"- Just rev -> do mu <- getCurUser- case mu of- Nothing -> return $ Left "Must login first"- Just u -> doReadReview rev- where doReadReview rev = liftReviewDC $ do- (Review r) <- readLIORef (review rev)- return (Right r)---- ^ Given a paper/review number print the review, if the entry exists-readReview :: Id -> ReviewDC ()-readReview i = retrieveReview i >>= \r -> reviewDCPutStrLn $ show r---- ^ Computer the label of the output' channel-getOutputChLbl :: ReviewDC (DCLabel) -getOutputChLbl = do- mu <- getCurUser- case mu of- Nothing -> liftReviewDC $ throwIO (ErrorCall "No user is logged in.")- Just u -> do- as <- getReviews >>= return . map paperId -- all reviews- let cs = conflicts u -- conflicting reviews- c_cat = map id2conf_cat (cs) -- conflicting categories- nc_cat = map id2cat (as \\ cs) -- noconflicting categories- return $ newDC (listToComponent $ c_cat ++ nc_cat) (<>)- where id2cat i = MkDisj [ principal . C.pack $ "Review"++(show i)]- id2conf_cat i = MkDisj [ principal . C.pack $ "Review" ++ (show i)- , principal $ "CONFLICT" ]- --- ^ Print if the current label flows to the output channel label, i.e.,--- there is no conflict of interest.-dcPutStrLn :: DCLabel -> Content -> DC ()-dcPutStrLn lo cont = do- l <- getLabel- if l `leq` lo- then dcPutStrLnTCB cont- else throwIO . ErrorCall $ "Trying to print conflicting review:\n" ++- (prettyShow l) ++ " [/= " ++ (prettyShow lo)---- ^ Main printing function. Print to a labeled output channel.-reviewDCPutStrLn :: String -> ReviewDC ()-reviewDCPutStrLn s = do - l <- getOutputChLbl- liftReviewDC $ dcPutStrLn l $ "-> "++ s--reviewDCPutStrLnTCB :: String -> ReviewDC ()-reviewDCPutStrLnTCB = liftReviewDC . dcPutStrLnTCB---- ^ Given a paper number and review content append to the current review.-appendToReview :: Id -> Content -> ReviewDC (Either ErrorStr ())-appendToReview pId content = do- mRev <- findReview pId - case mRev of - Nothing -> return $ Left "Invalid Id"- Just rev -> do privs <- getPrivs- doWriteReview privs rev content- return $ Right ()- where doWriteReview privs rev content = liftReviewDC $ do- toLabeledP privs ltop $ do- (Review rs) <- readLIORef (review rev)- -- restrict writes: - writeLIORef (review rev) (Review (rs++content))---- ^ Set the current label to the assignments-assign2curLabel :: [Id] -> ReviewDC() -assign2curLabel as = liftReviewDC $ do- let l = newDC (<>) (listToComponent $ map id2cat as)- setLabelTCB l- where id2cat i = MkDisj [principal . C.pack $ "Review"++(show i)]- --- ^ Safely execute untrusted code-safeExecTCB :: ReviewDC () -> ReviewDC ()-safeExecTCB m = do- s <- get'- s' <- liftReviewDC $ do- cc <- getClearance- cl <- getLabel- (_, s') <- (runReviewDC m s) `catch` - (\(e::SomeException) -> do- dcPutStrLnTCB "-> ERROR: IFC violated\n"- return ((), s))- lowerClrTCB cc- setLabelTCB cl- return s'- put' s'---- ^ Execute on behalf of user-asUser :: Name -> ReviewDC a -> ReviewDC ()-asUser n m = do- putCurUserName n- mu <- getCurUser- case mu of- Nothing -> return ()- Just u -> do- reviewDCPutStrLnTCB $ "| Hi, "++ (name u)++".\n| Password>"- p <- liftReviewDC dcGetLineTCB- if p /= (password u)- then reviewDCPutStrLnTCB "| Failed, try again" >> asUser n m- else do- reviewDCPutStrLnTCB $ "| Executing on behalf of "++(name u)++"...\n"- safeExecTCB $ assign2curLabel (assignments u) >> m >> return ()- clearCurUserName---- | Given a paper prefix return either an error string, if the paper cannot be--- found or the paper id.-findPaper :: String -> ReviewDC (Either ErrorStr Id)-findPaper s = do- revs <- getReviews- res <- mapM (simpleMatch s) revs >>= return . find isJust- case res of- Nothing -> return . Left $ "Could not find paper"- Just i -> return . Right $ fromJust i- -- ^ Fing paper by checking for prefix- where simpleMatch :: String -> ReviewEnt -> ReviewDC (Maybe Id)- simpleMatch s ent = do- (Paper pap) <- liftReviewDC $ readLIORefTCB (paper ent)- if s `isPrefixOf` pap- then return . Just $ paperId ent- else return Nothing
− examples/LambdaChair/Main.hs
@@ -1,38 +0,0 @@-{-# LANGUAGE CPP #-}-#if __GLASGOW_HASKELL__ >= 702 && __GLASGOW_HASKELL__ < 704-{-# LANGUAGE SafeImports #-}-#endif-#if __GLASGOW_HASKELL__ >= 704-{-# LANGUAGE Unsafe #-}-#endif-import LambdaChair-import DCLabel.PrettyShow--#if __GLASGOW_HASKELL__ >= 702-import safe AliceCode as Alice-import safe BobCode as Bob-#else-import AliceCode as Alice-import BobCode as Bob-#endif---main :: IO ()-main = printL . evalReviewDC $ do- addUser "Alice" "password"- - p1 <- addPaper "Flexible Dynamic..."- p2 <- addPaper "A Static..."- - addAssignment "Alice" p1- addAssignment "Alice" p2- - asUser "Alice" $ Alice.mainReview- - addUser "Bob" "password"- - addAssignment "Bob" p2- addConflict "Bob" p1-- asUser "Bob" $ Bob.mainReview- where printL m = m >>= (putStrLn . prettyShow . snd)
− examples/LambdaChair/Safe.hs
@@ -1,10 +0,0 @@-{-# LANGUAGE CPP #-}-#if __GLASGOW_HASKELL__ >= 702-{-# LANGUAGE Trustworthy #-}-#endif-module Safe ( module LambdaChair ) where-import LambdaChair ( findPaper- , retrievePaper, readPaper- , retrieveReview, readReview- , appendToReview- , reviewDCPutStrLn )
+ examples/dclabel.hs view
@@ -0,0 +1,47 @@+module Main where ++import LIO+import LIO.Privs.TCB (mintTCB)+import LIO.DCLabel++-- | Simple secrecy component example+s :: Component+s = "Alice" \/ "Bob" /\ "Carla"++-- | Simple integrity component example+i :: Component+i = "Alice" /\ "Carla"++-- | Simple label+l1 :: DCLabel+l1 = dcLabel s i++-- | Simple label+l2 :: DCLabel+l2 = dcLabel (toComponent "Djon") (toComponent "Alice")++-- | Creating privilege using constructor from TCB+p :: DCPriv+p = mintTCB $ "Alice" /\ "Carla"++main = do+ putStrLn $ "Label 1: " ++ show l1+ putStrLn $ "Label 2: " ++ show l2+ putStrLn $ "Join of labels: " ++ show (l1 `upperBound` l2)+ putStrLn $ "Meet of labels: " ++ show (l1 `lowerBound` l2)+ putStrLn $ "Privileges: " ++ show p+ putStrLn $ "Label 1 flows to Label 2? " ++ (show $ canFlowTo l1 l2)+ putStrLn $ "Label 1 flows to Label 2 given privileges? " +++ (show $ canFlowToP p l1 l2)+{-+Output:+ghci> main+Label 1: < {[Carla] /\ [Alice \/ Bob]} , {[Alice] /\ [Carla]} >+Label 2: < {[Djon]} , {[Alice]} >+Join of labels: < {[Carla] /\ [Djon] /\ [Alice \/ Bob]} , {[Alice]} >+Meet of labels: < {[Carla \/ Djon] /\ [Alice \/ Bob \/ Djon]} ,+{[Alice] /\ [Carla]} >+Privileges: DCPrivTCB {unDCPriv = {[Alice] /\ [Carla]}}+Label 1 flows to Label 2? False+Label 1 flows to Label 2 given privileges? True+-}
examples/fsExample.hs view
@@ -1,76 +1,78 @@ {-# LANGUAGE ScopedTypeVariables #-} import Prelude hiding (catch)-import System.FilePath-import Data.Functor ((<$>))-import Data.Serialize --import Control.Exception (SomeException)-import LIO-import LIO.MonadCatch-import LIO.TCB (ioTCB, setLabelTCB, lowerClrTCB)-import LIO.DCLabel-import LIO.Handle-import DCLabel.PrettyShow-import DCLabel.Core (createPrivTCB)--import Control.Monad import qualified Data.ByteString.Lazy as L import qualified Data.ByteString.Lazy.Char8 as LC -lpub :: DCLabel-lpub = newDC (<>) (<>)+import Control.Monad+import Control.Exception (SomeException) -dcEvalWithRoot :: FilePath -> DC a -> IO (a, DCLabel)-dcEvalWithRoot path act = evalWithRoot path (Just lpub) act ()+import System.FilePath +import LIO+import LIO.DCLabel+import LIO.Concurrent+import LIO.Handle+import LIO.TCB (ioTCB, updateLIOStateTCB)+import LIO.Privs.TCB (mintTCB) ++dcEvalWithRoot :: FilePath -> DC a -> IO a+dcEvalWithRoot path act = evalWithRootFS path (Just dcPub) act defaultState++ -- Enable malicious code: malicious :: Bool malicious = False +-- Execute alice first?+execAliceFirst :: Bool+execAliceFirst = True+ -- -- Labels and privileges -- lalice, lbob, lbobOralice :: DCLabel-lalice = newDC "alice" "alice"-lbob = newDC "bob" "bob"-lclarice = newDC "clarice" "clarice"-lbobOralice = newDC ("bob" .\/. "alice") ("bob" .\/. "alice")+lalice = dcLabel (toComponent "alice") (toComponent "alice")+lbob = dcLabel (toComponent "bob") (toComponent "bob")+lclarice = dcLabel (toComponent "clarice") (toComponent "clarice")+lbobOralice = dcLabel ("bob" \/ "alice") ("bob" \/ "alice") -palice, pbob, pclarice :: DCPrivTCB-pbob = createPrivTCB $ newPriv "bob"-palice = createPrivTCB $ newPriv "alice"-pclarice = createPrivTCB $ newPriv "clarice"+palice, pbob, pclarice :: DCPriv+pbob = mintTCB $ dcPrivDesc "bob"+palice = mintTCB $ dcPrivDesc "alice"+pclarice = mintTCB $ dcPrivDesc "clarice" -main = ignore $ dcEvalWithRoot "/tmp/rootFS" $ do- exec bobsCode pbob (newDC "bob" (<>)) "bob"- exec alicesCode palice (newDC "alice" (<>)) "alice"- exec claricesCode pclarice (newDC "clairce" (<>)) "clarice"- where exec act p l s = do setLabelTCB lbot- lowerClrTCB ltop- putStrLnTCB $ ">Executing for " ++ s ++ ":"- catch (withClearance l $ setLabelTCB lpub >>- act p)- (\(e::SomeException) ->- putStrLnTCB "IFC violation attempt!")+main = dcEvalWithRoot "/tmp/lio_fs/root" $ do+ putStrLnTCB $ "malicious = " ++ show malicious+ putStrLnTCB $ "execAliceFirst = " ++ show execAliceFirst+ let execA = exec alicesCode palice (dcLabel (toComponent "alice") dcTrue) "alice"+ execB = exec bobsCode pbob (dcLabel (toComponent "bob") dcTrue) "bob"+ if execAliceFirst + then execA >> execB+ else execB >> execA+ exec claricesCode pclarice (dcLabel (toComponent "clarice") dcTrue) "clarice"+ where exec act p l s = do putStrLnTCB $ ">Executing for " ++ s ++ ":"+ catchLIO (withClearance l $ setLabelTCB dcPub >> act p)+ (\(e::SomeException) ->+ putStrLnTCB "IFC violation attempt!") printCurLabel $ ">" ++ s- ignore act = act >> return ()+ putStrLnTCB "\n"+ setLabelTCB l = updateLIOStateTCB $ \s -> s { lioLabel = l } printCurLabel s = do l <- getLabel c <- getClearance- ioTCB . putStrLn $ s ++ " : " ++ (prettyShow l)- ++ " : " ++ (prettyShow c)+ ioTCB . putStrLn $ s ++ " : " ++ show l ++ " : " ++ show c -- -- Bob's code: -- -bobsCode :: DCPrivTCB -> DC ()+bobsCode :: DCPriv -> DC () bobsCode p = do discard_ $ createDirectoryP p lbobOralice "bobOralice" discard_ $ createDirectoryP p lbob ("bobOralice" </> "bob")@@ -79,14 +81,15 @@ hPutStrLnP p h (LC.pack "Hi Alice!") hCloseP p h -- Write secret:- taint lbob -- writeFileP uses current label to label file- writeFileP p ("bobOralice" </> "bob" </> "secret")- (LC.pack "I am Chuck Norris!")+ writeFileP p lbob ("bobOralice" </> "bob" </> "secret")+ (LC.pack "I am Chuck Norris!") + -- -- Alice's code: -- -alicesCode :: DCPrivTCB -> DC ()++alicesCode :: DCPriv -> DC () alicesCode p = do when malicious malCode discard_ $ createDirectoryP p lbobOralice "bobOralice"@@ -95,7 +98,7 @@ if "messages" `elem` files then do msg <- readFileP p ("bobOralice" </> "messages") putStrLnTCB $ "Message log:\n" ++ (LC.unpack msg)- else writeFileLP p lbobOralice ("bobOralice" </> "messages")+ else writeFileP p lbobOralice ("bobOralice" </> "messages") (LC.pack "Hello Bob!") where malCode = do msg <- readFileP p ("bobOralice" </> "bob" </> "secret")@@ -107,7 +110,7 @@ -- Clarice's malicious code: -- -claricesCode :: DCPrivTCB -> DC ()+claricesCode :: DCPriv -> DC () claricesCode p = do discard_ $ createDirectoryP p lclarice "clarice" when malicious $ do@@ -121,9 +124,83 @@ discard_ :: DC a -> DC () discard_ act = do- c <- getClearance- catch (discard c act) (\(e::SomeException) -> return ())+ forkLIO $ void act+ threadDelay 100 putStrLnTCB :: String -> DC () putStrLnTCB s = ioTCB $ putStrLn s++++{- OUTPUT:+*Main> main+malicious = False+execAliceFirst = False+>Executing for bob:+>bob : < |True , |True > : < |False , |True >+++>Executing for alice:+Message log:+Hi Alice!++>alice : < |True , |True > : < |False , |True >+++>Executing for clarice:+>clarice : < |True , |True > : < |False , |True >++- rm -rf /tmp/lio_fs/root -------------------------------------------+*Main> main+malicious = True+execAliceFirst = False+>Executing for bob:+>bob : < |True , |True > : < |False , |True >+++>Executing for alice:+IFC violation attempt!+>alice : < |True , |True > : < |False , |True >+++>Executing for clarice:+IFC violation attempt!+>clarice : < |True , |True > : < |False , |True >++- rm -rf /tmp/lio_fs/root -------------------------------------------+*Main> main+malicious = False+execAliceFirst = True+>Executing for alice:+>alice : < |True , |True > : < |False , |True >+++>Executing for bob:+>bob : < |True , |True > : < |False , |True >+++>Executing for clarice:+>clarice : < |True , |True > : < |False , |True >+++- rm -rf /tmp/lio_fs/root -------------------------------------------+*Main> main+malicious = True+execAliceFirst = True+>Executing for alice:+IFC violation attempt!+>alice : < |True , |True > : < |False , |True >+++>Executing for bob:+>bob : < |True , |True > : < |False , |True >+++>Executing for clarice:+IFC violation attempt!+>clarice : < |True , |True > : < |False , |True >+++-}+
+ examples/gate.hs view
@@ -0,0 +1,24 @@+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` (map dcPrivDesc ["Alice", "Bob"])+ then Just $ a + b+ else Nothing+++alice, bob, clark :: DCPriv+alice = mintTCB . dcPrivDesc $ "Alice"+bob = mintTCB . dcPrivDesc $ "Bob"+clark = mintTCB . 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+ ]
examples/waitAndCatch.hs view
@@ -2,24 +2,24 @@ import Prelude hiding (catch) import LIO import LIO.TCB (ioTCB)-import LIO.MonadCatch import LIO.DCLabel import LIO.Concurrent-import DCLabel.PrettyShow+import Control.Exception (SomeException) l,m,h :: DCLabel-l = lbot-m = newDC "M" (<>)-h = ltop+l = dcLabel ("A" \/ "B") dcTrue+m = dcLabel (toComponent "M") dcTrue+h = dcLabel ("A" /\ "B") dcTrue main = do- (_,l) <- evalDC $ do+ (_,l) <- runDC $ do lb <- label m 6- f <- lFork l $ do+ f <- lFork (if doFail then m else l) $ do v <- unlabel lb return (3+v)- catch (do r <- lWait f- ioTCB . print $ r - ) (\(e::LabelFault) -> ioTCB . putStrLn $ "exception")- print . pShow $ l+ catchLIO (do r <- lWait f+ ioTCB . putStrLn $ "No exception: " ++ show r + ) (\(_::SomeException) -> ioTCB . putStrLn $ "Exception")+ print l+ where doFail = True
− include/HsTmp.h
@@ -1,22 +0,0 @@-#ifndef HSTMP_H-#define HSTMP_H--#include "HsTmpConfig.h"--#include <stdlib.h>-#include <stdio.h>---#if HAVE_MKSTEMP-int __hstmp_mkstemp(char *filetemplate);-#endif--#if HAVE_MKSTEMPS-int __hstmp_mkstemps(char *filetemplate, int suffixlen);-#endif--#if HAVE_MKDTEMP-char *__hstmp_mkdtemp(char *filetemplate);-#endif--#endif
− include/HsTmpConfig.h
@@ -1,29 +0,0 @@-/* include/HsTmpConfig.h. Generated from HsTmpConfig.h.in by configure. */-/* include/HsTmpConfig.h.in. Generated from configure.ac by autoheader. */--/* Define to 1 if you have the `mkdtemp' function. */-#define HAVE_MKDTEMP 1--/* Define to 1 if you have the `mkstemp' function. */-#define HAVE_MKSTEMP 1--/* Define to 1 if you have the `mkstemps' function. */-#define HAVE_MKSTEMPS 1--/* Define to the address where bug reports for this package should be sent. */-#define PACKAGE_BUGREPORT "deian@cs.stanford.edu"--/* Define to the full name of this package. */-#define PACKAGE_NAME "Labeled IO library"--/* Define to the full name and version of this package. */-#define PACKAGE_STRING "Labeled IO library 0.1.0"--/* Define to the one symbol short name of this package. */-#define PACKAGE_TARNAME "lio"--/* Define to the home page for this package. */-#define PACKAGE_URL ""--/* Define to the version of this package. */-#define PACKAGE_VERSION "0.1.0"
− include/HsTmpConfig.h.in
@@ -1,28 +0,0 @@-/* include/HsTmpConfig.h.in. Generated from configure.ac by autoheader. */--/* Define to 1 if you have the `mkdtemp' function. */-#undef HAVE_MKDTEMP--/* Define to 1 if you have the `mkstemp' function. */-#undef HAVE_MKSTEMP--/* Define to 1 if you have the `mkstemps' function. */-#undef HAVE_MKSTEMPS--/* Define to the address where bug reports for this package should be sent. */-#undef PACKAGE_BUGREPORT--/* Define to the full name of this package. */-#undef PACKAGE_NAME--/* Define to the full name and version of this package. */-#undef PACKAGE_STRING--/* Define to the one symbol short name of this package. */-#undef PACKAGE_TARNAME--/* Define to the home page for this package. */-#undef PACKAGE_URL--/* Define to the version of this package. */-#undef PACKAGE_VERSION
lio.cabal view
@@ -1,113 +1,108 @@ Name: lio-Version: 0.1.3-build-type: Simple+Version: 0.9.0.0+Cabal-Version: >= 1.8+Build-type: Simple License: GPL License-File: LICENSE Author: HAILS team-Maintainer: Deian Stefan <deian at cs dot stanford dot edu>-Stability: experimental+Maintainer: HAILS team <hails at scs dot stanford dot edu> Synopsis: Labeled IO Information Flow Control Library Category: Security Description:- The /Labeled IO/ (LIO) library provides information flow- control for incorporating untrusted code within Haskell- applications. Most code should import module "LIO" and- whichever label type the application is using (e.g.,- "LIO.DCLabel"). The core functionality of the library is- documented in "LIO.TCB". LIO was implemented by David- Mazieres (<http://www.scs.stanford.edu/~dm/>), Deian Stefan- (<http://www.scs.stanford.edu/~deian/>), Alejandro Russo- (<http://www.cse.chalmers.se/~russo/>) and John C. Mitchell- (<http://www.stanford.edu/~jcm/>).+ 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. - The extended version of our paper, that includes the proofs- is available here:- <http://www.scs.stanford.edu/~deian/pubs/stefan:2011:flexible-ext.pdf>.+ .+ 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.)+ 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. - The library depends on the @DCLabel@ module. You can read more on- DC Labels here:- <http://www.scs.stanford.edu/~deian/dclabels/>.-Cabal-Version: >= 1.8+ . -Build-Type: Configure-Extra-source-files:- examples/LambdaChair/AliceCode.hs- examples/LambdaChair/BobCode.hs- examples/LambdaChair/LambdaChair.hs- examples/LambdaChair/Main.hs- examples/LambdaChair/Safe.hs- examples/fsExample.hs- examples/waitAndCatch.hs- configure.ac- configure- include/HsTmpConfig.h.in-Extra-Tmp-Files:- include/HsTmpConfig.h+ 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. -Source-repository head- Type: git- Location: http://www.github.com/scslab/lio.git+ . + The paper that describes the core of LIO, including motivation and+ formal modeling/proofs, is available here:+ <http://arxiv.org/abs/1207.1457> -Library- Build-Depends: base >= 4 && < 5,- array >= 0.2 && < 1,- bytestring >= 0.9 && < 1,- containers >= 0.2 && < 1,- directory >= 1.0 && < 2,- filepath >= 1.1 && < 2,- mtl >= 1.1.0.2 && < 3,- old-time >= 1 && < 2,- unix >= 2.5.0.0 && < 3,- SHA >= 1.4.1.1 && < 2,- time >= 1.1.4 && < 2,- dclabel >= 0.0.4 && < 2,- cereal >= 0.3.3 && < 0.4,- base64-bytestring >= 0.1.1.0 - ghc-options: -Wall -fno-warn-orphans+Extra-source-files:+ examples/dclabel.hs+ examples/gate.hs+ examples/waitAndCatch.hs+ examples/fsExample.hs - Exposed-modules:- -- Core:- LIO,- LIO.Safe,- LIO.TCB,- LIO.MonadCatch,- LIO.MonadLIO,- -- Label formats:- LIO.DCLabel,- -- References:- LIO.LIORef,- LIO.LIORef.TCB,- LIO.LIORef.Safe,- -- Concurrency:- LIO.Concurrent,- LIO.Concurrent.LMVar,- LIO.Concurrent.LMVar.Safe,- LIO.Concurrent.LMVar.TCB,- -- Filesystem:- LIO.FS,- LIO.Handle- if impl(ghc < 7.6)- Other-Modules:- System.Posix.Tmp- Include-Dirs: include- Includes: HsTmp.h- Install-Includes: HsTmp.h HsTmpConfig.h- C-Sources: cbits/HsTmp.c+Library+ Build-Depends:+ base >= 4.5 && < 5.0,+ transformers >= 0.2.2 && < 1.0,+ containers >= 0.4.2.1 && < 0.5,+ bytestring >= 0.9.2.1 && < 1.0,+ cereal >= 0.3.5.1 && < 0.4,+ filepath >= 1.3.0.0 && < 1.4,+ directory >= 1.1.0.2 && < 1.2,+ xattr >= 0.6.1 && < 1.0,+ zlib >= 0.5.3.1 && < 0.6,+ SHA >= 1.5.0.0 && < 1.6,+ time >= 1.2.0.5 && < 1.5 -test-suite tests- type: exitcode-stdio-1.0- hs-source-dirs: tests- main-is: Tests.hs - ghc-options:- -Wall -threaded -rtsopts+ Ghc-options: -Wall -fno-warn-orphans - build-depends:- QuickCheck,- base,- dclabel,- lio,- test-framework,- test-framework-quickcheck2+ Exposed-modules:+ -- * Top-level exporter+ LIO+ -- * Label definition+ LIO.Label+ -- * Core library+ LIO.Core+ LIO.TCB+ -- * Labeled values+ LIO.Labeled+ LIO.Labeled.TCB+ -- * Labeled IORefs+ LIO.LIORef+ LIO.LIORef.TCB+ -- * Gates+ LIO.Gate+ -- * LIO privileges+ LIO.Privs+ LIO.Privs.TCB+ -- * Concurrency+ LIO.Concurrent+ LIO.Concurrent.TCB+ LIO.Concurrent.LMVar+ LIO.Concurrent.LMVar.TCB+ -- * Time library+ LIO.Data.Time+ -- * DCLabels+ LIO.DCLabel+ LIO.DCLabel.Core+ LIO.DCLabel.Privs+ LIO.DCLabel.Privs.TCB+ LIO.DCLabel.Serialize+ LIO.DCLabel.DSL+ -- * File system+ LIO.Handle+ LIO.FS.TCB
− tests/Tests.hs
@@ -1,273 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}-{-# OPTIONS_GHC -fno-warn-orphans #-}-module Main (main) where--import Prelude hiding (catch)-import Test.Framework (Test, defaultMain, testGroup)-import Test.Framework.Providers.QuickCheck2 (testProperty)--import Test.QuickCheck hiding (label)-import Test.QuickCheck.Monadic--import Data.List (tails)-import Data.Functor ((<$>))-import Data.IORef--import LIO.TCB-import LIO.DCLabel-import DCLabel.TCB-import DCLabel.PrettyShow--import Control.Monad (liftM, void, forM_)-import Control.Exception (SomeException(..))-import Control.Concurrent--import System.IO.Unsafe--instance Arbitrary Principal where - arbitrary = do p <- oneof $ map return ["A", "B", "C", "D"]- return $ principal (p :: String)---instance Arbitrary Disj where - arbitrary = sized disjunction - where disjunction 0 = return $ MkDisj { disj = [] }- disjunction n = do a <- arbitrary- m <- choose (0, n-1) - djs <- disjunction m- return $ MkDisj $ a:(disj djs) ---instance Arbitrary Conj where - arbitrary = sized conjunction - where conjunction 0 = oneof - [ return $ MkConj { conj = [] }- , return $ MkConj { conj = [MkDisj []] }- , return $ MkConj { conj = [MkDisj [], MkDisj []] }- ] - conjunction n = do a <- arbitrary- m <- choose (0, n-1) - cjs <- conjunction m- return $ MkConj $ a:(conj cjs) - shrink (MkConj ls) = [MkConj ll | l <- tails ls, ll <- shrink l]--instance Arbitrary Component where- arbitrary = do m <- choose (0, 1) :: Gen Int- if m==0 then mkArbLbl arbitrary- else return MkComponentAll- where mkArbLbl :: Gen Conj -> Gen Component- mkArbLbl = liftM MkComponent--instance Arbitrary DCLabel where- arbitrary = do s <- arbitrary- i <- arbitrary - return $ MkDCLabel { secrecy = s, integrity = i }--instance Arbitrary TCBPriv where- arbitrary = do p <- arbitrary- return $ MkTCBPriv p------------main :: IO ()-main = defaultMain tests------------monadicDC :: PropertyM DC a -> Property-monadicDC (MkPropertyM m) =- property $ unsafePerformIO <$> dorun <$> m (const (return (return (property True))))- where dorun x =fst <$> evalDC x---- Helper function-printLIOState :: String -> DC ()-printLIOState m = do- s <- getTCB- ioTCB . putStrLn $ "\n" ++ m ++ ":\nLabel = " ++ (prettyShow $ lioL s ) - ++ "\nClear = " ++ (prettyShow $ lioC s ) - ++ "\nPrivs = " ++ (prettyShow $ lioP s ) ------------tests :: [Test]-tests = [- testGroup "label" [- testProperty- "unlabel raises current label"- prop_label_unlabel- ]- , testGroup "withPrivileges" [- testProperty- "restores privileges: nop" $- prop_withPrivileges_restores_privs (return ())- , testProperty- "restores privileges: exception" $- prop_withPrivileges_restores_privs (throwIO . userError $ "ex")- , testProperty- "restores privileges: IFC violation" $- prop_withPrivileges_restores_privs (do taint ltop- void $ label lbot '1')- , testProperty- "does not escallate privileges"- prop_withPrivileges_no_escalate - ]- , testGroup "catch" [- testProperty- "does not untaint computation: id" $- prop_catch_preserves_taint (const id)- , testProperty- "does not untaint computation: withPrivileges" $- prop_catch_preserves_taint withPrivileges- , testProperty- "does not untaint computation: toLabeled" $- prop_catch_preserves_taint withPrivileges- ]- , testGroup "onException" [- testProperty- "does not untaint computation: id" $- prop_onException_preserves_taint (const id)- , testProperty- "does not untaint computation: withPrivileges" $- prop_onException_preserves_taint withPrivileges- , testProperty- "does not untaint computation: toLabeled" $- prop_onException_preserves_taint withPrivileges- ]- , testGroup "mask" [- testProperty- "does not untaint computation: id" $- prop_mask_preserves_taint (const id)- , testProperty- "does not untaint computation: withPrivileges" $- prop_mask_preserves_taint withPrivileges- , testProperty- "does not untaint computation: toLabeled" $- prop_mask_preserves_taint withPrivileges- , testProperty- "restore allows throwTo exceptions" $- prop_mask_correct True- , testProperty- "mask without restore ignores throwTo exceptions" $- prop_mask_correct False- ]- ]------- label/unlabe related--- ---- NOTE: we assume that the initial label is bottom and initial--- clearance is top---- | Check that the current label is raised when unlabeling a labeled value-prop_label_unlabel :: Property-prop_label_unlabel = monadicDC $ do- l <- pick (arbitrary :: Gen DCLabel)- x <- pick (arbitrary :: Gen Int)- lx <- run $ label l x- x' <- run $ unlabel lx- lbl1 <- run $ getLabel- assert $ lbl1 == l && x' == x- ------ withPrivileges related--- ---- | Makesure that the privileges after a withPrivileges are restored--- correctly-prop_withPrivileges_restores_privs :: DC () -> Property -prop_withPrivileges_restores_privs act = monadicDC $ do- p0 <- pick (arbitrary :: Gen TCBPriv)- p1 <- pick (arbitrary :: Gen TCBPriv)- pre $ p0 /= p1- run $ setPrivileges p0- run $ (withPrivileges p1 act) `catchTCB` (\_ -> return ())- p0' <- run $ getPrivileges- assert $ p0 == p0'----- | Assert that the privileges in a withPrivileged block are only--- what is provided.-prop_withPrivileges_no_escalate :: Property -prop_withPrivileges_no_escalate = monadicDC $ do- p0 <- pick (arbitrary :: Gen TCBPriv)- p1 <- pick (arbitrary :: Gen TCBPriv)- pre $ p0 /= p1- run $ setPrivileges p0- p1' <- run $ withPrivileges p1 getPrivileges- assert $ p1 == p1'------- catch related--- ---- | Taint within catch does not get ignored-prop_catch_preserves_taint :: (TCBPriv -> DC DCLabel -> DC DCLabel) -> Property -prop_catch_preserves_taint wrapper = monadicDC $ do- p <- pick (arbitrary :: Gen TCBPriv)- l <- pick (arbitrary :: Gen DCLabel)- l' <- run $ catch (wrapper p $ do taint l- _ <- throwIO (userError "u")- getLabel- ) (\(SomeException _) -> getLabel)- l'' <- run $ getLabel- assert $ l == l' && l == l''------- onException related--- ---- | onException does not ignore taint-prop_onException_preserves_taint :: (TCBPriv -> DC DCLabel -> DC DCLabel)- -> Property -prop_onException_preserves_taint wrapper = monadicDC $ do- p <- pick (arbitrary :: Gen TCBPriv)- l <- pick (arbitrary :: Gen DCLabel)- l' <- run $ catch ((wrapper p $ do taint l- _ <- throwIO (userError "u")- getLabel- ) `onException`- (return ())) (\(SomeException _) -> getLabel)- l'' <- run $ getLabel- assert $ l == l' && l == l''------- mask related--- ---- | onException does not ignore taint-prop_mask_preserves_taint :: (TCBPriv -> DC DCLabel -> DC DCLabel) -> Property -prop_mask_preserves_taint wrapper = monadicDC $ do- p <- pick (arbitrary :: Gen TCBPriv)- l <- pick (arbitrary :: Gen DCLabel)- doRestore <- pick (arbitrary :: Gen Bool)- l' <- run $ catch (mask $ \restore -> (if doRestore then restore else id) $- wrapper p $ do taint l- _ <- throwIO (userError "u")- getLabel- ) (\(SomeException _) -> getLabel)- l'' <- run $ getLabel- assert $ l == l' && l == l''----- | Check that if restore is used then a throwTo is not ignored.--- Conversely, if restore is not used then a throwTo should not affect--- the count.-prop_mask_correct :: Bool -> Property-prop_mask_correct doRestore = monadicIO $ do- let count = 1000000- ref <- run $ newIORef 1- run $ do- tid <- forkIO $ (\(SomeException _) -> return ()) `handle` (void $ evalDC $ - mask $ \restore -> (if doRestore then restore else id) $ do- forM_ [1..count] (ioTCB . writeIORef ref))- threadDelay 10- throwTo tid (userError "kill")- res <- run $ readIORef ref- assert $ if doRestore then res < count else res == count