extensions/auth/nsAuthSambaNTLM.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /* vim:set ts=4 sw=4 et cindent: */
michael@0 2 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 3 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 5
michael@0 6 #include "nsAuth.h"
michael@0 7 #include "nsAuthSambaNTLM.h"
michael@0 8 #include "prenv.h"
michael@0 9 #include "plbase64.h"
michael@0 10 #include "prerror.h"
michael@0 11 #include "mozilla/Telemetry.h"
michael@0 12
michael@0 13 #include <stdlib.h>
michael@0 14
michael@0 15 nsAuthSambaNTLM::nsAuthSambaNTLM()
michael@0 16 : mInitialMessage(nullptr), mChildPID(nullptr), mFromChildFD(nullptr),
michael@0 17 mToChildFD(nullptr)
michael@0 18 {
michael@0 19 }
michael@0 20
michael@0 21 nsAuthSambaNTLM::~nsAuthSambaNTLM()
michael@0 22 {
michael@0 23 // ntlm_auth reads from stdin regularly so closing our file handles
michael@0 24 // should cause it to exit.
michael@0 25 Shutdown();
michael@0 26 free(mInitialMessage);
michael@0 27 }
michael@0 28
michael@0 29 void
michael@0 30 nsAuthSambaNTLM::Shutdown()
michael@0 31 {
michael@0 32 if (mFromChildFD) {
michael@0 33 PR_Close(mFromChildFD);
michael@0 34 mFromChildFD = nullptr;
michael@0 35 }
michael@0 36 if (mToChildFD) {
michael@0 37 PR_Close(mToChildFD);
michael@0 38 mToChildFD = nullptr;
michael@0 39 }
michael@0 40 if (mChildPID) {
michael@0 41 int32_t exitCode;
michael@0 42 PR_WaitProcess(mChildPID, &exitCode);
michael@0 43 mChildPID = nullptr;
michael@0 44 }
michael@0 45 }
michael@0 46
michael@0 47 NS_IMPL_ISUPPORTS(nsAuthSambaNTLM, nsIAuthModule)
michael@0 48
michael@0 49 static bool
michael@0 50 SpawnIOChild(char* const* aArgs, PRProcess** aPID,
michael@0 51 PRFileDesc** aFromChildFD, PRFileDesc** aToChildFD)
michael@0 52 {
michael@0 53 PRFileDesc* toChildPipeRead;
michael@0 54 PRFileDesc* toChildPipeWrite;
michael@0 55 if (PR_CreatePipe(&toChildPipeRead, &toChildPipeWrite) != PR_SUCCESS)
michael@0 56 return false;
michael@0 57 PR_SetFDInheritable(toChildPipeRead, true);
michael@0 58 PR_SetFDInheritable(toChildPipeWrite, false);
michael@0 59
michael@0 60 PRFileDesc* fromChildPipeRead;
michael@0 61 PRFileDesc* fromChildPipeWrite;
michael@0 62 if (PR_CreatePipe(&fromChildPipeRead, &fromChildPipeWrite) != PR_SUCCESS) {
michael@0 63 PR_Close(toChildPipeRead);
michael@0 64 PR_Close(toChildPipeWrite);
michael@0 65 return false;
michael@0 66 }
michael@0 67 PR_SetFDInheritable(fromChildPipeRead, false);
michael@0 68 PR_SetFDInheritable(fromChildPipeWrite, true);
michael@0 69
michael@0 70 PRProcessAttr* attr = PR_NewProcessAttr();
michael@0 71 if (!attr) {
michael@0 72 PR_Close(fromChildPipeRead);
michael@0 73 PR_Close(fromChildPipeWrite);
michael@0 74 PR_Close(toChildPipeRead);
michael@0 75 PR_Close(toChildPipeWrite);
michael@0 76 return false;
michael@0 77 }
michael@0 78
michael@0 79 PR_ProcessAttrSetStdioRedirect(attr, PR_StandardInput, toChildPipeRead);
michael@0 80 PR_ProcessAttrSetStdioRedirect(attr, PR_StandardOutput, fromChildPipeWrite);
michael@0 81
michael@0 82 PRProcess* process = PR_CreateProcess(aArgs[0], aArgs, nullptr, attr);
michael@0 83 PR_DestroyProcessAttr(attr);
michael@0 84 PR_Close(fromChildPipeWrite);
michael@0 85 PR_Close(toChildPipeRead);
michael@0 86 if (!process) {
michael@0 87 LOG(("ntlm_auth exec failure [%d]", PR_GetError()));
michael@0 88 PR_Close(fromChildPipeRead);
michael@0 89 PR_Close(toChildPipeWrite);
michael@0 90 return false;
michael@0 91 }
michael@0 92
michael@0 93 *aPID = process;
michael@0 94 *aFromChildFD = fromChildPipeRead;
michael@0 95 *aToChildFD = toChildPipeWrite;
michael@0 96 return true;
michael@0 97 }
michael@0 98
michael@0 99 static bool WriteString(PRFileDesc* aFD, const nsACString& aString)
michael@0 100 {
michael@0 101 int32_t length = aString.Length();
michael@0 102 const char* s = aString.BeginReading();
michael@0 103 LOG(("Writing to ntlm_auth: %s", s));
michael@0 104
michael@0 105 while (length > 0) {
michael@0 106 int result = PR_Write(aFD, s, length);
michael@0 107 if (result <= 0)
michael@0 108 return false;
michael@0 109 s += result;
michael@0 110 length -= result;
michael@0 111 }
michael@0 112 return true;
michael@0 113 }
michael@0 114
michael@0 115 static bool ReadLine(PRFileDesc* aFD, nsACString& aString)
michael@0 116 {
michael@0 117 // ntlm_auth is defined to only send one line in response to each of our
michael@0 118 // input lines. So this simple unbuffered strategy works as long as we
michael@0 119 // read the response immediately after sending one request.
michael@0 120 aString.Truncate();
michael@0 121 for (;;) {
michael@0 122 char buf[1024];
michael@0 123 int result = PR_Read(aFD, buf, sizeof(buf));
michael@0 124 if (result <= 0)
michael@0 125 return false;
michael@0 126 aString.Append(buf, result);
michael@0 127 if (buf[result - 1] == '\n') {
michael@0 128 LOG(("Read from ntlm_auth: %s", nsPromiseFlatCString(aString).get()));
michael@0 129 return true;
michael@0 130 }
michael@0 131 }
michael@0 132 }
michael@0 133
michael@0 134 /**
michael@0 135 * Returns a heap-allocated array of PRUint8s, and stores the length in aLen.
michael@0 136 * Returns nullptr if there's an error of any kind.
michael@0 137 */
michael@0 138 static uint8_t* ExtractMessage(const nsACString& aLine, uint32_t* aLen)
michael@0 139 {
michael@0 140 // ntlm_auth sends blobs to us as base64-encoded strings after the "xx "
michael@0 141 // preamble on the response line.
michael@0 142 int32_t length = aLine.Length();
michael@0 143 // The caller should verify there is a valid "xx " prefix and the line
michael@0 144 // is terminated with a \n
michael@0 145 NS_ASSERTION(length >= 4, "Line too short...");
michael@0 146 const char* line = aLine.BeginReading();
michael@0 147 const char* s = line + 3;
michael@0 148 length -= 4; // lose first 3 chars plus trailing \n
michael@0 149 NS_ASSERTION(s[length] == '\n', "aLine not newline-terminated");
michael@0 150
michael@0 151 if (length & 3) {
michael@0 152 // The base64 encoded block must be multiple of 4. If not, something
michael@0 153 // screwed up.
michael@0 154 NS_WARNING("Base64 encoded block should be a multiple of 4 chars");
michael@0 155 return nullptr;
michael@0 156 }
michael@0 157
michael@0 158 // Calculate the exact length. I wonder why there isn't a function for this
michael@0 159 // in plbase64.
michael@0 160 int32_t numEquals;
michael@0 161 for (numEquals = 0; numEquals < length; ++numEquals) {
michael@0 162 if (s[length - 1 - numEquals] != '=')
michael@0 163 break;
michael@0 164 }
michael@0 165 *aLen = (length/4)*3 - numEquals;
michael@0 166 return reinterpret_cast<uint8_t*>(PL_Base64Decode(s, length, nullptr));
michael@0 167 }
michael@0 168
michael@0 169 nsresult
michael@0 170 nsAuthSambaNTLM::SpawnNTLMAuthHelper()
michael@0 171 {
michael@0 172 const char* username = PR_GetEnv("USER");
michael@0 173 if (!username)
michael@0 174 return NS_ERROR_FAILURE;
michael@0 175
michael@0 176 const char* const args[] = {
michael@0 177 "ntlm_auth",
michael@0 178 "--helper-protocol", "ntlmssp-client-1",
michael@0 179 "--use-cached-creds",
michael@0 180 "--username", username,
michael@0 181 nullptr
michael@0 182 };
michael@0 183
michael@0 184 bool isOK = SpawnIOChild(const_cast<char* const*>(args), &mChildPID, &mFromChildFD, &mToChildFD);
michael@0 185 if (!isOK)
michael@0 186 return NS_ERROR_FAILURE;
michael@0 187
michael@0 188 if (!WriteString(mToChildFD, NS_LITERAL_CSTRING("YR\n")))
michael@0 189 return NS_ERROR_FAILURE;
michael@0 190 nsCString line;
michael@0 191 if (!ReadLine(mFromChildFD, line))
michael@0 192 return NS_ERROR_FAILURE;
michael@0 193 if (!StringBeginsWith(line, NS_LITERAL_CSTRING("YR "))) {
michael@0 194 // Something went wrong. Perhaps no credentials are accessible.
michael@0 195 return NS_ERROR_FAILURE;
michael@0 196 }
michael@0 197
michael@0 198 // It gave us an initial client-to-server request packet. Save that
michael@0 199 // because we'll need it later.
michael@0 200 mInitialMessage = ExtractMessage(line, &mInitialMessageLen);
michael@0 201 if (!mInitialMessage)
michael@0 202 return NS_ERROR_FAILURE;
michael@0 203 return NS_OK;
michael@0 204 }
michael@0 205
michael@0 206 NS_IMETHODIMP
michael@0 207 nsAuthSambaNTLM::Init(const char *serviceName,
michael@0 208 uint32_t serviceFlags,
michael@0 209 const char16_t *domain,
michael@0 210 const char16_t *username,
michael@0 211 const char16_t *password)
michael@0 212 {
michael@0 213 NS_ASSERTION(!username && !domain && !password, "unexpected credentials");
michael@0 214
michael@0 215 static bool sTelemetrySent = false;
michael@0 216 if (!sTelemetrySent) {
michael@0 217 mozilla::Telemetry::Accumulate(
michael@0 218 mozilla::Telemetry::NTLM_MODULE_USED_2,
michael@0 219 serviceFlags & nsIAuthModule::REQ_PROXY_AUTH
michael@0 220 ? NTLM_MODULE_SAMBA_AUTH_PROXY
michael@0 221 : NTLM_MODULE_SAMBA_AUTH_DIRECT);
michael@0 222 sTelemetrySent = true;
michael@0 223 }
michael@0 224
michael@0 225 return NS_OK;
michael@0 226 }
michael@0 227
michael@0 228 NS_IMETHODIMP
michael@0 229 nsAuthSambaNTLM::GetNextToken(const void *inToken,
michael@0 230 uint32_t inTokenLen,
michael@0 231 void **outToken,
michael@0 232 uint32_t *outTokenLen)
michael@0 233 {
michael@0 234 if (!inToken) {
michael@0 235 /* someone wants our initial message */
michael@0 236 *outToken = nsMemory::Clone(mInitialMessage, mInitialMessageLen);
michael@0 237 if (!*outToken)
michael@0 238 return NS_ERROR_OUT_OF_MEMORY;
michael@0 239 *outTokenLen = mInitialMessageLen;
michael@0 240 return NS_OK;
michael@0 241 }
michael@0 242
michael@0 243 /* inToken must be a type 2 message. Get ntlm_auth to generate our response */
michael@0 244 char* encoded = PL_Base64Encode(static_cast<const char*>(inToken), inTokenLen, nullptr);
michael@0 245 if (!encoded)
michael@0 246 return NS_ERROR_OUT_OF_MEMORY;
michael@0 247
michael@0 248 nsCString request;
michael@0 249 request.AssignLiteral("TT ");
michael@0 250 request.Append(encoded);
michael@0 251 free(encoded);
michael@0 252 request.Append('\n');
michael@0 253
michael@0 254 if (!WriteString(mToChildFD, request))
michael@0 255 return NS_ERROR_FAILURE;
michael@0 256 nsCString line;
michael@0 257 if (!ReadLine(mFromChildFD, line))
michael@0 258 return NS_ERROR_FAILURE;
michael@0 259 if (!StringBeginsWith(line, NS_LITERAL_CSTRING("KK "))) {
michael@0 260 // Something went wrong. Perhaps no credentials are accessible.
michael@0 261 return NS_ERROR_FAILURE;
michael@0 262 }
michael@0 263 uint8_t* buf = ExtractMessage(line, outTokenLen);
michael@0 264 if (!buf)
michael@0 265 return NS_ERROR_FAILURE;
michael@0 266 // *outToken has to be freed by nsMemory::Free, which may not be free()
michael@0 267 *outToken = nsMemory::Clone(buf, *outTokenLen);
michael@0 268 if (!*outToken) {
michael@0 269 free(buf);
michael@0 270 return NS_ERROR_OUT_OF_MEMORY;
michael@0 271 }
michael@0 272
michael@0 273 // We're done. Close our file descriptors now and reap the helper
michael@0 274 // process.
michael@0 275 Shutdown();
michael@0 276 return NS_SUCCESS_AUTH_FINISHED;
michael@0 277 }
michael@0 278
michael@0 279 NS_IMETHODIMP
michael@0 280 nsAuthSambaNTLM::Unwrap(const void *inToken,
michael@0 281 uint32_t inTokenLen,
michael@0 282 void **outToken,
michael@0 283 uint32_t *outTokenLen)
michael@0 284 {
michael@0 285 return NS_ERROR_NOT_IMPLEMENTED;
michael@0 286 }
michael@0 287
michael@0 288 NS_IMETHODIMP
michael@0 289 nsAuthSambaNTLM::Wrap(const void *inToken,
michael@0 290 uint32_t inTokenLen,
michael@0 291 bool confidential,
michael@0 292 void **outToken,
michael@0 293 uint32_t *outTokenLen)
michael@0 294 {
michael@0 295 return NS_ERROR_NOT_IMPLEMENTED;
michael@0 296 }

mercurial