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