{- |
Module      : System.Linux.SELinux
Copyright   : Luite Stegeman
Licence     : BSD3

Maintainer  : stegeman@gmail.com
Stability   : provisional
Portability : portable

Haskell bindings for the SELinux API.
-}

{-#LANGUAGE ForeignFunctionInterface #-}

{-
  incomplete bindings, feel free to extend
-}

module System.Linux.SELinux (
  SecurityContext,
  EnforceMode(..),
  isSELinuxEnabled,
  isSELinuxMLSEnabled,
  getEnforceMode,
  getEnforce,
  getCon, getConRaw, setCon, setConRaw,
  getPidCon, getPidConRaw,
  getPrevCon, getPrevConRaw,
  getExecCon, getExecConRaw, setExecCon, setExecConRaw,
  getFsCreateCon, getFsCreateConRaw, setFsCreateCon, setFsCreateConRaw,
  getKeyCreateCon, getKeyCreateConRaw, setKeyCreateCon, setKeyCreateConRaw,
  getSockCreateCon, getSockCreateConRaw, setSockCreateCon, setSockCreateConRaw,
  getFileCon, getFileConRaw,
  lgetFileCon, lgetFileConRaw,
  fgetFileCon, fgetFileConRaw,
  setFileCon, setFileConRaw,
  lsetFileCon, lsetFileConRaw,
  fsetFileCon, fsetFileConRaw,
  getPeerCon, getPeerConRaw,
  getConfigPolicyRoot, getConfigBinaryPolicyPath,
  getConfigFailsafeContextPath, getConfigRemovableContextPath,
  getConfigDefaultContextPath, getConfigUserContextsPath,
  getConfigFileContextPath, getConfigFileContextHomedirPath,
  getConfigFileContextLocalPath, getConfigFileContextSubsPath,
  getConfigHomedirContextPath, getConfigMediaContextPath,
  getConfigVirtualDomainContextPath, getConfigVirtualImageContextPath,
  getConfigXContextPath, getConfigSepgsqlContextPath,
  getConfigContextsPath, getConfigSecurettyTypesPath,
  getConfigBooleansPath, getConfigCustomizableTypesPath,
  getConfigUsersPath, getConfigUsersconfPath,
  getConfigTranslationsPath, getConfigColorsPath,
  getConfigNetfilterContextPath, getConfigPath 
 ) where

import Control.Monad
import Foreign.C.Types (CInt(..))
import Foreign.C.String (CString, withCString, peekCString)
import Foreign.C.Error (throwErrnoIfMinus1, throwErrnoIfMinus1_, throwErrno)
import Foreign.Marshal.Alloc (alloca)
import Foreign.Ptr (Ptr, nullPtr)
import Foreign.Storable (peek)
import System.Posix.Types (CPid(..))


type SecurityContext = String

data EnforceMode = Enforcing 
                 | Permissive 
                 | Disabled deriving (Show, Ord, Eq)

isSELinuxEnabled :: IO Bool
isSELinuxEnabled = fmap i2b $ throwErrnoIfMinus1 "isSELinuxEnabled" c_is_selinux_enabled

isSELinuxMLSEnabled :: IO Bool
isSELinuxMLSEnabled = fmap i2b $ throwErrnoIfMinus1 "isSELinuxMLSEnabled" c_is_selinux_mls_enabled

getEnforceMode :: IO EnforceMode
getEnforceMode = alloca $ \ptr -> do
  r <- c_selinux_getenforcemode ptr
  if r == -1 
    then throwErrno "getEnforceMode"
    else fmap convertEnforceMode (peek ptr)
  where
    convertEnforceMode 1 = Enforcing
    convertEnforceMode 0 = Permissive
    convertEnforceMode _ = Disabled
    
getEnforce :: IO Bool
getEnforce = fmap i2b $ throwErrnoIfMinus1 "getEnforce" c_security_getenforce

getCon              = queryCon "getCon" c_getcon
getConRaw           = queryCon "getConRaw" c_getcon_raw
setCon              = withCon  "setCon" c_setcon
setConRaw           = withCon  "setConRaw" c_setcon_raw
getPidCon pid       = queryCon "getPidCon" (c_getpidcon pid)
getPidConRaw pid    = queryCon "getPidConRaw" (c_getpidcon_raw pid)
getPrevCon          = queryCon "getPrevCon" c_getprevcon
getPrevConRaw       = queryCon "getPrevConRaw" c_getprevcon_raw

getExecCon          = queryConMaybe "getExecCon" c_getexeccon
getExecConRaw       = queryConMaybe "getExecConRaw" c_getexeccon_raw
setExecCon          = withConMaybe "setExecCon" c_setexeccon
setExecConRaw       = withConMaybe "setExecConRaw" c_setexeccon_raw

getFsCreateCon      = queryConMaybe "getFsCreateCon" c_getfscreatecon
getFsCreateConRaw   = queryConMaybe "getFsCreateConRaw" c_getfscreatecon_raw
setFsCreateCon      = withConMaybe "setFsCreateCon" c_setfscreatecon
setFsCreateConRaw   = withConMaybe "setFsCreateConRaw" c_setfscreatecon_raw

getKeyCreateCon     = queryConMaybe "getKeyCreateCon" c_getkeycreatecon
getKeyCreateConRaw  = queryConMaybe "getKeyCreateConRaw" c_getkeycreatecon_raw
setKeyCreateCon     = withConMaybe "setKeyCreateCon" c_setkeycreatecon
setKeyCreateConRaw  = withConMaybe "setKeyCreateConRaw" c_setkeycreatecon_raw

getSockCreateCon    = queryConMaybe "getSockCreateCon" c_getsockcreatecon
getSockCreateConRaw = queryConMaybe "getSockCreateConRaw" c_getsockcreatecon_raw
setSockCreateCon    = withConMaybe "setSockCreateCon" c_setsockcreatecon
setSockCreateConRaw = withConMaybe "setSockCreateConRaw" c_setsockcreatecon_raw

getFileCon          = queryPathCon "getFileCon" c_getfilecon
getFileConRaw       = queryPathCon "getFileConRaw" c_getfilecon_raw
lgetFileCon         = queryPathCon "lgetFileCon" c_lgetfilecon
lgetFileConRaw      = queryPathCon "lgetFileConRw" c_lgetfilecon_raw
fgetFileCon fd      = queryCon "fgetFileCon" (c_fgetfilecon fd)
fgetFileConRaw fd   = queryCon "fgetFileConRaw" (c_fgetfilecon_raw fd)

setFileCon          = withPathCon "setFileCon" c_setfilecon
setFileConRaw       = withPathCon "setFileConRaw" c_setfilecon_raw
lsetFileCon         = withPathCon "lsetFileCon" c_lsetfilecon
lsetFileConRaw      = withPathCon "lsetfileConRaw" c_lsetfilecon_raw
fsetFileCon fd      = withCon "fsetFileCon" (c_fsetfilecon fd)
fsetFileConRaw fd   = withCon "fsetFileConRaw" (c_fsetfilecon_raw fd)

getPeerCon fd       = queryCon "getPeerCon" (c_getpeercon fd)
getPeerConRaw fd    = queryCon "getPeerConRaw" (c_getpeercon_raw fd)

getConfigPolicyRoot               = queryConfig c_selinux_policy_root
getConfigBinaryPolicyPath         = queryConfig c_selinux_binary_policy_path
getConfigFailsafeContextPath      = queryConfig c_selinux_failsafe_context_path
getConfigRemovableContextPath     = queryConfig c_selinux_removable_context_path
getConfigDefaultContextPath       = queryConfig c_selinux_default_context_path
getConfigUserContextsPath         = queryConfig c_selinux_user_contexts_path
getConfigFileContextPath          = queryConfig c_selinux_file_context_path
getConfigFileContextHomedirPath   = queryConfig c_selinux_file_context_homedir_path
getConfigFileContextLocalPath     = queryConfig c_selinux_file_context_local_path
getConfigFileContextSubsPath      = queryConfig c_selinux_file_context_subs_path
getConfigHomedirContextPath       = queryConfig c_selinux_homedir_context_path
getConfigMediaContextPath         = queryConfig c_selinux_media_context_path
getConfigVirtualDomainContextPath = queryConfig c_selinux_virtual_domain_context_path
getConfigVirtualImageContextPath  = queryConfig c_selinux_virtual_image_context_path
getConfigXContextPath             = queryConfig c_selinux_x_context_path
getConfigSepgsqlContextPath       = queryConfig c_selinux_sepgsql_context_path
getConfigContextsPath             = queryConfig c_selinux_contexts_path
getConfigSecurettyTypesPath       = queryConfig c_selinux_securetty_types_path
getConfigBooleansPath             = queryConfig c_selinux_booleans_path
getConfigCustomizableTypesPath    = queryConfig c_selinux_customizable_types_path
getConfigUsersPath                = queryConfig c_selinux_users_path
getConfigUsersconfPath            = queryConfig c_selinux_usersconf_path
getConfigTranslationsPath         = queryConfig c_selinux_translations_path
getConfigColorsPath               = queryConfig c_selinux_colors_path
getConfigNetfilterContextPath     = queryConfig c_selinux_netfilter_context_path
getConfigPath                     = queryConfig c_selinux_path

---------------------------------------------------------------------------

i2b :: CInt -> Bool
i2b 0 = False
i2b _ = True

withCon :: String -> (CSecurityContext -> IO CInt) -> SecurityContext -> IO ()
withCon e f c = withCString c $ \ctx -> throwErrnoIfMinus1_ e (f ctx)
  
withConMaybe :: String -> (CSecurityContext -> IO CInt) -> Maybe SecurityContext -> IO ()
withConMaybe e f (Just c) = withCon e f c
withConMaybe e f Nothing  = throwErrnoIfMinus1_ e (f nullPtr)
  
withPathCon :: String -> (CString -> CSecurityContext -> IO CInt) -> FilePath -> SecurityContext -> IO ()
withPathCon e f p c = withCString p $ \cp -> withCon e (f cp) c

queryPathCon :: String -> (CString -> Ptr CSecurityContext -> IO CInt) -> FilePath -> IO SecurityContext
queryPathCon e f p = withCString p $ \p' -> queryCon e (f p')

