Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

320 lines
9.3 KiB

  1. /***************************************************************************
  2. FILE cal.cxx
  3. MODULE Printers ISAPI DLL
  4. PURPOSE Enforces Client Access Licensing
  5. DESCRIBED IN
  6. HISTORY 12/10/97 babakj created
  7. ****************************************************************************/
  8. /* ==============================================================================
  9. Issues:
  10. Notes:
  11. - I am assumuing the IIS enforcement for authenticated access is done per Post?
  12. So we should not have the problem with receiving part of the job, but rejecting
  13. the rest due to CAL denial (assuming we use single post job submission).
  14. - So we end up consuming the CAL during the whole Post, even while our call back
  15. is waiting for the rest of the bytes from the Post. If the Post never completes,
  16. The clean up thread will kill the job and free up the CAL.
  17. - NtLicenseRequest API defined in ntlsapi.h in sdk\inc
  18. (could return LS_INSUFFICIENT_UNITS in per-server) (sample in net\svcdlls\srvsvc\server\xsproc.c.
  19. **********************************************************************************/
  20. #include "pch.h"
  21. #include "spool.h"
  22. // To define the IIS Metabase GUIDs used in this file. Every other file defines
  23. // GUIDs as extern, except here where we actually define them in initguid.h.
  24. #include <initguid.h>
  25. #include <iadmw.h> // Interface header
  26. #include <iiscnfg.h> // MD_ & IIS_MD_ defines
  27. #pragma hdrstop
  28. #define MY_META_TIMEOUT 1000
  29. TCHAR const c_szProductOptionsPath[] = TEXT( "System\\CurrentControlSet\\Control\\ProductOptions" );
  30. HANDLE
  31. RevertToPrinterSelf(
  32. VOID)
  33. {
  34. HANDLE NewToken, OldToken;
  35. NTSTATUS Status;
  36. NewToken = NULL;
  37. Status = NtOpenThreadToken(
  38. NtCurrentThread(),
  39. TOKEN_IMPERSONATE,
  40. TRUE,
  41. &OldToken
  42. );
  43. if ( !NT_SUCCESS(Status) ) {
  44. SetLastError(Status);
  45. return FALSE;
  46. }
  47. Status = NtSetInformationThread(
  48. NtCurrentThread(),
  49. ThreadImpersonationToken,
  50. (PVOID)&NewToken,
  51. (ULONG)sizeof(HANDLE)
  52. );
  53. if ( !NT_SUCCESS(Status) ) {
  54. SetLastError(Status);
  55. return FALSE;
  56. }
  57. return OldToken;
  58. }
  59. BOOL
  60. ImpersonatePrinterClient(
  61. HANDLE hToken)
  62. {
  63. NTSTATUS Status;
  64. Status = NtSetInformationThread(
  65. NtCurrentThread(),
  66. ThreadImpersonationToken,
  67. (PVOID)&hToken,
  68. (ULONG)sizeof(HANDLE)
  69. );
  70. if ( !NT_SUCCESS(Status) ) {
  71. SetLastError(Status);
  72. return FALSE;
  73. }
  74. NtClose(hToken);
  75. return TRUE;
  76. }
  77. BOOL IsNTW()
  78. {
  79. HKEY hKey;
  80. TCHAR szProductType[MAX_PATH+1];
  81. DWORD cbData = sizeof(szProductType)-sizeof(szProductType[0]);
  82. static BOOL fProductTypeKnown = FALSE;
  83. static BOOL bIsNTW = TRUE; // We are being forgiving. i.e. in case of a failure, we assume NTW, so no enforcement.
  84. if( !fProductTypeKnown ) {
  85. if( ERROR_SUCCESS == RegOpenKeyEx( HKEY_LOCAL_MACHINE, c_szProductOptionsPath, 0, KEY_READ, &hKey)) {
  86. szProductType[COUNTOF(szProductType) - 1] = 0x00;
  87. if( RegQueryValueEx( hKey, L"ProductType", NULL, NULL, (LPBYTE)szProductType, &cbData) == ERROR_SUCCESS ) {
  88. fProductTypeKnown = TRUE; // We will never check again...
  89. if( !lstrcmpi( L"ServerNT", szProductType ))
  90. bIsNTW = FALSE;
  91. }
  92. RegCloseKey( hKey );
  93. }
  94. }
  95. return bIsNTW;
  96. }
  97. //
  98. // if REMOTE_USER is empty it means anonymous, else, it has the username being impersonated
  99. //
  100. BOOL IsUserAnonymous(
  101. EXTENSION_CONTROL_BLOCK *pECB
  102. )
  103. {
  104. char szRemoteUser[ MAX_PATH ]; // ISAPI interface is ANSI
  105. ULONG uSize = sizeof( szRemoteUser );
  106. szRemoteUser[ 0 ] = 0; // Set it to empty ANSI string
  107. // Notice this is an ANSI call.
  108. pECB->GetServerVariable( pECB->ConnID, "REMOTE_USER", szRemoteUser, &uSize ) ;
  109. return( !(*szRemoteUser) ); // empty string should point to a zero char
  110. }
  111. //
  112. // Gets user name of the anonymous IIS account by reading it from
  113. // the Metabase's (IISADMIN) path w3svc/AnonymousUserName.
  114. //
  115. // - Returns TRUE if szAnonymousAccountUserName is filled in, FALSE if failed.
  116. // - Caller is supposed to make sure szAnonymousAccountName is MAX_PATH * sizeof(TCHAR) long.
  117. // - We get what "mdutil Get w3svc/AnonymousUserName" gets, e.g. IUSR_MACHINENAME.
  118. // I am ignoring the fact that the admin might have set the user name not per machine, rather
  119. // at a more granular level.
  120. //
  121. BOOL GetAnonymousAccountUserName(
  122. LPTSTR szAnonymousUserName,
  123. DWORD dwSize
  124. )
  125. {
  126. BOOL bRet = FALSE;
  127. HRESULT hr; // com error status
  128. METADATA_HANDLE hMeta = NULL; // handle to metabase
  129. CComPtr<IMSAdminBase> pIMeta; // ATL smart ptr
  130. DWORD dwMDRequiredDataLen;
  131. METADATA_RECORD mr;
  132. HANDLE hToken = NULL;
  133. // Need to revert to our service credential to be able to read IIS Metabase.
  134. hToken = RevertToPrinterSelf();
  135. if (hToken) {
  136. // Create a instance of the metabase object
  137. hr=::CoCreateInstance(CLSID_MSAdminBase,
  138. NULL,
  139. CLSCTX_ALL,
  140. IID_IMSAdminBase,
  141. (void **)&pIMeta);
  142. if( SUCCEEDED( hr )) {
  143. // open key to ROOT on website #1 (default)
  144. hr = pIMeta->OpenKey(METADATA_MASTER_ROOT_HANDLE,
  145. L"/LM",
  146. METADATA_PERMISSION_READ,
  147. MY_META_TIMEOUT,
  148. &hMeta);
  149. if( SUCCEEDED( hr )) {
  150. mr.dwMDIdentifier = MD_ANONYMOUS_USER_NAME;
  151. mr.dwMDAttributes = 0;
  152. mr.dwMDUserType = IIS_MD_UT_FILE;
  153. mr.dwMDDataType = STRING_METADATA;
  154. mr.dwMDDataLen = dwSize * sizeof(TCHAR);
  155. mr.pbMDData = reinterpret_cast<unsigned char *>(szAnonymousUserName);
  156. hr = pIMeta->GetData( hMeta, L"/W3svc", &mr, &dwMDRequiredDataLen );
  157. pIMeta->CloseKey( hMeta );
  158. if( SUCCEEDED( hr ))
  159. bRet = TRUE;
  160. }
  161. }
  162. if (!ImpersonatePrinterClient(hToken))
  163. bRet = FALSE;
  164. }
  165. return bRet;
  166. }
  167. //
  168. // Gets user name of the anonymous IIS account, combines with machine name g_szComputerName to
  169. // get the format LocalMachineName\IUSR_LocalMachineName
  170. //
  171. // - Returns TRUE if szAnonymousAccountName is filled in, FALSE if failed.
  172. // - Caller is supposed to make sure szAnonymousAccountName is MAX_PATH * sizeof(TCHAR) long.
  173. //
  174. BOOL GetAnonymousAccountName(
  175. LPTSTR szAnonymousAccountName,
  176. DWORD dwSize
  177. )
  178. {
  179. TCHAR szAnonymousUserName[MAX_PATH];
  180. ULONG uSize = dwSize;
  181. if( !GetComputerName( szAnonymousAccountName, &dwSize ))
  182. return FALSE;
  183. if( !GetAnonymousAccountUserName( szAnonymousUserName, MAX_PATH ))
  184. return FALSE;
  185. // Now add the user name to the machine name
  186. if (dwSize + 1 + lstrlen (szAnonymousUserName) < uSize )
  187. {
  188. DWORD bufSize = uSize - dwSize;
  189. if (SUCCEEDED (StringCchPrintf( &szAnonymousAccountName[dwSize], bufSize, L"\\%ws", szAnonymousUserName ) ) )
  190. return TRUE;
  191. }
  192. return FALSE;
  193. }
  194. //
  195. // Enforce the Client Access Licensing
  196. //
  197. // Returns TRUE if license granted, FALSE otherwise. phLicense contains a license handle that needs
  198. // to be freed later, or NULL if no license was really needed, like NTW, or if non-Anonymous user
  199. // which will be enforced by IIS.
  200. //
  201. // NOTE: We only read the Anonymous account name once, for performance sake, even though it
  202. // could change during operation.
  203. //
  204. //
  205. BOOL RequestLicense(
  206. LS_HANDLE *phLicense,
  207. LPEXTENSION_CONTROL_BLOCK pECB
  208. )
  209. {
  210. static TCHAR szAnonymousAccountName[ MAX_PATH ];
  211. static BOOL fHaveReadAnonymousAccount = FALSE;
  212. if( !phLicense )
  213. return FALSE;
  214. *phLicense = NULL;
  215. if( IsNTW() || !IsUserAnonymous( pECB ))
  216. return TRUE;
  217. if( !fHaveReadAnonymousAccount )
  218. if( !GetAnonymousAccountName( szAnonymousAccountName , MAX_PATH ))
  219. return TRUE; // CAL-wise, we need to be forgiving
  220. else
  221. fHaveReadAnonymousAccount = TRUE;
  222. NT_LS_DATA NtLSData;
  223. NtLSData.DataType = NT_LS_USER_NAME;
  224. NtLSData.Data = szAnonymousAccountName;
  225. NtLSData.IsAdmin = FALSE; // why is this important to know ??
  226. return( NT_SUCCESS( NtLicenseRequest( TEXT("FilePrint"),
  227. TEXT("5.0"),
  228. phLicense,
  229. &NtLSData )));
  230. }
  231. void FreeLicense(
  232. LS_HANDLE hLicense
  233. )
  234. {
  235. // hLicense of NULL means no license was needed (like NTW,
  236. // or non-anonymous case where IIS will take care of it.
  237. if( hLicense )
  238. NtLSFreeHandle( hLicense );
  239. }