You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

991 lines
34 KiB

#include "kerberos.h"
#include <stdlib.h>
#include <errno.h>
#include "worker.h"
#include "kerberos_context.h"
#ifndef ARRAY_SIZE
# define ARRAY_SIZE(a) (sizeof((a)) / sizeof((a)[0]))
#endif
void die(const char *message) {
if(errno) {
perror(message);
} else {
printf("ERROR: %s\n", message);
}
exit(1);
}
// Call structs
typedef struct AuthGSSClientCall {
uint32_t flags;
char *uri;
char *credentials_cache;
} AuthGSSClientCall;
typedef struct AuthGSSClientStepCall {
KerberosContext *context;
char *challenge;
} AuthGSSClientStepCall;
typedef struct AuthGSSClientUnwrapCall {
KerberosContext *context;
char *challenge;
} AuthGSSClientUnwrapCall;
typedef struct AuthGSSClientWrapCall {
KerberosContext *context;
char *challenge;
char *user_name;
} AuthGSSClientWrapCall;
typedef struct AuthGSSClientCleanCall {
KerberosContext *context;
} AuthGSSClientCleanCall;
typedef struct AuthGSSServerInitCall {
char *service;
bool constrained_delegation;
char *username;
} AuthGSSServerInitCall;
typedef struct AuthGSSServerCleanCall {
KerberosContext *context;
} AuthGSSServerCleanCall;
typedef struct AuthGSSServerStepCall {
KerberosContext *context;
char *auth_data;
} AuthGSSServerStepCall;
typedef struct AuthUserKrb5PasswordCall {
char *username;
char *password;
char *service;
} AuthUserKrb5PasswordCall;
Kerberos::Kerberos() : Nan::ObjectWrap() {
}
Nan::Persistent<FunctionTemplate> Kerberos::constructor_template;
void Kerberos::Initialize(Nan::ADDON_REGISTER_FUNCTION_ARGS_TYPE target) {
// Grab the scope of the call from Node
Nan::HandleScope scope;
// Define a new function template
Local<FunctionTemplate> t = Nan::New<FunctionTemplate>(New);
t->InstanceTemplate()->SetInternalFieldCount(1);
t->SetClassName(Nan::New<String>("Kerberos").ToLocalChecked());
// Set up method for the Kerberos instance
Nan::SetPrototypeMethod(t, "authGSSClientInit", AuthGSSClientInit);
Nan::SetPrototypeMethod(t, "authGSSClientStep", AuthGSSClientStep);
Nan::SetPrototypeMethod(t, "authGSSClientUnwrap", AuthGSSClientUnwrap);
Nan::SetPrototypeMethod(t, "authGSSClientWrap", AuthGSSClientWrap);
Nan::SetPrototypeMethod(t, "authGSSClientClean", AuthGSSClientClean);
Nan::SetPrototypeMethod(t, "authGSSServerInit", AuthGSSServerInit);
Nan::SetPrototypeMethod(t, "authGSSServerClean", AuthGSSServerClean);
Nan::SetPrototypeMethod(t, "authGSSServerStep", AuthGSSServerStep);
Nan::SetPrototypeMethod(t, "authUserKrb5Password", AuthUserKrb5Password);
constructor_template.Reset(t);
// Set the symbol
target->Set(Nan::New<String>("Kerberos").ToLocalChecked(), t->GetFunction());
}
NAN_METHOD(Kerberos::New) {
// Create a Kerberos instance
Kerberos *kerberos = new Kerberos();
// Return the kerberos object
kerberos->Wrap(info.This());
info.GetReturnValue().Set(info.This());
}
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// authGSSClientInit
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
static void _authGSSClientInit(Worker *worker) {
gss_client_state *state;
gss_client_response *response;
// Allocate state
state = (gss_client_state *)malloc(sizeof(gss_client_state));
if(state == NULL) die("Memory allocation failed");
// Unpack the parameter data struct
AuthGSSClientCall *call = (AuthGSSClientCall *)worker->parameters;
// Start the kerberos client
response = authenticate_gss_client_init(call->uri, call->flags, call->credentials_cache, state);
// Release the parameter struct memory
free(call->uri);
free(call->credentials_cache);
free(call);
// If we have an error mark worker as having had an error
if(response->return_code == AUTH_GSS_ERROR) {
worker->error = TRUE;
worker->error_code = response->return_code;
worker->error_message = response->message;
free(state);
} else {
worker->return_value = state;
}
// Free structure
free(response);
}
static Local<Value> _map_authGSSClientInit(Worker *worker) {
KerberosContext *context = KerberosContext::New();
context->state = (gss_client_state *)worker->return_value;
return context->handle();
}
// Initialize method
NAN_METHOD(Kerberos::AuthGSSClientInit) {
const char *usage = "Requires a service string uri, integer flags, string credentialsCache and a callback function";
// Ensure valid call
if(info.Length() != 4) return Nan::ThrowError(usage);
if(!info[0]->IsString() || !info[1]->IsInt32() || !info[2]->IsString() || !info[3]->IsFunction())
return Nan::ThrowError(usage);
Local<String> service = info[0]->ToString();
// Convert uri string to c-string
char *service_str = (char *)calloc(service->Utf8Length() + 1, sizeof(char));
if(service_str == NULL) die("Memory allocation failed");
// Write v8 string to c-string
service->WriteUtf8(service_str);
Local<String> credentialsCache = info[2]->ToString();
// Convert string to c-string
char *credentials_cache_str = (char *)calloc(credentialsCache->Utf8Length() + 1, sizeof(char));
if(credentials_cache_str == NULL) die("Memory allocation failed");
// Write v8 string to c-string
credentialsCache->WriteUtf8(credentials_cache_str);
// Allocate a structure
AuthGSSClientCall *call = (AuthGSSClientCall *)calloc(1, sizeof(AuthGSSClientCall));
if(call == NULL) die("Memory allocation failed");
call->flags = Nan::To<uint32_t>(info[1]).FromJust();
call->uri = service_str;
call->credentials_cache = credentials_cache_str;
// Unpack the callback
Local<Function> callbackHandle = Local<Function>::Cast(info[3]);
Nan::Callback *callback = new Nan::Callback(callbackHandle);
// Let's allocate some space
Worker *worker = new Worker();
worker->error = false;
worker->request.data = worker;
worker->callback = callback;
worker->parameters = call;
worker->execute = _authGSSClientInit;
worker->mapper = _map_authGSSClientInit;
// Schedule the worker with lib_uv
uv_queue_work(uv_default_loop(), &worker->request, Kerberos::Process, (uv_after_work_cb)Kerberos::After);
// Return no value as it's callback based
info.GetReturnValue().Set(Nan::Undefined());
}
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// authGSSClientStep
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
static void _authGSSClientStep(Worker *worker) {
gss_client_state *state;
gss_client_response *response;
char *challenge;
// Unpack the parameter data struct
AuthGSSClientStepCall *call = (AuthGSSClientStepCall *)worker->parameters;
// Get the state
state = call->context->state;
challenge = call->challenge;
// Check what kind of challenge we have
if(call->challenge == NULL) {
challenge = (char *)"";
}
// Perform authentication step
response = authenticate_gss_client_step(state, challenge);
// If we have an error mark worker as having had an error
if(response->return_code == AUTH_GSS_ERROR) {
worker->error = TRUE;
worker->error_code = response->return_code;
worker->error_message = response->message;
} else {
worker->return_code = response->return_code;
}
// Free up structure
if(call->challenge != NULL) free(call->challenge);
free(call);
free(response);
}
static Local<Value> _map_authGSSClientStep(Worker *worker) {
Nan::HandleScope scope;
// Return the return code
return Nan::New<Int32>(worker->return_code);
}
// Initialize method
NAN_METHOD(Kerberos::AuthGSSClientStep) {
// Ensure valid call
if(info.Length() != 2 && info.Length() != 3) return Nan::ThrowError("Requires a GSS context, optional challenge string and callback function");
if(info.Length() == 2 && (!KerberosContext::HasInstance(info[0]) || !info[1]->IsFunction())) return Nan::ThrowError("Requires a GSS context, optional challenge string and callback function");
if(info.Length() == 3 && (!KerberosContext::HasInstance(info[0]) || !info[1]->IsString() || !info[2]->IsFunction())) return Nan::ThrowError("Requires a GSS context, optional challenge string and callback function");
// Challenge string
char *challenge_str = NULL;
// Let's unpack the parameters
Local<Object> object = info[0]->ToObject();
KerberosContext *kerberos_context = KerberosContext::Unwrap<KerberosContext>(object);
if (!kerberos_context->IsClientInstance()) {
return Nan::ThrowError("GSS context is not a client instance");
}
int callbackArg = 1;
// If we have a challenge string
if(info.Length() == 3) {
// Unpack the challenge string
Local<String> challenge = info[1]->ToString();
// Convert uri string to c-string
challenge_str = (char *)calloc(challenge->Utf8Length() + 1, sizeof(char));
if(challenge_str == NULL) die("Memory allocation failed");
// Write v8 string to c-string
challenge->WriteUtf8(challenge_str);
callbackArg = 2;
}
// Allocate a structure
AuthGSSClientStepCall *call = (AuthGSSClientStepCall *)calloc(1, sizeof(AuthGSSClientStepCall));
if(call == NULL) die("Memory allocation failed");
call->context = kerberos_context;
call->challenge = challenge_str;
// Unpack the callback
Local<Function> callbackHandle = Local<Function>::Cast(info[callbackArg]);
Nan::Callback *callback = new Nan::Callback(callbackHandle);
// Let's allocate some space
Worker *worker = new Worker();
worker->error = false;
worker->request.data = worker;
worker->callback = callback;
worker->parameters = call;
worker->execute = _authGSSClientStep;
worker->mapper = _map_authGSSClientStep;
// Schedule the worker with lib_uv
uv_queue_work(uv_default_loop(), &worker->request, Kerberos::Process, (uv_after_work_cb)Kerberos::After);
// Return no value as it's callback based
info.GetReturnValue().Set(Nan::Undefined());
}
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// authGSSClientUnwrap
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
static void _authGSSClientUnwrap(Worker *worker) {
gss_client_response *response;
char *challenge;
// Unpack the parameter data struct
AuthGSSClientUnwrapCall *call = (AuthGSSClientUnwrapCall *)worker->parameters;
challenge = call->challenge;
// Check what kind of challenge we have
if(call->challenge == NULL) {
challenge = (char *)"";
}
// Perform authentication step
response = authenticate_gss_client_unwrap(call->context->state, challenge);
// If we have an error mark worker as having had an error
if(response->return_code == AUTH_GSS_ERROR) {
worker->error = TRUE;
worker->error_code = response->return_code;
worker->error_message = response->message;
} else {
worker->return_code = response->return_code;
}
// Free up structure
if(call->challenge != NULL) free(call->challenge);
free(call);
free(response);
}
static Local<Value> _map_authGSSClientUnwrap(Worker *worker) {
Nan::HandleScope scope;
// Return the return code
return Nan::New<Int32>(worker->return_code);
}
// Initialize method
NAN_METHOD(Kerberos::AuthGSSClientUnwrap) {
// Ensure valid call
if(info.Length() != 2 && info.Length() != 3) return Nan::ThrowError("Requires a GSS context, optional challenge string and callback function");
if(info.Length() == 2 && (!KerberosContext::HasInstance(info[0]) || !info[1]->IsFunction())) return Nan::ThrowError("Requires a GSS context, optional challenge string and callback function");
if(info.Length() == 3 && (!KerberosContext::HasInstance(info[0]) || !info[1]->IsString() || !info[2]->IsFunction())) return Nan::ThrowError("Requires a GSS context, optional challenge string and callback function");
// Challenge string
char *challenge_str = NULL;
// Let's unpack the parameters
Local<Object> object = info[0]->ToObject();
KerberosContext *kerberos_context = KerberosContext::Unwrap<KerberosContext>(object);
if (!kerberos_context->IsClientInstance()) {
return Nan::ThrowError("GSS context is not a client instance");
}
// If we have a challenge string
if(info.Length() == 3) {
// Unpack the challenge string
Local<String> challenge = info[1]->ToString();
// Convert uri string to c-string
challenge_str = (char *)calloc(challenge->Utf8Length() + 1, sizeof(char));
if(challenge_str == NULL) die("Memory allocation failed");
// Write v8 string to c-string
challenge->WriteUtf8(challenge_str);
}
// Allocate a structure
AuthGSSClientUnwrapCall *call = (AuthGSSClientUnwrapCall *)calloc(1, sizeof(AuthGSSClientUnwrapCall));
if(call == NULL) die("Memory allocation failed");
call->context = kerberos_context;
call->challenge = challenge_str;
// Unpack the callback
Local<Function> callbackHandle = info.Length() == 3 ? Local<Function>::Cast(info[2]) : Local<Function>::Cast(info[1]);
Nan::Callback *callback = new Nan::Callback(callbackHandle);
// Let's allocate some space
Worker *worker = new Worker();
worker->error = false;
worker->request.data = worker;
worker->callback = callback;
worker->parameters = call;
worker->execute = _authGSSClientUnwrap;
worker->mapper = _map_authGSSClientUnwrap;
// Schedule the worker with lib_uv
uv_queue_work(uv_default_loop(), &worker->request, Kerberos::Process, (uv_after_work_cb)Kerberos::After);
// Return no value as it's callback based
info.GetReturnValue().Set(Nan::Undefined());
}
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// authGSSClientWrap
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
static void _authGSSClientWrap(Worker *worker) {
gss_client_response *response;
char *user_name = NULL;
// Unpack the parameter data struct
AuthGSSClientWrapCall *call = (AuthGSSClientWrapCall *)worker->parameters;
user_name = call->user_name;
// Check what kind of challenge we have
if(call->user_name == NULL) {
user_name = (char *)"";
}
// Perform authentication step
response = authenticate_gss_client_wrap(call->context->state, call->challenge, user_name);
// If we have an error mark worker as having had an error
if(response->return_code == AUTH_GSS_ERROR) {
worker->error = TRUE;
worker->error_code = response->return_code;
worker->error_message = response->message;
} else {
worker->return_code = response->return_code;
}
// Free up structure
if(call->challenge != NULL) free(call->challenge);
if(call->user_name != NULL) free(call->user_name);
free(call);
free(response);
}
static Local<Value> _map_authGSSClientWrap(Worker *worker) {
Nan::HandleScope scope;
// Return the return code
return Nan::New<Int32>(worker->return_code);
}
// Initialize method
NAN_METHOD(Kerberos::AuthGSSClientWrap) {
// Ensure valid call
if(info.Length() != 3 && info.Length() != 4) return Nan::ThrowError("Requires a GSS context, the result from the authGSSClientResponse after authGSSClientUnwrap, optional user name and callback function");
if(info.Length() == 3 && (!KerberosContext::HasInstance(info[0]) || !info[1]->IsString() || !info[2]->IsFunction())) return Nan::ThrowError("Requires a GSS context, the result from the authGSSClientResponse after authGSSClientUnwrap, optional user name and callback function");
if(info.Length() == 4 && (!KerberosContext::HasInstance(info[0]) || !info[1]->IsString() || !info[2]->IsString() || !info[3]->IsFunction())) return Nan::ThrowError("Requires a GSS context, the result from the authGSSClientResponse after authGSSClientUnwrap, optional user name and callback function");
// Challenge string
char *challenge_str = NULL;
char *user_name_str = NULL;
// Let's unpack the kerberos context
Local<Object> object = info[0]->ToObject();
KerberosContext *kerberos_context = KerberosContext::Unwrap<KerberosContext>(object);
if (!kerberos_context->IsClientInstance()) {
return Nan::ThrowError("GSS context is not a client instance");
}
// Unpack the challenge string
Local<String> challenge = info[1]->ToString();
// Convert uri string to c-string
challenge_str = (char *)calloc(challenge->Utf8Length() + 1, sizeof(char));
if(challenge_str == NULL) die("Memory allocation failed");
// Write v8 string to c-string
challenge->WriteUtf8(challenge_str);
// If we have a user string
if(info.Length() == 4) {
// Unpack user name
Local<String> user_name = info[2]->ToString();
// Convert uri string to c-string
user_name_str = (char *)calloc(user_name->Utf8Length() + 1, sizeof(char));
if(user_name_str == NULL) die("Memory allocation failed");
// Write v8 string to c-string
user_name->WriteUtf8(user_name_str);
}
// Allocate a structure
AuthGSSClientWrapCall *call = (AuthGSSClientWrapCall *)calloc(1, sizeof(AuthGSSClientWrapCall));
if(call == NULL) die("Memory allocation failed");
call->context = kerberos_context;
call->challenge = challenge_str;
call->user_name = user_name_str;
// Unpack the callback
Local<Function> callbackHandle = info.Length() == 4 ? Local<Function>::Cast(info[3]) : Local<Function>::Cast(info[2]);
Nan::Callback *callback = new Nan::Callback(callbackHandle);
// Let's allocate some space
Worker *worker = new Worker();
worker->error = false;
worker->request.data = worker;
worker->callback = callback;
worker->parameters = call;
worker->execute = _authGSSClientWrap;
worker->mapper = _map_authGSSClientWrap;
// Schedule the worker with lib_uv
uv_queue_work(uv_default_loop(), &worker->request, Kerberos::Process, (uv_after_work_cb)Kerberos::After);
// Return no value as it's callback based
info.GetReturnValue().Set(Nan::Undefined());
}
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// authGSSClientClean
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
static void _authGSSClientClean(Worker *worker) {
gss_client_response *response;
// Unpack the parameter data struct
AuthGSSClientCleanCall *call = (AuthGSSClientCleanCall *)worker->parameters;
// Perform authentication step
response = authenticate_gss_client_clean(call->context->state);
// If we have an error mark worker as having had an error
if(response->return_code == AUTH_GSS_ERROR) {
worker->error = TRUE;
worker->error_code = response->return_code;
worker->error_message = response->message;
} else {
worker->return_code = response->return_code;
}
// Free up structure
free(call);
free(response);
}
static Local<Value> _map_authGSSClientClean(Worker *worker) {
Nan::HandleScope scope;
// Return the return code
return Nan::New<Int32>(worker->return_code);
}
// Initialize method
NAN_METHOD(Kerberos::AuthGSSClientClean) {
// Ensure valid call
if(info.Length() != 2) return Nan::ThrowError("Requires a GSS context and callback function");
if(!KerberosContext::HasInstance(info[0]) || !info[1]->IsFunction()) return Nan::ThrowError("Requires a GSS context and callback function");
// Let's unpack the kerberos context
Local<Object> object = info[0]->ToObject();
KerberosContext *kerberos_context = KerberosContext::Unwrap<KerberosContext>(object);
if (!kerberos_context->IsClientInstance()) {
return Nan::ThrowError("GSS context is not a client instance");
}
// Allocate a structure
AuthGSSClientCleanCall *call = (AuthGSSClientCleanCall *)calloc(1, sizeof(AuthGSSClientCleanCall));
if(call == NULL) die("Memory allocation failed");
call->context = kerberos_context;
// Unpack the callback
Local<Function> callbackHandle = Local<Function>::Cast(info[1]);
Nan::Callback *callback = new Nan::Callback(callbackHandle);
// Let's allocate some space
Worker *worker = new Worker();
worker->error = false;
worker->request.data = worker;
worker->callback = callback;
worker->parameters = call;
worker->execute = _authGSSClientClean;
worker->mapper = _map_authGSSClientClean;
// Schedule the worker with lib_uv
uv_queue_work(uv_default_loop(), &worker->request, Kerberos::Process, (uv_after_work_cb)Kerberos::After);
// Return no value as it's callback based
info.GetReturnValue().Set(Nan::Undefined());
}
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// authGSSServerInit
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
static void _authGSSServerInit(Worker *worker) {
gss_server_state *state;
gss_client_response *response;
// Allocate state
state = (gss_server_state *)malloc(sizeof(gss_server_state));
if(state == NULL) die("Memory allocation failed");
// Unpack the parameter data struct
AuthGSSServerInitCall *call = (AuthGSSServerInitCall *)worker->parameters;
// Start the kerberos service
response = authenticate_gss_server_init(call->service, call->constrained_delegation, call->username, state);
// Release the parameter struct memory
free(call->service);
free(call->username);
free(call);
// If we have an error mark worker as having had an error
if(response->return_code == AUTH_GSS_ERROR) {
worker->error = TRUE;
worker->error_code = response->return_code;
worker->error_message = response->message;
free(state);
} else {
worker->return_value = state;
}
// Free structure
free(response);
}
static Local<Value> _map_authGSSServerInit(Worker *worker) {
KerberosContext *context = KerberosContext::New();
context->server_state = (gss_server_state *)worker->return_value;
return context->handle();
}
// Server Initialize method
NAN_METHOD(Kerberos::AuthGSSServerInit) {
// Ensure valid call
if(info.Length() != 4) return Nan::ThrowError("Requires a service string, constrained delegation boolean, a username string (or NULL) for S4U2Self protocol transition and a callback function");
if(!info[0]->IsString() ||
!info[1]->IsBoolean() ||
!(info[2]->IsString() || info[2]->IsNull()) ||
!info[3]->IsFunction()
) return Nan::ThrowError("Requires a service string, constrained delegation boolean, a username string (or NULL) for S4U2Self protocol transition and a callback function");
if (!info[1]->BooleanValue() && !info[2]->IsNull()) return Nan::ThrowError("S4U2Self only possible when constrained delegation is enabled");
// Allocate a structure
AuthGSSServerInitCall *call = (AuthGSSServerInitCall *)calloc(1, sizeof(AuthGSSServerInitCall));
if(call == NULL) die("Memory allocation failed");
Local<String> service = info[0]->ToString();
// Convert service string to c-string
char *service_str = (char *)calloc(service->Utf8Length() + 1, sizeof(char));
if(service_str == NULL) die("Memory allocation failed");
// Write v8 string to c-string
service->WriteUtf8(service_str);
call->service = service_str;
call->constrained_delegation = info[1]->BooleanValue();
if (info[2]->IsNull())
{
call->username = NULL;
}
else
{
Local<String> tmpString = info[2]->ToString();
char *tmpCstr = (char *)calloc(tmpString->Utf8Length() + 1, sizeof(char));
if(tmpCstr == NULL) die("Memory allocation failed");
tmpString->WriteUtf8(tmpCstr);
call->username = tmpCstr;
}
// Unpack the callback
Local<Function> callbackHandle = Local<Function>::Cast(info[3]);
Nan::Callback *callback = new Nan::Callback(callbackHandle);
// Let's allocate some space
Worker *worker = new Worker();
worker->error = false;
worker->request.data = worker;
worker->callback = callback;
worker->parameters = call;
worker->execute = _authGSSServerInit;
worker->mapper = _map_authGSSServerInit;
// Schedule the worker with lib_uv
uv_queue_work(uv_default_loop(), &worker->request, Kerberos::Process, (uv_after_work_cb)Kerberos::After);
// Return no value as it's callback based
info.GetReturnValue().Set(Nan::Undefined());
}
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// authGSSServerClean
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
static void _authGSSServerClean(Worker *worker) {
gss_client_response *response;
// Unpack the parameter data struct
AuthGSSServerCleanCall *call = (AuthGSSServerCleanCall *)worker->parameters;
// Perform authentication step
response = authenticate_gss_server_clean(call->context->server_state);
// If we have an error mark worker as having had an error
if(response->return_code == AUTH_GSS_ERROR) {
worker->error = TRUE;
worker->error_code = response->return_code;
worker->error_message = response->message;
} else {
worker->return_code = response->return_code;
}
// Free up structure
free(call);
free(response);
}
static Local<Value> _map_authGSSServerClean(Worker *worker) {
Nan::HandleScope scope;
// Return the return code
return Nan::New<Int32>(worker->return_code);
}
// Initialize method
NAN_METHOD(Kerberos::AuthGSSServerClean) {
// // Ensure valid call
if(info.Length() != 2) return Nan::ThrowError("Requires a GSS context and callback function");
if(!KerberosContext::HasInstance(info[0]) || !info[1]->IsFunction()) return Nan::ThrowError("Requires a GSS context and callback function");
// Let's unpack the kerberos context
Local<Object> object = info[0]->ToObject();
KerberosContext *kerberos_context = KerberosContext::Unwrap<KerberosContext>(object);
if (!kerberos_context->IsServerInstance()) {
return Nan::ThrowError("GSS context is not a server instance");
}
// Allocate a structure
AuthGSSServerCleanCall *call = (AuthGSSServerCleanCall *)calloc(1, sizeof(AuthGSSServerCleanCall));
if(call == NULL) die("Memory allocation failed");
call->context = kerberos_context;
// Unpack the callback
Local<Function> callbackHandle = Local<Function>::Cast(info[1]);
Nan::Callback *callback = new Nan::Callback(callbackHandle);
// Let's allocate some space
Worker *worker = new Worker();
worker->error = false;
worker->request.data = worker;
worker->callback = callback;
worker->parameters = call;
worker->execute = _authGSSServerClean;
worker->mapper = _map_authGSSServerClean;
// Schedule the worker with lib_uv
uv_queue_work(uv_default_loop(), &worker->request, Kerberos::Process, (uv_after_work_cb)Kerberos::After);
// Return no value as it's callback based
info.GetReturnValue().Set(Nan::Undefined());
}
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// authGSSServerStep
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
static void _authGSSServerStep(Worker *worker) {
gss_server_state *state;
gss_client_response *response;
char *auth_data;
// Unpack the parameter data struct
AuthGSSServerStepCall *call = (AuthGSSServerStepCall *)worker->parameters;
// Get the state
state = call->context->server_state;
auth_data = call->auth_data;
// Check if we got auth_data or not
if(call->auth_data == NULL) {
auth_data = (char *)"";
}
// Perform authentication step
response = authenticate_gss_server_step(state, auth_data);
// If we have an error mark worker as having had an error
if(response->return_code == AUTH_GSS_ERROR) {
worker->error = TRUE;
worker->error_code = response->return_code;
worker->error_message = response->message;
} else {
worker->return_code = response->return_code;
}
// Free up structure
if(call->auth_data != NULL) free(call->auth_data);
free(call);
free(response);
}
static Local<Value> _map_authGSSServerStep(Worker *worker) {
Nan::HandleScope scope;
// Return the return code
return Nan::New<Int32>(worker->return_code);
}
// Initialize method
NAN_METHOD(Kerberos::AuthGSSServerStep) {
// Ensure valid call
if(info.Length() != 3) return Nan::ThrowError("Requires a GSS context, auth-data string and callback function");
if(!KerberosContext::HasInstance(info[0])) return Nan::ThrowError("1st arg must be a GSS context");
if (!info[1]->IsString()) return Nan::ThrowError("2nd arg must be auth-data string");
if (!info[2]->IsFunction()) return Nan::ThrowError("3rd arg must be a callback function");
// Auth-data string
char *auth_data_str = NULL;
// Let's unpack the parameters
Local<Object> object = info[0]->ToObject();
KerberosContext *kerberos_context = KerberosContext::Unwrap<KerberosContext>(object);
if (!kerberos_context->IsServerInstance()) {
return Nan::ThrowError("GSS context is not a server instance");
}
// Unpack the auth_data string
Local<String> auth_data = info[1]->ToString();
// Convert uri string to c-string
auth_data_str = (char *)calloc(auth_data->Utf8Length() + 1, sizeof(char));
if(auth_data_str == NULL) die("Memory allocation failed");
// Write v8 string to c-string
auth_data->WriteUtf8(auth_data_str);
// Allocate a structure
AuthGSSServerStepCall *call = (AuthGSSServerStepCall *)calloc(1, sizeof(AuthGSSServerStepCall));
if(call == NULL) die("Memory allocation failed");
call->context = kerberos_context;
call->auth_data = auth_data_str;
// Unpack the callback
Local<Function> callbackHandle = Local<Function>::Cast(info[2]);
Nan::Callback *callback = new Nan::Callback(callbackHandle);
// Let's allocate some space
Worker *worker = new Worker();
worker->error = false;
worker->request.data = worker;
worker->callback = callback;
worker->parameters = call;
worker->execute = _authGSSServerStep;
worker->mapper = _map_authGSSServerStep;
// Schedule the worker with lib_uv
uv_queue_work(uv_default_loop(), &worker->request, Kerberos::Process, (uv_after_work_cb)Kerberos::After);
// Return no value as it's callback based
info.GetReturnValue().Set(Nan::Undefined());
}
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// authUserKrb5Password
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
static void _authUserKrb5Password(Worker *worker) {
AuthUserKrb5PasswordCall *call = (AuthUserKrb5PasswordCall *)worker->parameters;
gss_client_response *response = authenticate_user_krb5_password(call->username, call->password, call->service);
free(call->username);
free(call->password);
free(call->service);
free(call);
// If we have an error mark worker as having had an error
if(response->return_code == AUTH_GSS_ERROR) {
worker->error = TRUE;
worker->error_code = response->return_code;
worker->error_message = response->message;
} else {
worker->return_code = response->return_code;
}
free(response);
}
static Local<Value> _map_authUserKrb5Password(Worker *worker) {
return worker->return_code ? Nan::True() : Nan::False();
}
NAN_METHOD(Kerberos::AuthUserKrb5Password) {
const char *usage = "Requires a username (string), password (string), service (string) and callback (function(err,boolean))";
if(info.Length() != 4) return Nan::ThrowError(usage);
if(!info[0]->IsString()) return Nan::ThrowError(usage);
if(!info[1]->IsString()) return Nan::ThrowError(usage);
if(!info[2]->IsString()) return Nan::ThrowError(usage);
if(!info[3]->IsFunction()) return Nan::ThrowError(usage);
AuthUserKrb5PasswordCall *call = (AuthUserKrb5PasswordCall *)calloc(1, sizeof(AuthUserKrb5PasswordCall));
if (call == NULL) die("Memory allocation failed");
// Unpack the strings
Local<String> username_str = info[0]->ToString();
Local<String> password_str = info[1]->ToString();
Local<String> service_str = info[2]->ToString();
call->username = (char *)calloc(username_str->Utf8Length() + 1, sizeof(char));
call->password = (char *)calloc(password_str->Utf8Length() + 1, sizeof(char));
call->service = (char *)calloc(service_str->Utf8Length() + 1, sizeof(char));
if (call->username == NULL || call->password == NULL || call->service == NULL) {
die("Memory allocation failed");
}
username_str->WriteUtf8(call->username);
password_str->WriteUtf8(call->password);
service_str->WriteUtf8(call->service);
// Unpack the callback
Local<Function> callbackHandle = Local<Function>::Cast(info[3]);
Nan::Callback *callback = new Nan::Callback(callbackHandle);
Worker *worker = new Worker();
worker->error = false;
worker->request.data = worker;
worker->callback = callback;
worker->parameters = call;
worker->execute = _authUserKrb5Password;
worker->mapper = _map_authUserKrb5Password;
// Schedule the worker with lib_uv
uv_queue_work(uv_default_loop(), &worker->request, Kerberos::Process, (uv_after_work_cb)Kerberos::After);
// Return no value as it's callback based
info.GetReturnValue().Set(Nan::Undefined());
}
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// UV Lib callbacks
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
void Kerberos::Process(uv_work_t* work_req) {
// Grab the worker
Worker *worker = static_cast<Worker*>(work_req->data);
// Execute the worker code
worker->execute(worker);
}
void Kerberos::After(uv_work_t* work_req) {
// Grab the scope of the call from Node
Nan::HandleScope scope;
// Get the worker reference
Worker *worker = static_cast<Worker*>(work_req->data);
// If we have an error
if(worker->error) {
Local<Value> err = v8::Exception::Error(Nan::New<String>(worker->error_message).ToLocalChecked());
Local<Object> obj = err->ToObject();
obj->Set(Nan::New<String>("code").ToLocalChecked(), Nan::New<Int32>(worker->error_code));
Local<Value> info[2] = { err, Nan::Null() };
// Execute the error
Nan::TryCatch try_catch;
// Call the callback
worker->callback->Call(ARRAY_SIZE(info), info);
// If we have an exception handle it as a fatalexception
if (try_catch.HasCaught()) {
Nan::FatalException(try_catch);
}
} else {
// // Map the data
Local<Value> result = worker->mapper(worker);
// Set up the callback with a null first
#if defined(V8_MAJOR_VERSION) && (V8_MAJOR_VERSION > 4 || \
(V8_MAJOR_VERSION == 4 && defined(V8_MINOR_VERSION) && V8_MINOR_VERSION >= 3))
Local<Value> info[2] = { Nan::Null(), result};
#else
Local<Value> info[2] = { Nan::Null(), Nan::New<v8::Value>(result)};
#endif
// Wrap the callback function call in a TryCatch so that we can call
// node's FatalException afterwards. This makes it possible to catch
// the exception from JavaScript land using the
// process.on('uncaughtException') event.
Nan::TryCatch try_catch;
// Call the callback
worker->callback->Call(ARRAY_SIZE(info), info);
// If we have an exception handle it as a fatalexception
if (try_catch.HasCaught()) {
Nan::FatalException(try_catch);
}
}
// Clean up the memory
delete worker->callback;
delete worker;
}
// Exporting function
NAN_MODULE_INIT(init) {
Kerberos::Initialize(target);
KerberosContext::Initialize(target);
}
NODE_MODULE(kerberos, init);