Skip to content

Commit

Permalink
feat(checkPassword): add implementation for checking krb5 passwords
Browse files Browse the repository at this point in the history
  • Loading branch information
mbroadst committed Jul 8, 2018
1 parent 385fcd1 commit 60f476e
Show file tree
Hide file tree
Showing 5 changed files with 172 additions and 2 deletions.
35 changes: 35 additions & 0 deletions lib/kerberos.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,40 @@ function validateParameter(parameter, spec) {
}
}

/**
* This function provides a simple way to verify that a user name and password
* match those normally used for Kerberos authentication.
* It does this by checking that the supplied user name and password can be
* used to get a ticket for the supplied service.
* If the user name does not contain a realm, then the default realm supplied
* is used.
*
* For this to work properly the Kerberos must be configured properly on this
* machine.
* That will likely mean ensuring that the edu.mit.Kerberos preference file
* has the correct realms and KDCs listed.
*
* IMPORTANT: This method is vulnerable to KDC spoofing attacks and it should
* only used for testing. Do not use this in any production system - your
* security could be compromised if you do.
*
* @param {string} username The Kerberos user name. If no realm is supplied, then the `defaultRealm` will be used.
* @param {string} password The password for the user.
* @param {string} service The Kerberos service to check access for.
* @param {string} [defaultRealm] The default realm to use if one is not supplied in the user argument.
* @param {function} callback
*/
function checkPassword(username, password, service, defaultRealm, callback) {
if (typeof defaultRealm === 'function') (callback = defaultRealm), (defaultRealm = null);
validateParameter(username, { name: 'service', type: 'string' });
validateParameter(password, { name: 'options', type: 'string' });
validateParameter(service, { name: 'options', type: 'string' });
validateParameter(defaultRealm, { name: 'options', type: 'string' });
validateParameter(callback, { name: 'callback', type: 'function' });

kerberos.checkPassword(username, password, service, defaultRealm, callback);
}

