packages feed

pam-0.2.0.0: src/System/Posix/PAM/LowLevel.hs

module System.Posix.PAM.LowLevel where

import Foreign.C
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

-- | `retCodeFromC` @responseCode@ converts @responseCode@ from PAM to
--   a PamRetCode
retCodeFromC :: CInt -> PamRetCode
retCodeFromC rc = case rc of
            0 -> PamSuccess
            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) = fromIntegral a

responseToC :: PamResponse -> IO CPamResponse
responseToC (PamResponse resp) = do
    resp' <- newCString resp
    return $ CPamResponse resp' 0

messageFromC :: CPamMessage -> IO PamMessage
messageFromC cmes =
    let style = case msg_style cmes of
            1 -> PamPromptEchoOff
            2 -> PamPromptEchoOn
            3 -> PamErrorMsg
            4 -> PamTextInfo
            a -> error $ "unknown style value: " ++ show a
    in do
        str <- peekCString $ msg cmes
        return $ PamMessage str style

cConv :: (Ptr () -> [PamMessage] -> IO [PamResponse]) -> CInt -> Ptr (Ptr ()) -> Ptr (Ptr ()) -> Ptr () -> IO CInt
cConv customConv num mesArrPtr respArrPtr appData =
    if num <= 0
        then return 19
        else do
            -- get array pointer (pointer to first element)
            voidArr <- peek mesArrPtr

            -- cast pointer type from ()
            let mesArr = castPtr voidArr :: Ptr CPamMessage

            -- peek message list from array
            cMessages <- peekArray (fromIntegral num) mesArr

            -- convert messages into high-level types
            messages <- mapM messageFromC cMessages

            -- create response list
            responses <- customConv appData messages

            -- convert responses into low-level types
            cResponses <- mapM responseToC responses

            -- alloc memory for response array
            respArr <- mallocArray (fromIntegral num)

            -- poke resonse list into array
            pokeArray respArr cResponses

            -- poke array pointer into respArrPtr
            poke respArrPtr $ castPtr respArr

            -- return PAM_SUCCESS
            return 0


pamStart :: String -> String -> (PamConv, Ptr ()) -> IO (PamHandle, PamRetCode)
pamStart serviceName userName (pamConv, appData) = do
    cServiceName <- newCString serviceName
    cUserName <- newCString userName

    -- create FunPtr pointer to function and embedd PamConv function into cConv
    pamConvPtr <- mkconvFunc $ cConv pamConv
    let conv = CPamConv pamConvPtr appData

    convPtr <- malloc
    poke convPtr conv

    pamhPtr <- malloc
    poke pamhPtr nullPtr

    r1 <- c_pam_start cServiceName cUserName convPtr pamhPtr

    cPamHandle_ <- peek pamhPtr

    let retCode = retCodeFromC r1

    free cServiceName
    free cUserName
    free convPtr

    free pamhPtr

    return (PamHandle cPamHandle_ pamConvPtr, retCode)

pamEnd :: PamHandle -> PamRetCode -> IO PamRetCode
pamEnd pamHandle inRetCode = do
    r <- c_pam_end (cPamHandle pamHandle) $ retCodeToC inRetCode
    freeHaskellFunPtr $ cPamCallback pamHandle

    return $ retCodeFromC r

pamAuthenticate :: PamHandle -> PamFlag -> IO PamRetCode
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 flag = do
    let cFlag = fromIntegral $ fromEnum flag
    r <- c_pam_acct_mgmt (cPamHandle pamHandle) cFlag
    return $ retCodeFromC r