2011-03-02 08:43:05 +00:00
|
|
|
{-# OPTIONS_HADDOCK hide #-}
|
2012-01-18 06:29:29 +00:00
|
|
|
{-# LANGUAGE DeriveDataTypeable #-}
|
2011-03-01 20:01:40 +00:00
|
|
|
-- |
|
|
|
|
-- Module : Network.TLS.Core
|
|
|
|
-- License : BSD-style
|
|
|
|
-- Maintainer : Vincent Hanquez <vincent@snarc.org>
|
|
|
|
-- Stability : experimental
|
|
|
|
-- Portability : unknown
|
|
|
|
--
|
|
|
|
module Network.TLS.Core
|
2011-03-01 23:09:17 +00:00
|
|
|
(
|
2011-03-19 21:45:43 +00:00
|
|
|
-- * Internal packet sending and receiving
|
2011-12-06 00:15:00 +00:00
|
|
|
sendPacket
|
2011-03-19 21:45:43 +00:00
|
|
|
, recvPacket
|
|
|
|
|
2011-12-06 00:15:00 +00:00
|
|
|
-- * Creating a client or server context
|
2011-03-01 20:01:40 +00:00
|
|
|
, client
|
2011-09-29 08:22:27 +00:00
|
|
|
, clientWith
|
2011-03-01 20:01:40 +00:00
|
|
|
, server
|
2011-09-29 08:22:27 +00:00
|
|
|
, serverWith
|
2011-03-02 07:56:37 +00:00
|
|
|
|
2011-03-02 08:43:05 +00:00
|
|
|
-- * Initialisation and Termination of context
|
2011-03-01 20:01:40 +00:00
|
|
|
, bye
|
2011-03-01 23:09:17 +00:00
|
|
|
, handshake
|
2012-01-19 05:31:31 +00:00
|
|
|
, HandshakeFailed(..)
|
2012-01-25 16:01:55 +00:00
|
|
|
, ConnectionNotEstablished(..)
|
2011-03-02 07:56:37 +00:00
|
|
|
|
|
|
|
-- * High level API
|
2011-03-01 20:01:40 +00:00
|
|
|
, sendData
|
2011-03-02 07:41:59 +00:00
|
|
|
, recvData
|
2012-02-07 20:41:28 +00:00
|
|
|
, recvData'
|
2011-03-01 20:01:40 +00:00
|
|
|
) where
|
|
|
|
|
2011-12-06 00:15:00 +00:00
|
|
|
import Network.TLS.Context
|
2011-03-01 20:01:40 +00:00
|
|
|
import Network.TLS.Struct
|
2011-08-12 17:41:49 +00:00
|
|
|
import Network.TLS.Record
|
2011-03-01 20:01:40 +00:00
|
|
|
import Network.TLS.Cipher
|
|
|
|
import Network.TLS.Compression
|
2011-03-01 20:01:40 +00:00
|
|
|
import Network.TLS.Packet
|
|
|
|
import Network.TLS.State
|
|
|
|
import Network.TLS.Sending
|
|
|
|
import Network.TLS.Receiving
|
2011-11-11 19:05:17 +00:00
|
|
|
import Network.TLS.Measurement
|
2011-12-06 00:12:00 +00:00
|
|
|
import Network.TLS.Wire (encodeWord16)
|
2011-03-02 07:35:25 +00:00
|
|
|
import Data.Maybe
|
2012-01-18 06:29:29 +00:00
|
|
|
import Data.Data
|
2011-12-06 00:15:00 +00:00
|
|
|
import Data.List (intersect, find)
|
2011-03-01 20:01:40 +00:00
|
|
|
import qualified Data.ByteString as B
|
2011-03-01 20:01:40 +00:00
|
|
|
import qualified Data.ByteString.Lazy as L
|
2011-03-01 20:01:40 +00:00
|
|
|
|
2011-04-11 18:54:21 +00:00
|
|
|
import Crypto.Random
|
2011-03-01 20:01:40 +00:00
|
|
|
import Control.Applicative ((<$>))
|
|
|
|
import Control.Monad.State
|
2012-01-16 12:36:45 +00:00
|
|
|
import Control.Exception (throwIO, Exception(), fromException, catch, SomeException)
|
2011-12-06 00:15:00 +00:00
|
|
|
import System.IO (Handle)
|
2011-06-13 07:19:29 +00:00
|
|
|
import System.IO.Error (mkIOError, eofErrorType)
|
2011-05-13 07:39:15 +00:00
|
|
|
import Prelude hiding (catch)
|
2011-03-01 20:01:40 +00:00
|
|
|
|
2012-01-18 06:29:29 +00:00
|
|
|
data HandshakeFailed = HandshakeFailed TLSError
|
|
|
|
deriving (Show,Eq,Typeable)
|
|
|
|
|
2012-01-25 16:01:55 +00:00
|
|
|
data ConnectionNotEstablished = ConnectionNotEstablished
|
|
|
|
deriving (Show,Eq,Typeable)
|
|
|
|
|
2012-01-18 06:29:29 +00:00
|
|
|
instance Exception HandshakeFailed
|
2012-01-25 16:01:55 +00:00
|
|
|
instance Exception ConnectionNotEstablished
|
2012-01-18 06:29:29 +00:00
|
|
|
|
2011-05-13 07:39:15 +00:00
|
|
|
errorToAlert :: TLSError -> Packet
|
2011-06-10 20:24:46 +00:00
|
|
|
errorToAlert (Error_Protocol (_, _, ad)) = Alert [(AlertLevel_Fatal, ad)]
|
|
|
|
errorToAlert _ = Alert [(AlertLevel_Fatal, InternalError)]
|
2011-05-13 07:39:15 +00:00
|
|
|
|
2012-01-18 06:29:29 +00:00
|
|
|
handshakeFailed :: TLSError -> IO ()
|
|
|
|
handshakeFailed err = throwIO $ HandshakeFailed err
|
|
|
|
|
2012-03-12 08:48:03 +00:00
|
|
|
checkValid :: MonadIO m => TLSCtx -> m ()
|
2012-01-25 16:01:55 +00:00
|
|
|
checkValid ctx = do
|
|
|
|
established <- ctxEstablished ctx
|
|
|
|
unless established $ liftIO $ throwIO ConnectionNotEstablished
|
|
|
|
eofed <- ctxEOF ctx
|
|
|
|
when eofed $ liftIO $ throwIO $ mkIOError eofErrorType "data" Nothing Nothing
|
|
|
|
|
2012-03-12 08:48:03 +00:00
|
|
|
readExact :: MonadIO m => TLSCtx -> Int -> m Bytes
|
2011-06-13 07:19:29 +00:00
|
|
|
readExact ctx sz = do
|
2011-09-29 07:29:28 +00:00
|
|
|
hdrbs <- liftIO $ connectionRecv ctx sz
|
2011-06-13 07:19:29 +00:00
|
|
|
when (B.length hdrbs < sz) $ do
|
|
|
|
setEOF ctx
|
|
|
|
if B.null hdrbs
|
|
|
|
then throwCore Error_EOF
|
|
|
|
else throwCore (Error_Packet ("partial packet: expecting " ++ show sz ++ " bytes, got: " ++ (show $B.length hdrbs)))
|
|
|
|
return hdrbs
|
|
|
|
|
2012-03-12 08:48:03 +00:00
|
|
|
recvRecord :: MonadIO m => TLSCtx -> m (Either TLSError (Record Plaintext))
|
2011-11-30 22:01:31 +00:00
|
|
|
recvRecord ctx = readExact ctx 5 >>= either (return . Left) recvLength . decodeHeader
|
|
|
|
where recvLength header@(Header _ _ readlen)
|
|
|
|
| readlen > 16384 + 2048 = return $ Left $ Error_Protocol ("record exceeding maximum size", True, RecordOverflow)
|
|
|
|
| otherwise = do
|
|
|
|
content <- readExact ctx (fromIntegral readlen)
|
|
|
|
liftIO $ (loggingIORecv $ ctxLogging ctx) header content
|
|
|
|
usingState ctx $ disengageRecord $ rawToRecord header (fragmentCiphertext content)
|
|
|
|
|
|
|
|
|
2011-06-12 20:39:17 +00:00
|
|
|
-- | receive one packet from the context that contains 1 or
|
|
|
|
-- many messages (many only in case of handshake). if will returns a
|
2011-03-02 08:43:05 +00:00
|
|
|
-- TLSError if the packet is unexpected or malformed
|
2012-03-12 08:48:03 +00:00
|
|
|
recvPacket :: MonadIO m => TLSCtx -> m (Either TLSError Packet)
|
2011-03-01 20:01:40 +00:00
|
|
|
recvPacket ctx = do
|
2011-11-30 22:01:31 +00:00
|
|
|
erecord <- recvRecord ctx
|
|
|
|
case erecord of
|
|
|
|
Left err -> return $ Left err
|
|
|
|
Right record -> do
|
|
|
|
pkt <- usingState ctx $ processPacket record
|
|
|
|
case pkt of
|
|
|
|
Right p -> liftIO $ (loggingPacketRecv $ ctxLogging ctx) $ show p
|
|
|
|
_ -> return ()
|
|
|
|
return pkt
|
2011-03-01 20:01:40 +00:00
|
|
|
|
2012-03-12 08:48:03 +00:00
|
|
|
recvPacketHandshake :: MonadIO m => TLSCtx -> m [Handshake]
|
2011-11-13 11:11:39 +00:00
|
|
|
recvPacketHandshake ctx = do
|
|
|
|
pkts <- recvPacket ctx
|
|
|
|
case pkts of
|
|
|
|
Right (Handshake l) -> return l
|
|
|
|
Right x -> fail ("unexpected type received. expecting handshake and got: " ++ show x)
|
|
|
|
Left err -> throwCore err
|
|
|
|
|
2011-11-29 08:59:41 +00:00
|
|
|
data RecvState m =
|
|
|
|
RecvStateNext (Packet -> m (RecvState m))
|
|
|
|
| RecvStateHandshake (Handshake -> m (RecvState m))
|
|
|
|
| RecvStateDone
|
|
|
|
|
2012-03-12 08:48:03 +00:00
|
|
|
runRecvState :: MonadIO m => TLSCtx -> RecvState m -> m ()
|
2011-11-29 08:59:41 +00:00
|
|
|
runRecvState _ (RecvStateDone) = return ()
|
|
|
|
runRecvState ctx (RecvStateNext f) = recvPacket ctx >>= either throwCore f >>= runRecvState ctx
|
|
|
|
runRecvState ctx iniState = recvPacketHandshake ctx >>= loop iniState >>= runRecvState ctx
|
|
|
|
where
|
|
|
|
loop :: MonadIO m => RecvState m -> [Handshake] -> m (RecvState m)
|
|
|
|
loop recvState [] = return recvState
|
2011-12-01 08:42:43 +00:00
|
|
|
loop (RecvStateHandshake f) (x:xs) = do
|
|
|
|
nstate <- f x
|
|
|
|
usingState_ ctx $ processHandshake x
|
|
|
|
loop nstate xs
|
2011-11-29 08:59:41 +00:00
|
|
|
loop _ _ = unexpected "spurious handshake" Nothing
|
|
|
|
|
2012-03-12 08:48:03 +00:00
|
|
|
sendChangeCipherAndFinish :: MonadIO m => TLSCtx -> Bool -> m ()
|
2011-11-29 08:59:41 +00:00
|
|
|
sendChangeCipherAndFinish ctx isClient = do
|
|
|
|
sendPacket ctx ChangeCipherSpec
|
|
|
|
liftIO $ connectionFlush ctx
|
|
|
|
cf <- usingState_ ctx $ getHandshakeDigest isClient
|
|
|
|
sendPacket ctx (Handshake [Finished cf])
|
|
|
|
liftIO $ connectionFlush ctx
|
|
|
|
|
2012-03-12 08:48:03 +00:00
|
|
|
recvChangeCipherAndFinish :: MonadIO m => TLSCtx -> m ()
|
2011-12-01 22:33:53 +00:00
|
|
|
recvChangeCipherAndFinish ctx = runRecvState ctx (RecvStateNext expectChangeCipher)
|
|
|
|
where
|
|
|
|
expectChangeCipher ChangeCipherSpec = return $ RecvStateHandshake expectFinish
|
|
|
|
expectChangeCipher p = unexpected (show p) (Just "change cipher")
|
|
|
|
expectFinish (Finished _) = return RecvStateDone
|
|
|
|
expectFinish p = unexpected (show p) (Just "Handshake Finished")
|
|
|
|
|
2011-11-29 08:59:41 +00:00
|
|
|
unexpected :: MonadIO m => String -> Maybe [Char] -> m a
|
|
|
|
unexpected msg expected = throwCore $ Error_Packet_unexpected msg (maybe "" (" expected: " ++) expected)
|
2011-06-12 20:38:18 +00:00
|
|
|
|
2012-03-12 08:48:03 +00:00
|
|
|
newSession :: MonadIO m => TLSCtx -> m Session
|
2011-12-20 07:42:13 +00:00
|
|
|
newSession ctx
|
|
|
|
| pUseSession $ ctxParams ctx = getStateRNG ctx 32 >>= return . Session . Just
|
|
|
|
| otherwise = return $ Session Nothing
|
|
|
|
|
2011-03-02 08:43:05 +00:00
|
|
|
-- | Send one packet to the context
|
2012-03-12 08:48:03 +00:00
|
|
|
sendPacket :: MonadIO m => TLSCtx -> Packet -> m ()
|
2011-03-01 20:01:40 +00:00
|
|
|
sendPacket ctx pkt = do
|
2011-05-04 07:41:16 +00:00
|
|
|
liftIO $ (loggingPacketSent $ ctxLogging ctx) (show pkt)
|
2011-03-01 20:01:40 +00:00
|
|
|
dataToSend <- usingState_ ctx $ writePacket pkt
|
2011-05-04 07:41:16 +00:00
|
|
|
liftIO $ (loggingIOSent $ ctxLogging ctx) dataToSend
|
2011-09-29 07:29:28 +00:00
|
|
|
liftIO $ connectionSend ctx dataToSend
|
2011-03-01 20:01:40 +00:00
|
|
|
|
2011-09-29 08:22:27 +00:00
|
|
|
-- | Create a new Client context with a configuration, a RNG, a generic connection and the connection operation.
|
2012-02-07 06:26:26 +00:00
|
|
|
clientWith :: (MonadIO m, CryptoRandomGen g)
|
|
|
|
=> TLSParams -- ^ Parameters to use for this context
|
|
|
|
-> g -- ^ Random number generator associated
|
2012-03-12 08:48:03 +00:00
|
|
|
-> TLSBackend -- ^ Backend abstraction with specific methods to interact with the connection type.
|
|
|
|
-> m TLSCtx
|
|
|
|
clientWith params rng backend =
|
|
|
|
liftIO $ newCtxWith backend params st
|
2011-09-29 08:22:27 +00:00
|
|
|
where st = (newTLSState rng) { stClientContext = True }
|
|
|
|
|
2011-03-02 08:43:05 +00:00
|
|
|
-- | Create a new Client context with a configuration, a RNG, and a Handle.
|
2012-03-10 20:51:46 +00:00
|
|
|
-- It reconfigures the handle's 'System.IO.BufferMode' to @NoBuffering@.
|
2012-02-07 06:26:26 +00:00
|
|
|
client :: (MonadIO m, CryptoRandomGen g)
|
|
|
|
=> TLSParams -- ^ parameters to use for this context
|
|
|
|
-> g -- ^ random number generator associated with the context
|
|
|
|
-> Handle -- ^ handle to use
|
2012-03-12 08:48:03 +00:00
|
|
|
-> m TLSCtx
|
2011-04-24 12:43:57 +00:00
|
|
|
client params rng handle = liftIO $ newCtx handle params st
|
|
|
|
where st = (newTLSState rng) { stClientContext = True }
|
2011-03-01 20:01:40 +00:00
|
|
|
|
2011-09-29 08:22:27 +00:00
|
|
|
-- | Create a new Server context with a configuration, a RNG, a generic connection and the connection operation.
|
2012-03-12 08:48:03 +00:00
|
|
|
serverWith :: (MonadIO m, CryptoRandomGen g) => TLSParams -> g -> TLSBackend -> m TLSCtx
|
|
|
|
serverWith params rng backend =
|
|
|
|
liftIO $ newCtxWith backend params st
|
2011-09-29 08:22:27 +00:00
|
|
|
where st = (newTLSState rng) { stClientContext = False }
|
|
|
|
|
2011-03-02 08:43:05 +00:00
|
|
|
-- | Create a new Server context with a configuration, a RNG, and a Handle.
|
2012-03-10 20:51:46 +00:00
|
|
|
-- It reconfigures the handle's 'System.IO.BufferMode' to @NoBuffering@.
|
2012-03-12 08:48:03 +00:00
|
|
|
server :: (MonadIO m, CryptoRandomGen g) => TLSParams -> g -> Handle -> m TLSCtx
|
2011-04-24 12:43:57 +00:00
|
|
|
server params rng handle = liftIO $ newCtx handle params st
|
|
|
|
where st = (newTLSState rng) { stClientContext = False }
|
2011-03-01 20:01:40 +00:00
|
|
|
|
2011-03-02 08:43:05 +00:00
|
|
|
-- | notify the context that this side wants to close connection.
|
|
|
|
-- this is important that it is called before closing the handle, otherwise
|
|
|
|
-- the session might not be resumable (for version < TLS1.2).
|
|
|
|
--
|
|
|
|
-- this doesn't actually close the handle
|
2012-03-12 08:48:03 +00:00
|
|
|
bye :: MonadIO m => TLSCtx -> m ()
|
2011-06-10 20:24:46 +00:00
|
|
|
bye ctx = sendPacket ctx $ Alert [(AlertLevel_Warning, CloseNotify)]
|
2011-03-01 20:01:40 +00:00
|
|
|
|
2011-12-20 07:39:24 +00:00
|
|
|
-- | when a new handshake is done, wrap up & clean up.
|
2012-03-12 08:48:03 +00:00
|
|
|
handshakeTerminate :: MonadIO m => TLSCtx -> m ()
|
2011-12-12 08:43:52 +00:00
|
|
|
handshakeTerminate ctx = do
|
2011-12-20 07:39:24 +00:00
|
|
|
session <- usingState_ ctx getSession
|
|
|
|
-- only callback the session established if we have a session
|
|
|
|
case session of
|
|
|
|
Session (Just sessionId) -> do
|
|
|
|
sessionData <- usingState_ ctx getSessionData
|
|
|
|
liftIO $ (onSessionEstablished $ ctxParams ctx) sessionId (fromJust sessionData)
|
|
|
|
_ -> return ()
|
|
|
|
-- forget all handshake data now and reset bytes counters.
|
2011-12-12 08:43:52 +00:00
|
|
|
usingState_ ctx endHandshake
|
|
|
|
updateMeasure ctx resetBytesCounters
|
2012-01-25 16:01:55 +00:00
|
|
|
-- mark the secure connection up and running.
|
|
|
|
setEstablished ctx True
|
2011-12-12 08:43:52 +00:00
|
|
|
return ()
|
|
|
|
|
2011-03-02 08:43:05 +00:00
|
|
|
-- client part of handshake. send a bunch of handshake of client
|
|
|
|
-- values intertwined with response from the server.
|
2012-03-12 08:48:03 +00:00
|
|
|
handshakeClient :: MonadIO m => TLSCtx -> m ()
|
2011-03-01 23:09:17 +00:00
|
|
|
handshakeClient ctx = do
|
2011-11-11 19:05:17 +00:00
|
|
|
updateMeasure ctx incrementNbHandshakes
|
2011-11-29 08:59:41 +00:00
|
|
|
sendClientHello
|
2011-11-13 11:12:26 +00:00
|
|
|
recvServerHello
|
2011-12-20 07:43:43 +00:00
|
|
|
sessionResuming <- usingState_ ctx isSessionResuming
|
|
|
|
if sessionResuming
|
2011-12-01 22:33:53 +00:00
|
|
|
then sendChangeCipherAndFinish ctx True
|
|
|
|
else do
|
|
|
|
sendCertificate >> sendClientKeyXchg >> sendCertificateVerify
|
|
|
|
sendChangeCipherAndFinish ctx True
|
|
|
|
recvChangeCipherAndFinish ctx
|
2011-12-12 08:43:52 +00:00
|
|
|
handshakeTerminate ctx
|
2011-03-01 23:09:17 +00:00
|
|
|
where
|
2011-03-02 07:56:37 +00:00
|
|
|
params = ctxParams ctx
|
2011-03-01 23:09:17 +00:00
|
|
|
ver = pConnectVersion params
|
|
|
|
allowedvers = pAllowedVersions params
|
|
|
|
ciphers = pCiphers params
|
|
|
|
compressions = pCompressions params
|
|
|
|
clientCerts = map fst $ pCertificates params
|
2011-06-07 07:13:43 +00:00
|
|
|
getExtensions =
|
|
|
|
if pUseSecureRenegotiation params
|
2011-06-19 20:23:01 +00:00
|
|
|
then usingState_ ctx (getVerifiedData True) >>= \vd -> return [ (0xff01, encodeExtSecureRenegotiation vd Nothing) ]
|
2011-06-07 07:13:43 +00:00
|
|
|
else return []
|
2011-03-01 23:09:17 +00:00
|
|
|
|
2011-11-29 08:59:41 +00:00
|
|
|
sendClientHello = do
|
|
|
|
crand <- getStateRNG ctx 32 >>= return . ClientRandom
|
2011-12-20 07:43:43 +00:00
|
|
|
let clientSession = Session . maybe Nothing (Just . fst) $ sessionResumeWith params
|
2011-11-29 08:59:41 +00:00
|
|
|
extensions <- getExtensions
|
|
|
|
usingState_ ctx (startHandshakeClient ver crand)
|
|
|
|
sendPacket ctx $ Handshake
|
2011-12-20 07:43:43 +00:00
|
|
|
[ ClientHello ver crand clientSession (map cipherID ciphers)
|
2011-11-29 08:59:41 +00:00
|
|
|
(map compressionID compressions) extensions
|
|
|
|
]
|
|
|
|
|
2011-12-01 22:33:53 +00:00
|
|
|
expectChangeCipher ChangeCipherSpec = return $ RecvStateHandshake expectFinish
|
|
|
|
expectChangeCipher p = unexpected (show p) (Just "change cipher")
|
|
|
|
expectFinish (Finished _) = return RecvStateDone
|
|
|
|
expectFinish p = unexpected (show p) (Just "Handshake Finished")
|
2011-11-29 08:59:41 +00:00
|
|
|
|
|
|
|
sendCertificate = do
|
|
|
|
-- Send Certificate if requested. XXX disabled for now.
|
|
|
|
certRequested <- return False
|
|
|
|
when certRequested (sendPacket ctx $ Handshake [Certificates clientCerts])
|
2011-11-13 11:12:26 +00:00
|
|
|
|
2011-11-28 08:01:19 +00:00
|
|
|
sendCertificateVerify =
|
|
|
|
{- maybe send certificateVerify -}
|
|
|
|
{- FIXME not implemented yet -}
|
|
|
|
return ()
|
2011-06-10 20:24:46 +00:00
|
|
|
|
2011-12-20 07:41:53 +00:00
|
|
|
recvServerHello = runRecvState ctx (RecvStateHandshake onServerHello)
|
2011-11-13 11:12:26 +00:00
|
|
|
|
2011-12-20 07:41:53 +00:00
|
|
|
onServerHello :: MonadIO m => Handshake -> m (RecvState m)
|
|
|
|
onServerHello sh@(ServerHello rver _ serverSession cipher _ _) = do
|
2011-05-12 08:12:09 +00:00
|
|
|
when (rver == SSL2) $ throwCore $ Error_Protocol ("ssl2 is not supported", True, ProtocolVersion)
|
2011-03-01 23:09:17 +00:00
|
|
|
case find ((==) rver) allowedvers of
|
2011-05-13 06:03:16 +00:00
|
|
|
Nothing -> throwCore $ Error_Protocol ("version " ++ show ver ++ "is not supported", True, ProtocolVersion)
|
2011-03-01 23:09:17 +00:00
|
|
|
Just _ -> usingState_ ctx $ setVersion ver
|
|
|
|
case find ((==) cipher . cipherID) ciphers of
|
2011-05-12 08:12:31 +00:00
|
|
|
Nothing -> throwCore $ Error_Protocol ("no cipher in common with the server", True, HandshakeFailure)
|
2011-03-01 23:09:17 +00:00
|
|
|
Just c -> usingState_ ctx $ setCipher c
|
|
|
|
|
2011-12-20 07:46:40 +00:00
|
|
|
let resumingSession = case sessionResumeWith params of
|
|
|
|
Just (sessionId, sessionData) -> if serverSession == Session (Just sessionId) then Just sessionData else Nothing
|
|
|
|
Nothing -> Nothing
|
|
|
|
usingState_ ctx $ setSession serverSession (isJust resumingSession)
|
|
|
|
usingState_ ctx $ processServerHello sh
|
|
|
|
case resumingSession of
|
|
|
|
Nothing -> return $ RecvStateHandshake processCertificate
|
|
|
|
Just sessionData -> do
|
|
|
|
usingState_ ctx (setMasterSecret $ sessionSecret sessionData)
|
|
|
|
return $ RecvStateNext expectChangeCipher
|
2011-12-20 07:41:53 +00:00
|
|
|
onServerHello p = unexpected (show p) (Just "server hello")
|
2011-03-01 23:09:17 +00:00
|
|
|
|
2011-11-29 08:59:41 +00:00
|
|
|
processCertificate :: MonadIO m => Handshake -> m (RecvState m)
|
|
|
|
processCertificate (Certificates certs) = do
|
2012-01-16 12:36:45 +00:00
|
|
|
usage <- liftIO $ catch (onCertificatesRecv params $ certs) rejectOnException
|
2011-05-13 06:02:44 +00:00
|
|
|
case usage of
|
|
|
|
CertificateUsageAccept -> return ()
|
|
|
|
CertificateUsageReject reason -> certificateRejected reason
|
2011-11-29 08:59:41 +00:00
|
|
|
return $ RecvStateHandshake processServerKeyExchange
|
2012-01-16 12:36:45 +00:00
|
|
|
where
|
|
|
|
rejectOnException :: SomeException -> IO TLSCertificateUsage
|
|
|
|
rejectOnException e = return $ CertificateUsageReject $ CertificateRejectOther $ show e
|
2011-11-29 08:59:41 +00:00
|
|
|
processCertificate p = processServerKeyExchange p
|
2011-03-01 23:09:17 +00:00
|
|
|
|
2011-11-29 08:59:41 +00:00
|
|
|
processServerKeyExchange :: MonadIO m => Handshake -> m (RecvState m)
|
2011-12-01 08:34:41 +00:00
|
|
|
processServerKeyExchange (ServerKeyXchg _) = return $ RecvStateHandshake processCertificateRequest
|
|
|
|
processServerKeyExchange p = processCertificateRequest p
|
2011-11-29 08:59:41 +00:00
|
|
|
|
|
|
|
processCertificateRequest (CertRequest _ _ _) = do
|
2011-06-10 20:24:46 +00:00
|
|
|
--modify (\sc -> sc { scCertRequested = True })
|
2011-11-29 08:59:41 +00:00
|
|
|
return $ RecvStateHandshake processServerHelloDone
|
|
|
|
processCertificateRequest p = processServerHelloDone p
|
|
|
|
|
|
|
|
processServerHelloDone ServerHelloDone = return RecvStateDone
|
|
|
|
processServerHelloDone p = unexpected (show p) (Just "server hello data")
|
2011-06-10 20:24:46 +00:00
|
|
|
|
2011-06-12 20:55:22 +00:00
|
|
|
sendClientKeyXchg = do
|
2011-12-01 08:41:01 +00:00
|
|
|
encryptedPreMaster <- usingState_ ctx $ do
|
|
|
|
xver <- stVersion <$> get
|
|
|
|
prerand <- genTLSRandom 46
|
|
|
|
let premaster = encodePreMasterSecret xver prerand
|
2011-12-20 07:30:19 +00:00
|
|
|
setMasterSecretFromPre premaster
|
2011-12-06 00:12:00 +00:00
|
|
|
|
|
|
|
-- SSL3 implementation generally forget this length field since it's redundant,
|
|
|
|
-- however TLS10 make it clear that the length field need to be present.
|
|
|
|
e <- encryptRSA premaster
|
|
|
|
let extra = if xver < TLS10
|
|
|
|
then B.empty
|
|
|
|
else encodeWord16 $ fromIntegral $ B.length e
|
|
|
|
return $ extra `B.append` e
|
2011-12-01 08:41:01 +00:00
|
|
|
sendPacket ctx $ Handshake [ClientKeyXchg encryptedPreMaster]
|
2011-03-01 23:09:17 +00:00
|
|
|
|
2011-05-13 06:02:44 +00:00
|
|
|
-- on certificate reject, throw an exception with the proper protocol alert error.
|
|
|
|
certificateRejected CertificateRejectRevoked =
|
|
|
|
throwCore $ Error_Protocol ("certificate is revoked", True, CertificateRevoked)
|
|
|
|
certificateRejected CertificateRejectExpired =
|
|
|
|
throwCore $ Error_Protocol ("certificate has expired", True, CertificateExpired)
|
|
|
|
certificateRejected CertificateRejectUnknownCA =
|
|
|
|
throwCore $ Error_Protocol ("certificate has unknown CA", True, UnknownCa)
|
|
|
|
certificateRejected (CertificateRejectOther s) =
|
|
|
|
throwCore $ Error_Protocol ("certificate rejected: " ++ s, True, CertificateUnknown)
|
|
|
|
|
2012-03-12 08:48:03 +00:00
|
|
|
handshakeServerWith :: MonadIO m => TLSCtx -> Handshake -> m ()
|
2011-12-20 07:47:17 +00:00
|
|
|
handshakeServerWith ctx clientHello@(ClientHello ver _ clientSession ciphers compressions _) = do
|
2011-11-12 11:05:12 +00:00
|
|
|
-- check if policy allow this new handshake to happens
|
|
|
|
handshakeAuthorized <- withMeasure ctx (onHandshake $ ctxParams ctx)
|
|
|
|
unless handshakeAuthorized (throwCore $ Error_HandshakePolicy "server: handshake denied")
|
2011-11-11 19:05:17 +00:00
|
|
|
updateMeasure ctx incrementNbHandshakes
|
|
|
|
|
2011-03-02 07:35:25 +00:00
|
|
|
-- Handle Client hello
|
2011-12-01 08:55:44 +00:00
|
|
|
usingState_ ctx $ processHandshake clientHello
|
2011-05-13 06:08:27 +00:00
|
|
|
when (ver == SSL2) $ throwCore $ Error_Protocol ("ssl2 is not supported", True, ProtocolVersion)
|
|
|
|
when (not $ elem ver (pAllowedVersions params)) $
|
|
|
|
throwCore $ Error_Protocol ("version " ++ show ver ++ "is not supported", True, ProtocolVersion)
|
|
|
|
when (commonCiphers == []) $
|
|
|
|
throwCore $ Error_Protocol ("no cipher in common with the client", True, HandshakeFailure)
|
2011-08-12 17:31:58 +00:00
|
|
|
when (null commonCompressions) $
|
2011-05-13 06:08:27 +00:00
|
|
|
throwCore $ Error_Protocol ("no compression in common with the client", True, HandshakeFailure)
|
2011-03-02 07:35:25 +00:00
|
|
|
usingState_ ctx $ modify (\st -> st
|
2011-08-12 17:31:58 +00:00
|
|
|
{ stVersion = ver
|
|
|
|
, stCipher = Just usedCipher
|
|
|
|
, stCompression = usedCompression
|
2011-03-02 07:35:25 +00:00
|
|
|
})
|
|
|
|
|
2011-12-20 07:47:17 +00:00
|
|
|
resumeSessionData <- case clientSession of
|
|
|
|
(Session (Just clientSessionId)) -> liftIO $ onSessionResumption params $ clientSessionId
|
|
|
|
(Session Nothing) -> return Nothing
|
|
|
|
case resumeSessionData of
|
2011-11-12 16:15:05 +00:00
|
|
|
Nothing -> do
|
|
|
|
handshakeSendServerData
|
|
|
|
liftIO $ connectionFlush ctx
|
|
|
|
|
|
|
|
-- Receive client info until client Finished.
|
2011-12-01 22:33:53 +00:00
|
|
|
recvClientData
|
|
|
|
sendChangeCipherAndFinish ctx False
|
2011-12-20 07:47:17 +00:00
|
|
|
Just sessionData -> do
|
|
|
|
usingState_ ctx (setSession clientSession True)
|
|
|
|
serverhello <- makeServerHello clientSession
|
2011-11-12 16:15:05 +00:00
|
|
|
sendPacket ctx $ Handshake [serverhello]
|
2011-12-20 07:47:17 +00:00
|
|
|
usingState_ ctx $ setMasterSecret $ sessionSecret sessionData
|
2011-12-01 22:33:53 +00:00
|
|
|
sendChangeCipherAndFinish ctx False
|
|
|
|
recvChangeCipherAndFinish ctx
|
2011-12-12 08:43:52 +00:00
|
|
|
handshakeTerminate ctx
|
2011-03-02 07:35:25 +00:00
|
|
|
where
|
2011-03-02 07:56:37 +00:00
|
|
|
params = ctxParams ctx
|
2011-03-02 07:35:25 +00:00
|
|
|
commonCiphers = intersect ciphers (map cipherID $ pCiphers params)
|
|
|
|
usedCipher = fromJust $ find (\c -> cipherID c == head commonCiphers) (pCiphers params)
|
2011-08-12 17:31:58 +00:00
|
|
|
commonCompressions = compressionIntersectID (pCompressions params) compressions
|
|
|
|
usedCompression = head commonCompressions
|
2011-03-02 07:35:25 +00:00
|
|
|
srvCerts = map fst $ pCertificates params
|
|
|
|
privKeys = map snd $ pCertificates params
|
|
|
|
needKeyXchg = cipherExchangeNeedMoreData $ cipherKeyExchange usedCipher
|
|
|
|
|
2011-11-29 08:59:41 +00:00
|
|
|
---
|
|
|
|
recvClientData = runRecvState ctx (RecvStateHandshake $ processClientCertificate)
|
|
|
|
|
|
|
|
processClientCertificate (Certificates _) = return $ RecvStateHandshake processClientKeyExchange
|
|
|
|
processClientCertificate p = processClientKeyExchange p
|
|
|
|
|
2011-12-01 08:41:01 +00:00
|
|
|
processClientKeyExchange (ClientKeyXchg _) = return $ RecvStateNext processCertificateVerify
|
|
|
|
processClientKeyExchange p = unexpected (show p) (Just "client key exchange")
|
2011-11-29 08:59:41 +00:00
|
|
|
|
|
|
|
processCertificateVerify (Handshake [CertVerify _]) = return $ RecvStateNext expectChangeCipher
|
|
|
|
processCertificateVerify p = expectChangeCipher p
|
|
|
|
|
|
|
|
expectChangeCipher ChangeCipherSpec = return $ RecvStateHandshake expectFinish
|
|
|
|
expectChangeCipher p = unexpected (show p) (Just "change cipher")
|
|
|
|
|
|
|
|
expectFinish (Finished _) = return RecvStateDone
|
|
|
|
expectFinish p = unexpected (show p) (Just "Handshake Finished")
|
|
|
|
---
|
2011-03-02 07:35:25 +00:00
|
|
|
|
2011-11-12 16:15:05 +00:00
|
|
|
makeServerHello session = do
|
|
|
|
srand <- getStateRNG ctx 32 >>= return . ServerRandom
|
2011-03-02 07:35:25 +00:00
|
|
|
case privKeys of
|
|
|
|
(Just privkey : _) -> usingState_ ctx $ setPrivateKey privkey
|
|
|
|
_ -> return () -- return a sensible error
|
|
|
|
|
|
|
|
-- in TLS12, we need to check as well the certificates we are sending if they have in the extension
|
|
|
|
-- the necessary bits set.
|
2011-06-07 07:28:02 +00:00
|
|
|
secReneg <- usingState_ ctx getSecureRenegotiation
|
|
|
|
extensions <- if secReneg
|
|
|
|
then do
|
|
|
|
vf <- usingState_ ctx $ do
|
|
|
|
cvf <- getVerifiedData True
|
|
|
|
svf <- getVerifiedData False
|
2011-06-13 07:33:14 +00:00
|
|
|
return $ encodeExtSecureRenegotiation cvf (Just svf)
|
2011-06-07 07:28:02 +00:00
|
|
|
return [ (0xff01, vf) ]
|
|
|
|
else return []
|
2011-06-12 20:42:55 +00:00
|
|
|
usingState_ ctx (setVersion ver >> setServerRandom srand)
|
2011-12-20 07:47:17 +00:00
|
|
|
return $ ServerHello ver srand session (cipherID usedCipher)
|
2011-11-12 16:15:05 +00:00
|
|
|
(compressionID usedCompression) extensions
|
|
|
|
|
|
|
|
handshakeSendServerData = do
|
2011-12-20 07:47:17 +00:00
|
|
|
serverSession <- newSession ctx
|
|
|
|
usingState_ ctx (setSession serverSession False)
|
|
|
|
serverhello <- makeServerHello serverSession
|
2011-11-13 08:53:00 +00:00
|
|
|
-- send ServerHello & Certificate & ServerKeyXchg & CertReq
|
2011-11-12 16:15:05 +00:00
|
|
|
sendPacket ctx $ Handshake [ serverhello, Certificates srvCerts ]
|
2011-03-02 07:35:25 +00:00
|
|
|
when needKeyXchg $ do
|
|
|
|
let skg = SKX_RSA Nothing
|
2011-06-10 20:24:46 +00:00
|
|
|
sendPacket ctx (Handshake [ServerKeyXchg skg])
|
2011-03-02 07:35:25 +00:00
|
|
|
-- FIXME we don't do this on a Anonymous server
|
|
|
|
when (pWantClientCert params) $ do
|
|
|
|
let certTypes = [ CertificateType_RSA_Sign ]
|
|
|
|
let creq = CertRequest certTypes Nothing [0,0,0]
|
2011-06-10 20:24:46 +00:00
|
|
|
sendPacket ctx (Handshake [creq])
|
2011-03-02 07:35:25 +00:00
|
|
|
-- Send HelloDone
|
2011-06-10 20:24:46 +00:00
|
|
|
sendPacket ctx (Handshake [ServerHelloDone])
|
2011-03-02 07:35:25 +00:00
|
|
|
|
2011-03-02 07:56:37 +00:00
|
|
|
handshakeServerWith _ _ = fail "unexpected handshake type received. expecting client hello"
|
2011-03-02 07:35:25 +00:00
|
|
|
|
2011-11-11 19:05:17 +00:00
|
|
|
-- after receiving a client hello, we need to redo a handshake
|
2012-03-12 08:48:03 +00:00
|
|
|
handshakeServer :: MonadIO m => TLSCtx -> m ()
|
2011-03-02 07:35:25 +00:00
|
|
|
handshakeServer ctx = do
|
2011-11-13 11:11:39 +00:00
|
|
|
hss <- recvPacketHandshake ctx
|
|
|
|
case hss of
|
|
|
|
[ch] -> handshakeServerWith ctx ch
|
|
|
|
_ -> fail ("unexpected handshake received, excepting client hello and received " ++ show hss)
|
2011-03-02 07:35:25 +00:00
|
|
|
|
2011-03-02 08:43:05 +00:00
|
|
|
-- | Handshake for a new TLS connection
|
2012-03-10 21:04:44 +00:00
|
|
|
-- This is to be called at the beginning of a connection, and during renegotiation
|
2012-03-12 08:48:03 +00:00
|
|
|
handshake :: MonadIO m => TLSCtx -> m ()
|
2011-03-01 23:09:17 +00:00
|
|
|
handshake ctx = do
|
|
|
|
cc <- usingState_ ctx (stClientContext <$> get)
|
2011-05-13 07:39:15 +00:00
|
|
|
liftIO $ handleException $ if cc then handshakeClient ctx else handshakeServer ctx
|
|
|
|
where
|
2012-01-18 06:29:29 +00:00
|
|
|
handleException f = catch f $ \exception -> do
|
|
|
|
let tlserror = maybe (Error_Misc $ show exception) id $ fromException exception
|
2012-01-25 16:01:55 +00:00
|
|
|
setEstablished ctx False
|
2012-01-18 06:29:29 +00:00
|
|
|
sendPacket ctx (errorToAlert tlserror)
|
|
|
|
handshakeFailed tlserror
|
2011-03-01 23:09:17 +00:00
|
|
|
|
2011-03-02 08:43:05 +00:00
|
|
|
-- | sendData sends a bunch of data.
|
|
|
|
-- It will automatically chunk data to acceptable packet size
|
2012-03-12 08:48:03 +00:00
|
|
|
sendData :: MonadIO m => TLSCtx -> L.ByteString -> m ()
|
2012-01-25 16:03:31 +00:00
|
|
|
sendData ctx dataToSend = checkValid ctx >> mapM_ sendDataChunk (L.toChunks dataToSend)
|
|
|
|
where sendDataChunk d
|
|
|
|
| B.length d > 16384 = do
|
|
|
|
let (sending, remain) = B.splitAt 16384 d
|
|
|
|
sendPacket ctx $ AppData sending
|
|
|
|
sendDataChunk remain
|
|
|
|
| otherwise = sendPacket ctx $ AppData d
|
2011-03-02 07:41:59 +00:00
|
|
|
|
2012-03-10 21:04:44 +00:00
|
|
|
-- | recvData get data out of Data packet, and automatically renegotiate if
|
2011-03-02 08:43:05 +00:00
|
|
|
-- a Handshake ClientHello is received
|
2012-03-12 08:48:03 +00:00
|
|
|
recvData :: MonadIO m => TLSCtx -> m B.ByteString
|
2011-03-02 07:41:59 +00:00
|
|
|
recvData ctx = do
|
2012-01-25 16:01:55 +00:00
|
|
|
checkValid ctx
|
|
|
|
pkt <- recvPacket ctx
|
2011-03-02 07:41:59 +00:00
|
|
|
case pkt of
|
2012-03-10 21:04:44 +00:00
|
|
|
-- on server context receiving a client hello == renegotiation
|
2012-03-12 07:11:21 +00:00
|
|
|
Right (Handshake [ch@(ClientHello {})]) ->
|
2011-03-02 07:41:59 +00:00
|
|
|
handshakeServerWith ctx ch >> recvData ctx
|
2012-03-10 21:04:44 +00:00
|
|
|
-- on client context, receiving a hello request == renegotiation
|
2011-06-10 20:24:46 +00:00
|
|
|
Right (Handshake [HelloRequest]) ->
|
2011-03-02 07:41:59 +00:00
|
|
|
handshakeClient ctx >> recvData ctx
|
2011-06-13 07:19:29 +00:00
|
|
|
Right (Alert [(AlertLevel_Fatal, _)]) -> do
|
|
|
|
setEOF ctx
|
2012-02-07 20:41:28 +00:00
|
|
|
return B.empty
|
2011-06-10 20:24:46 +00:00
|
|
|
Right (Alert [(AlertLevel_Warning, CloseNotify)]) -> do
|
2011-06-13 07:19:29 +00:00
|
|
|
setEOF ctx
|
2012-02-07 20:41:28 +00:00
|
|
|
return B.empty
|
|
|
|
Right (AppData x) -> return x
|
2011-10-08 08:41:09 +00:00
|
|
|
Right p -> error ("error unexpected packet: " ++ show p)
|
2011-03-02 07:41:59 +00:00
|
|
|
Left err -> error ("error received: " ++ show err)
|
2012-02-07 20:41:28 +00:00
|
|
|
|
2012-03-12 08:48:03 +00:00
|
|
|
recvData' :: MonadIO m => TLSCtx -> m L.ByteString
|
2012-02-07 20:41:28 +00:00
|
|
|
recvData' ctx = recvData ctx >>= return . L.fromChunks . (:[])
|