michael@0: /* vim:set ts=4 sw=4 sts=4 et cindent: */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: // michael@0: // Negotiate Authentication Support Module michael@0: // michael@0: // Described by IETF Internet draft: draft-brezak-kerberos-http-00.txt michael@0: // (formerly draft-brezak-spnego-http-04.txt) michael@0: // michael@0: // Also described here: michael@0: // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnsecure/html/http-sso-1.asp michael@0: // michael@0: michael@0: #include "nsAuthSSPI.h" michael@0: #include "nsIServiceManager.h" michael@0: #include "nsIDNSService.h" michael@0: #include "nsIDNSRecord.h" michael@0: #include "nsNetCID.h" michael@0: #include "nsCOMPtr.h" michael@0: #include "nsICryptoHash.h" michael@0: #include "mozilla/Telemetry.h" michael@0: michael@0: #include michael@0: michael@0: #define SEC_SUCCESS(Status) ((Status) >= 0) michael@0: michael@0: #ifndef KERB_WRAP_NO_ENCRYPT michael@0: #define KERB_WRAP_NO_ENCRYPT 0x80000001 michael@0: #endif michael@0: michael@0: #ifndef SECBUFFER_PADDING michael@0: #define SECBUFFER_PADDING 9 michael@0: #endif michael@0: michael@0: #ifndef SECBUFFER_STREAM michael@0: #define SECBUFFER_STREAM 10 michael@0: #endif michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: michael@0: static const wchar_t *const pTypeName [] = { michael@0: L"Kerberos", michael@0: L"Negotiate", michael@0: L"NTLM" michael@0: }; michael@0: michael@0: #ifdef DEBUG michael@0: #define CASE_(_x) case _x: return # _x; michael@0: static const char *MapErrorCode(int rc) michael@0: { michael@0: switch (rc) { michael@0: CASE_(SEC_E_OK) michael@0: CASE_(SEC_I_CONTINUE_NEEDED) michael@0: CASE_(SEC_I_COMPLETE_NEEDED) michael@0: CASE_(SEC_I_COMPLETE_AND_CONTINUE) michael@0: CASE_(SEC_E_INCOMPLETE_MESSAGE) michael@0: CASE_(SEC_I_INCOMPLETE_CREDENTIALS) michael@0: CASE_(SEC_E_INVALID_HANDLE) michael@0: CASE_(SEC_E_TARGET_UNKNOWN) michael@0: CASE_(SEC_E_LOGON_DENIED) michael@0: CASE_(SEC_E_INTERNAL_ERROR) michael@0: CASE_(SEC_E_NO_CREDENTIALS) michael@0: CASE_(SEC_E_NO_AUTHENTICATING_AUTHORITY) michael@0: CASE_(SEC_E_INSUFFICIENT_MEMORY) michael@0: CASE_(SEC_E_INVALID_TOKEN) michael@0: } michael@0: return ""; michael@0: } michael@0: #else michael@0: #define MapErrorCode(_rc) "" michael@0: #endif michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: michael@0: static PSecurityFunctionTableW sspi; michael@0: michael@0: static nsresult michael@0: InitSSPI() michael@0: { michael@0: LOG((" InitSSPI\n")); michael@0: michael@0: sspi = InitSecurityInterfaceW(); michael@0: if (!sspi) { michael@0: LOG(("InitSecurityInterfaceW failed")); michael@0: return NS_ERROR_UNEXPECTED; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: michael@0: static nsresult michael@0: MakeSN(const char *principal, nsCString &result) michael@0: { michael@0: nsresult rv; michael@0: michael@0: nsAutoCString buf(principal); michael@0: michael@0: // The service name looks like "protocol@hostname", we need to map michael@0: // this to a value that SSPI expects. To be consistent with IE, we michael@0: // need to map '@' to '/' and canonicalize the hostname. michael@0: int32_t index = buf.FindChar('@'); michael@0: if (index == kNotFound) michael@0: return NS_ERROR_UNEXPECTED; michael@0: michael@0: nsCOMPtr dns = do_GetService(NS_DNSSERVICE_CONTRACTID, &rv); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: // This could be expensive if our DNS cache cannot satisfy the request. michael@0: // However, we should have at least hit the OS resolver once prior to michael@0: // reaching this code, so provided the OS resolver has this information michael@0: // cached, we should not have to worry about blocking on this function call michael@0: // for very long. NOTE: because we ask for the canonical hostname, we michael@0: // might end up requiring extra network activity in cases where the OS michael@0: // resolver might not have enough information to satisfy the request from michael@0: // its cache. This is not an issue in versions of Windows up to WinXP. michael@0: nsCOMPtr record; michael@0: rv = dns->Resolve(Substring(buf, index + 1), michael@0: nsIDNSService::RESOLVE_CANONICAL_NAME, michael@0: getter_AddRefs(record)); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: nsAutoCString cname; michael@0: rv = record->GetCanonicalName(cname); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: result = StringHead(buf, index) + NS_LITERAL_CSTRING("/") + cname; michael@0: LOG(("Using SPN of [%s]\n", result.get())); michael@0: } michael@0: return rv; michael@0: } michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: michael@0: nsAuthSSPI::nsAuthSSPI(pType package) michael@0: : mServiceFlags(REQ_DEFAULT) michael@0: , mMaxTokenLen(0) michael@0: , mPackage(package) michael@0: , mCertDERData(nullptr) michael@0: , mCertDERLength(0) michael@0: { michael@0: memset(&mCred, 0, sizeof(mCred)); michael@0: memset(&mCtxt, 0, sizeof(mCtxt)); michael@0: } michael@0: michael@0: nsAuthSSPI::~nsAuthSSPI() michael@0: { michael@0: Reset(); michael@0: michael@0: if (mCred.dwLower || mCred.dwUpper) { michael@0: #ifdef __MINGW32__ michael@0: (sspi->FreeCredentialsHandle)(&mCred); michael@0: #else michael@0: (sspi->FreeCredentialHandle)(&mCred); michael@0: #endif michael@0: memset(&mCred, 0, sizeof(mCred)); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsAuthSSPI::Reset() michael@0: { michael@0: mIsFirst = true; michael@0: michael@0: if (mCertDERData){ michael@0: nsMemory::Free(mCertDERData); michael@0: mCertDERData = nullptr; michael@0: mCertDERLength = 0; michael@0: } michael@0: michael@0: if (mCtxt.dwLower || mCtxt.dwUpper) { michael@0: (sspi->DeleteSecurityContext)(&mCtxt); michael@0: memset(&mCtxt, 0, sizeof(mCtxt)); michael@0: } michael@0: } michael@0: michael@0: NS_IMPL_ISUPPORTS(nsAuthSSPI, nsIAuthModule) michael@0: michael@0: NS_IMETHODIMP michael@0: nsAuthSSPI::Init(const char *serviceName, michael@0: uint32_t serviceFlags, michael@0: const char16_t *domain, michael@0: const char16_t *username, michael@0: const char16_t *password) michael@0: { michael@0: LOG((" nsAuthSSPI::Init\n")); michael@0: michael@0: mIsFirst = true; michael@0: mCertDERLength = 0; michael@0: mCertDERData = nullptr; michael@0: michael@0: // The caller must supply a service name to be used. (For why we now require michael@0: // a service name for NTLM, see bug 487872.) michael@0: NS_ENSURE_TRUE(serviceName && *serviceName, NS_ERROR_INVALID_ARG); michael@0: michael@0: nsresult rv; michael@0: michael@0: // XXX lazy initialization like this assumes that we are single threaded michael@0: if (!sspi) { michael@0: rv = InitSSPI(); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: } michael@0: SEC_WCHAR *package; michael@0: michael@0: package = (SEC_WCHAR *) pTypeName[(int)mPackage]; michael@0: michael@0: if (mPackage == PACKAGE_TYPE_NTLM) { michael@0: // (bug 535193) For NTLM, just use the uri host, do not do canonical host lookups. michael@0: // The incoming serviceName is in the format: "protocol@hostname", SSPI expects michael@0: // "/", so swap the '@' for a '/'. michael@0: mServiceName.Assign(serviceName); michael@0: int32_t index = mServiceName.FindChar('@'); michael@0: if (index == kNotFound) michael@0: return NS_ERROR_UNEXPECTED; michael@0: mServiceName.Replace(index, 1, '/'); michael@0: } michael@0: else { michael@0: // Kerberos requires the canonical host, MakeSN takes care of this through a michael@0: // DNS lookup. michael@0: rv = MakeSN(serviceName, mServiceName); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: } michael@0: michael@0: mServiceFlags = serviceFlags; michael@0: michael@0: SECURITY_STATUS rc; michael@0: michael@0: PSecPkgInfoW pinfo; michael@0: rc = (sspi->QuerySecurityPackageInfoW)(package, &pinfo); michael@0: if (rc != SEC_E_OK) { michael@0: LOG(("%s package not found\n", package)); michael@0: return NS_ERROR_UNEXPECTED; michael@0: } michael@0: mMaxTokenLen = pinfo->cbMaxToken; michael@0: (sspi->FreeContextBuffer)(pinfo); michael@0: michael@0: MS_TimeStamp useBefore; michael@0: michael@0: SEC_WINNT_AUTH_IDENTITY_W ai; michael@0: SEC_WINNT_AUTH_IDENTITY_W *pai = nullptr; michael@0: michael@0: // domain, username, and password will be null if nsHttpNTLMAuth's ChallengeReceived michael@0: // returns false for identityInvalid. Use default credentials in this case by passing michael@0: // null for pai. michael@0: if (username && password) { michael@0: // Keep a copy of these strings for the duration michael@0: mUsername.Assign(username); michael@0: mPassword.Assign(password); michael@0: mDomain.Assign(domain); michael@0: ai.Domain = reinterpret_cast(mDomain.BeginWriting()); michael@0: ai.DomainLength = mDomain.Length(); michael@0: ai.User = reinterpret_cast(mUsername.BeginWriting()); michael@0: ai.UserLength = mUsername.Length(); michael@0: ai.Password = reinterpret_cast(mPassword.BeginWriting()); michael@0: ai.PasswordLength = mPassword.Length(); michael@0: ai.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE; michael@0: pai = &ai; michael@0: } michael@0: michael@0: rc = (sspi->AcquireCredentialsHandleW)(nullptr, michael@0: package, michael@0: SECPKG_CRED_OUTBOUND, michael@0: nullptr, michael@0: pai, michael@0: nullptr, michael@0: nullptr, michael@0: &mCred, michael@0: &useBefore); michael@0: if (rc != SEC_E_OK) michael@0: return NS_ERROR_UNEXPECTED; michael@0: michael@0: static bool sTelemetrySent = false; michael@0: if (!sTelemetrySent) { michael@0: mozilla::Telemetry::Accumulate( michael@0: mozilla::Telemetry::NTLM_MODULE_USED_2, michael@0: serviceFlags & nsIAuthModule::REQ_PROXY_AUTH michael@0: ? NTLM_MODULE_WIN_API_PROXY michael@0: : NTLM_MODULE_WIN_API_DIRECT); michael@0: sTelemetrySent = true; michael@0: } michael@0: michael@0: LOG(("AcquireCredentialsHandle() succeeded.\n")); michael@0: return NS_OK; michael@0: } michael@0: michael@0: // The arguments inToken and inTokenLen are used to pass in the server michael@0: // certificate (when available) in the first call of the function. The michael@0: // second time these arguments hold an input token. michael@0: NS_IMETHODIMP michael@0: nsAuthSSPI::GetNextToken(const void *inToken, michael@0: uint32_t inTokenLen, michael@0: void **outToken, michael@0: uint32_t *outTokenLen) michael@0: { michael@0: // String for end-point bindings. michael@0: const char end_point[] = "tls-server-end-point:"; michael@0: const int end_point_length = sizeof(end_point) - 1; michael@0: const int hash_size = 32; // Size of a SHA256 hash. michael@0: const int cbt_size = hash_size + end_point_length; michael@0: michael@0: SECURITY_STATUS rc; michael@0: MS_TimeStamp ignored; michael@0: michael@0: DWORD ctxAttr, ctxReq = 0; michael@0: CtxtHandle *ctxIn; michael@0: SecBufferDesc ibd, obd; michael@0: // Optional second input buffer for the CBT (Channel Binding Token) michael@0: SecBuffer ib[2], ob; michael@0: // Pointer to the block of memory that stores the CBT michael@0: char* sspi_cbt = nullptr; michael@0: SEC_CHANNEL_BINDINGS pendpoint_binding; michael@0: michael@0: LOG(("entering nsAuthSSPI::GetNextToken()\n")); michael@0: michael@0: if (!mCred.dwLower && !mCred.dwUpper) { michael@0: LOG(("nsAuthSSPI::GetNextToken(), not initialized. exiting.")); michael@0: return NS_ERROR_NOT_INITIALIZED; michael@0: } michael@0: michael@0: if (mServiceFlags & REQ_DELEGATE) michael@0: ctxReq |= ISC_REQ_DELEGATE; michael@0: if (mServiceFlags & REQ_MUTUAL_AUTH) michael@0: ctxReq |= ISC_REQ_MUTUAL_AUTH; michael@0: michael@0: if (inToken) { michael@0: if (mIsFirst) { michael@0: // First time if it comes with a token, michael@0: // the token represents the server certificate. michael@0: mIsFirst = false; michael@0: mCertDERLength = inTokenLen; michael@0: mCertDERData = nsMemory::Alloc(inTokenLen); michael@0: if (!mCertDERData) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: memcpy(mCertDERData, inToken, inTokenLen); michael@0: michael@0: // We are starting a new authentication sequence. michael@0: // If we have already initialized our michael@0: // security context, then we're in trouble because it means that the michael@0: // first sequence failed. We need to bail or else we might end up in michael@0: // an infinite loop. michael@0: if (mCtxt.dwLower || mCtxt.dwUpper) { michael@0: LOG(("Cannot restart authentication sequence!")); michael@0: return NS_ERROR_UNEXPECTED; michael@0: } michael@0: ctxIn = nullptr; michael@0: // The certificate needs to be erased before being passed michael@0: // to InitializeSecurityContextW(). michael@0: inToken = nullptr; michael@0: inTokenLen = 0; michael@0: } else { michael@0: ibd.ulVersion = SECBUFFER_VERSION; michael@0: ibd.cBuffers = 0; michael@0: ibd.pBuffers = ib; michael@0: michael@0: // If we have stored a certificate, the Channel Binding Token michael@0: // needs to be generated and sent in the first input buffer. michael@0: if (mCertDERLength > 0) { michael@0: // First we create a proper Endpoint Binding structure. michael@0: pendpoint_binding.dwInitiatorAddrType = 0; michael@0: pendpoint_binding.cbInitiatorLength = 0; michael@0: pendpoint_binding.dwInitiatorOffset = 0; michael@0: pendpoint_binding.dwAcceptorAddrType = 0; michael@0: pendpoint_binding.cbAcceptorLength = 0; michael@0: pendpoint_binding.dwAcceptorOffset = 0; michael@0: pendpoint_binding.cbApplicationDataLength = cbt_size; michael@0: pendpoint_binding.dwApplicationDataOffset = michael@0: sizeof(SEC_CHANNEL_BINDINGS); michael@0: michael@0: // Then add it to the array of sec buffers accordingly. michael@0: ib[ibd.cBuffers].BufferType = SECBUFFER_CHANNEL_BINDINGS; michael@0: ib[ibd.cBuffers].cbBuffer = michael@0: pendpoint_binding.cbApplicationDataLength michael@0: + pendpoint_binding.dwApplicationDataOffset; michael@0: michael@0: sspi_cbt = (char *) nsMemory::Alloc(ib[ibd.cBuffers].cbBuffer); michael@0: if (!sspi_cbt){ michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: } michael@0: michael@0: // Helper to write in the memory block that stores the CBT michael@0: char* sspi_cbt_ptr = sspi_cbt; michael@0: michael@0: ib[ibd.cBuffers].pvBuffer = sspi_cbt; michael@0: ibd.cBuffers++; michael@0: michael@0: memcpy(sspi_cbt_ptr, &pendpoint_binding, michael@0: pendpoint_binding.dwApplicationDataOffset); michael@0: sspi_cbt_ptr += pendpoint_binding.dwApplicationDataOffset; michael@0: michael@0: memcpy(sspi_cbt_ptr, end_point, end_point_length); michael@0: sspi_cbt_ptr += end_point_length; michael@0: michael@0: // Start hashing. We are always doing SHA256, but depending michael@0: // on the certificate, a different alogirthm might be needed. michael@0: nsAutoCString hashString; michael@0: michael@0: nsresult rv; michael@0: nsCOMPtr crypto; michael@0: crypto = do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID, &rv); michael@0: if (NS_SUCCEEDED(rv)) michael@0: rv = crypto->Init(nsICryptoHash::SHA256); michael@0: if (NS_SUCCEEDED(rv)) michael@0: rv = crypto->Update((unsigned char*)mCertDERData, mCertDERLength); michael@0: if (NS_SUCCEEDED(rv)) michael@0: rv = crypto->Finish(false, hashString); michael@0: if (NS_FAILED(rv)) { michael@0: nsMemory::Free(mCertDERData); michael@0: mCertDERData = nullptr; michael@0: mCertDERLength = 0; michael@0: nsMemory::Free(sspi_cbt); michael@0: return rv; michael@0: } michael@0: michael@0: // Once the hash has been computed, we store it in memory right michael@0: // after the Endpoint structure and the "tls-server-end-point:" michael@0: // char array. michael@0: memcpy(sspi_cbt_ptr, hashString.get(), hash_size); michael@0: michael@0: // Free memory used to store the server certificate michael@0: nsMemory::Free(mCertDERData); michael@0: mCertDERData = nullptr; michael@0: mCertDERLength = 0; michael@0: } // End of CBT computation. michael@0: michael@0: // We always need this SECBUFFER. michael@0: ib[ibd.cBuffers].BufferType = SECBUFFER_TOKEN; michael@0: ib[ibd.cBuffers].cbBuffer = inTokenLen; michael@0: ib[ibd.cBuffers].pvBuffer = (void *) inToken; michael@0: ibd.cBuffers++; michael@0: ctxIn = &mCtxt; michael@0: } michael@0: } else { // First time and without a token (no server certificate) michael@0: // We are starting a new authentication sequence. If we have already michael@0: // initialized our security context, then we're in trouble because it michael@0: // means that the first sequence failed. We need to bail or else we michael@0: // might end up in an infinite loop. michael@0: if (mCtxt.dwLower || mCtxt.dwUpper || mCertDERData || mCertDERLength) { michael@0: LOG(("Cannot restart authentication sequence!")); michael@0: return NS_ERROR_UNEXPECTED; michael@0: } michael@0: ctxIn = nullptr; michael@0: mIsFirst = false; michael@0: } michael@0: michael@0: obd.ulVersion = SECBUFFER_VERSION; michael@0: obd.cBuffers = 1; michael@0: obd.pBuffers = &ob; michael@0: ob.BufferType = SECBUFFER_TOKEN; michael@0: ob.cbBuffer = mMaxTokenLen; michael@0: ob.pvBuffer = nsMemory::Alloc(ob.cbBuffer); michael@0: if (!ob.pvBuffer){ michael@0: if (sspi_cbt) michael@0: nsMemory::Free(sspi_cbt); michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: } michael@0: memset(ob.pvBuffer, 0, ob.cbBuffer); michael@0: michael@0: NS_ConvertUTF8toUTF16 wSN(mServiceName); michael@0: SEC_WCHAR *sn = (SEC_WCHAR *) wSN.get(); michael@0: michael@0: rc = (sspi->InitializeSecurityContextW)(&mCred, michael@0: ctxIn, michael@0: sn, michael@0: ctxReq, michael@0: 0, michael@0: SECURITY_NATIVE_DREP, michael@0: inToken ? &ibd : nullptr, michael@0: 0, michael@0: &mCtxt, michael@0: &obd, michael@0: &ctxAttr, michael@0: &ignored); michael@0: if (rc == SEC_I_CONTINUE_NEEDED || rc == SEC_E_OK) { michael@0: michael@0: #ifdef PR_LOGGING michael@0: if (rc == SEC_E_OK) michael@0: LOG(("InitializeSecurityContext: succeeded.\n")); michael@0: else michael@0: LOG(("InitializeSecurityContext: continue.\n")); michael@0: #endif michael@0: if (sspi_cbt) michael@0: nsMemory::Free(sspi_cbt); michael@0: michael@0: if (!ob.cbBuffer) { michael@0: nsMemory::Free(ob.pvBuffer); michael@0: ob.pvBuffer = nullptr; michael@0: } michael@0: *outToken = ob.pvBuffer; michael@0: *outTokenLen = ob.cbBuffer; michael@0: michael@0: if (rc == SEC_E_OK) michael@0: return NS_SUCCESS_AUTH_FINISHED; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: LOG(("InitializeSecurityContext failed [rc=%d:%s]\n", rc, MapErrorCode(rc))); michael@0: Reset(); michael@0: nsMemory::Free(ob.pvBuffer); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsAuthSSPI::Unwrap(const void *inToken, michael@0: uint32_t inTokenLen, michael@0: void **outToken, michael@0: uint32_t *outTokenLen) michael@0: { michael@0: SECURITY_STATUS rc; michael@0: SecBufferDesc ibd; michael@0: SecBuffer ib[2]; michael@0: michael@0: ibd.cBuffers = 2; michael@0: ibd.pBuffers = ib; michael@0: ibd.ulVersion = SECBUFFER_VERSION; michael@0: michael@0: // SSPI Buf michael@0: ib[0].BufferType = SECBUFFER_STREAM; michael@0: ib[0].cbBuffer = inTokenLen; michael@0: ib[0].pvBuffer = nsMemory::Alloc(ib[0].cbBuffer); michael@0: if (!ib[0].pvBuffer) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: michael@0: memcpy(ib[0].pvBuffer, inToken, inTokenLen); michael@0: michael@0: // app data michael@0: ib[1].BufferType = SECBUFFER_DATA; michael@0: ib[1].cbBuffer = 0; michael@0: ib[1].pvBuffer = nullptr; michael@0: michael@0: rc = (sspi->DecryptMessage)( michael@0: &mCtxt, michael@0: &ibd, michael@0: 0, // no sequence numbers michael@0: nullptr michael@0: ); michael@0: michael@0: if (SEC_SUCCESS(rc)) { michael@0: // check if ib[1].pvBuffer is really just ib[0].pvBuffer, in which michael@0: // case we can let the caller free it. Otherwise, we need to michael@0: // clone it, and free the original michael@0: if (ib[0].pvBuffer == ib[1].pvBuffer) { michael@0: *outToken = ib[1].pvBuffer; michael@0: } michael@0: else { michael@0: *outToken = nsMemory::Clone(ib[1].pvBuffer, ib[1].cbBuffer); michael@0: nsMemory::Free(ib[0].pvBuffer); michael@0: if (!*outToken) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: } michael@0: *outTokenLen = ib[1].cbBuffer; michael@0: } michael@0: else michael@0: nsMemory::Free(ib[0].pvBuffer); michael@0: michael@0: if (!SEC_SUCCESS(rc)) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: // utility class used to free memory on exit michael@0: class secBuffers michael@0: { michael@0: public: michael@0: michael@0: SecBuffer ib[3]; michael@0: michael@0: secBuffers() { memset(&ib, 0, sizeof(ib)); } michael@0: michael@0: ~secBuffers() michael@0: { michael@0: if (ib[0].pvBuffer) michael@0: nsMemory::Free(ib[0].pvBuffer); michael@0: michael@0: if (ib[1].pvBuffer) michael@0: nsMemory::Free(ib[1].pvBuffer); michael@0: michael@0: if (ib[2].pvBuffer) michael@0: nsMemory::Free(ib[2].pvBuffer); michael@0: } michael@0: }; michael@0: michael@0: NS_IMETHODIMP michael@0: nsAuthSSPI::Wrap(const void *inToken, michael@0: uint32_t inTokenLen, michael@0: bool confidential, michael@0: void **outToken, michael@0: uint32_t *outTokenLen) michael@0: { michael@0: SECURITY_STATUS rc; michael@0: michael@0: SecBufferDesc ibd; michael@0: secBuffers bufs; michael@0: SecPkgContext_Sizes sizes; michael@0: michael@0: rc = (sspi->QueryContextAttributesW)( michael@0: &mCtxt, michael@0: SECPKG_ATTR_SIZES, michael@0: &sizes); michael@0: michael@0: if (!SEC_SUCCESS(rc)) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: ibd.cBuffers = 3; michael@0: ibd.pBuffers = bufs.ib; michael@0: ibd.ulVersion = SECBUFFER_VERSION; michael@0: michael@0: // SSPI michael@0: bufs.ib[0].cbBuffer = sizes.cbSecurityTrailer; michael@0: bufs.ib[0].BufferType = SECBUFFER_TOKEN; michael@0: bufs.ib[0].pvBuffer = nsMemory::Alloc(sizes.cbSecurityTrailer); michael@0: michael@0: if (!bufs.ib[0].pvBuffer) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: michael@0: // APP Data michael@0: bufs.ib[1].BufferType = SECBUFFER_DATA; michael@0: bufs.ib[1].pvBuffer = nsMemory::Alloc(inTokenLen); michael@0: bufs.ib[1].cbBuffer = inTokenLen; michael@0: michael@0: if (!bufs.ib[1].pvBuffer) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: michael@0: memcpy(bufs.ib[1].pvBuffer, inToken, inTokenLen); michael@0: michael@0: // SSPI michael@0: bufs.ib[2].BufferType = SECBUFFER_PADDING; michael@0: bufs.ib[2].cbBuffer = sizes.cbBlockSize; michael@0: bufs.ib[2].pvBuffer = nsMemory::Alloc(bufs.ib[2].cbBuffer); michael@0: michael@0: if (!bufs.ib[2].pvBuffer) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: michael@0: rc = (sspi->EncryptMessage)(&mCtxt, michael@0: confidential ? 0 : KERB_WRAP_NO_ENCRYPT, michael@0: &ibd, 0); michael@0: michael@0: if (SEC_SUCCESS(rc)) { michael@0: int len = bufs.ib[0].cbBuffer + bufs.ib[1].cbBuffer + bufs.ib[2].cbBuffer; michael@0: char *p = (char *) nsMemory::Alloc(len); michael@0: michael@0: if (!p) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: michael@0: *outToken = (void *) p; michael@0: *outTokenLen = len; michael@0: michael@0: memcpy(p, bufs.ib[0].pvBuffer, bufs.ib[0].cbBuffer); michael@0: p += bufs.ib[0].cbBuffer; michael@0: michael@0: memcpy(p,bufs.ib[1].pvBuffer, bufs.ib[1].cbBuffer); michael@0: p += bufs.ib[1].cbBuffer; michael@0: michael@0: memcpy(p,bufs.ib[2].pvBuffer, bufs.ib[2].cbBuffer); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: return NS_ERROR_FAILURE; michael@0: }