UNPKG

win-sso

Version:

NTLM single-sign-on for Node.js. Only Windows OS supported.

160 lines (159 loc) 6.82 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.WinSso = void 0; const debug_logger_1 = require("./utils/debug.logger"); const path_1 = __importDefault(require("path")); let winSsoAddon; try { // eslint-disable-next-line @typescript-eslint/no-require-imports winSsoAddon = require("node-gyp-build")(path_1.default.join(__dirname, "..")); (0, debug_logger_1.debug)("Loaded win-sso native module"); } catch (_a) { (0, debug_logger_1.debug)("Could not load win-sso native module"); } /** * Creates authentication tokens for NTLM or Negotiate handshake using the executing users credentials. */ class WinSso { /** * Creates an authentication context for SSO. * This allocates memory buffers, the freeAuthContext method should be called * to free them (on error or after authentication is no longer needed) * @param securityPackage The name of the security package (NTLM or Negotiate) * @param targetHost The FQDN hostname of the target (optional for NTLM, required for Kerberos) * @param peerCert The certificate of the target server * (optional, for HTTPS channel binding) * @param flags Flags to set in the authentication context * If not set, NTML defaults to no flags, while Negotiate defaults to ISC_REQ_MUTUAL_AUTH | ISC_REQ_SEQUENCE_DETECT * (optional, allows customizing security features) */ constructor(securityPackage, targetHost, peerCert, flags) { this.securityPackage = securityPackage; let applicationData; if (!targetHost) { targetHost = ""; } if (peerCert) { applicationData = this.getChannelBindingsApplicationData(peerCert); } else { applicationData = Buffer.alloc(0); } this.authContextId = winSsoAddon.createAuthContext(securityPackage, targetHost, applicationData, flags); } /** * Retrieves the username of the logged in user * @returns user name including domain */ static getLogonUserName() { return winSsoAddon.getLogonUserName(); } /** * Transforms target TLS certificate into a channel binding application data buffer * @param peerCert Target TLS certificate * @returns Application data buffer */ getChannelBindingsApplicationData(peerCert) { const hash = peerCert.fingerprint256.replace(/:/g, ""); const hashBuf = Buffer.from(hash, "hex"); const tlsServerEndPoint = "tls-server-end-point:"; const applicationDataBuffer = Buffer.alloc(tlsServerEndPoint.length + hashBuf.length); applicationDataBuffer.write(tlsServerEndPoint, 0, "ascii"); hashBuf.copy(applicationDataBuffer, tlsServerEndPoint.length); return applicationDataBuffer; } /** * Releases all allocated resources for the authorization context. * Should be called when the context is no longer required, such as when the * socket was closed. */ freeAuthContext() { winSsoAddon.freeAuthContext(this.authContextId); } /** * Creates an authentication request token * @returns Raw token buffer */ createAuthRequest() { const token = winSsoAddon.createAuthRequest(this.authContextId); (0, debug_logger_1.debug)("Created " + this.securityPackage + " authentication request token", token.toString("base64")); return token; } /** * Creates an authentication request header * @returns The www-authenticate header */ createAuthRequestHeader() { const header = this.securityPackage + " " + this.createAuthRequest().toString("base64"); return header; } /** * Creates an authentication response token * @param inTokenHeader The www-authentication header received from the target * in response to the authentication request * @returns Raw token buffer. May be empty if Negotiate handshake is complete. */ createAuthResponse(inTokenHeader) { (0, debug_logger_1.debug)("Received www-authentication response", inTokenHeader); const packageMatch = new RegExp("^" + this.securityPackage + "\\s([^,\\s]+)").exec(inTokenHeader); if (!packageMatch) { throw new Error("Invalid input token, missing " + this.securityPackage + " prefix: " + inTokenHeader); } const inToken = Buffer.from(packageMatch[1], "base64"); try { const token = winSsoAddon.createAuthResponse(this.authContextId, inToken); if (token.length > 0) { (0, debug_logger_1.debug)("Created " + this.securityPackage + " authentication response token", token.toString("base64")); } else { (0, debug_logger_1.debug)("No response token, authentication complete"); } return token; } catch (err) { if (err.message === "Could not init security context. Result: -2146893054") { // If incoming token is for NTLMv1, this error can occur when // LMCompatibilityLevel prevents the client to send NTLMv1 messages if (this.securityPackage === "NTLM" && this.isNtlmV1(inToken)) { throw new Error("Could not create NTLM type 3 message. Incoming type 2 message uses NTLMv1, " + "it is likely that the client is prevented from sending such messages. " + "Update target host to use NTLMv2 (recommended) or adjust LMCompatibilityLevel on the client (insecure)"); } } throw err; } } isNtlmV1(type2message) { if (type2message.length >= 24) { const inTokenFlags = type2message.readInt32BE(20); if ((inTokenFlags & WinSso.NEGOTIATE_NTLM2_KEY) === 0) { return true; } } return false; } /** * Creates an authentication response header * @param inTokenHeader The www-authentication header received from the target * in response to the authentication request * @returns The www-authenticate header. May be an empty string if Negotiate handshake is complete. */ createAuthResponseHeader(inTokenHeader) { const tokenBuffer = this.createAuthResponse(inTokenHeader); if (tokenBuffer.length == 0) { return ""; } const header = this.securityPackage + " " + tokenBuffer.toString("base64"); return header; } } exports.WinSso = WinSso; WinSso.NEGOTIATE_NTLM2_KEY = 1 << 19;