queryCon :: String -> (Ptr CSecurityContext -> IO CInt) -> IO SecurityContext
queryCon e f = alloca $ \ptr -> do
  r <- f ptr
  if r == -1
    then throwErrno e
    else peek ptr >>= \ptr' -> peekCString ptr' >>= \str -> c_freecon ptr' >> return str
                                              
queryConMaybe :: String -> (Ptr CSecurityContext -> IO CInt) -> IO (Maybe SecurityContext)
queryConMaybe e f = alloca $ \ptr -> do
  r <- f ptr
  if r == -1
    then throwErrno e
    else peek ptr >>= \ptr' -> 
      if ptr' == nullPtr
        then return Nothing 
        else peekCString ptr' >>= \str -> c_freecon ptr' >> return (Just str)
                                          
-- |A helper function for reading values from the selinux config file
queryConfig :: IO CString -> IO String
queryConfig f = f >>= peekCString >>= return


type CSecurityContext = CString
foreign import ccall unsafe "selinux/selinux.h is_selinux_enabled"     c_is_selinux_enabled     :: IO CInt
foreign import ccall unsafe "selinux/selinux.h is_selinux_mls_enabled" c_is_selinux_mls_enabled :: IO CInt
foreign import ccall unsafe "selinux/selinux.h selinux_getenforcemode" c_selinux_getenforcemode :: Ptr CInt -> IO CInt
foreign import ccall unsafe "selinux/selinux.h security_getenforce"    c_security_getenforce    :: IO CInt
foreign import ccall unsafe "selinux/selinux.h freecon"                c_freecon                :: CSecurityContext -> IO ()

