pam 0.1 → 0.2.0.0
raw patch · 9 files changed
+257/−69 lines, 9 filesnew-uploader
Files
- CONTRIBUTING.md +26/−0
- ChangeLog.md +13/−0
- README.md +43/−0
- pam.cabal +37/−24
- src/System/Posix/PAM.hs +44/−24
- src/System/Posix/PAM/Internals.chs +0/−1
- src/System/Posix/PAM/LowLevel.hs +18/−18
- src/System/Posix/PAM/Types.hs +10/−2
- stack.yaml +66/−0
+ CONTRIBUTING.md view
@@ -0,0 +1,26 @@+# Contributing:++## Feature requests:+If you have a feature request open an issue and use the `enhancement` label. Make sure you describe the feature you want in sufficient detail++## Issues:+If you find an issue then firstly search to see if it has already been reported. If not then open a new issue with the label `bug`. Make sure you include details of:+- The events leading to the issue+- What the issue is/how it manifests+- The PAM logs relating to the issue++Try to give the "minimum" example to reproduce the bug where possible++## Pull requests:+When opening a pull request:+- Open a draft PR when you start working. This allows others to see you are working on a bug/feature and avoids duplicate work+- Ensure your code meets the coding conventions listed below+- Try to stick to 1 feature/bug per PR+- Include details of what your changes do, for example what feature you implement or which bug you fix++## Code Conventions:+- Use 4 spaces for tabs+- Include type signatures for all top level functions+- Include Haddock comments for all functions/type definitions, as well as inline comments explaining logic where it may not be clear+- Try to avoid long lines - if a line is looking long then split it across 2 lines, or use `let...in...` or `...where` if it makes your code more readable+- Update the README with new features or changes to existing ones
+ ChangeLog.md view
@@ -0,0 +1,13 @@+# Change Log:++## v0.2.0.0:+- `authenticate` function now returns `MonadIO m => m PamRetCode` rather than `IO (Either Int ())`+- `authSuccess :: PamRetCode -> Bool` added which checks if the given `PamRetCode` is `PamSuccess` or not+- `whenSuccess :: MonadIO m => PamRetCode -> m PamRetCode -> m PamRetCode` added, which returns the second argument if the given response code is `PamSuccess`, otherwise it returns the given response code (useful for continuing only if the PAM action succeeded).+- `pamCodeToMessage`, `pamCodeToCDefine` and `pamCodeDetails` now take a `PamRetCode` rather than an `Int`+- `checkAccount` function has now been implemented+- Switched to PVP versioning+- Some documentation added++## v0.1:+Initial release
+ README.md view
@@ -0,0 +1,43 @@+# pam+++++Haskell bindings for PAM. Note that this does not currently include the required bindings to write PAM modules++## Usage:++To authenticate a user `test` using the `system-local-login` PAM configuration:+```haskell+authenticate "system-local-login" "test" "PASSWORD"+```++To check if a user `test` exists using the `system-auth` PAM configuration:+```haskell+checkAccount "system-auth" "test"+```++If the operation was successful then `PamSuccess` will be returned, otherwise `PamCode a`, where `a` represents the reason for the failure.++`pamCodeToMessage`, `pamCodeToCDefine` and `pamCodeDetails` can be used to get further details about the response, and `isSuccess` can be used to check if the operation was successful. See the Haddock documentation for more details on these functions.++## Documentation:++The documentation is available on [Hackage](https://hackage.haskell.org/package/pam)++## Changes in v0.2.0.0:+- `authenticate` function now returns `MonadIO m => m PamRetCode` rather than `IO (Either Int ())`+- `authSuccess :: PamRetCode -> Bool` added which checks if the given `PamRetCode` is `PamSuccess` or not+- `whenSuccess :: MonadIO m => PamRetCode -> m PamRetCode -> m PamRetCode` added, which returns the second argument if the given response code is `PamSuccess`, otherwise it returns the given response code (useful for continuing only if the PAM action succeeded).+- `pamCodeToMessage`, `pamCodeToCDefine` and `pamCodeDetails` now take a `PamRetCode` rather than an `Int`+- `checkAccount` function has now been implemented++## Planned Changes:+- Document and refactoring the code (started)+- Add ability to give multiple passwords (for 2FA systems) (see [#2](https://github.com/oscar-h64/pam-haskell/issues/2))+- Add functions that do the prompting rather than being given details - useful as if a program using the library is intended to be distributed the number of prompts will not be known (see [#3](https://github.com/oscar-h64/pam-haskell/issues/3))+- Add functions to allow writing PAM modules in Haskell (see [#8](https://github.com/oscar-h64/pam-haskell/issues/8))++## Contributing:++See [here](https://github.com/oscar-h64/pam-haskell/blob/master/CONTRIBUTING.md) for more information
pam.cabal view
@@ -1,32 +1,45 @@-Name: pam-Version: 0.1-Cabal-Version: >= 1.2.3-Build-type: Simple-License: BSD3-License-File: LICENSE-Copyright: Copyright (c) 2011 Evgeny Tarasov-Maintainer: etarasov.ekb@gmail.com-Stability: alpha-Synopsis: Haskell binding for C PAM API-Description: This package provides PAM interface for Haskell programs. It contains subset of C PAM API bindings. The bindings don't include functions for writing PAM modules.-Category: System-Tested-with: GHC==6.12.3-Extra-source-files: Setup.hs+cabal-version: 3.0+name: pam+version: 0.2.0.0+build-type: Simple+license: BSD-3-Clause+license-file: LICENSE+copyright: (c) 2011 Evgeny Tarasov+ (c) 2020 Oscar Harris+author: Evgeny Tarasov <etarasov.ekb@gmail.com>+maintainer: Oscar Harris <oscar@oscar-h.com>+stability: provisional+synopsis: Haskell binding for C PAM API+description: This package provides PAM interface for Haskell programs. See the README on GitHub at <Please see the README on GitHub at <https://github.com/oscar-h64/pam-haskell#readme> for more information.+bug-reports: https://github.com/oscar-h64/pam-haskell/issues+category: System, Authentication+tested-with: GHC==8.8.3+homepage: https://github.com/oscar-h64/pam-haskell -Library+extra-source-files:+ Setup.hs+ +extra-doc-files:+ README.md+ CONTRIBUTING.md+ ChangeLog.md++source-repository head+ type: git+ location: https://github.com/oscar-h64/pam-haskell++library+ default-language: Haskell2010+ exposed-modules: System.Posix.PAM System.Posix.PAM.LowLevel System.Posix.PAM.Types System.Posix.PAM.Internals - --other-modules: System.Posix.PAM.Internals-- extensions: ForeignFunctionInterface-- Build-Depends: base >= 4 && < 5- Hs-Source-Dirs: src- Ghc-options: -Wall+ build-depends: base >= 4 && < 5+ hs-source-dirs: src+ ghc-options: -Wall - extra-libraries : pam+ extra-libraries: pam - build-tools: c2hs+ build-tool-depends: c2hs:c2hs
src/System/Posix/PAM.hs view
@@ -1,42 +1,62 @@ module System.Posix.PAM where +import Control.Monad.IO.Class ( MonadIO, liftIO )+ import Foreign.Ptr+ import System.Posix.PAM.LowLevel import System.Posix.PAM.Types -authenticate :: String -> String -> String -> IO (Either Int ())+-- | `isSuccess` @responseCode@ checks if @responseCode@ is equal to `PamSuccess`,+-- i.e. checking if the account check/authentication succeeded+isSuccess :: PamRetCode -> Bool+isSuccess = (== PamSuccess)++-- | `whenSuccess` @responseCode action@ returns @action@ if @responseCode@ is+-- `PamSuccess`, otherwise returns @responseCode@+whenSuccess :: MonadIO m => PamRetCode -> m PamRetCode -> m PamRetCode+whenSuccess code action = if isSuccess code then action else pure code++-- | `authenticate` @service user password@ attempts to authenticate @user@ and+-- @password@ with PAM using @service@ to determine which file in /etc/pam.d to+-- use+authenticate :: MonadIO m => String -> String -> String -> m PamRetCode authenticate serviceName userName password = do- let custConv :: String -> PamConv- custConv pass _ messages = do- let rs = map (\ _ -> PamResponse pass) messages- return rs- (pamH, r1) <- pamStart serviceName userName (custConv password, nullPtr)- case r1 of- PamRetCode code -> return $ Left $ fromInteger $ toInteger code- PamSuccess -> do- r2 <- pamAuthenticate pamH (PamFlag 0)- case r2 of- PamRetCode code -> return $ Left $ fromInteger $ toInteger code- PamSuccess -> do- r3 <- pamEnd pamH r2- case r3 of- PamSuccess -> return $ Right ()- PamRetCode code -> return $ Left $ fromInteger $ toInteger code+ let custConv :: PamConv+ custConv _ messages = return $ map (\ _ -> PamResponse password) messages -checkAccount :: String -> String -> IO (Either Int ())-checkAccount = undefined+ (pamH, r1) <- liftIO $ pamStart serviceName userName (custConv, nullPtr) + whenSuccess r1 $ do+ status <- liftIO $ pamAuthenticate pamH PamSilent+ whenSuccess status $ liftIO $ pamEnd pamH PamSuccess -pamCodeToMessage :: Int -> String+-- | `checkAccount` @service user@ checks if @user@ is a valid user. @service@ is+-- is the service name given to PAM (see `authenticate`)+checkAccount :: MonadIO m => String -> String -> m PamRetCode+checkAccount serviceName userName = do+ (pamH, r1) <- liftIO $ pamStart serviceName userName (\_ _ -> pure [], nullPtr)++ whenSuccess r1 $ do+ status <- liftIO $ pamAcctMgmt pamH PamSilent+ whenSuccess status $ liftIO $ pamEnd pamH PamSuccess++-- | `pamCodeToMessage` @responseCode@ returns a description of @responseCode@+-- in the context of PAM+pamCodeToMessage :: PamRetCode -> String pamCodeToMessage = snd . pamCodeDetails -pamCodeToCDefine :: Int -> String+-- | `pamCodeToMessage` @responseCode@ returns the name of the define used in C+-- to represent @responseCode@+pamCodeToCDefine :: PamRetCode -> String pamCodeToCDefine = fst . pamCodeDetails -pamCodeDetails :: Int -> (String, String)-pamCodeDetails code = case code of- 0 -> ("PAM_SUCCESS", "Successful function return")+-- | `pamCodeDetails` @responseCode@ returns a tuple of the name of the C define+-- and a description of @responseCode@+pamCodeDetails :: PamRetCode -> (String, String)+pamCodeDetails PamSuccess = ("PAM_SUCCESS", "Successful function return")+pamCodeDetails (PamRetCode code) = case code of 1 -> ("PAM_OPEN_ERR", "dlopen() failure when dynamically loading a service module") 2 -> ("PAM_SYMBOL_ERR", "Symbol not found") 3 -> ("PAM_SERVICE_ERR", "Error in service module")
src/System/Posix/PAM/Internals.chs view
@@ -1,7 +1,6 @@ {-# LANGUAGE CPP, ForeignFunctionInterface #-} module System.Posix.PAM.Internals where -import Control.Applicative import Foreign.C import Foreign.Ptr import Foreign.Storable
src/System/Posix/PAM/LowLevel.hs view
@@ -2,21 +2,26 @@ module System.Posix.PAM.LowLevel where import Foreign.C-import Foreign.Marshal.Array import Foreign.Marshal.Alloc+import Foreign.Marshal.Array import Foreign.Ptr import Foreign.Storable++import System.Posix.PAM.Internals hiding ( conv, resp ) import System.Posix.PAM.Types-import System.Posix.PAM.Internals hiding (resp, conv) +-- | `retCodeFromC` @responseCode@ converts @responseCode@ from PAM to+-- a PamRetCode retCodeFromC :: CInt -> PamRetCode retCodeFromC rc = case rc of 0 -> PamSuccess- a -> PamRetCode $ fromInteger $ toInteger a+ a -> PamRetCode $ fromIntegral a +-- | `retCodeToC` @retCode@ converts @retCode@ to the corresponding integer+-- used in the PAM C library retCodeToC :: PamRetCode -> CInt-retCodeToC PamSuccess = 0-retCodeToC (PamRetCode a) = fromInteger $ toInteger a+retCodeToC PamSuccess = 0+retCodeToC (PamRetCode a) = fromIntegral a responseToC :: PamResponse -> IO CPamResponse responseToC (PamResponse resp) = do@@ -47,7 +52,7 @@ let mesArr = castPtr voidArr :: Ptr CPamMessage -- peek message list from array- cMessages <- peekArray (fromInteger $ toInteger num) mesArr+ cMessages <- peekArray (fromIntegral num) mesArr -- convert messages into high-level types messages <- mapM messageFromC cMessages@@ -59,7 +64,7 @@ cResponses <- mapM responseToC responses -- alloc memory for response array- respArr <- mallocArray (fromInteger $ toInteger num)+ respArr <- mallocArray (fromIntegral num) -- poke resonse list into array pokeArray respArr cResponses@@ -90,9 +95,7 @@ cPamHandle_ <- peek pamhPtr - let retCode = case r1 of- 0 -> PamSuccess- a -> PamRetCode $ fromInteger $ toInteger a+ let retCode = retCodeFromC r1 free cServiceName free cUserName@@ -104,22 +107,19 @@ pamEnd :: PamHandle -> PamRetCode -> IO PamRetCode pamEnd pamHandle inRetCode = do- let cRetCode = case inRetCode of- PamSuccess -> 0- PamRetCode a -> fromInteger $ toInteger a- r <- c_pam_end (cPamHandle pamHandle) cRetCode+ r <- c_pam_end (cPamHandle pamHandle) $ retCodeToC inRetCode freeHaskellFunPtr $ cPamCallback pamHandle return $ retCodeFromC r pamAuthenticate :: PamHandle -> PamFlag -> IO PamRetCode-pamAuthenticate pamHandle (PamFlag flag) = do- let cFlag = fromInteger $ toInteger flag+pamAuthenticate pamHandle flag = do+ let cFlag = fromIntegral $ fromEnum flag r <- c_pam_authenticate (cPamHandle pamHandle) cFlag return $ retCodeFromC r pamAcctMgmt :: PamHandle -> PamFlag -> IO PamRetCode-pamAcctMgmt pamHandle (PamFlag flag) = do- let cFlag = fromInteger $ toInteger flag+pamAcctMgmt pamHandle flag = do+ let cFlag = fromIntegral $ fromEnum flag r <- c_pam_acct_mgmt (cPamHandle pamHandle) cFlag return $ retCodeFromC r
src/System/Posix/PAM/Types.hs view
@@ -5,7 +5,7 @@ import Foreign.Ptr data PamMessage = PamMessage { pmString :: String- , pmStyle :: PamStyle+ , pmStyle :: PamStyle } deriving (Show, Eq) @@ -25,7 +25,15 @@ | PamRetCode Int deriving (Show, Eq) -data PamFlag = PamFlag Int+data PamFlag = PamSilent+ | PamDisallowNullAuthTok+ | PamEstablishCred+ | PamDeleteCred+ | PamReinitializeCred+ | PamRefreshCred+ | PamChangeExpiredAuthTok+ | PamNone+ deriving (Show, Eq, Enum) type PamConv = Ptr () -> [PamMessage] -> IO [PamResponse]
+ stack.yaml view
@@ -0,0 +1,66 @@+# This file was automatically generated by 'stack init'+#+# Some commonly used options have been documented as comments in this file.+# For advanced use and comprehensive documentation of the format, please see:+# https://docs.haskellstack.org/en/stable/yaml_configuration/++# Resolver to choose a 'specific' stackage snapshot or a compiler version.+# A snapshot resolver dictates the compiler version and the set of packages+# to be used for project dependencies. For example:+#+# resolver: lts-3.5+# resolver: nightly-2015-09-21+# resolver: ghc-7.10.2+#+# The location of a snapshot can be provided as a file or url. Stack assumes+# a snapshot provided as a file might change, whereas a url resource does not.+#+# resolver: ./custom-snapshot.yaml+# resolver: https://example.com/snapshots/2018-01-01.yaml+resolver: lts-16.2++# User packages to be built.+# Various formats can be used as shown in the example below.+#+# packages:+# - some-directory+# - https://example.com/foo/bar/baz-0.0.2.tar.gz+# subdirs:+# - auto-update+# - wai+packages:+- .+# Dependency packages to be pulled from upstream that are not in the resolver.+# These entries can reference officially published versions as well as+# forks / in-progress versions pinned to a git hash. For example:+#+# extra-deps:+# - acme-missiles-0.3+# - git: https://github.com/commercialhaskell/stack.git+# commit: e7b331f14bcffb8367cd58fbfc8b40ec7642100a+#+# extra-deps: []++# Override default flag values for local packages and extra-deps+# flags: {}++# Extra package databases containing global packages+# extra-package-dbs: []++# Control whether we use the GHC we find on the path+# system-ghc: true+#+# Require a specific version of stack, using version ranges+# require-stack-version: -any # Default+# require-stack-version: ">=2.3"+#+# Override the architecture used by stack, especially useful on Windows+# arch: i386+# arch: x86_64+#+# Extra directories used by stack for building+# extra-include-dirs: [/path/to/dir]+# extra-lib-dirs: [/path/to/dir]+#+# Allow a newer minor version of GHC than the snapshot specifies+# compiler-check: newer-minor