PageRenderTime 25ms CodeModel.GetById 26ms RepoModel.GetById 1ms app.codeStats 0ms

/server/src/mozilla/netwerk/protocol/http/src/nsHttpNTLMAuth.cpp

https://github.com/aptana/Jaxer
C++ | 367 lines | 218 code | 51 blank | 98 comment | 63 complexity | c4b4d85066668dc89a3988b49df2c8e4 MD5 | raw file
Possible License(s): Apache-2.0, BSD-3-Clause, GPL-2.0, MPL-2.0-no-copyleft-exception, LGPL-3.0
  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
  4. *
  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/
  9. *
  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.
  14. *
  15. * The Original Code is Mozilla.
  16. *
  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.
  21. *
  22. * Contributor(s):
  23. * Darin Fisher <darin@meer.net>
  24. *
  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.
  36. *
  37. * ***** END LICENSE BLOCK ***** */
  38. #include <stdlib.h>
  39. #include "nsHttp.h"
  40. #include "nsHttpNTLMAuth.h"
  41. #include "nsIComponentManager.h"
  42. #include "nsIAuthModule.h"
  43. #include "nsCOMPtr.h"
  44. #include "plbase64.h"
  45. //-----------------------------------------------------------------------------
  46. #include "nsIPrefBranch.h"
  47. #include "nsIPrefService.h"
  48. #include "nsIServiceManager.h"
  49. #include "nsIHttpChannel.h"
  50. #include "nsIURI.h"
  51. static const char kAllowProxies[] = "network.automatic-ntlm-auth.allow-proxies";
  52. static const char kTrustedURIs[] = "network.automatic-ntlm-auth.trusted-uris";
  53. // XXX MatchesBaseURI and TestPref are duplicated in nsHttpNegotiateAuth.cpp,
  54. // but since that file lives in a separate library we cannot directly share it.
  55. // bug 236865 addresses this problem.
  56. static PRBool
  57. MatchesBaseURI(const nsCSubstring &matchScheme,
  58. const nsCSubstring &matchHost,
  59. PRInt32 matchPort,
  60. const char *baseStart,
  61. const char *baseEnd)
  62. {
  63. // check if scheme://host:port matches baseURI
  64. // parse the base URI
  65. const char *hostStart, *schemeEnd = strstr(baseStart, "://");
  66. if (schemeEnd) {
  67. // the given scheme must match the parsed scheme exactly
  68. if (!matchScheme.Equals(Substring(baseStart, schemeEnd)))
  69. return PR_FALSE;
  70. hostStart = schemeEnd + 3;
  71. }
  72. else
  73. hostStart = baseStart;
  74. // XXX this does not work for IPv6-literals
  75. const char *hostEnd = strchr(hostStart, ':');
  76. if (hostEnd && hostEnd < baseEnd) {
  77. // the given port must match the parsed port exactly
  78. int port = atoi(hostEnd + 1);
  79. if (matchPort != (PRInt32) port)
  80. return PR_FALSE;
  81. }
  82. else
  83. hostEnd = baseEnd;
  84. // if we didn't parse out a host, then assume we got a match.
  85. if (hostStart == hostEnd)
  86. return PR_TRUE;
  87. PRUint32 hostLen = hostEnd - hostStart;
  88. // matchHost must either equal host or be a subdomain of host
  89. if (matchHost.Length() < hostLen)
  90. return PR_FALSE;
  91. const char *end = matchHost.EndReading();
  92. if (PL_strncasecmp(end - hostLen, hostStart, hostLen) == 0) {
  93. // if matchHost ends with host from the base URI, then make sure it is
  94. // either an exact match, or prefixed with a dot. we don't want
  95. // "foobar.com" to match "bar.com"
  96. if (matchHost.Length() == hostLen ||
  97. *(end - hostLen) == '.' ||
  98. *(end - hostLen - 1) == '.')
  99. return PR_TRUE;
  100. }
  101. return PR_FALSE;
  102. }
  103. static PRBool
  104. TestPref(nsIURI *uri, const char *pref)
  105. {
  106. nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
  107. if (!prefs)
  108. return PR_FALSE;
  109. nsCAutoString scheme, host;
  110. PRInt32 port;
  111. if (NS_FAILED(uri->GetScheme(scheme)))
  112. return PR_FALSE;
  113. if (NS_FAILED(uri->GetAsciiHost(host)))
  114. return PR_FALSE;
  115. if (NS_FAILED(uri->GetPort(&port)))
  116. return PR_FALSE;
  117. char *hostList;
  118. if (NS_FAILED(prefs->GetCharPref(pref, &hostList)) || !hostList)
  119. return PR_FALSE;
  120. // pseudo-BNF
  121. // ----------
  122. //
  123. // url-list base-url ( base-url "," LWS )*
  124. // base-url ( scheme-part | host-part | scheme-part host-part )
  125. // scheme-part scheme "://"
  126. // host-part host [":" port]
  127. //
  128. // for example:
  129. // "https://, http://office.foo.com"
  130. //
  131. char *start = hostList, *end;
  132. for (;;) {
  133. // skip past any whitespace
  134. while (*start == ' ' || *start == '\t')
  135. ++start;
  136. end = strchr(start, ',');
  137. if (!end)
  138. end = start + strlen(start);
  139. if (start == end)
  140. break;
  141. if (MatchesBaseURI(scheme, host, port, start, end))
  142. return PR_TRUE;
  143. if (*end == '\0')
  144. break;
  145. start = end + 1;
  146. }
  147. nsMemory::Free(hostList);
  148. return PR_FALSE;
  149. }
  150. static PRBool
  151. CanUseSysNTLM(nsIHttpChannel *channel, PRBool isProxyAuth)
  152. {
  153. // check prefs
  154. nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
  155. if (!prefs)
  156. return PR_FALSE;
  157. PRBool val;
  158. if (isProxyAuth) {
  159. if (NS_FAILED(prefs->GetBoolPref(kAllowProxies, &val)))
  160. val = PR_FALSE;
  161. LOG(("sys-ntlm allowed for proxy: %d\n", val));
  162. return val;
  163. }
  164. else {
  165. nsCOMPtr<nsIURI> uri;
  166. channel->GetURI(getter_AddRefs(uri));
  167. if (uri && TestPref(uri, kTrustedURIs)) {
  168. LOG(("sys-ntlm allowed for host\n"));
  169. return PR_TRUE;
  170. }
  171. }
  172. return PR_FALSE;
  173. }
  174. // Dummy class for session state object. This class doesn't hold any data.
  175. // Instead we use its existance as a flag. See ChallengeReceived.
  176. class nsNTLMSessionState : public nsISupports
  177. {
  178. public:
  179. NS_DECL_ISUPPORTS
  180. };
  181. NS_IMPL_ISUPPORTS0(nsNTLMSessionState)
  182. //-----------------------------------------------------------------------------
  183. NS_IMPL_ISUPPORTS1(nsHttpNTLMAuth, nsIHttpAuthenticator)
  184. NS_IMETHODIMP
  185. nsHttpNTLMAuth::ChallengeReceived(nsIHttpChannel *channel,
  186. const char *challenge,
  187. PRBool isProxyAuth,
  188. nsISupports **sessionState,
  189. nsISupports **continuationState,
  190. PRBool *identityInvalid)
  191. {
  192. LOG(("nsHttpNTLMAuth::ChallengeReceived [ss=%p cs=%p]\n",
  193. *sessionState, *continuationState));
  194. // NOTE: we don't define any session state
  195. *identityInvalid = PR_FALSE;
  196. // start new auth sequence if challenge is exactly "NTLM"
  197. if (PL_strcasecmp(challenge, "NTLM") == 0) {
  198. nsCOMPtr<nsISupports> module;
  199. //
  200. // our session state is non-null to indicate that we've flagged
  201. // this auth domain as not accepting the system's default login.
  202. //
  203. PRBool trySysNTLM = (*sessionState == nsnull);
  204. //
  205. // we may have access to a built-in SSPI library,
  206. // which could be used to authenticate the user without prompting.
  207. //
  208. // if the continuationState is null, then we may want to try using
  209. // the SSPI NTLM module. however, we need to take care to only use
  210. // that module when speaking to a trusted host. because the SSPI
  211. // may send a weak LMv1 hash of the user's password, we cannot just
  212. // send it to any server.
  213. //
  214. if (trySysNTLM && !*continuationState && CanUseSysNTLM(channel, isProxyAuth)) {
  215. module = do_CreateInstance(NS_AUTH_MODULE_CONTRACTID_PREFIX "sys-ntlm");
  216. #ifdef PR_LOGGING
  217. if (!module)
  218. LOG(("failed to load sys-ntlm module\n"));
  219. #endif
  220. }
  221. // it's possible that there is no ntlm-sspi auth module...
  222. if (!module) {
  223. if (!*sessionState) {
  224. // remember the fact that we cannot use the "sys-ntlm" module,
  225. // so we don't ever bother trying again for this auth domain.
  226. *sessionState = new nsNTLMSessionState();
  227. if (!*sessionState)
  228. return NS_ERROR_OUT_OF_MEMORY;
  229. NS_ADDREF(*sessionState);
  230. }
  231. module = do_CreateInstance(NS_AUTH_MODULE_CONTRACTID_PREFIX "ntlm");
  232. // prompt user for domain, username, and password...
  233. *identityInvalid = PR_TRUE;
  234. }
  235. // if this fails, then it means that we cannot do NTLM auth.
  236. if (!module)
  237. return NS_ERROR_UNEXPECTED;
  238. // non-null continuation state implies that we failed to authenticate.
  239. // blow away the old authentication state, and use the new one.
  240. module.swap(*continuationState);
  241. }
  242. return NS_OK;
  243. }
  244. NS_IMETHODIMP
  245. nsHttpNTLMAuth::GenerateCredentials(nsIHttpChannel *httpChannel,
  246. const char *challenge,
  247. PRBool isProxyAuth,
  248. const PRUnichar *domain,
  249. const PRUnichar *user,
  250. const PRUnichar *pass,
  251. nsISupports **sessionState,
  252. nsISupports **continuationState,
  253. char **creds)
  254. {
  255. LOG(("nsHttpNTLMAuth::GenerateCredentials\n"));
  256. *creds = nsnull;
  257. nsresult rv;
  258. nsCOMPtr<nsIAuthModule> module = do_QueryInterface(*continuationState, &rv);
  259. NS_ENSURE_SUCCESS(rv, rv);
  260. void *inBuf, *outBuf;
  261. PRUint32 inBufLen, outBufLen;
  262. // initial challenge
  263. if (PL_strcasecmp(challenge, "NTLM") == 0) {
  264. // initialize auth module
  265. rv = module->Init(nsnull, nsIAuthModule::REQ_DEFAULT, domain, user, pass);
  266. if (NS_FAILED(rv))
  267. return rv;
  268. inBufLen = 0;
  269. inBuf = nsnull;
  270. }
  271. else {
  272. // decode challenge; skip past "NTLM " to the start of the base64
  273. // encoded data.
  274. int len = strlen(challenge);
  275. if (len < 6)
  276. return NS_ERROR_UNEXPECTED; // bogus challenge
  277. challenge += 5;
  278. len -= 5;
  279. // decode into the input secbuffer
  280. inBufLen = (len * 3)/4; // sufficient size (see plbase64.h)
  281. inBuf = nsMemory::Alloc(inBufLen);
  282. if (!inBuf)
  283. return NS_ERROR_OUT_OF_MEMORY;
  284. // strip off any padding (see bug 230351)
  285. while (challenge[len - 1] == '=')
  286. len--;
  287. if (PL_Base64Decode(challenge, len, (char *) inBuf) == nsnull) {
  288. nsMemory::Free(inBuf);
  289. return NS_ERROR_UNEXPECTED; // improper base64 encoding
  290. }
  291. }
  292. rv = module->GetNextToken(inBuf, inBufLen, &outBuf, &outBufLen);
  293. if (NS_SUCCEEDED(rv)) {
  294. // base64 encode data in output buffer and prepend "NTLM "
  295. int credsLen = 5 + ((outBufLen + 2)/3)*4;
  296. *creds = (char *) nsMemory::Alloc(credsLen + 1);
  297. if (!*creds)
  298. rv = NS_ERROR_OUT_OF_MEMORY;
  299. else {
  300. memcpy(*creds, "NTLM ", 5);
  301. PL_Base64Encode((char *) outBuf, outBufLen, *creds + 5);
  302. (*creds)[credsLen] = '\0'; // null terminate
  303. }
  304. // OK, we are done with |outBuf|
  305. nsMemory::Free(outBuf);
  306. }
  307. if (inBuf)
  308. nsMemory::Free(inBuf);
  309. return rv;
  310. }
  311. NS_IMETHODIMP
  312. nsHttpNTLMAuth::GetAuthFlags(PRUint32 *flags)
  313. {
  314. *flags = CONNECTION_BASED | IDENTITY_INCLUDES_DOMAIN | IDENTITY_ENCRYPTED;
  315. return NS_OK;
  316. }