foreign import ccall unsafe "selinux/selinux.h getcon"                 c_getcon                 :: Ptr CSecurityContext -> IO CInt
foreign import ccall unsafe "selinux/selinux.h getcon_raw"             c_getcon_raw             :: Ptr CSecurityContext -> IO CInt
foreign import ccall unsafe "selinux/selinux.h setcon"                 c_setcon                 :: CSecurityContext     -> IO CInt
foreign import ccall unsafe "selinux/selinux.h setcon_raw"             c_setcon_raw             :: CSecurityContext     -> IO CInt
foreign import ccall unsafe "selinux/selinux.h getpidcon"              c_getpidcon              :: CPid -> Ptr CSecurityContext -> IO CInt
foreign import ccall unsafe "selinux/selinux.h getpidcon_raw"          c_getpidcon_raw          :: CPid -> Ptr CSecurityContext -> IO CInt
foreign import ccall unsafe "selinux/selinux.h getprevcon"             c_getprevcon             :: Ptr CSecurityContext -> IO CInt
foreign import ccall unsafe "selinux/selinux.h getprevcon_raw"         c_getprevcon_raw         :: Ptr CSecurityContext -> IO CInt
foreign import ccall unsafe "selinux/selinux.h getexeccon"             c_getexeccon             :: Ptr CSecurityContext -> IO CInt
foreign import ccall unsafe "selinux/selinux.h getexeccon_raw"         c_getexeccon_raw         :: Ptr CSecurityContext -> IO CInt
foreign import ccall unsafe "selinux/selinux.h setexeccon"             c_setexeccon             :: CSecurityContext     -> IO CInt
foreign import ccall unsafe "selinux/selinux.h setexeccon_raw"         c_setexeccon_raw         :: CSecurityContext     -> IO CInt
foreign import ccall unsafe "selinux/selinux.h getfscreatecon"         c_getfscreatecon         :: Ptr CSecurityContext -> IO CInt
foreign import ccall unsafe "selinux/selinux.h getfscreatecon_raw"     c_getfscreatecon_raw     :: Ptr CSecurityContext -> IO CInt
foreign import ccall unsafe "selinux/selinux.h setfscreatecon"         c_setfscreatecon         :: CSecurityContext     -> IO CInt
foreign import ccall unsafe "selinux/selinux.h setfscreatecon_raw"     c_setfscreatecon_raw     :: CSecurityContext     -> IO CInt
foreign import ccall unsafe "selinux/selinux.h getkeycreatecon"        c_getkeycreatecon        :: Ptr CSecurityContext -> IO CInt
foreign import ccall unsafe "selinux/selinux.h getkeycreatecon_raw"    c_getkeycreatecon_raw    :: Ptr CSecurityContext -> IO CInt
foreign import ccall unsafe "selinux/selinux.h setkeycreatecon"        c_setkeycreatecon        :: CSecurityContext     -> IO CInt
foreign import ccall unsafe "selinux/selinux.h setkeycreatecon_raw"    c_setkeycreatecon_raw    :: CSecurityContext     -> IO CInt
foreign import ccall unsafe "selinux/selinux.h getsockcreatecon"       c_getsockcreatecon       :: Ptr CSecurityContext -> IO CInt
foreign import ccall unsafe "selinux/selinux.h getsockcreatecon_raw"   c_getsockcreatecon_raw   :: Ptr CSecurityContext -> IO CInt
foreign import ccall unsafe "selinux/selinux.h setsockcreatecon"       c_setsockcreatecon       :: CSecurityContext     -> IO CInt
foreign import ccall unsafe "selinux/selinux.h setsockcreatecon_raw"   c_setsockcreatecon_raw   :: CSecurityContext     -> IO CInt

