Compare commits
	
		
			10 Commits
		
	
	
		
			7.6.3
			...
			feature/ma
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 84361c16a9 | ||
|  | 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, | ||||||
|   | |||||||
| @@ -130,6 +130,104 @@ namespace | |||||||
|         return errMsg; |         return errMsg; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  | #undef CURL_BUILD_IOS | ||||||
|  |     OSStatus CopyIdentityFromPKCS12File( | ||||||
|  |         const char *cPath, | ||||||
|  |         const char *cPassword, | ||||||
|  |         SecIdentityRef *out_cert_and_key) | ||||||
|  |     { | ||||||
|  |         OSStatus status = errSecItemNotFound; | ||||||
|  |         CFURLRef pkcs_url = CFURLCreateFromFileSystemRepresentation( | ||||||
|  |             NULL, (const UInt8 *)cPath, strlen(cPath), false); | ||||||
|  |         CFStringRef password = cPassword ? CFStringCreateWithCString(NULL, | ||||||
|  |             cPassword, kCFStringEncodingUTF8) : NULL; | ||||||
|  |         CFDataRef pkcs_data = NULL; | ||||||
|  |  | ||||||
|  |         /* We can import P12 files on iOS or OS X 10.7 or later: */ | ||||||
|  |         /* These constants are documented as having first appeared in 10.6 but they | ||||||
|  |            raise linker errors when used on that cat for some reason. */ | ||||||
|  |         if (CFURLCreateDataAndPropertiesFromResource( | ||||||
|  |             NULL, pkcs_url, &pkcs_data, NULL, NULL, &status)) { | ||||||
|  |             CFArrayRef items = NULL; | ||||||
|  |  | ||||||
|  |             /* On iOS SecPKCS12Import will never add the client certificate to the | ||||||
|  |              * Keychain. | ||||||
|  |              * | ||||||
|  |              * It gives us back a SecIdentityRef that we can use directly. */ | ||||||
|  | #if CURL_BUILD_IOS | ||||||
|  |             const void *cKeys[] = {kSecImportExportPassphrase}; | ||||||
|  |             const void *cValues[] = {password}; | ||||||
|  |             CFDictionaryRef options = CFDictionaryCreate(NULL, cKeys, cValues, | ||||||
|  |                     password ? 1L : 0L, NULL, NULL); | ||||||
|  |  | ||||||
|  |             if (options != NULL) { | ||||||
|  |                 status = SecPKCS12Import(pkcs_data, options, &items); | ||||||
|  |                 CFRelease(options); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             /* On macOS SecPKCS12Import will always add the client certificate to | ||||||
|  |              * the Keychain. | ||||||
|  |              * | ||||||
|  |              * As this doesn't match iOS, and apps may not want to see their client | ||||||
|  |              * certificate saved in the the user's keychain, we use SecItemImport | ||||||
|  |              * with a NULL keychain to avoid importing it. | ||||||
|  |              * | ||||||
|  |              * This returns a SecCertificateRef from which we can construct a | ||||||
|  |              * SecIdentityRef. | ||||||
|  |              */ | ||||||
|  | #else | ||||||
|  |             SecItemImportExportKeyParameters keyParams; | ||||||
|  |             SecExternalFormat inputFormat = kSecFormatPKCS12; | ||||||
|  |             SecExternalItemType inputType = kSecItemTypeCertificate; | ||||||
|  |  | ||||||
|  |             memset(&keyParams, 0x00, sizeof(keyParams)); | ||||||
|  |             keyParams.version    = SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION; | ||||||
|  |             keyParams.passphrase = password; | ||||||
|  |  | ||||||
|  |             status = SecItemImport(pkcs_data, NULL, &inputFormat, &inputType, | ||||||
|  |                                    0, &keyParams, NULL, &items); | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  |             /* Extract the SecIdentityRef */ | ||||||
|  |             if (status == errSecSuccess && items && CFArrayGetCount(items)) | ||||||
|  |             { | ||||||
|  |                 CFIndex i, count; | ||||||
|  |                 count = CFArrayGetCount(items); | ||||||
|  |  | ||||||
|  |                 for (i = 0; i < count; i++) | ||||||
|  |                 { | ||||||
|  |                     CFTypeRef item = (CFTypeRef) CFArrayGetValueAtIndex(items, i); | ||||||
|  |                     CFTypeID  itemID = CFGetTypeID(item); | ||||||
|  |  | ||||||
|  |                     if (itemID == CFDictionaryGetTypeID()) | ||||||
|  |                     { | ||||||
|  |                         CFTypeRef identity = (CFTypeRef) CFDictionaryGetValue( | ||||||
|  |                                 (CFDictionaryRef) item, | ||||||
|  |                                 kSecImportItemIdentity); | ||||||
|  |                         CFRetain(identity); | ||||||
|  |                         *out_cert_and_key = (SecIdentityRef) identity; | ||||||
|  |                         break; | ||||||
|  |                     } | ||||||
|  |                     else if (itemID == SecCertificateGetTypeID()) | ||||||
|  |                     { | ||||||
|  |                         status = SecIdentityCreateWithCertificate(NULL, | ||||||
|  |                                 (SecCertificateRef) item, | ||||||
|  |                                 out_cert_and_key); | ||||||
|  |                         break; | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             if (items) CFRelease(items); | ||||||
|  |             CFRelease(pkcs_data); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (password) CFRelease(password); | ||||||
|  |         CFRelease(pkcs_url); | ||||||
|  |         return status; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |  | ||||||
| } // anonymous namespace | } // anonymous namespace | ||||||
|  |  | ||||||
| namespace ix | namespace ix | ||||||
| @@ -153,6 +251,63 @@ namespace ix | |||||||
|         return false; |         return false; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     bool SocketAppleSSL::handleTLSOptions(std::string& errMsg) | ||||||
|  |     { | ||||||
|  |         SecIdentityRef cert_and_key = NULL; | ||||||
|  |  | ||||||
|  |         const char* ssl_cert = _tlsOptions.certFile.c_str(); | ||||||
|  |  | ||||||
|  |         OSStatus err = CopyIdentityFromPKCS12File(ssl_cert, "foobar", &cert_and_key); | ||||||
|  |  | ||||||
|  |         if (err == noErr && cert_and_key) | ||||||
|  |         { | ||||||
|  |             SecCertificateRef cert = NULL; | ||||||
|  |             CFTypeRef certs_c[1]; | ||||||
|  |             CFArrayRef certs; | ||||||
|  |  | ||||||
|  |             err = SecIdentityCopyCertificate(cert_and_key, &cert); | ||||||
|  |  | ||||||
|  |             certs_c[0] = cert_and_key; | ||||||
|  |             certs = CFArrayCreate(NULL, (const void **)certs_c, 1L, | ||||||
|  |                     &kCFTypeArrayCallBacks); | ||||||
|  |             err = SSLSetCertificate(_sslContext, certs); | ||||||
|  |             if (err != noErr) | ||||||
|  |             { | ||||||
|  |                 errMsg = "SSLSetCertificate failed"; | ||||||
|  |                 return false; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         else | ||||||
|  |         { | ||||||
|  |             switch(err) { | ||||||
|  |                 case errSecAuthFailed: case -25264: /* errSecPkcs12VerifyFailure */ | ||||||
|  |                     errMsg = "SSL: Incorrect password for the certificate \"%s\" " | ||||||
|  |                             "and its private key."; // , ssl_cert); | ||||||
|  |                     break; | ||||||
|  |                 case -26275: /* errSecDecode */ case -25257: /* errSecUnknownFormat */ | ||||||
|  |                     errMsg = "SSL: Couldn't make sense of the data in the " | ||||||
|  |                             "certificate \"%s\" and its private key."; | ||||||
|  |                             ; // ssl_cert); | ||||||
|  |                     break; | ||||||
|  |                 case -25260: /* errSecPassphraseRequired */ | ||||||
|  |                     errMsg = "SSL The certificate \"%s\" requires a password."; | ||||||
|  |                             // ssl_cert); | ||||||
|  |                     break; | ||||||
|  |                 case errSecItemNotFound: | ||||||
|  |                     errMsg = "SSL: Can't find the certificate \"%s\" and its private " | ||||||
|  |                             "key in the Keychain."; // , ssl_cert); | ||||||
|  |                     break; | ||||||
|  |                 default: | ||||||
|  |                     errMsg = "SSL: Can't load the certificate \"%s\" and its private " | ||||||
|  |                             "key: OSStatus %d" ; // , ssl_cert, err); | ||||||
|  |                     break; | ||||||
|  |             } | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         return true; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     // No wait support |     // No wait support | ||||||
|     bool SocketAppleSSL::connect(const std::string& host, |     bool SocketAppleSSL::connect(const std::string& host, | ||||||
|                                  int port, |                                  int port, | ||||||
| @@ -173,6 +328,8 @@ namespace ix | |||||||
|             SSLSetProtocolVersionMin(_sslContext, kTLSProtocol12); |             SSLSetProtocolVersionMin(_sslContext, kTLSProtocol12); | ||||||
|             SSLSetPeerDomainName(_sslContext, host.c_str(), host.size()); |             SSLSetPeerDomainName(_sslContext, host.c_str(), host.size()); | ||||||
|  |  | ||||||
|  |             if (!handleTLSOptions(errMsg)) return false; // FIXME not calling close() | ||||||
|  |  | ||||||
|             if (_tlsOptions.isPeerVerifyDisabled()) |             if (_tlsOptions.isPeerVerifyDisabled()) | ||||||
|             { |             { | ||||||
|                 Boolean option(1); |                 Boolean option(1); | ||||||
|   | |||||||
| @@ -34,6 +34,8 @@ namespace ix | |||||||
|         virtual ssize_t recv(void* buffer, size_t length) final; |         virtual ssize_t recv(void* buffer, size_t length) final; | ||||||
|  |  | ||||||
|     private: |     private: | ||||||
|  |         bool handleTLSOptions(std::string& errMsg); | ||||||
|  |  | ||||||
|         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