http_auth_handler_negotiate.cc 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415
  1. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "net/http/http_auth_handler_negotiate.h"
  5. #include <utility>
  6. #include "base/bind.h"
  7. #include "base/callback_helpers.h"
  8. #include "base/check_op.h"
  9. #include "base/logging.h"
  10. #include "base/strings/string_number_conversions.h"
  11. #include "base/strings/string_util.h"
  12. #include "base/strings/stringprintf.h"
  13. #include "base/values.h"
  14. #include "build/build_config.h"
  15. #include "build/chromeos_buildflags.h"
  16. #include "net/base/address_family.h"
  17. #include "net/base/address_list.h"
  18. #include "net/base/host_port_pair.h"
  19. #include "net/base/net_errors.h"
  20. #include "net/cert/x509_util.h"
  21. #include "net/dns/host_resolver.h"
  22. #include "net/http/http_auth.h"
  23. #include "net/http/http_auth_filter.h"
  24. #include "net/http/http_auth_preferences.h"
  25. #include "net/log/net_log_capture_mode.h"
  26. #include "net/log/net_log_event_type.h"
  27. #include "net/log/net_log_with_source.h"
  28. #include "net/ssl/ssl_info.h"
  29. #include "url/scheme_host_port.h"
  30. namespace net {
  31. using DelegationType = HttpAuth::DelegationType;
  32. namespace {
  33. base::Value NetLogParameterChannelBindings(
  34. const std::string& channel_binding_token,
  35. NetLogCaptureMode capture_mode) {
  36. base::Value::Dict dict;
  37. if (!NetLogCaptureIncludesSocketBytes(capture_mode))
  38. return base::Value(std::move(dict));
  39. dict.Set("token", base::HexEncode(channel_binding_token.data(),
  40. channel_binding_token.size()));
  41. return base::Value(std::move(dict));
  42. }
  43. // Uses |negotiate_auth_system_factory| to create the auth system, otherwise
  44. // creates the default auth system for each platform.
  45. std::unique_ptr<HttpAuthMechanism> CreateAuthSystem(
  46. #if !BUILDFLAG(IS_ANDROID)
  47. HttpAuthHandlerNegotiate::AuthLibrary* auth_library,
  48. #endif
  49. const HttpAuthPreferences* prefs,
  50. HttpAuthMechanismFactory negotiate_auth_system_factory) {
  51. if (negotiate_auth_system_factory)
  52. return negotiate_auth_system_factory.Run(prefs);
  53. #if BUILDFLAG(IS_ANDROID)
  54. return std::make_unique<net::android::HttpAuthNegotiateAndroid>(prefs);
  55. #elif BUILDFLAG(IS_WIN)
  56. return std::make_unique<HttpAuthSSPI>(auth_library,
  57. HttpAuth::AUTH_SCHEME_NEGOTIATE);
  58. #elif BUILDFLAG(IS_POSIX)
  59. return std::make_unique<HttpAuthGSSAPI>(auth_library,
  60. CHROME_GSS_SPNEGO_MECH_OID_DESC);
  61. #endif
  62. }
  63. } // namespace
  64. HttpAuthHandlerNegotiate::Factory::Factory(
  65. HttpAuthMechanismFactory negotiate_auth_system_factory)
  66. : negotiate_auth_system_factory_(negotiate_auth_system_factory) {}
  67. HttpAuthHandlerNegotiate::Factory::~Factory() = default;
  68. #if !BUILDFLAG(IS_ANDROID) && BUILDFLAG(IS_POSIX)
  69. const std::string& HttpAuthHandlerNegotiate::Factory::GetLibraryNameForTesting()
  70. const {
  71. return auth_library_->GetLibraryNameForTesting();
  72. }
  73. #endif // !BUILDFLAG(IS_ANDROID) && BUILDFLAG(IS_POSIX)
  74. int HttpAuthHandlerNegotiate::Factory::CreateAuthHandler(
  75. HttpAuthChallengeTokenizer* challenge,
  76. HttpAuth::Target target,
  77. const SSLInfo& ssl_info,
  78. const NetworkIsolationKey& network_isolation_key,
  79. const url::SchemeHostPort& scheme_host_port,
  80. CreateReason reason,
  81. int digest_nonce_count,
  82. const NetLogWithSource& net_log,
  83. HostResolver* host_resolver,
  84. std::unique_ptr<HttpAuthHandler>* handler) {
  85. #if BUILDFLAG(IS_WIN)
  86. if (is_unsupported_ || reason == CREATE_PREEMPTIVE)
  87. return ERR_UNSUPPORTED_AUTH_SCHEME;
  88. // TODO(cbentzel): Move towards model of parsing in the factory
  89. // method and only constructing when valid.
  90. std::unique_ptr<HttpAuthHandler> tmp_handler(
  91. std::make_unique<HttpAuthHandlerNegotiate>(
  92. CreateAuthSystem(auth_library_.get(), http_auth_preferences(),
  93. negotiate_auth_system_factory_),
  94. http_auth_preferences(), host_resolver));
  95. #elif BUILDFLAG(IS_ANDROID)
  96. if (is_unsupported_ || !http_auth_preferences() ||
  97. http_auth_preferences()->AuthAndroidNegotiateAccountType().empty() ||
  98. reason == CREATE_PREEMPTIVE)
  99. return ERR_UNSUPPORTED_AUTH_SCHEME;
  100. // TODO(cbentzel): Move towards model of parsing in the factory
  101. // method and only constructing when valid.
  102. std::unique_ptr<HttpAuthHandler> tmp_handler(
  103. std::make_unique<HttpAuthHandlerNegotiate>(
  104. CreateAuthSystem(http_auth_preferences(),
  105. negotiate_auth_system_factory_),
  106. http_auth_preferences(), host_resolver));
  107. #elif BUILDFLAG(IS_POSIX)
  108. if (is_unsupported_)
  109. return ERR_UNSUPPORTED_AUTH_SCHEME;
  110. #if BUILDFLAG(IS_CHROMEOS)
  111. // Note: Don't set is_unsupported_ = true here. AllowGssapiLibraryLoad()
  112. // might change to true during a session.
  113. if (!http_auth_preferences()->AllowGssapiLibraryLoad())
  114. return ERR_UNSUPPORTED_AUTH_SCHEME;
  115. #endif // BUILDFLAG(IS_CHROMEOS)
  116. if (!auth_library_->Init(net_log)) {
  117. is_unsupported_ = true;
  118. return ERR_UNSUPPORTED_AUTH_SCHEME;
  119. }
  120. // TODO(ahendrickson): Move towards model of parsing in the factory
  121. // method and only constructing when valid.
  122. std::unique_ptr<HttpAuthHandler> tmp_handler(
  123. std::make_unique<HttpAuthHandlerNegotiate>(
  124. CreateAuthSystem(auth_library_.get(), http_auth_preferences(),
  125. negotiate_auth_system_factory_),
  126. http_auth_preferences(), host_resolver));
  127. #endif
  128. if (!tmp_handler->InitFromChallenge(challenge, target, ssl_info,
  129. network_isolation_key, scheme_host_port,
  130. net_log)) {
  131. return ERR_INVALID_RESPONSE;
  132. }
  133. handler->swap(tmp_handler);
  134. return OK;
  135. }
  136. HttpAuthHandlerNegotiate::HttpAuthHandlerNegotiate(
  137. std::unique_ptr<HttpAuthMechanism> auth_system,
  138. const HttpAuthPreferences* prefs,
  139. HostResolver* resolver)
  140. : auth_system_(std::move(auth_system)),
  141. resolver_(resolver),
  142. http_auth_preferences_(prefs) {}
  143. HttpAuthHandlerNegotiate::~HttpAuthHandlerNegotiate() = default;
  144. // Require identity on first pass instead of second.
  145. bool HttpAuthHandlerNegotiate::NeedsIdentity() {
  146. return auth_system_->NeedsIdentity();
  147. }
  148. bool HttpAuthHandlerNegotiate::AllowsDefaultCredentials() {
  149. if (target_ == HttpAuth::AUTH_PROXY)
  150. return true;
  151. if (!http_auth_preferences_)
  152. return false;
  153. return http_auth_preferences_->CanUseDefaultCredentials(scheme_host_port_);
  154. }
  155. bool HttpAuthHandlerNegotiate::AllowsExplicitCredentials() {
  156. return auth_system_->AllowsExplicitCredentials();
  157. }
  158. // The Negotiate challenge header looks like:
  159. // WWW-Authenticate: NEGOTIATE auth-data
  160. bool HttpAuthHandlerNegotiate::Init(
  161. HttpAuthChallengeTokenizer* challenge,
  162. const SSLInfo& ssl_info,
  163. const NetworkIsolationKey& network_isolation_key) {
  164. network_isolation_key_ = network_isolation_key;
  165. #if BUILDFLAG(IS_POSIX)
  166. if (!auth_system_->Init(net_log())) {
  167. VLOG(1) << "can't initialize GSSAPI library";
  168. return false;
  169. }
  170. // GSSAPI does not provide a way to enter username/password to
  171. // obtain a TGT. If the default credentials are not allowed for
  172. // a particular site (based on allowlist), fall back to a
  173. // different scheme.
  174. if (!AllowsDefaultCredentials())
  175. return false;
  176. #endif
  177. auth_system_->SetDelegation(GetDelegationType());
  178. auth_scheme_ = HttpAuth::AUTH_SCHEME_NEGOTIATE;
  179. score_ = 4;
  180. properties_ = ENCRYPTS_IDENTITY | IS_CONNECTION_BASED;
  181. HttpAuth::AuthorizationResult auth_result =
  182. auth_system_->ParseChallenge(challenge);
  183. if (auth_result != HttpAuth::AUTHORIZATION_RESULT_ACCEPT)
  184. return false;
  185. // Try to extract channel bindings.
  186. if (ssl_info.is_valid())
  187. x509_util::GetTLSServerEndPointChannelBinding(*ssl_info.cert,
  188. &channel_bindings_);
  189. if (!channel_bindings_.empty())
  190. net_log().AddEvent(NetLogEventType::AUTH_CHANNEL_BINDINGS,
  191. [&](NetLogCaptureMode capture_mode) {
  192. return NetLogParameterChannelBindings(
  193. channel_bindings_, capture_mode);
  194. });
  195. return true;
  196. }
  197. int HttpAuthHandlerNegotiate::GenerateAuthTokenImpl(
  198. const AuthCredentials* credentials,
  199. const HttpRequestInfo* request,
  200. CompletionOnceCallback callback,
  201. std::string* auth_token) {
  202. DCHECK(callback_.is_null());
  203. DCHECK(auth_token_ == nullptr);
  204. auth_token_ = auth_token;
  205. if (already_called_) {
  206. DCHECK((!has_credentials_ && credentials == nullptr) ||
  207. (has_credentials_ && credentials->Equals(credentials_)));
  208. next_state_ = STATE_GENERATE_AUTH_TOKEN;
  209. } else {
  210. already_called_ = true;
  211. if (credentials) {
  212. has_credentials_ = true;
  213. credentials_ = *credentials;
  214. }
  215. next_state_ = STATE_RESOLVE_CANONICAL_NAME;
  216. }
  217. int rv = DoLoop(OK);
  218. if (rv == ERR_IO_PENDING)
  219. callback_ = std::move(callback);
  220. return rv;
  221. }
  222. HttpAuth::AuthorizationResult
  223. HttpAuthHandlerNegotiate::HandleAnotherChallengeImpl(
  224. HttpAuthChallengeTokenizer* challenge) {
  225. return auth_system_->ParseChallenge(challenge);
  226. }
  227. std::string HttpAuthHandlerNegotiate::CreateSPN(
  228. const std::string& server,
  229. const url::SchemeHostPort& scheme_host_port) {
  230. // Kerberos Web Server SPNs are in the form HTTP/<host>:<port> through SSPI,
  231. // and in the form HTTP@<host>:<port> through GSSAPI
  232. // http://msdn.microsoft.com/en-us/library/ms677601%28VS.85%29.aspx
  233. //
  234. // However, reality differs from the specification. A good description of
  235. // the problems can be found here:
  236. // http://blog.michelbarneveld.nl/michel/archive/2009/11/14/the-reason-why-kb911149-and-kb908209-are-not-the-soluton.aspx
  237. //
  238. // Typically the <host> portion should be the canonical FQDN for the service.
  239. // If this could not be resolved, the original hostname in the URL will be
  240. // attempted instead. However, some intranets register SPNs using aliases
  241. // for the same canonical DNS name to allow multiple web services to reside
  242. // on the same host machine without requiring different ports. IE6 and IE7
  243. // have hotpatches that allow the default behavior to be overridden.
  244. // http://support.microsoft.com/kb/911149
  245. // http://support.microsoft.com/kb/938305
  246. //
  247. // According to the spec, the <port> option should be included if it is a
  248. // non-standard port (i.e. not 80 or 443 in the HTTP case). However,
  249. // historically browsers have not included the port, even on non-standard
  250. // ports. IE6 required a hotpatch and a registry setting to enable
  251. // including non-standard ports, and IE7 and IE8 also require the same
  252. // registry setting, but no hotpatch. Firefox does not appear to have an
  253. // option to include non-standard ports as of 3.6.
  254. // http://support.microsoft.com/kb/908209
  255. //
  256. // Without any command-line flags, Chrome matches the behavior of Firefox
  257. // and IE. Users can override the behavior so aliases are allowed and
  258. // non-standard ports are included.
  259. int port = scheme_host_port.port();
  260. #if BUILDFLAG(IS_WIN)
  261. static const char kSpnSeparator = '/';
  262. #elif BUILDFLAG(IS_POSIX)
  263. static const char kSpnSeparator = '@';
  264. #endif
  265. if (port != 80 && port != 443 &&
  266. (http_auth_preferences_ &&
  267. http_auth_preferences_->NegotiateEnablePort())) {
  268. return base::StringPrintf("HTTP%c%s:%d", kSpnSeparator, server.c_str(),
  269. port);
  270. } else {
  271. return base::StringPrintf("HTTP%c%s", kSpnSeparator, server.c_str());
  272. }
  273. }
  274. void HttpAuthHandlerNegotiate::OnIOComplete(int result) {
  275. int rv = DoLoop(result);
  276. if (rv != ERR_IO_PENDING)
  277. DoCallback(rv);
  278. }
  279. void HttpAuthHandlerNegotiate::DoCallback(int rv) {
  280. DCHECK(rv != ERR_IO_PENDING);
  281. DCHECK(!callback_.is_null());
  282. std::move(callback_).Run(rv);
  283. }
  284. int HttpAuthHandlerNegotiate::DoLoop(int result) {
  285. DCHECK(next_state_ != STATE_NONE);
  286. int rv = result;
  287. do {
  288. State state = next_state_;
  289. next_state_ = STATE_NONE;
  290. switch (state) {
  291. case STATE_RESOLVE_CANONICAL_NAME:
  292. DCHECK_EQ(OK, rv);
  293. rv = DoResolveCanonicalName();
  294. break;
  295. case STATE_RESOLVE_CANONICAL_NAME_COMPLETE:
  296. rv = DoResolveCanonicalNameComplete(rv);
  297. break;
  298. case STATE_GENERATE_AUTH_TOKEN:
  299. DCHECK_EQ(OK, rv);
  300. rv = DoGenerateAuthToken();
  301. break;
  302. case STATE_GENERATE_AUTH_TOKEN_COMPLETE:
  303. rv = DoGenerateAuthTokenComplete(rv);
  304. break;
  305. default:
  306. NOTREACHED() << "bad state";
  307. rv = ERR_FAILED;
  308. break;
  309. }
  310. } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE);
  311. return rv;
  312. }
  313. int HttpAuthHandlerNegotiate::DoResolveCanonicalName() {
  314. next_state_ = STATE_RESOLVE_CANONICAL_NAME_COMPLETE;
  315. if ((http_auth_preferences_ &&
  316. http_auth_preferences_->NegotiateDisableCnameLookup()) ||
  317. !resolver_)
  318. return OK;
  319. // TODO(cbentzel): Add reverse DNS lookup for numeric addresses.
  320. HostResolver::ResolveHostParameters parameters;
  321. parameters.include_canonical_name = true;
  322. resolve_host_request_ = resolver_->CreateRequest(
  323. scheme_host_port_, network_isolation_key_, net_log(), parameters);
  324. return resolve_host_request_->Start(base::BindOnce(
  325. &HttpAuthHandlerNegotiate::OnIOComplete, base::Unretained(this)));
  326. }
  327. int HttpAuthHandlerNegotiate::DoResolveCanonicalNameComplete(int rv) {
  328. DCHECK_NE(ERR_IO_PENDING, rv);
  329. std::string server = scheme_host_port_.host();
  330. if (resolve_host_request_) {
  331. if (rv == OK) {
  332. // Expect at most a single DNS alias representing the canonical name
  333. // because the `HostResolver` request was made with
  334. // `include_canonical_name`.
  335. DCHECK(resolve_host_request_->GetDnsAliasResults());
  336. DCHECK_LE(resolve_host_request_->GetDnsAliasResults()->size(), 1u);
  337. if (!resolve_host_request_->GetDnsAliasResults()->empty()) {
  338. server = *resolve_host_request_->GetDnsAliasResults()->begin();
  339. DCHECK(!server.empty());
  340. }
  341. } else {
  342. // Even in the error case, try to use origin_.host instead of
  343. // passing the failure on to the caller.
  344. VLOG(1) << "Problem finding canonical name for SPN for host "
  345. << scheme_host_port_.host() << ": " << ErrorToString(rv);
  346. rv = OK;
  347. }
  348. }
  349. next_state_ = STATE_GENERATE_AUTH_TOKEN;
  350. spn_ = CreateSPN(server, scheme_host_port_);
  351. resolve_host_request_ = nullptr;
  352. return rv;
  353. }
  354. int HttpAuthHandlerNegotiate::DoGenerateAuthToken() {
  355. next_state_ = STATE_GENERATE_AUTH_TOKEN_COMPLETE;
  356. AuthCredentials* credentials = has_credentials_ ? &credentials_ : nullptr;
  357. return auth_system_->GenerateAuthToken(
  358. credentials, spn_, channel_bindings_, auth_token_, net_log(),
  359. base::BindOnce(&HttpAuthHandlerNegotiate::OnIOComplete,
  360. base::Unretained(this)));
  361. }
  362. int HttpAuthHandlerNegotiate::DoGenerateAuthTokenComplete(int rv) {
  363. DCHECK_NE(ERR_IO_PENDING, rv);
  364. auth_token_ = nullptr;
  365. return rv;
  366. }
  367. DelegationType HttpAuthHandlerNegotiate::GetDelegationType() const {
  368. if (!http_auth_preferences_)
  369. return DelegationType::kNone;
  370. // TODO(cbentzel): Should delegation be allowed on proxies?
  371. if (target_ == HttpAuth::AUTH_PROXY)
  372. return DelegationType::kNone;
  373. return http_auth_preferences_->GetDelegationType(scheme_host_port_);
  374. }
  375. } // namespace net