michael@0: /* vim:set ts=4 sw=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: #include "nsComponentManagerUtils.h" michael@0: #include "nsNativeCharsetUtils.h" michael@0: #include "nsIServiceManager.h" michael@0: #include "nsIPrefService.h" michael@0: michael@0: #include "nsAuthSASL.h" michael@0: michael@0: static const char kNegotiateAuthSSPI[] = "network.auth.use-sspi"; michael@0: michael@0: nsAuthSASL::nsAuthSASL() michael@0: { michael@0: mSASLReady = false; michael@0: } michael@0: michael@0: void nsAuthSASL::Reset() michael@0: { michael@0: mSASLReady = false; michael@0: } michael@0: michael@0: /* Limitations apply to this class's thread safety. See the header file */ michael@0: NS_IMPL_ISUPPORTS(nsAuthSASL, nsIAuthModule) michael@0: michael@0: NS_IMETHODIMP michael@0: nsAuthSASL::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: nsresult rv; michael@0: michael@0: NS_ASSERTION(username, "SASL requires a username"); michael@0: NS_ASSERTION(!domain && !password, "unexpected credentials"); michael@0: michael@0: mUsername = username; michael@0: michael@0: // If we're doing SASL, we should do mutual auth michael@0: serviceFlags |= REQ_MUTUAL_AUTH; michael@0: michael@0: // Find out whether we should be trying SSPI or not michael@0: const char *contractID = NS_AUTH_MODULE_CONTRACTID_PREFIX "kerb-gss"; michael@0: michael@0: nsCOMPtr prefs = do_GetService(NS_PREFSERVICE_CONTRACTID); michael@0: if (prefs) { michael@0: bool val; michael@0: rv = prefs->GetBoolPref(kNegotiateAuthSSPI, &val); michael@0: if (NS_SUCCEEDED(rv) && val) michael@0: contractID = NS_AUTH_MODULE_CONTRACTID_PREFIX "kerb-sspi"; michael@0: } michael@0: michael@0: mInnerModule = do_CreateInstance(contractID, &rv); michael@0: // if we can't create the GSSAPI module, then bail michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: mInnerModule->Init(serviceName, serviceFlags, nullptr, nullptr, nullptr); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsAuthSASL::GetNextToken(const void *inToken, michael@0: uint32_t inTokenLen, michael@0: void **outToken, michael@0: uint32_t *outTokenLen) michael@0: { michael@0: nsresult rv; michael@0: void *unwrappedToken; michael@0: char *message; michael@0: uint32_t unwrappedTokenLen, messageLen; michael@0: nsAutoCString userbuf; michael@0: michael@0: if (!mInnerModule) michael@0: return NS_ERROR_NOT_INITIALIZED; michael@0: michael@0: if (mSASLReady) { michael@0: // If the server COMPLETEs with an empty token, Cyrus sends us that token. michael@0: // I don't think this is correct, but we need to handle that behaviour. michael@0: // Cyrus ignores the contents of our reply token. michael@0: if (inTokenLen == 0) { michael@0: *outToken = nullptr; michael@0: *outTokenLen = 0; michael@0: return NS_OK; michael@0: } michael@0: // We've completed the GSSAPI portion of the handshake, and are michael@0: // now ready to do the SASL security layer and authzid negotiation michael@0: michael@0: // Input packet from the server needs to be unwrapped. michael@0: rv = mInnerModule->Unwrap(inToken, inTokenLen, &unwrappedToken, michael@0: &unwrappedTokenLen); michael@0: if (NS_FAILED(rv)) { michael@0: Reset(); michael@0: return rv; michael@0: } michael@0: michael@0: // If we were doing security layers then we'd care what the michael@0: // server had sent us. We're not, so all we had to do was make michael@0: // sure that the signature was correct with the above unwrap() michael@0: nsMemory::Free(unwrappedToken); michael@0: michael@0: NS_CopyUnicodeToNative(mUsername, userbuf); michael@0: messageLen = userbuf.Length() + 4 + 1; michael@0: message = (char *)nsMemory::Alloc(messageLen); michael@0: if (!message) { michael@0: Reset(); michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: } michael@0: message[0] = 0x01; // No security layer michael@0: message[1] = 0x00; michael@0: message[2] = 0x00; michael@0: message[3] = 0x00; // Maxbuf must be zero if we've got no sec layer michael@0: strcpy(message+4, userbuf.get()); michael@0: // Userbuf should not be nullptr terminated, so trim the trailing nullptr michael@0: // when wrapping the message michael@0: rv = mInnerModule->Wrap((void *) message, messageLen-1, false, michael@0: outToken, outTokenLen); michael@0: nsMemory::Free(message); michael@0: Reset(); // All done michael@0: return NS_SUCCEEDED(rv) ? NS_SUCCESS_AUTH_FINISHED : rv; michael@0: } michael@0: rv = mInnerModule->GetNextToken(inToken, inTokenLen, outToken, michael@0: outTokenLen); michael@0: if (rv == NS_SUCCESS_AUTH_FINISHED) { michael@0: mSASLReady = true; michael@0: rv = NS_OK; michael@0: } michael@0: return rv; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsAuthSASL::Unwrap(const void *inToken, michael@0: uint32_t inTokenLen, michael@0: void **outToken, michael@0: uint32_t *outTokenLen) michael@0: { michael@0: return NS_ERROR_NOT_IMPLEMENTED; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsAuthSASL::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: return NS_ERROR_NOT_IMPLEMENTED; michael@0: }