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