Skip to content

Commit 385fcd1

Browse files
committed
feat(serverPrincipalDetails): add server pricipal details method
1 parent c12a5c4 commit 385fcd1

File tree

5 files changed

+87
-66
lines changed

5 files changed

+87
-66
lines changed

lib/kerberos.js

+30-65
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,24 @@ function validateParameter(parameter, spec) {
3636
}
3737
}
3838

39+
/**
40+
* This function returns the service principal for the server given a service
41+
* type and hostname.
42+
*
43+
* Details are looked up via the `/etc/keytab` file.
44+
*
45+
* @param {string} service The Kerberos service type for the server.
46+
* @param {string} hostname The hostname of the server.
47+
* @param {function} callback
48+
*/
49+
function serverPrincipalDetails(service, hostname, callback) {
50+
validateParameter(service, { name: 'service', type: 'string' });
51+
validateParameter(hostname, { name: 'options', type: 'string' });
52+
validateParameter(callback, { name: 'callback', type: 'function' });
53+
54+
kerberos.serverPrincipalDetails(service, hostname, callback);
55+
}
56+
3957
/**
4058
* The callback format for inserts
4159
* @callback authGSSClientInitCallback
@@ -54,7 +72,7 @@ function validateParameter(parameter, spec) {
5472
* @param {string} [options.principal] Optional string containing the client principal in the form 'user@realm' (e.g. '[email protected]').
5573
* @param {number} [options.gssFlags] Optional integer used to set GSS flags. (e.g. GSS_C_DELEG_FLAG|GSS_C_MUTUAL_FLAG|GSS_C_SEQUENCE_FLAG will allow for forwarding credentials to the remote host)
5674
* @param {number} [options.mechOID] Optional GSS mech OID. Defaults to None (GSS_C_NO_OID). Other possible values are GSS_MECH_OID_KRB5, GSS_MECH_OID_SPNEGO.
57-
* @param {authGSSClientInitCallback} callback The operation callback
75+
* @param {initializeClientCallback} callback The operation callback
5876
*/
5977
function initializeClient(service, options, callback) {
6078
if (typeof options === 'function') (callback = options), (options = {});
@@ -74,7 +92,7 @@ function initializeClient(service, options, callback) {
7492
* are complete.
7593
*
7694
* @param {string} service A string containing the service principal in the form 'type@fqdn' (e.g. '[email protected]').
77-
* @param {function} callback
95+
* @param {initializeServerCallback} callback
7896
*/
7997
function initializeServer(service, callback) {
8098
validateParameter(service, { name: 'service', type: 'string' });
@@ -83,105 +101,50 @@ function initializeServer(service, callback) {
83101
kerberos.initializeServer(service, callback);
84102
}
85103

86-
/**
87-
* Destroys the context for GSSAPI client-side authentication.
88-
*
89-
* @param {KerberosContext} context The context object returned from `authGSSClientInit`
90-
* @param {function} callback
91-
*/
92-
// function authGSSClientClean(context, callback) {
93-
// validateParameter(context, { name: 'context', type: 'object' });
94-
// validateParameter(callback, { name: 'callback', type: 'function' });
95-
96-
// kerberos.authGSSClientClean(context, callback);
97-
// }
98-
99104
/**
100105
* Processes a single GSSAPI client-side step using the supplied server data.
101106
*
102-
* @param {KerberosContext} context The context object returned from `authGSSClientInit`
107+
* @memberof KerberosClient
103108
* @param {string} challenge A string containing the base64-encoded server data (which may be empty for the first step)
104109
* @param {function} callback Returns a result code, or an error if one was encountered
105110
*/
106-
// function authGSSClientStep(context, challenge, callback) {
107-
// validateParameter(context, { name: 'context', type: 'object' });
108-
// validateParameter(challenge, { name: 'challenge', type: 'string' });
109-
// validateParameter(callback, { name: 'callback', type: 'function' });
110-
111-
// kerberos.authGSSClientStep(context, challenge, callback);
112-
// }
113111

114112
/**
115113
* Perform the client side GSSAPI unwrap step
116114
*
117-
* @param {KerberosContext} context The context object returned from `authGSSClientInit`
115+
* @memberof KerberosClient
118116
* @param {string} challenge A string containing the base64-encoded server data
119117
* @param {function} callback
120118
*/
121-
// function authGSSClientUnwrap(context, challenge, callback) {
122-
// validateParameter(context, { name: 'context', type: 'object' });
123-
// validateParameter(challenge, { name: 'challenge', type: 'string' });
124-
// validateParameter(callback, { name: 'callback', type: 'function' });
125-
126-
// kerberos.authGSSClientUnwrap(context, challenge, callback);
127-
// }
128119

129120
/**
130121
* Perform the client side GSSAPI wrap step.
131122
*
132-
* @param {KerberosContext} context The context object returned from `authGSSClientInit`
123+
* @memberof KerberosClient
133124
* @param {string} challenge The result of the `authGSSClientResponse` after the `authGSSClientUnwrap`
134125
* @param {object} [options] Optional settings
135126
* @param {string} [options.user] The user to authorize
136127
* @param {function} callback
137128
*/
138-
// function authGSSClientWrap(context, challenge, options, callback) {
139-
// if (typeof options === 'function') (callback = options), (options = {});
140-
// options = options || {};
141-
142-
// validateParameter(context, { name: 'context', type: 'object' });
143-
// validateParameter(challenge, { name: 'challenge', type: 'string' });
144-
// validateParameter(options, { name: 'options', type: 'object' });
145-
// validateParameter(callback, { name: 'callback', type: 'function' });
146-
147-
// kerberos.authGSSClientWrap(context, challenge, options, callback);
148-
// }
149-
150-
/**
151-
* Destroys the context for GSSAPI server-side authentication.
152-
*
153-
* @param {KerberosContext} context The context object returned from `authGSSServerInit`
154-
* @param {function} callback
155-
*/
156-
// function authGSSServerClean(context, callback) {
157-
// validateParameter(context, { name: 'context', type: 'object' });
158-
// validateParameter(callback, { name: 'callback', type: 'function' });
159-
160-
// kerberos.authGSSServerClean(context, callback);
161-
// }
162129

163130
/**
164131
* Processes a single GSSAPI server-side step using the supplied client data.
165132
*
166-
* @param {KerberosContext} context The context object returned from `authGSSServerInit`
133+
* @memberof KerberosServer
167134
* @param {string} challenge A string containing the base64-encoded client data
168135
* @param {function} callback
169136
*/
170-
// function authGSSServerStep(context, challenge, callback) {
171-
// validateParameter(context, { name: 'context', type: 'object' });
172-
// validateParameter(challenge, { name: 'challenge', type: 'string' });
173-
// validateParameter(callback, { name: 'callback', type: 'function' });
174-
175-
// kerberos.authGSSServerStep(context, challenge, callback);
176-
// }
177137

178138
module.exports = {
179139
initializeClient,
180140
initializeServer,
141+
serverPrincipalDetails,
181142

182-
// Constants
143+
// result codes
183144
AUTH_GSS_CONTINUE,
184145
AUTH_GSS_COMPLETE,
146+
147+
// gss flags
185148
GSS_C_DELEG_FLAG,
186149
GSS_C_MUTUAL_FLAG,
187150
GSS_C_REPLAY_FLAG,
@@ -192,6 +155,8 @@ module.exports = {
192155
GSS_C_PROT_READY_FLAG,
193156
GSS_C_TRANS_FLAG,
194157
GSS_C_NO_OID,
158+
159+
// mechanism OIDs
195160
GSS_MECH_OID_KRB5,
196161
GSS_MECH_OID_SPNEGO
197162
};

src/kerberos.cc

+44
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,48 @@ NAN_METHOD(InitializeServer) {
115115
AsyncQueueWorker(new InitializeServerWorker(service, callback));
116116
}
117117

118+
class ServerPrincipalDetailsWorker : public Nan::AsyncWorker {
119+
public:
120+
ServerPrincipalDetailsWorker(std::string service, std::string hostname, Nan::Callback *callback)
121+
: AsyncWorker(callback, "kerberos:ServerPrincipalDetails"),
122+
_service(service),
123+
_hostname(hostname)
124+
{}
125+
126+
virtual void Execute() {
127+
std::unique_ptr<gss_result, FreeDeleter> result(
128+
server_principal_details(_service.c_str(), _hostname.c_str()));
129+
130+
if (result->code == AUTH_GSS_ERROR) {
131+
SetErrorMessage(result->message);
132+
return;
133+
}
134+
135+
_details = std::string(result->data);
136+
}
137+
138+
protected:
139+
virtual void HandleOKCallback() {
140+
Nan::HandleScope scope;
141+
v8::Local<v8::Value> argv[] = { Nan::Null(), Nan::New(_details).ToLocalChecked() };
142+
callback->Call(2, argv, async_resource);
143+
}
144+
145+
private:
146+
std::string _service;
147+
std::string _hostname;
148+
std::string _details;
149+
150+
};
151+
152+
NAN_METHOD(ServerPrincipalDetails) {
153+
std::string service(*Nan::Utf8String(info[0]));
154+
std::string hostname(*Nan::Utf8String(info[1]));
155+
Nan::Callback* callback = new Nan::Callback(Nan::To<v8::Function>(info[2]).ToLocalChecked());
156+
157+
AsyncQueueWorker(new ServerPrincipalDetailsWorker(service, hostname, callback));
158+
}
159+
118160
NAN_MODULE_INIT(Init) {
119161
// Custom types
120162
KerberosClient::Init(target);
@@ -124,6 +166,8 @@ NAN_MODULE_INIT(Init) {
124166
Nan::GetFunction(Nan::New<FunctionTemplate>(InitializeClient)).ToLocalChecked());
125167
Nan::Set(target, Nan::New("initializeServer").ToLocalChecked(),
126168
Nan::GetFunction(Nan::New<FunctionTemplate>(InitializeServer)).ToLocalChecked());
169+
Nan::Set(target, Nan::New("serverPrincipalDetails").ToLocalChecked(),
170+
Nan::GetFunction(Nan::New<FunctionTemplate>(ServerPrincipalDetails)).ToLocalChecked());
127171
}
128172

129173
NODE_MODULE(kerberos, Init)

src/kerberos.h

+1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
#include <nan.h>
66

7+
NAN_METHOD(ServerPrincipalDetails);
78
NAN_METHOD(InitializeClient);
89
NAN_METHOD(InitializeServer);
910

src/kerberos_gss.cc

+3-1
Original file line numberDiff line numberDiff line change
@@ -110,11 +110,13 @@ gss_result* server_principal_details(const char* service, const char* hostname)
110110
}
111111

112112
if (details == NULL)
113+
{
113114
result = gss_error_result_with_message_and_code("Principal not found in keytab", -1);
115+
}
114116
else
115117
{
116118
result = gss_success_result(AUTH_GSS_COMPLETE);
117-
result->message = details;
119+
result->data = details;
118120
}
119121
end:
120122
if (cursor)

test/kerberos_tests.js

+9
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,15 @@ const hostname = process.env.KERBEROS_HOSTNAME || 'hostname.example.com';
1414
const port = process.env.KERBEROS_PORT || '80';
1515

1616
describe('Kerberos', function() {
17+
it('should lookup principal details on a server', function(done) {
18+
const expected = `HTTP/${hostname}@${realm.toUpperCase()}`;
19+
kerberos.serverPrincipalDetails('HTTP', hostname, (err, details) => {
20+
expect(err).to.not.exist;
21+
expect(details).to.equal(expected);
22+
done();
23+
});
24+
});
25+
1726
it('should authenticate against a kerberos server using GSSAPI', function(done) {
1827
const service = `HTTP@${hostname}`;
1928

0 commit comments

Comments
 (0)