extensions/auth/nsAuthSambaNTLM.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

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

mercurial