nsAuthSSPI.cpp (20165B)
1 /* vim:set ts=4 sw=2 sts=2 et cindent: */ 2 /* This Source Code Form is subject to the terms of the Mozilla Public 3 * License, v. 2.0. If a copy of the MPL was not distributed with this 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 5 6 // 7 // Negotiate Authentication Support Module 8 // 9 // Described by IETF Internet draft: draft-brezak-kerberos-http-00.txt 10 // (formerly draft-brezak-spnego-http-04.txt) 11 // 12 // Also described here: 13 // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnsecure/html/http-sso-1.asp 14 // 15 16 #include "nsAuthSSPI.h" 17 #include "nsComponentManagerUtils.h" 18 #include "nsDNSService2.h" 19 #include "nsIDNSService.h" 20 #include "nsIDNSRecord.h" 21 #include "nsNetCID.h" 22 #include "nsServiceManagerUtils.h" 23 #include "nsCOMPtr.h" 24 #include "nsICryptoHash.h" 25 #include "mozilla/glean/SecurityManagerSslMetrics.h" 26 #include "mozilla/StaticPrefs_network.h" 27 28 #include <windows.h> 29 30 // for safer certificate parsing 31 #include "nss/mozpkix/pkixutil.h" 32 #include "nss/mozpkix/pkixder.h" 33 34 #define SEC_SUCCESS(Status) ((Status) >= 0) 35 36 #ifndef KERB_WRAP_NO_ENCRYPT 37 # define KERB_WRAP_NO_ENCRYPT 0x80000001 38 #endif 39 40 #ifndef SECBUFFER_PADDING 41 # define SECBUFFER_PADDING 9 42 #endif 43 44 #ifndef SECBUFFER_STREAM 45 # define SECBUFFER_STREAM 10 46 #endif 47 48 //----------------------------------------------------------------------------- 49 50 static const wchar_t* const pTypeName[] = {L"Kerberos", L"Negotiate", L"NTLM"}; 51 52 #ifdef DEBUG 53 # define CASE_(_x) \ 54 case _x: \ 55 return #_x; 56 static const char* MapErrorCode(int rc) { 57 switch (rc) { 58 CASE_(SEC_E_OK) 59 CASE_(SEC_I_CONTINUE_NEEDED) 60 CASE_(SEC_I_COMPLETE_NEEDED) 61 CASE_(SEC_I_COMPLETE_AND_CONTINUE) 62 CASE_(SEC_E_INCOMPLETE_MESSAGE) 63 CASE_(SEC_I_INCOMPLETE_CREDENTIALS) 64 CASE_(SEC_E_INVALID_HANDLE) 65 CASE_(SEC_E_TARGET_UNKNOWN) 66 CASE_(SEC_E_LOGON_DENIED) 67 CASE_(SEC_E_INTERNAL_ERROR) 68 CASE_(SEC_E_NO_CREDENTIALS) 69 CASE_(SEC_E_NO_AUTHENTICATING_AUTHORITY) 70 CASE_(SEC_E_INSUFFICIENT_MEMORY) 71 CASE_(SEC_E_INVALID_TOKEN) 72 } 73 return "<unknown>"; 74 } 75 #else 76 # define MapErrorCode(_rc) "" 77 #endif 78 79 //----------------------------------------------------------------------------- 80 81 static PSecurityFunctionTableW sspi; 82 83 static nsresult InitSSPI() { 84 LOG((" InitSSPI\n")); 85 86 sspi = InitSecurityInterfaceW(); 87 if (!sspi) { 88 LOG(("InitSecurityInterfaceW failed")); 89 return NS_ERROR_UNEXPECTED; 90 } 91 92 return NS_OK; 93 } 94 95 //----------------------------------------------------------------------------- 96 97 nsresult nsAuthSSPI::MakeSN(const nsACString& principal, nsCString& result) { 98 nsresult rv; 99 100 nsAutoCString buf(principal); 101 102 // The service name looks like "protocol@hostname", we need to map 103 // this to a value that SSPI expects. To be consistent with IE, we 104 // need to map '@' to '/' and canonicalize the hostname. 105 int32_t index = buf.FindChar('@'); 106 if (index == kNotFound) return NS_ERROR_UNEXPECTED; 107 108 nsCOMPtr<nsIDNSService> dnsService = 109 do_GetService(NS_DNSSERVICE_CONTRACTID, &rv); 110 if (NS_FAILED(rv)) return rv; 111 112 auto dns = static_cast<nsDNSService*>(dnsService.get()); 113 114 // This could be expensive if our DNS cache cannot satisfy the request. 115 // However, we should have at least hit the OS resolver once prior to 116 // reaching this code, so provided the OS resolver has this information 117 // cached, we should not have to worry about blocking on this function call 118 // for very long. NOTE: because we ask for the canonical hostname, we 119 // might end up requiring extra network activity in cases where the OS 120 // resolver might not have enough information to satisfy the request from 121 // its cache. This is not an issue in versions of Windows up to WinXP. 122 nsCOMPtr<nsIDNSRecord> record; 123 mozilla::OriginAttributes attrs; 124 rv = dns->DeprecatedSyncResolve(Substring(buf, index + 1), 125 nsIDNSService::RESOLVE_CANONICAL_NAME, attrs, 126 getter_AddRefs(record)); 127 if (NS_FAILED(rv)) return rv; 128 nsCOMPtr<nsIDNSAddrRecord> rec = do_QueryInterface(record); 129 if (!rec) { 130 return NS_ERROR_UNEXPECTED; 131 } 132 133 nsAutoCString cname; 134 rv = rec->GetCanonicalName(cname); 135 if (NS_SUCCEEDED(rv)) { 136 result = StringHead(buf, index) + "/"_ns + cname; 137 LOG(("Using SPN of [%s]\n", result.get())); 138 } 139 return rv; 140 } 141 142 //----------------------------------------------------------------------------- 143 144 nsAuthSSPI::nsAuthSSPI(pType package) 145 : mServiceFlags(REQ_DEFAULT), 146 mMaxTokenLen(0), 147 mPackage(package), 148 mCertDERData(nullptr), 149 mCertDERLength(0) { 150 memset(&mCred, 0, sizeof(mCred)); 151 memset(&mCtxt, 0, sizeof(mCtxt)); 152 } 153 154 nsAuthSSPI::~nsAuthSSPI() { 155 Reset(); 156 157 if (mCred.dwLower || mCred.dwUpper) { 158 (sspi->FreeCredentialsHandle)(&mCred); 159 memset(&mCred, 0, sizeof(mCred)); 160 } 161 } 162 163 void nsAuthSSPI::Reset() { 164 mIsFirst = true; 165 166 if (mCertDERData) { 167 free(mCertDERData); 168 mCertDERData = nullptr; 169 mCertDERLength = 0; 170 } 171 172 if (mCtxt.dwLower || mCtxt.dwUpper) { 173 (sspi->DeleteSecurityContext)(&mCtxt); 174 memset(&mCtxt, 0, sizeof(mCtxt)); 175 } 176 } 177 178 NS_IMPL_ISUPPORTS(nsAuthSSPI, nsIAuthModule) 179 180 NS_IMETHODIMP 181 nsAuthSSPI::Init(const nsACString& aServiceName, uint32_t aServiceFlags, 182 const nsAString& aDomain, const nsAString& aUsername, 183 const nsAString& aPassword) { 184 LOG((" nsAuthSSPI::Init\n")); 185 186 mIsFirst = true; 187 mCertDERLength = 0; 188 mCertDERData = nullptr; 189 190 // The caller must supply a service name to be used. (For why we now require 191 // a service name for NTLM, see bug 487872.) 192 NS_ENSURE_TRUE(!aServiceName.IsEmpty(), NS_ERROR_INVALID_ARG); 193 194 nsresult rv; 195 196 // XXX lazy initialization like this assumes that we are single threaded 197 if (!sspi) { 198 rv = InitSSPI(); 199 if (NS_FAILED(rv)) return rv; 200 } 201 SEC_WCHAR* package; 202 203 package = (SEC_WCHAR*)pTypeName[(int)mPackage]; 204 205 if (mPackage == PACKAGE_TYPE_NTLM) { 206 // (bug 535193) For NTLM, just use the uri host, do not do canonical host 207 // lookups. The incoming serviceName is in the format: "protocol@hostname", 208 // SSPI expects 209 // "<service class>/<hostname>", so swap the '@' for a '/'. 210 mServiceName = aServiceName; 211 int32_t index = mServiceName.FindChar('@'); 212 if (index == kNotFound) return NS_ERROR_UNEXPECTED; 213 mServiceName.Replace(index, 1, '/'); 214 } else { 215 // Kerberos requires the canonical host, MakeSN takes care of this through a 216 // DNS lookup. 217 rv = MakeSN(aServiceName, mServiceName); 218 if (NS_FAILED(rv)) return rv; 219 } 220 221 mServiceFlags = aServiceFlags; 222 223 SECURITY_STATUS rc; 224 225 PSecPkgInfoW pinfo; 226 rc = (sspi->QuerySecurityPackageInfoW)(package, &pinfo); 227 if (rc != SEC_E_OK) { 228 LOG(("%S package not found\n", package)); 229 return NS_ERROR_UNEXPECTED; 230 } 231 mMaxTokenLen = pinfo->cbMaxToken; 232 (sspi->FreeContextBuffer)(pinfo); 233 234 MS_TimeStamp useBefore; 235 236 SEC_WINNT_AUTH_IDENTITY_W ai; 237 SEC_WINNT_AUTH_IDENTITY_W* pai = nullptr; 238 239 // domain, username, and password will be null if nsHttpNTLMAuth's 240 // ChallengeReceived returns false for identityInvalid. Use default 241 // credentials in this case by passing null for pai. 242 if (!aUsername.IsEmpty() && !aPassword.IsEmpty()) { 243 // Keep a copy of these strings for the duration 244 mUsername = aUsername; 245 mPassword = aPassword; 246 mDomain = aDomain; 247 ai.Domain = reinterpret_cast<unsigned short*>(mDomain.BeginWriting()); 248 ai.DomainLength = mDomain.Length(); 249 ai.User = reinterpret_cast<unsigned short*>(mUsername.BeginWriting()); 250 ai.UserLength = mUsername.Length(); 251 ai.Password = reinterpret_cast<unsigned short*>(mPassword.BeginWriting()); 252 ai.PasswordLength = mPassword.Length(); 253 ai.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE; 254 pai = &ai; 255 } 256 257 rc = (sspi->AcquireCredentialsHandleW)(nullptr, package, SECPKG_CRED_OUTBOUND, 258 nullptr, pai, nullptr, nullptr, &mCred, 259 &useBefore); 260 if (rc != SEC_E_OK) return NS_ERROR_UNEXPECTED; 261 262 static bool sTelemetrySent = false; 263 if (!sTelemetrySent) { 264 mozilla::glean::security::ntlm_module_used.AccumulateSingleSample( 265 aServiceFlags & nsIAuthModule::REQ_PROXY_AUTH 266 ? NTLM_MODULE_WIN_API_PROXY 267 : NTLM_MODULE_WIN_API_DIRECT); 268 sTelemetrySent = true; 269 } 270 271 LOG(("AcquireCredentialsHandle() succeeded.\n")); 272 return NS_OK; 273 } 274 275 // The arguments inToken and inTokenLen are used to pass in the server 276 // certificate (when available) in the first call of the function. The 277 // second time these arguments hold an input token. 278 NS_IMETHODIMP 279 nsAuthSSPI::GetNextToken(const void* inToken, uint32_t inTokenLen, 280 void** outToken, uint32_t* outTokenLen) { 281 // String for end-point bindings. 282 const char end_point[] = "tls-server-end-point:"; 283 const int end_point_length = sizeof(end_point) - 1; 284 285 SECURITY_STATUS rc; 286 MS_TimeStamp ignored; 287 288 DWORD ctxAttr, ctxReq = 0; 289 CtxtHandle* ctxIn; 290 SecBufferDesc ibd, obd; 291 // Optional second input buffer for the CBT (Channel Binding Token) 292 SecBuffer ib[2], ob; 293 // Pointer to the block of memory that stores the CBT 294 char* sspi_cbt = nullptr; 295 SEC_CHANNEL_BINDINGS pendpoint_binding; 296 297 LOG(("entering nsAuthSSPI::GetNextToken()\n")); 298 299 if (!mCred.dwLower && !mCred.dwUpper) { 300 LOG(("nsAuthSSPI::GetNextToken(), not initialized. exiting.")); 301 return NS_ERROR_NOT_INITIALIZED; 302 } 303 304 if (mServiceFlags & REQ_DELEGATE) ctxReq |= ISC_REQ_DELEGATE; 305 if (mServiceFlags & REQ_MUTUAL_AUTH) ctxReq |= ISC_REQ_MUTUAL_AUTH; 306 307 if (inToken) { 308 if (mIsFirst) { 309 // First time if it comes with a token, 310 // the token represents the server certificate. 311 mIsFirst = false; 312 mCertDERLength = inTokenLen; 313 mCertDERData = moz_xmalloc(inTokenLen); 314 memcpy(mCertDERData, inToken, inTokenLen); 315 316 // We are starting a new authentication sequence. 317 // If we have already initialized our 318 // security context, then we're in trouble because it means that the 319 // first sequence failed. We need to bail or else we might end up in 320 // an infinite loop. 321 if (mCtxt.dwLower || mCtxt.dwUpper) { 322 LOG(("Cannot restart authentication sequence!")); 323 return NS_ERROR_UNEXPECTED; 324 } 325 ctxIn = nullptr; 326 // The certificate needs to be erased before being passed 327 // to InitializeSecurityContextW(). 328 inToken = nullptr; 329 inTokenLen = 0; 330 } else { 331 ibd.ulVersion = SECBUFFER_VERSION; 332 ibd.cBuffers = 0; 333 ibd.pBuffers = ib; 334 335 // If we have stored a certificate, the Channel Binding Token 336 // needs to be generated and sent in the first input buffer. 337 if (mCertDERLength > 0) { 338 // Default to SHA256 for compatibility, but detect SHA384 and SHA512 339 uint32_t hashAlgorithm = nsICryptoHash::SHA256; 340 uint32_t hashSize = 32; // SHA256 hash size 341 342 // Compute the hash size. 343 [&]() { 344 if (!mozilla::StaticPrefs::network_auth_sspi_detect_hash()) { 345 // This check only exists to make sure that the hash algorithm check 346 // doesn't break previous working behaviour. 347 return; 348 } 349 using namespace mozilla::pkix; 350 Input certDER; 351 352 mozilla::pkix::Result pkixResult = certDER.Init( 353 static_cast<const uint8_t*>(mCertDERData), mCertDERLength); 354 if (pkixResult != Success) { 355 return; 356 } 357 358 BackCert cert(certDER, EndEntityOrCA::MustBeEndEntity, nullptr); 359 pkixResult = cert.Init(); 360 if (pkixResult != Success) { 361 return; 362 } 363 364 // Parse the signature algorithm from the signed data 365 der::PublicKeyAlgorithm publicKeyAlg; 366 DigestAlgorithm digestAlg; 367 Reader signatureAlgorithmReader(cert.GetSignedData().algorithm); 368 pkixResult = der::SignatureAlgorithmIdentifierValue( 369 signatureAlgorithmReader, publicKeyAlg, digestAlg); 370 371 if (pkixResult != Success) { 372 return; 373 } 374 // Map digest algorithms to hash algorithms for Extended Protection 375 switch (digestAlg) { 376 case DigestAlgorithm::sha384: 377 hashAlgorithm = nsICryptoHash::SHA384; 378 hashSize = 48; // SHA384 hash size 379 break; 380 case DigestAlgorithm::sha512: 381 hashAlgorithm = nsICryptoHash::SHA512; 382 hashSize = 64; // SHA512 hash size 383 break; 384 case DigestAlgorithm::sha256: 385 default: 386 // Use SHA256 as default for compatibility 387 hashAlgorithm = nsICryptoHash::SHA256; 388 hashSize = 32; 389 break; 390 } 391 }(); 392 393 // Create Endpoint Binding structure with correct size 394 const int cbt_size = hashSize + end_point_length; 395 pendpoint_binding.dwInitiatorAddrType = 0; 396 pendpoint_binding.cbInitiatorLength = 0; 397 pendpoint_binding.dwInitiatorOffset = 0; 398 pendpoint_binding.dwAcceptorAddrType = 0; 399 pendpoint_binding.cbAcceptorLength = 0; 400 pendpoint_binding.dwAcceptorOffset = 0; 401 pendpoint_binding.cbApplicationDataLength = cbt_size; 402 pendpoint_binding.dwApplicationDataOffset = 403 sizeof(SEC_CHANNEL_BINDINGS); 404 405 // Then add it to the array of sec buffers accordingly. 406 ib[ibd.cBuffers].BufferType = SECBUFFER_CHANNEL_BINDINGS; 407 ib[ibd.cBuffers].cbBuffer = pendpoint_binding.cbApplicationDataLength + 408 pendpoint_binding.dwApplicationDataOffset; 409 410 sspi_cbt = (char*)moz_xmalloc(ib[ibd.cBuffers].cbBuffer); 411 412 // Helper to write in the memory block that stores the CBT 413 char* sspi_cbt_ptr = sspi_cbt; 414 415 ib[ibd.cBuffers].pvBuffer = sspi_cbt; 416 ibd.cBuffers++; 417 418 memcpy(sspi_cbt_ptr, &pendpoint_binding, 419 pendpoint_binding.dwApplicationDataOffset); 420 sspi_cbt_ptr += pendpoint_binding.dwApplicationDataOffset; 421 422 memcpy(sspi_cbt_ptr, end_point, end_point_length); 423 sspi_cbt_ptr += end_point_length; 424 425 nsAutoCString hashString; 426 nsresult rv = NS_ERROR_FAILURE; 427 428 nsCOMPtr<nsICryptoHash> crypto; 429 crypto = do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID, &rv); 430 if (NS_SUCCEEDED(rv)) { 431 rv = crypto->Init(hashAlgorithm); 432 } 433 if (NS_SUCCEEDED(rv)) { 434 rv = crypto->Update((unsigned char*)mCertDERData, mCertDERLength); 435 } 436 if (NS_SUCCEEDED(rv)) rv = crypto->Finish(false, hashString); 437 438 if (NS_FAILED(rv)) { 439 free(mCertDERData); 440 mCertDERData = nullptr; 441 mCertDERLength = 0; 442 free(sspi_cbt); 443 return rv; 444 } 445 446 // Store the computed hash in memory right after the Endpoint 447 // structure and the "tls-server-end-point:" char array 448 memcpy(sspi_cbt_ptr, hashString.get(), hashSize); 449 450 // Free memory used to store the server certificate 451 free(mCertDERData); 452 mCertDERData = nullptr; 453 mCertDERLength = 0; 454 } // End of CBT computation. 455 456 // We always need this SECBUFFER. 457 ib[ibd.cBuffers].BufferType = SECBUFFER_TOKEN; 458 ib[ibd.cBuffers].cbBuffer = inTokenLen; 459 ib[ibd.cBuffers].pvBuffer = (void*)inToken; 460 ibd.cBuffers++; 461 ctxIn = &mCtxt; 462 } 463 } else { // First time and without a token (no server certificate) 464 // We are starting a new authentication sequence. If we have already 465 // initialized our security context, then we're in trouble because it 466 // means that the first sequence failed. We need to bail or else we 467 // might end up in an infinite loop. 468 if (mCtxt.dwLower || mCtxt.dwUpper || mCertDERData || mCertDERLength) { 469 LOG(("Cannot restart authentication sequence!")); 470 return NS_ERROR_UNEXPECTED; 471 } 472 ctxIn = nullptr; 473 mIsFirst = false; 474 } 475 476 obd.ulVersion = SECBUFFER_VERSION; 477 obd.cBuffers = 1; 478 obd.pBuffers = &ob; 479 ob.BufferType = SECBUFFER_TOKEN; 480 ob.cbBuffer = mMaxTokenLen; 481 ob.pvBuffer = moz_xmalloc(ob.cbBuffer); 482 memset(ob.pvBuffer, 0, ob.cbBuffer); 483 484 NS_ConvertUTF8toUTF16 wSN(mServiceName); 485 SEC_WCHAR* sn = (SEC_WCHAR*)wSN.get(); 486 487 rc = (sspi->InitializeSecurityContextW)( 488 &mCred, ctxIn, sn, ctxReq, 0, SECURITY_NATIVE_DREP, 489 inToken ? &ibd : nullptr, 0, &mCtxt, &obd, &ctxAttr, &ignored); 490 if (rc == SEC_I_CONTINUE_NEEDED || rc == SEC_E_OK) { 491 if (rc == SEC_E_OK) 492 LOG(("InitializeSecurityContext: succeeded.\n")); 493 else 494 LOG(("InitializeSecurityContext: continue.\n")); 495 496 if (sspi_cbt) free(sspi_cbt); 497 498 if (!ob.cbBuffer) { 499 free(ob.pvBuffer); 500 ob.pvBuffer = nullptr; 501 } 502 *outToken = ob.pvBuffer; 503 *outTokenLen = ob.cbBuffer; 504 505 if (rc == SEC_E_OK) return NS_SUCCESS_AUTH_FINISHED; 506 507 return NS_OK; 508 } 509 510 LOG(("InitializeSecurityContext failed [rc=%ld:%s]\n", rc, MapErrorCode(rc))); 511 Reset(); 512 free(ob.pvBuffer); 513 return NS_ERROR_FAILURE; 514 } 515 516 NS_IMETHODIMP 517 nsAuthSSPI::Unwrap(const void* inToken, uint32_t inTokenLen, void** outToken, 518 uint32_t* outTokenLen) { 519 SECURITY_STATUS rc; 520 SecBufferDesc ibd; 521 SecBuffer ib[2]; 522 523 ibd.cBuffers = 2; 524 ibd.pBuffers = ib; 525 ibd.ulVersion = SECBUFFER_VERSION; 526 527 // SSPI Buf 528 ib[0].BufferType = SECBUFFER_STREAM; 529 ib[0].cbBuffer = inTokenLen; 530 ib[0].pvBuffer = moz_xmalloc(ib[0].cbBuffer); 531 532 memcpy(ib[0].pvBuffer, inToken, inTokenLen); 533 534 // app data 535 ib[1].BufferType = SECBUFFER_DATA; 536 ib[1].cbBuffer = 0; 537 ib[1].pvBuffer = nullptr; 538 539 rc = (sspi->DecryptMessage)(&mCtxt, &ibd, 540 0, // no sequence numbers 541 nullptr); 542 543 if (SEC_SUCCESS(rc)) { 544 // check if ib[1].pvBuffer is really just ib[0].pvBuffer, in which 545 // case we can let the caller free it. Otherwise, we need to 546 // clone it, and free the original 547 if (ib[0].pvBuffer == ib[1].pvBuffer) { 548 *outToken = ib[1].pvBuffer; 549 } else { 550 *outToken = moz_xmemdup(ib[1].pvBuffer, ib[1].cbBuffer); 551 free(ib[0].pvBuffer); 552 } 553 *outTokenLen = ib[1].cbBuffer; 554 } else 555 free(ib[0].pvBuffer); 556 557 if (!SEC_SUCCESS(rc)) return NS_ERROR_FAILURE; 558 559 return NS_OK; 560 } 561 562 // utility class used to free memory on exit 563 class secBuffers { 564 public: 565 SecBuffer ib[3]; 566 567 secBuffers() { memset(&ib, 0, sizeof(ib)); } 568 569 ~secBuffers() { 570 if (ib[0].pvBuffer) free(ib[0].pvBuffer); 571 572 if (ib[1].pvBuffer) free(ib[1].pvBuffer); 573 574 if (ib[2].pvBuffer) free(ib[2].pvBuffer); 575 } 576 }; 577 578 NS_IMETHODIMP 579 nsAuthSSPI::Wrap(const void* inToken, uint32_t inTokenLen, bool confidential, 580 void** outToken, uint32_t* outTokenLen) { 581 SECURITY_STATUS rc; 582 583 SecBufferDesc ibd; 584 secBuffers bufs; 585 SecPkgContext_Sizes sizes; 586 587 rc = (sspi->QueryContextAttributesW)(&mCtxt, SECPKG_ATTR_SIZES, &sizes); 588 589 if (!SEC_SUCCESS(rc)) return NS_ERROR_FAILURE; 590 591 ibd.cBuffers = 3; 592 ibd.pBuffers = bufs.ib; 593 ibd.ulVersion = SECBUFFER_VERSION; 594 595 // SSPI 596 bufs.ib[0].cbBuffer = sizes.cbSecurityTrailer; 597 bufs.ib[0].BufferType = SECBUFFER_TOKEN; 598 bufs.ib[0].pvBuffer = moz_xmalloc(sizes.cbSecurityTrailer); 599 600 // APP Data 601 bufs.ib[1].BufferType = SECBUFFER_DATA; 602 bufs.ib[1].pvBuffer = moz_xmalloc(inTokenLen); 603 bufs.ib[1].cbBuffer = inTokenLen; 604 605 memcpy(bufs.ib[1].pvBuffer, inToken, inTokenLen); 606 607 // SSPI 608 bufs.ib[2].BufferType = SECBUFFER_PADDING; 609 bufs.ib[2].cbBuffer = sizes.cbBlockSize; 610 bufs.ib[2].pvBuffer = moz_xmalloc(bufs.ib[2].cbBuffer); 611 612 rc = (sspi->EncryptMessage)(&mCtxt, confidential ? 0 : KERB_WRAP_NO_ENCRYPT, 613 &ibd, 0); 614 615 if (SEC_SUCCESS(rc)) { 616 int len = bufs.ib[0].cbBuffer + bufs.ib[1].cbBuffer + bufs.ib[2].cbBuffer; 617 char* p = (char*)moz_xmalloc(len); 618 619 *outToken = (void*)p; 620 *outTokenLen = len; 621 622 memcpy(p, bufs.ib[0].pvBuffer, bufs.ib[0].cbBuffer); 623 p += bufs.ib[0].cbBuffer; 624 625 memcpy(p, bufs.ib[1].pvBuffer, bufs.ib[1].cbBuffer); 626 p += bufs.ib[1].cbBuffer; 627 628 memcpy(p, bufs.ib[2].pvBuffer, bufs.ib[2].cbBuffer); 629 630 return NS_OK; 631 } 632 633 return NS_ERROR_FAILURE; 634 }