foreign import ccall unsafe "selinux/selinux.h getfilecon"             c_getfilecon             :: CString -> Ptr CSecurityContext -> IO CInt
foreign import ccall unsafe "selinux/selinux.h getfilecon_raw"         c_getfilecon_raw         :: CString -> Ptr CSecurityContext -> IO CInt
foreign import ccall unsafe "selinux/selinux.h lgetfilecon"            c_lgetfilecon            :: CString -> Ptr CSecurityContext -> IO CInt
foreign import ccall unsafe "selinux/selinux.h lgetfilecon_raw"        c_lgetfilecon_raw        :: CString -> Ptr CSecurityContext -> IO CInt
foreign import ccall unsafe "selinux/selinux.h fgetfilecon"            c_fgetfilecon            :: CInt -> Ptr CSecurityContext -> IO CInt
foreign import ccall unsafe "selinux/selinux.h fgetfilecon_raw"        c_fgetfilecon_raw        :: CInt -> Ptr CSecurityContext -> IO CInt

foreign import ccall unsafe "selinux/selinux.h setfilecon"             c_setfilecon             :: CString -> CSecurityContext -> IO CInt
foreign import ccall unsafe "selinux/selinux.h setfilecon_raw"         c_setfilecon_raw         :: CString -> CSecurityContext -> IO CInt
foreign import ccall unsafe "selinux/selinux.h lsetfilecon"            c_lsetfilecon            :: CString -> CSecurityContext -> IO CInt
foreign import ccall unsafe "selinux/selinux.h lsetfilecon_raw"        c_lsetfilecon_raw        :: CString -> CSecurityContext -> IO CInt
foreign import ccall unsafe "selinux/selinux.h fsetfilecon"            c_fsetfilecon            :: CInt -> CSecurityContext -> IO CInt
foreign import ccall unsafe "selinux/selinux.h fsetfilecon_raw"        c_fsetfilecon_raw        :: CInt -> CSecurityContext -> IO CInt

