Bug 470455 - test_database_sync_embed_visits.js leaks, r=sdwilsh
[wine-gecko.git] / extensions / auth / nsAuthSambaNTLM.cpp
blobb0bd5b803ce04a41afdd9f70212cb471ea411238
1 /* vim:set ts=4 sw=4 et cindent: */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
13 * License.
15 * The Original Code is Samba NTLM Authentication.
17 * The Initial Developer of the Original Code is Novell.
18 * Portions created by the Initial Developer are Copyright (C) 2005
19 * the Initial Developer. All Rights Reserved.
21 * Contributor(s):
22 * Robert O'Callahan (rocallahan@novell.com)
24 * Alternatively, the contents of this file may be used under the terms of
25 * either the GNU General Public License Version 2 or later (the "GPL"), or
26 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 * in which case the provisions of the GPL or the LGPL are applicable instead
28 * of those above. If you wish to allow use of your version of this file only
29 * under the terms of either the GPL or the LGPL, and not to allow others to
30 * use your version of this file under the terms of the MPL, indicate your
31 * decision by deleting the provisions above and replace them with the notice
32 * and other provisions required by the GPL or the LGPL. If you do not delete
33 * the provisions above, a recipient may use your version of this file under
34 * the terms of any one of the MPL, the GPL or the LGPL.
36 * ***** END LICENSE BLOCK ***** */
38 #include "nsAuth.h"
39 #include "nsAuthSambaNTLM.h"
40 #include "prenv.h"
41 #include "plbase64.h"
42 #include "prerror.h"
44 #include <stdlib.h>
46 nsAuthSambaNTLM::nsAuthSambaNTLM()
47 : mInitialMessage(nsnull), mChildPID(nsnull), mFromChildFD(nsnull),
48 mToChildFD(nsnull)
52 nsAuthSambaNTLM::~nsAuthSambaNTLM()
54 // ntlm_auth reads from stdin regularly so closing our file handles
55 // should cause it to exit.
56 Shutdown();
57 free(mInitialMessage);
60 void
61 nsAuthSambaNTLM::Shutdown()
63 if (mFromChildFD) {
64 PR_Close(mFromChildFD);
65 mFromChildFD = nsnull;
67 if (mToChildFD) {
68 PR_Close(mToChildFD);
69 mToChildFD = nsnull;
71 if (mChildPID) {
72 PRInt32 exitCode;
73 PR_WaitProcess(mChildPID, &exitCode);
74 mChildPID = nsnull;
78 NS_IMPL_ISUPPORTS1(nsAuthSambaNTLM, nsIAuthModule)
80 static PRBool
81 SpawnIOChild(char** aArgs, PRProcess** aPID,
82 PRFileDesc** aFromChildFD, PRFileDesc** aToChildFD)
84 PRFileDesc* toChildPipeRead;
85 PRFileDesc* toChildPipeWrite;
86 if (PR_CreatePipe(&toChildPipeRead, &toChildPipeWrite) != PR_SUCCESS)
87 return PR_FALSE;
88 PR_SetFDInheritable(toChildPipeRead, PR_TRUE);
89 PR_SetFDInheritable(toChildPipeWrite, PR_FALSE);
91 PRFileDesc* fromChildPipeRead;
92 PRFileDesc* fromChildPipeWrite;
93 if (PR_CreatePipe(&fromChildPipeRead, &fromChildPipeWrite) != PR_SUCCESS) {
94 PR_Close(toChildPipeRead);
95 PR_Close(toChildPipeWrite);
96 return PR_FALSE;
98 PR_SetFDInheritable(fromChildPipeRead, PR_FALSE);
99 PR_SetFDInheritable(fromChildPipeWrite, PR_TRUE);
101 PRProcessAttr* attr = PR_NewProcessAttr();
102 if (!attr) {
103 PR_Close(fromChildPipeRead);
104 PR_Close(fromChildPipeWrite);
105 PR_Close(toChildPipeRead);
106 PR_Close(toChildPipeWrite);
107 return PR_FALSE;
110 PR_ProcessAttrSetStdioRedirect(attr, PR_StandardInput, toChildPipeRead);
111 PR_ProcessAttrSetStdioRedirect(attr, PR_StandardOutput, fromChildPipeWrite);
113 PRProcess* process = PR_CreateProcess(aArgs[0], aArgs, nsnull, attr);
114 PR_DestroyProcessAttr(attr);
115 PR_Close(fromChildPipeWrite);
116 PR_Close(toChildPipeRead);
117 if (!process) {
118 LOG(("ntlm_auth exec failure [%d]", PR_GetError()));
119 PR_Close(fromChildPipeRead);
120 PR_Close(toChildPipeWrite);
121 return PR_FALSE;
124 *aPID = process;
125 *aFromChildFD = fromChildPipeRead;
126 *aToChildFD = toChildPipeWrite;
127 return PR_TRUE;
130 static PRBool WriteString(PRFileDesc* aFD, const nsACString& aString)
132 PRInt32 length = aString.Length();
133 const char* s = aString.BeginReading();
134 LOG(("Writing to ntlm_auth: %s", s));
136 while (length > 0) {
137 int result = PR_Write(aFD, s, length);
138 if (result <= 0)
139 return PR_FALSE;
140 s += result;
141 length -= result;
143 return PR_TRUE;
146 static PRBool ReadLine(PRFileDesc* aFD, nsACString& aString)
148 // ntlm_auth is defined to only send one line in response to each of our
149 // input lines. So this simple unbuffered strategy works as long as we
150 // read the response immediately after sending one request.
151 aString.Truncate();
152 for (;;) {
153 char buf[1024];
154 int result = PR_Read(aFD, buf, sizeof(buf));
155 if (result <= 0)
156 return PR_FALSE;
157 aString.Append(buf, result);
158 if (buf[result - 1] == '\n') {
159 LOG(("Read from ntlm_auth: %s", nsPromiseFlatCString(aString).get()));
160 return PR_TRUE;
166 * Returns a heap-allocated array of PRUint8s, and stores the length in aLen.
167 * Returns nsnull if there's an error of any kind.
169 static PRUint8* ExtractMessage(const nsACString& aLine, PRUint32* aLen)
171 // ntlm_auth sends blobs to us as base64-encoded strings after the "xx "
172 // preamble on the response line.
173 PRInt32 length = aLine.Length();
174 // The caller should verify there is a valid "xx " prefix and the line
175 // is terminated with a \n
176 NS_ASSERTION(length >= 4, "Line too short...");
177 const char* line = aLine.BeginReading();
178 const char* s = line + 3;
179 length -= 4; // lose first 3 chars plus trailing \n
180 NS_ASSERTION(s[length] == '\n', "aLine not newline-terminated");
182 if (length & 3) {
183 // The base64 encoded block must be multiple of 4. If not, something
184 // screwed up.
185 NS_WARNING("Base64 encoded block should be a multiple of 4 chars");
186 return nsnull;
189 // Calculate the exact length. I wonder why there isn't a function for this
190 // in plbase64.
191 PRInt32 numEquals;
192 for (numEquals = 0; numEquals < length; ++numEquals) {
193 if (s[length - 1 - numEquals] != '=')
194 break;
196 *aLen = (length/4)*3 - numEquals;
197 return reinterpret_cast<PRUint8*>(PL_Base64Decode(s, length, nsnull));
200 nsresult
201 nsAuthSambaNTLM::SpawnNTLMAuthHelper()
203 const char* username = PR_GetEnv("USER");
204 if (!username)
205 return NS_ERROR_FAILURE;
207 char* args[] = {
208 "ntlm_auth",
209 "--helper-protocol", "ntlmssp-client-1",
210 "--use-cached-creds",
211 "--username", const_cast<char*>(username),
212 nsnull
215 PRBool isOK = SpawnIOChild(args, &mChildPID, &mFromChildFD, &mToChildFD);
216 if (!isOK)
217 return NS_ERROR_FAILURE;
219 if (!WriteString(mToChildFD, NS_LITERAL_CSTRING("YR\n")))
220 return NS_ERROR_FAILURE;
221 nsCString line;
222 if (!ReadLine(mFromChildFD, line))
223 return NS_ERROR_FAILURE;
224 if (!StringBeginsWith(line, NS_LITERAL_CSTRING("YR "))) {
225 // Something went wrong. Perhaps no credentials are accessible.
226 return NS_ERROR_FAILURE;
229 // It gave us an initial client-to-server request packet. Save that
230 // because we'll need it later.
231 mInitialMessage = ExtractMessage(line, &mInitialMessageLen);
232 if (!mInitialMessage)
233 return NS_ERROR_FAILURE;
234 return NS_OK;
237 NS_IMETHODIMP
238 nsAuthSambaNTLM::Init(const char *serviceName,
239 PRUint32 serviceFlags,
240 const PRUnichar *domain,
241 const PRUnichar *username,
242 const PRUnichar *password)
244 NS_ASSERTION(!username && !domain && !password, "unexpected credentials");
245 return NS_OK;
248 NS_IMETHODIMP
249 nsAuthSambaNTLM::GetNextToken(const void *inToken,
250 PRUint32 inTokenLen,
251 void **outToken,
252 PRUint32 *outTokenLen)
254 if (!inToken) {
255 /* someone wants our initial message */
256 *outToken = nsMemory::Clone(mInitialMessage, mInitialMessageLen);
257 if (!*outToken)
258 return NS_ERROR_OUT_OF_MEMORY;
259 *outTokenLen = mInitialMessageLen;
260 return NS_OK;
263 /* inToken must be a type 2 message. Get ntlm_auth to generate our response */
264 char* encoded = PL_Base64Encode(static_cast<const char*>(inToken), inTokenLen, nsnull);
265 if (!encoded)
266 return NS_ERROR_OUT_OF_MEMORY;
268 nsCString request;
269 request.AssignLiteral("TT ");
270 request.Append(encoded);
271 free(encoded);
272 request.Append('\n');
274 if (!WriteString(mToChildFD, request))
275 return NS_ERROR_FAILURE;
276 nsCString line;
277 if (!ReadLine(mFromChildFD, line))
278 return NS_ERROR_FAILURE;
279 if (!StringBeginsWith(line, NS_LITERAL_CSTRING("KK "))) {
280 // Something went wrong. Perhaps no credentials are accessible.
281 return NS_ERROR_FAILURE;
283 PRUint8* buf = ExtractMessage(line, outTokenLen);
284 if (!buf)
285 return NS_ERROR_FAILURE;
286 // *outToken has to be freed by nsMemory::Free, which may not be free()
287 *outToken = nsMemory::Clone(buf, *outTokenLen);
288 if (!*outToken) {
289 free(buf);
290 return NS_ERROR_OUT_OF_MEMORY;
293 // We're done. Close our file descriptors now and reap the helper
294 // process.
295 Shutdown();
296 return NS_SUCCESS_AUTH_FINISHED;
299 NS_IMETHODIMP
300 nsAuthSambaNTLM::Unwrap(const void *inToken,
301 PRUint32 inTokenLen,
302 void **outToken,
303 PRUint32 *outTokenLen)
305 return NS_ERROR_NOT_IMPLEMENTED;
308 NS_IMETHODIMP
309 nsAuthSambaNTLM::Wrap(const void *inToken,
310 PRUint32 inTokenLen,
311 PRBool confidential,
312 void **outToken,
313 PRUint32 *outTokenLen)
315 return NS_ERROR_NOT_IMPLEMENTED;