From b97fb1f0b0d1ecdb83250f00c46a15f8c4d2a376 Mon Sep 17 00:00:00 2001 From: gboutry Date: Sat, 19 Feb 2022 20:30:06 +0100 Subject: [PATCH 01/11] feat: remove cpp --- .gitignore | 4 +- binding.gyp | 95 ------ src/gss_bind.cc | 185 ---------- src/gss_bind.h | 7 - src/krb5_bind.cc | 869 ----------------------------------------------- src/krb5_bind.h | 24 -- src/krb5_zos.h | 12 - src/module.cc | 31 -- 8 files changed, 3 insertions(+), 1224 deletions(-) delete mode 100644 binding.gyp delete mode 100644 src/gss_bind.cc delete mode 100644 src/gss_bind.h delete mode 100644 src/krb5_bind.cc delete mode 100644 src/krb5_bind.h delete mode 100644 src/krb5_zos.h delete mode 100644 src/module.cc diff --git a/.gitignore b/.gitignore index bdf9a5b..c8d8a87 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,6 @@ .vscode package-lock.json yarn.lock -.vagrant/ \ No newline at end of file +.vagrant/ +target/ +lib/index.node diff --git a/binding.gyp b/binding.gyp deleted file mode 100644 index 7a1a889..0000000 --- a/binding.gyp +++ /dev/null @@ -1,95 +0,0 @@ -{ - "targets": [{ - "target_name": "krb5", - "sources": [ - "./src/module.cc", - "./src/krb5_bind.cc", - "./src/gss_bind.cc" - ], - 'include_dirs': [" -#include "gss_bind.h" -#ifdef __MVS__ -#include "krb5_zos.h" -#endif - -// https://docs.microsoft.com/en-us/troubleshoot/windows-server/group-policy/group-policy-add-maxtokensize-registry-entry -// Max token size in most recent Microsoft OS is 48 000 before B64 -// the hard limit is set to 65 535 but discouraged because of B64 conversion that is around +30% in size -const size_t MAX_AD_TOKEN_SIZE_BEFORE_B64 = 48000; - -static gss_OID_desc _gss_mech_spnego = {6, (void *)"\x2b\x06\x01\x05\x05\x02"}; //Spnego OID: 1.3.6.1.5.5.2 -static const gss_OID GSS_MECH_SPNEGO = &_gss_mech_spnego; //gss_OID == gss_OID_desc* - -void _convert_gss_error(const OM_uint32 gss_err, const OM_uint32 gss_minor, std::string &error_msg); - -bool exists(const char *path) -{ - struct stat buffer; - return (stat(path, &buffer) == 0); -} - -/** - * generate_spnego_token - */ -class Worker_generate_spnego_token : public Napi::AsyncWorker -{ -public: - Worker_generate_spnego_token(std::string str_server, - std::string gss_name_type, - std::string ccname, - Napi::Function &callback) - : Napi::AsyncWorker(callback), str_server(str_server), - input_name_type(gss_name_type), - krb_ccname(ccname), - error_msg("") - { - } - -private: - OM_uint32 import_name(const char *principal, gss_name_t *desired_name) - { - gss_buffer_desc service; - service.length = strlen(principal); - service.value = (char *)principal; - auto name_type = (input_name_type == "GSS_C_NT_USER_NAME") ? GSS_C_NT_USER_NAME - : GSS_C_NT_HOSTBASED_SERVICE; - return gss_import_name(&gss_minor, &service, name_type, desired_name); - } - - void Execute() - { -#ifdef __MVS__ - __ae_runmode rm(__AE_ASCII_MODE); -#endif - const char *server = str_server.c_str(); - gss_ctx_id_t gss_context = GSS_C_NO_CONTEXT; - gss_buffer_desc input_buf, output_buf; - gss_name_t target_name; - gss_err = import_name(server, &target_name); - if (gss_err) - { - error_msg = "Error while importing name."; - return; - } - - if (krb_ccname != "") - { - // gss_krb5_ccache_name is actually thread safe, - // as discussed in https://krbdev.mit.narkive.com/hOSNjHRA/krb5-get-in-tkt-with-password-gss-init-sec-context - gss_err = gss_krb5_ccache_name(&gss_minor, krb_ccname.c_str(), NULL); - } - - gss_err = gss_init_sec_context(&gss_minor, - GSS_C_NO_CREDENTIAL, // uses ccache specified with gss_krb5_ccache_name or default - &gss_context, - target_name, - GSS_MECH_SPNEGO, - GSS_C_REPLAY_FLAG | GSS_C_SEQUENCE_FLAG, - GSS_C_INDEFINITE, - GSS_C_NO_CHANNEL_BINDINGS, - &input_buf, - NULL, - &output_buf, - NULL, - NULL); - - if (!(GSS_ERROR(gss_err))) - { - if (output_buf.length > MAX_AD_TOKEN_SIZE_BEFORE_B64) - { - this->error_msg += "The token returned by GSS is greater than the size allowed by Windows AD"; - this->spnego_token_length = 0; - this->spnego_token = NULL; - } - else - { - this->spnego_token_length = output_buf.length; - this->spnego_token = new uint8_t[this->spnego_token_length]; - memcpy((void *)this->spnego_token, output_buf.value, output_buf.length); - } - } - else - { - _convert_gss_error(gss_err, gss_minor, this->error_msg); - this->spnego_token_length = 0; - this->spnego_token = NULL; - } - gss_err = gss_delete_sec_context(&gss_minor, &gss_context, &output_buf); - if (GSS_ERROR(gss_err)) - { - _convert_gss_error(gss_err, gss_minor, this->error_msg); - } - } - - void OnOK() - { - Napi::HandleScope scope(Env()); - - Callback().Call({Napi::String::New(Env(), error_msg), - (spnego_token) ? Napi::Buffer::New(Env(), this->spnego_token, this->spnego_token_length, [](Napi::Env env, uint8_t *ref) - { free(ref); }) - : Env().Undefined()}); - } - - // - OM_uint32 gss_err; - OM_uint32 gss_minor; - - // In parameter - std::string str_server; - std::string input_name_type; - std::string krb_ccname; - - // Out parameter - uint8_t *spnego_token; - size_t spnego_token_length; - std::string error_msg; -}; - -void _convert_gss_error(const OM_uint32 gss_err, const OM_uint32 gss_minor, std::string &error_msg) -{ - if (error_msg.length() > 0) - { - error_msg += "\n"; - } - OM_uint32 message_context; - OM_uint32 min_status; - gss_buffer_desc status_string; - message_context = 0; - do - { - gss_display_status(&min_status, - gss_err, - GSS_C_GSS_CODE, - GSS_C_NO_OID, - &message_context, - &status_string); - error_msg += (char *)status_string.value; - gss_release_buffer(&min_status, &status_string); - } while (message_context != 0); - error_msg += " (minor "; - error_msg += std::to_string(gss_minor); - error_msg += ")"; -} - -Napi::Value _generate_spnego_token(const Napi::CallbackInfo &info) -{ - if (info.Length() < 4) - { - throw Napi::TypeError::New(info.Env(), "4 arguments expected"); - } - - std::string server = info[0].As().Utf8Value(); - std::string input_name_type = info[1].As().Utf8Value(); - std::string ccname = info[2].As().Utf8Value(); - Napi::Function callback = info[3].As(); - - Worker_generate_spnego_token *worker = new Worker_generate_spnego_token(server, - input_name_type, - ccname, - callback); - worker->Queue(); - return info.Env().Undefined(); -} diff --git a/src/gss_bind.h b/src/gss_bind.h deleted file mode 100644 index b7a86f7..0000000 --- a/src/gss_bind.h +++ /dev/null @@ -1,7 +0,0 @@ -#include -#include -#include -#include -#include - -Napi::Value _generate_spnego_token(const Napi::CallbackInfo& info); diff --git a/src/krb5_bind.cc b/src/krb5_bind.cc deleted file mode 100644 index 89251f1..0000000 --- a/src/krb5_bind.cc +++ /dev/null @@ -1,869 +0,0 @@ -#include "krb5_bind.h" -#ifdef __MVS__ -#include "krb5_zos.h" -#endif - - -/** - * krb5_build_principal - */ -class Worker_krb5_build_principal : public Napi::AsyncWorker { - public: - Worker_krb5_build_principal(krb5_context ctx, - uint32_t rlen, - std::string realm, - std::string user, - Napi::Function& callback) - : Napi::AsyncWorker(callback), context(ctx), - rlen(rlen), - realm(realm), - user(user) { - } - - private: - void Execute() { -#ifdef __MVS__ - __ae_runmode rm(__AE_ASCII_MODE); -#endif - auto pos = user.find("/"); - if(pos != std::string::npos) { - err = krb5_build_principal(context, - &princ, - rlen, - realm.c_str(), - user.substr(0, pos).c_str(), - user.substr(pos+1, user.length()).c_str(), - NULL); - } - else { - err = krb5_build_principal(context, - &princ, - rlen, - realm.c_str(), - user.c_str(), - NULL); - } - } - - void OnOK() { - Napi::HandleScope scope(Env()); - - Callback().Call({ - Napi::Number::New(Env(), err), - Napi::External::New(Env(), princ) - }); - } - - // In parameters - krb5_context context; - uint32_t rlen; - std::string realm; - std::string user; - - // Out parameters - krb5_error_code err; - krb5_principal princ; -}; - -Napi::Value _krb5_build_principal(const Napi::CallbackInfo& info) { - if (info.Length() < 5) { - throw Napi::TypeError::New(info.Env(), - "5 arguments expected : context, rlen, realm, user, callback"); - } - - krb5_context krb_context = info[0].As>().Data(); - uint32_t rlen = info[1].As().Uint32Value(); - std::string realm = info[2].As().Utf8Value(); - std::string user = info[3].As().Utf8Value(); - Napi::Function callback = info[4].As(); - - Worker_krb5_build_principal* worker = - new Worker_krb5_build_principal(krb_context, - rlen, - realm, - user, - callback); - worker->Queue(); - return info.Env().Undefined(); -} - - -/** - * krb5_cc_close - */ -class Worker_krb5_cc_close : public Napi::AsyncWorker { - public: - Worker_krb5_cc_close(krb5_context ctx, - krb5_ccache ccache, - Napi::Function& callback) - : Napi::AsyncWorker(callback), krb_context(ctx), - krb_ccache(ccache) { - } - - private: - void Execute() { -#ifdef __MVS__ - __ae_runmode rm(__AE_ASCII_MODE); -#endif - err = krb5_cc_close(krb_context, krb_ccache); - } - - void OnOK() { - Napi::HandleScope scope(Env()); - - Callback().Call({ - Napi::Number::New(Env(), err) - }); - } - - // In parameters - krb5_context krb_context; - krb5_ccache krb_ccache; - - // Out parameters - krb5_error_code err; -}; - -Napi::Value _krb5_cc_close(const Napi::CallbackInfo& info) { - if (info.Length() < 3) { - throw Napi::TypeError::New(info.Env(), "3 arguments expected"); - } - - krb5_context krb_context = info[0].As>().Data(); - krb5_ccache krb_ccache = info[1].As>().Data(); - Napi::Function callback = info[2].As(); - - Worker_krb5_cc_close* worker = new Worker_krb5_cc_close(krb_context, - krb_ccache, - callback); - worker->Queue(); - return info.Env().Undefined(); -} - - -/** - * krb5_cc_defaut - */ -class Worker_krb5_cc_default: public Napi::AsyncWorker { - public: - Worker_krb5_cc_default(krb5_context ctx, Napi::Function& callback) - : Napi::AsyncWorker(callback), context(ctx) { - } - - private: - void Execute() { -#ifdef __MVS__ - __ae_runmode rm(__AE_ASCII_MODE); -#endif - err = krb5_cc_default(context, &ccache); - } - - void OnOK() { - Napi::HandleScope scope(Env()); - - Callback().Call({ - Napi::Number::New(Env(), err), - Napi::External::New(Env(), ccache) - }); - } - - // In parameters - krb5_context context; - - // Out parameters - krb5_error_code err; - krb5_ccache ccache; -}; - -Napi::Value _krb5_cc_default(const Napi::CallbackInfo& info) { - if (info.Length() < 2) { - throw Napi::TypeError::New(info.Env(), "2 argument expected"); - } - - krb5_context krb_context = info[0].As>().Data(); - Napi::Function callback = info[1].As(); - - Worker_krb5_cc_default* worker = new Worker_krb5_cc_default(krb_context, callback); - worker->Queue(); - return info.Env().Undefined(); -} - - -/** - * krb5_cc_destroy - */ -class Worker_krb5_cc_destroy : public Napi::AsyncWorker { - public: - Worker_krb5_cc_destroy(krb5_context ctx, - krb5_ccache ccache, - Napi::Function& callback) - : Napi::AsyncWorker(callback), krb_context(ctx), - krb_ccache(ccache) { - } - - private: - void Execute() { -#ifdef __MVS__ - __ae_runmode rm(__AE_ASCII_MODE); -#endif - err = krb5_cc_destroy(krb_context, krb_ccache); - } - - void OnOK() { - Napi::HandleScope scope(Env()); - - Callback().Call({ - Napi::Number::New(Env(), err) - }); - } - - // In parameters - krb5_context krb_context; - krb5_ccache krb_ccache; - - // Out parameters - krb5_error_code err; -}; - -Napi::Value _krb5_cc_destroy(const Napi::CallbackInfo& info) { - if (info.Length() < 3) { - throw Napi::TypeError::New(info.Env(), "3 arguments expected"); - } - - krb5_context krb_context = info[0].As>().Data(); - krb5_ccache krb_ccache = info[1].As>().Data(); - Napi::Function callback = info[2].As(); - - Worker_krb5_cc_destroy* worker = new Worker_krb5_cc_destroy(krb_context, - krb_ccache, - callback); - worker->Queue(); - return info.Env().Undefined(); -} - - -/** - * krb5_cc_get_name_sync - */ -Napi::Value _krb5_cc_get_name_sync(const Napi::CallbackInfo& info) { - if (info.Length() < 2) { - throw Napi::TypeError::New(info.Env(), "2 argument expected"); - } - - krb5_context krb_context = info[0].As>().Data(); - krb5_ccache krb_ccache = info[1].As>().Data(); - - const char* cc_name = krb5_cc_get_name(krb_context, krb_ccache); - - return Napi::String::New(info.Env(), cc_name); -} - - -/** - * krb5_cc_initialize_sync - */ -Napi::Value _krb5_cc_initialize_sync(const Napi::CallbackInfo& info) { - if (info.Length() < 3) { - throw Napi::TypeError::New(info.Env(), "3 argument expected"); - } - - krb5_context krb_context = info[0].As>().Data(); - krb5_ccache krb_ccache = info[1].As>().Data(); - krb5_principal krb_princ = info[2].As>().Data(); - - krb5_error_code err = krb5_cc_initialize(krb_context, krb_ccache, krb_princ); - - return Napi::Number::New(info.Env(), err); -} - - -/** - * krb5_cc_initialize - */ -class Worker_krb5_cc_initialize : public Napi::AsyncWorker { - public: - Worker_krb5_cc_initialize(krb5_context ctx, - krb5_ccache ccache, - krb5_principal princ, - Napi::Function& callback) - : Napi::AsyncWorker(callback), krb_context(ctx), - krb_ccache(ccache), - krb_princ(princ) { - } - - private: - void Execute() { -#ifdef __MVS__ - __ae_runmode rm(__AE_ASCII_MODE); -#endif - err = krb5_cc_initialize(krb_context, krb_ccache, krb_princ); - } - - void OnOK() { - Napi::HandleScope scope(Env()); - - Callback().Call({ - Napi::Number::New(Env(), err) - }); - } - - // In parameter - krb5_context krb_context; - krb5_ccache krb_ccache; - krb5_principal krb_princ; - - // Out parameter - krb5_error_code err; -}; - -Napi::Value _krb5_cc_initialize(const Napi::CallbackInfo& info) { - if (info.Length() < 4) { - throw Napi::TypeError::New(info.Env(), "4 arguments expected"); - } - - krb5_context krb_context = info[0].As>().Data(); - krb5_ccache krb_ccache = info[1].As>().Data(); - krb5_principal krb_princ = info[2].As>().Data(); - Napi::Function callback = info[3].As(); - - Worker_krb5_cc_initialize* worker = new Worker_krb5_cc_initialize(krb_context, - krb_ccache, - krb_princ, - callback); - worker->Queue(); - return info.Env().Undefined(); -} - - - - - - -/** - * krb5_cc_resolve - */ -class Worker_krb5_cc_resolve : public Napi::AsyncWorker { - public: - Worker_krb5_cc_resolve(krb5_context ctx, - std::string name, - Napi::Function& callback) - : Napi::AsyncWorker(callback), krb_context(ctx), - cc_name(name) { - } - - private: - void Execute() { -#ifdef __MVS__ - __ae_runmode rm(__AE_ASCII_MODE); -#endif - err = krb5_cc_resolve(krb_context, cc_name.c_str(), &ccache); - } - - void OnOK() { - Napi::HandleScope scope(Env()); - - Callback().Call({ - Napi::Number::New(Env(), err), - Napi::External::New(Env(), ccache) - }); - - } - - // In parameter - krb5_context krb_context; - std::string cc_name; - - // Out parameter - krb5_error_code err; - krb5_ccache ccache; -}; - -Napi::Value _krb5_cc_resolve(const Napi::CallbackInfo& info) { - if (info.Length() < 3) { - throw Napi::TypeError::New(info.Env(), "3 arguments expected"); - } - - krb5_context krb_context = info[0].As>().Data(); - std::string name = info[1].As().Utf8Value(); - Napi::Function callback = info[2].As(); - - Worker_krb5_cc_resolve* worker = new Worker_krb5_cc_resolve(krb_context, - name, - callback); - worker->Queue(); - return info.Env().Undefined(); -} - - - - - - - - - -/** - * krb5_cc_store_cred - */ -class Worker_krb5_cc_store_cred : public Napi::AsyncWorker { - public: - Worker_krb5_cc_store_cred(krb5_context ctx, - krb5_ccache ccache, - krb5_creds creds, - Napi::Function& callback) - : Napi::AsyncWorker(callback), krb_context(ctx), - krb_ccache(ccache), - krb_creds(creds) { - } - - private: - void Execute() { -#ifdef __MVS__ - __ae_runmode rm(__AE_ASCII_MODE); -#endif - err = krb5_cc_store_cred(krb_context, krb_ccache, &krb_creds); - } - - void OnOK() { - Napi::HandleScope scope(Env()); - - Callback().Call({ - Napi::Number::New(Env(), err) - }); - } - - // In parameter - krb5_context krb_context; - krb5_ccache krb_ccache; - krb5_creds krb_creds; - - // Out parameter - krb5_error_code err; -}; - -Napi::Value _krb5_cc_store_cred(const Napi::CallbackInfo& info) { - if (info.Length() < 4) { - throw Napi::TypeError::New(info.Env(), "4 arguments expected"); - } - - krb5_context krb_context = info[0].As>().Data(); - krb5_ccache krb_ccache = info[1].As>().Data(); - krb5_creds krb_creds = *info[2].As>().Data(); - Napi::Function callback = info[3].As(); - - Worker_krb5_cc_store_cred* worker = new Worker_krb5_cc_store_cred(krb_context, - krb_ccache, - krb_creds, - callback); - worker->Queue(); - return info.Env().Undefined(); -} - - -/** - * krb5_free_context - */ -class Worker_krb5_free_context : public Napi::AsyncWorker { - public: - Worker_krb5_free_context(krb5_context ctx, Napi::Function& callback) - : Napi::AsyncWorker(callback), context(ctx) { - } - - private: - void Execute() { -#ifdef __MVS__ - __ae_runmode rm(__AE_ASCII_MODE); -#endif - krb5_free_context(context); - } - - void OnOK() { - Napi::HandleScope scope(Env()); - - Callback().Call({}); - } - - // In parameters - krb5_context context; -}; - -Napi::Value _krb5_free_context(const Napi::CallbackInfo& info) { - if (info.Length() < 2) { - throw Napi::TypeError::New(info.Env(), "2 arguments expected"); - } - - krb5_context krb_context = info[0].As>().Data(); - Napi::Function callback = info[1].As(); - - Worker_krb5_free_context* worker = new Worker_krb5_free_context(krb_context, callback); - worker->Queue(); - return info.Env().Undefined(); -} - -Napi::Value _krb5_free_context_sync(const Napi::CallbackInfo& info) { - if (info.Length() < 1) { - throw Napi::TypeError::New(info.Env(), "1 arguments expected"); - } - - krb5_context krb_context = info[0].As>().Data(); - krb5_free_context(krb_context); - - return info.Env().Undefined(); -} - - -/** - * krb5_free_principal - */ -Napi::Value _krb5_free_principal_sync(const Napi::CallbackInfo& info) { - if (info.Length() < 2) { - throw Napi::TypeError::New(info.Env(), "2 arguments expected"); - } - - krb5_context krb_context = info[0].As>().Data(); - krb5_principal krb_princ = info[1].As>().Data(); - krb5_free_principal(krb_context, krb_princ); - - return info.Env().Undefined(); -} - -/** - * krb5_free_creds - */ -Napi::Value _krb5_free_creds_sync(const Napi::CallbackInfo& info) { - if (info.Length() < 2) { - throw Napi::TypeError::New(info.Env(), "2 arguments expected"); - } - - krb5_context krb_context = info[0].As>().Data(); - krb5_creds krb_creds = *info[1].As>().Data(); - krb5_free_creds(krb_context, &krb_creds); - - return info.Env().Undefined(); -} - - -/** - * krb5_get_default_realm - */ -class Worker_krb5_get_default_realm : public Napi::AsyncWorker { - public: - Worker_krb5_get_default_realm(krb5_context ctx, Napi::Function& callback) - : Napi::AsyncWorker(callback), context(ctx), realm(nullptr) { - } - - private: - void Execute() { -#ifdef __MVS__ - __ae_runmode rm(__AE_ASCII_MODE); -#endif - err = krb5_get_default_realm(context, &realm); - //pid_t pid = syscall(__NR_gettid); - //std::cout << "get)Thread id : " << pid << std::endl; - } - - void OnOK() { - Napi::HandleScope scope(Env()); - - Callback().Call({ - Napi::Number::New(Env(), err), - Napi::String::New(Env(), realm ? realm : "") - }); - } - - // In parameters - krb5_context context; - - // Out parameters - krb5_error_code err; - char* realm; -}; - -Napi::Value _krb5_get_default_realm(const Napi::CallbackInfo& info) { - if (info.Length() < 2) { - throw Napi::TypeError::New(info.Env(), "2 argument expected"); - } - - krb5_context krb_context = info[0].As>().Data(); - Napi::Function callback = info[1].As(); - - Worker_krb5_get_default_realm* worker = - new Worker_krb5_get_default_realm(krb_context, callback); - worker->Queue(); - return info.Env().Undefined(); -} - - -/** - * krb5_get_error_message_sync - */ -Napi::Value _krb5_get_error_message_sync(const Napi::CallbackInfo& info) { - if (info.Length() < 2) { - throw Napi::TypeError::New(info.Env(), "2 argument expected"); - } - krb5_context krb_context = info[0].As>().Data(); - krb5_error_code err_code = info[1].As(); - - const char* err_msg = krb5_get_error_message(krb_context, err_code); - Napi::String ret_msg = Napi::String::New(info.Env(), err_msg); - krb5_free_error_message(krb_context, err_msg); - - return ret_msg; -} - - -/** - * krb5_get_init_creds_password - */ - -class Worker_krb5_get_init_creds_password : public Napi::AsyncWorker { - public: - Worker_krb5_get_init_creds_password(krb5_context ctx, - krb5_principal princ, - std::string password, - Napi::Function& callback) - : Napi::AsyncWorker(callback), krb_context(ctx), - krb_princ(princ), - krb_password(password) { - } - - private: - void Execute() { -#ifdef __MVS__ - __ae_runmode rm(__AE_ASCII_MODE); -#endif - err = krb5_get_init_creds_password(krb_context, - &creds, - krb_princ, - krb_password.c_str(), - nullptr, - nullptr, - 0, - NULL, - nullptr); - } - - void OnOK() { - Napi::HandleScope scope(Env()); - - Callback().Call({ - Napi::Number::New(Env(), err), - Napi::Buffer::Copy(Env(), &creds, sizeof(creds)) - }); - } - - // In parameter - krb5_context krb_context; - krb5_principal krb_princ; - std::string krb_password; - - // Out parameter - krb5_error_code err; - krb5_creds creds; -}; - -Napi::Value _krb5_get_init_creds_password(const Napi::CallbackInfo& info) { - if (info.Length() < 4) { - throw Napi::TypeError::New(info.Env(), "4 arguments expected"); - } - - krb5_context krb_context = info[0].As>().Data(); - krb5_principal krb_princ = info[1].As>().Data(); - std::string password = info[2].As().Utf8Value(); - Napi::Function callback = info[3].As(); - - Worker_krb5_get_init_creds_password* worker = - new Worker_krb5_get_init_creds_password(krb_context, - krb_princ, - password, - callback); - worker->Queue(); - return info.Env().Undefined(); -} - - -/** - * krb5_init_context - */ -class Worker_krb5_init_context : public Napi::AsyncWorker { - public: - Worker_krb5_init_context(Napi::Function& callback) - : Napi::AsyncWorker(callback) { - } - - private: - void Execute() { -#ifdef __MVS__ - __ae_runmode rm(__AE_ASCII_MODE); -#endif - err = krb5_init_context(&context); - } - - void OnOK() { - Napi::HandleScope scope(Env()); - //pid_t pid = syscall(__NR_gettid); - //std::cout << "cb)Thread id : " << pid << std::endl; - - Callback().Call({ - Napi::Number::New(Env(), err), - Napi::External::New(Env(), context) - }); - } - - // Out parameters - krb5_error_code err; - krb5_context context; -}; - -Napi::Value _krb5_init_context(const Napi::CallbackInfo& info) { - if (info.Length() < 1) { - throw Napi::TypeError::New(info.Env(), "1 argument expected"); - } - if (!info[0].IsFunction()) { - throw Napi::TypeError::New(info.Env(), "Expected callback"); - } - - Napi::Function callback = info[0].As(); - Worker_krb5_init_context* worker = new Worker_krb5_init_context(callback); - worker->Queue(); - return info.Env().Undefined(); -} - - -/** - * krb5_kt_resolve - */ -class Worker_krb5_kt_resolve : public Napi::AsyncWorker { - public: - Worker_krb5_kt_resolve(krb5_context ctx, - std::string kt_name, - Napi::Function& callback) - : Napi::AsyncWorker(callback), krb_context(ctx), - kt_name(kt_name) { - } - - private: - void Execute() { -#ifdef __MVS__ - __ae_runmode rm(__AE_ASCII_MODE); -#endif - err = krb5_kt_resolve(krb_context, kt_name.c_str(), &ktid); - } - - void OnOK() { - Napi::HandleScope scope(Env()); - - Callback().Call({ - Napi::Number::New(Env(), err), - Napi::External::New(Env(), ktid) - }); - } - - - // In parameters - krb5_context krb_context; - std::string kt_name; - - // Out parameters - krb5_error_code err; - krb5_keytab ktid; -}; - -Napi::Value _krb5_kt_resolve(const Napi::CallbackInfo& info) { - if (info.Length() < 3) { - throw Napi::TypeError::New(info.Env(), "3 argument expected"); - } - - krb5_context krb_context = info[0].As>().Data(); - std::string name = info[1].As().Utf8Value(); - Napi::Function callback = info[2].As(); - - Worker_krb5_kt_resolve* worker = - new Worker_krb5_kt_resolve(krb_context, name, callback); - worker->Queue(); - return info.Env().Undefined(); -} - - -/** - * krb5_get_init_creds_keytab - * missing param (nullptr): const char* in_tkt_service - * missing param (nullptr): krb5_get_init_creds_opt * k5_gic_options - */ -class Worker_krb5_get_init_creds_keytab : public Napi::AsyncWorker { - public: - Worker_krb5_get_init_creds_keytab(krb5_context ctx, - krb5_principal princ, - krb5_keytab kt, - krb5_deltat start, - std::string tkt_service, - krb5_get_init_creds_opt * k5_gic_options, - Napi::Function& callback) - : Napi::AsyncWorker(callback), krb_context(ctx), - krb_client(princ), - krb_kt(kt), - start_time(start), - krb_tkt_service(tkt_service), - krb_init_creds_opt(k5_gic_options) { - } - - private: - void Execute() { -#ifdef __MVS__ - __ae_runmode rm(__AE_ASCII_MODE); -#endif - err = krb5_get_init_creds_keytab(krb_context, - &creds, - krb_client, - krb_kt, - start_time, - (krb_tkt_service == "") ? - nullptr : krb_tkt_service.c_str(), - krb_init_creds_opt); - } - - void OnOK() { - Napi::HandleScope scope(Env()); - - Callback().Call({ - Napi::Number::New(Env(), err), - Napi::Buffer::Copy(Env(), &creds, sizeof(creds)) - }); - } - - - // In parameters - krb5_context krb_context; - krb5_principal krb_client; - krb5_keytab krb_kt; - krb5_deltat start_time; - std::string krb_tkt_service; - krb5_get_init_creds_opt* krb_init_creds_opt; - - // Out parameters - krb5_error_code err; - krb5_creds creds; -}; - -Napi::Value _krb5_get_init_creds_keytab(const Napi::CallbackInfo& info) { - if (info.Length() < 3) { - throw Napi::TypeError::New(info.Env(), "5 argument expected"); - } - - krb5_context krb_context = info[0].As>().Data(); - krb5_principal krb_princ = info[1].As>().Data(); - krb5_keytab krb_kt = info[2].As>().Data(); - krb5_deltat start = info[3].As().Int32Value(); - Napi::Function callback = info[4].As(); - - Worker_krb5_get_init_creds_keytab* worker = - new Worker_krb5_get_init_creds_keytab(krb_context, - krb_princ, - krb_kt, - start, - "", // std::string("") becomes nullptr - nullptr, - callback); - worker->Queue(); - return info.Env().Undefined(); -} diff --git a/src/krb5_bind.h b/src/krb5_bind.h deleted file mode 100644 index d1da4f6..0000000 --- a/src/krb5_bind.h +++ /dev/null @@ -1,24 +0,0 @@ -#include -#include -#include -#include - -Napi::Value _krb5_build_principal(const Napi::CallbackInfo& info); -Napi::Value _krb5_cc_close(const Napi::CallbackInfo& info); -Napi::Value _krb5_cc_default(const Napi::CallbackInfo& info); -Napi::Value _krb5_cc_destroy(const Napi::CallbackInfo& info); -Napi::Value _krb5_cc_get_name_sync(const Napi::CallbackInfo& info); -Napi::Value _krb5_cc_initialize(const Napi::CallbackInfo& info); -Napi::Value _krb5_cc_initialize_sync(const Napi::CallbackInfo& info); -Napi::Value _krb5_cc_resolve(const Napi::CallbackInfo& info); -Napi::Value _krb5_cc_store_cred(const Napi::CallbackInfo& info); -Napi::Value _krb5_free_context(const Napi::CallbackInfo& info); -Napi::Value _krb5_free_context_sync(const Napi::CallbackInfo& info); -Napi::Value _krb5_free_creds_sync(const Napi::CallbackInfo& info); -Napi::Value _krb5_free_principal_sync(const Napi::CallbackInfo& info); -Napi::Value _krb5_get_default_realm(const Napi::CallbackInfo& info); -Napi::Value _krb5_get_error_message_sync(const Napi::CallbackInfo& info); -Napi::Value _krb5_get_init_creds_keytab(const Napi::CallbackInfo& info); -Napi::Value _krb5_get_init_creds_password(const Napi::CallbackInfo& info); -Napi::Value _krb5_init_context(const Napi::CallbackInfo& info); -Napi::Value _krb5_kt_resolve(const Napi::CallbackInfo& info); diff --git a/src/krb5_zos.h b/src/krb5_zos.h deleted file mode 100644 index d17350d..0000000 --- a/src/krb5_zos.h +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef KRB5_ZOS_H_ -#define KRB5_ZOS_H_ -#include <_Nascii.h> - -class __ae_runmode { - int mode; - -public: - __ae_runmode(int new_mode) { mode = __ae_thread_swapmode(new_mode); } - ~__ae_runmode() { __ae_thread_swapmode(mode); } -}; -#endif diff --git a/src/module.cc b/src/module.cc deleted file mode 100644 index cd9054b..0000000 --- a/src/module.cc +++ /dev/null @@ -1,31 +0,0 @@ -#include "krb5_bind.h" -#include "gss_bind.h" - - -Napi::Object init(Napi::Env env, Napi::Object exports) { - exports.Set(Napi::String::New(env, "krb5_build_principal"), Napi::Function::New(env, _krb5_build_principal)); - exports.Set(Napi::String::New(env, "krb5_cc_close"), Napi::Function::New(env, _krb5_cc_close)); - exports.Set(Napi::String::New(env, "krb5_cc_default"), Napi::Function::New(env, _krb5_cc_default)); - exports.Set(Napi::String::New(env, "krb5_cc_destroy"), Napi::Function::New(env, _krb5_cc_destroy)); - exports.Set(Napi::String::New(env, "krb5_cc_get_name_sync"), Napi::Function::New(env, _krb5_cc_get_name_sync)); - exports.Set(Napi::String::New(env, "krb5_cc_initialize"), Napi::Function::New(env, _krb5_cc_initialize)); - exports.Set(Napi::String::New(env, "krb5_cc_initialize_sync"), Napi::Function::New(env, _krb5_cc_initialize_sync)); - exports.Set(Napi::String::New(env, "krb5_cc_resolve"), Napi::Function::New(env, _krb5_cc_resolve)); - exports.Set(Napi::String::New(env, "krb5_cc_store_cred"), Napi::Function::New(env, _krb5_cc_store_cred)); - exports.Set(Napi::String::New(env, "krb5_free_context"), Napi::Function::New(env, _krb5_free_context)); - exports.Set(Napi::String::New(env, "krb5_free_context_sync"), Napi::Function::New(env, _krb5_free_context_sync)); - exports.Set(Napi::String::New(env, "krb5_free_creds_sync"), Napi::Function::New(env, _krb5_free_creds_sync)); - exports.Set(Napi::String::New(env, "krb5_free_principal_sync"), Napi::Function::New(env, _krb5_free_principal_sync)); - exports.Set(Napi::String::New(env, "krb5_get_default_realm"), Napi::Function::New(env, _krb5_get_default_realm)); - exports.Set(Napi::String::New(env, "krb5_get_error_message_sync"), Napi::Function::New(env, _krb5_get_error_message_sync)); - exports.Set(Napi::String::New(env, "krb5_get_init_creds_keytab"), Napi::Function::New(env, _krb5_get_init_creds_keytab)); - exports.Set(Napi::String::New(env, "krb5_get_init_creds_password"), Napi::Function::New(env, _krb5_get_init_creds_password)); - exports.Set(Napi::String::New(env, "krb5_init_context"), Napi::Function::New(env, _krb5_init_context)); - exports.Set(Napi::String::New(env, "krb5_kt_resolve"), Napi::Function::New(env, _krb5_kt_resolve)); - - exports.Set(Napi::String::New(env, "generate_spnego_token"), Napi::Function::New(env, _generate_spnego_token)); - - return exports; -}; - -NODE_API_MODULE(NODE_GYP_MODULE_NAME, init); From e28e5c964faf39ba3eac4aeeaf62006bf46d38f1 Mon Sep 17 00:00:00 2001 From: gboutry Date: Sat, 19 Feb 2022 20:30:59 +0100 Subject: [PATCH 02/11] feat: add rust implementation --- Cargo.lock | 625 ++++++++++++++++++++++++++++++ Cargo.toml | 9 + crates/krb5_js/Cargo.toml | 27 ++ crates/krb5_js/build.rs | 4 + crates/krb5_js/src/error.rs | 24 ++ crates/krb5_js/src/lib.rs | 158 ++++++++ crates/krb5_rs/Cargo.toml | 11 + crates/krb5_rs/src/ccache.rs | 86 ++++ crates/krb5_rs/src/context.rs | 97 +++++ crates/krb5_rs/src/credentials.rs | 75 ++++ crates/krb5_rs/src/error.rs | 32 ++ crates/krb5_rs/src/gssapi.rs | 173 +++++++++ crates/krb5_rs/src/keytab.rs | 34 ++ crates/krb5_rs/src/lib.rs | 98 +++++ crates/krb5_rs/src/principal.rs | 61 +++ crates/krb5_sys/Cargo.toml | 18 + crates/krb5_sys/build.rs | 118 ++++++ crates/krb5_sys/src/lib.rs | 5 + crates/krb5_sys/src/wrapper.c | 1 + crates/krb5_sys/src/wrapper.h | 23 ++ lib/index.d.ts | 24 ++ lib/index.js | 261 ++----------- package.json | 25 +- src/index.coffee | 156 +------- 24 files changed, 1761 insertions(+), 384 deletions(-) create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 crates/krb5_js/Cargo.toml create mode 100644 crates/krb5_js/build.rs create mode 100644 crates/krb5_js/src/error.rs create mode 100644 crates/krb5_js/src/lib.rs create mode 100644 crates/krb5_rs/Cargo.toml create mode 100644 crates/krb5_rs/src/ccache.rs create mode 100644 crates/krb5_rs/src/context.rs create mode 100644 crates/krb5_rs/src/credentials.rs create mode 100644 crates/krb5_rs/src/error.rs create mode 100644 crates/krb5_rs/src/gssapi.rs create mode 100644 crates/krb5_rs/src/keytab.rs create mode 100644 crates/krb5_rs/src/lib.rs create mode 100644 crates/krb5_rs/src/principal.rs create mode 100644 crates/krb5_sys/Cargo.toml create mode 100644 crates/krb5_sys/build.rs create mode 100644 crates/krb5_sys/src/lib.rs create mode 100644 crates/krb5_sys/src/wrapper.c create mode 100644 crates/krb5_sys/src/wrapper.h create mode 100644 lib/index.d.ts diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..adb2065 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,625 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aho-corasick" +version = "0.7.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" +dependencies = [ + "memchr", +] + +[[package]] +name = "ansi_term" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" +dependencies = [ + "winapi", +] + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi", +] + +[[package]] +name = "base64" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" + +[[package]] +name = "bindgen" +version = "0.59.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bd2a9a458e8f4304c52c43ebb0cfbd520289f8379a52e329a38afda99bf8eb8" +dependencies = [ + "bitflags", + "cexpr", + "clang-sys", + "clap", + "env_logger", + "lazy_static", + "lazycell", + "log", + "peeking_take_while", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "which", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "cc" +version = "1.0.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" + +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "clang-sys" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cc00842eed744b858222c4c9faf7243aafc6d33f92f96935263ef4d8a41ce21" +dependencies = [ + "glob", + "libc", + "libloading", +] + +[[package]] +name = "clap" +version = "2.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" +dependencies = [ + "ansi_term", + "atty", + "bitflags", + "strsim", + "textwrap", + "unicode-width", + "vec_map", +] + +[[package]] +name = "convert_case" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb4a24b1aaf0fd0ce8b45161144d6f42cd91677fd5940fd431183eb023b3a2b8" + +[[package]] +name = "ctor" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccc0a48a9b826acdf4028595adc9db92caea352f7af011a3034acd172a52a0aa" +dependencies = [ + "quote", + "syn", +] + +[[package]] +name = "either" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" + +[[package]] +name = "env_logger" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b2cf0344971ee6c64c31be0d530793fba457d322dfec2810c453d0ef228f9c3" +dependencies = [ + "atty", + "humantime", + "log", + "regex", + "termcolor", +] + +[[package]] +name = "glob" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + +[[package]] +name = "itoa" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35" + +[[package]] +name = "krb5-js" +version = "0.1.0" +dependencies = [ + "base64", + "krb5-rs", + "lazy_static", + "napi", + "napi-build", + "napi-derive", + "regex", + "serde", + "thiserror", +] + +[[package]] +name = "krb5-rs" +version = "0.1.0" +dependencies = [ + "krb5-sys", + "lazy_static", + "thiserror", +] + +[[package]] +name = "krb5-sys" +version = "0.1.0" +dependencies = [ + "bindgen", + "cc", + "pkg-config", + "winreg", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + +[[package]] +name = "libc" +version = "0.2.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e74d72e0f9b65b5b4ca49a346af3976df0f9c61d550727f349ecd559f251a26c" + +[[package]] +name = "libloading" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "efbc0f03f9a775e9f6aed295c6a1ba2253c5757a9e03d55c6caa46a681abcddd" +dependencies = [ + "cfg-if", + "winapi", +] + +[[package]] +name = "log" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "memchr" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "napi" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17ec66e60f000c78dd7c6215b6fa260e0591e09805024332bc5b3f55acc12244" +dependencies = [ + "ctor", + "lazy_static", + "napi-sys", + "serde", + "serde_json", + "tokio", + "windows", +] + +[[package]] +name = "napi-build" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebd4419172727423cf30351406c54f6cc1b354a2cfb4f1dba3e6cd07f6d5522b" + +[[package]] +name = "napi-derive" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74ac5287a5e94a8728fc82d16c5127acc5eb5b8ad6404ef5f82d6a4ce8d5bdd2" +dependencies = [ + "convert_case", + "napi-derive-backend", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "napi-derive-backend" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "427f4f04525635cdf22005d1be62d6d671bcb5550d694a1efb480a315422b4af" +dependencies = [ + "convert_case", + "once_cell", + "proc-macro2", + "quote", + "regex", + "syn", +] + +[[package]] +name = "napi-sys" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a385494dac3c52cbcacb393bb3b42669e7db8ab240c7ad5115f549eb061f2cc" + +[[package]] +name = "nom" +version = "7.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b1d11e1ef389c76fe5b81bcaf2ea32cf88b62bc494e19f493d0b30e7a930109" +dependencies = [ + "memchr", + "minimal-lexical", + "version_check", +] + +[[package]] +name = "num_cpus" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "once_cell" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da32515d9f6e6e489d7bc9d84c71b060db7247dc035bbe44eac88cf87486d8d5" + +[[package]] +name = "peeking_take_while" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" + +[[package]] +name = "pin-project-lite" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e280fbe77cc62c91527259e9442153f4688736748d24660126286329742b4c6c" + +[[package]] +name = "pkg-config" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58893f751c9b0412871a09abd62ecd2a00298c6c83befa223ef98c52aef40cbe" + +[[package]] +name = "proc-macro2" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "quote" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "864d3e96a899863136fc6e99f3d7cae289dafe43bf2c5ac19b70df7210c0a145" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "regex" +version = "1.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.6.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "ryu" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f" + +[[package]] +name = "serde" +version = "1.0.136" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce31e24b01e1e524df96f1c2fdd054405f8d7376249a5110886fb4b658484789" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.136" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08597e7152fcd306f41838ed3e37be9eaeed2b61c42e2117266a554fab4662f9" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e8d9fa5c3b304765ce1fd9c4c8a3de2c8db365a5b91be52f186efc675681d95" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "shlex" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43b2853a4d09f215c24cc5489c992ce46052d359b5109343cbafbf26bc62f8a3" + +[[package]] +name = "strsim" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" + +[[package]] +name = "syn" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a65b3f4ffa0092e9887669db0eae07941f023991ab58ea44da8fe8e2d511c6b" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "termcolor" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "textwrap" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" +dependencies = [ + "unicode-width", +] + +[[package]] +name = "thiserror" +version = "1.0.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2af73ac49756f3f7c01172e34a23e5d0216f6c32333757c2c61feb2bbff5a5ee" +dependencies = [ + "num_cpus", + "pin-project-lite", +] + +[[package]] +name = "unicode-width" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973" + +[[package]] +name = "unicode-xid" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" + +[[package]] +name = "vec_map" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "which" +version = "4.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a5a7e487e921cf220206864a94a89b6c6905bfc19f1057fa26a4cb360e5c1d2" +dependencies = [ + "either", + "lazy_static", + "libc", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b749ebd2304aa012c5992d11a25d07b406bdbe5f79d371cb7a918ce501a19eb0" +dependencies = [ + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_msvc" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29277a4435d642f775f63c7d1faeb927adba532886ce0287bd985bffb16b6bca" + +[[package]] +name = "windows_i686_gnu" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1145e1989da93956c68d1864f32fb97c8f561a8f89a5125f6a2b7ea75524e4b8" + +[[package]] +name = "windows_i686_msvc" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4a09e3a0d4753b73019db171c1339cd4362c8c44baf1bcea336235e955954a6" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ca64fcb0220d58db4c119e050e7af03c69e6f4f415ef69ec1773d9aab422d5a" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08cabc9f0066848fef4bc6a1c1668e6efce38b661d2aeec75d18d8617eebb5f1" + +[[package]] +name = "winreg" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" +dependencies = [ + "winapi", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..1f5e930 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,9 @@ +[workspace] +members = ["crates/krb5_sys", "crates/krb5_rs", "crates/krb5_js"] +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + + +[profile.release] +lto = true +opt-level = 3 +codegen-units = 1 diff --git a/crates/krb5_js/Cargo.toml b/crates/krb5_js/Cargo.toml new file mode 100644 index 0000000..cb79de4 --- /dev/null +++ b/crates/krb5_js/Cargo.toml @@ -0,0 +1,27 @@ +[package] +name = "krb5-js" +version = "0.1.0" +edition = "2021" + +[lib] +crate-type = ["cdylib"] +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + + +[build-dependencies] +napi-build = "1" + +[dependencies] +krb5-rs = { path = "../krb5_rs" } +thiserror = "1" +napi = { version = "2", default-features = false, features = [ + "async", + "napi6", + "tokio_rt", + "serde-json", +] } +napi-derive = "2" +serde = { version = "1", features = ["derive"] } +base64 = "0.13" +regex = "1" +lazy_static = "1.4" diff --git a/crates/krb5_js/build.rs b/crates/krb5_js/build.rs new file mode 100644 index 0000000..df244ef --- /dev/null +++ b/crates/krb5_js/build.rs @@ -0,0 +1,4 @@ + +fn main() { + napi_build::setup(); +} diff --git a/crates/krb5_js/src/error.rs b/crates/krb5_js/src/error.rs new file mode 100644 index 0000000..64a9822 --- /dev/null +++ b/crates/krb5_js/src/error.rs @@ -0,0 +1,24 @@ +use krb5_rs::Krb5Error; +use thiserror::Error; + +#[derive(Error, Debug)] +pub enum Error { + #[error("{0}")] + Library(Krb5Error), + #[error("Please provide either a password or a keytab")] + MissingPasswordOrKeytab, + #[error("{0}")] + Generic(String), +} + +impl From for Error { + fn from(error: Krb5Error) -> Self { + Error::Library(error) + } +} + +impl From for napi::Error { + fn from(error: Error) -> Self { + napi::Error::from_reason(error.to_string()) + } +} diff --git a/crates/krb5_js/src/lib.rs b/crates/krb5_js/src/lib.rs new file mode 100644 index 0000000..8cec94f --- /dev/null +++ b/crates/krb5_js/src/lib.rs @@ -0,0 +1,158 @@ +use std::path::Path; + +use krb5_rs::{gssapi, CCache, Context, Credentials, Keytab, Principal}; +use lazy_static::lazy_static; +use napi::tokio; +use napi_derive::napi; + +mod error; +use error::Error; +use regex::Regex; + +lazy_static! { + static ref RE: Regex = Regex::new(".*[@]").unwrap(); +} + +#[napi(object)] +#[derive(Debug)] +pub struct KinitParameters { + pub principal: String, + pub password: Option, + pub keytab: Option, + pub realm: Option, + pub ccname: Option, +} + +fn kinit_function(parameters: KinitParameters) -> Result { + let KinitParameters { + principal, + password, + keytab, + realm, + ccname, + } = parameters; + if password.is_none() && keytab.is_none() { + return Err(Error::MissingPasswordOrKeytab); + } + + let (principal, realm_from_split) = if principal.contains('@') { + let splits: Vec<&str> = principal.split('@').collect(); + if splits.len() > 2 { + return Err(Error::Generic(String::from( + "Principal can contain at most one `@`", + ))); + } + (splits[0], Some(splits[1])) + } else { + (principal.as_str(), None) + }; + let context = Context::new()?; + let realm = match realm_from_split.or_else(|| realm.as_deref()) { + Some(realm) => realm.to_owned(), + None => context.get_default_realm()?, + }; + let principal = Principal::build_principal(&context, realm.as_str(), principal)?; + let ccache = match ccname { + Some(ref cc_name) => CCache::resolve(&context, cc_name)?, + None => CCache::default(&context)?, + }; + let credentials = match keytab { + Some(ref keytab) => { + let keytab = Keytab::resolve(&context, keytab)?; + Credentials::get_init_credentials_keytab(&context, &principal, &keytab)? + } + None => { + Credentials::get_init_credentials_password(&context, &principal, &password.unwrap())? + } + }; + let ccache_path = ccache.name()?; + if !Path::new(&ccache_path).exists() { + ccache.initialize(&principal)?; + } + ccache.store_creds(credentials)?; + + Ok(ccache_path) +} + +#[napi(object)] +#[derive(Debug)] +pub struct KdestroyParameters { + pub ccname: Option, +} + +fn kdestroy_function(options: KdestroyParameters) -> std::result::Result<(), Error> { + let context = Context::new()?; + let ccache = match options.ccname { + Some(ref cc_name) => CCache::resolve(&context, cc_name)?, + None => CCache::default(&context)?, + }; + ccache.destroy()?; + + Ok(()) +} + +#[napi(object)] +#[derive(Debug)] +pub struct GenerateSpnegoTokenParameters { + #[napi(js_name = "service_principal")] + pub service_principal: Option, + #[napi(js_name = "service_fqdn")] + pub service_fqdn: Option, + #[napi(js_name = "hostbased_service")] + pub hostbased_service: Option, + pub ccname: Option, +} + +fn generate_spnego_token_function( + options: GenerateSpnegoTokenParameters, +) -> std::result::Result { + let GenerateSpnegoTokenParameters { + service_principal, + service_fqdn, + hostbased_service, + ccname, + } = options; + + let (service_principal, input_name_type) = if let Some(service) = service_principal { + (service, String::from("GSS_C_NT_USER_NAME")) + } else { + let mut service = service_fqdn + .or(hostbased_service) + .ok_or_else(|| String::from("Invalid service principal in input"))?; + if !RE.is_match(&service) { + service.insert_str(0, "HTTP@"); + } + (service, String::from("GSS_C_NT_HOSTBASED_SERVICE")) + }; + + let target_name = gssapi::import_name(&service_principal, &input_name_type)?; + + if ccname.is_some() { + gssapi::krb5_ccache_name(&ccname.unwrap())?; + } + let token = gssapi::get_token(target_name)?; + Ok(base64::encode(token)) +} + +#[napi] +pub async fn kinit(options: KinitParameters) -> napi::Result { + tokio::task::spawn(async move { kinit_function(options).map_err(napi::Error::from) }) + .await + .unwrap() +} + +#[napi] +pub async fn kdestroy(options: KdestroyParameters) -> napi::Result<()> { + tokio::task::spawn(async move { kdestroy_function(options).map_err(napi::Error::from) }) + .await + .unwrap() +} + +#[napi] +pub async fn spnego(options: GenerateSpnegoTokenParameters) -> napi::Result { + tokio::task::spawn(async move { + generate_spnego_token_function(options).map_err(napi::Error::from_reason) + }) + .await + .unwrap() +} diff --git a/crates/krb5_rs/Cargo.toml b/crates/krb5_rs/Cargo.toml new file mode 100644 index 0000000..e08f7a4 --- /dev/null +++ b/crates/krb5_rs/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "krb5-rs" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +lazy_static = "1.4" +thiserror = "1" +krb5-sys = {path = "../krb5_sys"} diff --git a/crates/krb5_rs/src/ccache.rs b/crates/krb5_rs/src/ccache.rs new file mode 100644 index 0000000..28bca83 --- /dev/null +++ b/crates/krb5_rs/src/ccache.rs @@ -0,0 +1,86 @@ +use std::{ + ffi::{CStr, CString}, + mem::MaybeUninit, +}; + +use krb5_sys::{ + krb5_cc_close, krb5_cc_default, krb5_cc_destroy, krb5_cc_get_name, krb5_cc_initialize, + krb5_cc_resolve, krb5_cc_store_cred, krb5_ccache, +}; + +use super::{Context, Credentials, Krb5Error, Principal, Result}; + +pub struct CCache<'a> { + pub(crate) context: &'a Context, + pub(crate) inner: krb5_ccache, +} + +impl<'a> CCache<'a> { + pub fn default(context: &'a Context) -> Result { + let mut ccache: MaybeUninit = MaybeUninit::uninit(); + let error_code = unsafe { krb5_cc_default(context.inner, ccache.as_mut_ptr()) }; + Krb5Error::exit_if_library_error(context, error_code)?; + + Ok(CCache { + context, + inner: unsafe { ccache.assume_init() }, + }) + } + + pub fn resolve(context: &'a Context, cc_name: &str) -> Result> { + let mut ccache: MaybeUninit = MaybeUninit::uninit(); + + let cc_name = CString::new(cc_name).unwrap(); + + let error_code = + unsafe { krb5_cc_resolve(context.inner, cc_name.as_ptr(), ccache.as_mut_ptr()) }; + Krb5Error::exit_if_library_error(context, error_code)?; + Ok(CCache { + context, + inner: unsafe { ccache.assume_init() }, + }) + } + + pub fn destroy(mut self) -> Result<()> { + if !self.context.inner.is_null() && !self.inner.is_null() { + let error_code = unsafe { krb5_cc_destroy(self.context.inner, self.inner) }; + self.inner = std::ptr::null_mut() as *mut _; + Krb5Error::exit_if_library_error(self.context, error_code)?; + } + Ok(()) + } + + pub fn initialize(&self, principal: &Principal) -> Result<()> { + let error_code = + unsafe { krb5_cc_initialize(self.context.inner, self.inner, principal.inner) }; + Krb5Error::exit_if_library_error(self.context, error_code)?; + + Ok(()) + } + + pub fn store_creds(&self, mut credentials: Credentials) -> Result<()> { + let error_code = + unsafe { krb5_cc_store_cred(self.context.inner, self.inner, &mut credentials.inner) }; + Krb5Error::exit_if_library_error(self.context, error_code)?; + Ok(()) + } + + pub fn name(&self) -> Result { + let ccache_name = unsafe { krb5_cc_get_name(self.context.inner, self.inner) }; + unsafe { + let cstr = CStr::from_ptr(ccache_name); + match cstr.to_owned().into_string() { + Ok(ccache_name) => Ok(ccache_name), + Err(_) => Err(Krb5Error::StringConversionError), + } + } + } +} + +impl<'a> Drop for CCache<'a> { + fn drop(&mut self) { + if !self.context.inner.is_null() && !self.inner.is_null() { + unsafe { krb5_cc_close(self.context.inner, self.inner) }; + } + } +} diff --git a/crates/krb5_rs/src/context.rs b/crates/krb5_rs/src/context.rs new file mode 100644 index 0000000..75d5126 --- /dev/null +++ b/crates/krb5_rs/src/context.rs @@ -0,0 +1,97 @@ +// use std::ops::{Deref, DerefMut}; +use std::{ffi::CStr, mem::MaybeUninit, os::raw::c_char, sync::Mutex}; + +use crate::Result; +use krb5_sys::{ + krb5_context, krb5_error_code, krb5_free_context, krb5_free_default_realm, + krb5_free_error_message, krb5_get_default_realm, krb5_get_error_message, krb5_init_context, +}; + +use lazy_static::lazy_static; + +use super::Krb5Error; + +lazy_static! { + static ref CONTEXT_LOCK: Mutex<()> = Mutex::new(()); +} + +pub struct Context { + pub(crate) inner: krb5_context, +} + +// impl Deref for Context { +// type Target = krb5_context; + +// fn deref(&self) -> &Self::Target { +// &self.inner +// } +// } + +// impl DerefMut for Context { +// fn deref_mut(&mut self) -> &mut Self::Target { +// &mut self.inner +// } +// } + +impl Context { + pub fn get_default_realm(&self) -> Result { + let mut default_realm: MaybeUninit<*mut c_char> = MaybeUninit::zeroed(); + let error_code = unsafe { krb5_get_default_realm(self.inner, default_realm.as_mut_ptr()) }; + let default_realm = unsafe { default_realm.assume_init() }; + if error_code != 0 { + unsafe { krb5_free_default_realm(self.inner, default_realm) } + return Err(Krb5Error::from_library_error(self, error_code)); + } + + let result = unsafe { + match CStr::from_ptr(default_realm).to_owned().into_string() { + Ok(string) => Ok(string), + Err(_) => Err(Krb5Error::StringConversionError), + } + }; + unsafe { krb5_free_default_realm(self.inner, default_realm) } + result + } + + pub(crate) fn get_error_message(&self, error_code: krb5_error_code) -> String { + let c_str = unsafe { krb5_get_error_message(self.inner, error_code) }; + + if c_str.is_null() { + unsafe { + krb5_free_error_message(self.inner, c_str); + } + return String::from("krb5 error message is null"); + } + let result = unsafe { + match CStr::from_ptr(c_str).to_owned().into_string() { + Ok(string) => string, + Err(_) => String::from("Failed to convert CString to String"), + } + }; + unsafe { krb5_free_error_message(self.inner, c_str) }; + result + } + + pub fn new() -> Result { + let _guard = CONTEXT_LOCK.lock().expect("Failed to lock context"); + let mut krb5_context: MaybeUninit = MaybeUninit::uninit(); + let error_code = unsafe { krb5_init_context(krb5_context.as_mut_ptr()) }; + if error_code != 0 { + return Err(Krb5Error::ContextInitializationError); + } + + let krb5_context = Context { + inner: unsafe { krb5_context.assume_init() }, + }; + Ok(krb5_context) + } +} + +impl Drop for Context { + fn drop(&mut self) { + let _guard = CONTEXT_LOCK.lock().expect("Failed to lock context"); + if !self.inner.is_null() { + unsafe { krb5_free_context(self.inner) } + } + } +} diff --git a/crates/krb5_rs/src/credentials.rs b/crates/krb5_rs/src/credentials.rs new file mode 100644 index 0000000..8c5d8d8 --- /dev/null +++ b/crates/krb5_rs/src/credentials.rs @@ -0,0 +1,75 @@ +use std::{ffi::CString, mem::MaybeUninit}; + +use krb5_sys::{ + krb5_creds, krb5_free_cred_contents, krb5_get_init_creds_keytab, krb5_get_init_creds_password, +}; + +use crate::{Context, Keytab, Krb5Error, Principal, Result}; + +pub struct Credentials<'a> { + pub(crate) context: &'a Context, + pub(crate) inner: krb5_creds, +} + +impl<'a> Credentials<'a> { + pub fn get_init_credentials_password( + context: &'a Context, + principal: &Principal, + password: &str, + ) -> Result> { + let mut credentials: MaybeUninit = MaybeUninit::uninit(); + let password = CString::new(password).map_err(|_err| Krb5Error::StringConversionError)?; + let error_code = unsafe { + krb5_get_init_creds_password( + context.inner, + credentials.as_mut_ptr(), + principal.inner, + password.as_ptr(), + None, + std::ptr::null_mut() as *mut _, + 0, + std::ptr::null() as *const _, + std::ptr::null_mut() as *mut _, + ) + }; + Krb5Error::exit_if_library_error(context, error_code)?; + + Ok(Credentials { + context, + inner: unsafe { credentials.assume_init() }, + }) + } + + pub fn get_init_credentials_keytab( + context: &'a Context, + principal: &Principal, + keytab: &Keytab, + ) -> Result> { + let mut credentials: MaybeUninit = MaybeUninit::uninit(); + let error_code = unsafe { + krb5_get_init_creds_keytab( + context.inner, + credentials.as_mut_ptr(), + principal.inner, + keytab.inner, + 0, + std::ptr::null_mut() as *mut _, + std::ptr::null_mut() as *mut _, + ) + }; + Krb5Error::exit_if_library_error(context, error_code)?; + + Ok(Credentials { + context, + inner: unsafe { credentials.assume_init() }, + }) + } +} + +impl<'a> Drop for Credentials<'a> { + fn drop(&mut self) { + if !self.context.inner.is_null() { + unsafe { krb5_free_cred_contents(self.context.inner, &mut self.inner) } + } + } +} diff --git a/crates/krb5_rs/src/error.rs b/crates/krb5_rs/src/error.rs new file mode 100644 index 0000000..a4db7c2 --- /dev/null +++ b/crates/krb5_rs/src/error.rs @@ -0,0 +1,32 @@ +use super::Context; +use krb5_sys::krb5_error_code; + +use thiserror::Error; + +pub type Result = std::result::Result; + +#[derive(Error, Debug)] +pub enum Krb5Error { + #[error("{0}")] + LibraryError(String), + #[error("Failed to convert string returned from libkr5")] + StringConversionError, + #[error("Invalid principal")] + InvalidPrincipal, + #[error("Failed to initialize context")] + ContextInitializationError +} + +impl Krb5Error { + pub fn from_library_error(context: &Context, error_code: krb5_error_code) -> Krb5Error { + Krb5Error::LibraryError(context.get_error_message(error_code)) + } + + pub(crate) fn exit_if_library_error(context: &Context, error_code: krb5_error_code) -> Result<()> { + if error_code != 0 { + Err(Krb5Error::from_library_error(context, error_code)) + } else { + Ok(()) + } + } +} diff --git a/crates/krb5_rs/src/gssapi.rs b/crates/krb5_rs/src/gssapi.rs new file mode 100644 index 0000000..49bd978 --- /dev/null +++ b/crates/krb5_rs/src/gssapi.rs @@ -0,0 +1,173 @@ +use std::{ + ffi::{c_void, CString}, + mem::MaybeUninit, + slice, +}; + +use krb5_sys::{ + gss_OID, gss_OID_desc, gss_buffer_desc, gss_buffer_desc_struct, gss_delete_sec_context, + gss_display_status, gss_error, gss_import_name, gss_init_sec_context, gss_int32, + gss_krb5_ccache_name, gss_name_t, gss_release_buffer, gss_release_name, OM_uint32, + GSS_C_NO_CHANNEL_BINDINGS, GSS_C_NO_CONTEXT, GSS_C_NO_CREDENTIAL, GSS_C_NT_HOSTBASED_SERVICE, + GSS_C_NT_USER_NAME, +}; + +const MAX_AD_TOKEN_SIZE_BEFORE_B64: u64 = 48000; + +const GSS_C_REPLAY_FLAG: OM_uint32 = 4; +const GSS_C_SEQUENCE_FLAG: OM_uint32 = 8; +const GSS_C_INDEFINITE: OM_uint32 = 0xffffffff; +const GSS_C_GSS_CODE: gss_int32 = 1; + +pub struct Name(gss_name_t); + +impl Drop for Name { + fn drop(&mut self) { + if !self.0.is_null() { + unsafe { + gss_release_name(std::ptr::null_mut(), &mut self.0); + } + } + } +} + +pub fn import_name(principal: &str, input_name_type: &str) -> Result { + let mut minor = 0; + let mut service = gss_buffer_desc { + length: principal.len() as u64, + value: principal.as_ptr() as *mut _, + }; + let gss_oid = unsafe { + if input_name_type == "GSS_C_NT_USER_NAME" { + GSS_C_NT_USER_NAME + } else { + GSS_C_NT_HOSTBASED_SERVICE + } + }; + let mut desired_name: MaybeUninit = MaybeUninit::uninit(); + let error_code = + unsafe { gss_import_name(&mut minor, &mut service, gss_oid, desired_name.as_mut_ptr()) }; + if unsafe { gss_error(error_code) } != 0 { + return Err(convert_gss_error(error_code, minor)); + } + + let desired_name = unsafe { desired_name.assume_init() }; + + Ok(Name(desired_name)) +} + +pub fn krb5_ccache_name(ccache_name: &str) -> Result<(), String> { + let mut minor = 0; + let ccache_name = + CString::new(ccache_name).map_err(|_err| "Failed to convert ccache name to a c string")?; + + let error_code = + unsafe { gss_krb5_ccache_name(&mut minor, ccache_name.as_ptr(), std::ptr::null_mut()) }; + if unsafe { gss_error(error_code) } != 0 { + return Err(convert_gss_error(error_code, minor)); + } + Ok(()) +} + +pub fn get_token(target_name: Name) -> Result, String> { + let mut minor: OM_uint32 = 0; + let mut gss_context = unsafe { GSS_C_NO_CONTEXT }; + let mut gss_mech_spnego = gss_OID_desc { + length: 6, + elements: (*b"\x2b\x06\x01\x05\x05\x02").as_ptr() as *mut c_void, + }; + let mut input_token: MaybeUninit = MaybeUninit::uninit(); + let mut output_token: MaybeUninit = MaybeUninit::uninit(); + let error_code = unsafe { + gss_init_sec_context( + &mut minor, + GSS_C_NO_CREDENTIAL, // uses ccache specified with gss_krb5_ccache_name or default + &mut gss_context, + target_name.0, + &mut gss_mech_spnego, + GSS_C_REPLAY_FLAG | GSS_C_SEQUENCE_FLAG, + GSS_C_INDEFINITE, + GSS_C_NO_CHANNEL_BINDINGS, + input_token.as_mut_ptr(), // make null + std::ptr::null_mut(), + output_token.as_mut_ptr(), + std::ptr::null_mut(), + std::ptr::null_mut(), + ) + }; + let res = if unsafe { gss_error(error_code) } != 0 { + Err(convert_gss_error(error_code, minor)) + } else { + let output_token = unsafe { output_token.assume_init() }; + if output_token.length > MAX_AD_TOKEN_SIZE_BEFORE_B64 { + Err(String::from( + "The token returned by GSS is greater than the size allowed by Windows AD", + )) + } else { + // try_into().unwrap() won't fail, because output_token.length cannot be higher than MAX_AD_TOKEN_SIZE_BEFORE_B64 + let size = output_token.length.try_into().unwrap(); + let mut vec: Vec = Vec::with_capacity(size); + unsafe { + std::ptr::copy(output_token.value, vec.as_mut_ptr() as *mut _, size); + vec.set_len(size); + }; + + Ok(vec) + } + }; + let error_code = + unsafe { gss_delete_sec_context(&mut minor, &mut gss_context, output_token.as_mut_ptr()) }; + if unsafe { gss_error(error_code) } != 0 { + let error_message = convert_gss_error(error_code, minor); + if let Err(message) = res { + Err(message + &error_message) + } else { + Err(error_message) + } + } else { + res + } +} + +fn convert_gss_error(error_code: OM_uint32, minor: OM_uint32) -> String { + let mut message_context = 0; + let mut min_status = 0; + let gss_c_no_oid: gss_OID = unsafe { MaybeUninit::zeroed().assume_init() }; + let mut status_string = gss_buffer_desc { + length: 0, + value: std::ptr::null_mut(), + }; + let mut error_msg = Vec::new(); + loop { + unsafe { + gss_display_status( + &mut min_status, + error_code, + GSS_C_GSS_CODE, + gss_c_no_oid, + &mut message_context, + &mut status_string, + ) + }; + let slice_from_data: &[u8] = unsafe { + slice::from_raw_parts( + status_string.value as *mut _, + status_string + .length + .try_into() + .expect("Failed to convert status_string.length to usize"), + ) + }; + error_msg.extend_from_slice(slice_from_data); + unsafe { gss_release_buffer(&mut min_status, &mut status_string) }; + + if message_context == 0 { + break; + } + } + error_msg.extend_from_slice(b" (minor "); + error_msg.extend_from_slice(minor.to_string().as_bytes()); + error_msg.extend_from_slice(b")"); + + unsafe { String::from_utf8_unchecked(error_msg) } +} diff --git a/crates/krb5_rs/src/keytab.rs b/crates/krb5_rs/src/keytab.rs new file mode 100644 index 0000000..9dfe38f --- /dev/null +++ b/crates/krb5_rs/src/keytab.rs @@ -0,0 +1,34 @@ +use std::ffi::CString; +use std::mem::MaybeUninit; + +use crate::{Context, Result}; +use krb5_sys::{krb5_keytab, krb5_kt_close, krb5_kt_resolve}; + +use super::Krb5Error; + +pub struct Keytab<'a> { + pub(crate) context: &'a Context, + pub(crate) inner: krb5_keytab, +} + +impl<'a> Keytab<'a> { + pub fn resolve(context: &'a Context, name: &str) -> Result> { + let mut keytab: MaybeUninit = MaybeUninit::uninit(); + let name = CString::new(name).unwrap(); + let error_code = + unsafe { krb5_kt_resolve(context.inner, name.as_ptr(), keytab.as_mut_ptr()) }; + Krb5Error::exit_if_library_error(context, error_code)?; + Ok(Keytab { + context, + inner: unsafe { keytab.assume_init() }, + }) + } +} + +impl<'a> Drop for Keytab<'a> { + fn drop(&mut self) { + if !self.context.inner.is_null() && !self.inner.is_null() { + unsafe { krb5_kt_close(self.context.inner, self.inner) }; + } + } +} diff --git a/crates/krb5_rs/src/lib.rs b/crates/krb5_rs/src/lib.rs new file mode 100644 index 0000000..446d45d --- /dev/null +++ b/crates/krb5_rs/src/lib.rs @@ -0,0 +1,98 @@ +mod error; +pub use error::*; + +mod context; +pub use context::*; + +mod credentials; +pub use credentials::*; + +mod keytab; +pub use keytab::*; + +mod principal; +pub use principal::*; + +mod ccache; +pub use ccache::*; + +pub mod gssapi; + + +#[cfg(test)] +mod test { + use super::CCache; + use super::Context; + use super::Credentials; + use super::Principal; + use super::Result; + use super::Krb5Error; + + #[test] + fn test_init() -> Result<()> { + let _krb5_context = Context::new()?; + Ok(()) + } + + #[test] + fn test_default_realm() -> Result<()> { + let krb5_context = Context::new()?; + let realm = krb5_context.get_default_realm()?; + println!("Default realm: {}", realm); + Ok(()) + } + + #[test] + fn test_principal_data() -> Result<()> { + let krb5_context = Context::new()?; + let realm = krb5_context.get_default_realm()?; + let _princ = Principal::build_principal(&krb5_context, &realm, "patrick")?; + let _princ = Principal::build_principal(&krb5_context, &realm, "patrick/host")?; + Ok(()) + } + + #[test] + fn test_cc_default() -> Result<()> { + let context = Context::new()?; + let _ccache = CCache::default(&context)?; + Ok(()) + } + + #[test] + fn test_cc_resolve() -> Result<()> { + let context = Context::new()?; + let _ccache = CCache::resolve(&context, "pipo")?; + Ok(()) + } + + #[test] + fn test_cc_destroy_memory_ccache() -> Result<()> { + let context = Context::new()?; + let ccache = CCache::resolve(&context, "MEMORY:pipo")?; + ccache.destroy()?; + Ok(()) + } + + #[test] + fn test_cc_destroy_unexisting_ccache() { + let context = Context::new().unwrap(); + let ccache = CCache::resolve(&context, "/tmp/pipo").unwrap(); + match ccache.destroy() { + Err(Krb5Error::LibraryError(_)) => assert!(true), + Ok(_) => assert!(false, "Shouldn't be able to destroy a non-existing ccache"), + _ => assert!(false, "Destroy a non-existing ccache should trigger a `Krb5Error::LibraryError`") + } + } + + #[test] + fn test_store_creds_then_destroy() -> Result<()> { + let context = Context::new()?; + let principal = Principal::build_principal(&context, &context.get_default_realm()?, "admin")?; + let ccache = CCache::default(&context)?; + ccache.initialize(&principal)?; + let credentials = Credentials::get_init_credentials_password(&context, &principal, "adm1n_p4ssw0rd")?; + ccache.store_creds(credentials)?; + ccache.destroy()?; + Ok(()) + } +} diff --git a/crates/krb5_rs/src/principal.rs b/crates/krb5_rs/src/principal.rs new file mode 100644 index 0000000..6ddc506 --- /dev/null +++ b/crates/krb5_rs/src/principal.rs @@ -0,0 +1,61 @@ +use std::{mem::MaybeUninit, os::raw::c_uint}; + +use crate::{Context, Krb5Error, Result}; +use krb5_sys::{krb5_build_principal_ext, krb5_free_principal, krb5_principal}; + +pub struct Principal<'a> { + pub(crate) context: &'a Context, + pub(crate) inner: krb5_principal, +} + +impl<'a> Principal<'a> { + pub fn build_principal(context: &'a Context, realm: &str, user: &str) -> Result> { + let mut krb5_principal: MaybeUninit = MaybeUninit::uninit(); + let sp: Vec<&str> = user.split('/').collect(); + let error_code = match sp.len() { + 1 => unsafe { + let user = sp[0]; + + krb5_build_principal_ext( + context.inner, + krb5_principal.as_mut_ptr(), + realm.len() as c_uint, + realm.as_ptr() as *const i8, + user.len() as c_uint, + user.as_ptr(), + 0, + ) + }, + 2 => unsafe { + let user = sp[0]; + let host = sp[1]; + krb5_build_principal_ext( + context.inner, + krb5_principal.as_mut_ptr(), + realm.len() as c_uint, + realm.as_ptr() as *const i8, + user.len() as c_uint, + user.as_ptr(), + host.len() as c_uint, + host.as_ptr(), + 0, + ) + }, + _ => return Err(Krb5Error::InvalidPrincipal), + }; + Krb5Error::exit_if_library_error(context, error_code)?; + + Ok(Principal { + context, + inner: unsafe { krb5_principal.assume_init() }, + }) + } +} + +impl<'a> Drop for Principal<'a> { + fn drop(&mut self) { + if !self.context.inner.is_null() && !self.inner.is_null() { + unsafe { krb5_free_principal(self.context.inner, self.inner) } + } + } +} diff --git a/crates/krb5_sys/Cargo.toml b/crates/krb5_sys/Cargo.toml new file mode 100644 index 0000000..2bc7c05 --- /dev/null +++ b/crates/krb5_sys/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "krb5-sys" +version = "0.1.0" +edition = "2021" +links = "krb5" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + + +[build-dependencies] +bindgen = "0.59.2" +cc = "1.0" + +[target.'cfg(not(windows))'.build-dependencies] +pkg-config = "0.3.24" + +[target.'cfg(any(windows))'.build-dependencies] +winreg = { version = "0.10" } diff --git a/crates/krb5_sys/build.rs b/crates/krb5_sys/build.rs new file mode 100644 index 0000000..c4718f0 --- /dev/null +++ b/crates/krb5_sys/build.rs @@ -0,0 +1,118 @@ +use std::{env, path::PathBuf}; + +#[cfg(target_os = "windows")] +use winreg::enums::*; +#[cfg(target_os = "windows")] +use winreg::RegKey; + +#[cfg(target_os = "windows")] +fn link_library() -> Vec { + let krb5_home = env::var("KRB5_HOME"); + let path = match krb5_home { + Ok(krb5_home) => krb5_home, + Err(_) => { + let hklm = RegKey::predef(HKEY_LOCAL_MACHINE); + let mit_kerberos = hklm + .open_subkey("SOFTWARE\\MIT\\Kerberos\\SDK\\4.1.0") + .expect("Failed to find MIT Kerberos in WinRegistry"); + mit_kerberos + .get_value("PathName") + .expect("Failed to find MIT Kerberos path in WinRegistry") + } + }; + println!("cargo:rustc-link-search=native={}\\lib\\amd64\\", path); + println!("cargo:rustc-link-lib=dylib=krb5_64"); + println!("cargo:rustc-link-lib=dylib=gssapi64"); + vec![ + String::from("-I") + &path + "\\include", + String::from("-I") + &path + "\\include\\gssapi", + ] +} + +#[cfg(target_os = "linux")] +fn link_library() -> Vec { + let krb5_home = env::var("KRB5_HOME"); + if let Ok(krb5_home) = krb5_home { + let path: PathBuf = [&krb5_home, "lib", "pkgconfig"].iter().collect(); + if path.exists() { + env::set_var("PKG_CONFIG_PATH", path); + } + } + + pkg_config::Config::new() + .atleast_version("1.16.1") + .probe("mit-krb5") + .expect("Failed to bind libkrb5"); + + pkg_config::Config::new() + .atleast_version("1.16.1") + .probe("mit-krb5-gssapi") + .expect("Failed to bind libgssapi_krb5"); + Vec::new() +} + +fn main() { + println!("cargo:rerun-if-env-changed=KRB5_HOME"); + let clang_args = link_library(); + + println!("cargo:rerun-if-changed=src/wrapper.h"); + + let bindings = bindgen::Builder::default() + // The input header we would like to generate + // bindings for. + .header("src/wrapper.h") + .allowlist_var("krb5_.*") + .allowlist_function("krb5_init_context") + .allowlist_function("krb5_free_context") + .allowlist_function("krb5_get_error_message") + .allowlist_function("krb5_free_error_message") + .allowlist_function("krb5_get_default_realm") + .allowlist_function("krb5_free_default_realm") + .allowlist_function("krb5_build_principal_ext") + .allowlist_function("krb5_free_principal") + .allowlist_function("krb5_cc_default") + .allowlist_function("krb5_cc_initialize") + .allowlist_function("krb5_cc_resolve") + .allowlist_function("krb5_cc_close") + .allowlist_function("krb5_cc_destroy") + .allowlist_function("krb5_cc_store_cred") + .allowlist_function("krb5_cc_get_name") + .allowlist_function("krb5_get_init_creds_password") + .allowlist_function("krb5_free_cred_contents") + .allowlist_function("krb5_kt_resolve") + .allowlist_function("krb5_get_init_creds_keytab") + .allowlist_function("krb5_kt_close") + .allowlist_function("gss_import_name") + .allowlist_function("gss_release_name") + .allowlist_function("gss_krb5_ccache_name") + .allowlist_function("gss_init_sec_context") + .allowlist_function("gss_delete_sec_context") + .allowlist_function("gss_display_status") + .allowlist_function("gss_release_buffer") + .allowlist_function("gss_error") + .allowlist_var("GSS_C_NT_USER_NAME") + .allowlist_var("GSS_C_NT_HOSTBASED_SERVICE") + .allowlist_type("gss_int32") + .allowlist_var("GSS_C_NO_CREDENTIAL") + .allowlist_var("GSS_C_NO_CHANNEL_BINDINGS") + .allowlist_var("GSS_C_NO_CONTEXT") + .clang_args(clang_args) + // Tell cargo to invalidate the built crate whenever any of the + // included header files changed. + .parse_callbacks(Box::new(bindgen::CargoCallbacks)) + // Finish the builder and generate the bindings. + .generate() + // Unwrap the Result and panic on failure. + .expect("Unable to generate bindings"); + + // Write the bindings to the $OUT_DIR/bindings.rs file. + let out_path = PathBuf::from(env::var("OUT_DIR").unwrap()); + bindings + .write_to_file(out_path.join("bindings.rs")) + .expect("Couldn't write bindings!"); + + println!("cargo:rerun-if-changed=src/wrapper.c"); + let mut config = cc::Build::new(); + config.include("src").file("src/wrapper.c"); + config.compile("gssapi_wrapper"); +} diff --git a/crates/krb5_sys/src/lib.rs b/crates/krb5_sys/src/lib.rs new file mode 100644 index 0000000..a38a13a --- /dev/null +++ b/crates/krb5_sys/src/lib.rs @@ -0,0 +1,5 @@ +#![allow(non_upper_case_globals)] +#![allow(non_camel_case_types)] +#![allow(non_snake_case)] + +include!(concat!(env!("OUT_DIR"), "/bindings.rs")); diff --git a/crates/krb5_sys/src/wrapper.c b/crates/krb5_sys/src/wrapper.c new file mode 100644 index 0000000..01532ef --- /dev/null +++ b/crates/krb5_sys/src/wrapper.c @@ -0,0 +1 @@ +#include "wrapper.h" diff --git a/crates/krb5_sys/src/wrapper.h b/crates/krb5_sys/src/wrapper.h new file mode 100644 index 0000000..74ad297 --- /dev/null +++ b/crates/krb5_sys/src/wrapper.h @@ -0,0 +1,23 @@ +#include +#include +#include + +const gss_cred_id_t _GSS_C_NO_CREDENTIAL = GSS_C_NO_CREDENTIAL; +#undef GSS_C_NO_CREDENTIAL +const gss_cred_id_t GSS_C_NO_CREDENTIAL = _GSS_C_NO_CREDENTIAL; + +const gss_channel_bindings_t _GSS_C_NO_CHANNEL_BINDINGS = GSS_C_NO_CHANNEL_BINDINGS; +#undef GSS_C_NO_CHANNEL_BINDINGS +const gss_channel_bindings_t GSS_C_NO_CHANNEL_BINDINGS = _GSS_C_NO_CHANNEL_BINDINGS; + +const gss_ctx_id_t _GSS_C_NO_CONTEXT = GSS_C_NO_CONTEXT; +#undef GSS_C_NO_CONTEXT +const gss_ctx_id_t GSS_C_NO_CONTEXT = _GSS_C_NO_CONTEXT; + +#define GSS_ERROR(x) \ + ((x) & ((GSS_C_CALLING_ERROR_MASK << GSS_C_CALLING_ERROR_OFFSET) | \ + (GSS_C_ROUTINE_ERROR_MASK << GSS_C_ROUTINE_ERROR_OFFSET))) + +OM_uint32 gss_error(OM_uint32 x) { + return GSS_ERROR(x); +} diff --git a/lib/index.d.ts b/lib/index.d.ts new file mode 100644 index 0000000..b86ea61 --- /dev/null +++ b/lib/index.d.ts @@ -0,0 +1,24 @@ +/* tslint:disable */ +/* eslint-disable */ + +/* auto-generated by NAPI-RS */ + +export interface KinitParameters { + principal: string + password?: string | undefined | null + keytab?: string | undefined | null + realm?: string | undefined | null + ccname?: string | undefined | null +} +export interface KdestroyParameters { + ccname?: string | undefined | null +} +export interface GenerateSpnegoTokenParameters { + service_principal?: string | undefined | null + service_fqdn?: string | undefined | null + hostbased_service?: string | undefined | null + ccname?: string | undefined | null +} +export function kinit(options: KinitParameters): Promise +export function kdestroy(options: KdestroyParameters): Promise +export function spnego(options: GenerateSpnegoTokenParameters): Promise diff --git a/lib/index.js b/lib/index.js index b6ee541..d82bd48 100644 --- a/lib/index.js +++ b/lib/index.js @@ -1,259 +1,44 @@ // Generated by CoffeeScript 2.4.1 -var cleanup, fs, handle_error, k, kdestroy, kinit, spnego; +var krb5; -k = require('../build/Release/krb5'); - -fs = require('fs'); - -cleanup = function(ctx, princ, ccache) { - if (princ) { - k.krb5_free_principal_sync(ctx, princ); - } - if (ccache) { - return k.krb5_cc_close(ctx, ccache, function(err) { - if (ctx) { - return k.krb5_free_context_sync(ctx); - } - }); - } else { - if (ctx) { - return k.krb5_free_context_sync(ctx); - } - } -}; - -handle_error = function(callback, err, ctx, princ, ccache) { - if (!err) { - return err; - } - err = k.krb5_get_error_message_sync(ctx, err); - cleanup(ctx, princ, ccache); - return callback(Error(err)); -}; - -kinit = function(options, callback) { - var do_ccache, do_creds, do_init, do_principal, do_realm, split; - if (!options.principal) { - return callback(Error('Please specify principal for kinit')); - } - if (!(options.password || options.keytab)) { - return callback(Error('Please specify password or keytab for kinit')); - } - if (options.principal.indexOf('@') !== -1) { - split = options.principal.split('@'); - options.principal = split[0]; - options.realm = split[1]; - } - do_init = function() { - return k.krb5_init_context(function(err, ctx) { - if (err) { - return handle_error(callback, err, ctx); - } - return do_realm(ctx); - }); - }; - do_realm = function(ctx) { - if (!options.realm) { - return k.krb5_get_default_realm(ctx, function(err, realm) { - if (err) { - return handle_error(callback, err, ctx); - } - options.realm = realm; - return do_principal(ctx); - }); - } else { - return do_principal(ctx); - } - }; - do_principal = function(ctx) { - return k.krb5_build_principal(ctx, options.realm.length, options.realm, options.principal, function(err, princ) { - if (err) { - return handle_error(callback, err, ctx); - } - return do_ccache(ctx, princ); - }); - }; - do_ccache = function(ctx, princ) { - if (options.ccname) { - if (options.ccname.indexOf(':KEYRING') !== -1) { - cleanup(ctx, princ); - return callback(Error('KEYRING method not supported.')); - } - return k.krb5_cc_resolve(ctx, options.ccname, function(err, ccache) { - if (err) { - return handle_error(callback, err, ctx, princ); - } - return do_creds(ctx, princ, ccache); - }); - } else { - return k.krb5_cc_default(ctx, function(err, ccache) { - if (err) { - return handle_error(callback, err, ctx, princ); - } - return do_creds(ctx, princ, ccache); - }); - } - }; - do_creds = function(ctx, princ, ccache) { - var ccname, get_creds_keytab, get_creds_password, store_creds; - ccname = k.krb5_cc_get_name_sync(ctx, ccache); - fs.exists(ccname, function(exists) { - if (!exists) { - return k.krb5_cc_initialize(ctx, ccache, princ, function(err) { - if (err) { - return handle_error(callback, err, ctx, princ); - } - if (options.password) { - return get_creds_password(); - } else { - return get_creds_keytab(); - } - }); - } else { - if (options.password) { - return get_creds_password(); - } else { - return get_creds_keytab(); - } - } - }); - get_creds_password = function() { - return k.krb5_get_init_creds_password(ctx, princ, options.password, function(err, creds) { - if (err) { - return handle_error(callback, err, ctx, princ, ccache); - } - return store_creds(creds); - }); - }; - get_creds_keytab = function() { - return k.krb5_kt_resolve(ctx, options.keytab, function(err, kt) { - if (err) { - return handle_error(callback, err, ctx, princ, ccache); - } - return k.krb5_get_init_creds_keytab(ctx, princ, kt, 0, function(err, creds) { - if (err) { - return handle_error(callback, err, ctx, princ, ccache); - } - return store_creds(creds); - }); - }); - }; - return store_creds = function(creds) { - return k.krb5_cc_store_cred(ctx, ccache, creds, function(err) { - if (err) { - return handle_error(callback, err, ctx, princ, ccache); - } - cleanup(ctx, princ, ccache); - return callback(void 0, ccname); - }); - }; - }; - return do_init(); -}; - -kdestroy = function(options, callback) { - var do_ccache, do_destroy; - k.krb5_init_context(function(err, ctx) { - if (err) { - return handle_error(callback, err, ctx); - } - return do_ccache(ctx); - }); - do_ccache = function(ctx) { - if (options.ccname) { - return k.krb5_cc_resolve(ctx, options.ccname, function(err, ccache) { - if (err) { - return handle_error(callback, err, ctx, null, ccache); - } - return do_destroy(ctx, ccache); - }); - } else { - return k.krb5_cc_default(ctx, function(err, ccache) { - if (err) { - return handle_error(callback, err, ctx, null, ccache); - } - return do_destroy(ctx, ccache); - }); - } - }; - return do_destroy = function(ctx, ccache) { - return k.krb5_cc_destroy(ctx, ccache, function(err) { - if (err) { - return handle_error(callback, err, ctx); - } - return callback(void 0); - }); - }; -}; - -spnego = function(options, callback) { - var input_name_type, service; - if (options.ccname == null) { - options.ccname = ""; - } - if (options.service_principal) { - input_name_type = 'GSS_C_NT_USER_NAME'; - service = options.service_principal; - } else if (options.service_fqdn || options.hostbased_service) { - input_name_type = 'GSS_C_NT_HOSTBASED_SERVICE'; - service = options.service_fqdn || options.hostbased_service; - if (!/.*[@]/.test(service)) { - service = `HTTP@${service}`; - } - } else { - return callback(Error('Missing option "service_principal" or "hostbased_service"')); - } - return k.generate_spnego_token(service, input_name_type, options.ccname, function(err, token) { - return callback((err === "" ? void 0 : Error(err)), token != null ? token.toString('base64') : void 0); - }); -}; +krb5 = require('./index.node'); module.exports = { kinit: function(options, callback) { - if (typeof callback === 'function') { - return kinit(options, callback); + if (!callback) { + return krb5.kinit(options); } - return new Promise(function(resolve, reject) { - return kinit(options, function(err, ccname) { - if (err) { - reject(err); - } - return resolve(ccname); - }); + krb5.kinit(options).then(function(ccname) { + return callback(void 0, ccname); + }).catch(function(err) { + return callback(err); }); }, spnego: function(options, callback) { - if (typeof callback === 'function') { - return spnego(options, callback); + if (!callback) { + return krb5.spnego(options); } - return new Promise(function(resolve, reject) { - return spnego(options, function(err, token) { - if (err) { - reject(err); - } - return resolve(token); - }); + krb5.spnego(options).then(function(token) { + return callback(void 0, token); + }).catch(function(err) { + return callback(err); }); }, kdestroy: function(options, callback) { if (options == null) { options = {}; } + if (!(typeof options === 'function' || callback)) { + return krb5.kdestroy(options); + } if (typeof options === 'function') { callback = options; - return kdestroy({}, callback); - } else { - if (typeof callback === 'function') { - return kdestroy(options, callback); - } - return new Promise(function(resolve, reject) { - return kdestroy(options, function(err) { - if (err) { - reject(err); - } - return resolve(); - }); - }); + options = {}; } + krb5.kdestroy(options).then(function() { + return callback(); + }).catch(function(err) { + return callback(err); + }); } }; diff --git a/package.json b/package.json index c4387d1..666794c 100644 --- a/package.json +++ b/package.json @@ -3,21 +3,18 @@ "version": "0.5.4", "description": "Kerberos library bindings for Node.js", "main": "./lib/index.js", - "dependencies": { - "bindings": "1.5.0", - "node-addon-api": "1.7.1" - }, "devDependencies": { "coffeescript": "2.4.1", "mocha": "6.2.2", - "should": "13.2.3" + "should": "13.2.3", + "@napi-rs/cli": "^2.0.0" }, - "gypfile": true, "author": "Xavier Hermand (https://www.adaltas.com)", "contributors": [ "David Worms (https://www.adaltas.com)", "Pierre Sauvage (https://www.adaltas.com)", - "Xavier Hermand (https://www.adaltas.com)" + "Xavier Hermand (https://www.adaltas.com)", + "Guillaume Boutry (https://www.adaltas.com)" ], "license": "BSD-3-Clause", "keywords": [ @@ -31,6 +28,11 @@ "type": "git", "url": "https://github.com/adaltas/node-krb5" }, + "napi": { + "triples": { + "defaults": true + } + }, "scripts": { "preversion": "grep '## Trunk' CHANGELOG.md", "version": "version=`grep '^ \"version\": ' package.json | sed 's/.*\"\\([0-9\\.]*\\)\".*/\\1/'` && sed -i \"s/## Trunk/## Version $version/\" CHANGELOG.md && git add CHANGELOG.md", @@ -39,10 +41,13 @@ "minor": "npm version minor -m 'Bump to version %s'", "major": "npm version major -m 'Bump to version %s'", "coffee": "coffee -b -o lib src", - "rebuild": "node-gyp rebuild", "test": "mocha test/*.coffee", - "install": "node-gyp rebuild", "krb5-lib": "./install_krb5.sh", - "krb5-lib-zos": "./install_krb5_zos.sh" + "krb5-lib-zos": "./install_krb5_zos.sh", + "build": "napi build --cargo-name krb5_js ./lib", + "build-debug": "npm run build --", + "build-release": "npm run build -- --release", + "install": "npm run build-release", + "r_test": "cargo test" } } diff --git a/src/index.coffee b/src/index.coffee index c3af25d..6a2ab6a 100644 --- a/src/index.coffee +++ b/src/index.coffee @@ -1,146 +1,30 @@ -k = require '../build/Release/krb5' -fs = require 'fs' - -cleanup = (ctx, princ, ccache) -> - k.krb5_free_principal_sync ctx, princ if princ - - if ccache - k.krb5_cc_close ctx, ccache, (err) -> - k.krb5_free_context_sync ctx if ctx - else - k.krb5_free_context_sync ctx if ctx - -handle_error = (callback, err, ctx, princ, ccache) -> - return err unless err - err = k.krb5_get_error_message_sync(ctx, err) - cleanup ctx, princ, ccache - return callback Error err - -kinit = (options, callback) -> - return callback Error 'Please specify principal for kinit' unless options.principal - return callback Error 'Please specify password or keytab for kinit' unless options.password or options.keytab - if options.principal.indexOf('@') != -1 - split = options.principal.split('@') - options.principal = split[0] - options.realm = split[1] - do_init = -> - k.krb5_init_context (err, ctx) -> - return handle_error callback, err, ctx if err - do_realm ctx - do_realm = (ctx) -> - if !options.realm - k.krb5_get_default_realm ctx, (err, realm) -> - return handle_error callback, err, ctx if err - options.realm = realm - do_principal ctx - else - do_principal ctx - do_principal = (ctx) -> - k.krb5_build_principal ctx, - options.realm.length, - options.realm, - options.principal, - (err, princ) -> - return handle_error callback, err, ctx if err - do_ccache ctx, princ - do_ccache = (ctx, princ) -> - if options.ccname - if options.ccname.indexOf(':KEYRING') != -1 - cleanup ctx, princ - return callback Error 'KEYRING method not supported.' - k.krb5_cc_resolve ctx, options.ccname, (err, ccache) -> - return handle_error callback, err, ctx, princ if err - do_creds ctx, princ, ccache - else - k.krb5_cc_default ctx, (err, ccache) -> - return handle_error callback, err, ctx, princ if err - do_creds ctx, princ, ccache - do_creds = (ctx, princ, ccache) -> - ccname = k.krb5_cc_get_name_sync ctx, ccache - fs.exists ccname, (exists) -> - if !exists - k.krb5_cc_initialize ctx, ccache, princ, (err) -> - return handle_error callback, err, ctx, princ if err - if options.password then get_creds_password() else get_creds_keytab() - else - if options.password then get_creds_password() else get_creds_keytab() - get_creds_password = -> - k.krb5_get_init_creds_password ctx, princ, options.password, (err, creds) -> - return handle_error callback, err, ctx, princ, ccache if err - store_creds creds - get_creds_keytab = -> - k.krb5_kt_resolve ctx, options.keytab, (err, kt) -> - return handle_error callback, err, ctx, princ, ccache if err - k.krb5_get_init_creds_keytab ctx, princ, kt, 0, (err, creds) -> - return handle_error callback, err, ctx, princ, ccache if err - store_creds creds - store_creds = (creds) -> - k.krb5_cc_store_cred ctx, ccache, creds, (err) -> - return handle_error callback, err, ctx, princ, ccache if err - cleanup ctx, princ, ccache - callback undefined, ccname - do_init() - - -kdestroy = (options, callback) -> - k.krb5_init_context (err, ctx) -> - return handle_error(callback, err, ctx) if err - do_ccache(ctx) - - do_ccache = (ctx) -> - if options.ccname - k.krb5_cc_resolve ctx, options.ccname, (err, ccache) -> - return handle_error(callback, err, ctx, null, ccache) if err - do_destroy ctx, ccache - else - k.krb5_cc_default ctx, (err, ccache) -> - return handle_error(callback, err, ctx, null, ccache) if err - do_destroy ctx, ccache - - do_destroy = (ctx, ccache) -> - k.krb5_cc_destroy ctx, ccache, (err) -> - return handle_error(callback, err, ctx) if err - callback undefined - - -spnego = (options, callback) -> - options.ccname ?= "" - if options.service_principal - input_name_type = 'GSS_C_NT_USER_NAME' - service = options.service_principal - else if options.service_fqdn or options.hostbased_service - input_name_type = 'GSS_C_NT_HOSTBASED_SERVICE' - service = options.service_fqdn or options.hostbased_service - service = "HTTP@#{service}" unless /.*[@]/.test service - else return callback Error 'Missing option "service_principal" or "hostbased_service"' - - k.generate_spnego_token service, input_name_type, options.ccname, (err, token) -> - return callback (if err is "" then undefined else Error err), token?.toString 'base64' - +krb5 = require './index.node' module.exports = kinit: (options, callback) -> - return kinit options, callback if typeof callback is 'function' - return new Promise (resolve, reject) -> - kinit options, (err, ccname) -> - reject err if err - resolve ccname + return krb5.kinit options unless callback + krb5.kinit options + .then (ccname) -> callback undefined, ccname + .catch (err) -> callback err + return + spnego: (options, callback) -> - return spnego options, callback if typeof callback is 'function' - return new Promise (resolve, reject) -> - spnego options, (err, token) -> - reject err if err - resolve token + return krb5.spnego options unless callback + krb5.spnego options + .then (token) -> callback undefined, token + .catch (err) -> callback err + return kdestroy: (options, callback) -> options ?= {} + return krb5.kdestroy options unless typeof options is 'function' or callback if typeof options is 'function' callback = options - return kdestroy {}, callback - else - return kdestroy options, callback if typeof callback is 'function' - return new Promise (resolve, reject) -> - kdestroy options, (err) -> - reject err if err - resolve() + options = {} + + krb5.kdestroy options + .then () -> callback() + .catch (err) -> callback err + return + From 4a4e2ca314dfba795b799c9f186c873ea7b9dbd9 Mon Sep 17 00:00:00 2001 From: gboutry Date: Sun, 20 Feb 2022 13:31:52 +0100 Subject: [PATCH 03/11] fix: use asynctask instead of embedding tokio runtime --- Cargo.lock | 73 --------------- crates/krb5_js/Cargo.toml | 8 +- crates/krb5_js/build.rs | 1 - crates/krb5_js/src/kdestroy.rs | 40 ++++++++ crates/krb5_js/src/kinit.rs | 89 ++++++++++++++++++ crates/krb5_js/src/lib.rs | 165 ++++----------------------------- crates/krb5_js/src/spnego.rs | 76 +++++++++++++++ crates/krb5_rs/src/error.rs | 7 +- crates/krb5_rs/src/lib.rs | 14 ++- lib/index.d.ts | 12 +-- 10 files changed, 242 insertions(+), 243 deletions(-) create mode 100644 crates/krb5_js/src/kdestroy.rs create mode 100644 crates/krb5_js/src/kinit.rs create mode 100644 crates/krb5_js/src/spnego.rs diff --git a/Cargo.lock b/Cargo.lock index adb2065..61a7268 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -169,12 +169,6 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" -[[package]] -name = "itoa" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35" - [[package]] name = "krb5-js" version = "0.1.0" @@ -186,7 +180,6 @@ dependencies = [ "napi-build", "napi-derive", "regex", - "serde", "thiserror", ] @@ -267,9 +260,6 @@ dependencies = [ "ctor", "lazy_static", "napi-sys", - "serde", - "serde_json", - "tokio", "windows", ] @@ -323,16 +313,6 @@ dependencies = [ "version_check", ] -[[package]] -name = "num_cpus" -version = "1.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" -dependencies = [ - "hermit-abi", - "libc", -] - [[package]] name = "once_cell" version = "1.9.0" @@ -345,12 +325,6 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" -[[package]] -name = "pin-project-lite" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e280fbe77cc62c91527259e9442153f4688736748d24660126286329742b4c6c" - [[package]] name = "pkg-config" version = "0.3.24" @@ -398,43 +372,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" -[[package]] -name = "ryu" -version = "1.0.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f" - -[[package]] -name = "serde" -version = "1.0.136" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce31e24b01e1e524df96f1c2fdd054405f8d7376249a5110886fb4b658484789" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde_derive" -version = "1.0.136" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08597e7152fcd306f41838ed3e37be9eaeed2b61c42e2117266a554fab4662f9" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "serde_json" -version = "1.0.79" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e8d9fa5c3b304765ce1fd9c4c8a3de2c8db365a5b91be52f186efc675681d95" -dependencies = [ - "itoa", - "ryu", - "serde", -] - [[package]] name = "shlex" version = "1.1.0" @@ -496,16 +433,6 @@ dependencies = [ "syn", ] -[[package]] -name = "tokio" -version = "1.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2af73ac49756f3f7c01172e34a23e5d0216f6c32333757c2c61feb2bbff5a5ee" -dependencies = [ - "num_cpus", - "pin-project-lite", -] - [[package]] name = "unicode-width" version = "0.1.9" diff --git a/crates/krb5_js/Cargo.toml b/crates/krb5_js/Cargo.toml index cb79de4..b1625df 100644 --- a/crates/krb5_js/Cargo.toml +++ b/crates/krb5_js/Cargo.toml @@ -14,14 +14,8 @@ napi-build = "1" [dependencies] krb5-rs = { path = "../krb5_rs" } thiserror = "1" -napi = { version = "2", default-features = false, features = [ - "async", - "napi6", - "tokio_rt", - "serde-json", -] } +napi = { version = "2", default-features = false, features = ["napi6"] } napi-derive = "2" -serde = { version = "1", features = ["derive"] } base64 = "0.13" regex = "1" lazy_static = "1.4" diff --git a/crates/krb5_js/build.rs b/crates/krb5_js/build.rs index df244ef..0f1b010 100644 --- a/crates/krb5_js/build.rs +++ b/crates/krb5_js/build.rs @@ -1,4 +1,3 @@ - fn main() { napi_build::setup(); } diff --git a/crates/krb5_js/src/kdestroy.rs b/crates/krb5_js/src/kdestroy.rs new file mode 100644 index 0000000..c75f7bd --- /dev/null +++ b/crates/krb5_js/src/kdestroy.rs @@ -0,0 +1,40 @@ +use crate::error::Error; +use krb5_rs::{CCache, Context}; + +use napi::{Env, JsUndefined, Task}; +use napi_derive::napi; + +#[napi(object)] +#[derive(Debug)] +pub struct KdestroyParameters { + pub ccname: Option, +} + +pub fn kdestroy_function(options: &mut KdestroyParameters) -> std::result::Result<(), Error> { + let context = Context::new()?; + let ccache = match options.ccname { + Some(ref cc_name) => CCache::resolve(&context, cc_name)?, + None => CCache::default(&context)?, + }; + ccache.destroy()?; + + Ok(()) +} + +pub struct Kdestroy { + pub input: KdestroyParameters, +} + +#[napi] +impl Task for Kdestroy { + type Output = (); + type JsValue = JsUndefined; + + fn compute(&mut self) -> napi::Result { + kdestroy_function(&mut self.input).map_err(napi::Error::from) + } + + fn resolve(&mut self, env: Env, _output: Self::Output) -> napi::Result { + env.get_undefined() + } +} diff --git a/crates/krb5_js/src/kinit.rs b/crates/krb5_js/src/kinit.rs new file mode 100644 index 0000000..59071d6 --- /dev/null +++ b/crates/krb5_js/src/kinit.rs @@ -0,0 +1,89 @@ +use std::path::Path; + +use napi::{Env, JsString, Task}; +use napi_derive::napi; + +use krb5_rs::{CCache, Context, Credentials, Keytab, Principal}; + +use crate::error::Error; + +#[napi(object)] +#[derive(Debug)] +pub struct KinitParameters { + pub principal: String, + pub password: Option, + pub keytab: Option, + pub realm: Option, + pub ccname: Option, +} + +pub fn kinit_function(parameters: &mut KinitParameters) -> Result { + let KinitParameters { + principal, + password, + keytab, + realm, + ccname, + } = parameters; + if password.is_none() && keytab.is_none() { + return Err(Error::MissingPasswordOrKeytab); + } + + let (principal, realm_from_split) = if principal.contains('@') { + let splits: Vec<&str> = principal.split('@').collect(); + if splits.len() > 2 { + return Err(Error::Generic(String::from( + "Principal can contain at most one `@`", + ))); + } + (splits[0], Some(splits[1])) + } else { + (principal.as_str(), None) + }; + let context = Context::new()?; + let realm = match realm_from_split.or_else(|| realm.as_deref()) { + Some(realm) => realm.to_owned(), + None => context.get_default_realm()?, + }; + let principal = Principal::build_principal(&context, realm.as_str(), principal)?; + let ccache = match ccname { + Some(ref cc_name) => CCache::resolve(&context, cc_name)?, + None => CCache::default(&context)?, + }; + let credentials = match keytab { + Some(ref keytab) => { + let keytab = Keytab::resolve(&context, keytab)?; + Credentials::get_init_credentials_keytab(&context, &principal, &keytab)? + } + None => Credentials::get_init_credentials_password( + &context, + &principal, + password.as_ref().unwrap(), + )?, + }; + let ccache_path = ccache.name()?; + if !Path::new(&ccache_path).exists() { + ccache.initialize(&principal)?; + } + ccache.store_creds(credentials)?; + + Ok(ccache_path) +} + +pub struct Kinit { + pub input: KinitParameters, +} + +#[napi] +impl Task for Kinit { + type Output = String; + type JsValue = JsString; + + fn compute(&mut self) -> napi::Result { + kinit_function(&mut self.input).map_err(napi::Error::from) + } + + fn resolve(&mut self, env: Env, output: Self::Output) -> napi::Result { + env.create_string(&output) + } +} diff --git a/crates/krb5_js/src/lib.rs b/crates/krb5_js/src/lib.rs index 8cec94f..88d921c 100644 --- a/crates/krb5_js/src/lib.rs +++ b/crates/krb5_js/src/lib.rs @@ -1,158 +1,25 @@ -use std::path::Path; - -use krb5_rs::{gssapi, CCache, Context, Credentials, Keytab, Principal}; -use lazy_static::lazy_static; -use napi::tokio; +pub(crate) mod error; +mod kdestroy; +mod kinit; +mod spnego; + +use kdestroy::{Kdestroy, KdestroyParameters}; +use kinit::{Kinit, KinitParameters}; +use napi::bindgen_prelude::AsyncTask; use napi_derive::napi; - -mod error; -use error::Error; -use regex::Regex; - -lazy_static! { - static ref RE: Regex = Regex::new(".*[@]").unwrap(); -} - -#[napi(object)] -#[derive(Debug)] -pub struct KinitParameters { - pub principal: String, - pub password: Option, - pub keytab: Option, - pub realm: Option, - pub ccname: Option, -} - -fn kinit_function(parameters: KinitParameters) -> Result { - let KinitParameters { - principal, - password, - keytab, - realm, - ccname, - } = parameters; - if password.is_none() && keytab.is_none() { - return Err(Error::MissingPasswordOrKeytab); - } - - let (principal, realm_from_split) = if principal.contains('@') { - let splits: Vec<&str> = principal.split('@').collect(); - if splits.len() > 2 { - return Err(Error::Generic(String::from( - "Principal can contain at most one `@`", - ))); - } - (splits[0], Some(splits[1])) - } else { - (principal.as_str(), None) - }; - let context = Context::new()?; - let realm = match realm_from_split.or_else(|| realm.as_deref()) { - Some(realm) => realm.to_owned(), - None => context.get_default_realm()?, - }; - let principal = Principal::build_principal(&context, realm.as_str(), principal)?; - let ccache = match ccname { - Some(ref cc_name) => CCache::resolve(&context, cc_name)?, - None => CCache::default(&context)?, - }; - let credentials = match keytab { - Some(ref keytab) => { - let keytab = Keytab::resolve(&context, keytab)?; - Credentials::get_init_credentials_keytab(&context, &principal, &keytab)? - } - None => { - Credentials::get_init_credentials_password(&context, &principal, &password.unwrap())? - } - }; - let ccache_path = ccache.name()?; - if !Path::new(&ccache_path).exists() { - ccache.initialize(&principal)?; - } - ccache.store_creds(credentials)?; - - Ok(ccache_path) -} - -#[napi(object)] -#[derive(Debug)] -pub struct KdestroyParameters { - pub ccname: Option, -} - -fn kdestroy_function(options: KdestroyParameters) -> std::result::Result<(), Error> { - let context = Context::new()?; - let ccache = match options.ccname { - Some(ref cc_name) => CCache::resolve(&context, cc_name)?, - None => CCache::default(&context)?, - }; - ccache.destroy()?; - - Ok(()) -} - -#[napi(object)] -#[derive(Debug)] -pub struct GenerateSpnegoTokenParameters { - #[napi(js_name = "service_principal")] - pub service_principal: Option, - #[napi(js_name = "service_fqdn")] - pub service_fqdn: Option, - #[napi(js_name = "hostbased_service")] - pub hostbased_service: Option, - pub ccname: Option, -} - -fn generate_spnego_token_function( - options: GenerateSpnegoTokenParameters, -) -> std::result::Result { - let GenerateSpnegoTokenParameters { - service_principal, - service_fqdn, - hostbased_service, - ccname, - } = options; - - let (service_principal, input_name_type) = if let Some(service) = service_principal { - (service, String::from("GSS_C_NT_USER_NAME")) - } else { - let mut service = service_fqdn - .or(hostbased_service) - .ok_or_else(|| String::from("Invalid service principal in input"))?; - if !RE.is_match(&service) { - service.insert_str(0, "HTTP@"); - } - (service, String::from("GSS_C_NT_HOSTBASED_SERVICE")) - }; - - let target_name = gssapi::import_name(&service_principal, &input_name_type)?; - - if ccname.is_some() { - gssapi::krb5_ccache_name(&ccname.unwrap())?; - } - let token = gssapi::get_token(target_name)?; - Ok(base64::encode(token)) -} +use spnego::{GenerateSpnegoTokenParameters, Spnego}; #[napi] -pub async fn kinit(options: KinitParameters) -> napi::Result { - tokio::task::spawn(async move { kinit_function(options).map_err(napi::Error::from) }) - .await - .unwrap() +pub fn kinit(input: KinitParameters) -> AsyncTask { + AsyncTask::new(Kinit { input }) } -#[napi] -pub async fn kdestroy(options: KdestroyParameters) -> napi::Result<()> { - tokio::task::spawn(async move { kdestroy_function(options).map_err(napi::Error::from) }) - .await - .unwrap() +#[napi(ts_return_type="Promise")] +pub fn kdestroy(input: KdestroyParameters) -> AsyncTask { + AsyncTask::new(Kdestroy { input }) } #[napi] -pub async fn spnego(options: GenerateSpnegoTokenParameters) -> napi::Result { - tokio::task::spawn(async move { - generate_spnego_token_function(options).map_err(napi::Error::from_reason) - }) - .await - .unwrap() +pub fn spnego(input: GenerateSpnegoTokenParameters) -> AsyncTask { + AsyncTask::new(Spnego { input }) } diff --git a/crates/krb5_js/src/spnego.rs b/crates/krb5_js/src/spnego.rs new file mode 100644 index 0000000..6a858ee --- /dev/null +++ b/crates/krb5_js/src/spnego.rs @@ -0,0 +1,76 @@ +use krb5_rs::gssapi; +use napi::{Env, JsString, Task}; +use napi_derive::napi; + +use lazy_static::lazy_static; +use regex::Regex; + +lazy_static! { + static ref RE: Regex = Regex::new(".*[@]").unwrap(); +} + +#[napi(object)] +#[derive(Debug)] +pub struct GenerateSpnegoTokenParameters { + #[napi(js_name = "service_principal")] + pub service_principal: Option, + #[napi(js_name = "service_fqdn")] + pub service_fqdn: Option, + #[napi(js_name = "hostbased_service")] + pub hostbased_service: Option, + pub ccname: Option, +} + +pub fn generate_spnego_token_function( + options: &mut GenerateSpnegoTokenParameters, +) -> std::result::Result { + let GenerateSpnegoTokenParameters { + service_principal, + service_fqdn, + hostbased_service, + ccname, + } = options; + + let (service_principal, input_name_type) = if let Some(service) = service_principal { + (service.to_owned(), "GSS_C_NT_USER_NAME") + } else { + let mut service = match service_fqdn { + Some(service) => service.to_string(), + None => hostbased_service + .as_ref() + .ok_or("Invalid service principal in input")? + .to_string(), + }; + + if !RE.is_match(&service) { + service.insert_str(0, "HTTP@"); + } + (service.to_owned(), "GSS_C_NT_HOSTBASED_SERVICE") + }; + + let target_name = gssapi::import_name(&service_principal, input_name_type)?; + + if ccname.is_some() { + gssapi::krb5_ccache_name(ccname.as_ref().unwrap())?; + } + let token = gssapi::get_token(target_name)?; + Ok(base64::encode(token)) +} + +pub struct Spnego { + pub input: GenerateSpnegoTokenParameters, +} + +#[napi] +impl Task for Spnego { + type Output = String; + type JsValue = JsString; + + fn compute(&mut self) -> napi::Result { + generate_spnego_token_function(&mut self.input).map_err(napi::Error::from_reason) + } + + fn resolve(&mut self, env: Env, output: Self::Output) -> napi::Result { + env.create_string(&output) + } +} diff --git a/crates/krb5_rs/src/error.rs b/crates/krb5_rs/src/error.rs index a4db7c2..1702f64 100644 --- a/crates/krb5_rs/src/error.rs +++ b/crates/krb5_rs/src/error.rs @@ -14,7 +14,7 @@ pub enum Krb5Error { #[error("Invalid principal")] InvalidPrincipal, #[error("Failed to initialize context")] - ContextInitializationError + ContextInitializationError, } impl Krb5Error { @@ -22,7 +22,10 @@ impl Krb5Error { Krb5Error::LibraryError(context.get_error_message(error_code)) } - pub(crate) fn exit_if_library_error(context: &Context, error_code: krb5_error_code) -> Result<()> { + pub(crate) fn exit_if_library_error( + context: &Context, + error_code: krb5_error_code, + ) -> Result<()> { if error_code != 0 { Err(Krb5Error::from_library_error(context, error_code)) } else { diff --git a/crates/krb5_rs/src/lib.rs b/crates/krb5_rs/src/lib.rs index 446d45d..d6d9a35 100644 --- a/crates/krb5_rs/src/lib.rs +++ b/crates/krb5_rs/src/lib.rs @@ -18,15 +18,14 @@ pub use ccache::*; pub mod gssapi; - #[cfg(test)] mod test { use super::CCache; use super::Context; use super::Credentials; + use super::Krb5Error; use super::Principal; use super::Result; - use super::Krb5Error; #[test] fn test_init() -> Result<()> { @@ -80,17 +79,22 @@ mod test { match ccache.destroy() { Err(Krb5Error::LibraryError(_)) => assert!(true), Ok(_) => assert!(false, "Shouldn't be able to destroy a non-existing ccache"), - _ => assert!(false, "Destroy a non-existing ccache should trigger a `Krb5Error::LibraryError`") + _ => assert!( + false, + "Destroy a non-existing ccache should trigger a `Krb5Error::LibraryError`" + ), } } #[test] fn test_store_creds_then_destroy() -> Result<()> { let context = Context::new()?; - let principal = Principal::build_principal(&context, &context.get_default_realm()?, "admin")?; + let principal = + Principal::build_principal(&context, &context.get_default_realm()?, "admin")?; let ccache = CCache::default(&context)?; ccache.initialize(&principal)?; - let credentials = Credentials::get_init_credentials_password(&context, &principal, "adm1n_p4ssw0rd")?; + let credentials = + Credentials::get_init_credentials_password(&context, &principal, "adm1n_p4ssw0rd")?; ccache.store_creds(credentials)?; ccache.destroy()?; Ok(()) diff --git a/lib/index.d.ts b/lib/index.d.ts index b86ea61..c685283 100644 --- a/lib/index.d.ts +++ b/lib/index.d.ts @@ -3,6 +3,9 @@ /* auto-generated by NAPI-RS */ +export interface KdestroyParameters { + ccname?: string | undefined | null +} export interface KinitParameters { principal: string password?: string | undefined | null @@ -10,15 +13,12 @@ export interface KinitParameters { realm?: string | undefined | null ccname?: string | undefined | null } -export interface KdestroyParameters { - ccname?: string | undefined | null -} export interface GenerateSpnegoTokenParameters { service_principal?: string | undefined | null service_fqdn?: string | undefined | null hostbased_service?: string | undefined | null ccname?: string | undefined | null } -export function kinit(options: KinitParameters): Promise -export function kdestroy(options: KdestroyParameters): Promise -export function spnego(options: GenerateSpnegoTokenParameters): Promise +export function kinit(input: KinitParameters): Promise +export function kdestroy(input: KdestroyParameters): Promise +export function spnego(input: GenerateSpnegoTokenParameters): Promise From 20e27fbf0373b4cfeb914ed4081fb7598d6c6382 Mon Sep 17 00:00:00 2001 From: gboutry Date: Sun, 20 Feb 2022 15:07:42 +0100 Subject: [PATCH 04/11] fix: windows build fix: update windows10 vm --- crates/krb5_rs/src/gssapi.rs | 13 ++++++------- crates/krb5_sys/build.rs | 25 ++++++++++++++++--------- crates/krb5_sys/src/wrapper.h | 21 +++++++++++---------- vagrant/provision.yaml | 8 +++++--- 4 files changed, 38 insertions(+), 29 deletions(-) diff --git a/crates/krb5_rs/src/gssapi.rs b/crates/krb5_rs/src/gssapi.rs index 49bd978..a330970 100644 --- a/crates/krb5_rs/src/gssapi.rs +++ b/crates/krb5_rs/src/gssapi.rs @@ -5,11 +5,10 @@ use std::{ }; use krb5_sys::{ - gss_OID, gss_OID_desc, gss_buffer_desc, gss_buffer_desc_struct, gss_delete_sec_context, - gss_display_status, gss_error, gss_import_name, gss_init_sec_context, gss_int32, - gss_krb5_ccache_name, gss_name_t, gss_release_buffer, gss_release_name, OM_uint32, - GSS_C_NO_CHANNEL_BINDINGS, GSS_C_NO_CONTEXT, GSS_C_NO_CREDENTIAL, GSS_C_NT_HOSTBASED_SERVICE, - GSS_C_NT_USER_NAME, + gss_OID, gss_OID_desc, gss_buffer_desc, gss_buffer_desc_struct, gss_c_nt_hostbased_service, + gss_c_nt_user_name, gss_delete_sec_context, gss_display_status, gss_error, gss_import_name, + gss_init_sec_context, gss_int32, gss_krb5_ccache_name, gss_name_t, gss_release_buffer, + gss_release_name, OM_uint32, GSS_C_NO_CHANNEL_BINDINGS, GSS_C_NO_CONTEXT, GSS_C_NO_CREDENTIAL, }; const MAX_AD_TOKEN_SIZE_BEFORE_B64: u64 = 48000; @@ -39,9 +38,9 @@ pub fn import_name(principal: &str, input_name_type: &str) -> Result = MaybeUninit::uninit(); diff --git a/crates/krb5_sys/build.rs b/crates/krb5_sys/build.rs index c4718f0..2a9d53c 100644 --- a/crates/krb5_sys/build.rs +++ b/crates/krb5_sys/build.rs @@ -24,8 +24,8 @@ fn link_library() -> Vec { println!("cargo:rustc-link-lib=dylib=krb5_64"); println!("cargo:rustc-link-lib=dylib=gssapi64"); vec![ - String::from("-I") + &path + "\\include", - String::from("-I") + &path + "\\include\\gssapi", + String::new() + &path + "\\include", + String::new() + &path + "\\include\\gssapi", ] } @@ -53,7 +53,7 @@ fn link_library() -> Vec { fn main() { println!("cargo:rerun-if-env-changed=KRB5_HOME"); - let clang_args = link_library(); + let include_dirs = link_library(); println!("cargo:rerun-if-changed=src/wrapper.h"); @@ -90,13 +90,18 @@ fn main() { .allowlist_function("gss_display_status") .allowlist_function("gss_release_buffer") .allowlist_function("gss_error") - .allowlist_var("GSS_C_NT_USER_NAME") - .allowlist_var("GSS_C_NT_HOSTBASED_SERVICE") + .allowlist_function("gss_c_nt_user_name") + .allowlist_function("gss_c_nt_hostbased_service") .allowlist_type("gss_int32") .allowlist_var("GSS_C_NO_CREDENTIAL") .allowlist_var("GSS_C_NO_CHANNEL_BINDINGS") .allowlist_var("GSS_C_NO_CONTEXT") - .clang_args(clang_args) + .clang_args( + include_dirs + .iter() + .map(|include_dir| String::from("-I") + include_dir) + .collect::>(), + ) // Tell cargo to invalidate the built crate whenever any of the // included header files changed. .parse_callbacks(Box::new(bindgen::CargoCallbacks)) @@ -112,7 +117,9 @@ fn main() { .expect("Couldn't write bindings!"); println!("cargo:rerun-if-changed=src/wrapper.c"); - let mut config = cc::Build::new(); - config.include("src").file("src/wrapper.c"); - config.compile("gssapi_wrapper"); + cc::Build::new() + .includes(include_dirs) + .include("src") + .file("src/wrapper.c") + .compile("gssapi_wrapper"); } diff --git a/crates/krb5_sys/src/wrapper.h b/crates/krb5_sys/src/wrapper.h index 74ad297..0f3b825 100644 --- a/crates/krb5_sys/src/wrapper.h +++ b/crates/krb5_sys/src/wrapper.h @@ -2,22 +2,23 @@ #include #include -const gss_cred_id_t _GSS_C_NO_CREDENTIAL = GSS_C_NO_CREDENTIAL; #undef GSS_C_NO_CREDENTIAL -const gss_cred_id_t GSS_C_NO_CREDENTIAL = _GSS_C_NO_CREDENTIAL; +const gss_cred_id_t GSS_C_NO_CREDENTIAL = ((gss_cred_id_t) 0); -const gss_channel_bindings_t _GSS_C_NO_CHANNEL_BINDINGS = GSS_C_NO_CHANNEL_BINDINGS; #undef GSS_C_NO_CHANNEL_BINDINGS -const gss_channel_bindings_t GSS_C_NO_CHANNEL_BINDINGS = _GSS_C_NO_CHANNEL_BINDINGS; +const gss_channel_bindings_t GSS_C_NO_CHANNEL_BINDINGS = ((gss_channel_bindings_t) 0); -const gss_ctx_id_t _GSS_C_NO_CONTEXT = GSS_C_NO_CONTEXT; #undef GSS_C_NO_CONTEXT -const gss_ctx_id_t GSS_C_NO_CONTEXT = _GSS_C_NO_CONTEXT; - -#define GSS_ERROR(x) \ - ((x) & ((GSS_C_CALLING_ERROR_MASK << GSS_C_CALLING_ERROR_OFFSET) | \ - (GSS_C_ROUTINE_ERROR_MASK << GSS_C_ROUTINE_ERROR_OFFSET))) +const gss_ctx_id_t GSS_C_NO_CONTEXT = ((gss_ctx_id_t) 0); OM_uint32 gss_error(OM_uint32 x) { return GSS_ERROR(x); } + +gss_OID gss_c_nt_hostbased_service() { + return GSS_C_NT_HOSTBASED_SERVICE; +} + +gss_OID gss_c_nt_user_name() { + return GSS_C_NT_USER_NAME; +} diff --git a/vagrant/provision.yaml b/vagrant/provision.yaml index be23d56..cc07f19 100644 --- a/vagrant/provision.yaml +++ b/vagrant/provision.yaml @@ -14,9 +14,11 @@ - name: Install dev dependencies win_chocolatey: name: - - python - - nodejs + - llvm + - rustup.install - git + - nodejs + - python state: present - name: Install MIT Kerberos 4.1 @@ -29,4 +31,4 @@ win_shell: npm config set msvs_version 2017 - name: Clone krb5 folder - win_shell: git clone https://github.com/adaltas/node-krb5 /node-krb5 \ No newline at end of file + win_shell: git clone https://github.com/adaltas/node-krb5 /node-krb5 From 67b5d6b51730708370f5d39f9cba563bb8bd1e69 Mon Sep 17 00:00:00 2001 From: gboutry Date: Sun, 20 Feb 2022 19:42:20 +0100 Subject: [PATCH 05/11] fix: macos build --- crates/krb5_sys/build.rs | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/crates/krb5_sys/build.rs b/crates/krb5_sys/build.rs index 2a9d53c..c0d3bdf 100644 --- a/crates/krb5_sys/build.rs +++ b/crates/krb5_sys/build.rs @@ -6,31 +6,31 @@ use winreg::enums::*; use winreg::RegKey; #[cfg(target_os = "windows")] -fn link_library() -> Vec { +fn link_library() -> Vec { let krb5_home = env::var("KRB5_HOME"); let path = match krb5_home { Ok(krb5_home) => krb5_home, Err(_) => { let hklm = RegKey::predef(HKEY_LOCAL_MACHINE); let mit_kerberos = hklm - .open_subkey("SOFTWARE\\MIT\\Kerberos\\SDK\\4.1.0") + .open_subkey(r"SOFTWARE\MIT\Kerberos\SDK\4.1.0") .expect("Failed to find MIT Kerberos in WinRegistry"); mit_kerberos .get_value("PathName") .expect("Failed to find MIT Kerberos path in WinRegistry") } }; - println!("cargo:rustc-link-search=native={}\\lib\\amd64\\", path); + println!(r"cargo:rustc-link-search=native={}\lib\amd64\", path); println!("cargo:rustc-link-lib=dylib=krb5_64"); println!("cargo:rustc-link-lib=dylib=gssapi64"); vec![ - String::new() + &path + "\\include", - String::new() + &path + "\\include\\gssapi", + [&path, r"include"].iter().collect::(), + [&path, r"include\gssapi"].iter().collect::(), ] } -#[cfg(target_os = "linux")] -fn link_library() -> Vec { +#[cfg(any(target_os = "linux", target_os = "macos"))] +fn link_library() -> Vec { let krb5_home = env::var("KRB5_HOME"); if let Ok(krb5_home) = krb5_home { let path: PathBuf = [&krb5_home, "lib", "pkgconfig"].iter().collect(); @@ -38,17 +38,20 @@ fn link_library() -> Vec { env::set_var("PKG_CONFIG_PATH", path); } } - - pkg_config::Config::new() + let krb5 = pkg_config::Config::new() .atleast_version("1.16.1") .probe("mit-krb5") .expect("Failed to bind libkrb5"); - pkg_config::Config::new() + let gssapi = pkg_config::Config::new() .atleast_version("1.16.1") .probe("mit-krb5-gssapi") .expect("Failed to bind libgssapi_krb5"); - Vec::new() + + let mut include_dirs = Vec::new(); + include_dirs.extend(krb5.include_paths); + include_dirs.extend(gssapi.include_paths); + include_dirs } fn main() { @@ -99,7 +102,7 @@ fn main() { .clang_args( include_dirs .iter() - .map(|include_dir| String::from("-I") + include_dir) + .map(|include_dir| String::from("-I") + include_dir.to_str().unwrap()) .collect::>(), ) // Tell cargo to invalidate the built crate whenever any of the From 655ccf8c2f5b505b45bdf0fe169db07e23a87a7f Mon Sep 17 00:00:00 2001 From: gboutry Date: Tue, 22 Feb 2022 00:35:00 +0100 Subject: [PATCH 06/11] feat: add local testing --- .npmignore | 2 +- package.json | 12 +++--- src/server.py | 8 +++- test/kdestroy.coffee | 3 ++ test/kinit.coffee | 22 ++++++---- test/local_kdc/.gitignore | 5 +++ test/local_kdc/conf.sh | 58 ++++++++++++++++++++++++++ test/local_kdc/start_kdc.sh | 83 +++++++++++++++++++++++++++++++++++++ test/local_kdc/stop_kdc.sh | 13 ++++++ test/local_test.sh | 9 ++++ test/spnego.coffee | 29 +++++++------ 11 files changed, 215 insertions(+), 29 deletions(-) create mode 100644 test/local_kdc/.gitignore create mode 100644 test/local_kdc/conf.sh create mode 100755 test/local_kdc/start_kdc.sh create mode 100755 test/local_kdc/stop_kdc.sh create mode 100755 test/local_test.sh diff --git a/.npmignore b/.npmignore index 378eac2..7a86917 100644 --- a/.npmignore +++ b/.npmignore @@ -1 +1 @@ -build +lib/index.node diff --git a/package.json b/package.json index 666794c..92e27eb 100644 --- a/package.json +++ b/package.json @@ -33,6 +33,9 @@ "defaults": true } }, + "engines": { + "node": ">= 14" + }, "scripts": { "preversion": "grep '## Trunk' CHANGELOG.md", "version": "version=`grep '^ \"version\": ' package.json | sed 's/.*\"\\([0-9\\.]*\\)\".*/\\1/'` && sed -i \"s/## Trunk/## Version $version/\" CHANGELOG.md && git add CHANGELOG.md", @@ -42,12 +45,11 @@ "major": "npm version major -m 'Bump to version %s'", "coffee": "coffee -b -o lib src", "test": "mocha test/*.coffee", + "test_rs": "cargo test", + "local_test": "$SHELL test/local_test.sh", "krb5-lib": "./install_krb5.sh", - "krb5-lib-zos": "./install_krb5_zos.sh", "build": "napi build --cargo-name krb5_js ./lib", - "build-debug": "npm run build --", - "build-release": "npm run build -- --release", - "install": "npm run build-release", - "r_test": "cargo test" + "build-release": "npm run build -- --release --strip", + "install": "npm run build-release" } } diff --git a/src/server.py b/src/server.py index 3c50363..0b95f05 100644 --- a/src/server.py +++ b/src/server.py @@ -3,10 +3,14 @@ import base64 import socket import gssapi +import sys -FQDN = socket.getfqdn() +if len(sys.argv) == 2 and sys.argv[1] == "-local": + FQDN = "rest.krb.local" + print("Local mode, FQDN forced") +else: + FQDN = socket.getfqdn() print('FQDN: %s' % FQDN) - princ_name = "HTTP/%s@%s" % (FQDN, 'KRB.LOCAL') server_name = gssapi.Name( 'HTTP@' + FQDN, name_type=gssapi.NameType.hostbased_service) diff --git a/test/kdestroy.coffee b/test/kdestroy.coffee index f026372..024010a 100644 --- a/test/kdestroy.coffee +++ b/test/kdestroy.coffee @@ -1,5 +1,8 @@ krb5 = require '../lib/' +ccacheStartName = process.env.DEFAULT_CCACHE_LOCATION || '/tmp' +customcc = ccacheStartName + "/customcc" +restKeytab = process.env.REST_KEYTAB || '/tmp/krb5_test/rest.service.keytab' describe 'kdestroy', -> diff --git a/test/kinit.coffee b/test/kinit.coffee index a773302..011073e 100644 --- a/test/kinit.coffee +++ b/test/kinit.coffee @@ -1,5 +1,9 @@ krb5 = require '../lib/' +ccacheStartName = process.env.DEFAULT_CCACHE_LOCATION || '/tmp' +customcc = ccacheStartName + "/customcc" +restKeytab = process.env.REST_KEYTAB || '/tmp/krb5_test/rest.service.keytab' + describe 'kinit', -> describe 'function with callback', -> @@ -11,7 +15,7 @@ describe 'kinit', -> realm: 'KRB.LOCAL' , (err, ccname) -> return done err if err - ccname.should.startWith('/tmp') + ccname.should.startWith(ccacheStartName) done() it 'returns default credential cache path (password provided using default realm)', (done) -> @@ -20,7 +24,7 @@ describe 'kinit', -> password: 'adm1n_p4ssw0rd' , (err, ccname) -> return done err if err - ccname.should.startWith('/tmp') + ccname.should.startWith(ccacheStartName) done err it 'returns default credential cache path (password provided using realm in principal)', (done) -> @@ -30,28 +34,28 @@ describe 'kinit', -> realm: 'to_override' , (err, ccname) -> return done err if err - ccname.should.startWith('/tmp') + ccname.should.startWith(ccacheStartName) done err it 'returns default credential cache path (keytab provided)', (done) -> krb5.kinit principal: 'rest/rest.krb.local' - keytab: '/tmp/krb5_test/rest.service.keytab' + keytab: restKeytab realm: 'KRB.LOCAL' , (err, ccname) -> return done err if err - ccname.should.startWith('/tmp') + ccname.should.startWith(ccacheStartName) done() it 'returns given credential cache path (keytab provided)', (done) -> krb5.kinit principal: 'rest/rest.krb.local' - keytab: '/tmp/krb5_test/rest.service.keytab' + keytab: restKeytab realm: 'KRB.LOCAL' - ccname: '/tmp/customcc' + ccname: customcc , (err, ccname) -> return done err if err - ccname.should.be.eql('/tmp/customcc') + ccname.should.be.eql(customcc) done() describe 'function with promise', -> @@ -62,4 +66,4 @@ describe 'kinit', -> password: 'adm1n_p4ssw0rd' realm: 'KRB.LOCAL' .then (ccname) -> - ccname.should.startWith('/tmp') + ccname.should.startWith(ccacheStartName) diff --git a/test/local_kdc/.gitignore b/test/local_kdc/.gitignore new file mode 100644 index 0000000..f819d80 --- /dev/null +++ b/test/local_kdc/.gitignore @@ -0,0 +1,5 @@ +* +!.gitignore +!conf.sh +!start_kdc.sh +!stop_kdc.sh diff --git a/test/local_kdc/conf.sh b/test/local_kdc/conf.sh new file mode 100644 index 0000000..d632c84 --- /dev/null +++ b/test/local_kdc/conf.sh @@ -0,0 +1,58 @@ +#!/usr/bin/env bash + +function re_mkdir() { + if [[ -d $1 ]]; then + rm -Rf "$1" + fi + mkdir -p "$1" +} + +function kill_pid() { + if [[ -f "$1" ]]; then + kill "$(cat $1)" + rm "$1" + fi +} + +function rm_kt { + if [[ -f "$1" ]]; then + rm "$1" + fi +} + +if [[ ! -z $1 ]]; then + basedir="$1" +else + basedir="$( cd -- "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )" +fi + +export CONF_DIR="$basedir/conf" +export KRB5_CONFIG="$CONF_DIR/krb5.conf" +export LOG_DIR="$basedir/logs" +export DEFAULT_LOG_FILE="$LOG_DIR/krb5libs.log" +export KDC_LOG_FILE="$LOG_DIR/krb5kdc.log" +export ADMIN_SERVER_LOG_FILE="$LOG_DIR/kadmind.log" +export DEFAULT_CCACHE_LOCATION="$basedir/ccaches" +export DB_LOCATION="$basedir/db" +export STASH_FILE="$DB_LOCATION/.k5.KRB.LOCAL" +export DB_NAME="$DB_LOCATION/principal" +export PID_DIR="$basedir/pid" +export ACL_FILE="$basedir/conf/kadm5.acl" +export KEYTAB_DIR="$basedir/keytab" +export REST_KEYTAB="$KEYTAB_DIR/rest.service.keytab" +export HTTP_KEYTAB="$KEYTAB_DIR/http.service.keytab" +export KRB5_KTNAME="FILE:$HTTP_KEYTAB" # keytab used by server.py +export REST_ADDRESS="$(hostname -f)" + + +if [[ 0 -ne $(command -v krb5kdc >/dev/null; echo $?) ]]; then + if [[ ! -z "$KRB5_HOME" ]]; then + export PATH="$KRB5_HOME/sbin:$PATH" + fi + if [[ 0 -ne $(command -v krb5kdc >/dev/null; echo $?) ]]; then + echo "Fail to find krb5kdc in PATH, either you installed krb5 via homebrew then you must set KRB5_HOME to your cellar/krb5 path" + echo "(example /opt/homebrew/opt/krb5/) or you didn't install the relevant package (krb5-kdc on debian-like)" + echo "In case you compiled mit krb5 to a custom prefix, set the prefix as KRB5_HOME" + exit 1 + fi +fi diff --git a/test/local_kdc/start_kdc.sh b/test/local_kdc/start_kdc.sh new file mode 100755 index 0000000..9d5ddff --- /dev/null +++ b/test/local_kdc/start_kdc.sh @@ -0,0 +1,83 @@ +#!/usr/bin/env bash + + +if [[ ! -z $1 ]]; then + basedir="$1" +else + basedir="$( cd -- "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )" +fi + + +source "$basedir/conf.sh" + +kill_pid "$PID_DIR/krb5kdc.pid" +kill_pid "$PID_DIR/kadmind.pid" +kill_pid "$PID_DIR/server.pid" + +re_mkdir "$CONF_DIR" +re_mkdir "$LOG_DIR" +re_mkdir "$DEFAULT_CCACHE_LOCATION" +re_mkdir "$DB_LOCATION" +re_mkdir "$PID_DIR" +re_mkdir "$KEYTAB_DIR" + +cat>"$KRB5_CONFIG"< "$ACL_FILE" + + +if [[ -f "$DB_NAME" ]]; then + rm -f "$DB_NAME*" +fi + +kdb5_util -d "$DB_NAME" -P masterkey create -s +sleep 2 +kadmin.local -q "addprinc -pw adm1n_p4ssw0rd admin" + +rm_kt "$REST_KEYTAB" +rm_kt "$HTTP_KEYTAB" + + +kadmin.local -q "addprinc -randkey rest/rest.krb.local@KRB.LOCAL" +kadmin.local -q "xst -k $REST_KEYTAB rest/rest.krb.local@KRB.LOCAL" + +kadmin.local -q "addprinc -randkey HTTP/rest.krb.local@KRB.LOCAL" +kadmin.local -q "xst -k $HTTP_KEYTAB HTTP/rest.krb.local@KRB.LOCAL" + + +krb5kdc -P "$PID_DIR/krb5kdc.pid" +kadmind -P "$PID_DIR/kadmind.pid" +sleep 2 +PYTHONUNBUFFERED=1 python3 "$basedir/../../src/server.py" -local > $LOG_DIR/server.log & echo $! > "$PID_DIR/server.pid" diff --git a/test/local_kdc/stop_kdc.sh b/test/local_kdc/stop_kdc.sh new file mode 100755 index 0000000..6bd8eac --- /dev/null +++ b/test/local_kdc/stop_kdc.sh @@ -0,0 +1,13 @@ +#!/usr/bin/env bash + +if [[ ! -z $1 ]]; then + basedir="$1" +else + basedir="$( cd -- "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )" +fi + +source "$basedir/conf.sh" + +kill_pid "$PID_DIR/krb5kdc.pid" +kill_pid "$PID_DIR/kadmind.pid" +kill_pid "$PID_DIR/server.pid" diff --git a/test/local_test.sh b/test/local_test.sh new file mode 100755 index 0000000..2c4ec53 --- /dev/null +++ b/test/local_test.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +current_script_basedir="$( cd -- "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )" +source "$current_script_basedir/local_kdc/conf.sh" "$current_script_basedir/local_kdc" +. "$current_script_basedir/local_kdc/start_kdc.sh" "$current_script_basedir/local_kdc" + +npm run test + +. "$current_script_basedir/local_kdc/stop_kdc.sh" "$current_script_basedir/local_kdc" diff --git a/test/spnego.coffee b/test/spnego.coffee index 25aa6e8..5a062bc 100644 --- a/test/spnego.coffee +++ b/test/spnego.coffee @@ -1,13 +1,18 @@ krb5 = require '../lib/' http = require 'http' +ccacheStartName = process.env.DEFAULT_CCACHE_LOCATION || '/tmp' +customcc = ccacheStartName + "/customcc" +restKeytab = process.env.REST_KEYTAB || '/tmp/krb5_test/rest.service.keytab' +hostname = process.env.REST_ADDRESS || 'rest.krb.local' + describe 'spnego', -> describe 'function with callback', -> it 'checks that server requires authentication', (done) -> http.request - hostname: 'rest.krb.local' + hostname: hostname port: 8080 path: '/' method: 'GET' @@ -19,7 +24,7 @@ describe 'spnego', -> it 'authenticates to REST server with SPNEGO token (hostbased service)', (done) -> krb5.kinit principal: 'rest/rest.krb.local' - keytab: 'FILE:/tmp/krb5_test/rest.service.keytab' + keytab: 'FILE:' + restKeytab realm: 'KRB.LOCAL' , (err, ccname) -> krb5.spnego @@ -28,7 +33,7 @@ describe 'spnego', -> return done err if err token.should.be.String() http.request - hostname: 'rest.krb.local' + hostname: hostname port: 8080 path: '/' method: 'GET' @@ -42,7 +47,7 @@ describe 'spnego', -> it 'authenticates to REST server with SPNEGO token (service principal)', (done) -> krb5.kinit principal: 'rest/rest.krb.local' - keytab: 'FILE:/tmp/krb5_test/rest.service.keytab' + keytab: 'FILE:' + restKeytab realm: 'KRB.LOCAL' , (err, ccname) -> krb5.spnego @@ -51,7 +56,7 @@ describe 'spnego', -> return done err if err token.should.be.String() http.request - hostname: 'rest.krb.local' + hostname: hostname port: 8080 path: '/' method: 'GET' @@ -65,7 +70,7 @@ describe 'spnego', -> it 'authenticates to REST server with SPNEGO token (service fqdn)', (done) -> krb5.kinit principal: 'rest/rest.krb.local' - keytab: 'FILE:/tmp/krb5_test/rest.service.keytab' + keytab: 'FILE:' + restKeytab realm: 'KRB.LOCAL' , (err, ccname) -> krb5.spnego @@ -74,7 +79,7 @@ describe 'spnego', -> return done err if err token.should.be.String() http.request - hostname: 'rest.krb.local' + hostname: hostname port: 8080 path: '/' method: 'GET' @@ -90,16 +95,16 @@ describe 'spnego', -> principal: 'admin' password: 'adm1n_p4ssw0rd' realm: 'KRB.LOCAL' - ccname: '/tmp/customcc' + ccname: customcc , (err, ccname) -> krb5.spnego hostbased_service: 'HTTP@rest.krb.local' - ccname: '/tmp/customcc' + ccname: customcc , (err, token) -> return done err if err token.should.be.String() http.request - hostname: 'rest.krb.local' + hostname: hostname port: 8080 path: '/' method: 'GET' @@ -115,7 +120,7 @@ describe 'spnego', -> it 'returns SPNEGO token', -> krb5.kinit principal: 'rest/rest.krb.local' - keytab: '/tmp/krb5_test/rest.service.keytab' + keytab: restKeytab realm: 'KRB.LOCAL' .then (ccname) -> krb5.spnego @@ -123,7 +128,7 @@ describe 'spnego', -> .then (token) -> token.should.be.String() http.request - hostname: 'rest.krb.local' + hostname: hostname port: 8080 path: '/' method: 'GET' From a86f18e8f13d0b315a50930342a1fdc87185caf1 Mon Sep 17 00:00:00 2001 From: gboutry Date: Tue, 22 Feb 2022 08:29:59 +0100 Subject: [PATCH 07/11] feat: install_krb5.sh vendors a krb5 version Installing a system dependency is out of scope for this script, therefore krb5 will be vendored --- install_krb5.sh | 42 +++++++++++++++++++++++++++--------------- 1 file changed, 27 insertions(+), 15 deletions(-) diff --git a/install_krb5.sh b/install_krb5.sh index 7999a1f..cb36f87 100755 --- a/install_krb5.sh +++ b/install_krb5.sh @@ -1,18 +1,30 @@ #!/usr/bin/env bash set -e -echo "Downloading MIT Kerberos..." -wget https://kerberos.org/dist/krb5/1.16/krb5-1.16.1.tar.gz -echo "Extracting..." -mkdir -p deps -tar -xzf krb5-1.16.1.tar.gz --directory deps -cd deps/krb5-1.16.1/src -echo "Compiling" -./configure -make -echo "Installing requires root privilege" -sudo make install -echo "Cleaning files..." -cd ../../.. -rm -rf deps -rm -rf krb5-1.16.1.tar.gz +vendor="$( cd -- "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )/vendor" +if [[ ! -d "$vendor/lib" || ! -d "$vendor/include" || ! -d "$vendor/sbin" ]]; then + echo "Downloading MIT Kerberos..." + wget https://kerberos.org/dist/krb5/1.16/krb5-1.16.1.tar.gz + echo "Extracting..." + mkdir deps + if [[ -d "$vendor" ]]; then + rm -rf "$vendor" + fi + mkdir "$vendor" + tar -xzf krb5-1.16.1.tar.gz --directory deps + cd deps/krb5-1.16.1/src + echo "Compiling" + ./configure --prefix "$vendor" + make + echo "Installing binaries to $vendor" + make install + echo "Cleaning files..." + cd ../../.. + rm -rf deps + rm -rf krb5-1.16.1.tar.gz +else + echo "$vendor seems to already exists, if you need to rebuild it, delete the folder" +fi + +echo "Export the following variable to make the vendored krb5 available to the build" +echo "export KRB5_HOME=$vendor" From fb5a602d8c386e634e0294d1a12f11d22b6ba87d Mon Sep 17 00:00:00 2001 From: gboutry Date: Tue, 22 Feb 2022 10:24:14 +0100 Subject: [PATCH 08/11] fix: docker test, add ubuntu-vendored target --- .dockerignore | 10 +++++++ .gitignore | 1 + crates/krb5_sys/build.rs | 4 +-- docker/archlinux/Dockerfile | 16 ++++++---- docker/archlinux/run.sh | 7 +++-- docker/centos7/Dockerfile | 18 ++++++----- docker/centos7/run.sh | 7 +++-- docker/docker-compose.yml | 50 ++++++++++++++----------------- docker/ubuntu-vendored/Dockerfile | 36 ++++++++++++++++++++++ docker/ubuntu-vendored/run.sh | 34 +++++++++++++++++++++ docker/ubuntu/Dockerfile | 17 +++++++---- docker/ubuntu/run.sh | 9 +++--- 12 files changed, 152 insertions(+), 57 deletions(-) create mode 100644 .dockerignore create mode 100644 docker/ubuntu-vendored/Dockerfile create mode 100755 docker/ubuntu-vendored/run.sh diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..0e082fb --- /dev/null +++ b/.dockerignore @@ -0,0 +1,10 @@ +* +!docker/**/run.sh +!crates/ +!lib/ +lib/index.node +!src/ +!test/ +!Cargo.* +!package* +!install_krb5.sh diff --git a/.gitignore b/.gitignore index c8d8a87..630a0f5 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,4 @@ yarn.lock .vagrant/ target/ lib/index.node +vendor/ diff --git a/crates/krb5_sys/build.rs b/crates/krb5_sys/build.rs index c0d3bdf..40d3a58 100644 --- a/crates/krb5_sys/build.rs +++ b/crates/krb5_sys/build.rs @@ -39,12 +39,12 @@ fn link_library() -> Vec { } } let krb5 = pkg_config::Config::new() - .atleast_version("1.16.1") + .atleast_version("1.15") .probe("mit-krb5") .expect("Failed to bind libkrb5"); let gssapi = pkg_config::Config::new() - .atleast_version("1.16.1") + .atleast_version("1.15") .probe("mit-krb5-gssapi") .expect("Failed to bind libgssapi_krb5"); diff --git a/docker/archlinux/Dockerfile b/docker/archlinux/Dockerfile index 94d6c77..e4ed5e6 100644 --- a/docker/archlinux/Dockerfile +++ b/docker/archlinux/Dockerfile @@ -1,14 +1,13 @@ FROM archlinux/archlinux:base -# Update repositories -RUN echo "Server = http://mirror.aarnet.edu.au/pub/archlinux/$repo/os/$arch" >> /etc/pacman.d/mirrorlist RUN pacman --noconfirm -Syu # Install misc -RUN pacman --noconfirm -S wget git vim sudo +RUN pacman --noconfirm -S wget git python3 make clang pkg-config -# Install compilation tools -RUN pacman --noconfirm -S python3 gcc make +RUN curl https://sh.rustup.rs -sSf | bash -s -- -y --profile minimal + +RUN echo 'source $HOME/.cargo/env' >> /etc/profile # Install Kerberos libs RUN pacman --noconfirm -S krb5 @@ -21,5 +20,10 @@ RUN n 14 RUN n 16 RUN mkdir -p /node-krb5 -COPY ./run.sh /run.sh +WORKDIR /node-krb5 + +COPY . . + +# Context is node-krb5 folder +COPY docker/archlinux/run.sh /run.sh ENTRYPOINT ["/run.sh"] diff --git a/docker/archlinux/run.sh b/docker/archlinux/run.sh index f24bb48..0e3a88d 100755 --- a/docker/archlinux/run.sh +++ b/docker/archlinux/run.sh @@ -6,6 +6,8 @@ RED='\033[0;31m' GREEN='\033[0;32m' NC='\033[0m' # No Color +source /etc/profile + while [ ! -f /tmp/krb5_test/rest_ready ] do echo "Waiting for REST server to be ready..." @@ -16,9 +18,8 @@ cp /tmp/krb5_test/krb5.conf /etc/krb5.conf for node_version in "16" "14"; do echo "Node.js version "$node_version n $node_version - # --unsafe-perm allows node-gyp build as root # --force forces reinstallation - npm install --unsafe-perm --force + npm install --force npm test err=$? if [ $err -ne 0 ] @@ -30,4 +31,4 @@ for node_version in "16" "14"; do fi done echo -e $GREEN"Tests passed for Node.js version 14 and 16"$NC -exit 0 \ No newline at end of file +exit 0 diff --git a/docker/centos7/Dockerfile b/docker/centos7/Dockerfile index dbd64d2..4850c27 100644 --- a/docker/centos7/Dockerfile +++ b/docker/centos7/Dockerfile @@ -1,16 +1,15 @@ FROM centos:7 -# Install epel (requirement for service nginx) -RUN yum install -y epel-release - +# Install scl for llvm-toolset RUN yum install -y centos-release-scl -RUN yum install -y devtoolset-10-gcc-c++ # Install misc -RUN yum install -y wget git vim sudo make +RUN yum install -y curl git sudo make devtoolset-10-gcc-c++ llvm-toolset-7 # Install compilation tools -RUN yum install -y python3 make +RUN curl https://sh.rustup.rs -sSf | bash -s -- -y --profile minimal + +RUN echo 'source $HOME/.cargo/env' >> /etc/profile # Install Kerberos libs RUN yum install -y krb5-devel @@ -23,5 +22,10 @@ RUN n 14 RUN n 16 RUN mkdir -p /node-krb5 -COPY ./run.sh /run.sh +WORKDIR /node-krb5 + +COPY . . + +# Context is node-krb5 folder +COPY docker/centos7/run.sh /run.sh ENTRYPOINT ["/run.sh"] diff --git a/docker/centos7/run.sh b/docker/centos7/run.sh index 1a8279f..9c8441a 100755 --- a/docker/centos7/run.sh +++ b/docker/centos7/run.sh @@ -6,7 +6,9 @@ RED='\033[0;31m' GREEN='\033[0;32m' NC='\033[0m' # No Color +source /etc/profile source scl_source enable devtoolset-10 # load environment configuration to use gcc 7 +source scl_source enable llvm-toolset-7 # load environment configuration to use clang 5 while [ ! -f /tmp/krb5_test/rest_ready ] do @@ -18,9 +20,8 @@ cp /tmp/krb5_test/krb5.conf /etc/krb5.conf for node_version in "16" "14"; do echo "Node.js version "$node_version n $node_version - # --unsafe-perm allows node-gyp build as root # --force forces reinstallation - npm install --unsafe-perm --force + npm install --force npm test err=$? if [ $err -ne 0 ] @@ -32,4 +33,4 @@ for node_version in "16" "14"; do fi done echo -e $GREEN"Tests passed for Node.js version 14 and 16"$NC -exit 0 \ No newline at end of file +exit 0 diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index f4de97c..4153f27 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -23,15 +23,12 @@ services: ports: - 8080:8080 volumes: - - ../src:/node-krb5/src - - ../lib:/node-krb5/lib - - ../test:/node-krb5/test - - ../package.json:/node-krb5/package.json - - ../binding.gyp:/node-krb5/binding.gyp - - ../install_krb5.sh:/node-krb5/install_krb5.sh + - ../src/server.py:/node-krb5/src/server.py - /tmp/krb5_test:/tmp/krb5_test archlinux: - build: ./archlinux + build: + context: ../ + dockerfile: docker/archlinux/Dockerfile image: krb5_archlinux depends_on: - kerberos @@ -40,15 +37,11 @@ services: - "kerberos:kdc.krb.local" - "rest:rest.krb.local" volumes: - - ../src:/node-krb5/src - - ../lib:/node-krb5/lib - - ../test:/node-krb5/test - - ../package.json:/node-krb5/package.json - - ../binding.gyp:/node-krb5/binding.gyp - - ../install_krb5.sh:/node-krb5/install_krb5.sh - /tmp/krb5_test:/tmp/krb5_test centos7: - build: ./centos7 + build: + context: ../ + dockerfile: docker/centos7/Dockerfile image: krb5_centos7 depends_on: - kerberos @@ -57,15 +50,11 @@ services: - "kerberos:kdc.krb.local" - "rest:rest.krb.local" volumes: - - ../src:/node-krb5/src - - ../lib:/node-krb5/lib - - ../test:/node-krb5/test - - ../package.json:/node-krb5/package.json - - ../binding.gyp:/node-krb5/binding.gyp - - ../install_krb5.sh:/node-krb5/install_krb5.sh - /tmp/krb5_test:/tmp/krb5_test ubuntu: - build: ./ubuntu + build: + context: ../ + dockerfile: docker/ubuntu/Dockerfile image: krb5_ubuntu depends_on: - kerberos @@ -74,10 +63,17 @@ services: - "kerberos:kdc.krb.local" - "rest:rest.krb.local" volumes: - - ../src:/node-krb5/src - - ../lib:/node-krb5/lib - - ../test:/node-krb5/test - - ../package.json:/node-krb5/package.json - - ../binding.gyp:/node-krb5/binding.gyp - - ../install_krb5.sh:/node-krb5/install_krb5.sh + - /tmp/krb5_test:/tmp/krb5_test + ubuntu-vendored: + build: + context: ../ + dockerfile: docker/ubuntu-vendored/Dockerfile + image: krb5_ubuntu_vendored + depends_on: + - kerberos + - rest + links: + - "kerberos:kdc.krb.local" + - "rest:rest.krb.local" + volumes: - /tmp/krb5_test:/tmp/krb5_test diff --git a/docker/ubuntu-vendored/Dockerfile b/docker/ubuntu-vendored/Dockerfile new file mode 100644 index 0000000..aad5b0e --- /dev/null +++ b/docker/ubuntu-vendored/Dockerfile @@ -0,0 +1,36 @@ +FROM ubuntu:bionic + +# Update repositories +RUN apt-get update + +# Install misc +RUN apt-get install -y wget git make curl clang bison pkg-config + +# Install compilation tools +RUN curl https://sh.rustup.rs -sSf | bash -s -- -y --profile minimal + +RUN echo 'source $HOME/.cargo/env' >> /etc/profile + +#RUN rm -f /usr/bin/python && ln -s /usr/bin/python3 /usr/bin/python + +# Kerberos libs will be built there +ENV KRB5_HOME="/node-krb5/vendor" + +# Install Node.js via n +ENV NPM_CONFIG_LOGLEVEL info +RUN git clone https://github.com/tj/n /n +RUN cd /n && make install +RUN n 14 +RUN n 16 + +RUN mkdir -p /node-krb5 +WORKDIR /node-krb5 + +COPY ./install_krb5.sh . +RUN ./install_krb5.sh + +COPY . . + +# Context is node-krb5 folder +COPY docker/ubuntu-vendored/run.sh /run.sh +ENTRYPOINT ["/run.sh"] diff --git a/docker/ubuntu-vendored/run.sh b/docker/ubuntu-vendored/run.sh new file mode 100755 index 0000000..ae7570b --- /dev/null +++ b/docker/ubuntu-vendored/run.sh @@ -0,0 +1,34 @@ +#!/bin/bash + +[[ "TRACE" ]] + +RED='\033[0;31m' +GREEN='\033[0;32m' +NC='\033[0m' # No Color + +source /etc/profile + +while [ ! -f /tmp/krb5_test/rest_ready ] +do + echo "Waiting for REST server to be ready..." + sleep 1 +done + +cp /tmp/krb5_test/krb5.conf /etc/krb5.conf +for node_version in "16" "14"; do + echo "Node.js version "$node_version + n $node_version + # --force forces reinstallation + npm install --force + npm test + err=$? + if [ $err -ne 0 ] + then + echo -e $RED"Tests failed for Node.js "$node_version$NC + exit $err + else + echo -e $GREEN"Tests passed for Node.js "$node_version$NC + fi +done +echo -e $GREEN"Tests passed for Node.js version 14 and 16"$NC +exit 0 diff --git a/docker/ubuntu/Dockerfile b/docker/ubuntu/Dockerfile index f5aecff..6524b1a 100644 --- a/docker/ubuntu/Dockerfile +++ b/docker/ubuntu/Dockerfile @@ -4,13 +4,14 @@ FROM ubuntu:bionic RUN apt-get update # Install misc -RUN apt-get install -y wget git vim sudo make +RUN apt-get install -y wget git make curl clang pkg-config # Install compilation tools -RUN apt-get install -y python3 g++ make -#RUN rm -f /usr/bin/python && ln -s /usr/bin/python3 /usr/bin/python +RUN curl https://sh.rustup.rs -sSf | bash -s -- -y --profile minimal -# Install Kerberos libs +RUN echo 'source $HOME/.cargo/env' >> /etc/profile + +# Kerberos libs will be built there RUN apt-get install -y libkrb5-dev # Install Node.js via n @@ -21,5 +22,11 @@ RUN n 14 RUN n 16 RUN mkdir -p /node-krb5 -COPY ./run.sh /run.sh +WORKDIR /node-krb5 + + +COPY . . + +# Context is node-krb5 folder +COPY docker/ubuntu/run.sh /run.sh ENTRYPOINT ["/run.sh"] diff --git a/docker/ubuntu/run.sh b/docker/ubuntu/run.sh index f24bb48..ae7570b 100755 --- a/docker/ubuntu/run.sh +++ b/docker/ubuntu/run.sh @@ -6,19 +6,20 @@ RED='\033[0;31m' GREEN='\033[0;32m' NC='\033[0m' # No Color +source /etc/profile + while [ ! -f /tmp/krb5_test/rest_ready ] do echo "Waiting for REST server to be ready..." sleep 1 done -cd /node-krb5 + cp /tmp/krb5_test/krb5.conf /etc/krb5.conf for node_version in "16" "14"; do echo "Node.js version "$node_version n $node_version - # --unsafe-perm allows node-gyp build as root # --force forces reinstallation - npm install --unsafe-perm --force + npm install --force npm test err=$? if [ $err -ne 0 ] @@ -30,4 +31,4 @@ for node_version in "16" "14"; do fi done echo -e $GREEN"Tests passed for Node.js version 14 and 16"$NC -exit 0 \ No newline at end of file +exit 0 From a76224cf232624e0c11770d43df11ba9e52ab6e9 Mon Sep 17 00:00:00 2001 From: gboutry Date: Tue, 22 Feb 2022 15:26:49 +0100 Subject: [PATCH 09/11] fix: use parameter destructuration --- crates/krb5_js/src/kdestroy.rs | 8 +++++--- crates/krb5_js/src/kinit.rs | 7 ++++--- crates/krb5_js/src/lib.rs | 2 +- crates/krb5_js/src/spnego.rs | 8 +++----- 4 files changed, 13 insertions(+), 12 deletions(-) diff --git a/crates/krb5_js/src/kdestroy.rs b/crates/krb5_js/src/kdestroy.rs index c75f7bd..c336951 100644 --- a/crates/krb5_js/src/kdestroy.rs +++ b/crates/krb5_js/src/kdestroy.rs @@ -10,9 +10,11 @@ pub struct KdestroyParameters { pub ccname: Option, } -pub fn kdestroy_function(options: &mut KdestroyParameters) -> std::result::Result<(), Error> { +pub fn kdestroy_function( + KdestroyParameters { ccname }: &mut KdestroyParameters, +) -> std::result::Result<(), Error> { let context = Context::new()?; - let ccache = match options.ccname { + let ccache = match ccname { Some(ref cc_name) => CCache::resolve(&context, cc_name)?, None => CCache::default(&context)?, }; @@ -33,7 +35,7 @@ impl Task for Kdestroy { fn compute(&mut self) -> napi::Result { kdestroy_function(&mut self.input).map_err(napi::Error::from) } - + fn resolve(&mut self, env: Env, _output: Self::Output) -> napi::Result { env.get_undefined() } diff --git a/crates/krb5_js/src/kinit.rs b/crates/krb5_js/src/kinit.rs index 59071d6..cfc5979 100644 --- a/crates/krb5_js/src/kinit.rs +++ b/crates/krb5_js/src/kinit.rs @@ -17,14 +17,15 @@ pub struct KinitParameters { pub ccname: Option, } -pub fn kinit_function(parameters: &mut KinitParameters) -> Result { - let KinitParameters { +pub fn kinit_function( + KinitParameters { principal, password, keytab, realm, ccname, - } = parameters; + }: &mut KinitParameters, +) -> Result { if password.is_none() && keytab.is_none() { return Err(Error::MissingPasswordOrKeytab); } diff --git a/crates/krb5_js/src/lib.rs b/crates/krb5_js/src/lib.rs index 88d921c..112df73 100644 --- a/crates/krb5_js/src/lib.rs +++ b/crates/krb5_js/src/lib.rs @@ -14,7 +14,7 @@ pub fn kinit(input: KinitParameters) -> AsyncTask { AsyncTask::new(Kinit { input }) } -#[napi(ts_return_type="Promise")] +#[napi(ts_return_type = "Promise")] pub fn kdestroy(input: KdestroyParameters) -> AsyncTask { AsyncTask::new(Kdestroy { input }) } diff --git a/crates/krb5_js/src/spnego.rs b/crates/krb5_js/src/spnego.rs index 6a858ee..300a8c9 100644 --- a/crates/krb5_js/src/spnego.rs +++ b/crates/krb5_js/src/spnego.rs @@ -22,15 +22,13 @@ pub struct GenerateSpnegoTokenParameters { } pub fn generate_spnego_token_function( - options: &mut GenerateSpnegoTokenParameters, -) -> std::result::Result { - let GenerateSpnegoTokenParameters { + GenerateSpnegoTokenParameters { service_principal, service_fqdn, hostbased_service, ccname, - } = options; - + }: &mut GenerateSpnegoTokenParameters, +) -> std::result::Result { let (service_principal, input_name_type) = if let Some(service) = service_principal { (service.to_owned(), "GSS_C_NT_USER_NAME") } else { From 2fa5740dd0778071e56868be11bc1f6d472e3407 Mon Sep 17 00:00:00 2001 From: gboutry Date: Thu, 24 Feb 2022 09:23:27 +0100 Subject: [PATCH 10/11] fix: remove unchecked unwrap, rename _err to _ --- crates/krb5_rs/src/ccache.rs | 2 +- crates/krb5_rs/src/credentials.rs | 2 +- crates/krb5_rs/src/gssapi.rs | 2 +- crates/krb5_rs/src/keytab.rs | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/krb5_rs/src/ccache.rs b/crates/krb5_rs/src/ccache.rs index 28bca83..9aaa37c 100644 --- a/crates/krb5_rs/src/ccache.rs +++ b/crates/krb5_rs/src/ccache.rs @@ -30,7 +30,7 @@ impl<'a> CCache<'a> { pub fn resolve(context: &'a Context, cc_name: &str) -> Result> { let mut ccache: MaybeUninit = MaybeUninit::uninit(); - let cc_name = CString::new(cc_name).unwrap(); + let cc_name = CString::new(cc_name).map_err(|_| Krb5Error::StringConversionError)?; let error_code = unsafe { krb5_cc_resolve(context.inner, cc_name.as_ptr(), ccache.as_mut_ptr()) }; diff --git a/crates/krb5_rs/src/credentials.rs b/crates/krb5_rs/src/credentials.rs index 8c5d8d8..f5d90d0 100644 --- a/crates/krb5_rs/src/credentials.rs +++ b/crates/krb5_rs/src/credentials.rs @@ -18,7 +18,7 @@ impl<'a> Credentials<'a> { password: &str, ) -> Result> { let mut credentials: MaybeUninit = MaybeUninit::uninit(); - let password = CString::new(password).map_err(|_err| Krb5Error::StringConversionError)?; + let password = CString::new(password).map_err(|_| Krb5Error::StringConversionError)?; let error_code = unsafe { krb5_get_init_creds_password( context.inner, diff --git a/crates/krb5_rs/src/gssapi.rs b/crates/krb5_rs/src/gssapi.rs index a330970..aa5cd9f 100644 --- a/crates/krb5_rs/src/gssapi.rs +++ b/crates/krb5_rs/src/gssapi.rs @@ -58,7 +58,7 @@ pub fn import_name(principal: &str, input_name_type: &str) -> Result Result<(), String> { let mut minor = 0; let ccache_name = - CString::new(ccache_name).map_err(|_err| "Failed to convert ccache name to a c string")?; + CString::new(ccache_name).map_err(|_| "Failed to convert ccache name to a c string")?; let error_code = unsafe { gss_krb5_ccache_name(&mut minor, ccache_name.as_ptr(), std::ptr::null_mut()) }; diff --git a/crates/krb5_rs/src/keytab.rs b/crates/krb5_rs/src/keytab.rs index 9dfe38f..041fd79 100644 --- a/crates/krb5_rs/src/keytab.rs +++ b/crates/krb5_rs/src/keytab.rs @@ -14,7 +14,7 @@ pub struct Keytab<'a> { impl<'a> Keytab<'a> { pub fn resolve(context: &'a Context, name: &str) -> Result> { let mut keytab: MaybeUninit = MaybeUninit::uninit(); - let name = CString::new(name).unwrap(); + let name = CString::new(name).map_err(|_| Krb5Error::StringConversionError)?; let error_code = unsafe { krb5_kt_resolve(context.inner, name.as_ptr(), keytab.as_mut_ptr()) }; Krb5Error::exit_if_library_error(context, error_code)?; From b5d24eb9e9aa368507764197334b2ff7e8c4b23b Mon Sep 17 00:00:00 2001 From: gboutry Date: Sun, 22 Jan 2023 12:52:57 +0100 Subject: [PATCH 11/11] feat: upgrade dependencies --- Cargo.lock | 298 ++++++++---------------------- crates/krb5_js/Cargo.toml | 2 +- crates/krb5_js/src/kinit.rs | 2 +- crates/krb5_js/src/spnego.rs | 3 +- crates/krb5_rs/Cargo.toml | 1 - crates/krb5_rs/src/ccache.rs | 12 +- crates/krb5_rs/src/context.rs | 25 +-- crates/krb5_rs/src/credentials.rs | 16 +- crates/krb5_rs/src/gssapi.rs | 18 +- crates/krb5_rs/src/keytab.rs | 10 +- crates/krb5_rs/src/principal.rs | 14 +- crates/krb5_sys/Cargo.toml | 4 +- crates/krb5_sys/src/wrapper.c | 6 + crates/krb5_sys/src/wrapper.h | 21 +-- lib/index.d.ts | 18 +- src/index.coffee | 4 +- 16 files changed, 146 insertions(+), 308 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 61a7268..c4bbf3c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,50 +4,28 @@ version = 3 [[package]] name = "aho-corasick" -version = "0.7.18" +version = "0.7.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" +checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac" dependencies = [ "memchr", ] -[[package]] -name = "ansi_term" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" -dependencies = [ - "winapi", -] - -[[package]] -name = "atty" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" -dependencies = [ - "hermit-abi", - "libc", - "winapi", -] - [[package]] name = "base64" -version = "0.13.0" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" +checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a" [[package]] name = "bindgen" -version = "0.59.2" +version = "0.63.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bd2a9a458e8f4304c52c43ebb0cfbd520289f8379a52e329a38afda99bf8eb8" +checksum = "36d860121800b2a9a94f9b5604b332d5cffb234ce17609ea479d723dbc9d3885" dependencies = [ "bitflags", "cexpr", "clang-sys", - "clap", - "env_logger", "lazy_static", "lazycell", "log", @@ -57,6 +35,7 @@ dependencies = [ "regex", "rustc-hash", "shlex", + "syn", "which", ] @@ -68,9 +47,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "cc" -version = "1.0.73" +version = "1.0.78" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" +checksum = "a20104e2335ce8a659d6dd92a51a767a0c062599c73b343fd152cb401e828c3d" [[package]] name = "cexpr" @@ -89,9 +68,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clang-sys" -version = "1.3.1" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cc00842eed744b858222c4c9faf7243aafc6d33f92f96935263ef4d8a41ce21" +checksum = "fa2e27ae6ab525c3d369ded447057bca5438d86dc3a68f6faafb8269ba82ebf3" dependencies = [ "glob", "libc", @@ -99,31 +78,19 @@ dependencies = [ ] [[package]] -name = "clap" -version = "2.34.0" +name = "convert_case" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" +checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca" dependencies = [ - "ansi_term", - "atty", - "bitflags", - "strsim", - "textwrap", - "unicode-width", - "vec_map", + "unicode-segmentation", ] -[[package]] -name = "convert_case" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb4a24b1aaf0fd0ce8b45161144d6f42cd91677fd5940fd431183eb023b3a2b8" - [[package]] name = "ctor" -version = "0.1.21" +version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccc0a48a9b826acdf4028595adc9db92caea352f7af011a3034acd172a52a0aa" +checksum = "6d2301688392eb071b0bf1a37be05c469d3cc4dbbd95df672fe28ab021e6a096" dependencies = [ "quote", "syn", @@ -131,43 +98,15 @@ dependencies = [ [[package]] name = "either" -version = "1.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" - -[[package]] -name = "env_logger" -version = "0.9.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b2cf0344971ee6c64c31be0d530793fba457d322dfec2810c453d0ef228f9c3" -dependencies = [ - "atty", - "humantime", - "log", - "regex", - "termcolor", -] +checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797" [[package]] name = "glob" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" - -[[package]] -name = "hermit-abi" -version = "0.1.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" -dependencies = [ - "libc", -] - -[[package]] -name = "humantime" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" [[package]] name = "krb5-js" @@ -188,7 +127,6 @@ name = "krb5-rs" version = "0.1.0" dependencies = [ "krb5-sys", - "lazy_static", "thiserror", ] @@ -216,15 +154,15 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.117" +version = "0.2.139" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e74d72e0f9b65b5b4ca49a346af3976df0f9c61d550727f349ecd559f251a26c" +checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" [[package]] name = "libloading" -version = "0.7.3" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efbc0f03f9a775e9f6aed295c6a1ba2253c5757a9e03d55c6caa46a681abcddd" +checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" dependencies = [ "cfg-if", "winapi", @@ -232,18 +170,18 @@ dependencies = [ [[package]] name = "log" -version = "0.4.14" +version = "0.4.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" +checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" dependencies = [ "cfg-if", ] [[package]] name = "memchr" -version = "2.4.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" [[package]] name = "minimal-lexical" @@ -253,14 +191,15 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "napi" -version = "2.1.0" +version = "2.10.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17ec66e60f000c78dd7c6215b6fa260e0591e09805024332bc5b3f55acc12244" +checksum = "b60e2bc632f89dad5d47da61591bd468f87f5dbfc85af27aa876772c89d8c4e4" dependencies = [ + "bitflags", "ctor", - "lazy_static", "napi-sys", - "windows", + "once_cell", + "thread_local", ] [[package]] @@ -271,9 +210,9 @@ checksum = "ebd4419172727423cf30351406c54f6cc1b354a2cfb4f1dba3e6cd07f6d5522b" [[package]] name = "napi-derive" -version = "2.1.0" +version = "2.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74ac5287a5e94a8728fc82d16c5127acc5eb5b8ad6404ef5f82d6a4ce8d5bdd2" +checksum = "47bff5a8ed70117bce55053a74ff8f423f90c48c704030609272e6e3bde1c5a4" dependencies = [ "convert_case", "napi-derive-backend", @@ -284,9 +223,9 @@ dependencies = [ [[package]] name = "napi-derive-backend" -version = "1.0.25" +version = "1.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "427f4f04525635cdf22005d1be62d6d671bcb5550d694a1efb480a315422b4af" +checksum = "e13b412301aeebee17724fff6d73536e9ecb8387f10bbbf317a9f7a006cc1c5b" dependencies = [ "convert_case", "once_cell", @@ -298,26 +237,28 @@ dependencies = [ [[package]] name = "napi-sys" -version = "2.1.0" +version = "2.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a385494dac3c52cbcacb393bb3b42669e7db8ab240c7ad5115f549eb061f2cc" +checksum = "166b5ef52a3ab5575047a9fe8d4a030cdd0f63c96f071cd6907674453b07bae3" +dependencies = [ + "libloading", +] [[package]] name = "nom" -version = "7.1.0" +version = "7.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b1d11e1ef389c76fe5b81bcaf2ea32cf88b62bc494e19f493d0b30e7a930109" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" dependencies = [ "memchr", "minimal-lexical", - "version_check", ] [[package]] name = "once_cell" -version = "1.9.0" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da32515d9f6e6e489d7bc9d84c71b060db7247dc035bbe44eac88cf87486d8d5" +checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66" [[package]] name = "peeking_take_while" @@ -327,33 +268,33 @@ checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" [[package]] name = "pkg-config" -version = "0.3.24" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58893f751c9b0412871a09abd62ecd2a00298c6c83befa223ef98c52aef40cbe" +checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" [[package]] name = "proc-macro2" -version = "1.0.36" +version = "1.0.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029" +checksum = "6ef7d57beacfaf2d8aee5937dab7b7f28de3cb8b1828479bb5de2a7106f2bae2" dependencies = [ - "unicode-xid", + "unicode-ident", ] [[package]] name = "quote" -version = "1.0.15" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "864d3e96a899863136fc6e99f3d7cae289dafe43bf2c5ac19b70df7210c0a145" +checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b" dependencies = [ "proc-macro2", ] [[package]] name = "regex" -version = "1.5.4" +version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461" +checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733" dependencies = [ "aho-corasick", "memchr", @@ -362,9 +303,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.6.25" +version = "0.6.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" +checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" [[package]] name = "rustc-hash" @@ -378,55 +319,31 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43b2853a4d09f215c24cc5489c992ce46052d359b5109343cbafbf26bc62f8a3" -[[package]] -name = "strsim" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" - [[package]] name = "syn" -version = "1.0.86" +version = "1.0.107" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a65b3f4ffa0092e9887669db0eae07941f023991ab58ea44da8fe8e2d511c6b" +checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5" dependencies = [ "proc-macro2", "quote", - "unicode-xid", -] - -[[package]] -name = "termcolor" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4" -dependencies = [ - "winapi-util", -] - -[[package]] -name = "textwrap" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" -dependencies = [ - "unicode-width", + "unicode-ident", ] [[package]] name = "thiserror" -version = "1.0.30" +version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417" +checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.30" +version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b" +checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f" dependencies = [ "proc-macro2", "quote", @@ -434,38 +351,35 @@ dependencies = [ ] [[package]] -name = "unicode-width" -version = "0.1.9" +name = "thread_local" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973" - -[[package]] -name = "unicode-xid" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" +checksum = "5516c27b78311c50bf42c071425c560ac799b11c30b31f87e3081965fe5e0180" +dependencies = [ + "once_cell", +] [[package]] -name = "vec_map" -version = "0.8.2" +name = "unicode-ident" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" +checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" [[package]] -name = "version_check" -version = "0.9.4" +name = "unicode-segmentation" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +checksum = "0fdbf052a0783de01e944a6ce7a8cb939e295b1e7be835a1112c3b9a7f047a5a" [[package]] name = "which" -version = "4.2.4" +version = "4.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a5a7e487e921cf220206864a94a89b6c6905bfc19f1057fa26a4cb360e5c1d2" +checksum = "2441c784c52b289a054b7201fc93253e288f094e2f4be9058343127c4226a269" dependencies = [ "either", - "lazy_static", "libc", + "once_cell", ] [[package]] @@ -484,64 +398,12 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" -[[package]] -name = "winapi-util" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" -dependencies = [ - "winapi", -] - [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -[[package]] -name = "windows" -version = "0.30.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b749ebd2304aa012c5992d11a25d07b406bdbe5f79d371cb7a918ce501a19eb0" -dependencies = [ - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_msvc", -] - -[[package]] -name = "windows_aarch64_msvc" -version = "0.30.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29277a4435d642f775f63c7d1faeb927adba532886ce0287bd985bffb16b6bca" - -[[package]] -name = "windows_i686_gnu" -version = "0.30.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1145e1989da93956c68d1864f32fb97c8f561a8f89a5125f6a2b7ea75524e4b8" - -[[package]] -name = "windows_i686_msvc" -version = "0.30.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4a09e3a0d4753b73019db171c1339cd4362c8c44baf1bcea336235e955954a6" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.30.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ca64fcb0220d58db4c119e050e7af03c69e6f4f415ef69ec1773d9aab422d5a" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.30.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08cabc9f0066848fef4bc6a1c1668e6efce38b661d2aeec75d18d8617eebb5f1" - [[package]] name = "winreg" version = "0.10.1" diff --git a/crates/krb5_js/Cargo.toml b/crates/krb5_js/Cargo.toml index b1625df..12c3c86 100644 --- a/crates/krb5_js/Cargo.toml +++ b/crates/krb5_js/Cargo.toml @@ -16,6 +16,6 @@ krb5-rs = { path = "../krb5_rs" } thiserror = "1" napi = { version = "2", default-features = false, features = ["napi6"] } napi-derive = "2" -base64 = "0.13" +base64 = "0.21" regex = "1" lazy_static = "1.4" diff --git a/crates/krb5_js/src/kinit.rs b/crates/krb5_js/src/kinit.rs index cfc5979..b5f6a81 100644 --- a/crates/krb5_js/src/kinit.rs +++ b/crates/krb5_js/src/kinit.rs @@ -42,7 +42,7 @@ pub fn kinit_function( (principal.as_str(), None) }; let context = Context::new()?; - let realm = match realm_from_split.or_else(|| realm.as_deref()) { + let realm = match realm_from_split.or(realm.as_deref()) { Some(realm) => realm.to_owned(), None => context.get_default_realm()?, }; diff --git a/crates/krb5_js/src/spnego.rs b/crates/krb5_js/src/spnego.rs index 300a8c9..83e1c82 100644 --- a/crates/krb5_js/src/spnego.rs +++ b/crates/krb5_js/src/spnego.rs @@ -1,3 +1,4 @@ +use base64::{engine, Engine}; use krb5_rs::gssapi; use napi::{Env, JsString, Task}; use napi_derive::napi; @@ -52,7 +53,7 @@ pub fn generate_spnego_token_function( gssapi::krb5_ccache_name(ccname.as_ref().unwrap())?; } let token = gssapi::get_token(target_name)?; - Ok(base64::encode(token)) + Ok(engine::general_purpose::STANDARD.encode(token)) } pub struct Spnego { diff --git a/crates/krb5_rs/Cargo.toml b/crates/krb5_rs/Cargo.toml index e08f7a4..0bc4550 100644 --- a/crates/krb5_rs/Cargo.toml +++ b/crates/krb5_rs/Cargo.toml @@ -6,6 +6,5 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -lazy_static = "1.4" thiserror = "1" krb5-sys = {path = "../krb5_sys"} diff --git a/crates/krb5_rs/src/ccache.rs b/crates/krb5_rs/src/ccache.rs index 9aaa37c..f1e873b 100644 --- a/crates/krb5_rs/src/ccache.rs +++ b/crates/krb5_rs/src/ccache.rs @@ -10,13 +10,13 @@ use krb5_sys::{ use super::{Context, Credentials, Krb5Error, Principal, Result}; -pub struct CCache<'a> { - pub(crate) context: &'a Context, +pub struct CCache<'context> { + pub(crate) context: &'context Context, pub(crate) inner: krb5_ccache, } -impl<'a> CCache<'a> { - pub fn default(context: &'a Context) -> Result { +impl<'context> CCache<'context> { + pub fn default(context: &'context Context) -> Result { let mut ccache: MaybeUninit = MaybeUninit::uninit(); let error_code = unsafe { krb5_cc_default(context.inner, ccache.as_mut_ptr()) }; Krb5Error::exit_if_library_error(context, error_code)?; @@ -27,7 +27,7 @@ impl<'a> CCache<'a> { }) } - pub fn resolve(context: &'a Context, cc_name: &str) -> Result> { + pub fn resolve(context: &'context Context, cc_name: &str) -> Result> { let mut ccache: MaybeUninit = MaybeUninit::uninit(); let cc_name = CString::new(cc_name).map_err(|_| Krb5Error::StringConversionError)?; @@ -77,7 +77,7 @@ impl<'a> CCache<'a> { } } -impl<'a> Drop for CCache<'a> { +impl<'context> Drop for CCache<'context> { fn drop(&mut self) { if !self.context.inner.is_null() && !self.inner.is_null() { unsafe { krb5_cc_close(self.context.inner, self.inner) }; diff --git a/crates/krb5_rs/src/context.rs b/crates/krb5_rs/src/context.rs index 75d5126..830fd10 100644 --- a/crates/krb5_rs/src/context.rs +++ b/crates/krb5_rs/src/context.rs @@ -1,4 +1,3 @@ -// use std::ops::{Deref, DerefMut}; use std::{ffi::CStr, mem::MaybeUninit, os::raw::c_char, sync::Mutex}; use crate::Result; @@ -7,32 +6,14 @@ use krb5_sys::{ krb5_free_error_message, krb5_get_default_realm, krb5_get_error_message, krb5_init_context, }; -use lazy_static::lazy_static; - use super::Krb5Error; -lazy_static! { - static ref CONTEXT_LOCK: Mutex<()> = Mutex::new(()); -} +static CONTEXT_LOCK: Mutex<()> = Mutex::new(()); pub struct Context { pub(crate) inner: krb5_context, } -// impl Deref for Context { -// type Target = krb5_context; - -// fn deref(&self) -> &Self::Target { -// &self.inner -// } -// } - -// impl DerefMut for Context { -// fn deref_mut(&mut self) -> &mut Self::Target { -// &mut self.inner -// } -// } - impl Context { pub fn get_default_realm(&self) -> Result { let mut default_realm: MaybeUninit<*mut c_char> = MaybeUninit::zeroed(); @@ -73,7 +54,7 @@ impl Context { } pub fn new() -> Result { - let _guard = CONTEXT_LOCK.lock().expect("Failed to lock context"); + let _guard = &CONTEXT_LOCK.lock().expect("Failed to lock context"); let mut krb5_context: MaybeUninit = MaybeUninit::uninit(); let error_code = unsafe { krb5_init_context(krb5_context.as_mut_ptr()) }; if error_code != 0 { @@ -89,7 +70,7 @@ impl Context { impl Drop for Context { fn drop(&mut self) { - let _guard = CONTEXT_LOCK.lock().expect("Failed to lock context"); + let _guard = &CONTEXT_LOCK.lock().expect("Failed to lock context"); if !self.inner.is_null() { unsafe { krb5_free_context(self.inner) } } diff --git a/crates/krb5_rs/src/credentials.rs b/crates/krb5_rs/src/credentials.rs index f5d90d0..6e1f274 100644 --- a/crates/krb5_rs/src/credentials.rs +++ b/crates/krb5_rs/src/credentials.rs @@ -6,17 +6,17 @@ use krb5_sys::{ use crate::{Context, Keytab, Krb5Error, Principal, Result}; -pub struct Credentials<'a> { - pub(crate) context: &'a Context, +pub struct Credentials<'context> { + pub(crate) context: &'context Context, pub(crate) inner: krb5_creds, } -impl<'a> Credentials<'a> { +impl<'context> Credentials<'context> { pub fn get_init_credentials_password( - context: &'a Context, + context: &'context Context, principal: &Principal, password: &str, - ) -> Result> { + ) -> Result> { let mut credentials: MaybeUninit = MaybeUninit::uninit(); let password = CString::new(password).map_err(|_| Krb5Error::StringConversionError)?; let error_code = unsafe { @@ -41,10 +41,10 @@ impl<'a> Credentials<'a> { } pub fn get_init_credentials_keytab( - context: &'a Context, + context: &'context Context, principal: &Principal, keytab: &Keytab, - ) -> Result> { + ) -> Result> { let mut credentials: MaybeUninit = MaybeUninit::uninit(); let error_code = unsafe { krb5_get_init_creds_keytab( @@ -66,7 +66,7 @@ impl<'a> Credentials<'a> { } } -impl<'a> Drop for Credentials<'a> { +impl<'context> Drop for Credentials<'context> { fn drop(&mut self) { if !self.context.inner.is_null() { unsafe { krb5_free_cred_contents(self.context.inner, &mut self.inner) } diff --git a/crates/krb5_rs/src/gssapi.rs b/crates/krb5_rs/src/gssapi.rs index aa5cd9f..b776aab 100644 --- a/crates/krb5_rs/src/gssapi.rs +++ b/crates/krb5_rs/src/gssapi.rs @@ -11,7 +11,7 @@ use krb5_sys::{ gss_release_name, OM_uint32, GSS_C_NO_CHANNEL_BINDINGS, GSS_C_NO_CONTEXT, GSS_C_NO_CREDENTIAL, }; -const MAX_AD_TOKEN_SIZE_BEFORE_B64: u64 = 48000; +const MAX_AD_TOKEN_SIZE_BEFORE_B64: usize = 48000; const GSS_C_REPLAY_FLAG: OM_uint32 = 4; const GSS_C_SEQUENCE_FLAG: OM_uint32 = 8; @@ -33,7 +33,7 @@ impl Drop for Name { pub fn import_name(principal: &str, input_name_type: &str) -> Result { let mut minor = 0; let mut service = gss_buffer_desc { - length: principal.len() as u64, + length: principal.len(), value: principal.as_ptr() as *mut _, }; let gss_oid = unsafe { @@ -103,8 +103,7 @@ pub fn get_token(target_name: Name) -> Result, String> { "The token returned by GSS is greater than the size allowed by Windows AD", )) } else { - // try_into().unwrap() won't fail, because output_token.length cannot be higher than MAX_AD_TOKEN_SIZE_BEFORE_B64 - let size = output_token.length.try_into().unwrap(); + let size = output_token.length; let mut vec: Vec = Vec::with_capacity(size); unsafe { std::ptr::copy(output_token.value, vec.as_mut_ptr() as *mut _, size); @@ -148,15 +147,8 @@ fn convert_gss_error(error_code: OM_uint32, minor: OM_uint32) -> String { &mut status_string, ) }; - let slice_from_data: &[u8] = unsafe { - slice::from_raw_parts( - status_string.value as *mut _, - status_string - .length - .try_into() - .expect("Failed to convert status_string.length to usize"), - ) - }; + let slice_from_data: &[u8] = + unsafe { slice::from_raw_parts(status_string.value as *mut _, status_string.length) }; error_msg.extend_from_slice(slice_from_data); unsafe { gss_release_buffer(&mut min_status, &mut status_string) }; diff --git a/crates/krb5_rs/src/keytab.rs b/crates/krb5_rs/src/keytab.rs index 041fd79..d29dedb 100644 --- a/crates/krb5_rs/src/keytab.rs +++ b/crates/krb5_rs/src/keytab.rs @@ -6,13 +6,13 @@ use krb5_sys::{krb5_keytab, krb5_kt_close, krb5_kt_resolve}; use super::Krb5Error; -pub struct Keytab<'a> { - pub(crate) context: &'a Context, +pub struct Keytab<'context> { + pub(crate) context: &'context Context, pub(crate) inner: krb5_keytab, } -impl<'a> Keytab<'a> { - pub fn resolve(context: &'a Context, name: &str) -> Result> { +impl<'context> Keytab<'context> { + pub fn resolve(context: &'context Context, name: &str) -> Result> { let mut keytab: MaybeUninit = MaybeUninit::uninit(); let name = CString::new(name).map_err(|_| Krb5Error::StringConversionError)?; let error_code = @@ -25,7 +25,7 @@ impl<'a> Keytab<'a> { } } -impl<'a> Drop for Keytab<'a> { +impl<'context> Drop for Keytab<'context> { fn drop(&mut self) { if !self.context.inner.is_null() && !self.inner.is_null() { unsafe { krb5_kt_close(self.context.inner, self.inner) }; diff --git a/crates/krb5_rs/src/principal.rs b/crates/krb5_rs/src/principal.rs index 6ddc506..7d3fdb9 100644 --- a/crates/krb5_rs/src/principal.rs +++ b/crates/krb5_rs/src/principal.rs @@ -3,13 +3,17 @@ use std::{mem::MaybeUninit, os::raw::c_uint}; use crate::{Context, Krb5Error, Result}; use krb5_sys::{krb5_build_principal_ext, krb5_free_principal, krb5_principal}; -pub struct Principal<'a> { - pub(crate) context: &'a Context, +pub struct Principal<'context> { + pub(crate) context: &'context Context, pub(crate) inner: krb5_principal, } -impl<'a> Principal<'a> { - pub fn build_principal(context: &'a Context, realm: &str, user: &str) -> Result> { +impl<'context> Principal<'context> { + pub fn build_principal( + context: &'context Context, + realm: &str, + user: &str, + ) -> Result> { let mut krb5_principal: MaybeUninit = MaybeUninit::uninit(); let sp: Vec<&str> = user.split('/').collect(); let error_code = match sp.len() { @@ -52,7 +56,7 @@ impl<'a> Principal<'a> { } } -impl<'a> Drop for Principal<'a> { +impl<'context> Drop for Principal<'context> { fn drop(&mut self) { if !self.context.inner.is_null() && !self.inner.is_null() { unsafe { krb5_free_principal(self.context.inner, self.inner) } diff --git a/crates/krb5_sys/Cargo.toml b/crates/krb5_sys/Cargo.toml index 2bc7c05..a246e4a 100644 --- a/crates/krb5_sys/Cargo.toml +++ b/crates/krb5_sys/Cargo.toml @@ -8,11 +8,11 @@ links = "krb5" [build-dependencies] -bindgen = "0.59.2" +bindgen = "0.63.0" cc = "1.0" [target.'cfg(not(windows))'.build-dependencies] -pkg-config = "0.3.24" +pkg-config = "0.3" [target.'cfg(any(windows))'.build-dependencies] winreg = { version = "0.10" } diff --git a/crates/krb5_sys/src/wrapper.c b/crates/krb5_sys/src/wrapper.c index 01532ef..21a77ef 100644 --- a/crates/krb5_sys/src/wrapper.c +++ b/crates/krb5_sys/src/wrapper.c @@ -1 +1,7 @@ #include "wrapper.h" + +OM_uint32 gss_error(OM_uint32 x) { return GSS_ERROR(x); } + +gss_OID gss_c_nt_hostbased_service() { return GSS_C_NT_HOSTBASED_SERVICE; } + +gss_OID gss_c_nt_user_name() { return GSS_C_NT_USER_NAME; } diff --git a/crates/krb5_sys/src/wrapper.h b/crates/krb5_sys/src/wrapper.h index 0f3b825..95d7d13 100644 --- a/crates/krb5_sys/src/wrapper.h +++ b/crates/krb5_sys/src/wrapper.h @@ -1,24 +1,19 @@ -#include #include #include +#include #undef GSS_C_NO_CREDENTIAL -const gss_cred_id_t GSS_C_NO_CREDENTIAL = ((gss_cred_id_t) 0); +const gss_cred_id_t GSS_C_NO_CREDENTIAL = ((gss_cred_id_t)0); #undef GSS_C_NO_CHANNEL_BINDINGS -const gss_channel_bindings_t GSS_C_NO_CHANNEL_BINDINGS = ((gss_channel_bindings_t) 0); +const gss_channel_bindings_t GSS_C_NO_CHANNEL_BINDINGS = + ((gss_channel_bindings_t)0); #undef GSS_C_NO_CONTEXT -const gss_ctx_id_t GSS_C_NO_CONTEXT = ((gss_ctx_id_t) 0); +const gss_ctx_id_t GSS_C_NO_CONTEXT = ((gss_ctx_id_t)0); -OM_uint32 gss_error(OM_uint32 x) { - return GSS_ERROR(x); -} +OM_uint32 gss_error(OM_uint32 x); -gss_OID gss_c_nt_hostbased_service() { - return GSS_C_NT_HOSTBASED_SERVICE; -} +gss_OID gss_c_nt_hostbased_service(); -gss_OID gss_c_nt_user_name() { - return GSS_C_NT_USER_NAME; -} +gss_OID gss_c_nt_user_name(); diff --git a/lib/index.d.ts b/lib/index.d.ts index c685283..279ee7c 100644 --- a/lib/index.d.ts +++ b/lib/index.d.ts @@ -4,20 +4,20 @@ /* auto-generated by NAPI-RS */ export interface KdestroyParameters { - ccname?: string | undefined | null + ccname?: string } export interface KinitParameters { principal: string - password?: string | undefined | null - keytab?: string | undefined | null - realm?: string | undefined | null - ccname?: string | undefined | null + password?: string + keytab?: string + realm?: string + ccname?: string } export interface GenerateSpnegoTokenParameters { - service_principal?: string | undefined | null - service_fqdn?: string | undefined | null - hostbased_service?: string | undefined | null - ccname?: string | undefined | null + service_principal?: string + service_fqdn?: string + hostbased_service?: string + ccname?: string } export function kinit(input: KinitParameters): Promise export function kdestroy(input: KdestroyParameters): Promise diff --git a/src/index.coffee b/src/index.coffee index 6a2ab6a..d7c468b 100644 --- a/src/index.coffee +++ b/src/index.coffee @@ -8,7 +8,6 @@ module.exports = .catch (err) -> callback err return - spnego: (options, callback) -> return krb5.spnego options unless callback krb5.spnego options @@ -22,9 +21,8 @@ module.exports = if typeof options is 'function' callback = options options = {} - + krb5.kdestroy options .then () -> callback() .catch (err) -> callback err return -