diff --git a/lib/kerberos.js b/lib/kerberos.js index 09f8425f..2768e305 100644 --- a/lib/kerberos.js +++ b/lib/kerberos.js @@ -52,6 +52,7 @@ KerberosClient.prototype.step = defineOperation(KerberosClient.prototype.step, [ * @param {string} challenge The response returned after calling `unwrap` * @param {object} [options] Optional settings * @param {string} [options.user] The user to authorize + * @param {string} [options.protect] Indicates if the wrap should request message confidentiality * @param {function} [callback] * @return {Promise} returns Promise if no callback passed */ diff --git a/src/kerberos.cc b/src/kerberos.cc index 7a760423..064f49b3 100644 --- a/src/kerberos.cc +++ b/src/kerberos.cc @@ -13,8 +13,8 @@ struct InstanceData { }; static constexpr napi_property_attributes writable_and_configurable = - static_cast( - static_cast(napi_writable) | static_cast(napi_configurable)); + static_cast(static_cast(napi_writable) | + static_cast(napi_configurable)); inline String NewMaybeWideString(Env env, const char* str) { return String::New(env, str); @@ -22,13 +22,13 @@ inline String NewMaybeWideString(Env env, const char* str) { #ifdef _WIN32 inline String NewMaybeWideString(Env env, const WCHAR* str) { static_assert(sizeof(std::wstring::value_type) == sizeof(std::u16string::value_type), - "wstring and u16string have the same value type on Windows"); + "wstring and u16string have the same value type on Windows"); std::wstring wstr(str); std::u16string u16(wstr.begin(), wstr.end()); return String::New(env, u16); } #endif -} +} // namespace std::string ToStringWithNonStringAsEmpty(Napi::Value value) { if (!value.IsString()) { @@ -37,19 +37,28 @@ std::string ToStringWithNonStringAsEmpty(Napi::Value value) { return value.As(); } +int KerberosClient::ParseWrapOptionsProtect(const Napi::Object& options) { + if (!options.Has("protect")) return 0; + + if (!options.Get("protect").IsBoolean()) { + throw TypeError::New(options.Env(), "options.protect must be a boolean."); + } + + bool protect = options.Get("protect"); + return protect ? 1 : 0; +} + Function KerberosClient::Init(Napi::Env env) { - return - DefineClass(env, - "KerberosClient", - { - InstanceMethod("step", &KerberosClient::Step, writable_and_configurable), - InstanceMethod("wrap", &KerberosClient::WrapData, writable_and_configurable), - InstanceMethod("unwrap", &KerberosClient::UnwrapData, writable_and_configurable), - InstanceAccessor("username", &KerberosClient::UserNameGetter, nullptr), - InstanceAccessor("response", &KerberosClient::ResponseGetter, nullptr), - InstanceAccessor("responseConf", &KerberosClient::ResponseConfGetter, nullptr), - InstanceAccessor("contextComplete", &KerberosClient::ContextCompleteGetter, nullptr) - }); + return DefineClass( + env, + "KerberosClient", + {InstanceMethod("step", &KerberosClient::Step, writable_and_configurable), + InstanceMethod("wrap", &KerberosClient::WrapData, writable_and_configurable), + InstanceMethod("unwrap", &KerberosClient::UnwrapData, writable_and_configurable), + InstanceAccessor("username", &KerberosClient::UserNameGetter, nullptr), + InstanceAccessor("response", &KerberosClient::ResponseGetter, nullptr), + InstanceAccessor("responseConf", &KerberosClient::ResponseConfGetter, nullptr), + InstanceAccessor("contextComplete", &KerberosClient::ContextCompleteGetter, nullptr)}); } Object KerberosClient::NewInstance(Napi::Env env, std::shared_ptr state) { @@ -60,8 +69,7 @@ Object KerberosClient::NewInstance(Napi::Env env, std::shared_ptr KerberosClient::state() const { return _state; @@ -91,16 +99,14 @@ Value KerberosClient::ContextCompleteGetter(const CallbackInfo& info) { /// KerberosServer Function KerberosServer::Init(Napi::Env env) { - return - DefineClass(env, - "KerberosServer", - { - InstanceMethod("step", &KerberosServer::Step, writable_and_configurable), - InstanceAccessor("username", &KerberosServer::UserNameGetter, nullptr), - InstanceAccessor("response", &KerberosServer::ResponseGetter, nullptr), - InstanceAccessor("targetName", &KerberosServer::TargetNameGetter, nullptr), - InstanceAccessor("contextComplete", &KerberosServer::ContextCompleteGetter, nullptr) - }); + return DefineClass( + env, + "KerberosServer", + {InstanceMethod("step", &KerberosServer::Step, writable_and_configurable), + InstanceAccessor("username", &KerberosServer::UserNameGetter, nullptr), + InstanceAccessor("response", &KerberosServer::ResponseGetter, nullptr), + InstanceAccessor("targetName", &KerberosServer::TargetNameGetter, nullptr), + InstanceAccessor("contextComplete", &KerberosServer::ContextCompleteGetter, nullptr)}); } Object KerberosServer::NewInstance(Napi::Env env, std::shared_ptr state) { @@ -111,8 +117,7 @@ Object KerberosServer::NewInstance(Napi::Env env, std::shared_ptr KerberosServer::state() const { return _state; @@ -156,18 +161,19 @@ void TestMethod(const CallbackInfo& info) { callback = info[3].As(); } - KerberosWorker::Run(callback, "kerberos:TestMethod", [=](KerberosWorker::SetOnFinishedHandler onFinished) { - return onFinished([=](KerberosWorker* worker) { - Napi::Env env = worker->Env(); - if (shouldError) { - worker->Call(std::initializer_list - { Error::New(env).Value(), env.Null() }); - } else { - worker->Call(std::initializer_list - { env.Null(), String::New(env, optionalString) }); - } + KerberosWorker::Run( + callback, "kerberos:TestMethod", [=](KerberosWorker::SetOnFinishedHandler onFinished) { + return onFinished([=](KerberosWorker* worker) { + Napi::Env env = worker->Env(); + if (shouldError) { + worker->Call( + std::initializer_list{Error::New(env).Value(), env.Null()}); + } else { + worker->Call(std::initializer_list{ + env.Null(), String::New(env, optionalString)}); + } + }); }); - }); } static Object Init(Env env, Object exports) { @@ -179,10 +185,8 @@ static Object Init(Env env, Object exports) { Function KerberosServerCtor = KerberosServer::Init(env); exports["KerberosClient"] = KerberosClientCtor; exports["KerberosServer"] = KerberosServerCtor; - env.SetInstanceData(new InstanceData { - Reference::New(KerberosClientCtor, 1), - Reference::New(KerberosServerCtor, 1) - }); + env.SetInstanceData(new InstanceData{Reference::New(KerberosClientCtor, 1), + Reference::New(KerberosServerCtor, 1)}); exports["initializeClient"] = Function::New(env, InitializeClient); exports["initializeServer"] = Function::New(env, InitializeServer); exports["principalDetails"] = Function::New(env, PrincipalDetails); @@ -193,4 +197,4 @@ static Object Init(Env env, Object exports) { NODE_API_MODULE(kerberos, Init) -} +} // namespace node_kerberos diff --git a/src/kerberos.h b/src/kerberos.h index 7f1c2096..6d5b9224 100644 --- a/src/kerberos.h +++ b/src/kerberos.h @@ -55,6 +55,8 @@ class KerberosClient : public Napi::ObjectWrap { void UnwrapData(const Napi::CallbackInfo& info); void WrapData(const Napi::CallbackInfo& info); + int ParseWrapOptionsProtect(const Napi::Object& options); + private: friend class Napi::ObjectWrap; explicit KerberosClient(const Napi::CallbackInfo& info); @@ -71,7 +73,6 @@ void CheckPassword(const Napi::CallbackInfo& info); void TestMethod(const Napi::CallbackInfo& info); std::string ToStringWithNonStringAsEmpty(Napi::Value value); - -} +} // namespace node_kerberos #endif // KERBEROS_NATIVE_EXTENSION_H diff --git a/src/unix/kerberos_gss.cc b/src/unix/kerberos_gss.cc index ddcf39b5..77163290 100644 --- a/src/unix/kerberos_gss.cc +++ b/src/unix/kerberos_gss.cc @@ -16,13 +16,13 @@ #include "kerberos_gss.h" -#include "base64.h" - #include #include #include #include +#include "base64.h" + #if defined(__clang__) #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" @@ -30,15 +30,15 @@ // Conditionally either use linking at compile time or dlopen() to access kerberos functionality #ifndef KERBEROS_USE_RTLD -# define GSS_CALL(x) x -# define GSS_VALUE(x) x -# define KRB5_CALL(x) x -# define COMERR_CALL(x) x +#define GSS_CALL(x) x +#define GSS_VALUE(x) x +#define KRB5_CALL(x) x +#define COMERR_CALL(x) x namespace node_kerberos { bool kerberos_libraries_available(std::string*) { return true; } -} +} // namespace node_kerberos #else #include // RTLD_DEEPBIND ensures that the kerberos system libraries are loaded in a more @@ -46,76 +46,82 @@ bool kerberos_libraries_available(std::string*) { // addon and OpenSSL 3.x can be statically linked into the binary, this will make // the kerberos system library still use symbols from the OpenSSL 1.1 system library // if it was built to link against that (e.g., on Linux distros based on RHEL 8 or 9). -# ifdef RTLD_DEEPBIND +#ifdef RTLD_DEEPBIND constexpr auto KERBEROS_RTLD_FLAGS = (RTLD_NOW | RTLD_DEEPBIND); -# else +#else constexpr auto KERBEROS_RTLD_FLAGS = RTLD_NOW; -# endif +#endif namespace { struct DLOpenHandle { - void* const handle_; - std::string error_; - DLOpenHandle(void* handle, const char* lib): handle_(handle) { - if (!handle_) error_ = std::string("Opening library ") + lib + " failed: " + dlerror(); - } - ~DLOpenHandle() { - if (handle_) dlclose(handle_); - } - DLOpenHandle(const DLOpenHandle&) = delete; - DLOpenHandle& operator=(DLOpenHandle&) = delete; + void* const handle_; + std::string error_; + DLOpenHandle(void* handle, const char* lib) : handle_(handle) { + if (!handle_) + error_ = std::string("Opening library ") + lib + " failed: " + dlerror(); + } + ~DLOpenHandle() { + if (handle_) + dlclose(handle_); + } + DLOpenHandle(const DLOpenHandle&) = delete; + DLOpenHandle& operator=(DLOpenHandle&) = delete; }; -#define DYLIBS(V) \ +#define DYLIBS(V) \ V(gssapi, "libgssapi_krb5.so.2") \ - V(krb5, "libkrb5.so.3") \ + V(krb5, "libkrb5.so.3") \ V(comerr, "libcom_err.so.2") -#define LIBRARY_HANDLE_GETTER(name, lib) \ - static const DLOpenHandle& name ## _handle() { \ - static const DLOpenHandle h{dlopen(lib, KERBEROS_RTLD_FLAGS), lib}; \ - return h; \ - } \ - static void* name ## _symbol(const char* symbol) { \ - void* value = dlsym(name ## _handle().handle_, symbol); \ - if (!value) \ +#define LIBRARY_HANDLE_GETTER(name, lib) \ + static const DLOpenHandle& name##_handle() { \ + static const DLOpenHandle h{dlopen(lib, KERBEROS_RTLD_FLAGS), lib}; \ + return h; \ + } \ + static void* name##_symbol(const char* symbol) { \ + void* value = dlsym(name##_handle().handle_, symbol); \ + if (!value) \ fprintf(stderr, "Unable to look up symbol %s in %s: %s\n", symbol, lib, dlerror()); \ - return value; \ + return value; \ } DYLIBS(LIBRARY_HANDLE_GETTER) -#define GSS_CALL(x) ([&]() { \ - static void* const sym = gssapi_symbol(#x); \ - return reinterpret_cast(sym); \ -})() -#define GSS_VALUE(x) ([&]() { \ - static void* const sym = gssapi_symbol(#x); \ - return *static_cast(sym); \ -})() -#define KRB5_CALL(x) ([&]() { \ - static void* const sym = krb5_symbol(#x); \ - return reinterpret_cast(sym); \ -})() -#define COMERR_CALL(x) ([&]() { \ - static void* const sym = comerr_symbol(#x); \ - return reinterpret_cast(sym); \ -})() -} // anonymous namespace +#define GSS_CALL(x) \ + ([&]() { \ + static void* const sym = gssapi_symbol(#x); \ + return reinterpret_cast(sym); \ + })() +#define GSS_VALUE(x) \ + ([&]() { \ + static void* const sym = gssapi_symbol(#x); \ + return *static_cast(sym); \ + })() +#define KRB5_CALL(x) \ + ([&]() { \ + static void* const sym = krb5_symbol(#x); \ + return reinterpret_cast(sym); \ + })() +#define COMERR_CALL(x) \ + ([&]() { \ + static void* const sym = comerr_symbol(#x); \ + return reinterpret_cast(sym); \ + })() +} // anonymous namespace namespace node_kerberos { bool kerberos_libraries_available(std::string* error) { -#define CHECK_HANDLE(name, lib) \ - { \ - const DLOpenHandle& handle = name ## _handle(); \ - if (!handle.handle_) { \ - *error = handle.error_; \ - return false; \ - } \ +#define CHECK_HANDLE(name, lib) \ + { \ + const DLOpenHandle& handle = name##_handle(); \ + if (!handle.handle_) { \ + *error = handle.error_; \ + return false; \ + } \ } DYLIBS(CHECK_HANDLE) return true; } -} +} // namespace node_kerberos #endif namespace node_kerberos { @@ -133,7 +139,7 @@ gss_result server_principal_details(const char* service, const char* hostname) { char match[1024]; size_t match_len = 0; std::string details; - gss_result result {}; + gss_result result{}; int code; krb5_context kcontext; @@ -199,16 +205,16 @@ gss_result server_principal_details(const char* service, const char* hostname) { } gss_result authenticate_gss_client_init(const char* service, - const char* principal, - long int gss_flags, - gss_server_state* delegatestate, - gss_OID mech_oid, - gss_client_state* state) { + const char* principal, + long int gss_flags, + gss_server_state* delegatestate, + gss_OID mech_oid, + gss_client_state* state) { OM_uint32 maj_stat; OM_uint32 min_stat; gss_buffer_desc name_token = GSS_C_EMPTY_BUFFER; gss_buffer_desc principal_token = GSS_C_EMPTY_BUFFER; - gss_result ret {}; + gss_result ret{}; state->server_name = GSS_C_NO_NAME; state->mech_oid = mech_oid; @@ -222,8 +228,8 @@ gss_result authenticate_gss_client_init(const char* service, name_token.length = strlen(service); name_token.value = (char*)service; - maj_stat = - GSS_CALL(gss_import_name)(&min_stat, &name_token, GSS_VALUE(gss_nt_service_name), &state->server_name); + maj_stat = GSS_CALL(gss_import_name)( + &min_stat, &name_token, GSS_VALUE(gss_nt_service_name), &state->server_name); if (GSS_ERROR(maj_stat)) { ret = gss_error_result(maj_stat, min_stat); @@ -240,20 +246,21 @@ gss_result authenticate_gss_client_init(const char* service, principal_token.length = strlen(principal); principal_token.value = (char*)principal; - maj_stat = GSS_CALL(gss_import_name)(&min_stat, &principal_token, GSS_VALUE(GSS_C_NT_USER_NAME), &name); + maj_stat = GSS_CALL(gss_import_name)( + &min_stat, &principal_token, GSS_VALUE(GSS_C_NT_USER_NAME), &name); if (GSS_ERROR(maj_stat)) { ret = gss_error_result(maj_stat, min_stat); goto end; } maj_stat = GSS_CALL(gss_acquire_cred)(&min_stat, - name, - GSS_C_INDEFINITE, - GSS_C_NO_OID_SET, - GSS_C_INITIATE, - &state->client_creds, - NULL, - NULL); + name, + GSS_C_INDEFINITE, + GSS_C_NO_OID_SET, + GSS_C_INITIATE, + &state->client_creds, + NULL, + NULL); if (GSS_ERROR(maj_stat)) { ret = gss_error_result(maj_stat, min_stat); goto end; @@ -285,13 +292,13 @@ gss_client_state::~gss_client_state() { } gss_result authenticate_gss_client_step(gss_client_state* state, - const char* challenge, - struct gss_channel_bindings_struct* channel_bindings) { + const char* challenge, + struct gss_channel_bindings_struct* channel_bindings) { OM_uint32 maj_stat; OM_uint32 min_stat; gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER; gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER; - gss_result ret {}; + gss_result ret{}; int temp_ret = AUTH_GSS_CONTINUE; // Always clear out the old response @@ -314,18 +321,18 @@ gss_result authenticate_gss_client_step(gss_client_state* state, // Do GSSAPI step maj_stat = GSS_CALL(gss_init_sec_context)(&min_stat, - state->client_creds, - &state->context, - state->server_name, - state->mech_oid, - (OM_uint32)state->gss_flags, - 0, - channel_bindings, - &input_token, - NULL, - &output_token, - NULL, - NULL); + state->client_creds, + &state->context, + state->server_name, + state->mech_oid, + (OM_uint32)state->gss_flags, + 0, + channel_bindings, + &input_token, + NULL, + &output_token, + NULL, + NULL); if ((maj_stat != GSS_S_COMPLETE) && (maj_stat != GSS_S_CONTINUE_NEEDED)) { ret = gss_error_result(maj_stat, min_stat); @@ -402,7 +409,7 @@ gss_result authenticate_gss_client_unwrap(gss_client_state* state, const char* c gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER; gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER; int conf = 0; - gss_result ret {}; + gss_result ret{}; // Always clear out the old response if (state->response != NULL) { @@ -419,7 +426,8 @@ gss_result authenticate_gss_client_unwrap(gss_client_state* state, const char* c } // Do GSSAPI step - maj_stat = GSS_CALL(gss_unwrap)(&min_stat, state->context, &input_token, &output_token, &conf, NULL); + maj_stat = + GSS_CALL(gss_unwrap)(&min_stat, state->context, &input_token, &output_token, &conf, NULL); if (maj_stat != GSS_S_COMPLETE) { ret = gss_error_result(maj_stat, min_stat); @@ -445,16 +453,16 @@ gss_result authenticate_gss_client_unwrap(gss_client_state* state, const char* c } gss_result authenticate_gss_client_wrap(gss_client_state* state, - const char* challenge, - const char* user, - int protect) { + const char* challenge, + const char* user, + int protect) { OM_uint32 maj_stat; OM_uint32 min_stat; gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER; gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER; char buf[4096]; unsigned long buf_size; - gss_result ret {}; + gss_result ret{}; // Always clear out the old response if (state->response != NULL) { @@ -527,7 +535,7 @@ gss_result authenticate_gss_server_init(const char* service, gss_server_state* s OM_uint32 min_stat; size_t service_len; gss_buffer_desc name_token = GSS_C_EMPTY_BUFFER; - gss_result ret {}; + gss_result ret{}; state->context = GSS_C_NO_CONTEXT; state->server_name = GSS_C_NO_NAME; @@ -555,13 +563,13 @@ gss_result authenticate_gss_server_init(const char* service, gss_server_state* s // Get credentials maj_stat = GSS_CALL(gss_acquire_cred)(&min_stat, - state->server_name, - GSS_C_INDEFINITE, - GSS_C_NO_OID_SET, - GSS_C_ACCEPT, - &state->server_creds, - NULL, - NULL); + state->server_name, + GSS_C_INDEFINITE, + GSS_C_NO_OID_SET, + GSS_C_ACCEPT, + &state->server_creds, + NULL, + NULL); if (GSS_ERROR(maj_stat)) { ret = gss_error_result(maj_stat, min_stat); @@ -599,7 +607,7 @@ gss_result authenticate_gss_server_step(gss_server_state* state, const char* cha gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER; gss_name_t target_name = GSS_C_NO_NAME; // int ret = AUTH_GSS_CONTINUE; - gss_result ret {}; + gss_result ret{}; // Always clear out the old response if (state->response != NULL) { @@ -618,16 +626,16 @@ gss_result authenticate_gss_server_step(gss_server_state* state, const char* cha } maj_stat = GSS_CALL(gss_accept_sec_context)(&min_stat, - &state->context, - state->server_creds, - &input_token, - GSS_C_NO_CHANNEL_BINDINGS, - &state->client_name, - NULL, - &output_token, - NULL, - NULL, - &state->client_creds); + &state->context, + state->server_creds, + &input_token, + GSS_C_NO_CHANNEL_BINDINGS, + &state->client_name, + NULL, + &output_token, + NULL, + NULL, + &state->client_creds); if (GSS_ERROR(maj_stat)) { ret = gss_error_result(maj_stat, min_stat); @@ -688,14 +696,14 @@ gss_result authenticate_gss_server_step(gss_server_state* state, const char* cha } gss_result authenticate_user_krb5pwd(const char* user, - const char* pswd, - const char* service, - const char* default_realm) { + const char* pswd, + const char* service, + const char* default_realm) { krb5_context kcontext = NULL; krb5_error_code code; krb5_principal client = NULL; krb5_principal server = NULL; - gss_result result {}; + gss_result result{}; int ret = 0; char* name = NULL; char* p = NULL; @@ -704,7 +712,7 @@ gss_result authenticate_user_krb5pwd(const char* user, krb5_creds creds; krb5_get_init_creds_opt gic_options; krb5_error_code verifyRet; - char *vName = NULL; + char* vName = NULL; code = KRB5_CALL(krb5_init_context)(&kcontext); if (code) { @@ -783,7 +791,7 @@ gss_result authenticate_user_krb5pwd(const char* user, } static gss_result gss_success_result(int ret) { - return { ret, "", "" }; + return {ret, "", ""}; } static gss_result gss_error_result(OM_uint32 err_maj, OM_uint32 err_min) { @@ -808,30 +816,18 @@ static gss_result gss_error_result(OM_uint32 err_maj, OM_uint32 err_min) { } } while (!GSS_ERROR(maj_stat) && msg_ctx != 0); - return { - AUTH_GSS_ERROR, - buf_maj + ": " + buf_min, - "" - }; + return {AUTH_GSS_ERROR, buf_maj + ": " + buf_min, ""}; } static gss_result gss_error_result_with_message(const char* message) { - return { - AUTH_GSS_ERROR, - message, - "" - }; + return {AUTH_GSS_ERROR, message, ""}; } static gss_result gss_error_result_with_message_and_code(const char* message, int code) { - return { - AUTH_GSS_ERROR, - std::string(message) + " (" + std::to_string(code) + ")", - "" - }; + return {AUTH_GSS_ERROR, std::string(message) + " (" + std::to_string(code) + ")", ""}; } -} +} // namespace node_kerberos #if defined(__clang__) #pragma clang diagnostic pop diff --git a/src/unix/kerberos_unix.cc b/src/unix/kerberos_unix.cc index 57a67ae8..8e769d2c 100644 --- a/src/unix/kerberos_unix.cc +++ b/src/unix/kerberos_unix.cc @@ -1,3 +1,4 @@ +#include #include #include "../kerberos.h" @@ -22,27 +23,26 @@ void KerberosClient::Step(const CallbackInfo& info) { std::string challenge = info[0].ToString(); Function callback = info[1].As(); - KerberosWorker::Run(callback, "kerberos:ClientStep", [=](KerberosWorker::SetOnFinishedHandler onFinished) { - gss_result result = - authenticate_gss_client_step(state.get(), challenge.c_str(), NULL); - - return onFinished([=](KerberosWorker* worker) { - Napi::Env env = worker->Env(); - if (result.code == AUTH_GSS_ERROR) { - worker->Call(std::initializer_list - { Error::New(env, result.message).Value(), env.Null() }); - return; - } - - Napi::Value response = env.Null(); - if (state->response != nullptr) { - response = String::New(env, state->response); - } - - worker->Call(std::initializer_list - { env.Null(), response }); + KerberosWorker::Run( + callback, "kerberos:ClientStep", [=](KerberosWorker::SetOnFinishedHandler onFinished) { + gss_result result = authenticate_gss_client_step(state.get(), challenge.c_str(), NULL); + + return onFinished([=](KerberosWorker* worker) { + Napi::Env env = worker->Env(); + if (result.code == AUTH_GSS_ERROR) { + worker->Call(std::initializer_list{ + Error::New(env, result.message).Value(), env.Null()}); + return; + } + + Napi::Value response = env.Null(); + if (state->response != nullptr) { + response = String::New(env, state->response); + } + + worker->Call(std::initializer_list{env.Null(), response}); + }); }); - }); } void KerberosClient::UnwrapData(const CallbackInfo& info) { @@ -50,22 +50,22 @@ void KerberosClient::UnwrapData(const CallbackInfo& info) { std::string challenge = info[0].ToString(); Function callback = info[1].As(); - KerberosWorker::Run(callback, "kerberos:ClientUnwrap", [=](KerberosWorker::SetOnFinishedHandler onFinished) { - gss_result result = - authenticate_gss_client_unwrap(state.get(), challenge.c_str()); - - return onFinished([=](KerberosWorker* worker) { - Napi::Env env = worker->Env(); - if (result.code == AUTH_GSS_ERROR) { - worker->Call(std::initializer_list - { Error::New(env, result.message).Value(), env.Null() }); - return; - } - - worker->Call(std::initializer_list - { env.Null(), String::New(env, state->response) }); + KerberosWorker::Run( + callback, "kerberos:ClientUnwrap", [=](KerberosWorker::SetOnFinishedHandler onFinished) { + gss_result result = authenticate_gss_client_unwrap(state.get(), challenge.c_str()); + + return onFinished([=](KerberosWorker* worker) { + Napi::Env env = worker->Env(); + if (result.code == AUTH_GSS_ERROR) { + worker->Call(std::initializer_list{ + Error::New(env, result.message).Value(), env.Null()}); + return; + } + + worker->Call(std::initializer_list{env.Null(), + String::New(env, state->response)}); + }); }); - }); } void KerberosClient::WrapData(const CallbackInfo& info) { @@ -75,24 +75,25 @@ void KerberosClient::WrapData(const CallbackInfo& info) { Function callback = info[2].As(); std::string user = ToStringWithNonStringAsEmpty(options["user"]); - int protect = 0; // NOTE: this should be an option + int protect = ParseWrapOptionsProtect(options); - KerberosWorker::Run(callback, "kerberos:ClientWrap", [=](KerberosWorker::SetOnFinishedHandler onFinished) { - gss_result result = authenticate_gss_client_wrap( - state.get(), challenge.c_str(), user.c_str(), protect); + KerberosWorker::Run( + callback, "kerberos:ClientWrap", [=](KerberosWorker::SetOnFinishedHandler onFinished) { + gss_result result = + authenticate_gss_client_wrap(state.get(), challenge.c_str(), user.c_str(), protect); - return onFinished([=](KerberosWorker* worker) { - Napi::Env env = worker->Env(); - if (result.code == AUTH_GSS_ERROR) { - worker->Call(std::initializer_list - { Error::New(env, result.message).Value(), env.Null() }); - return; - } + return onFinished([=](KerberosWorker* worker) { + Napi::Env env = worker->Env(); + if (result.code == AUTH_GSS_ERROR) { + worker->Call(std::initializer_list{ + Error::New(env, result.message).Value(), env.Null()}); + return; + } - worker->Call(std::initializer_list - { env.Null(), String::New(env, state->response) }); + worker->Call(std::initializer_list{env.Null(), + String::New(env, state->response)}); + }); }); - }); } /// KerberosServer @@ -101,27 +102,26 @@ void KerberosServer::Step(const CallbackInfo& info) { std::string challenge = info[0].ToString(); Function callback = info[1].As(); - KerberosWorker::Run(callback, "kerberos:ServerStep", [=](KerberosWorker::SetOnFinishedHandler onFinished) { - gss_result result = - authenticate_gss_server_step(state.get(), challenge.c_str()); - - return onFinished([=](KerberosWorker* worker) { - Napi::Env env = worker->Env(); - if (result.code == AUTH_GSS_ERROR) { - worker->Call(std::initializer_list - { Error::New(env, result.message).Value(), env.Null() }); - return; - } - - Napi::Value response = env.Null(); - if (state->response != nullptr) { - response = String::New(env, state->response); - } - - worker->Call(std::initializer_list - { env.Null(), response }); + KerberosWorker::Run( + callback, "kerberos:ServerStep", [=](KerberosWorker::SetOnFinishedHandler onFinished) { + gss_result result = authenticate_gss_server_step(state.get(), challenge.c_str()); + + return onFinished([=](KerberosWorker* worker) { + Napi::Env env = worker->Env(); + if (result.code == AUTH_GSS_ERROR) { + worker->Call(std::initializer_list{ + Error::New(env, result.message).Value(), env.Null()}); + return; + } + + Napi::Value response = env.Null(); + if (state->response != nullptr) { + response = String::New(env, state->response); + } + + worker->Call(std::initializer_list{env.Null(), response}); + }); }); - }); } /// Global Methods @@ -132,7 +132,8 @@ void InitializeClient(const CallbackInfo& info) { std::string principal = ToStringWithNonStringAsEmpty(options["principal"]); Value flags_v = options["flags"]; - uint32_t gss_flags = flags_v.IsNumber() ? flags_v.As().Uint32Value() : GSS_C_MUTUAL_FLAG|GSS_C_SEQUENCE_FLAG; + uint32_t gss_flags = flags_v.IsNumber() ? flags_v.As().Uint32Value() + : GSS_C_MUTUAL_FLAG | GSS_C_SEQUENCE_FLAG; Value mech_oid_v = options["mechOID"]; uint32_t mech_oid_int = mech_oid_v.IsNumber() ? mech_oid_v.As().Uint32Value() : 0; gss_OID mech_oid = GSS_C_NO_OID; @@ -142,46 +143,51 @@ void InitializeClient(const CallbackInfo& info) { mech_oid = &spnego_mech_oid; } - KerberosWorker::Run(callback, "kerberos:InitializeClient", [=](KerberosWorker::SetOnFinishedHandler onFinished) { - auto client_state = std::make_shared(); - gss_result result = authenticate_gss_client_init( - service.c_str(), principal.c_str(), gss_flags, NULL, mech_oid, client_state.get()); - - return onFinished([=](KerberosWorker* worker) { - Napi::Env env = worker->Env(); - if (result.code == AUTH_GSS_ERROR) { - worker->Call(std::initializer_list - { Error::New(env, result.message).Value(), env.Null() }); - return; - } - - worker->Call(std::initializer_list - { env.Null(), KerberosClient::NewInstance(env, std::move(client_state)) }); + KerberosWorker::Run( + callback, + "kerberos:InitializeClient", + [=](KerberosWorker::SetOnFinishedHandler onFinished) { + auto client_state = std::make_shared(); + gss_result result = authenticate_gss_client_init( + service.c_str(), principal.c_str(), gss_flags, NULL, mech_oid, client_state.get()); + + return onFinished([=](KerberosWorker* worker) { + Napi::Env env = worker->Env(); + if (result.code == AUTH_GSS_ERROR) { + worker->Call(std::initializer_list{ + Error::New(env, result.message).Value(), env.Null()}); + return; + } + + worker->Call(std::initializer_list{ + env.Null(), KerberosClient::NewInstance(env, std::move(client_state))}); + }); }); - }); } void InitializeServer(const CallbackInfo& info) { std::string service = info[0].ToString(); Function callback = info[1].As(); - KerberosWorker::Run(callback, "kerberos:InitializeServer", [=](KerberosWorker::SetOnFinishedHandler onFinished) { - auto server_state = std::make_shared(); - gss_result result = - authenticate_gss_server_init(service.c_str(), server_state.get()); - - return onFinished([=](KerberosWorker* worker) { - Napi::Env env = worker->Env(); - if (result.code == AUTH_GSS_ERROR) { - worker->Call(std::initializer_list - { Error::New(env, result.message).Value(), env.Null() }); - return; - } - - worker->Call(std::initializer_list - { env.Null(), KerberosServer::NewInstance(env, std::move(server_state)) }); + KerberosWorker::Run( + callback, + "kerberos:InitializeServer", + [=](KerberosWorker::SetOnFinishedHandler onFinished) { + auto server_state = std::make_shared(); + gss_result result = authenticate_gss_server_init(service.c_str(), server_state.get()); + + return onFinished([=](KerberosWorker* worker) { + Napi::Env env = worker->Env(); + if (result.code == AUTH_GSS_ERROR) { + worker->Call(std::initializer_list{ + Error::New(env, result.message).Value(), env.Null()}); + return; + } + + worker->Call(std::initializer_list{ + env.Null(), KerberosServer::NewInstance(env, std::move(server_state))}); + }); }); - }); } void PrincipalDetails(const CallbackInfo& info) { @@ -189,22 +195,24 @@ void PrincipalDetails(const CallbackInfo& info) { std::string hostname = info[1].ToString(); Function callback = info[2].As(); - KerberosWorker::Run(callback, "kerberos:PrincipalDetails", [=](KerberosWorker::SetOnFinishedHandler onFinished) { - gss_result result = - server_principal_details(service.c_str(), hostname.c_str()); - - return onFinished([=](KerberosWorker* worker) { - Napi::Env env = worker->Env(); - if (result.code == AUTH_GSS_ERROR) { - worker->Call(std::initializer_list - { Error::New(env, result.message).Value(), env.Null() }); - return; - } - - worker->Call(std::initializer_list - { env.Null(), String::New(env, result.data) }); - }); - }); + KerberosWorker::Run(callback, + "kerberos:PrincipalDetails", + [=](KerberosWorker::SetOnFinishedHandler onFinished) { + gss_result result = + server_principal_details(service.c_str(), hostname.c_str()); + + return onFinished([=](KerberosWorker* worker) { + Napi::Env env = worker->Env(); + if (result.code == AUTH_GSS_ERROR) { + worker->Call(std::initializer_list{ + Error::New(env, result.message).Value(), env.Null()}); + return; + } + + worker->Call(std::initializer_list{ + env.Null(), String::New(env, result.data)}); + }); + }); } void CheckPassword(const CallbackInfo& info) { @@ -221,21 +229,21 @@ void CheckPassword(const CallbackInfo& info) { callback = info[4].As(); } - KerberosWorker::Run(callback, "kerberos:CheckPassword", [=](KerberosWorker::SetOnFinishedHandler onFinished) { - gss_result result = authenticate_user_krb5pwd( - username.c_str(), password.c_str(), service.c_str(), defaultRealm.c_str()); - - return onFinished([=](KerberosWorker* worker) { - Napi::Env env = worker->Env(); - if (result.code == AUTH_GSS_ERROR) { - worker->Call(std::initializer_list - { Error::New(env, result.message).Value(), env.Null() }); - } else { - worker->Call(std::initializer_list - { env.Null(), env.Null() }); - } + KerberosWorker::Run( + callback, "kerberos:CheckPassword", [=](KerberosWorker::SetOnFinishedHandler onFinished) { + gss_result result = authenticate_user_krb5pwd( + username.c_str(), password.c_str(), service.c_str(), defaultRealm.c_str()); + + return onFinished([=](KerberosWorker* worker) { + Napi::Env env = worker->Env(); + if (result.code == AUTH_GSS_ERROR) { + worker->Call(std::initializer_list{ + Error::New(env, result.message).Value(), env.Null()}); + } else { + worker->Call(std::initializer_list{env.Null(), env.Null()}); + } + }); }); - }); } -} +} // namespace node_kerberos diff --git a/src/win32/kerberos_win32.cc b/src/win32/kerberos_win32.cc index aa653d43..10cd097c 100644 --- a/src/win32/kerberos_win32.cc +++ b/src/win32/kerberos_win32.cc @@ -9,7 +9,7 @@ using namespace Napi; inline std::wstring ToWStringWithNonStringAsEmpty(Value value) { static_assert(sizeof(std::wstring::value_type) == sizeof(std::u16string::value_type), - "wstring and u16string have the same value type on Windows"); + "wstring and u16string have the same value type on Windows"); if (!value.IsString()) { return std::wstring(); } @@ -32,27 +32,27 @@ void KerberosClient::Step(const CallbackInfo& info) { std::string challenge = info[0].ToString(); Function callback = info[1].As(); - KerberosWorker::Run(callback, "kerberos:ClientStep", [=](KerberosWorker::SetOnFinishedHandler onFinished) { - sspi_result result = - auth_sspi_client_step(state.get(), (SEC_CHAR*)challenge.c_str(), NULL); - - return onFinished([=](KerberosWorker* worker) { - Napi::Env env = worker->Env(); - if (result.code == AUTH_GSS_ERROR) { - worker->Call(std::initializer_list - { Error::New(env, result.message).Value(), env.Null() }); - return; - } - - Napi::Value response = env.Null(); - if (state->response != nullptr) { - response = String::New(env, state->response); - } - - worker->Call(std::initializer_list - { env.Null(), response }); + KerberosWorker::Run( + callback, "kerberos:ClientStep", [=](KerberosWorker::SetOnFinishedHandler onFinished) { + sspi_result result = + auth_sspi_client_step(state.get(), (SEC_CHAR*)challenge.c_str(), NULL); + + return onFinished([=](KerberosWorker* worker) { + Napi::Env env = worker->Env(); + if (result.code == AUTH_GSS_ERROR) { + worker->Call(std::initializer_list{ + Error::New(env, result.message).Value(), env.Null()}); + return; + } + + Napi::Value response = env.Null(); + if (state->response != nullptr) { + response = String::New(env, state->response); + } + + worker->Call(std::initializer_list{env.Null(), response}); + }); }); - }); } void KerberosClient::UnwrapData(const CallbackInfo& info) { @@ -60,22 +60,22 @@ void KerberosClient::UnwrapData(const CallbackInfo& info) { std::string challenge = info[0].ToString(); Function callback = info[1].As(); - KerberosWorker::Run(callback, "kerberos:ClientUnwrap", [=](KerberosWorker::SetOnFinishedHandler onFinished) { - sspi_result result = - auth_sspi_client_unwrap(state.get(), (SEC_CHAR*)challenge.c_str()); - - return onFinished([=](KerberosWorker* worker) { - Napi::Env env = worker->Env(); - if (result.code == AUTH_GSS_ERROR) { - worker->Call(std::initializer_list - { Error::New(env, result.message).Value(), env.Null() }); - return; - } - - worker->Call(std::initializer_list - { env.Null(), String::New(env, state->response) }); + KerberosWorker::Run( + callback, "kerberos:ClientUnwrap", [=](KerberosWorker::SetOnFinishedHandler onFinished) { + sspi_result result = auth_sspi_client_unwrap(state.get(), (SEC_CHAR*)challenge.c_str()); + + return onFinished([=](KerberosWorker* worker) { + Napi::Env env = worker->Env(); + if (result.code == AUTH_GSS_ERROR) { + worker->Call(std::initializer_list{ + Error::New(env, result.message).Value(), env.Null()}); + return; + } + + worker->Call(std::initializer_list{env.Null(), + String::New(env, state->response)}); + }); }); - }); } static bool isStringTooLong(const std::string& str) { @@ -92,29 +92,34 @@ void KerberosClient::WrapData(const CallbackInfo& info) { Object options = info[1].ToObject(); Function callback = info[2].As(); std::string user = ToStringWithNonStringAsEmpty(options["user"]); - int protect = 0; // NOTE: this should be an option + + int protect = ParseWrapOptionsProtect(options); if (isStringTooLong(user)) { throw Error::New(info.Env(), "User name is too long"); } - KerberosWorker::Run(callback, "kerberos:ClientWrap", [=](KerberosWorker::SetOnFinishedHandler onFinished) { - sspi_result result = auth_sspi_client_wrap( - state.get(), (SEC_CHAR*)challenge.c_str(), (SEC_CHAR*)user.c_str(), (ULONG)user.length(), protect); - - return onFinished([=](KerberosWorker* worker) { - Napi::Env env = worker->Env(); - - if (result.code == AUTH_GSS_ERROR) { - worker->Call(std::initializer_list - { Error::New(env, result.message).Value(), env.Null() }); - return; - } - - worker->Call(std::initializer_list - { env.Null(), String::New(env, state->response) }); + KerberosWorker::Run( + callback, "kerberos:ClientWrap", [=](KerberosWorker::SetOnFinishedHandler onFinished) { + sspi_result result = auth_sspi_client_wrap(state.get(), + (SEC_CHAR*)challenge.c_str(), + (SEC_CHAR*)user.c_str(), + (ULONG)user.length(), + protect); + + return onFinished([=](KerberosWorker* worker) { + Napi::Env env = worker->Env(); + + if (result.code == AUTH_GSS_ERROR) { + worker->Call(std::initializer_list{ + Error::New(env, result.message).Value(), env.Null()}); + return; + } + + worker->Call(std::initializer_list{env.Null(), + String::New(env, state->response)}); + }); }); - }); } /// KerberosServer @@ -143,7 +148,8 @@ void InitializeClient(const CallbackInfo& info) { } Value flags_v = options["flags"]; - ULONG gss_flags = flags_v.IsNumber() ? flags_v.As().Uint32Value() : GSS_C_MUTUAL_FLAG|GSS_C_SEQUENCE_FLAG; + ULONG gss_flags = flags_v.IsNumber() ? flags_v.As().Uint32Value() + : GSS_C_MUTUAL_FLAG | GSS_C_SEQUENCE_FLAG; Value mech_oid_v = options["mechOID"]; uint32_t mech_oid_int = mech_oid_v.IsNumber() ? mech_oid_v.As().Uint32Value() : 0; std::wstring mech_oid = GSS_MECH_OID_KRB5_STR; @@ -151,25 +157,34 @@ void InitializeClient(const CallbackInfo& info) { mech_oid = GSS_MECH_OID_SPNEGO_STR; } - KerberosWorker::Run(callback, "kerberos:InitializeClient", [=](KerberosWorker::SetOnFinishedHandler onFinished) { - auto client_state = std::make_shared(); - sspi_result result = auth_sspi_client_init( - (WCHAR*)service.c_str(), gss_flags, (WCHAR*)user.c_str(), (ULONG)user.length(), - (WCHAR*)domain.c_str(), (ULONG)domain.length(), (WCHAR*)password.c_str(), (ULONG)password.length(), - (WCHAR*)mech_oid.c_str(), client_state.get()); - - return onFinished([=](KerberosWorker* worker) { - Napi::Env env = worker->Env(); - if (result.code == AUTH_GSS_ERROR) { - worker->Call(std::initializer_list - { Error::New(env, result.message).Value(), env.Null() }); - return; - } - - worker->Call(std::initializer_list - { env.Null(), KerberosClient::NewInstance(env, std::move(client_state)) }); + KerberosWorker::Run( + callback, + "kerberos:InitializeClient", + [=](KerberosWorker::SetOnFinishedHandler onFinished) { + auto client_state = std::make_shared(); + sspi_result result = auth_sspi_client_init((WCHAR*)service.c_str(), + gss_flags, + (WCHAR*)user.c_str(), + (ULONG)user.length(), + (WCHAR*)domain.c_str(), + (ULONG)domain.length(), + (WCHAR*)password.c_str(), + (ULONG)password.length(), + (WCHAR*)mech_oid.c_str(), + client_state.get()); + + return onFinished([=](KerberosWorker* worker) { + Napi::Env env = worker->Env(); + if (result.code == AUTH_GSS_ERROR) { + worker->Call(std::initializer_list{ + Error::New(env, result.message).Value(), env.Null()}); + return; + } + + worker->Call(std::initializer_list{ + env.Null(), KerberosClient::NewInstance(env, std::move(client_state))}); + }); }); - }); } void InitializeServer(const CallbackInfo& info) { @@ -184,4 +199,4 @@ void CheckPassword(const CallbackInfo& info) { throw Error::New(info.Env(), "`checkPassword` is not implemented yet for windows"); } -} +} // namespace node_kerberos diff --git a/test/kerberos_tests.js b/test/kerberos_tests.js index 18263bff..404211b9 100644 --- a/test/kerberos_tests.js +++ b/test/kerberos_tests.js @@ -4,6 +4,7 @@ const request = require('request'); const chai = require('chai'); const expect = chai.expect; const os = require('os'); +const { test } = require('mocha'); chai.use(require('chai-string')); // environment variables @@ -122,4 +123,50 @@ describe('Kerberos', function () { }); }); }); + + describe('Client.wrap()', function () { + async function establishConext() { + const service = `HTTP@${hostname}`; + client = await kerberos.initializeClient(service, {}); + server = await kerberos.initializeServer(service); + const clientResponse = await client.step(''); + const serverResponse = await server.step(clientResponse); + await client.step(serverResponse); + expect(client.contextComplete).to.be.true; + return { client, server }; + } + + let client; + let server; + + before(establishConext); + describe('options.protect', function () { + context('valid values for `protect`', function () { + test('no options provided', async function () { + await client.wrap('challenge'); + }); + + test('options provided (protect omitted)', async function () { + await client.wrap('challenge', {}); + }); + + test('protect = false', async function () { + await client.wrap('challenge', { protect: false }); + }); + + test('protect = true', async function () { + await client.wrap('challenge', { protect: true }); + }); + }) + + context('when set to an invalid value', function () { + it('successfully wraps a payload', async function () { + const error = await client.wrap('challenge', { protect: 'non-boolean' }).catch(e => e); + expect(error) + .to.be.instanceOf(TypeError) + .to.match(/options.protect must be a boolean/); + }); + }); + }); + }); });