module Ganeti.Confd.Utils
( getClusterHmac
, parseSignedMessage
, parseRequest
, parseReply
, signMessage
, getCurrentTime
) where
import qualified Data.ByteString as B
import qualified Text.JSON as J
import Ganeti.BasicTypes
import Ganeti.Confd.Types
import Ganeti.Hash
import qualified Ganeti.Constants as C
import qualified Ganeti.Path as Path
import Ganeti.JSON
import Ganeti.Utils
maxClockSkew :: Integer
maxClockSkew = fromIntegral C.confdMaxClockSkew
getClusterHmac :: IO HashKey
getClusterHmac = Path.confdHmacKey >>= fmap B.unpack . B.readFile
parseSignedMessage :: (J.JSON a) => HashKey -> String
-> Result (String, String, a)
parseSignedMessage key str = do
(SignedMessage hmac msg salt) <- fromJResult "parsing signed message"
$ J.decode str
parsedMsg <- if verifyMac key (Just salt) msg hmac
then fromJResult "parsing message" $ J.decode msg
else Bad "HMAC verification failed"
return (salt, msg, parsedMsg)
parseRequest :: HashKey -> String -> Integer
-> Result (String, ConfdRequest)
parseRequest hmac msg curtime = do
(salt, origmsg, request) <- parseSignedMessage hmac msg
ts <- tryRead "Parsing timestamp" salt::Result Integer
if abs (ts curtime) > maxClockSkew
then fail "Too old/too new timestamp or clock skew"
else return (origmsg, request)
parseReply :: HashKey -> String -> String -> Result (String, ConfdReply)
parseReply hmac msg expSalt = do
(salt, origmsg, reply) <- parseSignedMessage hmac msg
if salt /= expSalt
then fail "The received salt differs from the expected salt"
else return (origmsg, reply)
signMessage :: HashKey -> String -> String -> SignedMessage
signMessage key salt msg =
SignedMessage { signedMsgMsg = msg
, signedMsgSalt = salt
, signedMsgHmac = hmac
}
where hmac = computeMac key (Just salt) msg