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
|
||||
*.pyc
|
||||
venv
|
||||
ixsnake/ixsnake/.certs/
|
||||
site/
|
||||
ws/.certs/
|
||||
ws/.srl
|
||||
|
@ -1,5 +1,20 @@
|
||||
# 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
|
||||
|
||||
|
@ -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.
|
||||
|
||||
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.
|
||||
|
||||
@ -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
|
||||
```
|
||||
|
||||
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;
|
||||
|
||||
ix::IXCoreLogger::Log(socketTLSOptions.getDescription().c_str());
|
||||
|
||||
ix::WebSocketPerMessageDeflateOptions webSocketPerMessageDeflateOptions(enablePerMessageDeflate);
|
||||
_cobra_connection.configure(appkey, endpoint,
|
||||
rolename, rolesecret,
|
||||
|
@ -130,6 +130,104 @@ namespace
|
||||
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
|
||||
|
||||
namespace ix
|
||||
@ -153,6 +251,63 @@ namespace ix
|
||||
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
|
||||
bool SocketAppleSSL::connect(const std::string& host,
|
||||
int port,
|
||||
@ -173,6 +328,8 @@ namespace ix
|
||||
SSLSetProtocolVersionMin(_sslContext, kTLSProtocol12);
|
||||
SSLSetPeerDomainName(_sslContext, host.c_str(), host.size());
|
||||
|
||||
if (!handleTLSOptions(errMsg)) return false; // FIXME not calling close()
|
||||
|
||||
if (_tlsOptions.isPeerVerifyDisabled())
|
||||
{
|
||||
Boolean option(1);
|
||||
|
@ -34,6 +34,8 @@ namespace ix
|
||||
virtual ssize_t recv(void* buffer, size_t length) final;
|
||||
|
||||
private:
|
||||
bool handleTLSOptions(std::string& errMsg);
|
||||
|
||||
SSLContextRef _sslContext;
|
||||
mutable std::mutex _mutex; // AppleSSL routines are not thread-safe
|
||||
|
||||
|
@ -71,11 +71,16 @@ namespace ix
|
||||
|
||||
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 + "'";
|
||||
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())
|
||||
@ -84,7 +89,7 @@ namespace ix
|
||||
}
|
||||
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 ?
|
||||
|
||||
@ -97,7 +102,13 @@ namespace ix
|
||||
errMsg = "Cannot parse CA file '" + _tlsOptions.caFile + "'";
|
||||
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)
|
||||
|
@ -45,6 +45,7 @@ namespace ix
|
||||
mbedtls_ctr_drbg_context _ctr_drbg;
|
||||
mbedtls_x509_crt _cacert;
|
||||
mbedtls_x509_crt _cert;
|
||||
mbedtls_pk_context _pkey;
|
||||
|
||||
std::mutex _mutex;
|
||||
SocketTLSOptions _tlsOptions;
|
||||
|
@ -8,6 +8,7 @@
|
||||
|
||||
#include <assert.h>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
|
||||
namespace ix
|
||||
{
|
||||
@ -71,4 +72,16 @@ namespace ix
|
||||
{
|
||||
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
|
||||
|
@ -43,6 +43,8 @@ namespace ix
|
||||
|
||||
const std::string& getErrorMsg() const;
|
||||
|
||||
std::string getDescription() const;
|
||||
|
||||
private:
|
||||
mutable std::string _errMsg;
|
||||
mutable bool _validated = false;
|
||||
|
@ -144,7 +144,9 @@ namespace ix
|
||||
|
||||
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;
|
||||
|
@ -6,4 +6,4 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#define IX_WEBSOCKET_VERSION "7.6.1"
|
||||
#define IX_WEBSOCKET_VERSION "7.6.4"
|
||||
|
@ -43,7 +43,6 @@ namespace ix
|
||||
rolesecret,
|
||||
ix::WebSocketPerMessageDeflateOptions(true),
|
||||
tlsOptions);
|
||||
conn.connect();
|
||||
|
||||
// Display incoming messages
|
||||
std::atomic<bool> authenticated(false);
|
||||
@ -94,6 +93,8 @@ namespace ix
|
||||
}
|
||||
});
|
||||
|
||||
conn.connect();
|
||||
|
||||
while (!authenticated)
|
||||
;
|
||||
while (!messageAcked)
|
||||
|
Reference in New Issue
Block a user