1 // Copyright (c) 2010 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 #include "net/http/mock_sspi_library_win.h"
6
7 #include <algorithm>
8 #include <cstring>
9 #include <memory>
10
11 #include "base/check_op.h"
12 #include "base/strings/string16.h"
13 #include "base/strings/stringprintf.h"
14 #include "base/strings/utf_string_conversions.h"
15 #include "base/time/time.h"
16 #include "testing/gtest/include/gtest/gtest.h"
17
18 // Comparator so we can use CredHandle and CtxtHandle with std::set. Both of
19 // those classes are typedefs for _SecHandle.
operator <(const _SecHandle left,const _SecHandle right)20 bool operator<(const _SecHandle left, const _SecHandle right) {
21 return left.dwUpper < right.dwUpper || left.dwLower < right.dwLower;
22 }
23
24 namespace net {
25
26 namespace {
27
28 int uniquifier_ = 0;
29
30 struct MockCredential {
31 base::string16 source_principal;
32 base::string16 package;
33 bool has_explicit_credentials = false;
34 int uniquifier = ++uniquifier_;
35
36 // CredHandle and CtxtHandle both shared the following definition:
37 //
38 // typedef struct _SecHandle {
39 // ULONG_PTR dwLower;
40 // ULONG_PTR dwUpper;
41 // } SecHandle, * PSecHandle;
42 //
43 // ULONG_PTR type can hold a pointer. This function stuffs |this| into dwUpper
44 // and adds a uniquifier to dwLower. This ensures that all PCredHandles issued
45 // by this method during the lifetime of this process is unique.
StoreInHandlenet::__anon67acea270111::MockCredential46 void StoreInHandle(PCredHandle handle) {
47 DCHECK(uniquifier > 0);
48 EXPECT_FALSE(SecIsValidHandle(handle));
49
50 handle->dwLower = uniquifier;
51 handle->dwUpper = reinterpret_cast<ULONG_PTR>(this);
52
53 DCHECK(SecIsValidHandle(handle));
54 }
55
FromHandlenet::__anon67acea270111::MockCredential56 static MockCredential* FromHandle(PCredHandle handle) {
57 return reinterpret_cast<MockCredential*>(handle->dwUpper);
58 }
59 };
60
61 struct MockContext {
62 MockCredential* credential = nullptr;
63 base::string16 target_principal;
64 int uniquifier = ++uniquifier_;
65 int rounds = 0;
66
67 // CredHandle and CtxtHandle both shared the following definition:
68 //
69 // typedef struct _SecHandle {
70 // ULONG_PTR dwLower;
71 // ULONG_PTR dwUpper;
72 // } SecHandle, * PSecHandle;
73 //
74 // ULONG_PTR type can hold a pointer. This function stuffs |this| into dwUpper
75 // and adds a uniquifier to dwLower. This ensures that all PCredHandles issued
76 // by this method during the lifetime of this process is unique.
StoreInHandlenet::__anon67acea270111::MockContext77 void StoreInHandle(PCtxtHandle handle) {
78 EXPECT_FALSE(SecIsValidHandle(handle));
79 DCHECK(uniquifier > 0);
80
81 handle->dwLower = uniquifier;
82 handle->dwUpper = reinterpret_cast<ULONG_PTR>(this);
83
84 DCHECK(SecIsValidHandle(handle));
85 }
86
ToStringnet::__anon67acea270111::MockContext87 std::string ToString() const {
88 return base::StringPrintf(
89 "%s's token #%d for %S",
90 base::UTF16ToUTF8(credential->source_principal).c_str(), rounds + 1,
91 base::as_wcstr(target_principal));
92 }
93
FromHandlenet::__anon67acea270111::MockContext94 static MockContext* FromHandle(PCtxtHandle handle) {
95 return reinterpret_cast<MockContext*>(handle->dwUpper);
96 }
97 };
98
99 } // namespace
100
MockSSPILibrary(const wchar_t * package)101 MockSSPILibrary::MockSSPILibrary(const wchar_t* package)
102 : SSPILibrary(package) {}
103
~MockSSPILibrary()104 MockSSPILibrary::~MockSSPILibrary() {
105 EXPECT_TRUE(expected_package_queries_.empty());
106 EXPECT_TRUE(expected_freed_packages_.empty());
107 EXPECT_TRUE(active_credentials_.empty());
108 EXPECT_TRUE(active_contexts_.empty());
109 }
110
AcquireCredentialsHandle(LPWSTR pszPrincipal,unsigned long fCredentialUse,void * pvLogonId,void * pvAuthData,SEC_GET_KEY_FN pGetKeyFn,void * pvGetKeyArgument,PCredHandle phCredential,PTimeStamp ptsExpiry)111 SECURITY_STATUS MockSSPILibrary::AcquireCredentialsHandle(
112 LPWSTR pszPrincipal,
113 unsigned long fCredentialUse,
114 void* pvLogonId,
115 void* pvAuthData,
116 SEC_GET_KEY_FN pGetKeyFn,
117 void* pvGetKeyArgument,
118 PCredHandle phCredential,
119 PTimeStamp ptsExpiry) {
120 DCHECK(!SecIsValidHandle(phCredential));
121 auto* credential = new MockCredential;
122 credential->source_principal = pszPrincipal ? base::as_u16cstr(pszPrincipal)
123 : STRING16_LITERAL("<Default>");
124 credential->package = base::as_u16cstr(package_name_.c_str());
125 credential->has_explicit_credentials = !!pvAuthData;
126
127 credential->StoreInHandle(phCredential);
128
129 if (ptsExpiry) {
130 ptsExpiry->LowPart = 0xBAA5B780;
131 ptsExpiry->HighPart = 0x01D54E17;
132 }
133
134 active_credentials_.insert(*phCredential);
135 return SEC_E_OK;
136 }
137
InitializeSecurityContext(PCredHandle phCredential,PCtxtHandle phContext,SEC_WCHAR * pszTargetName,unsigned long fContextReq,unsigned long Reserved1,unsigned long TargetDataRep,PSecBufferDesc pInput,unsigned long Reserved2,PCtxtHandle phNewContext,PSecBufferDesc pOutput,unsigned long * contextAttr,PTimeStamp ptsExpiry)138 SECURITY_STATUS MockSSPILibrary::InitializeSecurityContext(
139 PCredHandle phCredential,
140 PCtxtHandle phContext,
141 SEC_WCHAR* pszTargetName,
142 unsigned long fContextReq,
143 unsigned long Reserved1,
144 unsigned long TargetDataRep,
145 PSecBufferDesc pInput,
146 unsigned long Reserved2,
147 PCtxtHandle phNewContext,
148 PSecBufferDesc pOutput,
149 unsigned long* contextAttr,
150 PTimeStamp ptsExpiry) {
151 MockContext* new_context = new MockContext;
152 new_context->credential = MockCredential::FromHandle(phCredential);
153 new_context->target_principal = base::as_u16cstr(pszTargetName);
154 new_context->rounds = 0;
155
156 // Always rotate contexts. That way tests will fail if the caller's context
157 // management is broken.
158 if (phContext && SecIsValidHandle(phContext)) {
159 std::unique_ptr<MockContext> old_context{
160 MockContext::FromHandle(phContext)};
161 EXPECT_EQ(old_context->credential, new_context->credential);
162 EXPECT_EQ(1u, active_contexts_.erase(*phContext));
163
164 new_context->rounds = old_context->rounds + 1;
165 SecInvalidateHandle(phContext);
166 }
167
168 new_context->StoreInHandle(phNewContext);
169 active_contexts_.insert(*phNewContext);
170
171 auto token = new_context->ToString();
172 PSecBuffer out_buffer = pOutput->pBuffers;
173 out_buffer->cbBuffer = std::min<ULONG>(out_buffer->cbBuffer, token.size());
174 std::memcpy(out_buffer->pvBuffer, token.data(), out_buffer->cbBuffer);
175
176 if (ptsExpiry) {
177 ptsExpiry->LowPart = 0xBAA5B780;
178 ptsExpiry->HighPart = 0x01D54E15;
179 }
180 return SEC_E_OK;
181 }
182
QueryContextAttributesEx(PCtxtHandle phContext,ULONG ulAttribute,PVOID pBuffer,ULONG cbBuffer)183 SECURITY_STATUS MockSSPILibrary::QueryContextAttributesEx(PCtxtHandle phContext,
184 ULONG ulAttribute,
185 PVOID pBuffer,
186 ULONG cbBuffer) {
187 static const SecPkgInfoW kNegotiatedPackage = {
188 0,
189 0,
190 0,
191 0,
192 const_cast<SEC_WCHAR*>(L"Itsa me Kerberos!!"),
193 const_cast<SEC_WCHAR*>(L"I like turtles")};
194
195 auto* context = MockContext::FromHandle(phContext);
196
197 switch (ulAttribute) {
198 case SECPKG_ATTR_NATIVE_NAMES: {
199 auto* native_names =
200 reinterpret_cast<SecPkgContext_NativeNames*>(pBuffer);
201 DCHECK_EQ(sizeof(*native_names), cbBuffer);
202 native_names->sClientName =
203 base::as_writable_wcstr(context->credential->source_principal);
204 native_names->sServerName =
205 base::as_writable_wcstr(context->target_principal);
206 return SEC_E_OK;
207 }
208
209 case SECPKG_ATTR_NEGOTIATION_INFO: {
210 auto* negotiation_info =
211 reinterpret_cast<SecPkgContext_NegotiationInfo*>(pBuffer);
212 DCHECK_EQ(sizeof(*negotiation_info), cbBuffer);
213 negotiation_info->PackageInfo =
214 const_cast<SecPkgInfoW*>(&kNegotiatedPackage);
215 negotiation_info->NegotiationState = (context->rounds == 1)
216 ? SECPKG_NEGOTIATION_COMPLETE
217 : SECPKG_NEGOTIATION_IN_PROGRESS;
218 return SEC_E_OK;
219 }
220
221 case SECPKG_ATTR_AUTHORITY: {
222 auto* authority = reinterpret_cast<SecPkgContext_Authority*>(pBuffer);
223 DCHECK_EQ(sizeof(*authority), cbBuffer);
224 authority->sAuthorityName = const_cast<SEC_WCHAR*>(L"Dodgy Server");
225 return SEC_E_OK;
226 }
227
228 default:
229 return SEC_E_UNSUPPORTED_FUNCTION;
230 }
231 }
232
QuerySecurityPackageInfo(PSecPkgInfoW * pkgInfo)233 SECURITY_STATUS MockSSPILibrary::QuerySecurityPackageInfo(
234 PSecPkgInfoW* pkgInfo) {
235 if (expected_package_queries_.empty()) {
236 static SecPkgInfoW kDefaultPkgInfo{
237 0, 0, 0, kDefaultMaxTokenLength, nullptr, nullptr};
238 *pkgInfo = &kDefaultPkgInfo;
239 expected_freed_packages_.insert(&kDefaultPkgInfo);
240 return SEC_E_OK;
241 }
242
243 PackageQuery package_query = expected_package_queries_.front();
244 expected_package_queries_.pop_front();
245 *pkgInfo = package_query.package_info;
246 if (package_query.response_code == SEC_E_OK)
247 expected_freed_packages_.insert(package_query.package_info);
248 return package_query.response_code;
249 }
250
FreeCredentialsHandle(PCredHandle phCredential)251 SECURITY_STATUS MockSSPILibrary::FreeCredentialsHandle(
252 PCredHandle phCredential) {
253 DCHECK(SecIsValidHandle(phCredential));
254 EXPECT_EQ(1u, active_credentials_.erase(*phCredential));
255 std::unique_ptr<MockCredential> owned{
256 MockCredential::FromHandle(phCredential)};
257 SecInvalidateHandle(phCredential);
258 return SEC_E_OK;
259 }
260
DeleteSecurityContext(PCtxtHandle phContext)261 SECURITY_STATUS MockSSPILibrary::DeleteSecurityContext(PCtxtHandle phContext) {
262 std::unique_ptr<MockContext> context{MockContext::FromHandle(phContext)};
263 EXPECT_EQ(1u, active_contexts_.erase(*phContext));
264 SecInvalidateHandle(phContext);
265 return SEC_E_OK;
266 }
267
FreeContextBuffer(PVOID pvContextBuffer)268 SECURITY_STATUS MockSSPILibrary::FreeContextBuffer(PVOID pvContextBuffer) {
269 PSecPkgInfoW package_info = static_cast<PSecPkgInfoW>(pvContextBuffer);
270 std::set<PSecPkgInfoW>::iterator it = expected_freed_packages_.find(
271 package_info);
272 EXPECT_TRUE(it != expected_freed_packages_.end());
273 expected_freed_packages_.erase(it);
274 return SEC_E_OK;
275 }
276
ExpectQuerySecurityPackageInfo(SECURITY_STATUS response_code,PSecPkgInfoW package_info)277 void MockSSPILibrary::ExpectQuerySecurityPackageInfo(
278 SECURITY_STATUS response_code,
279 PSecPkgInfoW package_info) {
280 expected_package_queries_.emplace_back(
281 PackageQuery{response_code, package_info});
282 }
283
284 } // namespace net
285