Bug 458256. Use LoadLibraryW instead of LoadLibrary (patch by DougT). r+sr=vlad
[wine-gecko.git] / netwerk / protocol / http / src / nsHttpNTLMAuth.cpp
blobb95255c8e346e762df1320e65b6caf6a29fa3cef
1 /* vim:set ts=4 sw=4 sts=4 et ci: */
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 Mozilla.
17 * The Initial Developer of the Original Code is
18 * Netscape Communications Corporation.
19 * Portions created by the Initial Developer are Copyright (C) 2003
20 * the Initial Developer. All Rights Reserved.
22 * Contributor(s):
23 * Darin Fisher <darin@meer.net>
25 * Alternatively, the contents of this file may be used under the terms of
26 * either the GNU General Public License Version 2 or later (the "GPL"), or
27 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 * in which case the provisions of the GPL or the LGPL are applicable instead
29 * of those above. If you wish to allow use of your version of this file only
30 * under the terms of either the GPL or the LGPL, and not to allow others to
31 * use your version of this file under the terms of the MPL, indicate your
32 * decision by deleting the provisions above and replace them with the notice
33 * and other provisions required by the GPL or the LGPL. If you do not delete
34 * the provisions above, a recipient may use your version of this file under
35 * the terms of any one of the MPL, the GPL or the LGPL.
37 * ***** END LICENSE BLOCK ***** */
39 #include <stdlib.h>
40 #include "nsHttp.h"
41 #include "nsHttpNTLMAuth.h"
42 #include "nsIComponentManager.h"
43 #include "nsIAuthModule.h"
44 #include "nsCOMPtr.h"
45 #include "plbase64.h"
47 //-----------------------------------------------------------------------------
49 #include "nsIPrefBranch.h"
50 #include "nsIPrefService.h"
51 #include "nsIServiceManager.h"
52 #include "nsIHttpChannel.h"
53 #include "nsIURI.h"
55 static const char kAllowProxies[] = "network.automatic-ntlm-auth.allow-proxies";
56 static const char kTrustedURIs[] = "network.automatic-ntlm-auth.trusted-uris";
58 // XXX MatchesBaseURI and TestPref are duplicated in nsHttpNegotiateAuth.cpp,
59 // but since that file lives in a separate library we cannot directly share it.
60 // bug 236865 addresses this problem.
62 static PRBool
63 MatchesBaseURI(const nsCSubstring &matchScheme,
64 const nsCSubstring &matchHost,
65 PRInt32 matchPort,
66 const char *baseStart,
67 const char *baseEnd)
69 // check if scheme://host:port matches baseURI
71 // parse the base URI
72 const char *hostStart, *schemeEnd = strstr(baseStart, "://");
73 if (schemeEnd) {
74 // the given scheme must match the parsed scheme exactly
75 if (!matchScheme.Equals(Substring(baseStart, schemeEnd)))
76 return PR_FALSE;
77 hostStart = schemeEnd + 3;
79 else
80 hostStart = baseStart;
82 // XXX this does not work for IPv6-literals
83 const char *hostEnd = strchr(hostStart, ':');
84 if (hostEnd && hostEnd < baseEnd) {
85 // the given port must match the parsed port exactly
86 int port = atoi(hostEnd + 1);
87 if (matchPort != (PRInt32) port)
88 return PR_FALSE;
90 else
91 hostEnd = baseEnd;
94 // if we didn't parse out a host, then assume we got a match.
95 if (hostStart == hostEnd)
96 return PR_TRUE;
98 PRUint32 hostLen = hostEnd - hostStart;
100 // matchHost must either equal host or be a subdomain of host
101 if (matchHost.Length() < hostLen)
102 return PR_FALSE;
104 const char *end = matchHost.EndReading();
105 if (PL_strncasecmp(end - hostLen, hostStart, hostLen) == 0) {
106 // if matchHost ends with host from the base URI, then make sure it is
107 // either an exact match, or prefixed with a dot. we don't want
108 // "foobar.com" to match "bar.com"
109 if (matchHost.Length() == hostLen ||
110 *(end - hostLen) == '.' ||
111 *(end - hostLen - 1) == '.')
112 return PR_TRUE;
115 return PR_FALSE;
118 static PRBool
119 TestPref(nsIURI *uri, const char *pref)
121 nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
122 if (!prefs)
123 return PR_FALSE;
125 nsCAutoString scheme, host;
126 PRInt32 port;
128 if (NS_FAILED(uri->GetScheme(scheme)))
129 return PR_FALSE;
130 if (NS_FAILED(uri->GetAsciiHost(host)))
131 return PR_FALSE;
132 if (NS_FAILED(uri->GetPort(&port)))
133 return PR_FALSE;
135 char *hostList;
136 if (NS_FAILED(prefs->GetCharPref(pref, &hostList)) || !hostList)
137 return PR_FALSE;
139 // pseudo-BNF
140 // ----------
142 // url-list base-url ( base-url "," LWS )*
143 // base-url ( scheme-part | host-part | scheme-part host-part )
144 // scheme-part scheme "://"
145 // host-part host [":" port]
147 // for example:
148 // "https://, http://office.foo.com"
151 char *start = hostList, *end;
152 for (;;) {
153 // skip past any whitespace
154 while (*start == ' ' || *start == '\t')
155 ++start;
156 end = strchr(start, ',');
157 if (!end)
158 end = start + strlen(start);
159 if (start == end)
160 break;
161 if (MatchesBaseURI(scheme, host, port, start, end))
162 return PR_TRUE;
163 if (*end == '\0')
164 break;
165 start = end + 1;
168 nsMemory::Free(hostList);
169 return PR_FALSE;
172 static PRBool
173 CanUseSysNTLM(nsIHttpChannel *channel, PRBool isProxyAuth)
175 // check prefs
177 nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
178 if (!prefs)
179 return PR_FALSE;
181 PRBool val;
182 if (isProxyAuth) {
183 if (NS_FAILED(prefs->GetBoolPref(kAllowProxies, &val)))
184 val = PR_FALSE;
185 LOG(("sys-ntlm allowed for proxy: %d\n", val));
186 return val;
188 else {
189 nsCOMPtr<nsIURI> uri;
190 channel->GetURI(getter_AddRefs(uri));
191 if (uri && TestPref(uri, kTrustedURIs)) {
192 LOG(("sys-ntlm allowed for host\n"));
193 return PR_TRUE;
197 return PR_FALSE;
200 // Dummy class for session state object. This class doesn't hold any data.
201 // Instead we use its existance as a flag. See ChallengeReceived.
202 class nsNTLMSessionState : public nsISupports
204 public:
205 NS_DECL_ISUPPORTS
207 NS_IMPL_ISUPPORTS0(nsNTLMSessionState)
209 //-----------------------------------------------------------------------------
211 NS_IMPL_ISUPPORTS1(nsHttpNTLMAuth, nsIHttpAuthenticator)
213 NS_IMETHODIMP
214 nsHttpNTLMAuth::ChallengeReceived(nsIHttpChannel *channel,
215 const char *challenge,
216 PRBool isProxyAuth,
217 nsISupports **sessionState,
218 nsISupports **continuationState,
219 PRBool *identityInvalid)
221 LOG(("nsHttpNTLMAuth::ChallengeReceived [ss=%p cs=%p]\n",
222 *sessionState, *continuationState));
224 // NOTE: we don't define any session state
226 *identityInvalid = PR_FALSE;
227 // start new auth sequence if challenge is exactly "NTLM"
228 if (PL_strcasecmp(challenge, "NTLM") == 0) {
229 nsCOMPtr<nsISupports> module;
231 // our session state is non-null to indicate that we've flagged
232 // this auth domain as not accepting the system's default login.
234 PRBool trySysNTLM = (*sessionState == nsnull);
237 // we may have access to a built-in SSPI library,
238 // which could be used to authenticate the user without prompting.
240 // if the continuationState is null, then we may want to try using
241 // the SSPI NTLM module. however, we need to take care to only use
242 // that module when speaking to a trusted host. because the SSPI
243 // may send a weak LMv1 hash of the user's password, we cannot just
244 // send it to any server.
246 if (trySysNTLM && !*continuationState && CanUseSysNTLM(channel, isProxyAuth)) {
247 module = do_CreateInstance(NS_AUTH_MODULE_CONTRACTID_PREFIX "sys-ntlm");
248 #ifdef PR_LOGGING
249 if (!module)
250 LOG(("failed to load sys-ntlm module\n"));
251 #endif
254 // it's possible that there is no ntlm-sspi auth module...
255 if (!module) {
256 if (!*sessionState) {
257 // remember the fact that we cannot use the "sys-ntlm" module,
258 // so we don't ever bother trying again for this auth domain.
259 *sessionState = new nsNTLMSessionState();
260 if (!*sessionState)
261 return NS_ERROR_OUT_OF_MEMORY;
262 NS_ADDREF(*sessionState);
265 module = do_CreateInstance(NS_AUTH_MODULE_CONTRACTID_PREFIX "ntlm");
267 // prompt user for domain, username, and password...
268 *identityInvalid = PR_TRUE;
271 // if this fails, then it means that we cannot do NTLM auth.
272 if (!module)
273 return NS_ERROR_UNEXPECTED;
275 // non-null continuation state implies that we failed to authenticate.
276 // blow away the old authentication state, and use the new one.
277 module.swap(*continuationState);
279 return NS_OK;
282 NS_IMETHODIMP
283 nsHttpNTLMAuth::GenerateCredentials(nsIHttpChannel *httpChannel,
284 const char *challenge,
285 PRBool isProxyAuth,
286 const PRUnichar *domain,
287 const PRUnichar *user,
288 const PRUnichar *pass,
289 nsISupports **sessionState,
290 nsISupports **continuationState,
291 char **creds)
294 LOG(("nsHttpNTLMAuth::GenerateCredentials\n"));
296 *creds = nsnull;
298 nsresult rv;
299 nsCOMPtr<nsIAuthModule> module = do_QueryInterface(*continuationState, &rv);
300 NS_ENSURE_SUCCESS(rv, rv);
302 void *inBuf, *outBuf;
303 PRUint32 inBufLen, outBufLen;
305 // initial challenge
306 if (PL_strcasecmp(challenge, "NTLM") == 0) {
307 // initialize auth module
308 rv = module->Init(nsnull, nsIAuthModule::REQ_DEFAULT, domain, user, pass);
309 if (NS_FAILED(rv))
310 return rv;
312 inBufLen = 0;
313 inBuf = nsnull;
315 else {
316 // decode challenge; skip past "NTLM " to the start of the base64
317 // encoded data.
318 int len = strlen(challenge);
319 if (len < 6)
320 return NS_ERROR_UNEXPECTED; // bogus challenge
321 challenge += 5;
322 len -= 5;
324 // decode into the input secbuffer
325 inBufLen = (len * 3)/4; // sufficient size (see plbase64.h)
326 inBuf = nsMemory::Alloc(inBufLen);
327 if (!inBuf)
328 return NS_ERROR_OUT_OF_MEMORY;
330 // strip off any padding (see bug 230351)
331 while (challenge[len - 1] == '=')
332 len--;
334 if (PL_Base64Decode(challenge, len, (char *) inBuf) == nsnull) {
335 nsMemory::Free(inBuf);
336 return NS_ERROR_UNEXPECTED; // improper base64 encoding
340 rv = module->GetNextToken(inBuf, inBufLen, &outBuf, &outBufLen);
341 if (NS_SUCCEEDED(rv)) {
342 // base64 encode data in output buffer and prepend "NTLM "
343 int credsLen = 5 + ((outBufLen + 2)/3)*4;
344 *creds = (char *) nsMemory::Alloc(credsLen + 1);
345 if (!*creds)
346 rv = NS_ERROR_OUT_OF_MEMORY;
347 else {
348 memcpy(*creds, "NTLM ", 5);
349 PL_Base64Encode((char *) outBuf, outBufLen, *creds + 5);
350 (*creds)[credsLen] = '\0'; // null terminate
352 // OK, we are done with |outBuf|
353 nsMemory::Free(outBuf);
356 if (inBuf)
357 nsMemory::Free(inBuf);
359 return rv;
362 NS_IMETHODIMP
363 nsHttpNTLMAuth::GetAuthFlags(PRUint32 *flags)
365 *flags = CONNECTION_BASED | IDENTITY_INCLUDES_DOMAIN | IDENTITY_ENCRYPTED;
366 return NS_OK;