Documentation and comments for ssl_verify_callback.

This commit is contained in:
Morten Krogh 2017-09-22 11:17:59 +02:00
parent 3657ef385c
commit c1e198cbab
3 changed files with 65 additions and 16 deletions

View File

@ -1,3 +1,11 @@
NEXT RELEASE
=============================================================
### Enhancements
* Add a callback function used to verify SSL certificates in
the sync config.
2.0.0-rc10 Release notes (2017-9-19)
=============================================================
### Breaking changes

View File

@ -92,17 +92,17 @@ class Realm {
constructor(config) {}
/**
* Open a realm asynchronously with a promise. If the realm is synced, it will be fully
* Open a realm asynchronously with a promise. If the realm is synced, it will be fully
* synchronized before it is available.
* @param {Realm~Configuration} config
* @param {Realm~Configuration} config
* @returns {ProgressPromise} - a promise that will be resolved with the realm instance when it's available.
*/
static open(config) {}
/**
* Open a realm asynchronously with a callback. If the realm is synced, it will be fully
* Open a realm asynchronously with a callback. If the realm is synced, it will be fully
* synchronized before it is available.
* @param {Realm~Configuration} config
* @param {Realm~Configuration} config
* @param {callback(error, realm)} - will be called when the realm is ready.
* @param {callback(transferred, transferable)} [progressCallback] - an optional callback for download progress notifications
* @throws {Error} If anything in the provided `config` is invalid.
@ -217,7 +217,7 @@ class Realm {
/*
* Replaces all string columns in this Realm with a string enumeration column and compacts the
* database file.
*
*
* Cannot be called from a write transaction.
*
* Compaction will not occur if other `Realm` instances exist.
@ -269,12 +269,12 @@ Realm.defaultPath;
* This function takes two arguments:
* - `oldRealm` - The Realm before migration is performed.
* - `newRealm` - The Realm that uses the latest `schema`, which should be modified as necessary.
* @property {callback(number, number)} [shouldCompactOnLaunch] - The function called when opening
* a Realm for the first time during the life of a process to determine if it should be compacted
* @property {callback(number, number)} [shouldCompactOnLaunch] - The function called when opening
* a Realm for the first time during the life of a process to determine if it should be compacted
* before being returned to the user. The function takes two arguments:
* - `totalSize` - The total file size (data + free space)
* - `totalSize` - The total file size (data + free space)
* - `unusedSize` - The total bytes used by data in the file.
* It returns `true` to indicate that an attempt to compact the file should be made. The compaction
* It returns `true` to indicate that an attempt to compact the file should be made. The compaction
* will be skipped if another process is accessing it.
* @property {string} [path={@link Realm.defaultPath}] - The path to the file where the
* Realm database should be stored.
@ -288,13 +288,44 @@ Realm.defaultPath;
* object types in this Realm. **Required** when first creating a Realm at this `path`.
* @property {number} [schemaVersion] - **Required** (and must be incremented) after
* changing the `schema`.
* @property {Object} [sync] - Sync configuration parameters with the following
* @property {Object} [sync] - Sync configuration parameters with the following
* child properties:
* - `user` - A `User` object obtained by calling `Realm.Sync.User.login`
* - `url` - A `string` which contains a valid Realm Sync url
* - `error` - A callback function which is called in error situations
* - `validate_ssl` - Indicating if SSL certificates must be validated
* - `ssl_trust_certificate_path` - A path where to find trusted SSL certificates
* - `ssl_verify_callback` - A callback function used to accept or reject the server's
* SSL certificate. ssl_verify_callback is called with an object of type
* {
* serverAddress: String
* serverPort: Number
* pemCertificate: String
* preverifyOk: Number
* depth: Number
* }
*
* The return value of ssl_verify_callback decides whether the certificate is accepted (true)
* or rejected (false). The ssl_verify_callback function is only respected on platforms where
* OpenSSL is used for the sync client, e.g. Linux.
*
* When the sync client has received the server's certificate chain, it presents every certificate in
* the chain to the ssl_verify_callback function. The depth argument specifies the position of the
* certificate in the chain. depth = 0 represents the actual server certificate and the root
* certificate has the highest depth. The certificate of highest depth will be presented first.
*
* preverifyOk is 1 if OpenSSL has accepted the certificate, and
* 0 if OpenSSL has rejected it. It is generally safe to return true when preverifyOk is 1. If
* preverifyOk is 0, an independent verification should be made.
*
* One possible way of using the ssl_verify_callback function is to embed the known server certificate
* in the client and accept the presented certificate if and only if it is equal to the known
* certificate.
*
* The purpose of ssl_verify_callback is to enable custom certificate pinning and to solve cases where
* OpenSSL erroneously rejects valid certificates possibly because OpenSSL doesn't have access to the
* proper trust certificates.
*
* The `error` callback can take up to four optional arguments: `message`, `isFatal`, `category`, and `code`.
*/
@ -329,7 +360,7 @@ Realm.defaultPath;
* otherwise specified.
* @property {boolean} [optional] - Signals if this property may be assigned `null` or `undefined`.
* @property {boolean} [indexed] - Signals if this property should be indexed. Only supported for
* `"string"`, `"int"`, and `"bool"` properties.
* `"string"`, `"int"`, and `"bool"` properties.
*/
/**

View File

@ -243,6 +243,9 @@ private:
};
// An object of type SSLVerifyCallbackSyncThreadFunctor is registered with the sync client in order
// to verify SSL certificates. The SSLVerifyCallbackSyncThreadFunctor object's operator() is called
// on the sync client's event loop thread.
template <typename T>
class SSLVerifyCallbackSyncThreadFunctor {
public:
@ -255,6 +258,7 @@ public:
{
}
// This function is called on the sync client's event loop thread.
bool operator ()(const std::string& server_address, sync::Session::port_type server_port, const char* pem_data, size_t pem_size, int preverify_ok, int depth)
{
const std::string pem_certificate {pem_data, pem_size};
@ -264,10 +268,13 @@ public:
m_ssl_certificate_callback_done = false;
}
// Dispatch the call to the main_loop_handler on the node.js thread.
m_event_loop_dispatcher(this, server_address, server_port, pem_certificate, preverify_ok, depth);
bool ssl_certificate_accepted = false;
{
// Wait for the return value of the callback function on the node.js main thread.
// The sync client blocks during this wait.
std::unique_lock<std::mutex> lock(*m_mutex);
m_cond_var->wait(lock, [this] { return this->m_ssl_certificate_callback_done; });
ssl_certificate_accepted = m_ssl_certificate_accepted;
@ -276,6 +283,9 @@ public:
return ssl_certificate_accepted;
}
// main_loop_handler is called on the node.js main thread.
// main_loop_handler calls the user callback (m_func) and sends the return value
// back to the sync client's event loop thread through a condition variable.
static void main_loop_handler(SSLVerifyCallbackSyncThreadFunctor<T>* this_object,
const std::string& server_address,
sync::Session::port_type server_port,
@ -416,7 +426,7 @@ void SessionClass<T>::add_progress_notification(ContextType ctx, FunctionType, O
validate_argument_count(argc, 3);
if (auto session = get_internal<T, SessionClass<T>>(this_object)->lock()) {
std::string direction = Value::validated_to_string(ctx, arguments[0], "direction");
std::string mode = Value::validated_to_string(ctx, arguments[1], "mode");
SyncSession::NotifierType notifierType;
@ -446,20 +456,20 @@ void SessionClass<T>::add_progress_notification(ContextType ctx, FunctionType, O
Protected<FunctionType> protected_callback(ctx, callback_function);
Protected<ObjectType> protected_this(ctx, this_object);
Protected<typename T::GlobalContext> protected_ctx(Context<T>::get_global_context(ctx));
std::function<ProgressHandler> progressFunc;
std::function<ProgressHandler> progressFunc;
EventLoopDispatcher<ProgressHandler> progress_handler([=](uint64_t transferred_bytes, uint64_t transferrable_bytes) {
HANDLESCOPE
ValueType callback_arguments[2];
callback_arguments[0] = Value::from_number(protected_ctx, transferred_bytes);
callback_arguments[1] = Value::from_number(protected_ctx, transferrable_bytes);
Function<T>::callback(protected_ctx, protected_callback, typename T::Object(), 2, callback_arguments);
});
progressFunc = std::move(progress_handler);
auto registrationToken = session->register_progress_notifier(std::move(progressFunc), notifierType, false);
auto syncSession = create_object<T, SessionClass<T>>(ctx, new WeakSession(session));
@ -595,7 +605,7 @@ void SyncClass<T>::populate_sync_config(ContextType ctx, ObjectType realm_constr
static std::regex tilde("/~/");
raw_realm_url = std::regex_replace(raw_realm_url, tilde, "/__auth/");
}
bool client_validate_ssl = true;
ValueType validate_ssl_temp = Object::get_property(ctx, sync_config_object, "validate_ssl");
if (!Value::is_undefined(ctx, validate_ssl_temp)) {