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