{-# 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 ).
-- 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
-- ), 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