foreign import ccall unsafe "selinux/selinux.h getpeercon"             c_getpeercon             :: CInt -> Ptr CSecurityContext -> IO CInt
foreign import ccall unsafe "selinux/selinux.h getpeercon_raw"         c_getpeercon_raw         :: CInt -> Ptr CSecurityContext -> IO CInt

foreign import ccall unsafe "selinux/selinux.h selinux_policy_root"    c_selinux_policy_root    :: IO CString

-- These functions return the paths to specific files under the  
-- policy root directory.
foreign import ccall unsafe "selinux/selinux.h selinux_binary_policy_path"          c_selinux_binary_policy_path          :: IO CString
foreign import ccall unsafe "selinux/selinux.h selinux_failsafe_context_path"       c_selinux_failsafe_context_path       :: IO CString
foreign import ccall unsafe "selinux/selinux.h selinux_removable_context_path"      c_selinux_removable_context_path      :: IO CString
foreign import ccall unsafe "selinux/selinux.h selinux_default_context_path"        c_selinux_default_context_path        :: IO CString
foreign import ccall unsafe "selinux/selinux.h selinux_user_contexts_path"          c_selinux_user_contexts_path          :: IO CString
foreign import ccall unsafe "selinux/selinux.h selinux_file_context_path"           c_selinux_file_context_path           :: IO CString
foreign import ccall unsafe "selinux/selinux.h selinux_file_context_homedir_path"   c_selinux_file_context_homedir_path   :: IO CString
foreign import ccall unsafe "selinux/selinux.h selinux_file_context_local_path"     c_selinux_file_context_local_path     :: IO CString
foreign import ccall unsafe "selinux/selinux.h selinux_file_context_subs_path"      c_selinux_file_context_subs_path      :: IO CString
foreign import ccall unsafe "selinux/selinux.h selinux_homedir_context_path"        c_selinux_homedir_context_path        :: IO CString
foreign import ccall unsafe "selinux/selinux.h selinux_media_context_path"          c_selinux_media_context_path          :: IO CString
foreign import ccall unsafe "selinux/selinux.h selinux_virtual_domain_context_path" c_selinux_virtual_domain_context_path :: IO CString
foreign import ccall unsafe "selinux/selinux.h selinux_virtual_image_context_path"  c_selinux_virtual_image_context_path  :: IO CString
foreign import ccall unsafe "selinux/selinux.h selinux_x_context_path"              c_selinux_x_context_path              :: IO CString
foreign import ccall unsafe "selinux/selinux.h selinux_sepgsql_context_path"        c_selinux_sepgsql_context_path        :: IO CString
foreign import ccall unsafe "selinux/selinux.h selinux_contexts_path"               c_selinux_contexts_path               :: IO CString
foreign import ccall unsafe "selinux/selinux.h selinux_securetty_types_path"        c_selinux_securetty_types_path        :: IO CString
foreign import ccall unsafe "selinux/selinux.h selinux_booleans_path"               c_selinux_booleans_path               :: IO CString
foreign import ccall unsafe "selinux/selinux.h selinux_customizable_types_path"     c_selinux_customizable_types_path     :: IO CString
foreign import ccall unsafe "selinux/selinux.h selinux_users_path"                  c_selinux_users_path                  :: IO CString
foreign import ccall unsafe "selinux/selinux.h selinux_usersconf_path"              c_selinux_usersconf_path              :: IO CString
foreign import ccall unsafe "selinux/selinux.h selinux_translations_path"           c_selinux_translations_path           :: IO CString
foreign import ccall unsafe "selinux/selinux.h selinux_colors_path"                 c_selinux_colors_path                 :: IO CString
foreign import ccall unsafe "selinux/selinux.h selinux_netfilter_context_path"      c_selinux_netfilter_context_path      :: IO CString
foreign import ccall unsafe "selinux/selinux.h selinux_path"                        c_selinux_path                        :: IO CString