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