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 // This file contains common routines used by NTLM and Negotiate authentication 6 // using the SSPI API on Windows. 7 8 #ifndef NET_HTTP_HTTP_AUTH_SSPI_WIN_H_ 9 #define NET_HTTP_HTTP_AUTH_SSPI_WIN_H_ 10 #pragma once 11 12 // security.h needs to be included for CredHandle. Unfortunately CredHandle 13 // is a typedef and can't be forward declared. 14 #define SECURITY_WIN32 1 15 #include <windows.h> 16 #include <security.h> 17 18 #include <string> 19 20 #include "base/string16.h" 21 #include "net/http/http_auth.h" 22 23 namespace net { 24 25 // SSPILibrary is introduced so unit tests can mock the calls to Windows' SSPI 26 // implementation. The default implementation simply passes the arguments on to 27 // the SSPI implementation provided by Secur32.dll. 28 // NOTE(cbentzel): I considered replacing the Secur32.dll with a mock DLL, but 29 // decided that it wasn't worth the effort as this is unlikely to be performance 30 // sensitive code. 31 class SSPILibrary { 32 public: ~SSPILibrary()33 virtual ~SSPILibrary() {} 34 35 virtual SECURITY_STATUS AcquireCredentialsHandle(LPWSTR pszPrincipal, 36 LPWSTR pszPackage, 37 unsigned long fCredentialUse, 38 void* pvLogonId, 39 void* pvAuthData, 40 SEC_GET_KEY_FN pGetKeyFn, 41 void* pvGetKeyArgument, 42 PCredHandle phCredential, 43 PTimeStamp ptsExpiry) = 0; 44 45 virtual SECURITY_STATUS InitializeSecurityContext(PCredHandle phCredential, 46 PCtxtHandle phContext, 47 SEC_WCHAR* pszTargetName, 48 unsigned long fContextReq, 49 unsigned long Reserved1, 50 unsigned long TargetDataRep, 51 PSecBufferDesc pInput, 52 unsigned long Reserved2, 53 PCtxtHandle phNewContext, 54 PSecBufferDesc pOutput, 55 unsigned long* contextAttr, 56 PTimeStamp ptsExpiry) = 0; 57 58 virtual SECURITY_STATUS QuerySecurityPackageInfo(LPWSTR pszPackageName, 59 PSecPkgInfoW *pkgInfo) = 0; 60 61 virtual SECURITY_STATUS FreeCredentialsHandle(PCredHandle phCredential) = 0; 62 63 virtual SECURITY_STATUS DeleteSecurityContext(PCtxtHandle phContext) = 0; 64 65 virtual SECURITY_STATUS FreeContextBuffer(PVOID pvContextBuffer) = 0; 66 }; 67 68 class SSPILibraryDefault : public SSPILibrary { 69 public: SSPILibraryDefault()70 SSPILibraryDefault() {} ~SSPILibraryDefault()71 virtual ~SSPILibraryDefault() {} 72 AcquireCredentialsHandle(LPWSTR pszPrincipal,LPWSTR pszPackage,unsigned long fCredentialUse,void * pvLogonId,void * pvAuthData,SEC_GET_KEY_FN pGetKeyFn,void * pvGetKeyArgument,PCredHandle phCredential,PTimeStamp ptsExpiry)73 virtual SECURITY_STATUS AcquireCredentialsHandle(LPWSTR pszPrincipal, 74 LPWSTR pszPackage, 75 unsigned long fCredentialUse, 76 void* pvLogonId, 77 void* pvAuthData, 78 SEC_GET_KEY_FN pGetKeyFn, 79 void* pvGetKeyArgument, 80 PCredHandle phCredential, 81 PTimeStamp ptsExpiry) { 82 return ::AcquireCredentialsHandle(pszPrincipal, pszPackage, fCredentialUse, 83 pvLogonId, pvAuthData, pGetKeyFn, 84 pvGetKeyArgument, phCredential, 85 ptsExpiry); 86 } 87 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)88 virtual SECURITY_STATUS InitializeSecurityContext(PCredHandle phCredential, 89 PCtxtHandle phContext, 90 SEC_WCHAR* pszTargetName, 91 unsigned long fContextReq, 92 unsigned long Reserved1, 93 unsigned long TargetDataRep, 94 PSecBufferDesc pInput, 95 unsigned long Reserved2, 96 PCtxtHandle phNewContext, 97 PSecBufferDesc pOutput, 98 unsigned long* contextAttr, 99 PTimeStamp ptsExpiry) { 100 return ::InitializeSecurityContext(phCredential, phContext, pszTargetName, 101 fContextReq, Reserved1, TargetDataRep, 102 pInput, Reserved2, phNewContext, pOutput, 103 contextAttr, ptsExpiry); 104 } 105 QuerySecurityPackageInfo(LPWSTR pszPackageName,PSecPkgInfoW * pkgInfo)106 virtual SECURITY_STATUS QuerySecurityPackageInfo(LPWSTR pszPackageName, 107 PSecPkgInfoW *pkgInfo) { 108 return ::QuerySecurityPackageInfo(pszPackageName, pkgInfo); 109 } 110 FreeCredentialsHandle(PCredHandle phCredential)111 virtual SECURITY_STATUS FreeCredentialsHandle(PCredHandle phCredential) { 112 return ::FreeCredentialsHandle(phCredential); 113 } 114 DeleteSecurityContext(PCtxtHandle phContext)115 virtual SECURITY_STATUS DeleteSecurityContext(PCtxtHandle phContext) { 116 return ::DeleteSecurityContext(phContext); 117 } 118 FreeContextBuffer(PVOID pvContextBuffer)119 virtual SECURITY_STATUS FreeContextBuffer(PVOID pvContextBuffer) { 120 return ::FreeContextBuffer(pvContextBuffer); 121 } 122 }; 123 124 class HttpAuthSSPI { 125 public: 126 HttpAuthSSPI(SSPILibrary* sspi_library, 127 const std::string& scheme, 128 SEC_WCHAR* security_package, 129 ULONG max_token_length); 130 ~HttpAuthSSPI(); 131 132 bool NeedsIdentity() const; 133 134 HttpAuth::AuthorizationResult ParseChallenge( 135 HttpAuth::ChallengeTokenizer* tok); 136 137 // Generates an authentication token for the service specified by the 138 // Service Principal Name |spn| and stores the value in |*auth_token|. 139 // If the return value is not |OK|, then the value of |*auth_token| is 140 // unspecified. ERR_IO_PENDING is not a valid return code. 141 // If this is the first round of a multiple round scheme, credentials are 142 // obtained using |*username| and |*password|. If |username| and |password| 143 // are both NULL, the credentials for the currently logged in user are used 144 // instead. 145 int GenerateAuthToken(const string16* username, 146 const string16* password, 147 const std::wstring& spn, 148 std::string* auth_token); 149 150 // Delegation is allowed on the Kerberos ticket. This allows certain servers 151 // to act as the user, such as an IIS server retrieiving data from a 152 // Kerberized MSSQL server. 153 void Delegate(); 154 155 private: 156 int OnFirstRound(const string16* username, const string16* password); 157 158 int GetNextSecurityToken( 159 const std::wstring& spn, 160 const void* in_token, 161 int in_token_len, 162 void** out_token, 163 int* out_token_len); 164 165 void ResetSecurityContext(); 166 167 SSPILibrary* library_; 168 std::string scheme_; 169 SEC_WCHAR* security_package_; 170 std::string decoded_server_auth_token_; 171 ULONG max_token_length_; 172 CredHandle cred_; 173 CtxtHandle ctxt_; 174 bool can_delegate_; 175 }; 176 177 // Splits |combined| into domain and username. 178 // If |combined| is of form "FOO\bar", |domain| will contain "FOO" and |user| 179 // will contain "bar". 180 // If |combined| is of form "bar", |domain| will be empty and |user| will 181 // contain "bar". 182 // |domain| and |user| must be non-NULL. 183 void SplitDomainAndUser(const string16& combined, 184 string16* domain, 185 string16* user); 186 187 // Determines the maximum token length in bytes for a particular SSPI package. 188 // 189 // |library| and |max_token_length| must be non-NULL pointers to valid objects. 190 // 191 // If the return value is OK, |*max_token_length| contains the maximum token 192 // length in bytes. 193 // 194 // If the return value is ERR_UNSUPPORTED_AUTH_SCHEME, |package| is not an 195 // known SSPI authentication scheme on this system. |*max_token_length| is not 196 // changed. 197 // 198 // If the return value is ERR_UNEXPECTED, there was an unanticipated problem 199 // in the underlying SSPI call. The details are logged, and |*max_token_length| 200 // is not changed. 201 int DetermineMaxTokenLength(SSPILibrary* library, 202 const std::wstring& package, 203 ULONG* max_token_length); 204 205 } // namespace net 206 207 #endif // NET_HTTP_HTTP_AUTH_SSPI_WIN_H_ 208