GF shell restricted mode

By setting the environment variable GF_RESTRICTED before starting GF, the shell
will be run in restricted mode. This will prevent the GF shell from starting
arbitrary system commands (most uses of System.Cmd.system are blocked) and
writing arbitrary files (most commands that use writeFile et al are blocked).

Restricted mode is intended minimize the potential security risks involved
in allowing public access to the GF shell over the internet. It should be used
in conjuction with system level protection mechanisms (e.g. file permissions)
to make sure that a publicly acessible GF shell does not give access to parts
of the system that should not be publicly accessible.
This commit is contained in:
hallgren
2011-03-03 15:42:57 +00:00
parent 65e1ea2fc4
commit affdcf421e
3 changed files with 38 additions and 20 deletions

View File

@@ -45,7 +45,7 @@ import Data.Binary (encodeFile)
import Data.List import Data.List
import Data.Maybe import Data.Maybe
import qualified Data.Map as Map import qualified Data.Map as Map
import System.Cmd --import System.Cmd(system) -- use GF.Infra.UseIO.restricedSystem instead!
import Text.PrettyPrint import Text.PrettyPrint
import Data.List (sort) import Data.List (sort)
import Debug.Trace import Debug.Trace
@@ -172,8 +172,8 @@ allCommands env@(pgf, mos) = Map.fromList [
let view = optViewGraph opts let view = optViewGraph opts
let format = optViewFormat opts let format = optViewFormat opts
writeUTF8File (file "dot") grph writeUTF8File (file "dot") grph
system $ "dot -T" ++ format ++ " " ++ file "dot" ++ " > " ++ file format restrictedSystem $ "dot -T" ++ format ++ " " ++ file "dot" ++ " > " ++ file format
system $ view ++ " " ++ file format restrictedSystem $ view ++ " " ++ file format
return void return void
else return $ fromString grph, else return $ fromString grph,
examples = [ examples = [
@@ -769,9 +769,9 @@ allCommands env@(pgf, mos) = Map.fromList [
exec = \opts arg -> do exec = \opts arg -> do
let tmpi = "_tmpi" --- let tmpi = "_tmpi" ---
let tmpo = "_tmpo" let tmpo = "_tmpo"
writeFile tmpi $ toString arg restricted $ writeFile tmpi $ toString arg
let syst = optComm opts ++ " " ++ tmpi let syst = optComm opts ++ " " ++ tmpi
system $ syst ++ " <" ++ tmpi ++ " >" ++ tmpo restrictedSystem $ syst ++ " <" ++ tmpi ++ " >" ++ tmpo
s <- readFile tmpo s <- readFile tmpo
return $ fromString s, return $ fromString s,
flags = [ flags = [
@@ -843,9 +843,9 @@ allCommands env@(pgf, mos) = Map.fromList [
let file s = "_grphd." ++ s let file s = "_grphd." ++ s
let view = optViewGraph opts let view = optViewGraph opts
let format = optViewFormat opts let format = optViewFormat opts
writeUTF8File (file "dot") grphs restricted $ writeUTF8File (file "dot") grphs
system $ "dot -T" ++ format ++ " " ++ file "dot" ++ " > " ++ file format restrictedSystem $ "dot -T" ++ format ++ " " ++ file "dot" ++ " > " ++ file format
system $ view ++ " " ++ file format restrictedSystem $ view ++ " " ++ file format
return void return void
else return $ fromString grphs, else return $ fromString grphs,
examples = [ examples = [
@@ -884,9 +884,9 @@ allCommands env@(pgf, mos) = Map.fromList [
let file s = "_grph." ++ s let file s = "_grph." ++ s
let view = optViewGraph opts let view = optViewGraph opts
let format = optViewFormat opts let format = optViewFormat opts
writeUTF8File (file "dot") grph restricted $ writeUTF8File (file "dot") grph
system $ "dot -T" ++ format ++ " " ++ file "dot" ++ " > " ++ file format restrictedSystem $ "dot -T" ++ format ++ " " ++ file "dot" ++ " > " ++ file format
system $ view ++ " " ++ file format restrictedSystem $ view ++ " " ++ file format
return void return void
else return $ fromString grph, else return $ fromString grph,
examples = [ examples = [
@@ -929,9 +929,9 @@ allCommands env@(pgf, mos) = Map.fromList [
let file s = "_grph." ++ s let file s = "_grph." ++ s
let view = optViewGraph opts let view = optViewGraph opts
let format = optViewFormat opts let format = optViewFormat opts
writeUTF8File (file "dot") grph restricted $ writeUTF8File (file "dot") grph
system $ "dot -T" ++ format ++ " " ++ file "dot" ++ " > " ++ file format restrictedSystem $ "dot -T" ++ format ++ " " ++ file "dot" ++ " > " ++ file format
system $ view ++ " " ++ file format restrictedSystem $ view ++ " " ++ file format
return void return void
else return $ fromString grph, else return $ fromString grph,
examples = [ examples = [
@@ -955,8 +955,8 @@ allCommands env@(pgf, mos) = Map.fromList [
exec = \opts arg -> do exec = \opts arg -> do
let file = valStrOpts "file" "_gftmp" opts let file = valStrOpts "file" "_gftmp" opts
if isOpt "append" opts if isOpt "append" opts
then appendFile file (toString arg) then restricted $ appendFile file (toString arg)
else writeUTF8File file (toString arg) else restricted $ writeUTF8File file (toString arg)
return void, return void,
options = [ options = [
("append","append to file, instead of overwriting it") ("append","append to file, instead of overwriting it")

View File

@@ -26,6 +26,7 @@ import System.IO.Error
import System.Environment import System.Environment
import System.Exit import System.Exit
import System.CPUTime import System.CPUTime
import System.Cmd
import Text.Printf import Text.Printf
import Control.Monad import Control.Monad
import Control.Exception(evaluate) import Control.Exception(evaluate)
@@ -191,3 +192,19 @@ writeUTF8File fpath content = do
hSetEncoding h utf8 hSetEncoding h utf8
hPutStr h content hPutStr h content
hClose h hClose h
-- * Functions to limit acesss to arbitrary IO and system commands
restricted io =
either (const io) (const $ fail message) =<< try (getEnv "GF_RESTRICTED")
where
message =
"This operation is not allowed when GF is running in restricted mode."
restrictedSystem = restricted . system
-- Because GHC adds the confusing text "user error" for failures cased by
-- calls to fail.
ioErrorText e = if isUserError e
then ioeGetErrorString e
else show e

View File

@@ -36,7 +36,6 @@ import qualified Data.Map as Map
import qualified Data.ByteString.Char8 as BS import qualified Data.ByteString.Char8 as BS
import qualified Text.ParserCombinators.ReadP as RP import qualified Text.ParserCombinators.ReadP as RP
import System.IO import System.IO
import System.Cmd
import System.CPUTime import System.CPUTime
import System.Directory import System.Directory
import Control.Exception import Control.Exception
@@ -107,7 +106,7 @@ loop opts gfenv0 = do
r <- runInterruptibly $ case pwords of r <- runInterruptibly $ case pwords of
"!":ws -> do "!":ws -> do
system $ unwords ws restrictedSystem $ unwords ws
loopNewCPU gfenv loopNewCPU gfenv
"cc":ws -> do "cc":ws -> do
let let
@@ -154,7 +153,7 @@ loop opts gfenv0 = do
let stop = case ws of let stop = case ws of
('-':'o':'n':'l':'y':'=':fs):_ -> Just $ chunks ',' fs ('-':'o':'n':'l':'y':'=':fs):_ -> Just $ chunks ',' fs
_ -> Nothing _ -> Nothing
writeFile "_gfdepgraph.dot" (depGraph stop sgr) restricted $ writeFile "_gfdepgraph.dot" (depGraph stop sgr)
putStrLn "wrote graph in file _gfdepgraph.dot" putStrLn "wrote graph in file _gfdepgraph.dot"
loopNewCPU gfenv loopNewCPU gfenv
"eh":w:_ -> do "eh":w:_ -> do
@@ -220,9 +219,11 @@ loop opts gfenv0 = do
interpretCommandLine env s0 interpretCommandLine env s0
loopNewCPU gfenv loopNewCPU gfenv
-- gfenv' <- return $ either (const gfenv) id r -- gfenv' <- return $ either (const gfenv) id r
gfenv' <- either (\e -> (print e >> return gfenv)) return r gfenv' <- either (\e -> (printException e >> return gfenv)) return r
loop opts gfenv' loop opts gfenv'
printException e = maybe (print e) (putStrLn . ioErrorText) (fromException e)
checkComputeTerm sgr t = do checkComputeTerm sgr t = do
mo <- maybe (Bad "no source grammar in scope") return $ greatestResource sgr mo <- maybe (Bad "no source grammar in scope") return $ greatestResource sgr
((t,_),_) <- runCheck $ do t <- renameSourceTerm sgr mo t ((t,_),_) <- runCheck $ do t <- renameSourceTerm sgr mo t