tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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 }