[Pkg-javascript-commits] [node-kerberos] 01/02: Imported Upstream version 0.0.14
Christopher Hoskin
christopher.hoskin at gmail.com
Mon Sep 14 21:33:23 UTC 2015
This is an automated email from the git hooks/post-receive script.
grinorcole-guest pushed a commit to branch master
in repository node-kerberos.
commit 02dbc84f5caa699a0f8b6f0d39759aa358c6e542
Author: Christopher Hoskin <christopher.hoskin at gmail.com>
Date: Mon Sep 14 22:07:53 2015 +0100
Imported Upstream version 0.0.14
---
.gitignore | 17 +
.npmignore | 18 +
.travis.yml | 20 +
LICENSE | 201 ++++++
README.md | 4 +
binding.gyp | 46 ++
index.js | 6 +
lib/auth_processes/mongodb.js | 281 ++++++++
lib/base64.c | 134 ++++
lib/base64.h | 22 +
lib/kerberos.cc | 864 +++++++++++++++++++++++
lib/kerberos.h | 50 ++
lib/kerberos.js | 128 ++++
lib/kerberos_context.cc | 116 +++
lib/kerberos_context.h | 63 ++
lib/kerberosgss.c | 688 ++++++++++++++++++
lib/kerberosgss.h | 69 ++
lib/sspi.js | 15 +
lib/win32/base64.c | 121 ++++
lib/win32/base64.h | 18 +
lib/win32/kerberos.cc | 51 ++
lib/win32/kerberos.h | 60 ++
lib/win32/kerberos_sspi.c | 244 +++++++
lib/win32/kerberos_sspi.h | 106 +++
lib/win32/worker.cc | 7 +
lib/win32/worker.h | 38 +
lib/win32/wrappers/security_buffer.cc | 101 +++
lib/win32/wrappers/security_buffer.h | 48 ++
lib/win32/wrappers/security_buffer.js | 12 +
lib/win32/wrappers/security_buffer_descriptor.cc | 182 +++++
lib/win32/wrappers/security_buffer_descriptor.h | 46 ++
lib/win32/wrappers/security_buffer_descriptor.js | 3 +
lib/win32/wrappers/security_context.cc | 856 ++++++++++++++++++++++
lib/win32/wrappers/security_context.h | 74 ++
lib/win32/wrappers/security_context.js | 3 +
lib/win32/wrappers/security_credentials.cc | 348 +++++++++
lib/win32/wrappers/security_credentials.h | 68 ++
lib/win32/wrappers/security_credentials.js | 22 +
lib/worker.cc | 7 +
lib/worker.h | 38 +
package.json | 29 +
test/kerberos_tests.js | 34 +
test/kerberos_win32_test.js | 15 +
test/win32/security_buffer_descriptor_tests.js | 41 ++
test/win32/security_buffer_tests.js | 22 +
test/win32/security_credentials_tests.js | 55 ++
46 files changed, 5391 insertions(+)
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..c70380d
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,17 @@
+lib-cov
+*.seed
+*.log
+*.csv
+*.dat
+*.out
+*.pid
+*.gz
+.DS_Store
+
+pids
+logs
+results
+node_modules
+build
+
+npm-debug.log
diff --git a/.npmignore b/.npmignore
new file mode 100644
index 0000000..724ad09
--- /dev/null
+++ b/.npmignore
@@ -0,0 +1,18 @@
+.npmignore
+.gitignore
+.buildinfo
+.DS_Store
+
+HISTORY
+Readme.md
+TODO
+
+docs/
+docs/sphinx-docs
+data/
+dev/
+examples/
+
+
+build/
+node_modules/
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..b0fb9f4
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,20 @@
+language: node_js
+node_js:
+ - "0.8"
+ - "0.10"
+ - "0.12"
+ - "iojs-v1.8.4"
+ - "iojs-v2.5.0"
+ - "iojs-v3.3.0"
+ - "4"
+addons:
+ apt:
+ sources:
+ - ubuntu-toolchain-r-test
+ packages:
+ - g++-4.8
+before_install:
+ - '[ "${TRAVIS_NODE_VERSION}" != "0.8" ] || npm install -g npm at 1.4.28'
+ - if [[ $TRAVIS_OS_NAME == "linux" ]]; then export CXX=g++-4.8; fi
+ - $CXX --version
+ - npm explore npm -g -- npm install node-gyp at latest
\ No newline at end of file
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..261eeb9
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,201 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..7428b0d
--- /dev/null
+++ b/README.md
@@ -0,0 +1,4 @@
+kerberos
+========
+
+Kerberos library for node.js
\ No newline at end of file
diff --git a/binding.gyp b/binding.gyp
new file mode 100644
index 0000000..6655299
--- /dev/null
+++ b/binding.gyp
@@ -0,0 +1,46 @@
+{
+ 'targets': [
+ {
+ 'target_name': 'kerberos',
+ 'cflags!': [ '-fno-exceptions' ],
+ 'cflags_cc!': [ '-fno-exceptions' ],
+ 'include_dirs': [ '<!(node -e "require(\'nan\')")', '/usr/include/mit-krb5' ],
+ 'conditions': [
+ ['OS=="mac"', {
+ 'sources': [ 'lib/kerberos.cc', 'lib/worker.cc', 'lib/kerberosgss.c', 'lib/base64.c', 'lib/kerberos_context.cc' ],
+ 'defines': [
+ '__MACOSX_CORE__'
+ ],
+ 'xcode_settings': {
+ 'GCC_ENABLE_CPP_EXCEPTIONS': 'YES'
+ },
+ "link_settings": {
+ "libraries": [
+ "-lkrb5"
+ ]
+ }
+ }],
+ ['OS=="linux"', {
+ 'sources': [ 'lib/kerberos.cc', 'lib/worker.cc', 'lib/kerberosgss.c', 'lib/base64.c', 'lib/kerberos_context.cc' ],
+ 'libraries': ['-lkrb5', '-lgssapi_krb5']
+ }],
+ ['OS=="win"', {
+ 'sources': [
+ 'lib/win32/kerberos.cc',
+ 'lib/win32/base64.c',
+ 'lib/win32/worker.cc',
+ 'lib/win32/kerberos_sspi.c',
+ 'lib/win32/wrappers/security_buffer.cc',
+ 'lib/win32/wrappers/security_buffer_descriptor.cc',
+ 'lib/win32/wrappers/security_context.cc',
+ 'lib/win32/wrappers/security_credentials.cc'
+ ],
+ "link_settings": {
+ "libraries": [
+ ]
+ }
+ }]
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/index.js b/index.js
new file mode 100644
index 0000000..b8c8532
--- /dev/null
+++ b/index.js
@@ -0,0 +1,6 @@
+// Get the Kerberos library
+module.exports = require('./lib/kerberos');
+// Set up the auth processes
+module.exports['processes'] = {
+ MongoAuthProcess: require('./lib/auth_processes/mongodb').MongoAuthProcess
+}
\ No newline at end of file
diff --git a/lib/auth_processes/mongodb.js b/lib/auth_processes/mongodb.js
new file mode 100644
index 0000000..f1e9231
--- /dev/null
+++ b/lib/auth_processes/mongodb.js
@@ -0,0 +1,281 @@
+var format = require('util').format;
+
+var MongoAuthProcess = function(host, port, service_name) {
+ // Check what system we are on
+ if(process.platform == 'win32') {
+ this._processor = new Win32MongoProcessor(host, port, service_name);
+ } else {
+ this._processor = new UnixMongoProcessor(host, port, service_name);
+ }
+}
+
+MongoAuthProcess.prototype.init = function(username, password, callback) {
+ this._processor.init(username, password, callback);
+}
+
+MongoAuthProcess.prototype.transition = function(payload, callback) {
+ this._processor.transition(payload, callback);
+}
+
+/*******************************************************************
+ *
+ * Win32 SSIP Processor for MongoDB
+ *
+ *******************************************************************/
+var Win32MongoProcessor = function(host, port, service_name) {
+ this.host = host;
+ this.port = port
+ // SSIP classes
+ this.ssip = require("../kerberos").SSIP;
+ // Set up first transition
+ this._transition = Win32MongoProcessor.first_transition(this);
+ // Set up service name
+ service_name = service_name || "mongodb";
+ // Set up target
+ this.target = format("%s/%s", service_name, host);
+ // Number of retries
+ this.retries = 10;
+}
+
+Win32MongoProcessor.prototype.init = function(username, password, callback) {
+ var self = this;
+ // Save the values used later
+ this.username = username;
+ this.password = password;
+ // Aquire credentials
+ this.ssip.SecurityCredentials.aquire_kerberos(username, password, function(err, security_credentials) {
+ if(err) return callback(err);
+ // Save credentials
+ self.security_credentials = security_credentials;
+ // Callback with success
+ callback(null);
+ });
+}
+
+Win32MongoProcessor.prototype.transition = function(payload, callback) {
+ if(this._transition == null) return callback(new Error("Transition finished"));
+ this._transition(payload, callback);
+}
+
+Win32MongoProcessor.first_transition = function(self) {
+ return function(payload, callback) {
+ self.ssip.SecurityContext.initialize(
+ self.security_credentials,
+ self.target,
+ payload, function(err, security_context) {
+ if(err) return callback(err);
+
+ // If no context try again until we have no more retries
+ if(!security_context.hasContext) {
+ if(self.retries == 0) return callback(new Error("Failed to initialize security context"));
+ // Update the number of retries
+ self.retries = self.retries - 1;
+ // Set next transition
+ return self.transition(payload, callback);
+ }
+
+ // Set next transition
+ self._transition = Win32MongoProcessor.second_transition(self);
+ self.security_context = security_context;
+ // Return the payload
+ callback(null, security_context.payload);
+ });
+ }
+}
+
+Win32MongoProcessor.second_transition = function(self) {
+ return function(payload, callback) {
+ // Perform a step
+ self.security_context.initialize(self.target, payload, function(err, security_context) {
+ if(err) return callback(err);
+
+ // If no context try again until we have no more retries
+ if(!security_context.hasContext) {
+ if(self.retries == 0) return callback(new Error("Failed to initialize security context"));
+ // Update the number of retries
+ self.retries = self.retries - 1;
+ // Set next transition
+ self._transition = Win32MongoProcessor.first_transition(self);
+ // Retry
+ return self.transition(payload, callback);
+ }
+
+ // Set next transition
+ self._transition = Win32MongoProcessor.third_transition(self);
+ // Return the payload
+ callback(null, security_context.payload);
+ });
+ }
+}
+
+Win32MongoProcessor.third_transition = function(self) {
+ return function(payload, callback) {
+ var messageLength = 0;
+ // Get the raw bytes
+ var encryptedBytes = new Buffer(payload, 'base64');
+ var encryptedMessage = new Buffer(messageLength);
+ // Copy first byte
+ encryptedBytes.copy(encryptedMessage, 0, 0, messageLength);
+ // Set up trailer
+ var securityTrailerLength = encryptedBytes.length - messageLength;
+ var securityTrailer = new Buffer(securityTrailerLength);
+ // Copy the bytes
+ encryptedBytes.copy(securityTrailer, 0, messageLength, securityTrailerLength);
+
+ // Types used
+ var SecurityBuffer = self.ssip.SecurityBuffer;
+ var SecurityBufferDescriptor = self.ssip.SecurityBufferDescriptor;
+
+ // Set up security buffers
+ var buffers = [
+ new SecurityBuffer(SecurityBuffer.DATA, encryptedBytes)
+ , new SecurityBuffer(SecurityBuffer.STREAM, securityTrailer)
+ ];
+
+ // Set up the descriptor
+ var descriptor = new SecurityBufferDescriptor(buffers);
+
+ // Decrypt the data
+ self.security_context.decryptMessage(descriptor, function(err, security_context) {
+ if(err) return callback(err);
+
+ var length = 4;
+ if(self.username != null) {
+ length += self.username.length;
+ }
+
+ var bytesReceivedFromServer = new Buffer(length);
+ bytesReceivedFromServer[0] = 0x01; // NO_PROTECTION
+ bytesReceivedFromServer[1] = 0x00; // NO_PROTECTION
+ bytesReceivedFromServer[2] = 0x00; // NO_PROTECTION
+ bytesReceivedFromServer[3] = 0x00; // NO_PROTECTION
+
+ if(self.username != null) {
+ var authorization_id_bytes = new Buffer(self.username, 'utf8');
+ authorization_id_bytes.copy(bytesReceivedFromServer, 4, 0);
+ }
+
+ self.security_context.queryContextAttributes(0x00, function(err, sizes) {
+ if(err) return callback(err);
+
+ var buffers = [
+ new SecurityBuffer(SecurityBuffer.TOKEN, new Buffer(sizes.securityTrailer))
+ , new SecurityBuffer(SecurityBuffer.DATA, bytesReceivedFromServer)
+ , new SecurityBuffer(SecurityBuffer.PADDING, new Buffer(sizes.blockSize))
+ ]
+
+ var descriptor = new SecurityBufferDescriptor(buffers);
+
+ self.security_context.encryptMessage(descriptor, 0x80000001, function(err, security_context) {
+ if(err) return callback(err);
+ callback(null, security_context.payload);
+ });
+ });
+ });
+ }
+}
+
+/*******************************************************************
+ *
+ * UNIX MIT Kerberos processor
+ *
+ *******************************************************************/
+var UnixMongoProcessor = function(host, port, service_name) {
+ this.host = host;
+ this.port = port
+ // SSIP classes
+ this.Kerberos = require("../kerberos").Kerberos;
+ this.kerberos = new this.Kerberos();
+ service_name = service_name || "mongodb";
+ // Set up first transition
+ this._transition = UnixMongoProcessor.first_transition(this);
+ // Set up target
+ this.target = format("%s@%s", service_name, host);
+ // Number of retries
+ this.retries = 10;
+}
+
+UnixMongoProcessor.prototype.init = function(username, password, callback) {
+ var self = this;
+ this.username = username;
+ this.password = password;
+ // Call client initiate
+ this.kerberos.authGSSClientInit(
+ self.target
+ , this.Kerberos.GSS_C_MUTUAL_FLAG, function(err, context) {
+ self.context = context;
+ // Return the context
+ callback(null, context);
+ });
+}
+
+UnixMongoProcessor.prototype.transition = function(payload, callback) {
+ if(this._transition == null) return callback(new Error("Transition finished"));
+ this._transition(payload, callback);
+}
+
+UnixMongoProcessor.first_transition = function(self) {
+ return function(payload, callback) {
+ self.kerberos.authGSSClientStep(self.context, '', function(err, result) {
+ if(err) return callback(err);
+ // Set up the next step
+ self._transition = UnixMongoProcessor.second_transition(self);
+ // Return the payload
+ callback(null, self.context.response);
+ })
+ }
+}
+
+UnixMongoProcessor.second_transition = function(self) {
+ return function(payload, callback) {
+ self.kerberos.authGSSClientStep(self.context, payload, function(err, result) {
+ if(err && self.retries == 0) return callback(err);
+ // Attempt to re-establish a context
+ if(err) {
+ // Adjust the number of retries
+ self.retries = self.retries - 1;
+ // Call same step again
+ return self.transition(payload, callback);
+ }
+
+ // Set up the next step
+ self._transition = UnixMongoProcessor.third_transition(self);
+ // Return the payload
+ callback(null, self.context.response || '');
+ });
+ }
+}
+
+UnixMongoProcessor.third_transition = function(self) {
+ return function(payload, callback) {
+ // GSS Client Unwrap
+ self.kerberos.authGSSClientUnwrap(self.context, payload, function(err, result) {
+ if(err) return callback(err, false);
+
+ // Wrap the response
+ self.kerberos.authGSSClientWrap(self.context, self.context.response, self.username, function(err, result) {
+ if(err) return callback(err, false);
+ // Set up the next step
+ self._transition = UnixMongoProcessor.fourth_transition(self);
+ // Return the payload
+ callback(null, self.context.response);
+ });
+ });
+ }
+}
+
+UnixMongoProcessor.fourth_transition = function(self) {
+ return function(payload, callback) {
+ // Clean up context
+ self.kerberos.authGSSClientClean(self.context, function(err, result) {
+ if(err) return callback(err, false);
+ // Set the transition to null
+ self._transition = null;
+ // Callback with valid authentication
+ callback(null, true);
+ });
+ }
+}
+
+// Set the process
+exports.MongoAuthProcess = MongoAuthProcess;
\ No newline at end of file
diff --git a/lib/base64.c b/lib/base64.c
new file mode 100644
index 0000000..aca0a61
--- /dev/null
+++ b/lib/base64.c
@@ -0,0 +1,134 @@
+/**
+ * Copyright (c) 2006-2008 Apple Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ **/
+
+#include "base64.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+
+void die2(const char *message) {
+ if(errno) {
+ perror(message);
+ } else {
+ printf("ERROR: %s\n", message);
+ }
+
+ exit(1);
+}
+
+// base64 tables
+static char basis_64[] =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+static signed char index_64[128] =
+{
+ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
+ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
+ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,62, -1,-1,-1,63,
+ 52,53,54,55, 56,57,58,59, 60,61,-1,-1, -1,-1,-1,-1,
+ -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11,12,13,14,
+ 15,16,17,18, 19,20,21,22, 23,24,25,-1, -1,-1,-1,-1,
+ -1,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40,
+ 41,42,43,44, 45,46,47,48, 49,50,51,-1, -1,-1,-1,-1
+};
+#define CHAR64(c) (((c) < 0 || (c) > 127) ? -1 : index_64[(c)])
+
+// base64_encode : base64 encode
+//
+// value : data to encode
+// vlen : length of data
+// (result) : new char[] - c-str of result
+char *base64_encode(const unsigned char *value, int vlen)
+{
+ char *result = (char *)malloc((vlen * 4) / 3 + 5);
+ if(result == NULL) die2("Memory allocation failed");
+ char *out = result;
+ while (vlen >= 3)
+ {
+ *out++ = basis_64[value[0] >> 2];
+ *out++ = basis_64[((value[0] << 4) & 0x30) | (value[1] >> 4)];
+ *out++ = basis_64[((value[1] << 2) & 0x3C) | (value[2] >> 6)];
+ *out++ = basis_64[value[2] & 0x3F];
+ value += 3;
+ vlen -= 3;
+ }
+ if (vlen > 0)
+ {
+ *out++ = basis_64[value[0] >> 2];
+ unsigned char oval = (value[0] << 4) & 0x30;
+ if (vlen > 1) oval |= value[1] >> 4;
+ *out++ = basis_64[oval];
+ *out++ = (vlen < 2) ? '=' : basis_64[(value[1] << 2) & 0x3C];
+ *out++ = '=';
+ }
+ *out = '\0';
+
+ return result;
+}
+
+// base64_decode : base64 decode
+//
+// value : c-str to decode
+// rlen : length of decoded result
+// (result) : new unsigned char[] - decoded result
+unsigned char *base64_decode(const char *value, int *rlen)
+{
+ *rlen = 0;
+ int c1, c2, c3, c4;
+
+ int vlen = strlen(value);
+ unsigned char *result =(unsigned char *)malloc((vlen * 3) / 4 + 1);
+ if(result == NULL) die2("Memory allocation failed");
+ unsigned char *out = result;
+
+ while (1)
+ {
+ if (value[0]==0)
+ return result;
+ c1 = value[0];
+ if (CHAR64(c1) == -1)
+ goto base64_decode_error;;
+ c2 = value[1];
+ if (CHAR64(c2) == -1)
+ goto base64_decode_error;;
+ c3 = value[2];
+ if ((c3 != '=') && (CHAR64(c3) == -1))
+ goto base64_decode_error;;
+ c4 = value[3];
+ if ((c4 != '=') && (CHAR64(c4) == -1))
+ goto base64_decode_error;;
+
+ value += 4;
+ *out++ = (CHAR64(c1) << 2) | (CHAR64(c2) >> 4);
+ *rlen += 1;
+ if (c3 != '=')
+ {
+ *out++ = ((CHAR64(c2) << 4) & 0xf0) | (CHAR64(c3) >> 2);
+ *rlen += 1;
+ if (c4 != '=')
+ {
+ *out++ = ((CHAR64(c3) << 6) & 0xc0) | CHAR64(c4);
+ *rlen += 1;
+ }
+ }
+ }
+
+base64_decode_error:
+ *result = 0;
+ *rlen = 0;
+ return result;
+}
diff --git a/lib/base64.h b/lib/base64.h
new file mode 100644
index 0000000..9152e6a
--- /dev/null
+++ b/lib/base64.h
@@ -0,0 +1,22 @@
+/**
+ * Copyright (c) 2006-2008 Apple Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ **/
+#ifndef BASE64_H
+#define BASE64_H
+
+char *base64_encode(const unsigned char *value, int vlen);
+unsigned char *base64_decode(const char *value, int *rlen);
+
+#endif
\ No newline at end of file
diff --git a/lib/kerberos.cc b/lib/kerberos.cc
new file mode 100644
index 0000000..ffe865f
--- /dev/null
+++ b/lib/kerberos.cc
@@ -0,0 +1,864 @@
+#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;
+} 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;
+} AuthGSSServerInitCall;
+
+typedef struct AuthGSSServerCleanCall {
+ KerberosContext *context;
+} AuthGSSServerCleanCall;
+
+typedef struct AuthGSSServerStepCall {
+ KerberosContext *context;
+ char *auth_data;
+} AuthGSSServerStepCall;
+
+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);
+
+ constructor_template.Reset(t);
+
+ // Set the symbol
+ target->ForceSet(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, state);
+
+ // Release the parameter struct memory
+ free(call->uri);
+ 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) {
+ // Ensure valid call
+ if(info.Length() != 3) return Nan::ThrowError("Requires a service string uri, integer flags and a callback function");
+ if(info.Length() == 3 && (!info[0]->IsString() || !info[1]->IsInt32() || !info[2]->IsFunction()))
+ return Nan::ThrowError("Requires a service string uri, integer flags and a callback function");
+
+ 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);
+
+ // Allocate a structure
+ AuthGSSClientCall *call = (AuthGSSClientCall *)calloc(1, sizeof(AuthGSSClientCall));
+ if(call == NULL) die("Memory allocation failed");
+ call->flags =info[1]->ToInt32()->Uint32Value();
+ call->uri = service_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 = _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, state);
+
+ // Release the parameter struct memory
+ 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;
+ 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() != 2) return Nan::ThrowError("Requires a service string service and a callback function");
+ if(!info[0]->IsString() || !info[1]->IsFunction()) return Nan::ThrowError("Requires a service string service and a callback function");
+
+ 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);
+
+ // Allocate a structure
+ AuthGSSServerInitCall *call = (AuthGSSServerInitCall *)calloc(1, sizeof(AuthGSSServerInitCall));
+ if(call == NULL) die("Memory allocation failed");
+ call->service = service_str;
+
+ // 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 = _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());
+}
+
+// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+// 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);
diff --git a/lib/kerberos.h b/lib/kerberos.h
new file mode 100644
index 0000000..beafa4d
--- /dev/null
+++ b/lib/kerberos.h
@@ -0,0 +1,50 @@
+#ifndef KERBEROS_H
+#define KERBEROS_H
+
+#include <node.h>
+#include <gssapi/gssapi.h>
+#include <gssapi/gssapi_generic.h>
+#include <gssapi/gssapi_krb5.h>
+
+#include "nan.h"
+#include <node_object_wrap.h>
+#include <v8.h>
+
+extern "C" {
+ #include "kerberosgss.h"
+}
+
+using namespace v8;
+using namespace node;
+
+class Kerberos : public Nan::ObjectWrap {
+
+public:
+ Kerberos();
+ ~Kerberos() {};
+
+ // Constructor used for creating new Kerberos objects from C++
+ static Nan::Persistent<FunctionTemplate> constructor_template;
+
+ // Initialize function for the object
+ static void Initialize(Nan::ADDON_REGISTER_FUNCTION_ARGS_TYPE target);
+
+ // Method available
+ static NAN_METHOD(AuthGSSClientInit);
+ static NAN_METHOD(AuthGSSClientStep);
+ static NAN_METHOD(AuthGSSClientUnwrap);
+ static NAN_METHOD(AuthGSSClientWrap);
+ static NAN_METHOD(AuthGSSClientClean);
+ static NAN_METHOD(AuthGSSServerInit);
+ static NAN_METHOD(AuthGSSServerClean);
+ static NAN_METHOD(AuthGSSServerStep);
+
+private:
+ static NAN_METHOD(New);
+ // Handles the uv calls
+ static void Process(uv_work_t* work_req);
+ // Called after work is done
+ static void After(uv_work_t* work_req);
+};
+
+#endif
diff --git a/lib/kerberos.js b/lib/kerberos.js
new file mode 100644
index 0000000..4ce8fb1
--- /dev/null
+++ b/lib/kerberos.js
@@ -0,0 +1,128 @@
+var kerberos = require('../build/Release/kerberos')
+ , KerberosNative = kerberos.Kerberos;
+
+var Kerberos = function() {
+ this._native_kerberos = new KerberosNative();
+}
+
+// callback takes two arguments, an error string if defined and a new context
+// uri should be given as service at host. Services are not always defined
+// in a straightforward way. Use 'HTTP' for SPNEGO / Negotiate authentication.
+Kerberos.prototype.authGSSClientInit = function(uri, flags, callback) {
+ return this._native_kerberos.authGSSClientInit(uri, flags, callback);
+}
+
+// This will obtain credentials using a credentials cache. To override the default
+// location (posible /tmp/krb5cc_nnnnnn, where nnnn is your numeric uid) use
+// the environment variable KRB5CNAME.
+// The credentials (suitable for using in an 'Authenticate: ' header, when prefixed
+// with 'Negotiate ') will be available as context.response inside the callback
+// if no error is indicated.
+// callback takes one argument, an error string if defined
+Kerberos.prototype.authGSSClientStep = function(context, challenge, callback) {
+ if(typeof challenge == 'function') {
+ callback = challenge;
+ challenge = '';
+ }
+
+ return this._native_kerberos.authGSSClientStep(context, challenge, callback);
+}
+
+Kerberos.prototype.authGSSClientUnwrap = function(context, challenge, callback) {
+ if(typeof challenge == 'function') {
+ callback = challenge;
+ challenge = '';
+ }
+
+ return this._native_kerberos.authGSSClientUnwrap(context, challenge, callback);
+}
+
+Kerberos.prototype.authGSSClientWrap = function(context, challenge, user_name, callback) {
+ if(typeof user_name == 'function') {
+ callback = user_name;
+ user_name = '';
+ }
+
+ return this._native_kerberos.authGSSClientWrap(context, challenge, user_name, callback);
+}
+
+// free memory used by a context created using authGSSClientInit.
+// callback takes one argument, an error string if defined.
+Kerberos.prototype.authGSSClientClean = function(context, callback) {
+ return this._native_kerberos.authGSSClientClean(context, callback);
+}
+
+// The server will obtain credentials using a keytab. To override the
+// default location (probably /etc/krb5.keytab) set the KRB5_KTNAME
+// environment variable.
+// The service name should be in the form service, or service at host.name
+// e.g. for HTTP, use "HTTP" or "HTTP at my.host.name". See gss_import_name
+// for GSS_C_NT_HOSTBASED_SERVICE.
+// callback takes two arguments, an error string if defined and a new context
+Kerberos.prototype.authGSSServerInit = function(service, callback) {
+ return this._native_kerberos.authGSSServerInit(service, callback);
+};
+
+//callback takes one argument, an error string if defined.
+Kerberos.prototype.authGSSServerClean = function(context, callback) {
+ return this._native_kerberos.authGSSServerClean(context, callback);
+};
+
+// authData should be the base64 encoded authentication data obtained
+// from client, e.g., in the Authorization header (without the leading
+// "Negotiate " string) during SPNEGO authentication. The authenticated user
+// is available in context.username after successful authentication.
+// callback takes one argument, an error string if defined.
+Kerberos.prototype.authGSSServerStep = function(context, authData, callback) {
+ return this._native_kerberos.authGSSServerStep(context, authData, callback);
+};
+
+Kerberos.prototype.acquireAlternateCredentials = function(user_name, password, domain) {
+ return this._native_kerberos.acquireAlternateCredentials(user_name, password, domain);
+}
+
+Kerberos.prototype.prepareOutboundPackage = function(principal, inputdata) {
+ return this._native_kerberos.prepareOutboundPackage(principal, inputdata);
+}
+
+Kerberos.prototype.decryptMessage = function(challenge) {
+ return this._native_kerberos.decryptMessage(challenge);
+}
+
+Kerberos.prototype.encryptMessage = function(challenge) {
+ return this._native_kerberos.encryptMessage(challenge);
+}
+
+Kerberos.prototype.queryContextAttribute = function(attribute) {
+ if(typeof attribute != 'number' && attribute != 0x00) throw new Error("Attribute not supported");
+ return this._native_kerberos.queryContextAttribute(attribute);
+}
+
+// Some useful result codes
+Kerberos.AUTH_GSS_CONTINUE = 0;
+Kerberos.AUTH_GSS_COMPLETE = 1;
+
+// Some useful gss flags
+Kerberos.GSS_C_DELEG_FLAG = 1;
+Kerberos.GSS_C_MUTUAL_FLAG = 2;
+Kerberos.GSS_C_REPLAY_FLAG = 4;
+Kerberos.GSS_C_SEQUENCE_FLAG = 8;
+Kerberos.GSS_C_CONF_FLAG = 16;
+Kerberos.GSS_C_INTEG_FLAG = 32;
+Kerberos.GSS_C_ANON_FLAG = 64;
+Kerberos.GSS_C_PROT_READY_FLAG = 128;
+Kerberos.GSS_C_TRANS_FLAG = 256;
+
+// Export Kerberos class
+exports.Kerberos = Kerberos;
+
+// If we have SSPI (windows)
+if(kerberos.SecurityCredentials) {
+ // Put all SSPI classes in it's own namespace
+ exports.SSIP = {
+ SecurityCredentials: require('./win32/wrappers/security_credentials').SecurityCredentials
+ , SecurityContext: require('./win32/wrappers/security_context').SecurityContext
+ , SecurityBuffer: require('./win32/wrappers/security_buffer').SecurityBuffer
+ , SecurityBufferDescriptor: require('./win32/wrappers/security_buffer_descriptor').SecurityBufferDescriptor
+ }
+}
diff --git a/lib/kerberos_context.cc b/lib/kerberos_context.cc
new file mode 100644
index 0000000..f1b164a
--- /dev/null
+++ b/lib/kerberos_context.cc
@@ -0,0 +1,116 @@
+#include "kerberos_context.h"
+
+Nan::Persistent<FunctionTemplate> KerberosContext::constructor_template;
+
+KerberosContext::KerberosContext() : Nan::ObjectWrap() {
+ state = NULL;
+ server_state = NULL;
+}
+
+KerberosContext::~KerberosContext() {
+}
+
+KerberosContext* KerberosContext::New() {
+ Nan::HandleScope scope;
+ Local<Object> obj = Nan::New(constructor_template)->GetFunction()->NewInstance();
+ KerberosContext *kerberos_context = Nan::ObjectWrap::Unwrap<KerberosContext>(obj);
+ return kerberos_context;
+}
+
+NAN_METHOD(KerberosContext::New) {
+ // Create code object
+ KerberosContext *kerberos_context = new KerberosContext();
+ // Wrap it
+ kerberos_context->Wrap(info.This());
+ // Return the object
+ info.GetReturnValue().Set(info.This());
+}
+
+void KerberosContext::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<v8::FunctionTemplate>(static_cast<NAN_METHOD((*))>(New));
+ t->InstanceTemplate()->SetInternalFieldCount(1);
+ t->SetClassName(Nan::New<String>("KerberosContext").ToLocalChecked());
+
+ // Get prototype
+ Local<ObjectTemplate> proto = t->PrototypeTemplate();
+
+ // Getter for the response
+ Nan::SetAccessor(proto, Nan::New<String>("response").ToLocalChecked(), KerberosContext::ResponseGetter);
+
+ // Getter for the username
+ Nan::SetAccessor(proto, Nan::New<String>("username").ToLocalChecked(), KerberosContext::UsernameGetter);
+
+ // Getter for the targetname - server side only
+ Nan::SetAccessor(proto, Nan::New<String>("targetname").ToLocalChecked(), KerberosContext::TargetnameGetter);
+
+ // Set persistent
+ constructor_template.Reset(t);
+ // NanAssignPersistent(constructor_template, t);
+
+ // Set the symbol
+ target->ForceSet(Nan::New<String>("KerberosContext").ToLocalChecked(), t->GetFunction());
+}
+
+
+// Response Setter / Getter
+NAN_GETTER(KerberosContext::ResponseGetter) {
+ gss_client_state *client_state;
+ gss_server_state *server_state;
+
+ // Unpack the object
+ KerberosContext *context = Nan::ObjectWrap::Unwrap<KerberosContext>(info.This());
+
+ // Response could come from client or server state...
+ client_state = context->state;
+ server_state = context->server_state;
+
+ // If client state is in use, take response from there, otherwise from server
+ char *response = client_state != NULL ? client_state->response :
+ server_state != NULL ? server_state->response : NULL;
+
+ if(response == NULL) {
+ info.GetReturnValue().Set(Nan::Null());
+ } else {
+ // Return the response
+ info.GetReturnValue().Set(Nan::New<String>(response).ToLocalChecked());
+ }
+}
+
+// username Getter
+NAN_GETTER(KerberosContext::UsernameGetter) {
+ // Unpack the object
+ KerberosContext *context = Nan::ObjectWrap::Unwrap<KerberosContext>(info.This());
+
+ gss_client_state *client_state = context->state;
+ gss_server_state *server_state = context->server_state;
+
+ // If client state is in use, take response from there, otherwise from server
+ char *username = client_state != NULL ? client_state->username :
+ server_state != NULL ? server_state->username : NULL;
+
+ if(username == NULL) {
+ info.GetReturnValue().Set(Nan::Null());
+ } else {
+ info.GetReturnValue().Set(Nan::New<String>(username).ToLocalChecked());
+ }
+}
+
+// targetname Getter - server side only
+NAN_GETTER(KerberosContext::TargetnameGetter) {
+ // Unpack the object
+ KerberosContext *context = Nan::ObjectWrap::Unwrap<KerberosContext>(info.This());
+
+ gss_server_state *server_state = context->server_state;
+
+ char *targetname = server_state != NULL ? server_state->targetname : NULL;
+
+ if(targetname == NULL) {
+ info.GetReturnValue().Set(Nan::Null());
+ } else {
+ info.GetReturnValue().Set(Nan::New<String>(targetname).ToLocalChecked());
+ }
+}
diff --git a/lib/kerberos_context.h b/lib/kerberos_context.h
new file mode 100644
index 0000000..b20ff61
--- /dev/null
+++ b/lib/kerberos_context.h
@@ -0,0 +1,63 @@
+#ifndef KERBEROS_CONTEXT_H
+#define KERBEROS_CONTEXT_H
+
+#include <node.h>
+#include <gssapi/gssapi.h>
+#include <gssapi/gssapi_generic.h>
+#include <gssapi/gssapi_krb5.h>
+
+#include "nan.h"
+#include <node_object_wrap.h>
+#include <v8.h>
+
+extern "C" {
+ #include "kerberosgss.h"
+}
+
+using namespace v8;
+using namespace node;
+
+class KerberosContext : public Nan::ObjectWrap {
+
+public:
+ KerberosContext();
+ ~KerberosContext();
+
+ static inline bool HasInstance(Local<Value> val) {
+ if (!val->IsObject()) return false;
+ Local<Object> obj = val->ToObject();
+ return Nan::New(constructor_template)->HasInstance(obj);
+ };
+
+ inline bool IsClientInstance() {
+ return state != NULL;
+ }
+
+ inline bool IsServerInstance() {
+ return server_state != NULL;
+ }
+
+ // Constructor used for creating new Kerberos objects from C++
+ static Nan::Persistent<FunctionTemplate> constructor_template;
+
+ // Initialize function for the object
+ static void Initialize(Nan::ADDON_REGISTER_FUNCTION_ARGS_TYPE target);
+
+ // Public constructor
+ static KerberosContext* New();
+
+ // Handle to the kerberos client context
+ gss_client_state *state;
+
+ // Handle to the kerberos server context
+ gss_server_state *server_state;
+
+private:
+ static NAN_METHOD(New);
+ // In either client state or server state
+ static NAN_GETTER(ResponseGetter);
+ static NAN_GETTER(UsernameGetter);
+ // Only in the "server_state"
+ static NAN_GETTER(TargetnameGetter);
+};
+#endif
diff --git a/lib/kerberosgss.c b/lib/kerberosgss.c
new file mode 100644
index 0000000..c6983b5
--- /dev/null
+++ b/lib/kerberosgss.c
@@ -0,0 +1,688 @@
+/**
+ * Copyright (c) 2006-2010 Apple Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ **/
+
+#include "kerberosgss.h"
+
+#include "base64.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <arpa/inet.h>
+#include <errno.h>
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+
+void die1(const char *message) {
+ if(errno) {
+ perror(message);
+ } else {
+ printf("ERROR: %s\n", message);
+ }
+
+ exit(1);
+}
+
+static gss_client_response *gss_error(const char *func, const char *op, OM_uint32 err_maj, OM_uint32 err_min);
+
+/*
+char* server_principal_details(const char* service, const char* hostname)
+{
+ char match[1024];
+ int match_len = 0;
+ char* result = NULL;
+
+ int code;
+ krb5_context kcontext;
+ krb5_keytab kt = NULL;
+ krb5_kt_cursor cursor = NULL;
+ krb5_keytab_entry entry;
+ char* pname = NULL;
+
+ // Generate the principal prefix we want to match
+ snprintf(match, 1024, "%s/%s@", service, hostname);
+ match_len = strlen(match);
+
+ code = krb5_init_context(&kcontext);
+ if (code)
+ {
+ PyErr_SetObject(KrbException_class, Py_BuildValue("((s:i))",
+ "Cannot initialize Kerberos5 context", code));
+ return NULL;
+ }
+
+ if ((code = krb5_kt_default(kcontext, &kt)))
+ {
+ PyErr_SetObject(KrbException_class, Py_BuildValue("((s:i))",
+ "Cannot get default keytab", code));
+ goto end;
+ }
+
+ if ((code = krb5_kt_start_seq_get(kcontext, kt, &cursor)))
+ {
+ PyErr_SetObject(KrbException_class, Py_BuildValue("((s:i))",
+ "Cannot get sequence cursor from keytab", code));
+ goto end;
+ }
+
+ while ((code = krb5_kt_next_entry(kcontext, kt, &entry, &cursor)) == 0)
+ {
+ if ((code = krb5_unparse_name(kcontext, entry.principal, &pname)))
+ {
+ PyErr_SetObject(KrbException_class, Py_BuildValue("((s:i))",
+ "Cannot parse principal name from keytab", code));
+ goto end;
+ }
+
+ if (strncmp(pname, match, match_len) == 0)
+ {
+ result = malloc(strlen(pname) + 1);
+ strcpy(result, pname);
+ krb5_free_unparsed_name(kcontext, pname);
+ krb5_free_keytab_entry_contents(kcontext, &entry);
+ break;
+ }
+
+ krb5_free_unparsed_name(kcontext, pname);
+ krb5_free_keytab_entry_contents(kcontext, &entry);
+ }
+
+ if (result == NULL)
+ {
+ PyErr_SetObject(KrbException_class, Py_BuildValue("((s:i))",
+ "Principal not found in keytab", -1));
+ }
+
+end:
+ if (cursor)
+ krb5_kt_end_seq_get(kcontext, kt, &cursor);
+ if (kt)
+ krb5_kt_close(kcontext, kt);
+ krb5_free_context(kcontext);
+
+ return result;
+}
+*/
+gss_client_response *authenticate_gss_client_init(const char* service, long int gss_flags, gss_client_state* state) {
+ OM_uint32 maj_stat;
+ OM_uint32 min_stat;
+ gss_buffer_desc name_token = GSS_C_EMPTY_BUFFER;
+ gss_client_response *response = NULL;
+ int ret = AUTH_GSS_COMPLETE;
+
+ state->server_name = GSS_C_NO_NAME;
+ state->context = GSS_C_NO_CONTEXT;
+ state->gss_flags = gss_flags;
+ state->username = NULL;
+ state->response = NULL;
+
+ // Import server name first
+ name_token.length = strlen(service);
+ name_token.value = (char *)service;
+
+ maj_stat = gss_import_name(&min_stat, &name_token, gss_krb5_nt_service_name, &state->server_name);
+
+ if (GSS_ERROR(maj_stat)) {
+ response = gss_error(__func__, "gss_import_name", maj_stat, min_stat);
+ response->return_code = AUTH_GSS_ERROR;
+ goto end;
+ }
+
+end:
+ if(response == NULL) {
+ response = calloc(1, sizeof(gss_client_response));
+ if(response == NULL) die1("Memory allocation failed");
+ response->return_code = ret;
+ }
+
+ return response;
+}
+
+gss_client_response *authenticate_gss_client_clean(gss_client_state *state) {
+ OM_uint32 min_stat;
+ int ret = AUTH_GSS_COMPLETE;
+ gss_client_response *response = NULL;
+
+ if(state->context != GSS_C_NO_CONTEXT)
+ gss_delete_sec_context(&min_stat, &state->context, GSS_C_NO_BUFFER);
+
+ if(state->server_name != GSS_C_NO_NAME)
+ gss_release_name(&min_stat, &state->server_name);
+
+ if(state->username != NULL) {
+ free(state->username);
+ state->username = NULL;
+ }
+
+ if (state->response != NULL) {
+ free(state->response);
+ state->response = NULL;
+ }
+
+ if(response == NULL) {
+ response = calloc(1, sizeof(gss_client_response));
+ if(response == NULL) die1("Memory allocation failed");
+ response->return_code = ret;
+ }
+
+ return response;
+}
+
+gss_client_response *authenticate_gss_client_step(gss_client_state* state, const char* challenge) {
+ OM_uint32 maj_stat;
+ OM_uint32 min_stat;
+ gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
+ gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
+ int ret = AUTH_GSS_CONTINUE;
+ gss_client_response *response = NULL;
+
+ // Always clear out the old response
+ if (state->response != NULL) {
+ free(state->response);
+ state->response = NULL;
+ }
+
+ // If there is a challenge (data from the server) we need to give it to GSS
+ if (challenge && *challenge) {
+ int len;
+ input_token.value = base64_decode(challenge, &len);
+ input_token.length = len;
+ }
+
+ // Do GSSAPI step
+ maj_stat = gss_init_sec_context(&min_stat,
+ GSS_C_NO_CREDENTIAL,
+ &state->context,
+ state->server_name,
+ GSS_C_NO_OID,
+ (OM_uint32)state->gss_flags,
+ 0,
+ GSS_C_NO_CHANNEL_BINDINGS,
+ &input_token,
+ NULL,
+ &output_token,
+ NULL,
+ NULL);
+
+ if ((maj_stat != GSS_S_COMPLETE) && (maj_stat != GSS_S_CONTINUE_NEEDED)) {
+ response = gss_error(__func__, "gss_init_sec_context", maj_stat, min_stat);
+ response->return_code = AUTH_GSS_ERROR;
+ goto end;
+ }
+
+ ret = (maj_stat == GSS_S_COMPLETE) ? AUTH_GSS_COMPLETE : AUTH_GSS_CONTINUE;
+ // Grab the client response to send back to the server
+ if(output_token.length) {
+ state->response = base64_encode((const unsigned char *)output_token.value, output_token.length);
+ maj_stat = gss_release_buffer(&min_stat, &output_token);
+ }
+
+ // Try to get the user name if we have completed all GSS operations
+ if (ret == AUTH_GSS_COMPLETE) {
+ gss_name_t gssuser = GSS_C_NO_NAME;
+ maj_stat = gss_inquire_context(&min_stat, state->context, &gssuser, NULL, NULL, NULL, NULL, NULL, NULL);
+
+ if(GSS_ERROR(maj_stat)) {
+ response = gss_error(__func__, "gss_inquire_context", maj_stat, min_stat);
+ response->return_code = AUTH_GSS_ERROR;
+ goto end;
+ }
+
+ gss_buffer_desc name_token;
+ name_token.length = 0;
+ maj_stat = gss_display_name(&min_stat, gssuser, &name_token, NULL);
+
+ if(GSS_ERROR(maj_stat)) {
+ if(name_token.value)
+ gss_release_buffer(&min_stat, &name_token);
+ gss_release_name(&min_stat, &gssuser);
+
+ response = gss_error(__func__, "gss_display_name", maj_stat, min_stat);
+ response->return_code = AUTH_GSS_ERROR;
+ goto end;
+ } else {
+ state->username = (char *)malloc(name_token.length + 1);
+ if(state->username == NULL) die1("Memory allocation failed");
+ strncpy(state->username, (char*) name_token.value, name_token.length);
+ state->username[name_token.length] = 0;
+ gss_release_buffer(&min_stat, &name_token);
+ gss_release_name(&min_stat, &gssuser);
+ }
+ }
+
+end:
+ if(output_token.value)
+ gss_release_buffer(&min_stat, &output_token);
+ if(input_token.value)
+ free(input_token.value);
+
+ if(response == NULL) {
+ response = calloc(1, sizeof(gss_client_response));
+ if(response == NULL) die1("Memory allocation failed");
+ response->return_code = ret;
+ }
+
+ // Return the response
+ return response;
+}
+
+gss_client_response *authenticate_gss_client_unwrap(gss_client_state *state, const char *challenge) {
+ OM_uint32 maj_stat;
+ OM_uint32 min_stat;
+ gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
+ gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
+ gss_client_response *response = NULL;
+ int ret = AUTH_GSS_CONTINUE;
+
+ // Always clear out the old response
+ if(state->response != NULL) {
+ free(state->response);
+ state->response = NULL;
+ }
+
+ // If there is a challenge (data from the server) we need to give it to GSS
+ if(challenge && *challenge) {
+ int len;
+ input_token.value = base64_decode(challenge, &len);
+ input_token.length = len;
+ }
+
+ // Do GSSAPI step
+ maj_stat = gss_unwrap(&min_stat,
+ state->context,
+ &input_token,
+ &output_token,
+ NULL,
+ NULL);
+
+ if(maj_stat != GSS_S_COMPLETE) {
+ response = gss_error(__func__, "gss_unwrap", maj_stat, min_stat);
+ response->return_code = AUTH_GSS_ERROR;
+ goto end;
+ } else {
+ ret = AUTH_GSS_COMPLETE;
+ }
+
+ // Grab the client response
+ if(output_token.length) {
+ state->response = base64_encode((const unsigned char *)output_token.value, output_token.length);
+ gss_release_buffer(&min_stat, &output_token);
+ }
+end:
+ if(output_token.value)
+ gss_release_buffer(&min_stat, &output_token);
+ if(input_token.value)
+ free(input_token.value);
+
+ if(response == NULL) {
+ response = calloc(1, sizeof(gss_client_response));
+ if(response == NULL) die1("Memory allocation failed");
+ response->return_code = ret;
+ }
+
+ // Return the response
+ return response;
+}
+
+gss_client_response *authenticate_gss_client_wrap(gss_client_state* state, const char* challenge, const char* user) {
+ OM_uint32 maj_stat;
+ OM_uint32 min_stat;
+ gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
+ gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
+ int ret = AUTH_GSS_CONTINUE;
+ gss_client_response *response = NULL;
+ char buf[4096], server_conf_flags;
+ unsigned long buf_size;
+
+ // Always clear out the old response
+ if(state->response != NULL) {
+ free(state->response);
+ state->response = NULL;
+ }
+
+ if(challenge && *challenge) {
+ int len;
+ input_token.value = base64_decode(challenge, &len);
+ input_token.length = len;
+ }
+
+ if(user) {
+ // get bufsize
+ server_conf_flags = ((char*) input_token.value)[0];
+ ((char*) input_token.value)[0] = 0;
+ buf_size = ntohl(*((long *) input_token.value));
+ free(input_token.value);
+#ifdef PRINTFS
+ printf("User: %s, %c%c%c\n", user,
+ server_conf_flags & GSS_AUTH_P_NONE ? 'N' : '-',
+ server_conf_flags & GSS_AUTH_P_INTEGRITY ? 'I' : '-',
+ server_conf_flags & GSS_AUTH_P_PRIVACY ? 'P' : '-');
+ printf("Maximum GSS token size is %ld\n", buf_size);
+#endif
+
+ // agree to terms (hack!)
+ buf_size = htonl(buf_size); // not relevant without integrity/privacy
+ memcpy(buf, &buf_size, 4);
+ buf[0] = GSS_AUTH_P_NONE;
+ // server decides if principal can log in as user
+ strncpy(buf + 4, user, sizeof(buf) - 4);
+ input_token.value = buf;
+ input_token.length = 4 + strlen(user);
+ }
+
+ // Do GSSAPI wrap
+ maj_stat = gss_wrap(&min_stat,
+ state->context,
+ 0,
+ GSS_C_QOP_DEFAULT,
+ &input_token,
+ NULL,
+ &output_token);
+
+ if (maj_stat != GSS_S_COMPLETE) {
+ response = gss_error(__func__, "gss_wrap", maj_stat, min_stat);
+ response->return_code = AUTH_GSS_ERROR;
+ goto end;
+ } else
+ ret = AUTH_GSS_COMPLETE;
+ // Grab the client response to send back to the server
+ if (output_token.length) {
+ state->response = base64_encode((const unsigned char *)output_token.value, output_token.length);;
+ gss_release_buffer(&min_stat, &output_token);
+ }
+end:
+ if (output_token.value)
+ gss_release_buffer(&min_stat, &output_token);
+
+ if(response == NULL) {
+ response = calloc(1, sizeof(gss_client_response));
+ if(response == NULL) die1("Memory allocation failed");
+ response->return_code = ret;
+ }
+
+ // Return the response
+ return response;
+}
+
+gss_client_response *authenticate_gss_server_init(const char *service, gss_server_state *state)
+{
+ OM_uint32 maj_stat;
+ OM_uint32 min_stat;
+ gss_buffer_desc name_token = GSS_C_EMPTY_BUFFER;
+ int ret = AUTH_GSS_COMPLETE;
+ gss_client_response *response = NULL;
+
+ state->context = GSS_C_NO_CONTEXT;
+ state->server_name = GSS_C_NO_NAME;
+ state->client_name = GSS_C_NO_NAME;
+ state->server_creds = GSS_C_NO_CREDENTIAL;
+ state->client_creds = GSS_C_NO_CREDENTIAL;
+ state->username = NULL;
+ state->targetname = NULL;
+ state->response = NULL;
+
+ // Server name may be empty which means we aren't going to create our own creds
+ size_t service_len = strlen(service);
+ if (service_len != 0)
+ {
+ // Import server name first
+ name_token.length = strlen(service);
+ name_token.value = (char *)service;
+
+ maj_stat = gss_import_name(&min_stat, &name_token, GSS_C_NT_HOSTBASED_SERVICE, &state->server_name);
+
+ if (GSS_ERROR(maj_stat))
+ {
+ response = gss_error(__func__, "gss_import_name", maj_stat, min_stat);
+ response->return_code = AUTH_GSS_ERROR;
+ goto end;
+ }
+
+ // Get credentials
+ maj_stat = gss_acquire_cred(&min_stat, state->server_name, GSS_C_INDEFINITE,
+ GSS_C_NO_OID_SET, GSS_C_ACCEPT, &state->server_creds, NULL, NULL);
+
+ if (GSS_ERROR(maj_stat))
+ {
+ response = gss_error(__func__, "gss_acquire_cred", maj_stat, min_stat);
+ response->return_code = AUTH_GSS_ERROR;
+ goto end;
+ }
+ }
+
+end:
+ if(response == NULL) {
+ response = calloc(1, sizeof(gss_client_response));
+ if(response == NULL) die1("Memory allocation failed");
+ response->return_code = ret;
+ }
+
+ // Return the response
+ return response;
+}
+
+gss_client_response *authenticate_gss_server_clean(gss_server_state *state)
+{
+ OM_uint32 min_stat;
+ int ret = AUTH_GSS_COMPLETE;
+ gss_client_response *response = NULL;
+
+ if (state->context != GSS_C_NO_CONTEXT)
+ gss_delete_sec_context(&min_stat, &state->context, GSS_C_NO_BUFFER);
+ if (state->server_name != GSS_C_NO_NAME)
+ gss_release_name(&min_stat, &state->server_name);
+ if (state->client_name != GSS_C_NO_NAME)
+ gss_release_name(&min_stat, &state->client_name);
+ if (state->server_creds != GSS_C_NO_CREDENTIAL)
+ gss_release_cred(&min_stat, &state->server_creds);
+ if (state->client_creds != GSS_C_NO_CREDENTIAL)
+ gss_release_cred(&min_stat, &state->client_creds);
+ if (state->username != NULL)
+ {
+ free(state->username);
+ state->username = NULL;
+ }
+ if (state->targetname != NULL)
+ {
+ free(state->targetname);
+ state->targetname = NULL;
+ }
+ if (state->response != NULL)
+ {
+ free(state->response);
+ state->response = NULL;
+ }
+
+ if(response == NULL) {
+ response = calloc(1, sizeof(gss_client_response));
+ if(response == NULL) die1("Memory allocation failed");
+ response->return_code = ret;
+ }
+
+ // Return the response
+ return response;
+}
+
+gss_client_response *authenticate_gss_server_step(gss_server_state *state, const char *auth_data)
+{
+ OM_uint32 maj_stat;
+ OM_uint32 min_stat;
+ gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
+ gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
+ int ret = AUTH_GSS_CONTINUE;
+ gss_client_response *response = NULL;
+
+ // Always clear out the old response
+ if (state->response != NULL)
+ {
+ free(state->response);
+ state->response = NULL;
+ }
+
+ if (auth_data && *auth_data)
+ {
+ int len;
+ input_token.value = base64_decode(auth_data, &len);
+ input_token.length = len;
+ }
+ else
+ {
+ response = calloc(1, sizeof(gss_client_response));
+ if(response == NULL) die1("Memory allocation failed");
+ response->message = strdup("No auth_data value in request from client");
+ response->return_code = AUTH_GSS_ERROR;
+ goto end;
+ }
+
+ maj_stat = gss_accept_sec_context(&min_stat,
+ &state->context,
+ state->server_creds,
+ &input_token,
+ GSS_C_NO_CHANNEL_BINDINGS,
+ &state->client_name,
+ NULL,
+ &output_token,
+ NULL,
+ NULL,
+ &state->client_creds);
+
+ if (GSS_ERROR(maj_stat))
+ {
+ response = gss_error(__func__, "gss_accept_sec_context", maj_stat, min_stat);
+ response->return_code = AUTH_GSS_ERROR;
+ goto end;
+ }
+
+ // Grab the server response to send back to the client
+ if (output_token.length)
+ {
+ state->response = base64_encode((const unsigned char *)output_token.value, output_token.length);
+ maj_stat = gss_release_buffer(&min_stat, &output_token);
+ }
+
+ // Get the user name
+ maj_stat = gss_display_name(&min_stat, state->client_name, &output_token, NULL);
+ if (GSS_ERROR(maj_stat))
+ {
+ response = gss_error(__func__, "gss_display_name", maj_stat, min_stat);
+ response->return_code = AUTH_GSS_ERROR;
+ goto end;
+ }
+ state->username = (char *)malloc(output_token.length + 1);
+ strncpy(state->username, (char*) output_token.value, output_token.length);
+ state->username[output_token.length] = 0;
+
+ // Get the target name if no server creds were supplied
+ if (state->server_creds == GSS_C_NO_CREDENTIAL)
+ {
+ gss_name_t target_name = GSS_C_NO_NAME;
+ maj_stat = gss_inquire_context(&min_stat, state->context, NULL, &target_name, NULL, NULL, NULL, NULL, NULL);
+ if (GSS_ERROR(maj_stat))
+ {
+ response = gss_error(__func__, "gss_inquire_context", maj_stat, min_stat);
+ response->return_code = AUTH_GSS_ERROR;
+ goto end;
+ }
+ maj_stat = gss_display_name(&min_stat, target_name, &output_token, NULL);
+ if (GSS_ERROR(maj_stat))
+ {
+ response = gss_error(__func__, "gss_display_name", maj_stat, min_stat);
+ response->return_code = AUTH_GSS_ERROR;
+ goto end;
+ }
+ state->targetname = (char *)malloc(output_token.length + 1);
+ strncpy(state->targetname, (char*) output_token.value, output_token.length);
+ state->targetname[output_token.length] = 0;
+ }
+
+ ret = AUTH_GSS_COMPLETE;
+
+end:
+ if (output_token.length)
+ gss_release_buffer(&min_stat, &output_token);
+ if (input_token.value)
+ free(input_token.value);
+
+ if(response == NULL) {
+ response = calloc(1, sizeof(gss_client_response));
+ if(response == NULL) die1("Memory allocation failed");
+ response->return_code = ret;
+ }
+
+ // Return the response
+ return response;
+}
+
+gss_client_response *gss_error(const char *func, const char *op, OM_uint32 err_maj, OM_uint32 err_min) {
+ OM_uint32 maj_stat, min_stat;
+ OM_uint32 msg_ctx = 0;
+ gss_buffer_desc status_string;
+
+ gss_client_response *response = calloc(1, sizeof(gss_client_response));
+ if(response == NULL) die1("Memory allocation failed");
+
+ char *message = NULL;
+ message = calloc(1024, 1);
+ if(message == NULL) die1("Memory allocation failed");
+
+ response->message = message;
+
+ int nleft = 1024;
+ int n;
+
+ n = snprintf(message, nleft, "%s(%s)", func, op);
+ message += n;
+ nleft -= n;
+
+ do {
+ maj_stat = gss_display_status (&min_stat,
+ err_maj,
+ GSS_C_GSS_CODE,
+ GSS_C_NO_OID,
+ &msg_ctx,
+ &status_string);
+ if(GSS_ERROR(maj_stat))
+ break;
+
+ n = snprintf(message, nleft, ": %.*s",
+ (int)status_string.length, (char*)status_string.value);
+ message += n;
+ nleft -= n;
+
+ gss_release_buffer(&min_stat, &status_string);
+
+ maj_stat = gss_display_status (&min_stat,
+ err_min,
+ GSS_C_MECH_CODE,
+ GSS_C_NULL_OID,
+ &msg_ctx,
+ &status_string);
+ if(!GSS_ERROR(maj_stat)) {
+ n = snprintf(message, nleft, ": %.*s",
+ (int)status_string.length, (char*)status_string.value);
+ message += n;
+ nleft -= n;
+
+ gss_release_buffer(&min_stat, &status_string);
+ }
+ } while (!GSS_ERROR(maj_stat) && msg_ctx != 0);
+
+ return response;
+}
+
+#pragma clang diagnostic pop
+
diff --git a/lib/kerberosgss.h b/lib/kerberosgss.h
new file mode 100644
index 0000000..145613f
--- /dev/null
+++ b/lib/kerberosgss.h
@@ -0,0 +1,69 @@
+/**
+ * Copyright (c) 2006-2009 Apple Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ **/
+#ifndef KERBEROS_GSS_H
+#define KERBEROS_GSS_H
+
+#include <gssapi/gssapi.h>
+#include <gssapi/gssapi_generic.h>
+#include <gssapi/gssapi_krb5.h>
+
+#define krb5_get_err_text(context,code) error_message(code)
+
+#define AUTH_GSS_ERROR -1
+#define AUTH_GSS_COMPLETE 1
+#define AUTH_GSS_CONTINUE 0
+
+#define GSS_AUTH_P_NONE 1
+#define GSS_AUTH_P_INTEGRITY 2
+#define GSS_AUTH_P_PRIVACY 4
+
+typedef struct {
+ int return_code;
+ char *message;
+} gss_client_response;
+
+typedef struct {
+ gss_ctx_id_t context;
+ gss_name_t server_name;
+ long int gss_flags;
+ char* username;
+ char* response;
+} gss_client_state;
+
+typedef struct {
+ gss_ctx_id_t context;
+ gss_name_t server_name;
+ gss_name_t client_name;
+ gss_cred_id_t server_creds;
+ gss_cred_id_t client_creds;
+ char* username;
+ char* targetname;
+ char* response;
+} gss_server_state;
+
+// char* server_principal_details(const char* service, const char* hostname);
+
+gss_client_response *authenticate_gss_client_init(const char* service, long int gss_flags, gss_client_state* state);
+gss_client_response *authenticate_gss_client_clean(gss_client_state *state);
+gss_client_response *authenticate_gss_client_step(gss_client_state *state, const char *challenge);
+gss_client_response *authenticate_gss_client_unwrap(gss_client_state* state, const char* challenge);
+gss_client_response *authenticate_gss_client_wrap(gss_client_state* state, const char* challenge, const char* user);
+
+gss_client_response *authenticate_gss_server_init(const char* service, gss_server_state* state);
+gss_client_response *authenticate_gss_server_clean(gss_server_state *state);
+gss_client_response *authenticate_gss_server_step(gss_server_state *state, const char *challenge);
+
+#endif
diff --git a/lib/sspi.js b/lib/sspi.js
new file mode 100644
index 0000000..d9120fb
--- /dev/null
+++ b/lib/sspi.js
@@ -0,0 +1,15 @@
+// Load the native SSPI classes
+var kerberos = require('../build/Release/kerberos')
+ , Kerberos = kerberos.Kerberos
+ , SecurityBuffer = require('./win32/wrappers/security_buffer').SecurityBuffer
+ , SecurityBufferDescriptor = require('./win32/wrappers/security_buffer_descriptor').SecurityBufferDescriptor
+ , SecurityCredentials = require('./win32/wrappers/security_credentials').SecurityCredentials
+ , SecurityContext = require('./win32/wrappers/security_context').SecurityContext;
+var SSPI = function() {
+}
+
+exports.SSPI = SSPI;
+exports.SecurityBuffer = SecurityBuffer;
+exports.SecurityBufferDescriptor = SecurityBufferDescriptor;
+exports.SecurityCredentials = SecurityCredentials;
+exports.SecurityContext = SecurityContext;
\ No newline at end of file
diff --git a/lib/win32/base64.c b/lib/win32/base64.c
new file mode 100644
index 0000000..502a021
--- /dev/null
+++ b/lib/win32/base64.c
@@ -0,0 +1,121 @@
+/**
+ * Copyright (c) 2006-2008 Apple Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ **/
+
+#include "base64.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+// base64 tables
+static char basis_64[] =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+static signed char index_64[128] =
+{
+ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
+ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
+ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,62, -1,-1,-1,63,
+ 52,53,54,55, 56,57,58,59, 60,61,-1,-1, -1,-1,-1,-1,
+ -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11,12,13,14,
+ 15,16,17,18, 19,20,21,22, 23,24,25,-1, -1,-1,-1,-1,
+ -1,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40,
+ 41,42,43,44, 45,46,47,48, 49,50,51,-1, -1,-1,-1,-1
+};
+#define CHAR64(c) (((c) < 0 || (c) > 127) ? -1 : index_64[(c)])
+
+// base64_encode : base64 encode
+//
+// value : data to encode
+// vlen : length of data
+// (result) : new char[] - c-str of result
+char *base64_encode(const unsigned char *value, int vlen)
+{
+ char *result = (char *)malloc((vlen * 4) / 3 + 5);
+ char *out = result;
+ unsigned char oval;
+
+ while (vlen >= 3)
+ {
+ *out++ = basis_64[value[0] >> 2];
+ *out++ = basis_64[((value[0] << 4) & 0x30) | (value[1] >> 4)];
+ *out++ = basis_64[((value[1] << 2) & 0x3C) | (value[2] >> 6)];
+ *out++ = basis_64[value[2] & 0x3F];
+ value += 3;
+ vlen -= 3;
+ }
+ if (vlen > 0)
+ {
+ *out++ = basis_64[value[0] >> 2];
+ oval = (value[0] << 4) & 0x30;
+ if (vlen > 1) oval |= value[1] >> 4;
+ *out++ = basis_64[oval];
+ *out++ = (vlen < 2) ? '=' : basis_64[(value[1] << 2) & 0x3C];
+ *out++ = '=';
+ }
+ *out = '\0';
+
+ return result;
+}
+
+// base64_decode : base64 decode
+//
+// value : c-str to decode
+// rlen : length of decoded result
+// (result) : new unsigned char[] - decoded result
+unsigned char *base64_decode(const char *value, int *rlen)
+{
+ int c1, c2, c3, c4;
+ int vlen = (int)strlen(value);
+ unsigned char *result =(unsigned char *)malloc((vlen * 3) / 4 + 1);
+ unsigned char *out = result;
+ *rlen = 0;
+
+ while (1)
+ {
+ if (value[0]==0)
+ return result;
+ c1 = value[0];
+ if (CHAR64(c1) == -1)
+ goto base64_decode_error;;
+ c2 = value[1];
+ if (CHAR64(c2) == -1)
+ goto base64_decode_error;;
+ c3 = value[2];
+ if ((c3 != '=') && (CHAR64(c3) == -1))
+ goto base64_decode_error;;
+ c4 = value[3];
+ if ((c4 != '=') && (CHAR64(c4) == -1))
+ goto base64_decode_error;;
+
+ value += 4;
+ *out++ = (CHAR64(c1) << 2) | (CHAR64(c2) >> 4);
+ *rlen += 1;
+ if (c3 != '=')
+ {
+ *out++ = ((CHAR64(c2) << 4) & 0xf0) | (CHAR64(c3) >> 2);
+ *rlen += 1;
+ if (c4 != '=')
+ {
+ *out++ = ((CHAR64(c3) << 6) & 0xc0) | CHAR64(c4);
+ *rlen += 1;
+ }
+ }
+ }
+
+base64_decode_error:
+ *result = 0;
+ *rlen = 0;
+ return result;
+}
diff --git a/lib/win32/base64.h b/lib/win32/base64.h
new file mode 100644
index 0000000..f0e1f06
--- /dev/null
+++ b/lib/win32/base64.h
@@ -0,0 +1,18 @@
+/**
+ * Copyright (c) 2006-2008 Apple Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ **/
+
+char *base64_encode(const unsigned char *value, int vlen);
+unsigned char *base64_decode(const char *value, int *rlen);
diff --git a/lib/win32/kerberos.cc b/lib/win32/kerberos.cc
new file mode 100644
index 0000000..c7b583f
--- /dev/null
+++ b/lib/win32/kerberos.cc
@@ -0,0 +1,51 @@
+#include "kerberos.h"
+#include <stdlib.h>
+#include <tchar.h>
+#include "base64.h"
+#include "wrappers/security_buffer.h"
+#include "wrappers/security_buffer_descriptor.h"
+#include "wrappers/security_context.h"
+#include "wrappers/security_credentials.h"
+
+Nan::Persistent<FunctionTemplate> Kerberos::constructor_template;
+
+Kerberos::Kerberos() : Nan::ObjectWrap() {
+}
+
+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 persistent
+ constructor_template.Reset(t);
+
+ // Set the symbol
+ Nan::Set(target, Nan::New<String>("Kerberos").ToLocalChecked(), t->GetFunction());
+}
+
+NAN_METHOD(Kerberos::New) {
+ // Load the security.dll library
+ load_library();
+ // Create a Kerberos instance
+ Kerberos *kerberos = new Kerberos();
+ // Return the kerberos object
+ kerberos->Wrap(info.This());
+ // Return the object
+ info.GetReturnValue().Set(info.This());
+}
+
+// Exporting function
+NAN_MODULE_INIT(init) {
+ Kerberos::Initialize(target);
+ SecurityContext::Initialize(target);
+ SecurityBuffer::Initialize(target);
+ SecurityBufferDescriptor::Initialize(target);
+ SecurityCredentials::Initialize(target);
+}
+
+NODE_MODULE(kerberos, init);
diff --git a/lib/win32/kerberos.h b/lib/win32/kerberos.h
new file mode 100644
index 0000000..0fd2760
--- /dev/null
+++ b/lib/win32/kerberos.h
@@ -0,0 +1,60 @@
+#ifndef KERBEROS_H
+#define KERBEROS_H
+
+#include <node.h>
+#include <node_object_wrap.h>
+#include <v8.h>
+#include "nan.h"
+
+extern "C" {
+ #include "kerberos_sspi.h"
+ #include "base64.h"
+}
+
+using namespace v8;
+using namespace node;
+
+class Kerberos : public Nan::ObjectWrap {
+
+public:
+ Kerberos();
+ ~Kerberos() {};
+
+ // Constructor used for creating new Kerberos objects from C++
+ static Nan::Persistent<FunctionTemplate> constructor_template;
+
+ // Initialize function for the object
+ static void Initialize(Nan::ADDON_REGISTER_FUNCTION_ARGS_TYPE target);
+
+ // Method available
+ static NAN_METHOD(AcquireAlternateCredentials);
+ static NAN_METHOD(PrepareOutboundPackage);
+ static NAN_METHOD(DecryptMessage);
+ static NAN_METHOD(EncryptMessage);
+ static NAN_METHOD(QueryContextAttributes);
+
+private:
+ static NAN_METHOD(New);
+
+ // Pointer to context object
+ SEC_WINNT_AUTH_IDENTITY m_Identity;
+ // credentials
+ CredHandle m_Credentials;
+ // Expiry time for ticket
+ TimeStamp Expiration;
+ // package info
+ SecPkgInfo m_PkgInfo;
+ // context
+ CtxtHandle m_Context;
+ // Do we have a context
+ bool m_HaveContext;
+ // Attributes
+ DWORD CtxtAttr;
+
+ // Handles the uv calls
+ static void Process(uv_work_t* work_req);
+ // Called after work is done
+ static void After(uv_work_t* work_req);
+};
+
+#endif
\ No newline at end of file
diff --git a/lib/win32/kerberos_sspi.c b/lib/win32/kerberos_sspi.c
new file mode 100644
index 0000000..d75c9ab
--- /dev/null
+++ b/lib/win32/kerberos_sspi.c
@@ -0,0 +1,244 @@
+#include "kerberos_sspi.h"
+#include <stdlib.h>
+#include <stdio.h>
+
+static HINSTANCE _sspi_security_dll = NULL;
+static HINSTANCE _sspi_secur32_dll = NULL;
+
+/**
+ * Encrypt A Message
+ */
+SECURITY_STATUS SEC_ENTRY _sspi_EncryptMessage(PCtxtHandle phContext, unsigned long fQOP, PSecBufferDesc pMessage, unsigned long MessageSeqNo) {
+ // Create function pointer instance
+ encryptMessage_fn pfn_encryptMessage = NULL;
+
+ // Return error if library not loaded
+ if(_sspi_security_dll == NULL) return -1;
+
+ // Map function to library method
+ pfn_encryptMessage = (encryptMessage_fn)GetProcAddress(_sspi_security_dll, "EncryptMessage");
+ // Check if the we managed to map function pointer
+ if(!pfn_encryptMessage) {
+ printf("GetProcAddress failed.\n");
+ return -2;
+ }
+
+ // Call the function
+ return (*pfn_encryptMessage)(phContext, fQOP, pMessage, MessageSeqNo);
+}
+
+/**
+ * Acquire Credentials
+ */
+SECURITY_STATUS SEC_ENTRY _sspi_AcquireCredentialsHandle(
+ LPSTR pszPrincipal, LPSTR pszPackage, unsigned long fCredentialUse,
+ void * pvLogonId, void * pAuthData, SEC_GET_KEY_FN pGetKeyFn, void * pvGetKeyArgument,
+ PCredHandle phCredential, PTimeStamp ptsExpiry
+) {
+ SECURITY_STATUS status;
+ // Create function pointer instance
+ acquireCredentialsHandle_fn pfn_acquireCredentialsHandle = NULL;
+
+ // Return error if library not loaded
+ if(_sspi_security_dll == NULL) return -1;
+
+ // Map function
+ #ifdef _UNICODE
+ pfn_acquireCredentialsHandle = (acquireCredentialsHandle_fn)GetProcAddress(_sspi_security_dll, "AcquireCredentialsHandleW");
+ #else
+ pfn_acquireCredentialsHandle = (acquireCredentialsHandle_fn)GetProcAddress(_sspi_security_dll, "AcquireCredentialsHandleA");
+ #endif
+
+ // Check if the we managed to map function pointer
+ if(!pfn_acquireCredentialsHandle) {
+ printf("GetProcAddress failed.\n");
+ return -2;
+ }
+
+ // Status
+ status = (*pfn_acquireCredentialsHandle)(pszPrincipal, pszPackage, fCredentialUse,
+ pvLogonId, pAuthData, pGetKeyFn, pvGetKeyArgument, phCredential, ptsExpiry
+ );
+
+ // Call the function
+ return status;
+}
+
+/**
+ * Delete Security Context
+ */
+SECURITY_STATUS SEC_ENTRY _sspi_DeleteSecurityContext(PCtxtHandle phContext) {
+ // Create function pointer instance
+ deleteSecurityContext_fn pfn_deleteSecurityContext = NULL;
+
+ // Return error if library not loaded
+ if(_sspi_security_dll == NULL) return -1;
+ // Map function
+ pfn_deleteSecurityContext = (deleteSecurityContext_fn)GetProcAddress(_sspi_security_dll, "DeleteSecurityContext");
+
+ // Check if the we managed to map function pointer
+ if(!pfn_deleteSecurityContext) {
+ printf("GetProcAddress failed.\n");
+ return -2;
+ }
+
+ // Call the function
+ return (*pfn_deleteSecurityContext)(phContext);
+}
+
+/**
+ * Decrypt Message
+ */
+SECURITY_STATUS SEC_ENTRY _sspi_DecryptMessage(PCtxtHandle phContext, PSecBufferDesc pMessage, unsigned long MessageSeqNo, unsigned long pfQOP) {
+ // Create function pointer instance
+ decryptMessage_fn pfn_decryptMessage = NULL;
+
+ // Return error if library not loaded
+ if(_sspi_security_dll == NULL) return -1;
+ // Map function
+ pfn_decryptMessage = (decryptMessage_fn)GetProcAddress(_sspi_security_dll, "DecryptMessage");
+
+ // Check if the we managed to map function pointer
+ if(!pfn_decryptMessage) {
+ printf("GetProcAddress failed.\n");
+ return -2;
+ }
+
+ // Call the function
+ return (*pfn_decryptMessage)(phContext, pMessage, MessageSeqNo, pfQOP);
+}
+
+/**
+ * Initialize Security Context
+ */
+SECURITY_STATUS SEC_ENTRY _sspi_initializeSecurityContext(
+ PCredHandle phCredential, PCtxtHandle phContext,
+ LPSTR pszTargetName, unsigned long fContextReq,
+ unsigned long Reserved1, unsigned long TargetDataRep,
+ PSecBufferDesc pInput, unsigned long Reserved2,
+ PCtxtHandle phNewContext, PSecBufferDesc pOutput,
+ unsigned long * pfContextAttr, PTimeStamp ptsExpiry
+) {
+ SECURITY_STATUS status;
+ // Create function pointer instance
+ initializeSecurityContext_fn pfn_initializeSecurityContext = NULL;
+
+ // Return error if library not loaded
+ if(_sspi_security_dll == NULL) return -1;
+
+ // Map function
+ #ifdef _UNICODE
+ pfn_initializeSecurityContext = (initializeSecurityContext_fn)GetProcAddress(_sspi_security_dll, "InitializeSecurityContextW");
+ #else
+ pfn_initializeSecurityContext = (initializeSecurityContext_fn)GetProcAddress(_sspi_security_dll, "InitializeSecurityContextA");
+ #endif
+
+ // Check if the we managed to map function pointer
+ if(!pfn_initializeSecurityContext) {
+ printf("GetProcAddress failed.\n");
+ return -2;
+ }
+
+ // Execute intialize context
+ status = (*pfn_initializeSecurityContext)(
+ phCredential, phContext, pszTargetName, fContextReq,
+ Reserved1, TargetDataRep, pInput, Reserved2,
+ phNewContext, pOutput, pfContextAttr, ptsExpiry
+ );
+
+ // Call the function
+ return status;
+}
+/**
+ * Query Context Attributes
+ */
+SECURITY_STATUS SEC_ENTRY _sspi_QueryContextAttributes(
+ PCtxtHandle phContext, unsigned long ulAttribute, void * pBuffer
+) {
+ // Create function pointer instance
+ queryContextAttributes_fn pfn_queryContextAttributes = NULL;
+
+ // Return error if library not loaded
+ if(_sspi_security_dll == NULL) return -1;
+
+ #ifdef _UNICODE
+ pfn_queryContextAttributes = (queryContextAttributes_fn)GetProcAddress(_sspi_security_dll, "QueryContextAttributesW");
+ #else
+ pfn_queryContextAttributes = (queryContextAttributes_fn)GetProcAddress(_sspi_security_dll, "QueryContextAttributesA");
+ #endif
+
+ // Check if the we managed to map function pointer
+ if(!pfn_queryContextAttributes) {
+ printf("GetProcAddress failed.\n");
+ return -2;
+ }
+
+ // Call the function
+ return (*pfn_queryContextAttributes)(
+ phContext, ulAttribute, pBuffer
+ );
+}
+
+/**
+ * InitSecurityInterface
+ */
+PSecurityFunctionTable _ssip_InitSecurityInterface() {
+ INIT_SECURITY_INTERFACE InitSecurityInterface;
+ PSecurityFunctionTable pSecurityInterface = NULL;
+
+ // Return error if library not loaded
+ if(_sspi_security_dll == NULL) return NULL;
+
+ #ifdef _UNICODE
+ // Get the address of the InitSecurityInterface function.
+ InitSecurityInterface = (INIT_SECURITY_INTERFACE) GetProcAddress (
+ _sspi_secur32_dll,
+ TEXT("InitSecurityInterfaceW"));
+ #else
+ // Get the address of the InitSecurityInterface function.
+ InitSecurityInterface = (INIT_SECURITY_INTERFACE) GetProcAddress (
+ _sspi_secur32_dll,
+ TEXT("InitSecurityInterfaceA"));
+ #endif
+
+ if(!InitSecurityInterface) {
+ printf (TEXT("Failed in getting the function address, Error: %x"), GetLastError ());
+ return NULL;
+ }
+
+ // Use InitSecurityInterface to get the function table.
+ pSecurityInterface = (*InitSecurityInterface)();
+
+ if(!pSecurityInterface) {
+ printf (TEXT("Failed in getting the function table, Error: %x"), GetLastError ());
+ return NULL;
+ }
+
+ return pSecurityInterface;
+}
+
+/**
+ * Load security.dll dynamically
+ */
+int load_library() {
+ DWORD err;
+ // Load the library
+ _sspi_security_dll = LoadLibrary("security.dll");
+
+ // Check if the library loaded
+ if(_sspi_security_dll == NULL) {
+ err = GetLastError();
+ return err;
+ }
+
+ // Load the library
+ _sspi_secur32_dll = LoadLibrary("secur32.dll");
+
+ // Check if the library loaded
+ if(_sspi_secur32_dll == NULL) {
+ err = GetLastError();
+ return err;
+ }
+
+ return 0;
+}
\ No newline at end of file
diff --git a/lib/win32/kerberos_sspi.h b/lib/win32/kerberos_sspi.h
new file mode 100644
index 0000000..a3008dc
--- /dev/null
+++ b/lib/win32/kerberos_sspi.h
@@ -0,0 +1,106 @@
+#ifndef SSPI_C_H
+#define SSPI_C_H
+
+#define SECURITY_WIN32 1
+
+#include <windows.h>
+#include <sspi.h>
+
+/**
+ * Encrypt A Message
+ */
+SECURITY_STATUS SEC_ENTRY _sspi_EncryptMessage(PCtxtHandle phContext, unsigned long fQOP, PSecBufferDesc pMessage, unsigned long MessageSeqNo);
+
+typedef DWORD (WINAPI *encryptMessage_fn)(PCtxtHandle phContext, ULONG fQOP, PSecBufferDesc pMessage, ULONG MessageSeqNo);
+
+/**
+ * Acquire Credentials
+ */
+SECURITY_STATUS SEC_ENTRY _sspi_AcquireCredentialsHandle(
+ LPSTR pszPrincipal, // Name of principal
+ LPSTR pszPackage, // Name of package
+ unsigned long fCredentialUse, // Flags indicating use
+ void * pvLogonId, // Pointer to logon ID
+ void * pAuthData, // Package specific data
+ SEC_GET_KEY_FN pGetKeyFn, // Pointer to GetKey() func
+ void * pvGetKeyArgument, // Value to pass to GetKey()
+ PCredHandle phCredential, // (out) Cred Handle
+ PTimeStamp ptsExpiry // (out) Lifetime (optional)
+);
+
+typedef DWORD (WINAPI *acquireCredentialsHandle_fn)(
+ LPSTR pszPrincipal, LPSTR pszPackage, unsigned long fCredentialUse,
+ void * pvLogonId, void * pAuthData, SEC_GET_KEY_FN pGetKeyFn, void * pvGetKeyArgument,
+ PCredHandle phCredential, PTimeStamp ptsExpiry
+ );
+
+/**
+ * Delete Security Context
+ */
+SECURITY_STATUS SEC_ENTRY _sspi_DeleteSecurityContext(
+ PCtxtHandle phContext // Context to delete
+);
+
+typedef DWORD (WINAPI *deleteSecurityContext_fn)(PCtxtHandle phContext);
+
+/**
+ * Decrypt Message
+ */
+SECURITY_STATUS SEC_ENTRY _sspi_DecryptMessage(
+ PCtxtHandle phContext,
+ PSecBufferDesc pMessage,
+ unsigned long MessageSeqNo,
+ unsigned long pfQOP
+);
+
+typedef DWORD (WINAPI *decryptMessage_fn)(
+ PCtxtHandle phContext, PSecBufferDesc pMessage, unsigned long MessageSeqNo, unsigned long pfQOP);
+
+/**
+ * Initialize Security Context
+ */
+SECURITY_STATUS SEC_ENTRY _sspi_initializeSecurityContext(
+ PCredHandle phCredential, // Cred to base context
+ PCtxtHandle phContext, // Existing context (OPT)
+ LPSTR pszTargetName, // Name of target
+ unsigned long fContextReq, // Context Requirements
+ unsigned long Reserved1, // Reserved, MBZ
+ unsigned long TargetDataRep, // Data rep of target
+ PSecBufferDesc pInput, // Input Buffers
+ unsigned long Reserved2, // Reserved, MBZ
+ PCtxtHandle phNewContext, // (out) New Context handle
+ PSecBufferDesc pOutput, // (inout) Output Buffers
+ unsigned long * pfContextAttr, // (out) Context attrs
+ PTimeStamp ptsExpiry // (out) Life span (OPT)
+);
+
+typedef DWORD (WINAPI *initializeSecurityContext_fn)(
+ PCredHandle phCredential, PCtxtHandle phContext, LPSTR pszTargetName, unsigned long fContextReq,
+ unsigned long Reserved1, unsigned long TargetDataRep, PSecBufferDesc pInput, unsigned long Reserved2,
+ PCtxtHandle phNewContext, PSecBufferDesc pOutput, unsigned long * pfContextAttr, PTimeStamp ptsExpiry);
+
+/**
+ * Query Context Attributes
+ */
+SECURITY_STATUS SEC_ENTRY _sspi_QueryContextAttributes(
+ PCtxtHandle phContext, // Context to query
+ unsigned long ulAttribute, // Attribute to query
+ void * pBuffer // Buffer for attributes
+);
+
+typedef DWORD (WINAPI *queryContextAttributes_fn)(
+ PCtxtHandle phContext, unsigned long ulAttribute, void * pBuffer);
+
+/**
+ * InitSecurityInterface
+ */
+PSecurityFunctionTable _ssip_InitSecurityInterface();
+
+typedef DWORD (WINAPI *initSecurityInterface_fn) ();
+
+/**
+ * Load security.dll dynamically
+ */
+int load_library();
+
+#endif
\ No newline at end of file
diff --git a/lib/win32/worker.cc b/lib/win32/worker.cc
new file mode 100644
index 0000000..e7a472f
--- /dev/null
+++ b/lib/win32/worker.cc
@@ -0,0 +1,7 @@
+#include "worker.h"
+
+Worker::Worker() {
+}
+
+Worker::~Worker() {
+}
\ No newline at end of file
diff --git a/lib/win32/worker.h b/lib/win32/worker.h
new file mode 100644
index 0000000..c2ccb6b
--- /dev/null
+++ b/lib/win32/worker.h
@@ -0,0 +1,38 @@
+#ifndef WORKER_H_
+#define WORKER_H_
+
+#include <node.h>
+#include <node_object_wrap.h>
+#include <v8.h>
+#include <nan.h>
+
+using namespace node;
+using namespace v8;
+
+class Worker {
+ public:
+ Worker();
+ virtual ~Worker();
+
+ // libuv's request struct.
+ uv_work_t request;
+ // Callback
+ Nan::Callback *callback;
+ // Parameters
+ void *parameters;
+ // Results
+ void *return_value;
+ // Did we raise an error
+ bool error;
+ // The error message
+ char *error_message;
+ // Error code if not message
+ int error_code;
+ // Any return code
+ int return_code;
+ // Method we are going to fire
+ void (*execute)(Worker *worker);
+ Local<Value> (*mapper)(Worker *worker);
+};
+
+#endif // WORKER_H_
diff --git a/lib/win32/wrappers/security_buffer.cc b/lib/win32/wrappers/security_buffer.cc
new file mode 100644
index 0000000..fdf8e49
--- /dev/null
+++ b/lib/win32/wrappers/security_buffer.cc
@@ -0,0 +1,101 @@
+#include <node.h>
+#include <assert.h>
+#include <string.h>
+#include <stdlib.h>
+#include <v8.h>
+#include <node_buffer.h>
+#include <cstring>
+#include <cmath>
+#include <cstdlib>
+#include <iostream>
+#include <limits>
+
+#include "security_buffer.h"
+
+using namespace node;
+
+Nan::Persistent<FunctionTemplate> SecurityBuffer::constructor_template;
+
+SecurityBuffer::SecurityBuffer(uint32_t security_type, size_t size) : Nan::ObjectWrap() {
+ this->size = size;
+ this->data = calloc(size, sizeof(char));
+ this->security_type = security_type;
+ // Set up the data in the sec_buffer
+ this->sec_buffer.BufferType = security_type;
+ this->sec_buffer.cbBuffer = (unsigned long)size;
+ this->sec_buffer.pvBuffer = this->data;
+}
+
+SecurityBuffer::SecurityBuffer(uint32_t security_type, size_t size, void *data) : Nan::ObjectWrap() {
+ this->size = size;
+ this->data = data;
+ this->security_type = security_type;
+ // Set up the data in the sec_buffer
+ this->sec_buffer.BufferType = security_type;
+ this->sec_buffer.cbBuffer = (unsigned long)size;
+ this->sec_buffer.pvBuffer = this->data;
+}
+
+SecurityBuffer::~SecurityBuffer() {
+ free(this->data);
+}
+
+NAN_METHOD(SecurityBuffer::New) {
+ SecurityBuffer *security_obj;
+
+ if(info.Length() != 2)
+ return Nan::ThrowError("Two parameters needed integer buffer type and [32 bit integer/Buffer] required");
+
+ if(!info[0]->IsInt32())
+ return Nan::ThrowError("Two parameters needed integer buffer type and [32 bit integer/Buffer] required");
+
+ if(!info[1]->IsInt32() && !Buffer::HasInstance(info[1]))
+ return Nan::ThrowError("Two parameters needed integer buffer type and [32 bit integer/Buffer] required");
+
+ // Unpack buffer type
+ uint32_t buffer_type = info[0]->ToUint32()->Value();
+
+ // If we have an integer
+ if(info[1]->IsInt32()) {
+ security_obj = new SecurityBuffer(buffer_type, info[1]->ToUint32()->Value());
+ } else {
+ // Get the length of the Buffer
+ size_t length = Buffer::Length(info[1]->ToObject());
+ // Allocate space for the internal void data pointer
+ void *data = calloc(length, sizeof(char));
+ // Write the data to out of V8 heap space
+ memcpy(data, Buffer::Data(info[1]->ToObject()), length);
+ // Create new SecurityBuffer
+ security_obj = new SecurityBuffer(buffer_type, length, data);
+ }
+
+ // Wrap it
+ security_obj->Wrap(info.This());
+ // Return the object
+ info.GetReturnValue().Set(info.This());
+}
+
+NAN_METHOD(SecurityBuffer::ToBuffer) {
+ // Unpack the Security Buffer object
+ SecurityBuffer *security_obj = Nan::ObjectWrap::Unwrap<SecurityBuffer>(info.This());
+ // Create a Buffer
+ Local<Object> buffer = Nan::CopyBuffer((char *)security_obj->data, (uint32_t)security_obj->size).ToLocalChecked();
+ // Return the buffer
+ info.GetReturnValue().Set(buffer);
+}
+
+void SecurityBuffer::Initialize(Nan::ADDON_REGISTER_FUNCTION_ARGS_TYPE target) {
+ // Define a new function template
+ Local<FunctionTemplate> t = Nan::New<FunctionTemplate>(New);
+ t->InstanceTemplate()->SetInternalFieldCount(1);
+ t->SetClassName(Nan::New<String>("SecurityBuffer").ToLocalChecked());
+
+ // Class methods
+ Nan::SetPrototypeMethod(t, "toBuffer", ToBuffer);
+
+ // Set persistent
+ constructor_template.Reset(t);
+
+ // Set the symbol
+ target->ForceSet(Nan::New<String>("SecurityBuffer").ToLocalChecked(), t->GetFunction());
+}
diff --git a/lib/win32/wrappers/security_buffer.h b/lib/win32/wrappers/security_buffer.h
new file mode 100644
index 0000000..0c97d56
--- /dev/null
+++ b/lib/win32/wrappers/security_buffer.h
@@ -0,0 +1,48 @@
+#ifndef SECURITY_BUFFER_H
+#define SECURITY_BUFFER_H
+
+#include <node.h>
+#include <node_object_wrap.h>
+#include <v8.h>
+
+#define SECURITY_WIN32 1
+
+#include <winsock2.h>
+#include <windows.h>
+#include <sspi.h>
+#include <nan.h>
+
+using namespace v8;
+using namespace node;
+
+class SecurityBuffer : public Nan::ObjectWrap {
+ public:
+ SecurityBuffer(uint32_t security_type, size_t size);
+ SecurityBuffer(uint32_t security_type, size_t size, void *data);
+ ~SecurityBuffer();
+
+ // Internal values
+ void *data;
+ size_t size;
+ uint32_t security_type;
+ SecBuffer sec_buffer;
+
+ // Has instance check
+ static inline bool HasInstance(Local<Value> val) {
+ if (!val->IsObject()) return false;
+ Local<Object> obj = val->ToObject();
+ return Nan::New(constructor_template)->HasInstance(obj);
+ };
+
+ // Functions available from V8
+ static void Initialize(Nan::ADDON_REGISTER_FUNCTION_ARGS_TYPE target);
+ static NAN_METHOD(ToBuffer);
+
+ // Constructor used for creating new Long objects from C++
+ static Nan::Persistent<FunctionTemplate> constructor_template;
+
+ private:
+ static NAN_METHOD(New);
+};
+
+#endif
\ No newline at end of file
diff --git a/lib/win32/wrappers/security_buffer.js b/lib/win32/wrappers/security_buffer.js
new file mode 100644
index 0000000..4996163
--- /dev/null
+++ b/lib/win32/wrappers/security_buffer.js
@@ -0,0 +1,12 @@
+var SecurityBufferNative = require('../../../build/Release/kerberos').SecurityBuffer;
+
+// Add some attributes
+SecurityBufferNative.VERSION = 0;
+SecurityBufferNative.EMPTY = 0;
+SecurityBufferNative.DATA = 1;
+SecurityBufferNative.TOKEN = 2;
+SecurityBufferNative.PADDING = 9;
+SecurityBufferNative.STREAM = 10;
+
+// Export the modified class
+exports.SecurityBuffer = SecurityBufferNative;
\ No newline at end of file
diff --git a/lib/win32/wrappers/security_buffer_descriptor.cc b/lib/win32/wrappers/security_buffer_descriptor.cc
new file mode 100644
index 0000000..fce0d81
--- /dev/null
+++ b/lib/win32/wrappers/security_buffer_descriptor.cc
@@ -0,0 +1,182 @@
+#include <node.h>
+#include <assert.h>
+#include <string.h>
+#include <stdlib.h>
+#include <v8.h>
+#include <node_buffer.h>
+#include <cstring>
+#include <cmath>
+#include <cstdlib>
+#include <iostream>
+#include <limits>
+
+#define SECURITY_WIN32 1
+
+#include "security_buffer_descriptor.h"
+#include "security_buffer.h"
+
+Nan::Persistent<FunctionTemplate> SecurityBufferDescriptor::constructor_template;
+
+SecurityBufferDescriptor::SecurityBufferDescriptor() : Nan::ObjectWrap() {
+}
+
+SecurityBufferDescriptor::SecurityBufferDescriptor(const Nan::Persistent<Array>& arrayObjectPersistent) : Nan::ObjectWrap() {
+ SecurityBuffer *security_obj = NULL;
+ // Get the Local value
+ Local<Array> arrayObject = Nan::New(arrayObjectPersistent);
+
+ // Safe reference to array
+ this->arrayObject = arrayObject;
+
+ // Unpack the array and ensure we have a valid descriptor
+ this->secBufferDesc.cBuffers = arrayObject->Length();
+ this->secBufferDesc.ulVersion = SECBUFFER_VERSION;
+
+ if(arrayObject->Length() == 1) {
+ // Unwrap the buffer
+ security_obj = Nan::ObjectWrap::Unwrap<SecurityBuffer>(arrayObject->Get(0)->ToObject());
+ // Assign the buffer
+ this->secBufferDesc.pBuffers = &security_obj->sec_buffer;
+ } else {
+ this->secBufferDesc.pBuffers = new SecBuffer[arrayObject->Length()];
+ this->secBufferDesc.cBuffers = arrayObject->Length();
+
+ // Assign the buffers
+ for(uint32_t i = 0; i < arrayObject->Length(); i++) {
+ security_obj = Nan::ObjectWrap::Unwrap<SecurityBuffer>(arrayObject->Get(i)->ToObject());
+ this->secBufferDesc.pBuffers[i].BufferType = security_obj->sec_buffer.BufferType;
+ this->secBufferDesc.pBuffers[i].pvBuffer = security_obj->sec_buffer.pvBuffer;
+ this->secBufferDesc.pBuffers[i].cbBuffer = security_obj->sec_buffer.cbBuffer;
+ }
+ }
+}
+
+SecurityBufferDescriptor::~SecurityBufferDescriptor() {
+}
+
+size_t SecurityBufferDescriptor::bufferSize() {
+ SecurityBuffer *security_obj = NULL;
+
+ if(this->secBufferDesc.cBuffers == 1) {
+ security_obj = Nan::ObjectWrap::Unwrap<SecurityBuffer>(arrayObject->Get(0)->ToObject());
+ return security_obj->size;
+ } else {
+ int bytesToAllocate = 0;
+
+ for(unsigned int i = 0; i < this->secBufferDesc.cBuffers; i++) {
+ bytesToAllocate += this->secBufferDesc.pBuffers[i].cbBuffer;
+ }
+
+ // Return total size
+ return bytesToAllocate;
+ }
+}
+
+char *SecurityBufferDescriptor::toBuffer() {
+ SecurityBuffer *security_obj = NULL;
+ char *data = NULL;
+
+ if(this->secBufferDesc.cBuffers == 1) {
+ security_obj = Nan::ObjectWrap::Unwrap<SecurityBuffer>(arrayObject->Get(0)->ToObject());
+ data = (char *)malloc(security_obj->size * sizeof(char));
+ memcpy(data, security_obj->data, security_obj->size);
+ } else {
+ size_t bytesToAllocate = this->bufferSize();
+ char *data = (char *)calloc(bytesToAllocate, sizeof(char));
+ int offset = 0;
+
+ for(unsigned int i = 0; i < this->secBufferDesc.cBuffers; i++) {
+ memcpy((data + offset), this->secBufferDesc.pBuffers[i].pvBuffer, this->secBufferDesc.pBuffers[i].cbBuffer);
+ offset +=this->secBufferDesc.pBuffers[i].cbBuffer;
+ }
+
+ // Return the data
+ return data;
+ }
+
+ return data;
+}
+
+NAN_METHOD(SecurityBufferDescriptor::New) {
+ SecurityBufferDescriptor *security_obj;
+ Nan::Persistent<Array> arrayObject;
+
+ if(info.Length() != 1)
+ return Nan::ThrowError("There must be 1 argument passed in where the first argument is a [int32 or an Array of SecurityBuffers]");
+
+ if(!info[0]->IsInt32() && !info[0]->IsArray())
+ return Nan::ThrowError("There must be 1 argument passed in where the first argument is a [int32 or an Array of SecurityBuffers]");
+
+ if(info[0]->IsArray()) {
+ Local<Array> array = Local<Array>::Cast(info[0]);
+ // Iterate over all items and ensure we the right type
+ for(uint32_t i = 0; i < array->Length(); i++) {
+ if(!SecurityBuffer::HasInstance(array->Get(i))) {
+ return Nan::ThrowError("There must be 1 argument passed in where the first argument is a [int32 or an Array of SecurityBuffers]");
+ }
+ }
+ }
+
+ // We have a single integer
+ if(info[0]->IsInt32()) {
+ // Create new SecurityBuffer instance
+ Local<Value> argv[] = {Nan::New<Int32>(0x02), info[0]};
+ Local<Value> security_buffer = Nan::New(SecurityBuffer::constructor_template)->GetFunction()->NewInstance(2, argv);
+ // Create a new array
+ Local<Array> array = Nan::New<Array>(1);
+ // Set the first value
+ array->Set(0, security_buffer);
+
+ // Create persistent handle
+ Nan::Persistent<Array> persistenHandler;
+ persistenHandler.Reset(array);
+
+ // Create descriptor
+ security_obj = new SecurityBufferDescriptor(persistenHandler);
+ } else {
+ // Create a persistent handler
+ Nan::Persistent<Array> persistenHandler;
+ persistenHandler.Reset(Local<Array>::Cast(info[0]));
+ // Create a descriptor
+ security_obj = new SecurityBufferDescriptor(persistenHandler);
+ }
+
+ // Wrap it
+ security_obj->Wrap(info.This());
+ // Return the object
+ info.GetReturnValue().Set(info.This());
+}
+
+NAN_METHOD(SecurityBufferDescriptor::ToBuffer) {
+ // Unpack the Security Buffer object
+ SecurityBufferDescriptor *security_obj = Nan::ObjectWrap::Unwrap<SecurityBufferDescriptor>(info.This());
+
+ // Get the buffer
+ char *buffer_data = security_obj->toBuffer();
+ size_t buffer_size = security_obj->bufferSize();
+
+ // Create a Buffer
+ Local<Object> buffer = Nan::CopyBuffer(buffer_data, (uint32_t)buffer_size).ToLocalChecked();
+
+ // Return the buffer
+ info.GetReturnValue().Set(buffer);
+}
+
+void SecurityBufferDescriptor::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>("SecurityBufferDescriptor").ToLocalChecked());
+
+ // Class methods
+ Nan::SetPrototypeMethod(t, "toBuffer", ToBuffer);
+
+ // Set persistent
+ constructor_template.Reset(t);
+
+ // Set the symbol
+ target->ForceSet(Nan::New<String>("SecurityBufferDescriptor").ToLocalChecked(), t->GetFunction());
+}
diff --git a/lib/win32/wrappers/security_buffer_descriptor.h b/lib/win32/wrappers/security_buffer_descriptor.h
new file mode 100644
index 0000000..dc28f7e
--- /dev/null
+++ b/lib/win32/wrappers/security_buffer_descriptor.h
@@ -0,0 +1,46 @@
+#ifndef SECURITY_BUFFER_DESCRIPTOR_H
+#define SECURITY_BUFFER_DESCRIPTOR_H
+
+#include <node.h>
+#include <node_object_wrap.h>
+#include <v8.h>
+
+#include <WinSock2.h>
+#include <windows.h>
+#include <sspi.h>
+#include <nan.h>
+
+using namespace v8;
+using namespace node;
+
+class SecurityBufferDescriptor : public Nan::ObjectWrap {
+ public:
+ Local<Array> arrayObject;
+ SecBufferDesc secBufferDesc;
+
+ SecurityBufferDescriptor();
+ SecurityBufferDescriptor(const Nan::Persistent<Array>& arrayObjectPersistent);
+ ~SecurityBufferDescriptor();
+
+ // Has instance check
+ static inline bool HasInstance(Local<Value> val) {
+ if (!val->IsObject()) return false;
+ Local<Object> obj = val->ToObject();
+ return Nan::New(constructor_template)->HasInstance(obj);
+ };
+
+ char *toBuffer();
+ size_t bufferSize();
+
+ // Functions available from V8
+ static void Initialize(Nan::ADDON_REGISTER_FUNCTION_ARGS_TYPE target);
+ static NAN_METHOD(ToBuffer);
+
+ // Constructor used for creating new Long objects from C++
+ static Nan::Persistent<FunctionTemplate> constructor_template;
+
+ private:
+ static NAN_METHOD(New);
+};
+
+#endif
\ No newline at end of file
diff --git a/lib/win32/wrappers/security_buffer_descriptor.js b/lib/win32/wrappers/security_buffer_descriptor.js
new file mode 100644
index 0000000..9421392
--- /dev/null
+++ b/lib/win32/wrappers/security_buffer_descriptor.js
@@ -0,0 +1,3 @@
+var SecurityBufferDescriptorNative = require('../../../build/Release/kerberos').SecurityBufferDescriptor;
+// Export the modified class
+exports.SecurityBufferDescriptor = SecurityBufferDescriptorNative;
\ No newline at end of file
diff --git a/lib/win32/wrappers/security_context.cc b/lib/win32/wrappers/security_context.cc
new file mode 100644
index 0000000..5d7ad54
--- /dev/null
+++ b/lib/win32/wrappers/security_context.cc
@@ -0,0 +1,856 @@
+#include <assert.h>
+#include <string.h>
+#include <stdlib.h>
+#include <node.h>
+#include <v8.h>
+#include <node_buffer.h>
+#include <cstring>
+#include <cmath>
+#include <cstdlib>
+#include <iostream>
+#include <limits>
+
+#include "security_context.h"
+#include "security_buffer_descriptor.h"
+
+#ifndef ARRAY_SIZE
+# define ARRAY_SIZE(a) (sizeof((a)) / sizeof((a)[0]))
+#endif
+
+static LPSTR DisplaySECError(DWORD ErrCode);
+
+// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+// UV Lib callbacks
+// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+static void Process(uv_work_t* work_req) {
+ // Grab the worker
+ Worker *worker = static_cast<Worker*>(work_req->data);
+ // Execute the worker code
+ worker->execute(worker);
+}
+
+static void 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
+ Local<Value> info[2] = { Nan::Null(), result};
+ // 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;
+}
+
+Nan::Persistent<FunctionTemplate> SecurityContext::constructor_template;
+
+SecurityContext::SecurityContext() : Nan::ObjectWrap() {
+}
+
+SecurityContext::~SecurityContext() {
+ if(this->hasContext) {
+ _sspi_DeleteSecurityContext(&this->m_Context);
+ }
+}
+
+NAN_METHOD(SecurityContext::New) {
+ PSecurityFunctionTable pSecurityInterface = NULL;
+ DWORD dwNumOfPkgs;
+ SECURITY_STATUS status;
+
+ // Create code object
+ SecurityContext *security_obj = new SecurityContext();
+ // Get security table interface
+ pSecurityInterface = _ssip_InitSecurityInterface();
+ // Call the security interface
+ status = (*pSecurityInterface->EnumerateSecurityPackages)(
+ &dwNumOfPkgs,
+ &security_obj->m_PkgInfo);
+ if(status != SEC_E_OK) {
+ printf(TEXT("Failed in retrieving security packages, Error: %x"), GetLastError());
+ return Nan::ThrowError("Failed in retrieving security packages");
+ }
+
+ // Wrap it
+ security_obj->Wrap(info.This());
+ // Return the object
+ info.GetReturnValue().Set(info.This());
+}
+
+//
+// Async InitializeContext
+//
+typedef struct SecurityContextStaticInitializeCall {
+ char *service_principal_name_str;
+ char *decoded_input_str;
+ int decoded_input_str_length;
+ SecurityContext *context;
+} SecurityContextStaticInitializeCall;
+
+static void _initializeContext(Worker *worker) {
+ // Status of operation
+ SECURITY_STATUS status;
+ BYTE *out_bound_data_str = NULL;
+ SecurityContextStaticInitializeCall *call = (SecurityContextStaticInitializeCall *)worker->parameters;
+
+ // Structures used for c calls
+ SecBufferDesc ibd, obd;
+ SecBuffer ib, ob;
+
+ //
+ // Prepare data structure for returned data from SSPI
+ ob.BufferType = SECBUFFER_TOKEN;
+ ob.cbBuffer = call->context->m_PkgInfo->cbMaxToken;
+ // Allocate space for return data
+ out_bound_data_str = new BYTE[ob.cbBuffer + sizeof(DWORD)];
+ ob.pvBuffer = out_bound_data_str;
+ // prepare buffer description
+ obd.cBuffers = 1;
+ obd.ulVersion = SECBUFFER_VERSION;
+ obd.pBuffers = &ob;
+
+ //
+ // Prepare the data we are passing to the SSPI method
+ if(call->decoded_input_str_length > 0) {
+ ib.BufferType = SECBUFFER_TOKEN;
+ ib.cbBuffer = call->decoded_input_str_length;
+ ib.pvBuffer = call->decoded_input_str;
+ // prepare buffer description
+ ibd.cBuffers = 1;
+ ibd.ulVersion = SECBUFFER_VERSION;
+ ibd.pBuffers = &ib;
+ }
+
+ // Perform initialization step
+ status = _sspi_initializeSecurityContext(
+ &call->context->security_credentials->m_Credentials
+ , NULL
+ , const_cast<TCHAR*>(call->service_principal_name_str)
+ , 0x02 // MUTUAL
+ , 0
+ , 0 // Network
+ , call->decoded_input_str_length > 0 ? &ibd : NULL
+ , 0
+ , &call->context->m_Context
+ , &obd
+ , &call->context->CtxtAttr
+ , &call->context->Expiration
+ );
+
+ // If we have a ok or continue let's prepare the result
+ if(status == SEC_E_OK
+ || status == SEC_I_COMPLETE_NEEDED
+ || status == SEC_I_CONTINUE_NEEDED
+ || status == SEC_I_COMPLETE_AND_CONTINUE
+ ) {
+ call->context->hasContext = true;
+ call->context->payload = base64_encode((const unsigned char *)ob.pvBuffer, ob.cbBuffer);
+
+ // Set the context
+ worker->return_code = status;
+ worker->return_value = call->context;
+ } else if(status == SEC_E_INSUFFICIENT_MEMORY) {
+ worker->error = TRUE;
+ worker->error_code = status;
+ worker->error_message = "SEC_E_INSUFFICIENT_MEMORY There is not enough memory available to complete the requested action.";
+ } else if(status == SEC_E_INTERNAL_ERROR) {
+ worker->error = TRUE;
+ worker->error_code = status;
+ worker->error_message = "SEC_E_INTERNAL_ERROR An error occurred that did not map to an SSPI error code.";
+ } else if(status == SEC_E_INVALID_HANDLE) {
+ worker->error = TRUE;
+ worker->error_code = status;
+ worker->error_message = "SEC_E_INVALID_HANDLE The handle passed to the function is not valid.";
+ } else if(status == SEC_E_INVALID_TOKEN) {
+ worker->error = TRUE;
+ worker->error_code = status;
+ worker->error_message = "SEC_E_INVALID_TOKEN The error is due to a malformed input token, such as a token corrupted in transit, a token of incorrect size, or a token passed into the wrong security package. Passing a token to the wrong package can happen if the client and server did not negotiate the proper security package.";
+ } else if(status == SEC_E_LOGON_DENIED) {
+ worker->error = TRUE;
+ worker->error_code = status;
+ worker->error_message = "SEC_E_LOGON_DENIED The logon failed.";
+ } else if(status == SEC_E_NO_AUTHENTICATING_AUTHORITY) {
+ worker->error = TRUE;
+ worker->error_code = status;
+ worker->error_message = "SEC_E_NO_AUTHENTICATING_AUTHORITY No authority could be contacted for authentication. The domain name of the authenticating party could be wrong, the domain could be unreachable, or there might have been a trust relationship failure.";
+ } else if(status == SEC_E_NO_CREDENTIALS) {
+ worker->error = TRUE;
+ worker->error_code = status;
+ worker->error_message = "SEC_E_NO_CREDENTIALS No credentials are available in the security package.";
+ } else if(status == SEC_E_TARGET_UNKNOWN) {
+ worker->error = TRUE;
+ worker->error_code = status;
+ worker->error_message = "SEC_E_TARGET_UNKNOWN The target was not recognized.";
+ } else if(status == SEC_E_UNSUPPORTED_FUNCTION) {
+ worker->error = TRUE;
+ worker->error_code = status;
+ worker->error_message = "SEC_E_UNSUPPORTED_FUNCTION A context attribute flag that is not valid (ISC_REQ_DELEGATE or ISC_REQ_PROMPT_FOR_CREDS) was specified in the fContextReq parameter.";
+ } else if(status == SEC_E_WRONG_PRINCIPAL) {
+ worker->error = TRUE;
+ worker->error_code = status;
+ worker->error_message = "SEC_E_WRONG_PRINCIPAL The principal that received the authentication request is not the same as the one passed into the pszTargetName parameter. This indicates a failure in mutual authentication.";
+ } else {
+ worker->error = TRUE;
+ worker->error_code = status;
+ worker->error_message = DisplaySECError(status);
+ }
+
+ // Clean up data
+ if(call->decoded_input_str != NULL) free(call->decoded_input_str);
+ if(call->service_principal_name_str != NULL) free(call->service_principal_name_str);
+}
+
+static Local<Value> _map_initializeContext(Worker *worker) {
+ // Unwrap the security context
+ SecurityContext *context = (SecurityContext *)worker->return_value;
+ // Return the value
+ return context->handle();
+}
+
+NAN_METHOD(SecurityContext::InitializeContext) {
+ char *service_principal_name_str = NULL, *input_str = NULL, *decoded_input_str = NULL;
+ int decoded_input_str_length = NULL;
+ // Store reference to security credentials
+ SecurityCredentials *security_credentials = NULL;
+
+ // We need 3 parameters
+ if(info.Length() != 4)
+ return Nan::ThrowError("Initialize must be called with [credential:SecurityCredential, servicePrincipalName:string, input:string, callback:function]");
+
+ // First parameter must be an instance of SecurityCredentials
+ if(!SecurityCredentials::HasInstance(info[0]))
+ return Nan::ThrowError("First parameter for Initialize must be an instance of SecurityCredentials");
+
+ // Second parameter must be a string
+ if(!info[1]->IsString())
+ return Nan::ThrowError("Second parameter for Initialize must be a string");
+
+ // Third parameter must be a base64 encoded string
+ if(!info[2]->IsString())
+ return Nan::ThrowError("Second parameter for Initialize must be a string");
+
+ // Third parameter must be a callback
+ if(!info[3]->IsFunction())
+ return Nan::ThrowError("Third parameter for Initialize must be a callback function");
+
+ // Let's unpack the values
+ Local<String> service_principal_name = info[1]->ToString();
+ service_principal_name_str = (char *)calloc(service_principal_name->Utf8Length() + 1, sizeof(char));
+ service_principal_name->WriteUtf8(service_principal_name_str);
+
+ // Unpack the user name
+ Local<String> input = info[2]->ToString();
+
+ if(input->Utf8Length() > 0) {
+ input_str = (char *)calloc(input->Utf8Length() + 1, sizeof(char));
+ input->WriteUtf8(input_str);
+
+ // Now let's get the base64 decoded string
+ decoded_input_str = (char *)base64_decode(input_str, &decoded_input_str_length);
+ // Free original allocation
+ free(input_str);
+ }
+
+ // Unpack the Security credentials
+ security_credentials = Nan::ObjectWrap::Unwrap<SecurityCredentials>(info[0]->ToObject());
+ // Create Security context instance
+ Local<Object> security_context_value = Nan::New(constructor_template)->GetFunction()->NewInstance();
+ // Unwrap the security context
+ SecurityContext *security_context = Nan::ObjectWrap::Unwrap<SecurityContext>(security_context_value);
+ // Add a reference to the security_credentials
+ security_context->security_credentials = security_credentials;
+
+ // Build the call function
+ SecurityContextStaticInitializeCall *call = (SecurityContextStaticInitializeCall *)calloc(1, sizeof(SecurityContextStaticInitializeCall));
+ call->context = security_context;
+ call->decoded_input_str = decoded_input_str;
+ call->decoded_input_str_length = decoded_input_str_length;
+ call->service_principal_name_str = service_principal_name_str;
+
+ // Callback
+ Local<Function> callback = Local<Function>::Cast(info[3]);
+
+ // Let's allocate some space
+ Worker *worker = new Worker();
+ worker->error = false;
+ worker->request.data = worker;
+ worker->callback = new Nan::Callback(callback);
+ worker->parameters = call;
+ worker->execute = _initializeContext;
+ worker->mapper = _map_initializeContext;
+
+ // Schedule the worker with lib_uv
+ uv_queue_work(uv_default_loop(), &worker->request, Process, (uv_after_work_cb)After);
+
+ // Return no value as it's callback based
+ info.GetReturnValue().Set(Nan::Undefined());
+}
+
+NAN_GETTER(SecurityContext::PayloadGetter) {
+ // Unpack the context object
+ SecurityContext *context = Nan::ObjectWrap::Unwrap<SecurityContext>(info.This());
+ // Return the low bits
+ info.GetReturnValue().Set(Nan::New<String>(context->payload).ToLocalChecked());
+}
+
+NAN_GETTER(SecurityContext::HasContextGetter) {
+ // Unpack the context object
+ SecurityContext *context = Nan::ObjectWrap::Unwrap<SecurityContext>(info.This());
+ // Return the low bits
+ info.GetReturnValue().Set(Nan::New<Boolean>(context->hasContext));
+}
+
+//
+// Async InitializeContextStep
+//
+typedef struct SecurityContextStepStaticInitializeCall {
+ char *service_principal_name_str;
+ char *decoded_input_str;
+ int decoded_input_str_length;
+ SecurityContext *context;
+} SecurityContextStepStaticInitializeCall;
+
+static void _initializeContextStep(Worker *worker) {
+ // Outbound data array
+ BYTE *out_bound_data_str = NULL;
+ // Status of operation
+ SECURITY_STATUS status;
+ // Unpack data
+ SecurityContextStepStaticInitializeCall *call = (SecurityContextStepStaticInitializeCall *)worker->parameters;
+ SecurityContext *context = call->context;
+ // Structures used for c calls
+ SecBufferDesc ibd, obd;
+ SecBuffer ib, ob;
+
+ //
+ // Prepare data structure for returned data from SSPI
+ ob.BufferType = SECBUFFER_TOKEN;
+ ob.cbBuffer = context->m_PkgInfo->cbMaxToken;
+ // Allocate space for return data
+ out_bound_data_str = new BYTE[ob.cbBuffer + sizeof(DWORD)];
+ ob.pvBuffer = out_bound_data_str;
+ // prepare buffer description
+ obd.cBuffers = 1;
+ obd.ulVersion = SECBUFFER_VERSION;
+ obd.pBuffers = &ob;
+
+ //
+ // Prepare the data we are passing to the SSPI method
+ if(call->decoded_input_str_length > 0) {
+ ib.BufferType = SECBUFFER_TOKEN;
+ ib.cbBuffer = call->decoded_input_str_length;
+ ib.pvBuffer = call->decoded_input_str;
+ // prepare buffer description
+ ibd.cBuffers = 1;
+ ibd.ulVersion = SECBUFFER_VERSION;
+ ibd.pBuffers = &ib;
+ }
+
+ // Perform initialization step
+ status = _sspi_initializeSecurityContext(
+ &context->security_credentials->m_Credentials
+ , context->hasContext == true ? &context->m_Context : NULL
+ , const_cast<TCHAR*>(call->service_principal_name_str)
+ , 0x02 // MUTUAL
+ , 0
+ , 0 // Network
+ , call->decoded_input_str_length ? &ibd : NULL
+ , 0
+ , &context->m_Context
+ , &obd
+ , &context->CtxtAttr
+ , &context->Expiration
+ );
+
+ // If we have a ok or continue let's prepare the result
+ if(status == SEC_E_OK
+ || status == SEC_I_COMPLETE_NEEDED
+ || status == SEC_I_CONTINUE_NEEDED
+ || status == SEC_I_COMPLETE_AND_CONTINUE
+ ) {
+ // Set the new payload
+ if(context->payload != NULL) free(context->payload);
+ context->payload = base64_encode((const unsigned char *)ob.pvBuffer, ob.cbBuffer);
+ worker->return_code = status;
+ worker->return_value = context;
+ } else {
+ worker->error = TRUE;
+ worker->error_code = status;
+ worker->error_message = DisplaySECError(status);
+ }
+
+ // Clean up data
+ if(call->decoded_input_str != NULL) free(call->decoded_input_str);
+ if(call->service_principal_name_str != NULL) free(call->service_principal_name_str);
+}
+
+static Local<Value> _map_initializeContextStep(Worker *worker) {
+ // Unwrap the security context
+ SecurityContext *context = (SecurityContext *)worker->return_value;
+ // Return the value
+ return context->handle();
+}
+
+NAN_METHOD(SecurityContext::InitalizeStep) {
+ char *service_principal_name_str = NULL, *input_str = NULL, *decoded_input_str = NULL;
+ int decoded_input_str_length = NULL;
+
+ // We need 3 parameters
+ if(info.Length() != 3)
+ return Nan::ThrowError("Initialize must be called with [servicePrincipalName:string, input:string, callback:function]");
+
+ // Second parameter must be a string
+ if(!info[0]->IsString())
+ return Nan::ThrowError("First parameter for Initialize must be a string");
+
+ // Third parameter must be a base64 encoded string
+ if(!info[1]->IsString())
+ return Nan::ThrowError("Second parameter for Initialize must be a string");
+
+ // Third parameter must be a base64 encoded string
+ if(!info[2]->IsFunction())
+ return Nan::ThrowError("Third parameter for Initialize must be a callback function");
+
+ // Let's unpack the values
+ Local<String> service_principal_name = info[0]->ToString();
+ service_principal_name_str = (char *)calloc(service_principal_name->Utf8Length() + 1, sizeof(char));
+ service_principal_name->WriteUtf8(service_principal_name_str);
+
+ // Unpack the user name
+ Local<String> input = info[1]->ToString();
+
+ if(input->Utf8Length() > 0) {
+ input_str = (char *)calloc(input->Utf8Length() + 1, sizeof(char));
+ input->WriteUtf8(input_str);
+ // Now let's get the base64 decoded string
+ decoded_input_str = (char *)base64_decode(input_str, &decoded_input_str_length);
+ // Free input string
+ free(input_str);
+ }
+
+ // Unwrap the security context
+ SecurityContext *security_context = Nan::ObjectWrap::Unwrap<SecurityContext>(info.This());
+
+ // Create call structure
+ SecurityContextStepStaticInitializeCall *call = (SecurityContextStepStaticInitializeCall *)calloc(1, sizeof(SecurityContextStepStaticInitializeCall));
+ call->context = security_context;
+ call->decoded_input_str = decoded_input_str;
+ call->decoded_input_str_length = decoded_input_str_length;
+ call->service_principal_name_str = service_principal_name_str;
+
+ // Callback
+ Local<Function> callback = Local<Function>::Cast(info[2]);
+
+ // Let's allocate some space
+ Worker *worker = new Worker();
+ worker->error = false;
+ worker->request.data = worker;
+ worker->callback = new Nan::Callback(callback);
+ worker->parameters = call;
+ worker->execute = _initializeContextStep;
+ worker->mapper = _map_initializeContextStep;
+
+ // Schedule the worker with lib_uv
+ uv_queue_work(uv_default_loop(), &worker->request, Process, (uv_after_work_cb)After);
+
+ // Return no value as it's callback based
+ info.GetReturnValue().Set(Nan::Undefined());
+}
+
+//
+// Async EncryptMessage
+//
+typedef struct SecurityContextEncryptMessageCall {
+ SecurityContext *context;
+ SecurityBufferDescriptor *descriptor;
+ unsigned long flags;
+} SecurityContextEncryptMessageCall;
+
+static void _encryptMessage(Worker *worker) {
+ SECURITY_STATUS status;
+ // Unpack call
+ SecurityContextEncryptMessageCall *call = (SecurityContextEncryptMessageCall *)worker->parameters;
+ // Unpack the security context
+ SecurityContext *context = call->context;
+ SecurityBufferDescriptor *descriptor = call->descriptor;
+
+ // Let's execute encryption
+ status = _sspi_EncryptMessage(
+ &context->m_Context
+ , call->flags
+ , &descriptor->secBufferDesc
+ , 0
+ );
+
+ // We've got ok
+ if(status == SEC_E_OK) {
+ int bytesToAllocate = (int)descriptor->bufferSize();
+ // Free up existing payload
+ if(context->payload != NULL) free(context->payload);
+ // Save the payload
+ context->payload = base64_encode((unsigned char *)descriptor->toBuffer(), bytesToAllocate);
+ // Set result
+ worker->return_code = status;
+ worker->return_value = context;
+ } else {
+ worker->error = TRUE;
+ worker->error_code = status;
+ worker->error_message = DisplaySECError(status);
+ }
+}
+
+static Local<Value> _map_encryptMessage(Worker *worker) {
+ // Unwrap the security context
+ SecurityContext *context = (SecurityContext *)worker->return_value;
+ // Return the value
+ return context->handle();
+}
+
+NAN_METHOD(SecurityContext::EncryptMessage) {
+ if(info.Length() != 3)
+ return Nan::ThrowError("EncryptMessage takes an instance of SecurityBufferDescriptor, an integer flag and a callback function");
+ if(!SecurityBufferDescriptor::HasInstance(info[0]))
+ return Nan::ThrowError("EncryptMessage takes an instance of SecurityBufferDescriptor, an integer flag and a callback function");
+ if(!info[1]->IsUint32())
+ return Nan::ThrowError("EncryptMessage takes an instance of SecurityBufferDescriptor, an integer flag and a callback function");
+ if(!info[2]->IsFunction())
+ return Nan::ThrowError("EncryptMessage takes an instance of SecurityBufferDescriptor, an integer flag and a callback function");
+
+ // Unpack the security context
+ SecurityContext *security_context = Nan::ObjectWrap::Unwrap<SecurityContext>(info.This());
+
+ // Unpack the descriptor
+ SecurityBufferDescriptor *descriptor = Nan::ObjectWrap::Unwrap<SecurityBufferDescriptor>(info[0]->ToObject());
+
+ // Create call structure
+ SecurityContextEncryptMessageCall *call = (SecurityContextEncryptMessageCall *)calloc(1, sizeof(SecurityContextEncryptMessageCall));
+ call->context = security_context;
+ call->descriptor = descriptor;
+ call->flags = (unsigned long)info[1]->ToInteger()->Value();
+
+ // Callback
+ Local<Function> callback = Local<Function>::Cast(info[2]);
+
+ // Let's allocate some space
+ Worker *worker = new Worker();
+ worker->error = false;
+ worker->request.data = worker;
+ worker->callback = new Nan::Callback(callback);
+ worker->parameters = call;
+ worker->execute = _encryptMessage;
+ worker->mapper = _map_encryptMessage;
+
+ // Schedule the worker with lib_uv
+ uv_queue_work(uv_default_loop(), &worker->request, Process, (uv_after_work_cb)After);
+
+ // Return no value as it's callback based
+ info.GetReturnValue().Set(Nan::Undefined());
+}
+
+//
+// Async DecryptMessage
+//
+typedef struct SecurityContextDecryptMessageCall {
+ SecurityContext *context;
+ SecurityBufferDescriptor *descriptor;
+} SecurityContextDecryptMessageCall;
+
+static void _decryptMessage(Worker *worker) {
+ unsigned long quality = 0;
+ SECURITY_STATUS status;
+
+ // Unpack parameters
+ SecurityContextDecryptMessageCall *call = (SecurityContextDecryptMessageCall *)worker->parameters;
+ SecurityContext *context = call->context;
+ SecurityBufferDescriptor *descriptor = call->descriptor;
+
+ // Let's execute encryption
+ status = _sspi_DecryptMessage(
+ &context->m_Context
+ , &descriptor->secBufferDesc
+ , 0
+ , (unsigned long)&quality
+ );
+
+ // We've got ok
+ if(status == SEC_E_OK) {
+ int bytesToAllocate = (int)descriptor->bufferSize();
+ // Free up existing payload
+ if(context->payload != NULL) free(context->payload);
+ // Save the payload
+ context->payload = base64_encode((unsigned char *)descriptor->toBuffer(), bytesToAllocate);
+ // Set return values
+ worker->return_code = status;
+ worker->return_value = context;
+ } else {
+ worker->error = TRUE;
+ worker->error_code = status;
+ worker->error_message = DisplaySECError(status);
+ }
+}
+
+static Local<Value> _map_decryptMessage(Worker *worker) {
+ // Unwrap the security context
+ SecurityContext *context = (SecurityContext *)worker->return_value;
+ // Return the value
+ return context->handle();
+}
+
+NAN_METHOD(SecurityContext::DecryptMessage) {
+ if(info.Length() != 2)
+ return Nan::ThrowError("DecryptMessage takes an instance of SecurityBufferDescriptor and a callback function");
+ if(!SecurityBufferDescriptor::HasInstance(info[0]))
+ return Nan::ThrowError("DecryptMessage takes an instance of SecurityBufferDescriptor and a callback function");
+ if(!info[1]->IsFunction())
+ return Nan::ThrowError("DecryptMessage takes an instance of SecurityBufferDescriptor and a callback function");
+
+ // Unpack the security context
+ SecurityContext *security_context = Nan::ObjectWrap::Unwrap<SecurityContext>(info.This());
+ // Unpack the descriptor
+ SecurityBufferDescriptor *descriptor = Nan::ObjectWrap::Unwrap<SecurityBufferDescriptor>(info[0]->ToObject());
+ // Create call structure
+ SecurityContextDecryptMessageCall *call = (SecurityContextDecryptMessageCall *)calloc(1, sizeof(SecurityContextDecryptMessageCall));
+ call->context = security_context;
+ call->descriptor = descriptor;
+
+ // Callback
+ Local<Function> callback = Local<Function>::Cast(info[1]);
+
+ // Let's allocate some space
+ Worker *worker = new Worker();
+ worker->error = false;
+ worker->request.data = worker;
+ worker->callback = new Nan::Callback(callback);
+ worker->parameters = call;
+ worker->execute = _decryptMessage;
+ worker->mapper = _map_decryptMessage;
+
+ // Schedule the worker with lib_uv
+ uv_queue_work(uv_default_loop(), &worker->request, Process, (uv_after_work_cb)After);
+
+ // Return no value as it's callback based
+ info.GetReturnValue().Set(Nan::Undefined());
+}
+
+//
+// Async QueryContextAttributes
+//
+typedef struct SecurityContextQueryContextAttributesCall {
+ SecurityContext *context;
+ uint32_t attribute;
+} SecurityContextQueryContextAttributesCall;
+
+static void _queryContextAttributes(Worker *worker) {
+ SECURITY_STATUS status;
+
+ // Cast to data structure
+ SecurityContextQueryContextAttributesCall *call = (SecurityContextQueryContextAttributesCall *)worker->parameters;
+
+ // Allocate some space
+ SecPkgContext_Sizes *sizes = (SecPkgContext_Sizes *)calloc(1, sizeof(SecPkgContext_Sizes));
+ // Let's grab the query context attribute
+ status = _sspi_QueryContextAttributes(
+ &call->context->m_Context,
+ call->attribute,
+ sizes
+ );
+
+ if(status == SEC_E_OK) {
+ worker->return_code = status;
+ worker->return_value = sizes;
+ } else {
+ worker->error = TRUE;
+ worker->error_code = status;
+ worker->error_message = DisplaySECError(status);
+ }
+}
+
+static Local<Value> _map_queryContextAttributes(Worker *worker) {
+ // Cast to data structure
+ SecurityContextQueryContextAttributesCall *call = (SecurityContextQueryContextAttributesCall *)worker->parameters;
+ // Unpack the attribute
+ uint32_t attribute = call->attribute;
+
+ // Convert data
+ if(attribute == SECPKG_ATTR_SIZES) {
+ SecPkgContext_Sizes *sizes = (SecPkgContext_Sizes *)worker->return_value;
+ // Create object
+ Local<Object> value = Nan::New<Object>();
+ value->Set(Nan::New<String>("maxToken").ToLocalChecked(), Nan::New<Integer>(uint32_t(sizes->cbMaxToken)));
+ value->Set(Nan::New<String>("maxSignature").ToLocalChecked(), Nan::New<Integer>(uint32_t(sizes->cbMaxSignature)));
+ value->Set(Nan::New<String>("blockSize").ToLocalChecked(), Nan::New<Integer>(uint32_t(sizes->cbBlockSize)));
+ value->Set(Nan::New<String>("securityTrailer").ToLocalChecked(), Nan::New<Integer>(uint32_t(sizes->cbSecurityTrailer)));
+ return value;
+ }
+
+ // Return the value
+ return Nan::Null();
+}
+
+NAN_METHOD(SecurityContext::QueryContextAttributes) {
+ if(info.Length() != 2)
+ return Nan::ThrowError("QueryContextAttributes method takes a an integer Attribute specifier and a callback function");
+ if(!info[0]->IsInt32())
+ return Nan::ThrowError("QueryContextAttributes method takes a an integer Attribute specifier and a callback function");
+ if(!info[1]->IsFunction())
+ return Nan::ThrowError("QueryContextAttributes method takes a an integer Attribute specifier and a callback function");
+
+ // Unpack the security context
+ SecurityContext *security_context = Nan::ObjectWrap::Unwrap<SecurityContext>(info.This());
+
+ // Unpack the int value
+ uint32_t attribute = info[0]->ToInt32()->Value();
+
+ // Check that we have a supported attribute
+ if(attribute != SECPKG_ATTR_SIZES)
+ return Nan::ThrowError("QueryContextAttributes only supports the SECPKG_ATTR_SIZES attribute");
+
+ // Create call structure
+ SecurityContextQueryContextAttributesCall *call = (SecurityContextQueryContextAttributesCall *)calloc(1, sizeof(SecurityContextQueryContextAttributesCall));
+ call->attribute = attribute;
+ call->context = security_context;
+
+ // Callback
+ Local<Function> callback = Local<Function>::Cast(info[1]);
+
+ // Let's allocate some space
+ Worker *worker = new Worker();
+ worker->error = false;
+ worker->request.data = worker;
+ worker->callback = new Nan::Callback(callback);
+ worker->parameters = call;
+ worker->execute = _queryContextAttributes;
+ worker->mapper = _map_queryContextAttributes;
+
+ // Schedule the worker with lib_uv
+ uv_queue_work(uv_default_loop(), &worker->request, Process, (uv_after_work_cb)After);
+
+ // Return no value as it's callback based
+ info.GetReturnValue().Set(Nan::Undefined());
+}
+
+void SecurityContext::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<v8::FunctionTemplate>(static_cast<NAN_METHOD((*))>(SecurityContext::New));
+ t->InstanceTemplate()->SetInternalFieldCount(1);
+ t->SetClassName(Nan::New<String>("SecurityContext").ToLocalChecked());
+
+ // Class methods
+ Nan::SetMethod(t, "initialize", SecurityContext::InitializeContext);
+
+ // Set up method for the instance
+ Nan::SetPrototypeMethod(t, "initialize", SecurityContext::InitalizeStep);
+ Nan::SetPrototypeMethod(t, "decryptMessage", SecurityContext::DecryptMessage);
+ Nan::SetPrototypeMethod(t, "queryContextAttributes", SecurityContext::QueryContextAttributes);
+ Nan::SetPrototypeMethod(t, "encryptMessage", SecurityContext::EncryptMessage);
+
+ // Get prototype
+ Local<ObjectTemplate> proto = t->PrototypeTemplate();
+
+ // Getter for the response
+ Nan::SetAccessor(proto, Nan::New<String>("payload").ToLocalChecked(), SecurityContext::PayloadGetter);
+ Nan::SetAccessor(proto, Nan::New<String>("hasContext").ToLocalChecked(), SecurityContext::HasContextGetter);
+
+ // Set persistent
+ SecurityContext::constructor_template.Reset(t);
+
+ // Set the symbol
+ target->ForceSet(Nan::New<String>("SecurityContext").ToLocalChecked(), t->GetFunction());
+}
+
+static LPSTR DisplaySECError(DWORD ErrCode) {
+ LPSTR pszName = NULL; // WinError.h
+
+ switch(ErrCode) {
+ case SEC_E_BUFFER_TOO_SMALL:
+ pszName = "SEC_E_BUFFER_TOO_SMALL - The message buffer is too small. Used with the Digest SSP.";
+ break;
+
+ case SEC_E_CRYPTO_SYSTEM_INVALID:
+ pszName = "SEC_E_CRYPTO_SYSTEM_INVALID - The cipher chosen for the security context is not supported. Used with the Digest SSP.";
+ break;
+ case SEC_E_INCOMPLETE_MESSAGE:
+ pszName = "SEC_E_INCOMPLETE_MESSAGE - The data in the input buffer is incomplete. The application needs to read more data from the server and call DecryptMessageSync (General) again.";
+ break;
+
+ case SEC_E_INVALID_HANDLE:
+ pszName = "SEC_E_INVALID_HANDLE - A context handle that is not valid was specified in the phContext parameter. Used with the Digest and Schannel SSPs.";
+ break;
+
+ case SEC_E_INVALID_TOKEN:
+ pszName = "SEC_E_INVALID_TOKEN - The buffers are of the wrong type or no buffer of type SECBUFFER_DATA was found. Used with the Schannel SSP.";
+ break;
+
+ case SEC_E_MESSAGE_ALTERED:
+ pszName = "SEC_E_MESSAGE_ALTERED - The message has been altered. Used with the Digest and Schannel SSPs.";
+ break;
+
+ case SEC_E_OUT_OF_SEQUENCE:
+ pszName = "SEC_E_OUT_OF_SEQUENCE - The message was not received in the correct sequence.";
+ break;
+
+ case SEC_E_QOP_NOT_SUPPORTED:
+ pszName = "SEC_E_QOP_NOT_SUPPORTED - Neither confidentiality nor integrity are supported by the security context. Used with the Digest SSP.";
+ break;
+
+ case SEC_I_CONTEXT_EXPIRED:
+ pszName = "SEC_I_CONTEXT_EXPIRED - The message sender has finished using the connection and has initiated a shutdown.";
+ break;
+
+ case SEC_I_RENEGOTIATE:
+ pszName = "SEC_I_RENEGOTIATE - The remote party requires a new handshake sequence or the application has just initiated a shutdown.";
+ break;
+
+ case SEC_E_ENCRYPT_FAILURE:
+ pszName = "SEC_E_ENCRYPT_FAILURE - The specified data could not be encrypted.";
+ break;
+
+ case SEC_E_DECRYPT_FAILURE:
+ pszName = "SEC_E_DECRYPT_FAILURE - The specified data could not be decrypted.";
+ break;
+ case -1:
+ pszName = "Failed to load security.dll library";
+ break;
+ }
+
+ return pszName;
+}
+
diff --git a/lib/win32/wrappers/security_context.h b/lib/win32/wrappers/security_context.h
new file mode 100644
index 0000000..1d9387d
--- /dev/null
+++ b/lib/win32/wrappers/security_context.h
@@ -0,0 +1,74 @@
+#ifndef SECURITY_CONTEXT_H
+#define SECURITY_CONTEXT_H
+
+#include <node.h>
+#include <node_object_wrap.h>
+#include <v8.h>
+
+#define SECURITY_WIN32 1
+
+#include <winsock2.h>
+#include <windows.h>
+#include <sspi.h>
+#include <tchar.h>
+#include "security_credentials.h"
+#include "../worker.h"
+#include <nan.h>
+
+extern "C" {
+ #include "../kerberos_sspi.h"
+ #include "../base64.h"
+}
+
+using namespace v8;
+using namespace node;
+
+class SecurityContext : public Nan::ObjectWrap {
+ public:
+ SecurityContext();
+ ~SecurityContext();
+
+ // Security info package
+ PSecPkgInfo m_PkgInfo;
+ // Do we have a context
+ bool hasContext;
+ // Reference to security credentials
+ SecurityCredentials *security_credentials;
+ // Security context
+ CtxtHandle m_Context;
+ // Attributes
+ DWORD CtxtAttr;
+ // Expiry time for ticket
+ TimeStamp Expiration;
+ // Payload
+ char *payload;
+
+ // Has instance check
+ static inline bool HasInstance(Local<Value> val) {
+ if (!val->IsObject()) return false;
+ Local<Object> obj = val->ToObject();
+ return Nan::New(constructor_template)->HasInstance(obj);
+ };
+
+ // Functions available from V8
+ static void Initialize(Nan::ADDON_REGISTER_FUNCTION_ARGS_TYPE target);
+ static NAN_METHOD(InitializeContext);
+ static NAN_METHOD(InitalizeStep);
+ static NAN_METHOD(DecryptMessage);
+ static NAN_METHOD(QueryContextAttributes);
+ static NAN_METHOD(EncryptMessage);
+
+ // Payload getter
+ static NAN_GETTER(PayloadGetter);
+ // hasContext getter
+ static NAN_GETTER(HasContextGetter);
+
+ // Constructor used for creating new Long objects from C++
+ static Nan::Persistent<FunctionTemplate> constructor_template;
+
+ // private:
+ // Create a new instance
+ static NAN_METHOD(New);
+};
+
+#endif
diff --git a/lib/win32/wrappers/security_context.js b/lib/win32/wrappers/security_context.js
new file mode 100644
index 0000000..ef04e92
--- /dev/null
+++ b/lib/win32/wrappers/security_context.js
@@ -0,0 +1,3 @@
+var SecurityContextNative = require('../../../build/Release/kerberos').SecurityContext;
+// Export the modified class
+exports.SecurityContext = SecurityContextNative;
\ No newline at end of file
diff --git a/lib/win32/wrappers/security_credentials.cc b/lib/win32/wrappers/security_credentials.cc
new file mode 100644
index 0000000..732af3f
--- /dev/null
+++ b/lib/win32/wrappers/security_credentials.cc
@@ -0,0 +1,348 @@
+#include <node.h>
+#include <assert.h>
+#include <string.h>
+#include <stdlib.h>
+#include <v8.h>
+#include <node_buffer.h>
+#include <cstring>
+#include <cmath>
+#include <cstdlib>
+#include <iostream>
+#include <limits>
+
+#include "security_credentials.h"
+
+#ifndef ARRAY_SIZE
+# define ARRAY_SIZE(a) (sizeof((a)) / sizeof((a)[0]))
+#endif
+
+static LPSTR DisplaySECError(DWORD ErrCode);
+
+Nan::Persistent<FunctionTemplate> SecurityCredentials::constructor_template;
+
+SecurityCredentials::SecurityCredentials() : Nan::ObjectWrap() {
+}
+
+SecurityCredentials::~SecurityCredentials() {
+}
+
+NAN_METHOD(SecurityCredentials::New) {
+ // Create security credentials instance
+ SecurityCredentials *security_credentials = new SecurityCredentials();
+ // Wrap it
+ security_credentials->Wrap(info.This());
+ // Return the object
+ info.GetReturnValue().Set(info.This());
+}
+
+// Call structs
+typedef struct SecurityCredentialCall {
+ char *package_str;
+ char *username_str;
+ char *password_str;
+ char *domain_str;
+ SecurityCredentials *credentials;
+} SecurityCredentialCall;
+
+// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+// authGSSClientInit
+// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+static void _authSSPIAquire(Worker *worker) {
+ // Status of operation
+ SECURITY_STATUS status;
+
+ // Unpack data
+ SecurityCredentialCall *call = (SecurityCredentialCall *)worker->parameters;
+
+ // // Unwrap the credentials
+ // SecurityCredentials *security_credentials = (SecurityCredentials *)call->credentials;
+ SecurityCredentials *security_credentials = new SecurityCredentials();
+
+ // If we have domain string
+ if(call->domain_str != NULL) {
+ security_credentials->m_Identity.Domain = USTR(_tcsdup(call->domain_str));
+ security_credentials->m_Identity.DomainLength = (unsigned long)_tcslen(call->domain_str);
+ } else {
+ security_credentials->m_Identity.Domain = NULL;
+ security_credentials->m_Identity.DomainLength = 0;
+ }
+
+ // Set up the user
+ security_credentials->m_Identity.User = USTR(_tcsdup(call->username_str));
+ security_credentials->m_Identity.UserLength = (unsigned long)_tcslen(call->username_str);
+
+ // If we have a password string
+ if(call->password_str != NULL) {
+ // Set up the password
+ security_credentials->m_Identity.Password = USTR(_tcsdup(call->password_str));
+ security_credentials->m_Identity.PasswordLength = (unsigned long)_tcslen(call->password_str);
+ }
+
+ #ifdef _UNICODE
+ security_credentials->m_Identity.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
+ #else
+ security_credentials->m_Identity.Flags = SEC_WINNT_AUTH_IDENTITY_ANSI;
+ #endif
+
+ // Attempt to acquire credentials
+ status = _sspi_AcquireCredentialsHandle(
+ NULL,
+ call->package_str,
+ SECPKG_CRED_OUTBOUND,
+ NULL,
+ call->password_str != NULL ? &security_credentials->m_Identity : NULL,
+ NULL, NULL,
+ &security_credentials->m_Credentials,
+ &security_credentials->Expiration
+ );
+
+ // We have an error
+ if(status != SEC_E_OK) {
+ worker->error = TRUE;
+ worker->error_code = status;
+ worker->error_message = DisplaySECError(status);
+ } else {
+ worker->return_code = status;
+ worker->return_value = security_credentials;
+ }
+
+ // Free up parameter structure
+ if(call->package_str != NULL) free(call->package_str);
+ if(call->domain_str != NULL) free(call->domain_str);
+ if(call->password_str != NULL) free(call->password_str);
+ if(call->username_str != NULL) free(call->username_str);
+ free(call);
+}
+
+static Local<Value> _map_authSSPIAquire(Worker *worker) {
+ return Nan::Null();
+}
+
+NAN_METHOD(SecurityCredentials::Aquire) {
+ char *package_str = NULL, *username_str = NULL, *password_str = NULL, *domain_str = NULL;
+ // Unpack the variables
+ if(info.Length() != 2 && info.Length() != 3 && info.Length() != 4 && info.Length() != 5)
+ return Nan::ThrowError("Aquire must be called with either [package:string, username:string, [password:string, domain:string], callback:function]");
+
+ if(!info[0]->IsString())
+ return Nan::ThrowError("Aquire must be called with either [package:string, username:string, [password:string, domain:string], callback:function]");
+
+ if(!info[1]->IsString())
+ return Nan::ThrowError("Aquire must be called with either [package:string, username:string, [password:string, domain:string], callback:function]");
+
+ if(info.Length() == 3 && (!info[2]->IsString() && !info[2]->IsFunction()))
+ return Nan::ThrowError("Aquire must be called with either [package:string, username:string, [password:string, domain:string], callback:function]");
+
+ if(info.Length() == 4 && (!info[3]->IsString() && !info[3]->IsUndefined() && !info[3]->IsNull()) && !info[3]->IsFunction())
+ return Nan::ThrowError("Aquire must be called with either [package:string, username:string, [password:string, domain:string], callback:function]");
+
+ if(info.Length() == 5 && !info[4]->IsFunction())
+ return Nan::ThrowError("Aquire must be called with either [package:string, username:string, [password:string, domain:string], callback:function]");
+
+ Local<Function> callbackHandle;
+
+ // Figure out which parameter is the callback
+ if(info.Length() == 5) {
+ callbackHandle = Local<Function>::Cast(info[4]);
+ } else if(info.Length() == 4) {
+ callbackHandle = Local<Function>::Cast(info[3]);
+ } else if(info.Length() == 3) {
+ callbackHandle = Local<Function>::Cast(info[2]);
+ }
+
+ // Unpack the package
+ Local<String> package = info[0]->ToString();
+ package_str = (char *)calloc(package->Utf8Length() + 1, sizeof(char));
+ package->WriteUtf8(package_str);
+
+ // Unpack the user name
+ Local<String> username = info[1]->ToString();
+ username_str = (char *)calloc(username->Utf8Length() + 1, sizeof(char));
+ username->WriteUtf8(username_str);
+
+ // If we have a password
+ if(info.Length() == 3 || info.Length() == 4 || info.Length() == 5) {
+ Local<String> password = info[2]->ToString();
+ password_str = (char *)calloc(password->Utf8Length() + 1, sizeof(char));
+ password->WriteUtf8(password_str);
+ }
+
+ // If we have a domain
+ if((info.Length() == 4 || info.Length() == 5) && info[3]->IsString()) {
+ Local<String> domain = info[3]->ToString();
+ domain_str = (char *)calloc(domain->Utf8Length() + 1, sizeof(char));
+ domain->WriteUtf8(domain_str);
+ }
+
+ // Allocate call structure
+ SecurityCredentialCall *call = (SecurityCredentialCall *)calloc(1, sizeof(SecurityCredentialCall));
+ call->domain_str = domain_str;
+ call->package_str = package_str;
+ call->password_str = password_str;
+ call->username_str = username_str;
+
+ // Unpack the callback
+ 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 = _authSSPIAquire;
+ worker->mapper = _map_authSSPIAquire;
+
+ // Schedule the worker with lib_uv
+ uv_queue_work(uv_default_loop(), &worker->request, SecurityCredentials::Process, (uv_after_work_cb)SecurityCredentials::After);
+
+ // Return no value as it's callback based
+ info.GetReturnValue().Set(Nan::Undefined());
+}
+
+void SecurityCredentials::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>("SecurityCredentials").ToLocalChecked());
+
+ // Class methods
+ Nan::SetMethod(t, "aquire", Aquire);
+
+ // Set persistent
+ constructor_template.Reset(t);
+
+ // Set the symbol
+ target->ForceSet(Nan::New<String>("SecurityCredentials").ToLocalChecked(), t->GetFunction());
+
+ // Attempt to load the security.dll library
+ load_library();
+}
+
+static LPSTR DisplaySECError(DWORD ErrCode) {
+ LPSTR pszName = NULL; // WinError.h
+
+ switch(ErrCode) {
+ case SEC_E_BUFFER_TOO_SMALL:
+ pszName = "SEC_E_BUFFER_TOO_SMALL - The message buffer is too small. Used with the Digest SSP.";
+ break;
+
+ case SEC_E_CRYPTO_SYSTEM_INVALID:
+ pszName = "SEC_E_CRYPTO_SYSTEM_INVALID - The cipher chosen for the security context is not supported. Used with the Digest SSP.";
+ break;
+ case SEC_E_INCOMPLETE_MESSAGE:
+ pszName = "SEC_E_INCOMPLETE_MESSAGE - The data in the input buffer is incomplete. The application needs to read more data from the server and call DecryptMessage (General) again.";
+ break;
+
+ case SEC_E_INVALID_HANDLE:
+ pszName = "SEC_E_INVALID_HANDLE - A context handle that is not valid was specified in the phContext parameter. Used with the Digest and Schannel SSPs.";
+ break;
+
+ case SEC_E_INVALID_TOKEN:
+ pszName = "SEC_E_INVALID_TOKEN - The buffers are of the wrong type or no buffer of type SECBUFFER_DATA was found. Used with the Schannel SSP.";
+ break;
+
+ case SEC_E_MESSAGE_ALTERED:
+ pszName = "SEC_E_MESSAGE_ALTERED - The message has been altered. Used with the Digest and Schannel SSPs.";
+ break;
+
+ case SEC_E_OUT_OF_SEQUENCE:
+ pszName = "SEC_E_OUT_OF_SEQUENCE - The message was not received in the correct sequence.";
+ break;
+
+ case SEC_E_QOP_NOT_SUPPORTED:
+ pszName = "SEC_E_QOP_NOT_SUPPORTED - Neither confidentiality nor integrity are supported by the security context. Used with the Digest SSP.";
+ break;
+
+ case SEC_I_CONTEXT_EXPIRED:
+ pszName = "SEC_I_CONTEXT_EXPIRED - The message sender has finished using the connection and has initiated a shutdown.";
+ break;
+
+ case SEC_I_RENEGOTIATE:
+ pszName = "SEC_I_RENEGOTIATE - The remote party requires a new handshake sequence or the application has just initiated a shutdown.";
+ break;
+
+ case SEC_E_ENCRYPT_FAILURE:
+ pszName = "SEC_E_ENCRYPT_FAILURE - The specified data could not be encrypted.";
+ break;
+
+ case SEC_E_DECRYPT_FAILURE:
+ pszName = "SEC_E_DECRYPT_FAILURE - The specified data could not be decrypted.";
+ break;
+ case -1:
+ pszName = "Failed to load security.dll library";
+ break;
+
+ }
+
+ return pszName;
+}
+
+// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+// UV Lib callbacks
+// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+void SecurityCredentials::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 SecurityCredentials::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 {
+ SecurityCredentials *return_value = (SecurityCredentials *)worker->return_value;
+ // Create a new instance
+ Local<Object> result = Nan::New(constructor_template)->GetFunction()->NewInstance();
+ // Unwrap the credentials
+ SecurityCredentials *security_credentials = Nan::ObjectWrap::Unwrap<SecurityCredentials>(result);
+ // Set the values
+ security_credentials->m_Identity = return_value->m_Identity;
+ security_credentials->m_Credentials = return_value->m_Credentials;
+ security_credentials->Expiration = return_value->Expiration;
+ // Set up the callback with a null first
+ Local<Value> info[2] = { Nan::Null(), result};
+ // 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;
+}
+
diff --git a/lib/win32/wrappers/security_credentials.h b/lib/win32/wrappers/security_credentials.h
new file mode 100644
index 0000000..71751a0
--- /dev/null
+++ b/lib/win32/wrappers/security_credentials.h
@@ -0,0 +1,68 @@
+#ifndef SECURITY_CREDENTIALS_H
+#define SECURITY_CREDENTIALS_H
+
+#include <node.h>
+#include <node_object_wrap.h>
+#include <v8.h>
+
+#define SECURITY_WIN32 1
+
+#include <WinSock2.h>
+#include <windows.h>
+#include <sspi.h>
+#include <tchar.h>
+#include <nan.h>
+#include "../worker.h"
+#include <uv.h>
+
+extern "C" {
+ #include "../kerberos_sspi.h"
+}
+
+// SEC_WINNT_AUTH_IDENTITY makes it unusually hard
+// to compile for both Unicode and ansi, so I use this macro:
+#ifdef _UNICODE
+#define USTR(str) (str)
+#else
+#define USTR(str) ((unsigned char*)(str))
+#endif
+
+using namespace v8;
+using namespace node;
+
+class SecurityCredentials : public Nan::ObjectWrap {
+ public:
+ SecurityCredentials();
+ ~SecurityCredentials();
+
+ // Pointer to context object
+ SEC_WINNT_AUTH_IDENTITY m_Identity;
+ // credentials
+ CredHandle m_Credentials;
+ // Expiry time for ticket
+ TimeStamp Expiration;
+
+ // Has instance check
+ static inline bool HasInstance(Local<Value> val) {
+ if (!val->IsObject()) return false;
+ Local<Object> obj = val->ToObject();
+ return Nan::New(constructor_template)->HasInstance(obj);
+ };
+
+ // Functions available from V8
+ static void Initialize(Nan::ADDON_REGISTER_FUNCTION_ARGS_TYPE target);
+ static NAN_METHOD(Aquire);
+
+ // Constructor used for creating new Long objects from C++
+ static Nan::Persistent<FunctionTemplate> constructor_template;
+
+ private:
+ // Create a new instance
+ static NAN_METHOD(New);
+ // Handles the uv calls
+ static void Process(uv_work_t* work_req);
+ // Called after work is done
+ static void After(uv_work_t* work_req);
+};
+
+#endif
\ No newline at end of file
diff --git a/lib/win32/wrappers/security_credentials.js b/lib/win32/wrappers/security_credentials.js
new file mode 100644
index 0000000..4215c92
--- /dev/null
+++ b/lib/win32/wrappers/security_credentials.js
@@ -0,0 +1,22 @@
+var SecurityCredentialsNative = require('../../../build/Release/kerberos').SecurityCredentials;
+
+// Add simple kebros helper
+SecurityCredentialsNative.aquire_kerberos = function(username, password, domain, callback) {
+ if(typeof password == 'function') {
+ callback = password;
+ password = null;
+ } else if(typeof domain == 'function') {
+ callback = domain;
+ domain = null;
+ }
+
+ // We are going to use the async version
+ if(typeof callback == 'function') {
+ return SecurityCredentialsNative.aquire('Kerberos', username, password, domain, callback);
+ } else {
+ return SecurityCredentialsNative.aquireSync('Kerberos', username, password, domain);
+ }
+}
+
+// Export the modified class
+exports.SecurityCredentials = SecurityCredentialsNative;
\ No newline at end of file
diff --git a/lib/worker.cc b/lib/worker.cc
new file mode 100644
index 0000000..e7a472f
--- /dev/null
+++ b/lib/worker.cc
@@ -0,0 +1,7 @@
+#include "worker.h"
+
+Worker::Worker() {
+}
+
+Worker::~Worker() {
+}
\ No newline at end of file
diff --git a/lib/worker.h b/lib/worker.h
new file mode 100644
index 0000000..1b0dced
--- /dev/null
+++ b/lib/worker.h
@@ -0,0 +1,38 @@
+#ifndef WORKER_H_
+#define WORKER_H_
+
+#include <node.h>
+#include <node_object_wrap.h>
+#include <nan.h>
+#include <v8.h>
+
+using namespace node;
+using namespace v8;
+
+class Worker {
+ public:
+ Worker();
+ virtual ~Worker();
+
+ // libuv's request struct.
+ uv_work_t request;
+ // Callback
+ Nan::Callback *callback;
+ // Parameters
+ void *parameters;
+ // Results
+ void *return_value;
+ // Did we raise an error
+ bool error;
+ // The error message
+ char *error_message;
+ // Error code if not message
+ int error_code;
+ // Any return code
+ int return_code;
+ // Method we are going to fire
+ void (*execute)(Worker *worker);
+ Local<Value> (*mapper)(Worker *worker);
+};
+
+#endif // WORKER_H_
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..b3d74b5
--- /dev/null
+++ b/package.json
@@ -0,0 +1,29 @@
+{
+ "name": "kerberos",
+ "version": "0.0.14",
+ "description": "Kerberos library for Node.js",
+ "main": "index.js",
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/christkv/kerberos.git"
+ },
+ "keywords": [
+ "kerberos",
+ "security",
+ "authentication"
+ ],
+ "dependencies": {
+ "nan": "~2.0"
+ },
+ "devDependencies": {
+ "nodeunit": "latest"
+ },
+ "scripts": {
+ "install" : "(node-gyp rebuild) || (exit 0)",
+ "test" : "nodeunit ./test"
+ },
+ "author": "Christian Amor Kvalheim",
+ "license": "Apache 2.0",
+ "readmeFilename": "README.md",
+ "gitHead": "bb01d4fe322e022999aca19da564e7d9db59a8ed"
+}
diff --git a/test/kerberos_tests.js b/test/kerberos_tests.js
new file mode 100644
index 0000000..a06c5fd
--- /dev/null
+++ b/test/kerberos_tests.js
@@ -0,0 +1,34 @@
+exports.setUp = function(callback) {
+ callback();
+}
+
+exports.tearDown = function(callback) {
+ callback();
+}
+
+exports['Simple initialize of Kerberos object'] = function(test) {
+ var Kerberos = require('../lib/kerberos.js').Kerberos;
+ var kerberos = new Kerberos();
+ // console.dir(kerberos)
+
+ // Initiate kerberos client
+ kerberos.authGSSClientInit('mongodb at kdc.10gen.me', Kerberos.GSS_C_MUTUAL_FLAG, function(err, context) {
+ console.log("===================================== authGSSClientInit")
+ test.equal(null, err);
+ test.ok(context != null && typeof context == 'object');
+ // console.log("===================================== authGSSClientInit")
+ console.dir(err)
+ console.dir(context)
+ // console.dir(typeof result)
+
+ // Perform the first step
+ kerberos.authGSSClientStep(context, function(err, result) {
+ console.log("===================================== authGSSClientStep")
+ console.dir(err)
+ console.dir(result)
+ console.dir(context)
+
+ test.done();
+ });
+ });
+}
\ No newline at end of file
diff --git a/test/kerberos_win32_test.js b/test/kerberos_win32_test.js
new file mode 100644
index 0000000..c8509db
--- /dev/null
+++ b/test/kerberos_win32_test.js
@@ -0,0 +1,15 @@
+if (/^win/.test(process.platform)) {
+
+exports['Simple initialize of Kerberos win32 object'] = function(test) {
+ var KerberosNative = require('../build/Release/kerberos').Kerberos;
+ // console.dir(KerberosNative)
+ var kerberos = new KerberosNative();
+ console.log("=========================================== 0")
+ console.dir(kerberos.acquireAlternateCredentials("dev1 at 10GEN.ME", "a"));
+ console.log("=========================================== 1")
+ console.dir(kerberos.prepareOutboundPackage("mongodb/kdc.10gen.com"));
+ console.log("=========================================== 2")
+ test.done();
+}
+
+}
\ No newline at end of file
diff --git a/test/win32/security_buffer_descriptor_tests.js b/test/win32/security_buffer_descriptor_tests.js
new file mode 100644
index 0000000..3531b6b
--- /dev/null
+++ b/test/win32/security_buffer_descriptor_tests.js
@@ -0,0 +1,41 @@
+exports.setUp = function(callback) {
+ callback();
+}
+
+exports.tearDown = function(callback) {
+ callback();
+}
+
+exports['Initialize a security Buffer Descriptor'] = function(test) {
+ var SecurityBufferDescriptor = require('../../lib/sspi.js').SecurityBufferDescriptor
+ SecurityBuffer = require('../../lib/sspi.js').SecurityBuffer;
+
+ // Create descriptor with single Buffer
+ var securityDescriptor = new SecurityBufferDescriptor(100);
+ try {
+ // Fail to work due to no valid Security Buffer
+ securityDescriptor = new SecurityBufferDescriptor(["hello"]);
+ test.ok(false);
+ } catch(err){}
+
+ // Should Correctly construct SecurityBuffer
+ var buffer = new SecurityBuffer(SecurityBuffer.DATA, 100);
+ securityDescriptor = new SecurityBufferDescriptor([buffer]);
+ // Should correctly return a buffer
+ var result = securityDescriptor.toBuffer();
+ test.equal(100, result.length);
+
+ // Should Correctly construct SecurityBuffer
+ var buffer = new SecurityBuffer(SecurityBuffer.DATA, new Buffer("hello world"));
+ securityDescriptor = new SecurityBufferDescriptor([buffer]);
+ var result = securityDescriptor.toBuffer();
+ test.equal("hello world", result.toString());
+
+ // Test passing in more than one Buffer
+ var buffer = new SecurityBuffer(SecurityBuffer.DATA, new Buffer("hello world"));
+ var buffer2 = new SecurityBuffer(SecurityBuffer.STREAM, new Buffer("adam and eve"));
+ securityDescriptor = new SecurityBufferDescriptor([buffer, buffer2]);
+ var result = securityDescriptor.toBuffer();
+ test.equal("hello worldadam and eve", result.toString());
+ test.done();
+}
\ No newline at end of file
diff --git a/test/win32/security_buffer_tests.js b/test/win32/security_buffer_tests.js
new file mode 100644
index 0000000..b52b959
--- /dev/null
+++ b/test/win32/security_buffer_tests.js
@@ -0,0 +1,22 @@
+exports.setUp = function(callback) {
+ callback();
+}
+
+exports.tearDown = function(callback) {
+ callback();
+}
+
+exports['Initialize a security Buffer'] = function(test) {
+ var SecurityBuffer = require('../../lib/sspi.js').SecurityBuffer;
+ // Create empty buffer
+ var securityBuffer = new SecurityBuffer(SecurityBuffer.DATA, 100);
+ var buffer = securityBuffer.toBuffer();
+ test.equal(100, buffer.length);
+
+ // Access data passed in
+ var allocated_buffer = new Buffer(256);
+ securityBuffer = new SecurityBuffer(SecurityBuffer.DATA, allocated_buffer);
+ buffer = securityBuffer.toBuffer();
+ test.deepEqual(allocated_buffer, buffer);
+ test.done();
+}
\ No newline at end of file
diff --git a/test/win32/security_credentials_tests.js b/test/win32/security_credentials_tests.js
new file mode 100644
index 0000000..7758180
--- /dev/null
+++ b/test/win32/security_credentials_tests.js
@@ -0,0 +1,55 @@
+exports.setUp = function(callback) {
+ callback();
+}
+
+exports.tearDown = function(callback) {
+ callback();
+}
+
+exports['Initialize a set of security credentials'] = function(test) {
+ var SecurityCredentials = require('../../lib/sspi.js').SecurityCredentials;
+
+ // Aquire some credentials
+ try {
+ var credentials = SecurityCredentials.aquire('Kerberos', 'dev1 at 10GEN.ME', 'a');
+ } catch(err) {
+ console.dir(err)
+ test.ok(false);
+ }
+
+
+
+ // console.dir(SecurityCredentials);
+
+ // var SecurityBufferDescriptor = require('../../lib/sspi.js').SecurityBufferDescriptor
+ // SecurityBuffer = require('../../lib/sspi.js').SecurityBuffer;
+
+ // // Create descriptor with single Buffer
+ // var securityDescriptor = new SecurityBufferDescriptor(100);
+ // try {
+ // // Fail to work due to no valid Security Buffer
+ // securityDescriptor = new SecurityBufferDescriptor(["hello"]);
+ // test.ok(false);
+ // } catch(err){}
+
+ // // Should Correctly construct SecurityBuffer
+ // var buffer = new SecurityBuffer(SecurityBuffer.DATA, 100);
+ // securityDescriptor = new SecurityBufferDescriptor([buffer]);
+ // // Should correctly return a buffer
+ // var result = securityDescriptor.toBuffer();
+ // test.equal(100, result.length);
+
+ // // Should Correctly construct SecurityBuffer
+ // var buffer = new SecurityBuffer(SecurityBuffer.DATA, new Buffer("hello world"));
+ // securityDescriptor = new SecurityBufferDescriptor([buffer]);
+ // var result = securityDescriptor.toBuffer();
+ // test.equal("hello world", result.toString());
+
+ // // Test passing in more than one Buffer
+ // var buffer = new SecurityBuffer(SecurityBuffer.DATA, new Buffer("hello world"));
+ // var buffer2 = new SecurityBuffer(SecurityBuffer.STREAM, new Buffer("adam and eve"));
+ // securityDescriptor = new SecurityBufferDescriptor([buffer, buffer2]);
+ // var result = securityDescriptor.toBuffer();
+ // test.equal("hello worldadam and eve", result.toString());
+ test.done();
+}
\ No newline at end of file
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-javascript/node-kerberos.git
More information about the Pkg-javascript-commits
mailing list