credential-store-0.1.0.0: src/System/CredentialStore/Windows.hs
module System.CredentialStore.Windows
( CredentialStore
, getCredential
, putCredential
, deleteCredential
, withCredentialStore
) where
import Control.Exception.Safe
import Control.Monad
import Data.ByteArray
import Foreign
import System.Win32.Time
import System.Win32.Types
import System.CredentialStore.WinTypes
data CredentialStore = CredentialStore
withCredentialStore :: (CredentialStore -> IO a) -> IO a
withCredentialStore f = f CredentialStore
getCredential :: ByteArray ba => CredentialStore -> String -> IO (Maybe ba)
getCredential _ name =
withTString name $ \tstr ->
alloca $ \pptr -> do
result <- c_CredRead tstr cRED_TYPE_GENERIC 0 pptr
if result
then do
ptr <- peek pptr
cred <- finally
(do
rec <- peek ptr
decryptCredential (castPtr $ crCredentialBlob rec, crCredentialBlobSize rec)
) (c_CredFree ptr)
return $ Just cred
else do
errCode <- getLastError
unless (errCode == eRROR_NOT_FOUND) $ failWith "CredRead" errCode
return Nothing
putCredential :: ByteArrayAccess ba => CredentialStore -> String -> ba -> IO ()
putCredential _ name value =
withEncryptedCredential value $ \(val, len) ->
withTString name $ \tstr ->
withTString "" $ \emptystr ->
alloca $ \rec -> do
poke rec CREDENTIAL
{ crFlags = 0
, crType = cRED_TYPE_GENERIC
, crTargetName = tstr
, crComment = emptystr
, crLastWritten = FILETIME 0
, crCredentialBlobSize = len
, crCredentialBlob = castPtr val
, crPersist = cRED_PERSIST_LOCAL_MACHINE
, crAttributeCount = 0
, crAttributes = nullPtr
, crTargetAlias = emptystr
, crUserName = emptystr
}
failIfFalse_ "CredWrite" $ c_CredWrite rec 0
deleteCredential :: CredentialStore -> String -> IO ()
deleteCredential _ name = withTString name $ \tstr -> failIfFalse_ "CredDelete" $ c_CredDelete tstr cRED_TYPE_GENERIC 0
withEncryptedCredential :: ByteArrayAccess ba => ba -> ((LPTSTR, DWORD) -> IO a) -> IO a
withEncryptedCredential value f =
withByteArray value $ \val ->
alloca $ \protType ->
alloca $ \sizeptr -> do
let len = fromIntegral $ Data.ByteArray.length value
poke sizeptr 0
ret <- c_CredProtect True val len nullPtr sizeptr protType
errCode <- getLastError
unless (not ret && errCode == eRROR_INSUFFICIENT_BUFFER) $ failWith "CredProtect(NULL)" errCode
needed <- peek sizeptr
allocaBytes (fromIntegral needed * 2) $ \outputPtr -> do
failIfFalse_ "CredProtect" $ c_CredProtect True val len outputPtr sizeptr protType
outputLen <- peek sizeptr
f (outputPtr, outputLen * 2)
decryptCredential :: ByteArray ba => (LPTSTR, DWORD) -> IO ba
decryptCredential (val, len) =
alloca $ \sizeptr -> do
poke sizeptr 0
ret <- c_CredUnprotect True val len nullPtr sizeptr
errCode <- getLastError
unless (not ret && errCode == eRROR_INSUFFICIENT_BUFFER) $ failWith "CredUnprotect(NULL)" errCode
needed <- peek sizeptr
create (fromIntegral needed) $ \outputPtr ->
failIfFalse_ "CredUnprotect" $ c_CredUnprotect True val len outputPtr sizeptr