xref: /aosp_15_r20/external/cronet/net/http/mock_sspi_library_win.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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