Compare commits
	
		
			11 Commits
		
	
	
		
			7.6.3
			...
			feature/ma
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 8192da790f | ||
|  | 33e7271b85 | ||
|  | d72e5e70f6 | ||
|  | e2c5f751bd | ||
|  | 351b86e266 | ||
|  | d0cbff4f4e | ||
|  | cbfc9b9f94 | ||
|  | ca816d801f | ||
|  | 2f354d31eb | ||
|  | 2c6c1edd37 | ||
|  | 9799e7e84b | 
							
								
								
									
										4
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -1,3 +1,7 @@ | |||||||
| build | build | ||||||
| *.pyc | *.pyc | ||||||
| venv | venv | ||||||
|  | ixsnake/ixsnake/.certs/ | ||||||
|  | site/ | ||||||
|  | ws/.certs/ | ||||||
|  | ws/.srl | ||||||
|   | |||||||
| @@ -1,5 +1,20 @@ | |||||||
| # Changelog | # Changelog | ||||||
| All notable changes to this project will be documented in this file. | All changes to this project will be documented in this file. | ||||||
|  |  | ||||||
|  | ## [7.6.4] - 2019-12-22 | ||||||
|  |  | ||||||
|  | (client) error handling, quote url in error case when failing to parse one | ||||||
|  | (ws) ws_cobra_publish: register callbacks before connecting | ||||||
|  | (doc) mention mbedtls in supported ssl server backend | ||||||
|  |  | ||||||
|  |  | ||||||
|  | ## [7.6.3] - 2019-12-20 | ||||||
|  |  | ||||||
|  | (tls) add a simple description of the TLS configuration routine for debugging | ||||||
|  |  | ||||||
|  | ## [7.6.2] - 2019-12-20 | ||||||
|  |  | ||||||
|  | (mbedtls) correct support for using own certificate and private key | ||||||
|  |  | ||||||
| ## [7.6.1] - 2019-12-20 | ## [7.6.1] - 2019-12-20 | ||||||
|  |  | ||||||
|   | |||||||
| @@ -247,7 +247,7 @@ Options: | |||||||
|  |  | ||||||
| [cobra](https://github.com/machinezone/cobra) is a real time messenging server. ws has several sub-command to interact with cobra. There is also a minimal cobra compatible server named snake available. | [cobra](https://github.com/machinezone/cobra) is a real time messenging server. ws has several sub-command to interact with cobra. There is also a minimal cobra compatible server named snake available. | ||||||
|  |  | ||||||
| Below are examples on running a snake server and clients with TLS enabled (the server only works with the OpenSSL backend for now). | Below are examples on running a snake server and clients with TLS enabled (the server only works with the OpenSSL and the Mbed TLS backend for now). | ||||||
|  |  | ||||||
| First, generate certificates. | First, generate certificates. | ||||||
|  |  | ||||||
| @@ -366,4 +366,4 @@ $ ws cobra_publish --endpoint wss://127.0.0.1:8765 --appkey FC2F10139A2BAc53BB72 | |||||||
| [2019-12-19 20:46:42.659] [info] Published message id 3 acked | [2019-12-19 20:46:42.659] [info] Published message id 3 acked | ||||||
| ``` | ``` | ||||||
|  |  | ||||||
| To use OpenSSL on macOS, compile with `make ws_openssl`. First you will have to install OpenSSL libraries, which can be done with Homebrew. | To use OpenSSL on macOS, compile with `make ws_openssl`. First you will have to install OpenSSL libraries, which can be done with Homebrew. Use `make ws_mbedtls` accordingly to use MbedTLS. | ||||||
|   | |||||||
| @@ -98,6 +98,8 @@ namespace ix | |||||||
|     { |     { | ||||||
|         _channel = channel; |         _channel = channel; | ||||||
|  |  | ||||||
|  |         ix::IXCoreLogger::Log(socketTLSOptions.getDescription().c_str()); | ||||||
|  |  | ||||||
|         ix::WebSocketPerMessageDeflateOptions webSocketPerMessageDeflateOptions(enablePerMessageDeflate); |         ix::WebSocketPerMessageDeflateOptions webSocketPerMessageDeflateOptions(enablePerMessageDeflate); | ||||||
|         _cobra_connection.configure(appkey, endpoint, |         _cobra_connection.configure(appkey, endpoint, | ||||||
|                                     rolename, rolesecret, |                                     rolename, rolesecret, | ||||||
|   | |||||||
| @@ -24,9 +24,47 @@ | |||||||
|  |  | ||||||
| #include <Security/SecureTransport.h> | #include <Security/SecureTransport.h> | ||||||
|  |  | ||||||
| namespace | namespace ix | ||||||
| { | { | ||||||
|     OSStatus read_from_socket(SSLConnectionRef connection, void* data, size_t* len) |     SocketAppleSSL::SocketAppleSSL(const SocketTLSOptions& tlsOptions, int fd) | ||||||
|  |         : Socket(fd) | ||||||
|  |         , _sslContext(nullptr) | ||||||
|  |         , _tlsOptions(tlsOptions) | ||||||
|  |     { | ||||||
|  |         ; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     SocketAppleSSL::~SocketAppleSSL() | ||||||
|  |     { | ||||||
|  |         SocketAppleSSL::close(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     std::string SocketAppleSSL::getSSLErrorDescription(OSStatus status) | ||||||
|  |     { | ||||||
|  |         std::string errMsg("Unknown SSL error."); | ||||||
|  |  | ||||||
|  |         CFErrorRef error = CFErrorCreate(kCFAllocatorDefault, kCFErrorDomainOSStatus, status, NULL); | ||||||
|  |         if (error) | ||||||
|  |         { | ||||||
|  |             CFStringRef message = CFErrorCopyDescription(error); | ||||||
|  |             if (message) | ||||||
|  |             { | ||||||
|  |                 char localBuffer[128]; | ||||||
|  |                 Boolean success; | ||||||
|  |                 success = CFStringGetCString(message, localBuffer, 128, kCFStringEncodingUTF8); | ||||||
|  |                 if (success) | ||||||
|  |                 { | ||||||
|  |                     errMsg = localBuffer; | ||||||
|  |                 } | ||||||
|  |                 CFRelease(message); | ||||||
|  |             } | ||||||
|  |             CFRelease(error); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         return errMsg; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     OSStatus SocketAppleSSL::readFromSocket(SSLConnectionRef connection, void* data, size_t* len) | ||||||
|     { |     { | ||||||
|         int fd = (int) (long) connection; |         int fd = (int) (long) connection; | ||||||
|         if (fd < 0) return errSSLInternal; |         if (fd < 0) return errSSLInternal; | ||||||
| @@ -67,7 +105,7 @@ namespace | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     OSStatus write_to_socket(SSLConnectionRef connection, const void* data, size_t* len) |     OSStatus SocketAppleSSL::writeToSocket(SSLConnectionRef connection, const void* data, size_t* len) | ||||||
|     { |     { | ||||||
|         int fd = (int) (long) connection; |         int fd = (int) (long) connection; | ||||||
|         if (fd < 0) return errSSLInternal; |         if (fd < 0) return errSSLInternal; | ||||||
| @@ -105,52 +143,55 @@ namespace | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     std::string getSSLErrorDescription(OSStatus status) |  | ||||||
|     { |  | ||||||
|         std::string errMsg("Unknown SSL error."); |  | ||||||
|  |  | ||||||
|         CFErrorRef error = CFErrorCreate(kCFAllocatorDefault, kCFErrorDomainOSStatus, status, NULL); |  | ||||||
|         if (error) |  | ||||||
|         { |  | ||||||
|             CFStringRef message = CFErrorCopyDescription(error); |  | ||||||
|             if (message) |  | ||||||
|             { |  | ||||||
|                 char localBuffer[128]; |  | ||||||
|                 Boolean success; |  | ||||||
|                 success = CFStringGetCString(message, localBuffer, 128, kCFStringEncodingUTF8); |  | ||||||
|                 if (success) |  | ||||||
|                 { |  | ||||||
|                     errMsg = localBuffer; |  | ||||||
|                 } |  | ||||||
|                 CFRelease(message); |  | ||||||
|             } |  | ||||||
|             CFRelease(error); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         return errMsg; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
| } // anonymous namespace |  | ||||||
|  |  | ||||||
| namespace ix |  | ||||||
| { |  | ||||||
|     SocketAppleSSL::SocketAppleSSL(const SocketTLSOptions& tlsOptions, int fd) |  | ||||||
|         : Socket(fd) |  | ||||||
|         , _sslContext(nullptr) |  | ||||||
|         , _tlsOptions(tlsOptions) |  | ||||||
|     { |  | ||||||
|         ; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     SocketAppleSSL::~SocketAppleSSL() |  | ||||||
|     { |  | ||||||
|         SocketAppleSSL::close(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     bool SocketAppleSSL::accept(std::string& errMsg) |     bool SocketAppleSSL::accept(std::string& errMsg) | ||||||
|     { |     { | ||||||
|         errMsg = "TLS not supported yet in server mode with apple ssl backend"; |         OSStatus status; | ||||||
|         return false; |         { | ||||||
|  |             std::lock_guard<std::mutex> lock(_mutex); | ||||||
|  |  | ||||||
|  |             _sslContext = SSLCreateContext(kCFAllocatorDefault, kSSLServerSide, kSSLStreamType); | ||||||
|  |  | ||||||
|  |             SSLSetIOFuncs(_sslContext, SocketAppleSSL::readFromSocket, SocketAppleSSL::writeToSocket); | ||||||
|  |             SSLSetConnection(_sslContext, (SSLConnectionRef)(long) _sockfd); | ||||||
|  |             SSLSetProtocolVersionMin(_sslContext, kTLSProtocol12); | ||||||
|  |  | ||||||
|  |             if (_tlsOptions.isPeerVerifyDisabled()) | ||||||
|  |             { | ||||||
|  |                 Boolean option(1); | ||||||
|  |                 SSLSetSessionOption(_sslContext, kSSLSessionOptionBreakOnServerAuth, option); | ||||||
|  |  | ||||||
|  |                 do | ||||||
|  |                 { | ||||||
|  |                     status = SSLHandshake(_sslContext); | ||||||
|  |                 } while (errSSLWouldBlock == status || errSSLServerAuthCompleted == status); | ||||||
|  |  | ||||||
|  |                 if (status == errSSLServerAuthCompleted) | ||||||
|  |                 { | ||||||
|  |                     // proceed with the handshake | ||||||
|  |                     do | ||||||
|  |                     { | ||||||
|  |                         status = SSLHandshake(_sslContext); | ||||||
|  |                     } while (errSSLWouldBlock == status || errSSLServerAuthCompleted == status); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             else | ||||||
|  |             { | ||||||
|  |                 do | ||||||
|  |                 { | ||||||
|  |                     status = SSLHandshake(_sslContext); | ||||||
|  |                 } while (errSSLWouldBlock == status || errSSLServerAuthCompleted == status); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (noErr != status) | ||||||
|  |         { | ||||||
|  |             errMsg = getSSLErrorDescription(status); | ||||||
|  |             close(); | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         return true; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     // No wait support |     // No wait support | ||||||
| @@ -168,7 +209,7 @@ namespace ix | |||||||
|  |  | ||||||
|             _sslContext = SSLCreateContext(kCFAllocatorDefault, kSSLClientSide, kSSLStreamType); |             _sslContext = SSLCreateContext(kCFAllocatorDefault, kSSLClientSide, kSSLStreamType); | ||||||
|  |  | ||||||
|             SSLSetIOFuncs(_sslContext, read_from_socket, write_to_socket); |             SSLSetIOFuncs(_sslContext, SocketAppleSSL::readFromSocket, SocketAppleSSL::writeToSocket); | ||||||
|             SSLSetConnection(_sslContext, (SSLConnectionRef)(long) _sockfd); |             SSLSetConnection(_sslContext, (SSLConnectionRef)(long) _sockfd); | ||||||
|             SSLSetProtocolVersionMin(_sslContext, kTLSProtocol12); |             SSLSetProtocolVersionMin(_sslContext, kTLSProtocol12); | ||||||
|             SSLSetPeerDomainName(_sslContext, host.c_str(), host.size()); |             SSLSetPeerDomainName(_sslContext, host.c_str(), host.size()); | ||||||
|   | |||||||
| @@ -34,6 +34,10 @@ namespace ix | |||||||
|         virtual ssize_t recv(void* buffer, size_t length) final; |         virtual ssize_t recv(void* buffer, size_t length) final; | ||||||
|  |  | ||||||
|     private: |     private: | ||||||
|  |         static std::string getSSLErrorDescription(OSStatus status); | ||||||
|  |         static OSStatus writeToSocket(SSLConnectionRef connection, const void* data, size_t* len); | ||||||
|  |         static OSStatus readFromSocket(SSLConnectionRef connection, void* data, size_t* len); | ||||||
|  |  | ||||||
|         SSLContextRef _sslContext; |         SSLContextRef _sslContext; | ||||||
|         mutable std::mutex _mutex; // AppleSSL routines are not thread-safe |         mutable std::mutex _mutex; // AppleSSL routines are not thread-safe | ||||||
|  |  | ||||||
|   | |||||||
| @@ -71,11 +71,16 @@ namespace ix | |||||||
|  |  | ||||||
|         if (_tlsOptions.hasCertAndKey()) |         if (_tlsOptions.hasCertAndKey()) | ||||||
|         { |         { | ||||||
|             if (mbedtls_x509_crt_parse_file(&_cacert, _tlsOptions.certFile.c_str()) < 0) |             if (mbedtls_x509_crt_parse_file(&_cert, _tlsOptions.certFile.c_str()) < 0) | ||||||
|             { |             { | ||||||
|                 errMsg = "Cannot parse cert file '" + _tlsOptions.certFile + "'"; |                 errMsg = "Cannot parse cert file '" + _tlsOptions.certFile + "'"; | ||||||
|                 return false; |                 return false; | ||||||
|             } |             } | ||||||
|  |             if (mbedtls_pk_parse_keyfile(&_pkey, _tlsOptions.keyFile.c_str(), "") < 0) | ||||||
|  |             { | ||||||
|  |                 errMsg = "Cannot parse key file '" + _tlsOptions.keyFile + "'"; | ||||||
|  |                 return false; | ||||||
|  |             } | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         if (_tlsOptions.isPeerVerifyDisabled()) |         if (_tlsOptions.isPeerVerifyDisabled()) | ||||||
| @@ -84,7 +89,7 @@ namespace ix | |||||||
|         } |         } | ||||||
|         else |         else | ||||||
|         { |         { | ||||||
|             mbedtls_ssl_conf_ca_chain(&_conf, &_cacert, NULL); |             mbedtls_ssl_conf_authmode(&_conf, MBEDTLS_SSL_VERIFY_REQUIRED); | ||||||
|  |  | ||||||
|             // FIXME: should we call mbedtls_ssl_conf_verify ? |             // FIXME: should we call mbedtls_ssl_conf_verify ? | ||||||
|  |  | ||||||
| @@ -97,7 +102,13 @@ namespace ix | |||||||
|                 errMsg = "Cannot parse CA file '" + _tlsOptions.caFile + "'"; |                 errMsg = "Cannot parse CA file '" + _tlsOptions.caFile + "'"; | ||||||
|                 return false; |                 return false; | ||||||
|             } |             } | ||||||
|             mbedtls_ssl_conf_authmode(&_conf, MBEDTLS_SSL_VERIFY_REQUIRED); |  | ||||||
|  |             mbedtls_ssl_conf_ca_chain(&_conf, &_cacert, NULL); | ||||||
|  |  | ||||||
|  |             if (_tlsOptions.hasCertAndKey()) | ||||||
|  |             { | ||||||
|  |                 mbedtls_ssl_conf_own_cert(&_conf, &_cert, &_pkey); | ||||||
|  |             } | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         if (mbedtls_ssl_setup(&_ssl, &_conf) != 0) |         if (mbedtls_ssl_setup(&_ssl, &_conf) != 0) | ||||||
|   | |||||||
| @@ -45,6 +45,7 @@ namespace ix | |||||||
|         mbedtls_ctr_drbg_context _ctr_drbg; |         mbedtls_ctr_drbg_context _ctr_drbg; | ||||||
|         mbedtls_x509_crt _cacert; |         mbedtls_x509_crt _cacert; | ||||||
|         mbedtls_x509_crt _cert; |         mbedtls_x509_crt _cert; | ||||||
|  |         mbedtls_pk_context _pkey; | ||||||
|  |  | ||||||
|         std::mutex _mutex; |         std::mutex _mutex; | ||||||
|         SocketTLSOptions _tlsOptions; |         SocketTLSOptions _tlsOptions; | ||||||
|   | |||||||
| @@ -8,6 +8,7 @@ | |||||||
|  |  | ||||||
| #include <assert.h> | #include <assert.h> | ||||||
| #include <fstream> | #include <fstream> | ||||||
|  | #include <sstream> | ||||||
|  |  | ||||||
| namespace ix | namespace ix | ||||||
| { | { | ||||||
| @@ -71,4 +72,16 @@ namespace ix | |||||||
|     { |     { | ||||||
|         return _errMsg; |         return _errMsg; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     std::string SocketTLSOptions::getDescription() const | ||||||
|  |     { | ||||||
|  |         std::stringstream ss; | ||||||
|  |         ss << "TLS Options:" << std::endl; | ||||||
|  |         ss << "  certFile = " << certFile << std::endl; | ||||||
|  |         ss << "  keyFile  = " << keyFile << std::endl; | ||||||
|  |         ss << "  caFile   = " << caFile << std::endl; | ||||||
|  |         ss << "  ciphers  = " << ciphers << std::endl; | ||||||
|  |         ss << "  ciphers  = " << ciphers << std::endl; | ||||||
|  |         return ss.str(); | ||||||
|  |     } | ||||||
| } // namespace ix | } // namespace ix | ||||||
|   | |||||||
| @@ -43,6 +43,8 @@ namespace ix | |||||||
|  |  | ||||||
|         const std::string& getErrorMsg() const; |         const std::string& getErrorMsg() const; | ||||||
|  |  | ||||||
|  |         std::string getDescription() const; | ||||||
|  |  | ||||||
|     private: |     private: | ||||||
|         mutable std::string _errMsg; |         mutable std::string _errMsg; | ||||||
|         mutable bool _validated = false; |         mutable bool _validated = false; | ||||||
|   | |||||||
| @@ -144,7 +144,9 @@ namespace ix | |||||||
|  |  | ||||||
|         if (!UrlParser::parse(url, protocol, host, path, query, port)) |         if (!UrlParser::parse(url, protocol, host, path, query, port)) | ||||||
|         { |         { | ||||||
|             return WebSocketInitResult(false, 0, std::string("Could not parse URL ") + url); |             std::stringstream ss; | ||||||
|  |             ss << "Could not parse url: '" << url << "'"; | ||||||
|  |             return WebSocketInitResult(false, 0, ss.str()); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         std::string errorMsg; |         std::string errorMsg; | ||||||
|   | |||||||
| @@ -6,4 +6,4 @@ | |||||||
|  |  | ||||||
| #pragma once | #pragma once | ||||||
|  |  | ||||||
| #define IX_WEBSOCKET_VERSION "7.6.1" | #define IX_WEBSOCKET_VERSION "7.6.4" | ||||||
|   | |||||||
| @@ -43,7 +43,6 @@ namespace ix | |||||||
|                        rolesecret, |                        rolesecret, | ||||||
|                        ix::WebSocketPerMessageDeflateOptions(true), |                        ix::WebSocketPerMessageDeflateOptions(true), | ||||||
|                        tlsOptions); |                        tlsOptions); | ||||||
|         conn.connect(); |  | ||||||
|  |  | ||||||
|         // Display incoming messages |         // Display incoming messages | ||||||
|         std::atomic<bool> authenticated(false); |         std::atomic<bool> authenticated(false); | ||||||
| @@ -94,6 +93,8 @@ namespace ix | |||||||
|             } |             } | ||||||
|         }); |         }); | ||||||
|  |  | ||||||
|  |         conn.connect(); | ||||||
|  |  | ||||||
|         while (!authenticated) |         while (!authenticated) | ||||||
|             ; |             ; | ||||||
|         while (!messageAcked) |         while (!messageAcked) | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user