27 |
27 |
( GanetiDaemon(..)
|
28 |
28 |
, MiscGroup(..)
|
29 |
29 |
, GanetiGroup(..)
|
30 |
|
, RuntimeEnts
|
|
30 |
, RuntimeEnts(..)
|
31 |
31 |
, daemonName
|
32 |
32 |
, daemonOnlyOnMaster
|
33 |
33 |
, daemonLogBase
|
... | ... | |
42 |
42 |
, verifyDaemonUser
|
43 |
43 |
) where
|
44 |
44 |
|
45 |
|
import Control.Exception
|
46 |
45 |
import Control.Monad
|
|
46 |
import Control.Monad.Error
|
47 |
47 |
import qualified Data.Map as M
|
48 |
48 |
import System.Exit
|
49 |
49 |
import System.FilePath
|
50 |
50 |
import System.IO
|
51 |
|
import System.IO.Error
|
52 |
51 |
import System.Posix.Types
|
53 |
52 |
import System.Posix.User
|
54 |
53 |
import Text.Printf
|
... | ... | |
78 |
77 |
| ExtraGroup MiscGroup
|
79 |
78 |
deriving (Show, Eq, Ord)
|
80 |
79 |
|
81 |
|
type RuntimeEnts = (M.Map GanetiDaemon UserID, M.Map GanetiGroup GroupID)
|
|
80 |
data RuntimeEnts = RuntimeEnts
|
|
81 |
{ reUserToUid :: M.Map GanetiDaemon UserID
|
|
82 |
, reUidToUser :: M.Map UserID String
|
|
83 |
, reGroupToGid :: M.Map GanetiGroup GroupID
|
|
84 |
, reGidToGroup :: M.Map GroupID String
|
|
85 |
}
|
82 |
86 |
|
83 |
87 |
-- | Returns the daemon name for a given daemon.
|
84 |
88 |
daemonName :: GanetiDaemon -> String
|
... | ... | |
175 |
179 |
allGroups = map DaemonGroup [minBound..maxBound] ++
|
176 |
180 |
map ExtraGroup [minBound..maxBound]
|
177 |
181 |
|
178 |
|
ignoreDoesNotExistErrors :: IO a -> IO (Result a)
|
179 |
|
ignoreDoesNotExistErrors value = do
|
180 |
|
result <- tryJust (\e -> if isDoesNotExistError e
|
181 |
|
then Just (show e)
|
182 |
|
else Nothing) value
|
183 |
|
return $ eitherToResult result
|
184 |
|
|
185 |
182 |
-- | Computes the group/user maps.
|
186 |
|
getEnts :: IO (Result RuntimeEnts)
|
|
183 |
getEnts :: (Error e) => ResultT e IO RuntimeEnts
|
187 |
184 |
getEnts = do
|
188 |
|
users <- mapM (\daemon -> do
|
189 |
|
entry <- ignoreDoesNotExistErrors .
|
190 |
|
getUserEntryForName .
|
191 |
|
daemonUser $ daemon
|
192 |
|
return (entry >>= \e -> return (daemon, userID e))
|
193 |
|
) [minBound..maxBound]
|
194 |
|
groups <- mapM (\group -> do
|
195 |
|
entry <- ignoreDoesNotExistErrors .
|
196 |
|
getGroupEntryForName .
|
197 |
|
daemonGroup $ group
|
198 |
|
return (entry >>= \e -> return (group, groupID e))
|
199 |
|
) allGroups
|
200 |
|
return $ do -- 'Result' monad
|
201 |
|
users' <- sequence users
|
202 |
|
groups' <- sequence groups
|
203 |
|
let usermap = M.fromList users'
|
204 |
|
groupmap = M.fromList groups'
|
205 |
|
return (usermap, groupmap)
|
|
185 |
let userOf = liftM userID . liftIO . getUserEntryForName . daemonUser
|
|
186 |
let groupOf = liftM groupID . liftIO . getGroupEntryForName . daemonGroup
|
|
187 |
let allDaemons = [minBound..maxBound] :: [GanetiDaemon]
|
|
188 |
users <- mapM userOf allDaemons
|
|
189 |
groups <- mapM groupOf allGroups
|
|
190 |
return $ RuntimeEnts
|
|
191 |
(M.fromList $ zip allDaemons users)
|
|
192 |
(M.fromList $ zip users (map daemonUser allDaemons))
|
|
193 |
(M.fromList $ zip allGroups groups)
|
|
194 |
(M.fromList $ zip groups (map daemonGroup allGroups))
|
206 |
195 |
|
207 |
196 |
-- | Checks whether a daemon runs as the right user.
|
208 |
197 |
verifyDaemonUser :: GanetiDaemon -> RuntimeEnts -> IO ()
|
... | ... | |
210 |
199 |
myuid <- getEffectiveUserID
|
211 |
200 |
-- note: we use directly ! as lookup failues shouldn't happen, due
|
212 |
201 |
-- to the above map construction
|
213 |
|
checkUidMatch (daemonName daemon) ((M.!) (fst ents) daemon) myuid
|
|
202 |
checkUidMatch (daemonName daemon) ((M.!) (reUserToUid ents) daemon) myuid
|
214 |
203 |
|
215 |
204 |
-- | Check that two UIDs are matching or otherwise exit.
|
216 |
205 |
checkUidMatch :: String -> UserID -> UserID -> IO ()
|