diff --git a/mozilla/extensions/auth/Makefile.in b/mozilla/extensions/auth/Makefile.in index f0aff40..46e5f5e 100644 --- a/mozilla/extensions/auth/Makefile.in +++ b/mozilla/extensions/auth/Makefile.in @@ -78,6 +78,8 @@ CPPSRCS += \ ifeq ($(OS_ARCH),WINNT) LOCAL_INCLUDES += -DUSE_SSPI CPPSRCS += nsAuthSSPI.cpp +else +CPPSRCS += nsAuthSambaNTLM.cpp endif include $(topsrcdir)/config/rules.mk diff --git a/mozilla/extensions/auth/nsAuthFactory.cpp b/mozilla/extensions/auth/nsAuthFactory.cpp index b3d515c..71705e5 100644 --- a/mozilla/extensions/auth/nsAuthFactory.cpp +++ b/mozilla/extensions/auth/nsAuthFactory.cpp @@ -127,6 +127,36 @@ nsKerbSSPIAuthConstructor(nsISupports *outer, REFNSIID iid, void **result) {0x8c, 0xd6, 0x08, 0x00, 0x20, 0x0c, 0x9a, 0x66} \ } +#else + +#define NS_SAMBANTLMAUTH_CID \ +{ /* bc54f001-6eb0-4e32-9f49-7e064d8e70ef */ \ + 0xbc54f001, \ + 0x6eb0, \ + 0x4e32, \ + {0x9f, 0x49, 0x7e, 0x06, 0x4d, 0x8e, 0x70, 0xef} \ +} + +#include "nsAuthSambaNTLM.h" +static NS_METHOD +nsSambaNTLMAuthConstructor(nsISupports *outer, REFNSIID iid, void **result) +{ + if (outer) + return NS_ERROR_NO_AGGREGATION; + + nsCOMPtr auth = new nsAuthSambaNTLM(); + if (!auth) + return NS_ERROR_OUT_OF_MEMORY; + + nsresult rv = auth->SpawnNTLMAuthHelper(); + if (NS_FAILED(rv)) { + // Failure here probably means that cached credentials were not available + return rv; + } + + return auth->QueryInterface(iid, result); +} + #endif static NS_METHOD @@ -206,6 +236,12 @@ static nsModuleComponentInfo components[] = { NS_AUTH_MODULE_CONTRACTID_PREFIX "sys-ntlm", nsSysNTLMAuthConstructor }, +#else + { "nsAuthSambaNTLM", + NS_SAMBANTLMAUTH_CID, + NS_AUTH_MODULE_CONTRACTID_PREFIX "sys-ntlm", + nsSambaNTLMAuthConstructor + }, #endif { "nsHttpNegotiateAuth", NS_HTTPNEGOTIATEAUTH_CID, diff --git a/mozilla/extensions/auth/nsAuthSambaNTLM.cpp b/mozilla/extensions/auth/nsAuthSambaNTLM.cpp new file mode 100644 index 0000000..05d00f2 --- /dev/null +++ b/mozilla/extensions/auth/nsAuthSambaNTLM.cpp @@ -0,0 +1,323 @@ +/* vim:set ts=4 sw=4 et cindent: */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Samba NTLM Authentication. + * + * The Initial Developer of the Original Code is Novell. + * Portions created by the Initial Developer are Copyright (C) 2005 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Robert O'Callahan (rocallahan@novell.com) + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "nsAuth.h" +#include "nsAuthSambaNTLM.h" +#include "prenv.h" +#include "plbase64.h" + +#include +#include +#include +#include +#include + +nsAuthSambaNTLM::nsAuthSambaNTLM() + : mInitialMessage(nsnull), mChildPID(0), mFromChildFD(-1), mToChildFD(-1) +{ +} + +nsAuthSambaNTLM::~nsAuthSambaNTLM() +{ + // ntlm_auth reads from stdin regularly so closing our file handles + // should cause it to exit. Trying to kill it with a signal would be + // problematic because another process could have spawned with that pid + // if it has unexpectedly quit. + Shutdown(); + free(mInitialMessage); +} + +void +nsAuthSambaNTLM::Shutdown() +{ + if (mFromChildFD >= 0) { + close(mFromChildFD); + mFromChildFD = -1; + } + if (mToChildFD >= 0) { + close(mToChildFD); + mToChildFD = -1; + } + if (mChildPID) { + waitpid(mChildPID, nsnull, 0); + mChildPID = 0; + } +} + +NS_IMPL_ISUPPORTS1(nsAuthSambaNTLM, nsIAuthModule) + +static PRBool +SpawnIOChild(char** aArgs, pid_t* aPID, int* aFromChildFD, int* aToChildFD) +{ + int toChildPipe[2]; + if (pipe(toChildPipe) < 0) + return PR_FALSE; + int fromChildPipe[2]; + if (pipe(fromChildPipe) < 0) { + close(toChildPipe[0]); + close(toChildPipe[1]); + return PR_FALSE; + } + + pid_t pid = fork(); + if (pid < 0) { + PRInt32 i; + for (i = 0; i < 2; ++i) { + close(fromChildPipe[i]); + close(toChildPipe[i]); + } + NS_WARNING("fork() failed"); + return PR_FALSE; + } + + if (pid > 0) { + // We are the parent + close(toChildPipe[0]); + close(fromChildPipe[1]); + *aPID = pid; + *aFromChildFD = fromChildPipe[0]; + *aToChildFD = toChildPipe[1]; + return PR_TRUE; + } + + // We are the child + close(toChildPipe[1]); + close(fromChildPipe[0]); + // Make stdin read from toChildPipe + int result0 = dup2(toChildPipe[0], 0); + // Make stdout write to fromChildPipe + int result1 = dup2(fromChildPipe[1], 1); + if (result0 >= 0 && result1 >= 0) { + execvp(aArgs[0], aArgs); + } + + // Guess an error occurred! Just exit, the parent will notice + // that we're not responding correctly. + LOG(("ntlm_auth exec failure, errno=%d", errno)); + exit(0); + // ... satisfy the compiler + return PR_FALSE; +} + +static PRBool WriteString(int aFD, const nsACString& aString) +{ + PRInt32 length = aString.Length(); + nsPromiseFlatCString flat(aString); + const char* s = flat.get(); + LOG(("Writing to ntlm_auth: %s", s)); + + while (length > 0) { + int result = write(aFD, s, length); + if (result <= 0) + return PR_FALSE; + s += result; + length -= result; + } + return PR_TRUE; +} + +static PRBool ReadLine(int aFD, nsACString& aString) +{ + // ntlm_auth is defined to only send one line in response to each of our + // input lines. So this simple unbuffered strategy works as long as we + // read the response immediately after sending one request. + aString.Truncate(); + for (;;) { + char buf[1024]; + int result = read(aFD, buf, sizeof(buf)); + if (result <= 0) + return PR_FALSE; + aString.Append(buf, result); + if (buf[result - 1] == '\n') { + LOG(("Read from ntlm_auth: %s", nsPromiseFlatCString(aString).get())); + return PR_TRUE; + } + } +} + +/** + * Returns a heap-allocated array of PRUint8s, and stores the length in aLen. + * Returns nsnull if there's an error of any kind. + */ +static PRUint8* ExtractMessage(const nsACString& aLine, PRUint32* aLen) +{ + // ntlm_auth sends blobs to us as base64-encoded strings after the "xx " + // preamble on the response line. + PRInt32 length = aLine.Length(); + // The caller should verify there is a valid "xx " prefix and the line + // is terminated with a \n + NS_ASSERTION(length >= 4, "Line too short..."); + nsPromiseFlatCString flat(aLine); + const char* s = flat.get() + 3; + length -= 4; // lose first 3 chars plus trailing \n + NS_ASSERTION(s[length] == '\n', "aLine not newline-terminated"); + + if (length & 3) { + // The base64 encoded block must be multiple of 4. If not, something + // screwed up. + NS_WARNING("Base64 encoded block should be a multiple of 4 chars"); + return nsnull; + } + + // Calculate the exact length. I wonder why there isn't a function for this + // in plbase64. + PRInt32 numEquals; + for (numEquals = 0; numEquals < length; ++numEquals) { + if (flat.get()[length - 1 - numEquals] != '=') + break; + } + *aLen = (length/4)*3 - numEquals; + return NS_REINTERPRET_CAST(PRUint8*, PL_Base64Decode(flat.get() + 3, length - 4, nsnull)); +} + +nsresult +nsAuthSambaNTLM::SpawnNTLMAuthHelper() +{ + const char* username = PR_GetEnv("USER"); + if (!username) + return NS_ERROR_FAILURE; + + char* args[] = { + "ntlm_auth", + "--helper-protocol", "ntlmssp-client-1", + "--use-cached-creds", + "--username", NS_CONST_CAST(char*, username), + nsnull + }; + + PRBool isOK = SpawnIOChild(args, &mChildPID, &mFromChildFD, &mToChildFD); + if (!isOK) + return NS_ERROR_FAILURE; + + if (!WriteString(mToChildFD, NS_LITERAL_CSTRING("YR\n"))) + return NS_ERROR_FAILURE; + nsCString line; + if (!ReadLine(mFromChildFD, line)) + return NS_ERROR_FAILURE; + if (!StringBeginsWith(line, NS_LITERAL_CSTRING("YR "))) { + // Something went wrong. Perhaps no credentials are accessible. + return NS_ERROR_FAILURE; + } + + // It gave us an initial client-to-server request packet. Save that + // because we'll need it later. + mInitialMessage = ExtractMessage(line, &mInitialMessageLen); + if (!mInitialMessage) + return NS_ERROR_FAILURE; + return NS_OK; +} + +NS_IMETHODIMP +nsAuthSambaNTLM::Init(const char *serviceName, + PRUint32 serviceFlags, + const PRUnichar *domain, + const PRUnichar *username, + const PRUnichar *password) +{ + NS_ASSERTION(!username && !domain && !password, "unexpected credentials"); + return NS_OK; +} + +NS_IMETHODIMP +nsAuthSambaNTLM::GetNextToken(const void *inToken, + PRUint32 inTokenLen, + void **outToken, + PRUint32 *outTokenLen) +{ + if (!inToken) { + /* someone wants our initial message */ + *outToken = nsMemory::Clone(mInitialMessage, mInitialMessageLen); + if (!*outToken) + return NS_ERROR_OUT_OF_MEMORY; + *outTokenLen = mInitialMessageLen; + return NS_OK; + } + + /* inToken must be a type 2 message. Get ntlm_auth to generate our response */ + char* encoded = PL_Base64Encode(NS_STATIC_CAST(const char*, inToken), inTokenLen, nsnull); + if (!encoded) + return NS_ERROR_OUT_OF_MEMORY; + + nsCString request; + request.AssignLiteral("TT "); + request.Append(encoded); + free(encoded); + request.Append('\n'); + + if (!WriteString(mToChildFD, request)) + return NS_ERROR_FAILURE; + nsCString line; + if (!ReadLine(mFromChildFD, line)) + return NS_ERROR_FAILURE; + if (!StringBeginsWith(line, NS_LITERAL_CSTRING("KK "))) { + // Something went wrong. Perhaps no credentials are accessible. + return NS_ERROR_FAILURE; + } + PRUint8* buf = ExtractMessage(line, outTokenLen); + if (!buf) + return NS_ERROR_FAILURE; + // *outToken has to be freed by nsMemory::Free, which may not be free() + *outToken = nsMemory::Clone(buf, *outTokenLen); + if (!*outToken) { + free(buf); + return NS_ERROR_OUT_OF_MEMORY; + } + + // We're done. Close our file descriptors now and reap the helper + // process. + Shutdown(); + return NS_OK; +} + +NS_IMETHODIMP +nsAuthSambaNTLM::Unwrap(const void *inToken, + PRUint32 inTokenLen, + void **outToken, + PRUint32 *outTokenLen) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +nsAuthSambaNTLM::Wrap(const void *inToken, + PRUint32 inTokenLen, + PRBool confidential, + void **outToken, + PRUint32 *outTokenLen) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} diff --git a/mozilla/extensions/auth/nsAuthSambaNTLM.h b/mozilla/extensions/auth/nsAuthSambaNTLM.h new file mode 100644 index 0000000..6034f03 --- /dev/null +++ b/mozilla/extensions/auth/nsAuthSambaNTLM.h @@ -0,0 +1,78 @@ +/* vim:set ts=4 sw=4 et cindent: */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Samba NTLM Authentication. + * + * The Initial Developer of the Original Code is Novell. + * Portions created by the Initial Developer are Copyright (C) 2005 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Robert O'Callahan (rocallahan@novell.com) + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef nsAuthSambaNTLM_h__ +#define nsAuthSambaNTLM_h__ + +#include "nsIAuthModule.h" +#include "nsString.h" +#include "nsCOMPtr.h" + +/** + * This is an implementation of NTLM authentication that does single-signon + * by obtaining the user's Unix username and then asking Samba's + * ntlm_auth tool to do the authentication for us + * using the user's password cached in winbindd, if available. If the + * password is not available then this component fails to instantiate so + * nsHttpNTLMAuth will fall back to a different NTLM implementation. + */ +class nsAuthSambaNTLM : public nsIAuthModule +{ +public: + NS_DECL_ISUPPORTS + NS_DECL_NSIAUTHMODULE + + nsAuthSambaNTLM(); + + // We spawn the ntlm_auth helper from the module constructor, because + // that lets us fail to instantiate the module if ntlm_auth isn't + // available, triggering fallback to the built-in NTLM support (which + // doesn't support single signon, of course) + nsresult SpawnNTLMAuthHelper(); + +private: + ~nsAuthSambaNTLM(); + + void Shutdown(); + + PRUint8* mInitialMessage; /* free with free() */ + PRUint32 mInitialMessageLen; + PRInt32 mChildPID; + PRInt32 mFromChildFD, mToChildFD; +}; + +#endif /* nsAuthSambaNTLM_h__ */ diff --git a/mozilla/netwerk/protocol/http/src/nsHttpNTLMAuth.cpp b/mozilla/netwerk/protocol/http/src/nsHttpNTLMAuth.cpp index 9c25334..cde2695 100644 --- a/mozilla/netwerk/protocol/http/src/nsHttpNTLMAuth.cpp +++ b/mozilla/netwerk/protocol/http/src/nsHttpNTLMAuth.cpp @@ -46,8 +46,6 @@ //----------------------------------------------------------------------------- -#ifdef XP_WIN - #include "nsIPrefBranch.h" #include "nsIPrefService.h" #include "nsIServiceManager.h" @@ -208,8 +206,6 @@ public: }; NS_IMPL_ISUPPORTS0(nsNTLMSessionState) -#endif - //----------------------------------------------------------------------------- NS_IMPL_ISUPPORTS1(nsHttpNTLMAuth, nsIHttpAuthenticator) @@ -231,7 +227,6 @@ nsHttpNTLMAuth::ChallengeReceived(nsIHttpChannel *channel, // start new auth sequence if challenge is exactly "NTLM" if (PL_strcasecmp(challenge, "NTLM") == 0) { nsCOMPtr module; -#ifdef XP_WIN // // our session state is non-null to indicate that we've flagged // this auth domain as not accepting the system's default login. @@ -239,7 +234,7 @@ nsHttpNTLMAuth::ChallengeReceived(nsIHttpChannel *channel, PRBool trySysNTLM = (*sessionState == nsnull); // - // on windows, we may have access to the built-in SSPI library, + // we may have access to a built-in SSPI library, // which could be used to authenticate the user without prompting. // // if the continuationState is null, then we may want to try using @@ -266,9 +261,7 @@ nsHttpNTLMAuth::ChallengeReceived(nsIHttpChannel *channel, return NS_ERROR_OUT_OF_MEMORY; NS_ADDREF(*sessionState); } -#else - { -#endif + module = do_CreateInstance(NS_AUTH_MODULE_CONTRACTID_PREFIX "ntlm"); // prompt user for domain, username, and password...