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
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.
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 ***** */
39 #include "nsAuthSambaNTLM.h"
46 nsAuthSambaNTLM::nsAuthSambaNTLM()
47 : mInitialMessage(nsnull
), mChildPID(nsnull
), mFromChildFD(nsnull
),
52 nsAuthSambaNTLM::~nsAuthSambaNTLM()
54 // ntlm_auth reads from stdin regularly so closing our file handles
55 // should cause it to exit.
57 free(mInitialMessage
);
61 nsAuthSambaNTLM::Shutdown()
64 PR_Close(mFromChildFD
);
65 mFromChildFD
= nsnull
;
73 PR_WaitProcess(mChildPID
, &exitCode
);
78 NS_IMPL_ISUPPORTS1(nsAuthSambaNTLM
, nsIAuthModule
)
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
)
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
);
98 PR_SetFDInheritable(fromChildPipeRead
, PR_FALSE
);
99 PR_SetFDInheritable(fromChildPipeWrite
, PR_TRUE
);
101 PRProcessAttr
* attr
= PR_NewProcessAttr();
103 PR_Close(fromChildPipeRead
);
104 PR_Close(fromChildPipeWrite
);
105 PR_Close(toChildPipeRead
);
106 PR_Close(toChildPipeWrite
);
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
);
118 LOG(("ntlm_auth exec failure [%d]", PR_GetError()));
119 PR_Close(fromChildPipeRead
);
120 PR_Close(toChildPipeWrite
);
125 *aFromChildFD
= fromChildPipeRead
;
126 *aToChildFD
= toChildPipeWrite
;
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
));
137 int result
= PR_Write(aFD
, s
, length
);
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.
154 int result
= PR_Read(aFD
, buf
, sizeof(buf
));
157 aString
.Append(buf
, result
);
158 if (buf
[result
- 1] == '\n') {
159 LOG(("Read from ntlm_auth: %s", nsPromiseFlatCString(aString
).get()));
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");
183 // The base64 encoded block must be multiple of 4. If not, something
185 NS_WARNING("Base64 encoded block should be a multiple of 4 chars");
189 // Calculate the exact length. I wonder why there isn't a function for this
192 for (numEquals
= 0; numEquals
< length
; ++numEquals
) {
193 if (s
[length
- 1 - numEquals
] != '=')
196 *aLen
= (length
/4)*3 - numEquals
;
197 return reinterpret_cast<PRUint8
*>(PL_Base64Decode(s
, length
, nsnull
));
201 nsAuthSambaNTLM::SpawnNTLMAuthHelper()
203 const char* username
= PR_GetEnv("USER");
205 return NS_ERROR_FAILURE
;
209 "--helper-protocol", "ntlmssp-client-1",
210 "--use-cached-creds",
211 "--username", const_cast<char*>(username
),
215 PRBool isOK
= SpawnIOChild(args
, &mChildPID
, &mFromChildFD
, &mToChildFD
);
217 return NS_ERROR_FAILURE
;
219 if (!WriteString(mToChildFD
, NS_LITERAL_CSTRING("YR\n")))
220 return NS_ERROR_FAILURE
;
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
;
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");
249 nsAuthSambaNTLM::GetNextToken(const void *inToken
,
252 PRUint32
*outTokenLen
)
255 /* someone wants our initial message */
256 *outToken
= nsMemory::Clone(mInitialMessage
, mInitialMessageLen
);
258 return NS_ERROR_OUT_OF_MEMORY
;
259 *outTokenLen
= mInitialMessageLen
;
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
);
266 return NS_ERROR_OUT_OF_MEMORY
;
269 request
.AssignLiteral("TT ");
270 request
.Append(encoded
);
272 request
.Append('\n');
274 if (!WriteString(mToChildFD
, request
))
275 return NS_ERROR_FAILURE
;
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
);
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
);
290 return NS_ERROR_OUT_OF_MEMORY
;
293 // We're done. Close our file descriptors now and reap the helper
296 return NS_SUCCESS_AUTH_FINISHED
;
300 nsAuthSambaNTLM::Unwrap(const void *inToken
,
303 PRUint32
*outTokenLen
)
305 return NS_ERROR_NOT_IMPLEMENTED
;
309 nsAuthSambaNTLM::Wrap(const void *inToken
,
313 PRUint32
*outTokenLen
)
315 return NS_ERROR_NOT_IMPLEMENTED
;