aboutsummaryrefslogtreecommitdiff
path: root/Utility/Su.hs
blob: a0500e4837db1bdc5e3b7e52eea1285c9856f41e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
{- su to root
 -
 - Copyright 2016 Joey Hess <id@joeyh.name>
 -
 - License: BSD-2-clause
 -}

{-# LANGUAGE CPP #-}

module Utility.Su where

import Common

#ifndef mingw32_HOST_OS
import Utility.Env
import System.Posix.IO
import System.Posix.Terminal
#endif

data WhosePassword
	= RootPassword
	| UserPassword
	| SomePassword
	-- ^ may be user or root; su program should indicate which
	deriving (Show)

data PasswordPrompt
	= WillPromptPassword WhosePassword
	| MayPromptPassword WhosePassword
	| NoPromptPassword
	deriving (Show)

describePasswordPrompt :: PasswordPrompt -> Maybe String
describePasswordPrompt (WillPromptPassword whose) = Just $
	"You will be prompted for " ++ describeWhosePassword whose ++ " password"
describePasswordPrompt (MayPromptPassword whose) = Just $
	"You may be prompted for " ++ describeWhosePassword whose ++ " password"
describePasswordPrompt NoPromptPassword = Nothing

describeWhosePassword :: WhosePassword -> String
describeWhosePassword RootPassword = "root's"
describeWhosePassword UserPassword = "your"
describeWhosePassword SomePassword = "a"

data SuCommand = SuCommand PasswordPrompt String [CommandParam]
	deriving (Show)

describePasswordPrompt' :: Maybe SuCommand -> Maybe String
describePasswordPrompt' (Just (SuCommand p _ _)) = describePasswordPrompt p
describePasswordPrompt' Nothing = Nothing

runSuCommand :: (Maybe SuCommand) -> IO Bool
runSuCommand (Just (SuCommand _ cmd ps)) = boolSystem cmd ps
runSuCommand Nothing = return False

-- Generates a SuCommand that runs a command as root, fairly portably.
--
-- Does not use sudo commands if something else is available, because
-- the user may not be in sudoers and we couldn't differentiate between
-- that and the command failing. Although, some commands like gksu
-- decide based on the system's configuration whether sudo should be used.
mkSuCommand :: String -> [CommandParam] -> IO (Maybe SuCommand)
#ifndef mingw32_HOST_OS
mkSuCommand cmd ps = firstM (\(SuCommand _ p _) -> inPath p) =<< selectcmds
  where
	selectcmds = ifM (inx <||> (not <$> atconsole))
		( return (graphicalcmds ++ consolecmds)
		, return consolecmds
		)
	
	inx = isJust <$> getEnv "DISPLAY"
	atconsole = queryTerminal stdInput

	-- These will only work when the user is logged into a desktop.
	graphicalcmds =
		[ SuCommand (MayPromptPassword SomePassword) "gksu"
			[Param shellcmd]
		, SuCommand (MayPromptPassword SomePassword) "kdesu"
			[Param shellcmd]
		-- Available in Debian's menu package; knows about lots of
		-- ways to gain root.
		, SuCommand (MayPromptPassword SomePassword) "su-to-root"
			[Param "-X", Param "-c", Param shellcmd]
		-- OSX native way to run a command as root, prompts in GUI
		, SuCommand (WillPromptPassword RootPassword) "osascript"
			[Param "-e", Param ("do shell script \"" ++ shellcmd ++ "\" with administrator privileges")]
		]
	
	-- These will only work when run in a console.
	consolecmds = 
		[ SuCommand (WillPromptPassword RootPassword) "su"
			[Param "-c", Param shellcmd]
		, SuCommand (MayPromptPassword UserPassword) "sudo"
			([Param cmd] ++ ps)
		, SuCommand (MayPromptPassword SomePassword) "su-to-root"
			[Param "-c", Param shellcmd]
		]
	
	shellcmd = unwords $ map shellEscape (cmd:toCommand ps)
#else
-- For windows, we assume the user has administrator access.
mkSuCommand cmd ps = return $ Just $ SuCommand NoPromptPassword cmd ps
#endif