{-# LANGUAGE Unsafe #-}
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE ExistentialQuantification #-}

{- | 

This module exports symbols that must be accessible only to trusted
code.  By convention, the names of such symbols always end
\"@...TCB@\" (short for \"trusted computing base\").  In many cases, a
type is safe to export while its constructor is not.  Hence, only the
constructor ends \"@TCB@\", while the type is re-exported to safe code
(without constructors) from "LIO.Core".

Security rests on the fact that untrusted code must be compiled with
@-XSafe@.  Because this module is flagged unsafe, it cannot be
imported from safe modules.

-}

module LIO.TCB (
  -- * LIO monad
    LIOState(..), LIO(..)
  -- ** Accessing internal state
  , getLIOStateTCB, putLIOStateTCB, modifyLIOStateTCB
  -- * Executing IO actions
  , ioTCB
  -- * Privileged constructors
  , Priv(..), Labeled(..), LabelOf(..)
  -- * Uncatchable exception type
  , UncatchableTCB(..), makeCatchable
  -- * Trusted 'Show'
  , ShowTCB(..)
  -- * 'LabeledResult's
  , LabeledResult(..), LResStatus(..)
  ) where

import safe Control.Applicative
import safe Control.Exception (Exception(..), SomeException(..))
import safe qualified Control.Concurrent as IO
import safe Control.Monad
import safe Data.Monoid
import safe Data.IORef
import safe Data.Typeable

--
-- LIO Monad
--

-- | 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 wrapper around 'IO' that keeps track of a
-- /current label/ and /current clearance/.  Safe code cannot execute
-- arbitrary 'IO' actions from the 'LIO' monad.  However, trusted
-- runtime functions can use 'ioTCB' to perform 'IO' actions (which
-- they should only do after appropriately checking labels).
newtype LIO l a = LIOTCB (IORef (LIOState l) -> IO a) deriving (Typeable)

instance Monad (LIO l) where
  {-# INLINE return #-}
  return = LIOTCB . const . return
  {-# INLINE (>>=) #-}
  (LIOTCB ma) >>= k = LIOTCB $ \s -> do
    a <- ma s
    case k a of LIOTCB mb -> mb s
  fail = LIOTCB . const . fail

instance Functor (LIO l) where
  fmap f (LIOTCB a) = LIOTCB $ \s -> a s >>= return . f
-- fmap typically isn't inlined, so we don't inline our definition,
-- but we do define it in terms of >>= and return (which are inlined)

instance Applicative (LIO l) where
  {-# INLINE pure #-}
  pure = return
  {-# INLINE (<*>) #-}
  (<*>) = ap

--
-- 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 :: LIO l (LIOState l)
{-# INLINE getLIOStateTCB #-}
getLIOStateTCB = LIOTCB readIORef

-- | Set internal state.
putLIOStateTCB :: LIOState l -> LIO l ()
{-# INLINE putLIOStateTCB #-}
putLIOStateTCB s = LIOTCB $ \sp -> writeIORef sp $! s

-- | Update the internal state given some function.
modifyLIOStateTCB :: (LIOState l -> LIOState l) -> LIO l ()
{-# INLINE modifyLIOStateTCB #-}
modifyLIOStateTCB f = do
  s <- getLIOStateTCB
  putLIOStateTCB (f s)

--
-- Executing IO actions
--

-- | Lifts an 'IO' computation into the 'LIO' monad.  This function is
-- dangerous and should only be called after appropriate checks ensure
-- the 'IO' computation will not violate IFC policy.
ioTCB :: IO a -> LIO l a
{-# INLINE ioTCB #-}
ioTCB = LIOTCB . const

--
-- Exception handling
--

-- | An uncatchable exception hierarchy is used to terminate an
-- untrusted thread.  Wrap the uncatchable exception in
-- 'UncatchableTCB' before throwing it to the thread.  'runLIO' will
-- subsequently unwrap the 'UncatchableTCB' constructor.
--
-- Note this can be circumvented by 'IO.mapException', which should be
-- made unsafe. In the interim, auditing untrusted code for this is
-- necessary.
data UncatchableTCB = forall e. (Exception e) =>
                      UncatchableTCB e deriving (Typeable)

instance Show UncatchableTCB where
  showsPrec p (UncatchableTCB e) = showsPrec p e

instance Exception UncatchableTCB where
  toException = SomeException
  fromException (SomeException e) = cast e

-- | Simple utility function that strips 'UncatchableTCB' from around an
-- exception.
makeCatchable :: SomeException -> SomeException
makeCatchable e@(SomeException einner) =
  case cast einner of Just (UncatchableTCB enew) -> SomeException enew
                      Nothing                    -> e

--
-- Privileges
--

-- | A newtype wrapper that can be used by trusted code to transform a
-- powerless description of privileges into actual privileges.  The
-- constructor, 'PrivTCB', is dangerous as it allows creation of
-- arbitrary privileges.  Hence it is only exported by the unsafe
-- module "LIO.TCB".  A safe way to create arbitrary privileges is to
-- call 'privInit' (see "LIO.Run#v:privInit") from the 'IO' monad
-- before running your 'LIO' computation.
newtype Priv a = PrivTCB a deriving (Show, Eq, Typeable)

instance Monoid p => Monoid (Priv p) where
  mempty = PrivTCB mempty
  {-# INLINE mappend #-}
  mappend (PrivTCB m1) (PrivTCB m2) = PrivTCB $ m1 `mappend` m2
  {-# INLINE mconcat #-}
  mconcat ps = PrivTCB $ mconcat $ map (\(PrivTCB p) -> p) ps

--
-- Pure labeled values
--

-- | @Labeled l a@ is a value that associates a label of type @l@ with
-- a pure value of type @a@. Labeled values allow users to label data
-- with a label other than the current label.  Note that 'Labeled' is
-- an instance of 'LabelOf', which means that only the /contents/ of a
-- labeled value (the type @t@) is kept secret, not the label.  Of
-- course, if you have a @Labeled@ within a @Labeled@, then the label
-- on the inner value will be protected by the outer label.
data Labeled l t = LabeledTCB !l t deriving Typeable
-- Note: t cannot be strict if we want things like lFmap.

-- | Trusted 'Show' instance.
instance (Show l, Show a) => ShowTCB (Labeled l a) where
    showTCB (LabeledTCB l a) = show a ++ " {" ++ show l ++ "}"

-- | Generic class used to get the type of labeled objects. For,
-- instance, if you wish to associate a label with a pure value (as in
-- "LIO.Labeled"), you may create a data type:
-- 
-- > data 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 l a) = l
class LabelOf t where
  -- | Get the label of a labeled value or object.  Note the label
  -- must be the second to last type constructor argument.
  labelOf :: t l a -> l

instance LabelOf Labeled where
  {-# INLINE labelOf #-}
  labelOf (LabeledTCB l _) = l

--
-- Trusted 'Show'
--

-- | It would be a security issue to make certain objects members of
-- the 'Show' class.  Nonetheless it is useful to be able to examine
-- such objects when debugging.  The 'showTCB' method can be used to
-- examine such objects.
class ShowTCB a where
  showTCB :: a -> String


--
-- LabeledResult
--

-- | Status of a 'LabeledResult'.
data LResStatus l a = LResEmpty
                    | LResLabelTooHigh !l
                    | LResResult a
                      deriving (Show)

-- | A @LabeledResult@ encapsulates a future result from a computation
-- spawned by 'lFork' or 'lForkP'.  See "LIO.Concurrent" for a
-- description of the concurrency abstractions of LIO.
data LabeledResult l a = LabeledResultTCB {
    lresThreadIdTCB :: !IO.ThreadId
    -- ^ Thread executing the computation
  , lresLabelTCB :: !l
    -- ^ Label of the tresult
  , lresBlockTCB :: !(IO.MVar ())
    -- ^ This 'MVar' is empty until such point as 'lresStatusTCB' is
    -- no longer 'LResEmpty'.  Hence, calling 'readMVar' on this field
    -- allows one to wait for the thread to terminate.
  , lresStatusTCB :: !(IORef (LResStatus l a))
    -- ^ Result (when it is ready), or the label at which the thread
    -- terminated, if that label could not flow to 'lresLabelTCB'.
  }

instance LabelOf LabeledResult where
  labelOf = lresLabelTCB