From 7da0b3399c137070e5e5b9403428c98f52b9e2c5 Mon Sep 17 00:00:00 2001 From: "cbentzel@chromium.org" Date: Fri, 8 Jan 2010 14:56:56 +0000 Subject: Abstracted and moved common SSPI functionality. The NTLM and Negotiate authentication schemes follow very similar code paths when using the SSPI API on Windows. BUG=29862 TEST=New unittest, plus manual NTLM Authentication testing. Review URL: http://codereview.chromium.org/505048 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@35791 0039d316-1c4b-4281-b951-d872f2087c98 --- net/http/http_auth_handler_ntlm_win.cc | 51 +++------------------- net/http/http_auth_sspi_win.cc | 77 +++++++++++++++++++++++++++++++++ net/http/http_auth_sspi_win.h | 47 ++++++++++++++++++++ net/http/http_auth_sspi_win_unittest.cc | 26 +++++++++++ net/net.gyp | 3 ++ 5 files changed, 159 insertions(+), 45 deletions(-) create mode 100755 net/http/http_auth_sspi_win.cc create mode 100755 net/http/http_auth_sspi_win.h create mode 100755 net/http/http_auth_sspi_win_unittest.cc diff --git a/net/http/http_auth_handler_ntlm_win.cc b/net/http/http_auth_handler_ntlm_win.cc index 7970740..0f40236 100644 --- a/net/http/http_auth_handler_ntlm_win.cc +++ b/net/http/http_auth_handler_ntlm_win.cc @@ -13,6 +13,7 @@ #include "base/string_util.h" #include "net/base/net_errors.h" #include "net/base/net_util.h" +#include "net/http/http_auth_sspi_win.h" #pragma comment(lib, "secur32.lib") @@ -43,52 +44,12 @@ HttpAuthHandlerNTLM::~HttpAuthHandlerNTLM() { int HttpAuthHandlerNTLM::InitializeBeforeFirstChallenge() { DCHECK_EQ("ntlm", scheme_) << "This is not ntlm scheme"; - SEC_WCHAR* package = NTLMSP_NAME; - PSecPkgInfo pkg_info; - SECURITY_STATUS status; - - // The following API call is required to get the maximum token length - // for the scheme. - // TODO(arindam): Move this (PSecPkgInfo) to a static function. - status = QuerySecurityPackageInfo(package, &pkg_info); - if (status != SEC_E_OK) { - LOG(ERROR) << "Security package " << package << " not found"; - return ERR_UNEXPECTED; + int rv = DetermineMaxTokenLength(NTLMSP_NAME, &max_token_len_); + if (rv != OK) { + return rv; } - max_token_len_ = pkg_info->cbMaxToken; - FreeContextBuffer(pkg_info); - - SEC_WINNT_AUTH_IDENTITY identity; - identity.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE; - identity.User = - reinterpret_cast(const_cast(username_.c_str())); - identity.UserLength = username_.size(); - identity.Domain = - reinterpret_cast(const_cast(domain_.c_str())); - identity.DomainLength = domain_.size(); - identity.Password = - reinterpret_cast(const_cast(password_.c_str())); - identity.PasswordLength = password_.size(); - - TimeStamp expiry; // Since the credentials handle doesn't expire, ignore - // the expiration time. - - // Pass the username/password to get the credentials handle. - // Note: If the 5th argument is NULL, it uses the default cached credentials - // for the logged in user, which can be used for single sign-on. - status = AcquireCredentialsHandle(NULL, // pszPrincipal - package, // pszPackage - SECPKG_CRED_OUTBOUND, // fCredentialUse - NULL, // pvLogonID - &identity, // pAuthData - NULL, // pGetKeyFn (not used) - NULL, // pvGetKeyArgument (not used) - &cred_, // phCredential - &expiry); // ptsExpiry - if (status != SEC_E_OK) - return ERR_UNEXPECTED; - - return OK; + rv = AcquireCredentials(NTLMSP_NAME, domain_, username_, password_, &cred_); + return rv; } int HttpAuthHandlerNTLM::GetNextToken(const void* in_token, diff --git a/net/http/http_auth_sspi_win.cc b/net/http/http_auth_sspi_win.cc new file mode 100755 index 0000000..ff3ec82 --- /dev/null +++ b/net/http/http_auth_sspi_win.cc @@ -0,0 +1,77 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/http/http_auth_sspi_win.h" + +#include "base/logging.h" +#include "net/base/net_errors.h" + +namespace net { + +void SplitDomainAndUser(const std::wstring& combined, + std::wstring* domain, + std::wstring* user) { + size_t backslash_idx = combined.find(L'\\'); + if (backslash_idx == std::wstring::npos) { + domain->clear(); + *user = combined; + } else { + *domain = combined.substr(0, backslash_idx); + *user = combined.substr(backslash_idx + 1); + } +} + +int DetermineMaxTokenLength(const std::wstring& package, + ULONG* max_token_length) { + PSecPkgInfo pkg_info; + SECURITY_STATUS status = QuerySecurityPackageInfo( + const_cast(package.c_str()), &pkg_info); + if (status != SEC_E_OK) { + LOG(ERROR) << "Security package " << package << " not found"; + return ERR_UNEXPECTED; + } + *max_token_length = pkg_info->cbMaxToken; + FreeContextBuffer(pkg_info); + return OK; +} + +int AcquireCredentials(const SEC_WCHAR* package, + const std::wstring& domain, + const std::wstring& user, + const std::wstring& password, + CredHandle* cred) { + SEC_WINNT_AUTH_IDENTITY identity; + identity.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE; + identity.User = + reinterpret_cast(const_cast(user.c_str())); + identity.UserLength = user.size(); + identity.Domain = + reinterpret_cast(const_cast(domain.c_str())); + identity.DomainLength = domain.size(); + identity.Password = + reinterpret_cast(const_cast(password.c_str())); + identity.PasswordLength = password.size(); + + TimeStamp expiry; + + // Pass the username/password to get the credentials handle. + // Note: If the 5th argument is NULL, it uses the default cached credentials + // for the logged in user, which can be used for single sign-on. + SECURITY_STATUS status = AcquireCredentialsHandle( + NULL, // pszPrincipal + const_cast(package), // pszPackage + SECPKG_CRED_OUTBOUND, // fCredentialUse + NULL, // pvLogonID + &identity, // pAuthData + NULL, // pGetKeyFn (not used) + NULL, // pvGetKeyArgument (not used) + cred, // phCredential + &expiry); // ptsExpiry + + if (status != SEC_E_OK) + return ERR_UNEXPECTED; + return OK; +} + +} // namespace net diff --git a/net/http/http_auth_sspi_win.h b/net/http/http_auth_sspi_win.h new file mode 100755 index 0000000..d58678b --- /dev/null +++ b/net/http/http_auth_sspi_win.h @@ -0,0 +1,47 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This file contains common routines used by NTLM and Negotiate authentication +// using the SSPI API on Windows. + +#ifndef NET_HTTP_HTTP_AUTH_SSPI_WIN_H_ +#define NET_HTTP_HTTP_AUTH_SSPI_WIN_H_ + +// security.h needs to be included for CredHandle. Unfortunately CredHandle +// is a typedef and can't be forward declared. +#define SECURITY_WIN32 1 +#include +#include + +#include + +namespace net { + +// Splits |combined| into domain and username. +// If |combined| is of form "FOO\bar", |domain| will contain "FOO" and |user| +// will contain "bar". +// If |combined| is of form "bar", |domain| will be empty and |user| will +// contain "bar". +// |domain| and |user| must be non-NULL. +void SplitDomainAndUser(const std::wstring& combined, + std::wstring* domain, + std::wstring* user); + +// Determines the max token length for a particular SSPI package. +// If the return value is not OK, than the value of max_token_length +// is undefined. +// |max_token_length| must be non-NULL. +int DetermineMaxTokenLength(const std::wstring& package, + ULONG* max_token_length); + +// Acquire credentials for a user. +int AcquireCredentials(const SEC_WCHAR* package, + const std::wstring& domain, + const std::wstring& user, + const std::wstring& password, + CredHandle* cred); +} // namespace net + +#endif // NET_HTTP_HTTP_AUTH_SSPI_WIN_H_ + diff --git a/net/http/http_auth_sspi_win_unittest.cc b/net/http/http_auth_sspi_win_unittest.cc new file mode 100755 index 0000000..b421ca9 --- /dev/null +++ b/net/http/http_auth_sspi_win_unittest.cc @@ -0,0 +1,26 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/basictypes.h" +#include "net/http/http_auth_sspi_win.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace net { + +void MatchDomainUserAfterSplit(const std::wstring& combined, + const std::wstring& expected_domain, + const std::wstring& expected_user) { + std::wstring actual_domain; + std::wstring actual_user; + SplitDomainAndUser(combined, &actual_domain, &actual_user); + EXPECT_EQ(expected_domain, actual_domain); + EXPECT_EQ(expected_user, actual_user); +} + +TEST(HttpAuthHandlerSspiWinTest, SplitUserAndDomain) { + MatchDomainUserAfterSplit(L"foobar", L"", L"foobar"); + MatchDomainUserAfterSplit(L"FOO\\bar", L"FOO", L"bar"); +} + +} // namespace net diff --git a/net/net.gyp b/net/net.gyp index 1b54efb..e13248a 100644 --- a/net/net.gyp +++ b/net/net.gyp @@ -340,6 +340,8 @@ 'http/http_auth_handler_ntlm.h', 'http/http_auth_handler_ntlm_portable.cc', 'http/http_auth_handler_ntlm_win.cc', + 'http/http_auth_sspi_win.cc', + 'http/http_auth_sspi_win.h', 'http/http_basic_stream.cc', 'http/http_basic_stream.h', 'http/http_byte_range.cc', @@ -634,6 +636,7 @@ 'http/http_auth_cache_unittest.cc', 'http/http_auth_handler_basic_unittest.cc', 'http/http_auth_handler_digest_unittest.cc', + 'http/http_auth_sspi_win_unittest.cc', 'http/http_auth_unittest.cc', 'http/http_byte_range_unittest.cc', 'http/http_cache_unittest.cc', -- cgit v1.1