HsOpenSSL 0.4 → 0.4.1
raw patch · 11 files changed
+304/−267 lines, 11 files
Files
- HsOpenSSL.cabal +4/−4
- NEWS +12/−0
- OpenSSL/ASN1.hsc +2/−2
- OpenSSL/BN.hsc +14/−15
- OpenSSL/EVP/Cipher.hsc +7/−7
- OpenSSL/EVP/Digest.hsc +17/−1
- OpenSSL/Session.hsc +247/−62
- OpenSSL/SocketBIO.hsc +0/−75
- OpenSSL/X509/Name.hsc +1/−1
- cbits/HsOpenSSL.c +0/−96
- cbits/HsOpenSSL.h +0/−4
HsOpenSSL.cabal view
@@ -5,16 +5,17 @@ generate RSA and DSA keys, read and write PEM files, generate message digests, sign and verify messages, encrypt and decrypt messages.-Version: 0.4+Version: 0.4.1 License: PublicDomain License-File: COPYING-Author: PHO <phonohawk at ps dot sakura dot ne dot jp>-Maintainer: PHO <phonohawk at ps dot sakura dot ne dot jp>+Author: PHO <pho at cielonegro.org>+Maintainer: PHO <pho at cielonegro.org> Stability: experimental Homepage: http://ccm.sherry.jp/HsOpenSSL/ Category: Cryptography Tested-With: GHC == 6.8.1 Cabal-Version: >= 1.2+Build-Type: Configure Extra-Source-Files: AUTHORS@@ -66,7 +67,6 @@ OpenSSL.X509.Revocation OpenSSL.X509.Request OpenSSL.X509.Store- OpenSSL.SocketBIO OpenSSL.Session Other-Modules: OpenSSL.ASN1
NEWS view
@@ -1,3 +1,15 @@+Changes from 0.4 to 0.4.1+-------------------------+* Applied patches by Adam Langley:+ - Fix BN<->Integer conversions on 64-bit systems+ - Another 64-bit fix (OpenSSL.ASN1.peekASN1String)+ - Add ByteString version of digestBS+ - Fix the foreign types of the cipher functions to use CInt, not Int+ - 64-bit fix for HMAC+ - Turn the Session IO inside out+ - Silly cosmetic change++ Changes from 0.3.1 to 0.4 ------------------------- * Applied patches by Adam Langley:
OpenSSL/ASN1.hsc view
@@ -61,8 +61,8 @@ peekASN1String :: Ptr ASN1_STRING -> IO String peekASN1String strPtr = do buf <- (#peek ASN1_STRING, data ) strPtr- len <- (#peek ASN1_STRING, length) strPtr- peekCStringLen (buf, len)+ len <- (#peek ASN1_STRING, length) strPtr :: IO CInt+ peekCStringLen (buf, fromIntegral len) {- ASN1_INTEGER -------------------------------------------------------------- -}
OpenSSL/BN.hsc view
@@ -51,7 +51,6 @@ import Foreign.C #else import Foreign.C.Types-import Data.Word (Word32) import GHC.Base import GHC.Num import GHC.Prim@@ -164,11 +163,11 @@ -- | Convert a BIGNUM to an Integer bnToInteger :: BigNum -> IO Integer bnToInteger bn = do- nlimbs <- (#peek BIGNUM, top) (unwrapBN bn) :: IO CSize+ nlimbs <- (#peek BIGNUM, top) (unwrapBN bn) :: IO CInt case nlimbs of 0 -> return 0 1 -> do (I## i) <- (#peek BIGNUM, d) (unwrapBN bn) >>= peek- negative <- (#peek BIGNUM, neg) (unwrapBN bn) :: IO Word32+ negative <- (#peek BIGNUM, neg) (unwrapBN bn) :: IO CInt if negative == 0 then return $ S## i else return $ 0 - (S## i)@@ -179,7 +178,7 @@ (BA ba) <- freezeByteArray arr limbs <- (#peek BIGNUM, d) (unwrapBN bn) _copy_in ba limbs $ fromIntegral $ nlimbs * (#size unsigned long)- negative <- (#peek BIGNUM, neg) (unwrapBN bn) :: IO Word32+ negative <- (#peek BIGNUM, neg) (unwrapBN bn) :: IO CInt if negative == 0 then return $ J## nlimbsi ba else return $ 0 - (J## nlimbsi ba)@@ -191,9 +190,9 @@ bnptr <- mallocBytes (#size BIGNUM) (#poke BIGNUM, d) bnptr nullPtr -- This is needed to give GHC enough type information- let one :: Word32+ let one :: CInt one = 1- zero :: Word32+ zero :: CInt zero = 0 (#poke BIGNUM, flags) bnptr one (#poke BIGNUM, top) bnptr zero@@ -203,12 +202,12 @@ integerToBN (S## v) = do bnptr <- mallocBytes (#size BIGNUM)- limbs <- malloc :: IO (Ptr Word32)+ limbs <- malloc :: IO (Ptr CULong) poke limbs $ fromIntegral $ abs $ I## v (#poke BIGNUM, d) bnptr limbs -- This is needed to give GHC enough type information since #poke just -- uses an offset- let one :: Word32+ let one :: CInt one = 1 (#poke BIGNUM, flags) bnptr one (#poke BIGNUM, top) bnptr one@@ -220,16 +219,16 @@ | v >= 0 = do let nlimbs = (I## nlimbs_) bnptr <- mallocBytes (#size BIGNUM)- limbs <- mallocBytes ((#size unsigned) * nlimbs)+ limbs <- mallocBytes ((#size unsigned long) * nlimbs) (#poke BIGNUM, d) bnptr limbs- (#poke BIGNUM, flags) bnptr (1 :: Word32)- _copy_out limbs bytearray (fromIntegral $ (#size unsigned) * nlimbs)- (#poke BIGNUM, top) bnptr ((fromIntegral nlimbs) :: Word32)- (#poke BIGNUM, dmax) bnptr ((fromIntegral nlimbs) :: Word32)- (#poke BIGNUM, neg) bnptr (0 :: Word32)+ (#poke BIGNUM, flags) bnptr (1 :: CInt)+ _copy_out limbs bytearray (fromIntegral $ (#size unsigned long) * nlimbs)+ (#poke BIGNUM, top) bnptr ((fromIntegral nlimbs) :: CInt)+ (#poke BIGNUM, dmax) bnptr ((fromIntegral nlimbs) :: CInt)+ (#poke BIGNUM, neg) bnptr (0 :: CInt) return (wrapBN bnptr) | otherwise = do bnptr <- integerToBN (0-v)- (#poke BIGNUM, neg) (unwrapBN bnptr) (1 :: Word32)+ (#poke BIGNUM, neg) (unwrapBN bnptr) (1 :: CInt) return bnptr -- TODO: we could make a function which doesn't even allocate BN data if we
OpenSSL/EVP/Cipher.hsc view
@@ -121,16 +121,16 @@ foreign import ccall unsafe "EVP_CipherInit"- _CipherInit :: Ptr EVP_CIPHER_CTX -> Ptr EVP_CIPHER -> CString -> CString -> Int -> IO Int+ _CipherInit :: Ptr EVP_CIPHER_CTX -> Ptr EVP_CIPHER -> CString -> CString -> CInt -> IO CInt foreign import ccall unsafe "EVP_CipherUpdate"- _CipherUpdate :: Ptr EVP_CIPHER_CTX -> Ptr CChar -> Ptr Int -> Ptr CChar -> Int -> IO Int+ _CipherUpdate :: Ptr EVP_CIPHER_CTX -> Ptr CChar -> Ptr CInt -> Ptr CChar -> CInt -> IO CInt foreign import ccall unsafe "EVP_CipherFinal"- _CipherFinal :: Ptr EVP_CIPHER_CTX -> Ptr CChar -> Ptr Int -> IO Int+ _CipherFinal :: Ptr EVP_CIPHER_CTX -> Ptr CChar -> Ptr CInt -> IO CInt -cryptoModeToInt :: CryptoMode -> Int+cryptoModeToInt :: CryptoMode -> CInt cryptoModeToInt Encrypt = 1 cryptoModeToInt Decrypt = 0 @@ -152,9 +152,9 @@ unsafeUseAsCStringLen inBS $ \ (inBuf, inLen) -> createAndTrim (inLen + _ctx_block_size ctxPtr - 1) $ \ outBuf -> alloca $ \ outLenPtr ->- _CipherUpdate ctxPtr (castPtr outBuf) outLenPtr inBuf inLen+ _CipherUpdate ctxPtr (castPtr outBuf) outLenPtr inBuf (fromIntegral inLen) >>= failIf (/= 1)- >> peek outLenPtr+ >> liftM fromIntegral (peek outLenPtr) cipherFinalBS :: CipherCtx -> IO B8.ByteString@@ -164,7 +164,7 @@ alloca $ \ outLenPtr -> _CipherFinal ctxPtr (castPtr outBuf) outLenPtr >>= failIf (/= 1)- >> peek outLenPtr+ >> liftM fromIntegral (peek outLenPtr) -- |@'cipher'@ lazilly encrypts or decrypts a stream of data. The -- input string doesn't necessarily have to be finite.
OpenSSL/EVP/Digest.hsc view
@@ -23,6 +23,7 @@ , digest , digestBS+ , digestBS' , digestLBS , hmacBS@@ -30,6 +31,7 @@ where import Control.Monad+import Data.ByteString.Internal (createAndTrim) import Data.ByteString.Unsafe (unsafeUseAsCStringLen) import qualified Data.ByteString.Char8 as B8 import qualified Data.ByteString.Lazy.Char8 as L8@@ -133,7 +135,15 @@ bufLen <- liftM fromIntegral $ peek bufLenPtr peekCStringLen (bufPtr, bufLen) +digestFinalBS :: DigestCtx -> IO B8.ByteString+digestFinalBS ctx =+ withDigestCtxPtr ctx $ \ctxPtr ->+ createAndTrim (#const EVP_MAX_MD_SIZE) $ \bufPtr ->+ alloca $ \bufLenPtr ->+ do _DigestFinal ctxPtr (castPtr bufPtr) bufLenPtr >>= failIf (/= 1)+ liftM fromIntegral $ peek bufLenPtr + digestStrictly :: Digest -> B8.ByteString -> IO DigestCtx digestStrictly md input = do ctx <- digestInit md@@ -161,6 +171,12 @@ do ctx <- digestStrictly md input digestFinal ctx +digestBS' :: Digest -> B8.ByteString -> B8.ByteString+digestBS' md input+ = unsafePerformIO $+ do ctx <- digestStrictly md input+ digestFinalBS ctx+ -- |@'digestLBS'@ digests a stream of data. digestLBS :: Digest -> L8.ByteString -> String digestLBS md input@@ -171,7 +187,7 @@ {- HMAC ---------------------------------------------------------------------- -} foreign import ccall unsafe "HMAC"- _HMAC :: Ptr EVP_MD -> Ptr CChar -> CInt -> Ptr CChar -> CInt+ _HMAC :: Ptr EVP_MD -> Ptr CChar -> CInt -> Ptr CChar -> CSize -> Ptr CChar -> Ptr CUInt -> IO () -- | Perform a private key signing using the HMAC template with a given hash
OpenSSL/Session.hsc view
@@ -1,8 +1,7 @@ {-# OPTIONS_GHC -fno-warn-name-shadowing #-}--- | Functions for handling SSL/TLS connections over Sockets. The Sockets are--- wrapped in BIO objects so that openssl works well with Haskell's threading--- system (i.e. a 'blocking' read will actually allow other green threads to--- run rather than blocking the whole OS thread)+-- | Functions for handling SSL connections. These functions use GHC specific+-- calls to cooperative the with the scheduler so that 'blocking' functions+-- only actually block the Haskell thread, not a whole OS thread. module OpenSSL.Session ( -- * Contexts SSLContext@@ -12,29 +11,41 @@ , contextSetCiphers , contextSetDefaultCiphers , contextCheckPrivateKey+ , VerificationMode(..)+ , contextSetVerificationMode+ , contextSetCAFile+ , contextSetCADirectory -- * SSL connections , SSL , connection , accept+ , connect , read , write- , ShutdownType(..) , shutdown+ , ShutdownType(..)+ , getPeerCertificate+ , getVerifyResult+ , sslSocket ) where #include "openssl/ssl.h" -import Prelude hiding (read)+import Prelude hiding (read, ioError)+import Control.Concurrent (threadWaitWrite, threadWaitRead)+import Control.Concurrent.QSem+import Control.Exception (finally) import Foreign import Foreign.C import qualified Data.ByteString.Internal as B import qualified Data.ByteString.Unsafe as B-import Network.Socket (Socket)+import System.IO.Error (mkIOError, ioError, eofErrorType)+import System.Posix.Types (Fd(..))+import Network.Socket (Socket(..)) -import OpenSSL.Utils (failIfNull, failIf, raiseOpenSSLError)-import OpenSSL.BIO (BIO, BIO_, withBioPtr)-import OpenSSL.SocketBIO (socketToBIO)+import OpenSSL.Utils (failIfNull, failIf)+import OpenSSL.X509 (X509, X509_, wrapX509) data SSLContext_ -- | An SSL context. Contexts carry configuration such as a server's private@@ -42,7 +53,11 @@ -- start empty and various options are set on them by the functions in this -- module. Note that an empty context will pretty much cause any operation to -- fail since it doesn't even have any ciphers enabled.-newtype SSLContext = SSLContext (ForeignPtr SSLContext_)+--+-- Contexts are not thread safe so they carry a QSem with them which only+-- lets a single thread work inside them at a time. Thus, one must always use+-- withContext, not withForeignPtr directly.+newtype SSLContext = SSLContext (QSem, ForeignPtr SSLContext_) data SSLMethod_ @@ -52,12 +67,23 @@ -- | Create a new SSL context. context :: IO SSLContext-context = _ssl_method >>= _ssl_ctx_new >>= newForeignPtr _ssl_ctx_free >>= return . SSLContext+context = do+ ctx <- _ssl_method >>= _ssl_ctx_new+ context <- newForeignPtr _ssl_ctx_free ctx+ sem <- newQSem 1+ return $ SSLContext (sem, context) +-- | Run the given action with the raw context pointer and obtain the lock+-- while doing so.+withContext :: SSLContext -> (Ptr SSLContext_ -> IO a) -> IO a+withContext (SSLContext (sem, ctxfp)) action = do+ waitQSem sem+ finally (withForeignPtr ctxfp action) $ signalQSem sem+ contextLoadFile :: (Ptr SSLContext_ -> CString -> CInt -> IO Int) -> SSLContext -> String -> IO ()-contextLoadFile f (SSLContext context) path =- withForeignPtr context $ \ctx ->+contextLoadFile f context path =+ withContext context $ \ctx -> withCString path $ \cpath -> do result <- f ctx cpath (#const SSL_FILETYPE_PEM) if result == 1@@ -91,8 +117,8 @@ -- Unrecognised ciphers are ignored. If no ciphers from the list are -- recognised, an exception is raised. contextSetCiphers :: SSLContext -> String -> IO ()-contextSetCiphers (SSLContext context) list =- withForeignPtr context $ \ctx ->+contextSetCiphers context list =+ withContext context $ \ctx -> withCString list $ \cpath -> _ssl_ctx_set_cipher_list ctx cpath >>= failIf (/= 1) >> return () @@ -105,75 +131,202 @@ -- | Return true iff the private key installed in the given context matches the -- certificate also installed. contextCheckPrivateKey :: SSLContext -> IO Bool-contextCheckPrivateKey (SSLContext context) =- withForeignPtr context $ \ctx ->+contextCheckPrivateKey context =+ withContext context $ \ctx -> _ssl_ctx_check_private_key ctx >>= return . (==) 1 +-- | See <http://www.openssl.org/docs/ssl/SSL_CTX_set_verify.html>+data VerificationMode = VerifyNone+ | VerifyPeer Bool -- ^ is a certificate required+ Bool -- ^ only request once per connection++foreign import ccall unsafe "SSL_CTX_set_verify"+ _ssl_set_verify_mode :: Ptr SSLContext_ -> CInt -> Ptr () -> IO ()++contextSetVerificationMode :: SSLContext -> VerificationMode -> IO ()+contextSetVerificationMode context VerifyNone = do+ withContext context $ \ctx ->+ _ssl_set_verify_mode ctx (#const SSL_VERIFY_NONE) nullPtr >> return ()++contextSetVerificationMode context (VerifyPeer reqp oncep) = do+ let mode = (#const SSL_VERIFY_PEER) .|.+ (if reqp then (#const SSL_VERIFY_FAIL_IF_NO_PEER_CERT) else 0) .|.+ (if oncep then (#const SSL_VERIFY_CLIENT_ONCE) else 0)+ withContext context $ \ctx ->+ _ssl_set_verify_mode ctx mode nullPtr >> return ()++foreign import ccall unsafe "SSL_CTX_load_verify_locations"+ _ssl_load_verify_locations :: Ptr SSLContext_ -> Ptr CChar -> Ptr CChar -> IO CInt++-- | Set the location of a PEM encoded list of CA certificates to be used when+-- verifying a server's certificate+contextSetCAFile :: SSLContext -> FilePath -> IO ()+contextSetCAFile context path = do+ withContext context $ \ctx ->+ withCString path $ \cpath -> do+ _ssl_load_verify_locations ctx cpath nullPtr >>= failIf (/= 1)+ return ()++-- | Set the path to a directory which contains the PEM encoded CA root+-- certificates. This is an alternative to 'contextSetCAFile'. See+-- <http://www.openssl.org/docs/ssl/SSL_CTX_load_verify_locations.html> for+-- details of the file naming scheme+contextSetCADirectory :: SSLContext -> FilePath -> IO ()+contextSetCADirectory context path = do+ withContext context $ \ctx ->+ withCString path $ \cpath -> do+ _ssl_load_verify_locations ctx nullPtr cpath >>= failIf (/= 1)+ return ()+++ data SSL_ -- | This is the type of an SSL connection-newtype SSL = SSL (Socket, BIO, ForeignPtr SSL_)+--+-- SSL objects are not thread safe, so they carry a QSem around with them+-- which only lets a single thread work inside them at a time. Thus, one must+-- always use withSSL, rather than withForeignPtr directly.+--+-- IO with SSL objects is non-blocking and many SSL functions return a error+-- code which signifies that it needs to read or write more data. We handle+-- these calls and call threadWaitRead and threadWaitWrite at the correct+-- times. Thus multiple OS threads can be 'blocked' inside IO in the same SSL+-- object at a time, because they aren't really in the SSL object, they are+-- waiting for the RTS to wake the Haskell thread.+newtype SSL = SSL (QSem, ForeignPtr SSL_, Fd, Socket) foreign import ccall unsafe "SSL_new" _ssl_new :: Ptr SSLContext_ -> IO (Ptr SSL_) foreign import ccall unsafe "&SSL_free" _ssl_free :: FunPtr (Ptr SSL_ -> IO ())-foreign import ccall unsafe "SSL_set_bio" _ssl_set_bio :: Ptr SSL_ -> Ptr BIO_ -> Ptr BIO_ -> IO ()+foreign import ccall unsafe "SSL_set_fd" _ssl_set_fd :: Ptr SSL_ -> CInt -> IO () -- | Wrap a Socket in an SSL connection. Reading and writing to the Socket -- after this will cause weird errors in the SSL code. The SSL object -- carries a handle to the Socket so you need not worry about the garbage -- collector closing the file descriptor out from under you. connection :: SSLContext -> Socket -> IO SSL-connection (SSLContext context) sock = do- bio <- socketToBIO sock- ssl <- withBioPtr bio (\bio -> do- withForeignPtr context (\ctx -> do- ssl <- _ssl_new ctx >>= failIfNull- _ssl_set_bio ssl bio bio- return ssl))+connection context sock@(MkSocket fd _ _ _ _) = do+ sem <- newQSem 1+ ssl <- withContext context (\ctx -> do+ ssl <- _ssl_new ctx >>= failIfNull+ _ssl_set_fd ssl fd+ return ssl) fpssl <- newForeignPtr _ssl_free ssl- return $ SSL (sock, bio, fpssl)+ return $ SSL (sem, fpssl, Fd fd, sock) +withSSL :: SSL -> (Ptr SSL_ -> IO a) -> IO a+withSSL (SSL (sem, ssl, _, _)) action = do+ waitQSem sem+ finally (withForeignPtr ssl action) $ signalQSem sem+ foreign import ccall "SSL_accept" _ssl_accept :: Ptr SSL_ -> IO CInt+foreign import ccall "SSL_connect" _ssl_connect :: Ptr SSL_ -> IO CInt+foreign import ccall unsafe "SSL_get_error" _ssl_get_error :: Ptr SSL_ -> CInt -> IO CInt +sslErrorToString :: CInt -> String+sslErrorToString (#const SSL_ERROR_NONE) = "SSL: No error"+sslErrorToString (#const SSL_ERROR_ZERO_RETURN) = "SSL: connection cleanly closed"+sslErrorToString (#const SSL_ERROR_WANT_CONNECT) = "SSL: want connect"+sslErrorToString (#const SSL_ERROR_WANT_ACCEPT) = "SSL: want accept"+sslErrorToString (#const SSL_ERROR_WANT_X509_LOOKUP) = "SSL: want X509 lookup"+sslErrorToString (#const SSL_ERROR_SYSCALL) = "SSL: syscall error"+sslErrorToString (#const SSL_ERROR_SSL) = "SSL: ssl protocol error"+sslErrorToString x = "SSL: unknown error " ++ show x++-- | This is the type of an SSL IO operation. EOF and termination are handled+-- by exceptions while everything else is one of these. Note that reading+-- from an SSL socket can result in WantWrite and vice versa.+data SSLIOResult = Done CInt -- ^ successfully mananged *n* bytes+ | WantRead -- ^ needs more data from the network+ | WantWrite -- ^ needs more outgoing buffer space+ deriving (Eq)+++-- | Perform an SSL operation which can return non-blocking error codes, thus+-- requesting that the operation be performed when data or buffer space is+-- availible.+sslDoHandshake :: (Ptr SSL_ -> IO CInt) -> SSL -> IO CInt+sslDoHandshake action ssl@(SSL (_, _, fd, _)) = do+ let f ssl = do+ n <- action ssl+ case n of+ n | n >= 0 -> return $ Done n+ _ -> do+ err <- _ssl_get_error ssl n+ case err of+ (#const SSL_ERROR_WANT_READ) -> return WantRead+ (#const SSL_ERROR_WANT_WRITE) -> return WantWrite+ _ -> fail $ sslErrorToString err+ result <- withSSL ssl f+ case result of+ Done n -> return n+ WantRead -> threadWaitRead fd >> sslDoHandshake action ssl+ WantWrite -> threadWaitWrite fd >> sslDoHandshake action ssl+ -- | Perform an SSL server handshake accept :: SSL -> IO ()-accept (SSL (_, _, ssl)) = withForeignPtr ssl (\ssl -> do- _ssl_accept ssl >>= failIf (/= 1)) >> return ()+accept ssl = sslDoHandshake _ssl_accept ssl >>= failIf (/= 1) >> return () +-- | Perform an SSL client handshake+connect :: SSL -> IO ()+connect ssl = sslDoHandshake _ssl_connect ssl >>= failIf (/= 1) >> return ()+ foreign import ccall "SSL_read" _ssl_read :: Ptr SSL_ -> Ptr Word8 -> CInt -> IO CInt foreign import ccall unsafe "SSL_get_shutdown" _ssl_get_shutdown :: Ptr SSL_ -> IO CInt +-- | Perform an SSL operation which operates of a buffer and can return+-- non-blocking error codes, thus requesting that it be performed again when+-- more data or buffer space is availible.+--+-- Note that these SSL functions generally require that the arguments to the+-- repeated call be exactly the same. This presents an issue because multiple+-- threads could try writing at the same time (with different buffers) so the+-- calling function should probably hold the lock on the SSL object over the+-- whole time (include repeated calls)+sslIOInner :: (Ptr SSL_ -> Ptr Word8 -> CInt -> IO CInt) -- ^ the SSL IO function to call+ -> Ptr CChar -- ^ the buffer to pass+ -> Int -- ^ the length to pass+ -> Ptr SSL_+ -> IO SSLIOResult+sslIOInner f ptr nbytes ssl = do+ n <- f ssl (castPtr ptr) $ fromIntegral nbytes+ case n of+ n | n > 0 -> return $ Done $ fromIntegral n+ | n == 0 -> do+ shutdown <- _ssl_get_shutdown ssl+ if shutdown .&. (#const SSL_RECEIVED_SHUTDOWN) == 0+ then fail "SSL connection abruptly terminated"+ else ioError $ mkIOError eofErrorType "" Nothing Nothing+ _ -> do+ err <- _ssl_get_error ssl n+ case err of+ (#const SSL_ERROR_WANT_READ) -> return WantRead+ (#const SSL_ERROR_WANT_WRITE) -> return WantWrite+ _ -> fail $ sslErrorToString err+ -- | Try the read the given number of bytes from an SSL connection. On EOF an -- empty ByteString is returned. If the connection dies without a graceful -- SSL shutdown, an exception is raised. read :: SSL -> Int -> IO B.ByteString-read (SSL (_, _, ssl)) nbytes = B.createAndTrim nbytes $ \ptr ->- withForeignPtr ssl $ \ssl -> do- n <- _ssl_read ssl ptr $ fromIntegral nbytes- if n > 0- then return $ fromIntegral n- else if n < 0- then raiseOpenSSLError- else do- shutdown <- _ssl_get_shutdown ssl- if shutdown .&. (#const SSL_RECEIVED_SHUTDOWN) /= 0- then return 0- else fail "SSL connection abruptly terminated"+read ssl@(SSL (_, _, fd, _)) nbytes = B.createAndTrim nbytes $ f ssl where+ f ssl ptr = do+ result <- withSSL ssl $ sslIOInner _ssl_read (castPtr ptr) nbytes+ case result of+ Done n -> return $ fromIntegral n+ WantRead -> threadWaitRead fd >> f ssl ptr+ WantWrite -> threadWaitWrite fd >> f ssl ptr -foreign import ccall "SSL_write" _ssl_write :: Ptr SSL_ -> Ptr CChar -> CInt -> IO CInt+foreign import ccall "SSL_write" _ssl_write :: Ptr SSL_ -> Ptr Word8 -> CInt -> IO CInt -- | Write a given ByteString to the SSL connection. Either all the data is -- written or an exception is raised because of an error write :: SSL -> B.ByteString -> IO ()-write (SSL (_, _, ssl)) bs = do- withForeignPtr ssl $ \ssl ->- B.unsafeUseAsCStringLen bs $ \(ptr, nbytes) ->- let f _ 0 = return ()- f ptr nbytes = do- n <- _ssl_write ssl ptr (fromIntegral nbytes) >>= return . fromIntegral- if n <= 0- then raiseOpenSSLError- else f (ptr `plusPtr` n) (nbytes - n)- in f ptr nbytes+write ssl@(SSL (_, _, fd, _)) bs = B.unsafeUseAsCStringLen bs $ f ssl where+ f ssl (ptr, len) = do+ result <- withSSL ssl $ sslIOInner _ssl_write ptr len+ case result of+ Done _ -> return ()+ WantRead -> threadWaitRead fd >> f ssl (ptr, len)+ WantWrite -> threadWaitWrite fd >> f ssl (ptr, len) foreign import ccall "SSL_shutdown" _ssl_shutdown :: Ptr SSL_ -> IO CInt @@ -187,12 +340,44 @@ -- This can either just send a shutdown, or can send and wait for the peer's -- shutdown message. shutdown :: SSL -> ShutdownType -> IO ()-shutdown (SSL (_, _, ssl)) ty =- withForeignPtr ssl $ \ssl -> do- n <- _ssl_shutdown ssl >>= failIf (< 0)- case ty of- Unidirectional -> return ()- Bidirectional -> do- if n == 1- then return ()- else _ssl_shutdown ssl >>= failIf (< 0) >> return ()+shutdown ssl ty = do+ n <- sslDoHandshake _ssl_shutdown ssl+ case ty of+ Unidirectional -> return ()+ Bidirectional -> do+ if n == 1+ then return ()+ else shutdown ssl ty++foreign import ccall "SSL_get_peer_certificate" _ssl_get_peer_cert :: Ptr SSL_ -> IO (Ptr X509_)++-- | After a successful connection, get the certificate of the other party. If+-- this is a server connection, you probably won't get a certificate unless+-- you asked for it with contextSetVerificationMode+getPeerCertificate :: SSL -> IO (Maybe X509)+getPeerCertificate ssl =+ withSSL ssl $ \ssl -> do+ cert <- _ssl_get_peer_cert ssl+ if cert == nullPtr+ then return Nothing+ else wrapX509 cert >>= return . Just++foreign import ccall "SSL_get_verify_result" _ssl_get_verify_result :: Ptr SSL_ -> IO CLong++-- | Get the result of verifing the peer's certificate. This is mostly for+-- clients to verify the certificate of the server that they have connected+-- it. You must set a list of root CA certificates with contextSetCA... for+-- this to make sense.+--+-- Note that this returns True iff the peer's certificate has a valid chain+-- to a root CA. You also need to check that the certificate is correct (i.e.+-- has the correct hostname in it) with getPeerCertificate.+getVerifyResult :: SSL -> IO Bool+getVerifyResult ssl = do+ withSSL ssl $ \ssl -> do+ r <- _ssl_get_verify_result ssl+ return $ r == (#const X509_V_OK)++-- | Get the socket underlying an SSL connection+sslSocket :: SSL -> Socket+sslSocket (SSL (_, _, _, socket)) = socket
− OpenSSL/SocketBIO.hsc
@@ -1,75 +0,0 @@--- | This module wraps a Haskell Socket in a BIO. It's different from the--- @BIO@ module in that it works the 'other way' - gather than being the--- client of a BIO, we are the implementation.-module OpenSSL.SocketBIO (socketToBIO) where--import Foreign-import Foreign.C-import Network.Socket (Socket(..))-import GHC.Conc (threadWaitRead, threadWaitWrite)--import OpenSSL.BIO (BIO, BIO_, wrapBioPtr)--foreign import ccall "socket_BIO_wrapper" _wrapper :: CInt -> IO (Ptr BIO_)---- | Convert a Socket into a BIO object. Warning: the file descriptor--- underlying this Socket is saved in the BIO, but it doesn't carry a--- reference to the Socket itself. Thus, if you don't keep your own reference--- then the GC could close the socket from under the BIO.-socketToBIO :: Socket -> IO BIO-socketToBIO (MkSocket s _ _ _ _) = _wrapper s >>= wrapBioPtr--foreign import ccall unsafe "send"- c_send :: CInt -> Ptr a -> CSize -> CInt -> IO CInt-foreign import ccall unsafe "recv"- c_recv :: CInt -> Ptr CChar -> CSize -> CInt -> IO CInt------------------------------------------------------------------------------------- Taken from network-bytestring--{-# SPECIALISE- throwErrnoIfMinus1Retry_mayBlock- :: String -> IO CInt -> IO CInt -> IO CInt #-}-throwErrnoIfMinus1Retry_mayBlock :: Num a => String -> IO a -> IO a -> IO a-throwErrnoIfMinus1Retry_mayBlock name on_block act = do- res <- act- if res == -1- then do- err <- getErrno- if err == eINTR- then throwErrnoIfMinus1Retry_mayBlock name on_block act- else if err == eWOULDBLOCK || err == eAGAIN- then on_block- else return (-1)- else return res--throwErrnoIfMinus1Retry_repeatOnBlock :: Num a => String -> IO b -> IO a -> IO a-throwErrnoIfMinus1Retry_repeatOnBlock name on_block act = do- throwErrnoIfMinus1Retry_mayBlock name (on_block >> repeat) act- where repeat = throwErrnoIfMinus1Retry_repeatOnBlock name on_block act------------------------------------------------------------------------------------- The following functions are callback Haskell functions which are exported to--- the C code--bioRead :: CInt -> Ptr Word8 -> CInt -> IO CInt-bioRead fd ptr nbytes = do- len <- throwErrnoIfMinus1Retry_repeatOnBlock "bioRead"- (threadWaitRead (fromIntegral fd)) $- c_recv fd (castPtr ptr) (fromIntegral nbytes) 0- if fromIntegral len == (-1 :: Int)- then do errno <- getErrno- if errno == eINTR- then bioRead fd ptr nbytes- else return (-1)- else return len--bioWrite :: CInt -> Ptr Word8 -> CInt -> IO CInt-bioWrite fd ptr nbytes = do- len <- throwErrnoIfMinus1Retry_repeatOnBlock "bioWrite"- (threadWaitWrite (fromIntegral fd)) $- c_send fd (castPtr ptr) (fromIntegral nbytes) 0- return len--foreign export ccall "bioRead" bioRead :: CInt -> Ptr Word8 -> CInt -> IO CInt-foreign export ccall "bioWrite" bioWrite :: CInt -> Ptr Word8 -> CInt -> IO CInt
OpenSSL/X509/Name.hsc view
@@ -64,7 +64,7 @@ peekX509Name :: Ptr X509_NAME -> Bool -> IO [(String, String)] peekX509Name namePtr wantLongName = do count <- _entry_count namePtr >>= failIf (< 0)- mapM peekEntry $ take count [0..]+ mapM peekEntry [0..count - 1] where peekEntry :: Int -> IO (String, String) peekEntry n
cbits/HsOpenSSL.c view
@@ -203,99 +203,3 @@ sig.s = s; return dsa->meth->dsa_do_verify(ddata, dlen, &sig, dsa); }--/* Socket BIO *****************************************************************/--extern int bioRead(int fd, char *buffer, int size);-extern int bioWrite(int fd, const char *buffer, int size);--static int-wrapped_bio_fd(BIO *b) {- return (int) ((intptr_t) b->ptr);-}--static int-wrapped_bio_write(BIO *b, const char *ptr, int size) {- const int fd = wrapped_bio_fd(b);- return bioWrite(fd, ptr, size);-}--static int-wrapped_bio_read(BIO *b, char *ptr, int size) {- const int fd = wrapped_bio_fd(b);- return bioRead(fd, ptr, size);-}--static int-wrapped_bio_puts(BIO *b, const char *str) {- const int fd = wrapped_bio_fd(b);- const int n = strlen(str);- return bioWrite(fd, str, n);-}--static int-wrapped_bio_new(BIO *b) {- b->init = 1;- b->shutdown = 0;- b->flags = 0;- b->retry_reason = 0;- b->num = 0;- b->ptr = NULL;- b->next_bio = b->prev_bio = NULL;- b->callback = NULL;- // By default, reference is set to one and functions like SSL_set_bio 'steal'- // the reference. This makes sense for C programmers who can then ignore the- // reference count and everything will work out. However, we want to keep a- // reference because this ends up in a ForeignPtr and so the GC will want to- // destroy it at some point.- b->references = 2;-- return 1;-}--static int-wrapped_bio_free(BIO *b) {- return 1;-}--static long-wrapped_bio_ctl(BIO *b, int cmd, long num, void *ptr) {- long ret = 1;-- switch (cmd) {- case BIO_CTRL_RESET:- case BIO_C_FILE_SEEK:- case BIO_C_FILE_TELL:- case BIO_CTRL_INFO:- case BIO_C_SET_FD:- case BIO_C_GET_FD:- case BIO_CTRL_GET_CLOSE:- case BIO_CTRL_PENDING:- case BIO_CTRL_WPENDING:- return 0;- case BIO_CTRL_SET_CLOSE:- case BIO_CTRL_DUP:- case BIO_CTRL_FLUSH:- return 1;- default:- return 0;- }-}--static BIO_METHOD wrapper_methods =- { BIO_TYPE_SOCKET,- "socket",- wrapped_bio_write,- wrapped_bio_read,- wrapped_bio_puts,- NULL, /* gets */- wrapped_bio_ctl,- wrapped_bio_new,- wrapped_bio_free,- NULL, };--BIO *socket_BIO_wrapper(int fd) {- BIO *const b = BIO_new(&wrapper_methods);- b->ptr = (void *) ((intptr_t) fd);- return b;-}
cbits/HsOpenSSL.h view
@@ -69,8 +69,4 @@ int HsOpenSSL_dsa_verify(DSA *dsa, const unsigned char *ddata, int len, BIGNUM *r, BIGNUM *s); -/* Socket BIO *****************************************************************/--BIO *socket_BIO_wrapper(int fd);- #endif