/* vim:set ts=4 sw=2 sts=2 et cindent: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// The service name looks like "protocol@hostname", we need to map // this to a value that SSPI expects. To be consistent with IE, we // need to map '@' to '/' and canonicalize the hostname.
int32_t index = buf.FindChar('@'); if (index == kNotFound) return NS_ERROR_UNEXPECTED;
nsCOMPtr<nsIDNSService> dnsService =
do_GetService(NS_DNSSERVICE_CONTRACTID, &rv); if (NS_FAILED(rv)) return rv;
auto dns = static_cast<nsDNSService*>(dnsService.get());
// This could be expensive if our DNS cache cannot satisfy the request. // However, we should have at least hit the OS resolver once prior to // reaching this code, so provided the OS resolver has this information // cached, we should not have to worry about blocking on this function call // for very long. NOTE: because we ask for the canonical hostname, we // might end up requiring extra network activity in cases where the OS // resolver might not have enough information to satisfy the request from // its cache. This is not an issue in versions of Windows up to WinXP.
nsCOMPtr<nsIDNSRecord> record;
mozilla::OriginAttributes attrs;
rv = dns->DeprecatedSyncResolve(Substring(buf, index + 1),
nsIDNSService::RESOLVE_CANONICAL_NAME, attrs,
getter_AddRefs(record)); if (NS_FAILED(rv)) return rv;
nsCOMPtr<nsIDNSAddrRecord> rec = do_QueryInterface(record); if (!rec) { return NS_ERROR_UNEXPECTED;
}
nsAutoCString cname;
rv = rec->GetCanonicalName(cname); if (NS_SUCCEEDED(rv)) {
result = StringHead(buf, index) + "/"_ns + cname;
LOG(("Using SPN of [%s]\n", result.get()));
} return rv;
}
// The caller must supply a service name to be used. (For why we now require // a service name for NTLM, see bug 487872.)
NS_ENSURE_TRUE(!aServiceName.IsEmpty(), NS_ERROR_INVALID_ARG);
nsresult rv;
// XXX lazy initialization like this assumes that we are single threaded if (!sspi) {
rv = InitSSPI(); if (NS_FAILED(rv)) return rv;
}
SEC_WCHAR* package;
package = (SEC_WCHAR*)pTypeName[(int)mPackage];
if (mPackage == PACKAGE_TYPE_NTLM) { // (bug 535193) For NTLM, just use the uri host, do not do canonical host // lookups. The incoming serviceName is in the format: "protocol@hostname", // SSPI expects // "<service class>/<hostname>", so swap the '@' for a '/'.
mServiceName = aServiceName;
int32_t index = mServiceName.FindChar('@'); if (index == kNotFound) return NS_ERROR_UNEXPECTED;
mServiceName.Replace(index, 1, '/');
} else { // Kerberos requires the canonical host, MakeSN takes care of this through a // DNS lookup.
rv = MakeSN(aServiceName, mServiceName); if (NS_FAILED(rv)) return rv;
}
SEC_WINNT_AUTH_IDENTITY_W ai;
SEC_WINNT_AUTH_IDENTITY_W* pai = nullptr;
// domain, username, and password will be null if nsHttpNTLMAuth's // ChallengeReceived returns false for identityInvalid. Use default // credentials in this case by passing null for pai. if (!aUsername.IsEmpty() && !aPassword.IsEmpty()) { // Keep a copy of these strings for the duration
mUsername = aUsername;
mPassword = aPassword;
mDomain = aDomain;
ai.Domain = reinterpret_cast<unsignedshort*>(mDomain.BeginWriting());
ai.DomainLength = mDomain.Length();
ai.User = reinterpret_cast<unsignedshort*>(mUsername.BeginWriting());
ai.UserLength = mUsername.Length();
ai.Password = reinterpret_cast<unsignedshort*>(mPassword.BeginWriting());
ai.PasswordLength = mPassword.Length();
ai.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
pai = &ai;
}
// The arguments inToken and inTokenLen are used to pass in the server // certificate (when available) in the first call of the function. The // second time these arguments hold an input token.
NS_IMETHODIMP
nsAuthSSPI::GetNextToken(constvoid* inToken, uint32_t inTokenLen, void** outToken, uint32_t* outTokenLen) { // String for end-point bindings. constchar end_point[] = "tls-server-end-point:"; constint end_point_length = sizeof(end_point) - 1; constint hash_size = 32; // Size of a SHA256 hash. constint cbt_size = hash_size + end_point_length;
SECURITY_STATUS rc;
MS_TimeStamp ignored;
DWORD ctxAttr, ctxReq = 0;
CtxtHandle* ctxIn;
SecBufferDesc ibd, obd; // Optional second input buffer for the CBT (Channel Binding Token)
SecBuffer ib[2], ob; // Pointer to the block of memory that stores the CBT char* sspi_cbt = nullptr;
SEC_CHANNEL_BINDINGS pendpoint_binding;
LOG(("entering nsAuthSSPI::GetNextToken()\n"));
if (!mCred.dwLower && !mCred.dwUpper) {
LOG(("nsAuthSSPI::GetNextToken(), not initialized. exiting.")); return NS_ERROR_NOT_INITIALIZED;
}
if (mServiceFlags & REQ_DELEGATE) ctxReq |= ISC_REQ_DELEGATE; if (mServiceFlags & REQ_MUTUAL_AUTH) ctxReq |= ISC_REQ_MUTUAL_AUTH;
if (inToken) { if (mIsFirst) { // First time if it comes with a token, // the token represents the server certificate.
mIsFirst = false;
mCertDERLength = inTokenLen;
mCertDERData = moz_xmalloc(inTokenLen);
memcpy(mCertDERData, inToken, inTokenLen);
// We are starting a new authentication sequence. // If we have already initialized our // security context, then we're in trouble because it means that the // first sequence failed. We need to bail or else we might end up in // an infinite loop. if (mCtxt.dwLower || mCtxt.dwUpper) {
LOG(("Cannot restart authentication sequence!")); return NS_ERROR_UNEXPECTED;
}
ctxIn = nullptr; // The certificate needs to be erased before being passed // to InitializeSecurityContextW().
inToken = nullptr;
inTokenLen = 0;
} else {
ibd.ulVersion = SECBUFFER_VERSION;
ibd.cBuffers = 0;
ibd.pBuffers = ib;
// If we have stored a certificate, the Channel Binding Token // needs to be generated and sent in the first input buffer. if (mCertDERLength > 0) { // First we create a proper Endpoint Binding structure.
pendpoint_binding.dwInitiatorAddrType = 0;
pendpoint_binding.cbInitiatorLength = 0;
pendpoint_binding.dwInitiatorOffset = 0;
pendpoint_binding.dwAcceptorAddrType = 0;
pendpoint_binding.cbAcceptorLength = 0;
pendpoint_binding.dwAcceptorOffset = 0;
pendpoint_binding.cbApplicationDataLength = cbt_size;
pendpoint_binding.dwApplicationDataOffset = sizeof(SEC_CHANNEL_BINDINGS);
// Then add it to the array of sec buffers accordingly.
ib[ibd.cBuffers].BufferType = SECBUFFER_CHANNEL_BINDINGS;
ib[ibd.cBuffers].cbBuffer = pendpoint_binding.cbApplicationDataLength +
pendpoint_binding.dwApplicationDataOffset;
// Start hashing. We are always doing SHA256, but depending // on the certificate, a different alogirthm might be needed.
nsAutoCString hashString;
nsresult rv;
nsCOMPtr<nsICryptoHash> crypto;
crypto = do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID, &rv); if (NS_SUCCEEDED(rv)) rv = crypto->Init(nsICryptoHash::SHA256); if (NS_SUCCEEDED(rv))
rv = crypto->Update((unsignedchar*)mCertDERData, mCertDERLength); if (NS_SUCCEEDED(rv)) rv = crypto->Finish(false, hashString); if (NS_FAILED(rv)) {
free(mCertDERData);
mCertDERData = nullptr;
mCertDERLength = 0;
free(sspi_cbt); return rv;
}
// Once the hash has been computed, we store it in memory right // after the Endpoint structure and the "tls-server-end-point:" // char array.
memcpy(sspi_cbt_ptr, hashString.get(), hash_size);
// Free memory used to store the server certificate
free(mCertDERData);
mCertDERData = nullptr;
mCertDERLength = 0;
} // End of CBT computation.
// We always need this SECBUFFER.
ib[ibd.cBuffers].BufferType = SECBUFFER_TOKEN;
ib[ibd.cBuffers].cbBuffer = inTokenLen;
ib[ibd.cBuffers].pvBuffer = (void*)inToken;
ibd.cBuffers++;
ctxIn = &mCtxt;
}
} else { // First time and without a token (no server certificate) // We are starting a new authentication sequence. If we have already // initialized our security context, then we're in trouble because it // means that the first sequence failed. We need to bail or else we // might end up in an infinite loop. if (mCtxt.dwLower || mCtxt.dwUpper || mCertDERData || mCertDERLength) {
LOG(("Cannot restart authentication sequence!")); return NS_ERROR_UNEXPECTED;
}
ctxIn = nullptr;
mIsFirst = false;
}
rc = (sspi->DecryptMessage)(&mCtxt, &ibd,
0, // no sequence numbers
nullptr);
if (SEC_SUCCESS(rc)) { // check if ib[1].pvBuffer is really just ib[0].pvBuffer, in which // case we can let the caller free it. Otherwise, we need to // clone it, and free the original if (ib[0].pvBuffer == ib[1].pvBuffer) {
*outToken = ib[1].pvBuffer;
} else {
*outToken = moz_xmemdup(ib[1].pvBuffer, ib[1].cbBuffer);
free(ib[0].pvBuffer);
}
*outTokenLen = ib[1].cbBuffer;
} else
free(ib[0].pvBuffer);
if (!SEC_SUCCESS(rc)) return NS_ERROR_FAILURE;
return NS_OK;
}
// utility class used to free memory on exit class secBuffers { public:
SecBuffer ib[3];
secBuffers() { memset(&ib, 0, sizeof(ib)); }
~secBuffers() { if (ib[0].pvBuffer) free(ib[0].pvBuffer);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.