diff options
author | sternenseemann <0rpkxez4ksa01gb3typccl0i@systemli.org> | 2021-03-15 14:11:30 +0100 |
---|---|---|
committer | sterni <sternenseemann@systemli.org> | 2021-03-15 14:25:36 +0100 |
commit | 58973d3eb8e88ac4979a7057234ca59d9628b96c (patch) | |
tree | c26eee307de5c2f7f35cc246b6dcf63f61c54de8 | |
parent | 17c6cb0ae61dd31078d95bf0acf2ec559a09888d (diff) |
feat(Network.Gopher): remove cRunUserName from GopherConfig
It is not necessary to implement privilege dropping as part of the main Network.Gopher API anymore since it can easily be done in the ready action passed to runGopherManual. To ease transition we expose an exception-throwing version of the formerly internal function dropPrivileges via Network.Gopher.Util which can be plugged into the ready action of runGopherManual if desired. feat(server): exit if dropPrivileges fails We don't catch the exception of dropPrivileges anymore, exiting on a failure related to it instead (user doesn't exist, insufficient privileges). BREAKING CHANGES: * cRunUserName has been removed from GopherConfig. This breaks all library usage which relied on this feature. * The spacecookie server daemon now exits if changing user fails instead of just logging an error like before.
-rw-r--r-- | CHANGELOG.md | 7 | ||||
-rw-r--r-- | server/Main.hs | 21 | ||||
-rw-r--r-- | src/Network/Gopher.hs | 26 | ||||
-rw-r--r-- | src/Network/Gopher/Util.hs | 14 |
4 files changed, 39 insertions, 29 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 8269ae2..1ee5763 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -69,6 +69,8 @@ settings. can't be parsed. * An error is now logged when a gophermap file doesn't parse and the standard directory response is used as fallback. +* Exit if dropping privileges fails instead of just logging an error like before. + See [#45](https://github.com/sternenseemann/spacecookie/pull/45). ### Library @@ -219,6 +221,11 @@ The remaining, less significant changes are: * Requests from clients are now limited to 1MB in size and are received by a single call to `recv(2)`. If this causes issues with any gopher client, please open an issue. +* `cRunUserName` has been removed from `GopherConfig` since the functionality + doesn't need special treatment as users can implement it easily via the + ready action of `runGopherManual`. The formerly internal `dropPrivileges` + function is now available via `Network.Gopher.Util` to be used for this + purpose. See [#45](https://github.com/sternenseemann/spacecookie/pull/45). ## 0.2.1.2 Bump fast-logger diff --git a/server/Main.hs b/server/Main.hs index 37be629..30c57e4 100644 --- a/server/Main.hs +++ b/server/Main.hs @@ -6,7 +6,7 @@ import Network.Spacecookie.Systemd import Paths_spacecookie (version) import Network.Gopher -import Network.Gopher.Util (sanitizePath, boolToMaybe) +import Network.Gopher.Util (sanitizePath, boolToMaybe, dropPrivileges) import Network.Gopher.Util.Gophermap import qualified Data.ByteString as B import Control.Applicative ((<|>)) @@ -64,12 +64,12 @@ runServer configFile = do { cServerName = serverName config , cListenAddr = listenAddr config , cServerPort = serverPort config - , cRunUserName = runUserName config , cLogHandler = logHandler } + logIO = fromMaybe noLog logHandler let setupFailureHandler e = do - (fromMaybe noLog logHandler) GopherLogLevelError + logIO GopherLogLevelError $ "Exception occurred in setup step: " <> toGopherLogStr (show e) logStopAction @@ -81,12 +81,23 @@ runServer configFile = do catchSetupFailure $ runGopherManual (systemdSocket cfg) - (notifyReady >> pure ()) + (afterSocketSetup logIO config) (\s -> do _ <- notifyStopping logStopAction systemdStoreOrClose s) - cfg $ spacecookie (fromMaybe noLog logHandler) + cfg + (spacecookie logIO) + +afterSocketSetup :: GopherLogHandler -> Config -> IO () +afterSocketSetup logIO cfg = do + case runUserName cfg of + Nothing -> pure () + Just u -> do + dropPrivileges u + logIO GopherLogLevelInfo $ "Changed to user " <> toGopherLogStr u + _ <- notifyReady + pure () printUsage :: IO () printUsage = do diff --git a/src/Network/Gopher.hs b/src/Network/Gopher.hs index 00f7804..45f4d2d 100644 --- a/src/Network/Gopher.hs +++ b/src/Network/Gopher.hs @@ -89,7 +89,7 @@ import Network.Gopher.Util.Gophermap import Network.Gopher.Util.Socket import Control.Concurrent (forkIO, ThreadId ()) -import Control.Exception (bracket, catch, handle, throw, SomeException (), Exception ()) +import Control.Exception (bracket, catch, throw, SomeException (), Exception ()) import Control.Monad (forever, when, void) import Control.Monad.IO.Class (liftIO, MonadIO (..)) import Control.Monad.Reader (ask, runReaderT, MonadReader (..), ReaderT (..)) @@ -104,7 +104,6 @@ import System.Socket hiding (Error (..)) import System.Socket.Family.Inet6 import System.Socket.Type.Stream import System.Socket.Protocol.TCP -import System.Posix.User -- | Necessary information to handle gopher requests data GopherConfig @@ -118,8 +117,6 @@ data GopherConfig -- If 'Nothing', listen on all addresses. , cServerPort :: Integer -- ^ Port to listen on - , cRunUserName :: Maybe String - -- ^ User to run the process as , cLogHandler :: Maybe GopherLogHandler -- ^ 'IO' action spacecookie will call to output its log messages. -- If it is 'Nothing', logging is disabled. See [the logging section](#logging) @@ -129,7 +126,7 @@ data GopherConfig -- | Default 'GopherConfig' describing a server on @localhost:70@ with -- no registered log handler. defaultConfig :: GopherConfig -defaultConfig = GopherConfig "localhost" Nothing 70 Nothing Nothing +defaultConfig = GopherConfig "localhost" Nothing 70 Nothing -- | Type for an user defined 'IO' action which handles logging a -- given 'GopherLogStr' of a given 'GopherLogLevel'. It may @@ -232,15 +229,6 @@ receiveRequest sock = do then B.init req else req -dropPrivileges :: String -> IO Bool -dropPrivileges username = - handle ((\_ -> return False) :: SomeException -> IO Bool) - $ do - user <- getUserEntryForName username - setGroupID $ userGroupID user - setUserID $ userID user - return True - -- | Auxiliary function that sets up the listening socket for -- 'runGopherManual' correctly and starts to listen. -- @@ -315,16 +303,6 @@ runGopherManual sockAction ready term cfg f = bracket addr <- liftIO $ getAddress sock logInfo $ "Listening on " <> toGopherLogStr addr - -- Change UID and GID if necessary - case cRunUserName cfg of - Nothing -> pure () - Just u -> do - success <- liftIO $ dropPrivileges u - if success - then logInfo $ "Changed to user " <> toGopherLogStr u - else logError $ "Can' change to user " <> toGopherLogStr u - -- TODO: abort? - liftIO $ ready forever $ acceptAndHandle sock) diff --git a/src/Network/Gopher/Util.hs b/src/Network/Gopher/Util.hs index cf022e2..02edeb7 100644 --- a/src/Network/Gopher/Util.hs +++ b/src/Network/Gopher/Util.hs @@ -10,6 +10,7 @@ module Network.Gopher.Util ( -- * Security sanitizePath , sanitizeIfNotUrl + , dropPrivileges -- * String Encoding , asciiOrd , asciiChr @@ -27,6 +28,7 @@ import Data.Char (ord, chr, toLower) import qualified Data.String.UTF8 as U import Data.Word (Word8 ()) import System.FilePath.Posix.ByteString (RawFilePath, normalise, joinPath, splitPath) +import System.Posix.User -- | 'chr' a 'Word8' asciiChr :: Word8 -> Char @@ -82,3 +84,15 @@ sanitizeIfNotUrl path = boolToMaybe :: Bool -> a -> Maybe a boolToMaybe True a = Just a boolToMaybe False _ = Nothing + +-- | Call 'setGroupID' and 'setUserID' to switch to +-- the given user and their primary group. +-- Requires special privileges. +-- Will raise an exception if either the user +-- does not exist or the current user has no +-- permission to change UID/GID. +dropPrivileges :: String -> IO () +dropPrivileges username = do + user <- getUserEntryForName username + setGroupID $ userGroupID user + setUserID $ userID user |