/**
* This function returns the service principal for the server given a service
* type and hostname.
Expand Down Expand Up @@ -139,6 +173,7 @@ module.exports = {
initializeClient,
initializeServer,
serverPrincipalDetails,
checkPassword,

// result codes
AUTH_GSS_CONTINUE,
Expand Down
44 changes: 43 additions & 1 deletion src/kerberos.cc
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,6 @@ class ServerPrincipalDetailsWorker : public Nan::AsyncWorker {
std::string _service;
std::string _hostname;
std::string _details;

};

NAN_METHOD(ServerPrincipalDetails) {
Expand All @@ -157,6 +156,47 @@ NAN_METHOD(ServerPrincipalDetails) {
AsyncQueueWorker(new ServerPrincipalDetailsWorker(service, hostname, callback));
}

class CheckPasswordWorker : public Nan::AsyncWorker {
public:
CheckPasswordWorker(std::string username,
std::string password,
std::string service,
std::string defaultRealm,
Nan::Callback *callback)
: AsyncWorker(callback, "kerberos:CheckPassword"),
_username(username),
_password(password),
_service(service),
_defaultRealm(defaultRealm)
{}

virtual void Execute() {
std::unique_ptr<gss_result, FreeDeleter> result(
authenticate_user_krb5pwd(_username.c_str(), _password.c_str(), _service.c_str(), _defaultRealm.c_str()));

if (result->code == AUTH_GSS_ERROR) {
SetErrorMessage(result->message);
return;
}
}

private:
std::string _username;
std::string _password;
std::string _service;
std::string _defaultRealm;
};

NAN_METHOD(CheckPassword) {
std::string username(*Nan::Utf8String(info[0]));
std::string password(*Nan::Utf8String(info[1]));
std::string service(*Nan::Utf8String(info[2]));
std::string defaultRealm(*Nan::Utf8String(info[3]));
Nan::Callback* callback = new Nan::Callback(Nan::To<v8::Function>(info[4]).ToLocalChecked());

AsyncQueueWorker(new CheckPasswordWorker(username, password, service, defaultRealm, callback));
}

NAN_MODULE_INIT(Init) {
// Custom types
KerberosClient::Init(target);
Expand All @@ -168,6 +208,8 @@ NAN_MODULE_INIT(Init) {
Nan::GetFunction(Nan::New<FunctionTemplate>(InitializeServer)).ToLocalChecked());
Nan::Set(target, Nan::New("serverPrincipalDetails").ToLocalChecked(),
Nan::GetFunction(Nan::New<FunctionTemplate>(ServerPrincipalDetails)).ToLocalChecked());
Nan::Set(target, Nan::New("checkPassword").ToLocalChecked(),
Nan::GetFunction(Nan::New<FunctionTemplate>(CheckPassword)).ToLocalChecked());
}

NODE_MODULE(kerberos, Init)
1 change: 1 addition & 0 deletions src/kerberos.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,6 @@
NAN_METHOD(ServerPrincipalDetails);
NAN_METHOD(InitializeClient);
NAN_METHOD(InitializeServer);
NAN_METHOD(CheckPassword);

#endif
89 changes: 88 additions & 1 deletion src/kerberos_gss.cc
Original file line number Diff line number Diff line change
Expand Up @@ -668,6 +668,93 @@ gss_result* authenticate_gss_server_step(gss_server_state *state, const char *ch
return ret;
}

gss_result* authenticate_user_krb5pwd(
const char *user, const char *pswd, const char *service,
const char *default_realm
) {
krb5_context kcontext = NULL;
krb5_error_code code;
krb5_principal client = NULL;
krb5_principal server = NULL;
gss_result* result = NULL;
int ret = 0;
char *name = NULL;
char *p = NULL;

code = krb5_init_context(&kcontext);
if (code) {
result = gss_error_result_with_message_and_code("Cannot initialize Kerberos5 context", code);
return result;
}

ret = krb5_parse_name (kcontext, service, &server);
if (ret) {
result = gss_error_result_with_message_and_code(krb5_get_err_text(kcontext, ret), ret);
goto end;
}

code = krb5_unparse_name(kcontext, server, &name);
if (code) {
result = gss_error_result_with_message_and_code(krb5_get_err_text(kcontext, code), code);
goto end;
}

free(name);
name = NULL;
name = (char *)malloc(256);
if (name == NULL) {
result = gss_error_result_with_message("Ran out of memory allocating name");
goto end;
}

p = strchr((char *)user, '@');
if (p == NULL) {
snprintf(name, 256, "%s@%s", user, default_realm);
} else {
snprintf(name, 256, "%s", user);
}

code = krb5_parse_name(kcontext, name, &client);
if (code) {
result = gss_error_result_with_message_and_code(krb5_get_err_text(kcontext, code), code);
goto end;
}

// verify krb5 user
krb5_creds creds;
krb5_get_init_creds_opt gic_options;
krb5_error_code verifyRet;

memset(&creds, 0, sizeof(creds));
krb5_get_init_creds_opt_init(&gic_options);
verifyRet = krb5_get_init_creds_password(
kcontext, &creds, client, (char *)pswd,
NULL, NULL, 0, NULL, &gic_options
);
if (verifyRet) {
result = gss_error_result_with_message_and_code(krb5_get_err_text(kcontext, verifyRet), verifyRet);
krb5_free_cred_contents(kcontext, &creds);
goto end;
}

krb5_free_cred_contents(kcontext, &creds);
result = gss_success_result(1);

end:
if (name) {
free(name);
}
if (client) {
krb5_free_principal(kcontext, client);
}
if (server) {
krb5_free_principal(kcontext, server);
}
krb5_free_context(kcontext);

return result;
}

static gss_result* gss_success_result(int ret)
{
gss_result* result = (gss_result *) malloc(sizeof(gss_result));
Expand Down Expand Up @@ -723,7 +810,7 @@ static gss_result* gss_error_result_with_message(const char* message)
{
gss_result* result = (gss_result *) malloc(sizeof(gss_result));
result->code = AUTH_GSS_ERROR;
result->message = strdup("Ran out of memory decoding challenge");
result->message = strdup(message);
return result;
}

Expand Down
5 changes: 5 additions & 0 deletions src/kerberos_gss.h
Original file line number Diff line number Diff line change
Expand Up @@ -73,3 +73,8 @@ gss_result* authenticate_gss_client_wrap(gss_client_state* state, const char* ch
gss_result* authenticate_gss_server_init(const char* service, gss_server_state* state);
int authenticate_gss_server_clean(gss_server_state* state);
gss_result* authenticate_gss_server_step(gss_server_state* state, const char* challenge);

gss_result* authenticate_user_krb5pwd(
const char *user, const char *pswd, const char *service,
const char *default_realm
);

0 comments on commit 60f476e

Please sign in to comment.