| OLD | NEW |
| (Empty) | |
| 1 // Copyright (c) 2009 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 |
| 5 // See "SSPI Sample Application" at |
| 6 // http://msdn.microsoft.com/en-us/library/aa918273.aspx |
| 7 // and "NTLM Security Support Provider" at |
| 8 // http://msdn.microsoft.com/en-us/library/aa923611.aspx. |
| 9 |
| 10 #include "net/http/http_auth_handler_ntlm.h" |
| 11 |
| 12 #include "base/logging.h" |
| 13 #include "base/string_util.h" |
| 14 #include "net/base/net_errors.h" |
| 15 #include "net/base/net_util.h" |
| 16 |
| 17 #pragma comment(lib, "secur32.lib") |
| 18 |
| 19 namespace { |
| 20 |
| 21 void ZapString(string16* s) { |
| 22 memset(&(*s)[0], 0, s->length() * 2); |
| 23 } |
| 24 |
| 25 } // namespace |
| 26 |
| 27 namespace net { |
| 28 |
| 29 HttpAuthHandlerNTLM::HttpAuthHandlerNTLM() : max_token_len_(0) { |
| 30 SecInvalidateHandle(&cred_); |
| 31 SecInvalidateHandle(&ctxt_); |
| 32 } |
| 33 |
| 34 HttpAuthHandlerNTLM::~HttpAuthHandlerNTLM() { |
| 35 ResetSecurityContext(); |
| 36 if (SecIsValidHandle(&cred_)) { |
| 37 FreeCredentialsHandle(&cred_); |
| 38 SecInvalidateHandle(&cred_); |
| 39 } |
| 40 ZapString(&password_); |
| 41 } |
| 42 |
| 43 int HttpAuthHandlerNTLM::InitializeBeforeFirstChallenge() { |
| 44 DCHECK_EQ("ntlm", scheme_) << "This is not ntlm scheme"; |
| 45 |
| 46 SEC_WCHAR* package = NTLMSP_NAME; |
| 47 PSecPkgInfo pkg_info; |
| 48 SECURITY_STATUS status; |
| 49 |
| 50 // The following API call is required to get the maximum token length |
| 51 // for the scheme. |
| 52 // TODO(arindam): Move this (PSecPkgInfo) to a static function. |
| 53 status = QuerySecurityPackageInfo(package, &pkg_info); |
| 54 if (status != SEC_E_OK) { |
| 55 LOG(ERROR) << "Security package " << package << " not found"; |
| 56 return ERR_UNEXPECTED; |
| 57 } |
| 58 max_token_len_ = pkg_info->cbMaxToken; |
| 59 FreeContextBuffer(pkg_info); |
| 60 |
| 61 SEC_WINNT_AUTH_IDENTITY identity; |
| 62 identity.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE; |
| 63 identity.User = |
| 64 reinterpret_cast<USHORT*>(const_cast<wchar_t*>(username_.c_str())); |
| 65 identity.UserLength = username_.size(); |
| 66 identity.Domain = |
| 67 reinterpret_cast<USHORT*>(const_cast<wchar_t*>(domain_.c_str())); |
| 68 identity.DomainLength = domain_.size(); |
| 69 identity.Password = |
| 70 reinterpret_cast<USHORT*>(const_cast<wchar_t*>(password_.c_str())); |
| 71 identity.PasswordLength = password_.size(); |
| 72 |
| 73 TimeStamp expiry; // Since the credentials handle doesn't expire, ignore |
| 74 // the expiration time. |
| 75 |
| 76 // Pass the username/password to get the credentials handle. |
| 77 // Note: If the 5th argument is NULL, it uses the default cached credentials |
| 78 // for the logged in user, which can be used for single sign-on. |
| 79 status = AcquireCredentialsHandle(NULL, // pszPrincipal |
| 80 package, // pszPackage |
| 81 SECPKG_CRED_OUTBOUND, // fCredentialUse |
| 82 NULL, // pvLogonID |
| 83 &identity, // pAuthData |
| 84 NULL, // pGetKeyFn (not used) |
| 85 NULL, // pvGetKeyArgument (not used) |
| 86 &cred_, // phCredential |
| 87 &expiry); // ptsExpiry |
| 88 if (status != SEC_E_OK) |
| 89 return ERR_UNEXPECTED; |
| 90 |
| 91 return OK; |
| 92 } |
| 93 |
| 94 int HttpAuthHandlerNTLM::GetNextToken(const void* in_token, |
| 95 uint32 in_token_len, |
| 96 void** out_token, |
| 97 uint32* out_token_len) { |
| 98 SECURITY_STATUS status; |
| 99 TimeStamp expiry; |
| 100 |
| 101 DWORD ctxt_attr; |
| 102 CtxtHandle* ctxt_ptr; |
| 103 SecBufferDesc in_buffer_desc, out_buffer_desc; |
| 104 SecBufferDesc* in_buffer_desc_ptr; |
| 105 SecBuffer in_buffer, out_buffer; |
| 106 |
| 107 if (in_token) { |
| 108 // Prepare input buffer. |
| 109 in_buffer_desc.ulVersion = SECBUFFER_VERSION; |
| 110 in_buffer_desc.cBuffers = 1; |
| 111 in_buffer_desc.pBuffers = &in_buffer; |
| 112 in_buffer.BufferType = SECBUFFER_TOKEN; |
| 113 in_buffer.cbBuffer = in_token_len; |
| 114 in_buffer.pvBuffer = const_cast<void*>(in_token); |
| 115 ctxt_ptr = &ctxt_; |
| 116 in_buffer_desc_ptr = &in_buffer_desc; |
| 117 } else { |
| 118 // If there is no input token, then we are starting a new authentication |
| 119 // sequence. If we have already initialized our security context, then |
| 120 // we're incorrectly reusing the auth handler for a new sequence. |
| 121 if (SecIsValidHandle(&ctxt_)) { |
| 122 LOG(ERROR) << "Cannot restart authentication sequence"; |
| 123 return ERR_UNEXPECTED; |
| 124 } |
| 125 ctxt_ptr = NULL; |
| 126 in_buffer_desc_ptr = NULL; |
| 127 } |
| 128 |
| 129 // Prepare output buffer. |
| 130 out_buffer_desc.ulVersion = SECBUFFER_VERSION; |
| 131 out_buffer_desc.cBuffers = 1; |
| 132 out_buffer_desc.pBuffers = &out_buffer; |
| 133 out_buffer.BufferType = SECBUFFER_TOKEN; |
| 134 out_buffer.cbBuffer = max_token_len_; |
| 135 out_buffer.pvBuffer = malloc(out_buffer.cbBuffer); |
| 136 if (!out_buffer.pvBuffer) |
| 137 return ERR_OUT_OF_MEMORY; |
| 138 |
| 139 // The service principal name of the destination server. See |
| 140 // http://msdn.microsoft.com/en-us/library/ms677949%28VS.85%29.aspx |
| 141 std::wstring target(L"HTTP/"); |
| 142 target.append(ASCIIToWide(GetHostAndPort(origin_))); |
| 143 wchar_t* target_name = const_cast<wchar_t*>(target.c_str()); |
| 144 |
| 145 // This returns a token that is passed to the remote server. |
| 146 status = InitializeSecurityContext(&cred_, // phCredential |
| 147 ctxt_ptr, // phContext |
| 148 target_name, // pszTargetName |
| 149 0, // fContextReq |
| 150 0, // Reserved1 (must be 0) |
| 151 SECURITY_NATIVE_DREP, // TargetDataRep |
| 152 in_buffer_desc_ptr, // pInput |
| 153 0, // Reserved2 (must be 0) |
| 154 &ctxt_, // phNewContext |
| 155 &out_buffer_desc, // pOutput |
| 156 &ctxt_attr, // pfContextAttr |
| 157 &expiry); // ptsExpiry |
| 158 // On success, the function returns SEC_I_CONTINUE_NEEDED on the first call |
| 159 // and SEC_E_OK on the second call. On failure, the function returns an |
| 160 // error code. |
| 161 if (status != SEC_I_CONTINUE_NEEDED && status != SEC_E_OK) { |
| 162 LOG(ERROR) << "InitializeSecurityContext failed: " << status; |
| 163 ResetSecurityContext(); |
| 164 free(out_buffer.pvBuffer); |
| 165 return ERR_UNEXPECTED; // TODO(wtc): map error code. |
| 166 } |
| 167 if (!out_buffer.cbBuffer) { |
| 168 free(out_buffer.pvBuffer); |
| 169 out_buffer.pvBuffer = NULL; |
| 170 } |
| 171 *out_token = out_buffer.pvBuffer; |
| 172 *out_token_len = out_buffer.cbBuffer; |
| 173 return OK; |
| 174 } |
| 175 |
| 176 // Require identity on first pass instead of second. |
| 177 bool HttpAuthHandlerNTLM::NeedsIdentity() { |
| 178 return auth_data_.empty(); |
| 179 } |
| 180 |
| 181 bool HttpAuthHandlerNTLM::IsFinalRound() { |
| 182 return !auth_data_.empty(); |
| 183 } |
| 184 |
| 185 void HttpAuthHandlerNTLM::ResetSecurityContext() { |
| 186 if (SecIsValidHandle(&ctxt_)) { |
| 187 DeleteSecurityContext(&ctxt_); |
| 188 SecInvalidateHandle(&ctxt_); |
| 189 } |
| 190 } |
| 191 |
| 192 } // namespace net |
| 193 |
| OLD | NEW |