1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/extensions/auth/nsAuthSSPI.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,667 @@ 1.4 +/* vim:set ts=4 sw=4 sts=4 et cindent: */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +// 1.10 +// Negotiate Authentication Support Module 1.11 +// 1.12 +// Described by IETF Internet draft: draft-brezak-kerberos-http-00.txt 1.13 +// (formerly draft-brezak-spnego-http-04.txt) 1.14 +// 1.15 +// Also described here: 1.16 +// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnsecure/html/http-sso-1.asp 1.17 +// 1.18 + 1.19 +#include "nsAuthSSPI.h" 1.20 +#include "nsIServiceManager.h" 1.21 +#include "nsIDNSService.h" 1.22 +#include "nsIDNSRecord.h" 1.23 +#include "nsNetCID.h" 1.24 +#include "nsCOMPtr.h" 1.25 +#include "nsICryptoHash.h" 1.26 +#include "mozilla/Telemetry.h" 1.27 + 1.28 +#include <windows.h> 1.29 + 1.30 +#define SEC_SUCCESS(Status) ((Status) >= 0) 1.31 + 1.32 +#ifndef KERB_WRAP_NO_ENCRYPT 1.33 +#define KERB_WRAP_NO_ENCRYPT 0x80000001 1.34 +#endif 1.35 + 1.36 +#ifndef SECBUFFER_PADDING 1.37 +#define SECBUFFER_PADDING 9 1.38 +#endif 1.39 + 1.40 +#ifndef SECBUFFER_STREAM 1.41 +#define SECBUFFER_STREAM 10 1.42 +#endif 1.43 + 1.44 +//----------------------------------------------------------------------------- 1.45 + 1.46 +static const wchar_t *const pTypeName [] = { 1.47 + L"Kerberos", 1.48 + L"Negotiate", 1.49 + L"NTLM" 1.50 +}; 1.51 + 1.52 +#ifdef DEBUG 1.53 +#define CASE_(_x) case _x: return # _x; 1.54 +static const char *MapErrorCode(int rc) 1.55 +{ 1.56 + switch (rc) { 1.57 + CASE_(SEC_E_OK) 1.58 + CASE_(SEC_I_CONTINUE_NEEDED) 1.59 + CASE_(SEC_I_COMPLETE_NEEDED) 1.60 + CASE_(SEC_I_COMPLETE_AND_CONTINUE) 1.61 + CASE_(SEC_E_INCOMPLETE_MESSAGE) 1.62 + CASE_(SEC_I_INCOMPLETE_CREDENTIALS) 1.63 + CASE_(SEC_E_INVALID_HANDLE) 1.64 + CASE_(SEC_E_TARGET_UNKNOWN) 1.65 + CASE_(SEC_E_LOGON_DENIED) 1.66 + CASE_(SEC_E_INTERNAL_ERROR) 1.67 + CASE_(SEC_E_NO_CREDENTIALS) 1.68 + CASE_(SEC_E_NO_AUTHENTICATING_AUTHORITY) 1.69 + CASE_(SEC_E_INSUFFICIENT_MEMORY) 1.70 + CASE_(SEC_E_INVALID_TOKEN) 1.71 + } 1.72 + return "<unknown>"; 1.73 +} 1.74 +#else 1.75 +#define MapErrorCode(_rc) "" 1.76 +#endif 1.77 + 1.78 +//----------------------------------------------------------------------------- 1.79 + 1.80 +static PSecurityFunctionTableW sspi; 1.81 + 1.82 +static nsresult 1.83 +InitSSPI() 1.84 +{ 1.85 + LOG((" InitSSPI\n")); 1.86 + 1.87 + sspi = InitSecurityInterfaceW(); 1.88 + if (!sspi) { 1.89 + LOG(("InitSecurityInterfaceW failed")); 1.90 + return NS_ERROR_UNEXPECTED; 1.91 + } 1.92 + 1.93 + return NS_OK; 1.94 +} 1.95 + 1.96 +//----------------------------------------------------------------------------- 1.97 + 1.98 +static nsresult 1.99 +MakeSN(const char *principal, nsCString &result) 1.100 +{ 1.101 + nsresult rv; 1.102 + 1.103 + nsAutoCString buf(principal); 1.104 + 1.105 + // The service name looks like "protocol@hostname", we need to map 1.106 + // this to a value that SSPI expects. To be consistent with IE, we 1.107 + // need to map '@' to '/' and canonicalize the hostname. 1.108 + int32_t index = buf.FindChar('@'); 1.109 + if (index == kNotFound) 1.110 + return NS_ERROR_UNEXPECTED; 1.111 + 1.112 + nsCOMPtr<nsIDNSService> dns = do_GetService(NS_DNSSERVICE_CONTRACTID, &rv); 1.113 + if (NS_FAILED(rv)) 1.114 + return rv; 1.115 + 1.116 + // This could be expensive if our DNS cache cannot satisfy the request. 1.117 + // However, we should have at least hit the OS resolver once prior to 1.118 + // reaching this code, so provided the OS resolver has this information 1.119 + // cached, we should not have to worry about blocking on this function call 1.120 + // for very long. NOTE: because we ask for the canonical hostname, we 1.121 + // might end up requiring extra network activity in cases where the OS 1.122 + // resolver might not have enough information to satisfy the request from 1.123 + // its cache. This is not an issue in versions of Windows up to WinXP. 1.124 + nsCOMPtr<nsIDNSRecord> record; 1.125 + rv = dns->Resolve(Substring(buf, index + 1), 1.126 + nsIDNSService::RESOLVE_CANONICAL_NAME, 1.127 + getter_AddRefs(record)); 1.128 + if (NS_FAILED(rv)) 1.129 + return rv; 1.130 + 1.131 + nsAutoCString cname; 1.132 + rv = record->GetCanonicalName(cname); 1.133 + if (NS_SUCCEEDED(rv)) { 1.134 + result = StringHead(buf, index) + NS_LITERAL_CSTRING("/") + cname; 1.135 + LOG(("Using SPN of [%s]\n", result.get())); 1.136 + } 1.137 + return rv; 1.138 +} 1.139 + 1.140 +//----------------------------------------------------------------------------- 1.141 + 1.142 +nsAuthSSPI::nsAuthSSPI(pType package) 1.143 + : mServiceFlags(REQ_DEFAULT) 1.144 + , mMaxTokenLen(0) 1.145 + , mPackage(package) 1.146 + , mCertDERData(nullptr) 1.147 + , mCertDERLength(0) 1.148 +{ 1.149 + memset(&mCred, 0, sizeof(mCred)); 1.150 + memset(&mCtxt, 0, sizeof(mCtxt)); 1.151 +} 1.152 + 1.153 +nsAuthSSPI::~nsAuthSSPI() 1.154 +{ 1.155 + Reset(); 1.156 + 1.157 + if (mCred.dwLower || mCred.dwUpper) { 1.158 +#ifdef __MINGW32__ 1.159 + (sspi->FreeCredentialsHandle)(&mCred); 1.160 +#else 1.161 + (sspi->FreeCredentialHandle)(&mCred); 1.162 +#endif 1.163 + memset(&mCred, 0, sizeof(mCred)); 1.164 + } 1.165 +} 1.166 + 1.167 +void 1.168 +nsAuthSSPI::Reset() 1.169 +{ 1.170 + mIsFirst = true; 1.171 + 1.172 + if (mCertDERData){ 1.173 + nsMemory::Free(mCertDERData); 1.174 + mCertDERData = nullptr; 1.175 + mCertDERLength = 0; 1.176 + } 1.177 + 1.178 + if (mCtxt.dwLower || mCtxt.dwUpper) { 1.179 + (sspi->DeleteSecurityContext)(&mCtxt); 1.180 + memset(&mCtxt, 0, sizeof(mCtxt)); 1.181 + } 1.182 +} 1.183 + 1.184 +NS_IMPL_ISUPPORTS(nsAuthSSPI, nsIAuthModule) 1.185 + 1.186 +NS_IMETHODIMP 1.187 +nsAuthSSPI::Init(const char *serviceName, 1.188 + uint32_t serviceFlags, 1.189 + const char16_t *domain, 1.190 + const char16_t *username, 1.191 + const char16_t *password) 1.192 +{ 1.193 + LOG((" nsAuthSSPI::Init\n")); 1.194 + 1.195 + mIsFirst = true; 1.196 + mCertDERLength = 0; 1.197 + mCertDERData = nullptr; 1.198 + 1.199 + // The caller must supply a service name to be used. (For why we now require 1.200 + // a service name for NTLM, see bug 487872.) 1.201 + NS_ENSURE_TRUE(serviceName && *serviceName, NS_ERROR_INVALID_ARG); 1.202 + 1.203 + nsresult rv; 1.204 + 1.205 + // XXX lazy initialization like this assumes that we are single threaded 1.206 + if (!sspi) { 1.207 + rv = InitSSPI(); 1.208 + if (NS_FAILED(rv)) 1.209 + return rv; 1.210 + } 1.211 + SEC_WCHAR *package; 1.212 + 1.213 + package = (SEC_WCHAR *) pTypeName[(int)mPackage]; 1.214 + 1.215 + if (mPackage == PACKAGE_TYPE_NTLM) { 1.216 + // (bug 535193) For NTLM, just use the uri host, do not do canonical host lookups. 1.217 + // The incoming serviceName is in the format: "protocol@hostname", SSPI expects 1.218 + // "<service class>/<hostname>", so swap the '@' for a '/'. 1.219 + mServiceName.Assign(serviceName); 1.220 + int32_t index = mServiceName.FindChar('@'); 1.221 + if (index == kNotFound) 1.222 + return NS_ERROR_UNEXPECTED; 1.223 + mServiceName.Replace(index, 1, '/'); 1.224 + } 1.225 + else { 1.226 + // Kerberos requires the canonical host, MakeSN takes care of this through a 1.227 + // DNS lookup. 1.228 + rv = MakeSN(serviceName, mServiceName); 1.229 + if (NS_FAILED(rv)) 1.230 + return rv; 1.231 + } 1.232 + 1.233 + mServiceFlags = serviceFlags; 1.234 + 1.235 + SECURITY_STATUS rc; 1.236 + 1.237 + PSecPkgInfoW pinfo; 1.238 + rc = (sspi->QuerySecurityPackageInfoW)(package, &pinfo); 1.239 + if (rc != SEC_E_OK) { 1.240 + LOG(("%s package not found\n", package)); 1.241 + return NS_ERROR_UNEXPECTED; 1.242 + } 1.243 + mMaxTokenLen = pinfo->cbMaxToken; 1.244 + (sspi->FreeContextBuffer)(pinfo); 1.245 + 1.246 + MS_TimeStamp useBefore; 1.247 + 1.248 + SEC_WINNT_AUTH_IDENTITY_W ai; 1.249 + SEC_WINNT_AUTH_IDENTITY_W *pai = nullptr; 1.250 + 1.251 + // domain, username, and password will be null if nsHttpNTLMAuth's ChallengeReceived 1.252 + // returns false for identityInvalid. Use default credentials in this case by passing 1.253 + // null for pai. 1.254 + if (username && password) { 1.255 + // Keep a copy of these strings for the duration 1.256 + mUsername.Assign(username); 1.257 + mPassword.Assign(password); 1.258 + mDomain.Assign(domain); 1.259 + ai.Domain = reinterpret_cast<unsigned short*>(mDomain.BeginWriting()); 1.260 + ai.DomainLength = mDomain.Length(); 1.261 + ai.User = reinterpret_cast<unsigned short*>(mUsername.BeginWriting()); 1.262 + ai.UserLength = mUsername.Length(); 1.263 + ai.Password = reinterpret_cast<unsigned short*>(mPassword.BeginWriting()); 1.264 + ai.PasswordLength = mPassword.Length(); 1.265 + ai.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE; 1.266 + pai = &ai; 1.267 + } 1.268 + 1.269 + rc = (sspi->AcquireCredentialsHandleW)(nullptr, 1.270 + package, 1.271 + SECPKG_CRED_OUTBOUND, 1.272 + nullptr, 1.273 + pai, 1.274 + nullptr, 1.275 + nullptr, 1.276 + &mCred, 1.277 + &useBefore); 1.278 + if (rc != SEC_E_OK) 1.279 + return NS_ERROR_UNEXPECTED; 1.280 + 1.281 + static bool sTelemetrySent = false; 1.282 + if (!sTelemetrySent) { 1.283 + mozilla::Telemetry::Accumulate( 1.284 + mozilla::Telemetry::NTLM_MODULE_USED_2, 1.285 + serviceFlags & nsIAuthModule::REQ_PROXY_AUTH 1.286 + ? NTLM_MODULE_WIN_API_PROXY 1.287 + : NTLM_MODULE_WIN_API_DIRECT); 1.288 + sTelemetrySent = true; 1.289 + } 1.290 + 1.291 + LOG(("AcquireCredentialsHandle() succeeded.\n")); 1.292 + return NS_OK; 1.293 +} 1.294 + 1.295 +// The arguments inToken and inTokenLen are used to pass in the server 1.296 +// certificate (when available) in the first call of the function. The 1.297 +// second time these arguments hold an input token. 1.298 +NS_IMETHODIMP 1.299 +nsAuthSSPI::GetNextToken(const void *inToken, 1.300 + uint32_t inTokenLen, 1.301 + void **outToken, 1.302 + uint32_t *outTokenLen) 1.303 +{ 1.304 + // String for end-point bindings. 1.305 + const char end_point[] = "tls-server-end-point:"; 1.306 + const int end_point_length = sizeof(end_point) - 1; 1.307 + const int hash_size = 32; // Size of a SHA256 hash. 1.308 + const int cbt_size = hash_size + end_point_length; 1.309 + 1.310 + SECURITY_STATUS rc; 1.311 + MS_TimeStamp ignored; 1.312 + 1.313 + DWORD ctxAttr, ctxReq = 0; 1.314 + CtxtHandle *ctxIn; 1.315 + SecBufferDesc ibd, obd; 1.316 + // Optional second input buffer for the CBT (Channel Binding Token) 1.317 + SecBuffer ib[2], ob; 1.318 + // Pointer to the block of memory that stores the CBT 1.319 + char* sspi_cbt = nullptr; 1.320 + SEC_CHANNEL_BINDINGS pendpoint_binding; 1.321 + 1.322 + LOG(("entering nsAuthSSPI::GetNextToken()\n")); 1.323 + 1.324 + if (!mCred.dwLower && !mCred.dwUpper) { 1.325 + LOG(("nsAuthSSPI::GetNextToken(), not initialized. exiting.")); 1.326 + return NS_ERROR_NOT_INITIALIZED; 1.327 + } 1.328 + 1.329 + if (mServiceFlags & REQ_DELEGATE) 1.330 + ctxReq |= ISC_REQ_DELEGATE; 1.331 + if (mServiceFlags & REQ_MUTUAL_AUTH) 1.332 + ctxReq |= ISC_REQ_MUTUAL_AUTH; 1.333 + 1.334 + if (inToken) { 1.335 + if (mIsFirst) { 1.336 + // First time if it comes with a token, 1.337 + // the token represents the server certificate. 1.338 + mIsFirst = false; 1.339 + mCertDERLength = inTokenLen; 1.340 + mCertDERData = nsMemory::Alloc(inTokenLen); 1.341 + if (!mCertDERData) 1.342 + return NS_ERROR_OUT_OF_MEMORY; 1.343 + memcpy(mCertDERData, inToken, inTokenLen); 1.344 + 1.345 + // We are starting a new authentication sequence. 1.346 + // If we have already initialized our 1.347 + // security context, then we're in trouble because it means that the 1.348 + // first sequence failed. We need to bail or else we might end up in 1.349 + // an infinite loop. 1.350 + if (mCtxt.dwLower || mCtxt.dwUpper) { 1.351 + LOG(("Cannot restart authentication sequence!")); 1.352 + return NS_ERROR_UNEXPECTED; 1.353 + } 1.354 + ctxIn = nullptr; 1.355 + // The certificate needs to be erased before being passed 1.356 + // to InitializeSecurityContextW(). 1.357 + inToken = nullptr; 1.358 + inTokenLen = 0; 1.359 + } else { 1.360 + ibd.ulVersion = SECBUFFER_VERSION; 1.361 + ibd.cBuffers = 0; 1.362 + ibd.pBuffers = ib; 1.363 + 1.364 + // If we have stored a certificate, the Channel Binding Token 1.365 + // needs to be generated and sent in the first input buffer. 1.366 + if (mCertDERLength > 0) { 1.367 + // First we create a proper Endpoint Binding structure. 1.368 + pendpoint_binding.dwInitiatorAddrType = 0; 1.369 + pendpoint_binding.cbInitiatorLength = 0; 1.370 + pendpoint_binding.dwInitiatorOffset = 0; 1.371 + pendpoint_binding.dwAcceptorAddrType = 0; 1.372 + pendpoint_binding.cbAcceptorLength = 0; 1.373 + pendpoint_binding.dwAcceptorOffset = 0; 1.374 + pendpoint_binding.cbApplicationDataLength = cbt_size; 1.375 + pendpoint_binding.dwApplicationDataOffset = 1.376 + sizeof(SEC_CHANNEL_BINDINGS); 1.377 + 1.378 + // Then add it to the array of sec buffers accordingly. 1.379 + ib[ibd.cBuffers].BufferType = SECBUFFER_CHANNEL_BINDINGS; 1.380 + ib[ibd.cBuffers].cbBuffer = 1.381 + pendpoint_binding.cbApplicationDataLength 1.382 + + pendpoint_binding.dwApplicationDataOffset; 1.383 + 1.384 + sspi_cbt = (char *) nsMemory::Alloc(ib[ibd.cBuffers].cbBuffer); 1.385 + if (!sspi_cbt){ 1.386 + return NS_ERROR_OUT_OF_MEMORY; 1.387 + } 1.388 + 1.389 + // Helper to write in the memory block that stores the CBT 1.390 + char* sspi_cbt_ptr = sspi_cbt; 1.391 + 1.392 + ib[ibd.cBuffers].pvBuffer = sspi_cbt; 1.393 + ibd.cBuffers++; 1.394 + 1.395 + memcpy(sspi_cbt_ptr, &pendpoint_binding, 1.396 + pendpoint_binding.dwApplicationDataOffset); 1.397 + sspi_cbt_ptr += pendpoint_binding.dwApplicationDataOffset; 1.398 + 1.399 + memcpy(sspi_cbt_ptr, end_point, end_point_length); 1.400 + sspi_cbt_ptr += end_point_length; 1.401 + 1.402 + // Start hashing. We are always doing SHA256, but depending 1.403 + // on the certificate, a different alogirthm might be needed. 1.404 + nsAutoCString hashString; 1.405 + 1.406 + nsresult rv; 1.407 + nsCOMPtr<nsICryptoHash> crypto; 1.408 + crypto = do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID, &rv); 1.409 + if (NS_SUCCEEDED(rv)) 1.410 + rv = crypto->Init(nsICryptoHash::SHA256); 1.411 + if (NS_SUCCEEDED(rv)) 1.412 + rv = crypto->Update((unsigned char*)mCertDERData, mCertDERLength); 1.413 + if (NS_SUCCEEDED(rv)) 1.414 + rv = crypto->Finish(false, hashString); 1.415 + if (NS_FAILED(rv)) { 1.416 + nsMemory::Free(mCertDERData); 1.417 + mCertDERData = nullptr; 1.418 + mCertDERLength = 0; 1.419 + nsMemory::Free(sspi_cbt); 1.420 + return rv; 1.421 + } 1.422 + 1.423 + // Once the hash has been computed, we store it in memory right 1.424 + // after the Endpoint structure and the "tls-server-end-point:" 1.425 + // char array. 1.426 + memcpy(sspi_cbt_ptr, hashString.get(), hash_size); 1.427 + 1.428 + // Free memory used to store the server certificate 1.429 + nsMemory::Free(mCertDERData); 1.430 + mCertDERData = nullptr; 1.431 + mCertDERLength = 0; 1.432 + } // End of CBT computation. 1.433 + 1.434 + // We always need this SECBUFFER. 1.435 + ib[ibd.cBuffers].BufferType = SECBUFFER_TOKEN; 1.436 + ib[ibd.cBuffers].cbBuffer = inTokenLen; 1.437 + ib[ibd.cBuffers].pvBuffer = (void *) inToken; 1.438 + ibd.cBuffers++; 1.439 + ctxIn = &mCtxt; 1.440 + } 1.441 + } else { // First time and without a token (no server certificate) 1.442 + // We are starting a new authentication sequence. If we have already 1.443 + // initialized our security context, then we're in trouble because it 1.444 + // means that the first sequence failed. We need to bail or else we 1.445 + // might end up in an infinite loop. 1.446 + if (mCtxt.dwLower || mCtxt.dwUpper || mCertDERData || mCertDERLength) { 1.447 + LOG(("Cannot restart authentication sequence!")); 1.448 + return NS_ERROR_UNEXPECTED; 1.449 + } 1.450 + ctxIn = nullptr; 1.451 + mIsFirst = false; 1.452 + } 1.453 + 1.454 + obd.ulVersion = SECBUFFER_VERSION; 1.455 + obd.cBuffers = 1; 1.456 + obd.pBuffers = &ob; 1.457 + ob.BufferType = SECBUFFER_TOKEN; 1.458 + ob.cbBuffer = mMaxTokenLen; 1.459 + ob.pvBuffer = nsMemory::Alloc(ob.cbBuffer); 1.460 + if (!ob.pvBuffer){ 1.461 + if (sspi_cbt) 1.462 + nsMemory::Free(sspi_cbt); 1.463 + return NS_ERROR_OUT_OF_MEMORY; 1.464 + } 1.465 + memset(ob.pvBuffer, 0, ob.cbBuffer); 1.466 + 1.467 + NS_ConvertUTF8toUTF16 wSN(mServiceName); 1.468 + SEC_WCHAR *sn = (SEC_WCHAR *) wSN.get(); 1.469 + 1.470 + rc = (sspi->InitializeSecurityContextW)(&mCred, 1.471 + ctxIn, 1.472 + sn, 1.473 + ctxReq, 1.474 + 0, 1.475 + SECURITY_NATIVE_DREP, 1.476 + inToken ? &ibd : nullptr, 1.477 + 0, 1.478 + &mCtxt, 1.479 + &obd, 1.480 + &ctxAttr, 1.481 + &ignored); 1.482 + if (rc == SEC_I_CONTINUE_NEEDED || rc == SEC_E_OK) { 1.483 + 1.484 +#ifdef PR_LOGGING 1.485 + if (rc == SEC_E_OK) 1.486 + LOG(("InitializeSecurityContext: succeeded.\n")); 1.487 + else 1.488 + LOG(("InitializeSecurityContext: continue.\n")); 1.489 +#endif 1.490 + if (sspi_cbt) 1.491 + nsMemory::Free(sspi_cbt); 1.492 + 1.493 + if (!ob.cbBuffer) { 1.494 + nsMemory::Free(ob.pvBuffer); 1.495 + ob.pvBuffer = nullptr; 1.496 + } 1.497 + *outToken = ob.pvBuffer; 1.498 + *outTokenLen = ob.cbBuffer; 1.499 + 1.500 + if (rc == SEC_E_OK) 1.501 + return NS_SUCCESS_AUTH_FINISHED; 1.502 + 1.503 + return NS_OK; 1.504 + } 1.505 + 1.506 + LOG(("InitializeSecurityContext failed [rc=%d:%s]\n", rc, MapErrorCode(rc))); 1.507 + Reset(); 1.508 + nsMemory::Free(ob.pvBuffer); 1.509 + return NS_ERROR_FAILURE; 1.510 +} 1.511 + 1.512 +NS_IMETHODIMP 1.513 +nsAuthSSPI::Unwrap(const void *inToken, 1.514 + uint32_t inTokenLen, 1.515 + void **outToken, 1.516 + uint32_t *outTokenLen) 1.517 +{ 1.518 + SECURITY_STATUS rc; 1.519 + SecBufferDesc ibd; 1.520 + SecBuffer ib[2]; 1.521 + 1.522 + ibd.cBuffers = 2; 1.523 + ibd.pBuffers = ib; 1.524 + ibd.ulVersion = SECBUFFER_VERSION; 1.525 + 1.526 + // SSPI Buf 1.527 + ib[0].BufferType = SECBUFFER_STREAM; 1.528 + ib[0].cbBuffer = inTokenLen; 1.529 + ib[0].pvBuffer = nsMemory::Alloc(ib[0].cbBuffer); 1.530 + if (!ib[0].pvBuffer) 1.531 + return NS_ERROR_OUT_OF_MEMORY; 1.532 + 1.533 + memcpy(ib[0].pvBuffer, inToken, inTokenLen); 1.534 + 1.535 + // app data 1.536 + ib[1].BufferType = SECBUFFER_DATA; 1.537 + ib[1].cbBuffer = 0; 1.538 + ib[1].pvBuffer = nullptr; 1.539 + 1.540 + rc = (sspi->DecryptMessage)( 1.541 + &mCtxt, 1.542 + &ibd, 1.543 + 0, // no sequence numbers 1.544 + nullptr 1.545 + ); 1.546 + 1.547 + if (SEC_SUCCESS(rc)) { 1.548 + // check if ib[1].pvBuffer is really just ib[0].pvBuffer, in which 1.549 + // case we can let the caller free it. Otherwise, we need to 1.550 + // clone it, and free the original 1.551 + if (ib[0].pvBuffer == ib[1].pvBuffer) { 1.552 + *outToken = ib[1].pvBuffer; 1.553 + } 1.554 + else { 1.555 + *outToken = nsMemory::Clone(ib[1].pvBuffer, ib[1].cbBuffer); 1.556 + nsMemory::Free(ib[0].pvBuffer); 1.557 + if (!*outToken) 1.558 + return NS_ERROR_OUT_OF_MEMORY; 1.559 + } 1.560 + *outTokenLen = ib[1].cbBuffer; 1.561 + } 1.562 + else 1.563 + nsMemory::Free(ib[0].pvBuffer); 1.564 + 1.565 + if (!SEC_SUCCESS(rc)) 1.566 + return NS_ERROR_FAILURE; 1.567 + 1.568 + return NS_OK; 1.569 +} 1.570 + 1.571 +// utility class used to free memory on exit 1.572 +class secBuffers 1.573 +{ 1.574 +public: 1.575 + 1.576 + SecBuffer ib[3]; 1.577 + 1.578 + secBuffers() { memset(&ib, 0, sizeof(ib)); } 1.579 + 1.580 + ~secBuffers() 1.581 + { 1.582 + if (ib[0].pvBuffer) 1.583 + nsMemory::Free(ib[0].pvBuffer); 1.584 + 1.585 + if (ib[1].pvBuffer) 1.586 + nsMemory::Free(ib[1].pvBuffer); 1.587 + 1.588 + if (ib[2].pvBuffer) 1.589 + nsMemory::Free(ib[2].pvBuffer); 1.590 + } 1.591 +}; 1.592 + 1.593 +NS_IMETHODIMP 1.594 +nsAuthSSPI::Wrap(const void *inToken, 1.595 + uint32_t inTokenLen, 1.596 + bool confidential, 1.597 + void **outToken, 1.598 + uint32_t *outTokenLen) 1.599 +{ 1.600 + SECURITY_STATUS rc; 1.601 + 1.602 + SecBufferDesc ibd; 1.603 + secBuffers bufs; 1.604 + SecPkgContext_Sizes sizes; 1.605 + 1.606 + rc = (sspi->QueryContextAttributesW)( 1.607 + &mCtxt, 1.608 + SECPKG_ATTR_SIZES, 1.609 + &sizes); 1.610 + 1.611 + if (!SEC_SUCCESS(rc)) 1.612 + return NS_ERROR_FAILURE; 1.613 + 1.614 + ibd.cBuffers = 3; 1.615 + ibd.pBuffers = bufs.ib; 1.616 + ibd.ulVersion = SECBUFFER_VERSION; 1.617 + 1.618 + // SSPI 1.619 + bufs.ib[0].cbBuffer = sizes.cbSecurityTrailer; 1.620 + bufs.ib[0].BufferType = SECBUFFER_TOKEN; 1.621 + bufs.ib[0].pvBuffer = nsMemory::Alloc(sizes.cbSecurityTrailer); 1.622 + 1.623 + if (!bufs.ib[0].pvBuffer) 1.624 + return NS_ERROR_OUT_OF_MEMORY; 1.625 + 1.626 + // APP Data 1.627 + bufs.ib[1].BufferType = SECBUFFER_DATA; 1.628 + bufs.ib[1].pvBuffer = nsMemory::Alloc(inTokenLen); 1.629 + bufs.ib[1].cbBuffer = inTokenLen; 1.630 + 1.631 + if (!bufs.ib[1].pvBuffer) 1.632 + return NS_ERROR_OUT_OF_MEMORY; 1.633 + 1.634 + memcpy(bufs.ib[1].pvBuffer, inToken, inTokenLen); 1.635 + 1.636 + // SSPI 1.637 + bufs.ib[2].BufferType = SECBUFFER_PADDING; 1.638 + bufs.ib[2].cbBuffer = sizes.cbBlockSize; 1.639 + bufs.ib[2].pvBuffer = nsMemory::Alloc(bufs.ib[2].cbBuffer); 1.640 + 1.641 + if (!bufs.ib[2].pvBuffer) 1.642 + return NS_ERROR_OUT_OF_MEMORY; 1.643 + 1.644 + rc = (sspi->EncryptMessage)(&mCtxt, 1.645 + confidential ? 0 : KERB_WRAP_NO_ENCRYPT, 1.646 + &ibd, 0); 1.647 + 1.648 + if (SEC_SUCCESS(rc)) { 1.649 + int len = bufs.ib[0].cbBuffer + bufs.ib[1].cbBuffer + bufs.ib[2].cbBuffer; 1.650 + char *p = (char *) nsMemory::Alloc(len); 1.651 + 1.652 + if (!p) 1.653 + return NS_ERROR_OUT_OF_MEMORY; 1.654 + 1.655 + *outToken = (void *) p; 1.656 + *outTokenLen = len; 1.657 + 1.658 + memcpy(p, bufs.ib[0].pvBuffer, bufs.ib[0].cbBuffer); 1.659 + p += bufs.ib[0].cbBuffer; 1.660 + 1.661 + memcpy(p,bufs.ib[1].pvBuffer, bufs.ib[1].cbBuffer); 1.662 + p += bufs.ib[1].cbBuffer; 1.663 + 1.664 + memcpy(p,bufs.ib[2].pvBuffer, bufs.ib[2].cbBuffer); 1.665 + 1.666 + return NS_OK; 1.667 + } 1.668 + 1.669 + return NS_ERROR_FAILURE; 1.670 +}