Index: net/http/http_auth_handler_ntlm_portable.cc |
diff --git a/net/http/http_auth_handler_ntlm_portable.cc b/net/http/http_auth_handler_ntlm_portable.cc |
index 035a6dc80170c091536c615c49c013332ce13b73..d38228cc58af416b039c79ae1d1ac91e8baf9dfc 100644 |
--- a/net/http/http_auth_handler_ntlm_portable.cc |
+++ b/net/http/http_auth_handler_ntlm_portable.cc |
@@ -12,7 +12,10 @@ |
#include <winsock2.h> |
#endif |
+#include "base/base64.h" |
#include "base/md5.h" |
+#include "base/process/kill.h" |
+#include "base/process/launch.h" |
#include "base/rand_util.h" |
#include "base/strings/string_util.h" |
#include "base/strings/sys_string_conversions.h" |
@@ -631,28 +634,177 @@ HttpAuthHandlerNTLM::generate_random_proc_ = GenerateRandom; |
HttpAuthHandlerNTLM::HostNameProc |
HttpAuthHandlerNTLM::get_host_name_proc_ = GetHostName; |
-HttpAuthHandlerNTLM::HttpAuthHandlerNTLM() { |
+HttpAuthHandlerNTLM::HttpAuthHandlerNTLM( |
+ URLSecurityManager* url_security_manager) |
+ : url_security_manager_(url_security_manager) { |
+ helpername_ = getenv("CHROME_NTLM_USER_HELPER"); |
+ if (!helpername_) |
+ helpername_ = "/usr/bin/ntlm_auth"; |
+ try_winbind_ = !access(helpername_, X_OK); |
+ ntlm_write_pipe_ = -1; |
+ ntlm_read_pipe_ = -1; |
+ ntlm_auth_handle_ = -1; |
} |
bool HttpAuthHandlerNTLM::NeedsIdentity() { |
- // This gets called for each round-trip. Only require identity on |
- // the first call (when auth_data_ is empty). On subsequent calls, |
- // we use the initially established identity. |
- return auth_data_.empty(); |
+ // If we think winbind might be available, return true on the first phase |
+ // (when auth_data_ is empty) so that the default identity gets selected. |
+ // If it's *not* available, then only return true on the second phase. |
+ // This works even for the case where winbind *might* be available, but |
+ // turns out not to be when we actually get round to checking for it. |
+ // In that case, we'll send the portable challenge in the first phase |
+ // after we realise winbind can't help us, and then get a real identity |
+ // in time for the second phase. |
+ return !try_winbind_ ^ auth_data_.empty(); |
} |
-bool HttpAuthHandlerNTLM::AllowsDefaultCredentials() { |
- // Default credentials are not supported in the portable implementation of |
- // NTLM, but are supported in the SSPI implementation. |
+bool HttpAuthHandlerNTLM::start_ntlm_helper_() { |
+ /* fork_ntlm_auth_with_pipes(); */ |
+ int writepipe[2] = {-1, -1}; /* parent -> child */ |
+ int readpipe [2] = {-1, -1}; /* child -> parent */ |
+ |
+ if ( pipe(readpipe) < 0 || pipe(writepipe) < 0 ) |
+ { |
+ VLOG(1) << "Failed to open pipes for ntlm_auth helper"; |
+ return false; |
+ } |
+ char *username = getenv("NTLMUSER"); |
+ if (!username) |
+ username = getenv("USER"); |
+ if (!username) { |
+ VLOG(1) << "No username. No automatic NTLM"; |
+ return false; |
+ } |
+ |
+ std::vector<std::string> args; |
+ base::LaunchOptions options; |
+ base::FileHandleMappingVector fds_to_remap; |
+ |
+ args.push_back(helpername_); |
+ args.push_back("--helper-protocol"); |
+ args.push_back("ntlmssp-client-1"); |
+ args.push_back("--use-cached-creds"); |
+ args.push_back("--username"); |
+ args.push_back(username); |
+ |
+ fds_to_remap.push_back(std::make_pair(readpipe[1], 1)); |
+ fds_to_remap.push_back(std::make_pair(writepipe[0], 0)); |
+ |
+ |
+ options.wait = false; |
+ options.fds_to_remap = &fds_to_remap; |
+ |
+ if (base::LaunchProcess(args, options, &ntlm_auth_handle_) == false) { |
+ VLOG(1) << "Failed to launch ntlm_auth helper"; |
+ close(writepipe[0]); |
+ close(writepipe[1]); |
+ close(readpipe[0]); |
+ close(readpipe[1]); |
+ return false; |
+ } |
+ ntlm_write_pipe_ = writepipe[1]; |
+ close(writepipe[0]); |
+ |
+ ntlm_read_pipe_ = readpipe[0]; |
+ close(readpipe[1]); |
+ |
+ if (write(ntlm_write_pipe_, "YR\n", 3) != 3) { |
+ VLOG(1) << "Failed to send YR to ntlm_auth: " << strerror(errno); |
+ return false; |
+ } |
+ return true; |
+} |
+ |
+void HttpAuthHandlerNTLM::OnFileCanWriteWithoutBlocking(int fd) { |
+ NOTREACHED(); |
+} |
+ |
+static bool read_line(int fd, char *buf, int len) { |
+ |
+ while (len) { |
+ int result = read(fd, buf, len); |
+ if (result <= 0) |
+ return false; |
+ else { |
+ if (buf[result - 1] == '\n') { |
+ buf[result - 1] = 0; |
+ return true; |
+ } |
+ buf += result; |
+ len -= result; |
+ } |
+ } |
return false; |
} |
+void HttpAuthHandlerNTLM::OnFileCanReadWithoutBlocking(int fd) { |
+ |
+ char buf[1024]; |
+ if (!read_line(ntlm_read_pipe_, buf, sizeof(buf))) { |
+ VLOG(1) << "Read initial response from ntlm_auth failed"; |
+ fall_back_to_portable: |
+ void *out_buf; |
+ uint32 out_buf_len; |
+ |
+ int rv = GenerateType1Msg(&out_buf, &out_buf_len); |
+ if (rv != OK) { |
+ token_callback_.Run(rv); |
+ return; |
+ } |
+ |
+ // Base64 encode data in output buffer and prepend "NTLM ". |
+ std::string encode_input(static_cast<char*>(out_buf), out_buf_len); |
+ std::string encode_output; |
+ base::Base64Encode(encode_input, &encode_output); |
+ // OK, we are done with |out_buf| |
+ free(out_buf); |
+ *callback_auth_token_ = std::string("NTLM ") + encode_output; |
+ |
+ token_callback_.Run(OK); |
+ ntlm_read_watcher_.StopWatchingFileDescriptor(); |
+ try_winbind_ = false; |
+ return; |
+ } |
+ if (!strcmp(buf, "PW")) { |
+ VLOG(1) << "ntlm_auth asks for password"; |
+ goto fall_back_to_portable; |
+ } |
+ if (strncmp(buf, "YR ", 3) && strncmp(buf, "KK ", 3) && |
+ strncmp(buf, "AF ", 3)) { |
+ VLOG(1) << "Unknown response from ntlm_auth: " << buf; |
+ goto fall_back_to_portable; |
+ } |
+ |
+ VLOG(1) << "Yay, ntlm_auth gives challenge: " << buf; |
+ *callback_auth_token_ = std::string("NTLM ") + std::string(buf + 3); |
+ return token_callback_.Run(OK); |
+} |
+ |
+bool HttpAuthHandlerNTLM::AllowsDefaultCredentials() { |
+ // Always return true (assuning the UrlSecurityManager permits it) if |
+ // it looks like the winbind helper even might be available; we can't |
+ // check properly yet because we need to do that asynchronously. |
+ if (!try_winbind_) |
+ return false; |
+ if (target_ == HttpAuth::AUTH_PROXY) |
+ return true; |
+ if (!url_security_manager_) |
+ return false; |
+ return url_security_manager_->CanUseDefaultCredentials(origin_); |
+} |
+ |
int HttpAuthHandlerNTLM::InitializeBeforeFirstChallenge() { |
return OK; |
} |
HttpAuthHandlerNTLM::~HttpAuthHandlerNTLM() { |
credentials_.Zap(); |
+ if (ntlm_write_pipe_ != -1) |
+ close(ntlm_write_pipe_); |
+ if (ntlm_read_pipe_ != -1) |
+ close(ntlm_read_pipe_); |
+ if (ntlm_auth_handle_ != -1) |
+ base::KillProcess(ntlm_auth_handle_, 1, true); |
} |
// static |
@@ -720,7 +872,8 @@ int HttpAuthHandlerNTLM::Factory::CreateAuthHandler( |
// method and only constructing when valid. |
// NOTE: Default credentials are not supported for the portable implementation |
// of NTLM. |
- scoped_ptr<HttpAuthHandler> tmp_handler(new HttpAuthHandlerNTLM); |
+ scoped_ptr<HttpAuthHandler> tmp_handler( |
+ new HttpAuthHandlerNTLM(url_security_manager())); |
if (!tmp_handler->InitFromChallenge(challenge, target, origin, net_log)) |
return ERR_INVALID_RESPONSE; |
handler->swap(tmp_handler); |