Source code of Windows XP (NT5)
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.

327 lines
9.2 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? So we should not have the problem with receiving part of the job, but rejecting the rest due to CAL denial (assuming we use single post job submission).
  12. - So we end up consuming the CAL during the whole Post, even while our call back is waiting for the rest of the bytes from the Post. If the Post never completes, Weihai's clean up thread will kill the job and free up the CAL.
  13. - "mdutil Get w3svc/AnonymousUserName" returns IUSR_BABAKJ3"]
  14. - NtLicenseRequest API defined in ntlsapi.h in sdk\inc
  15. (could return LS_INSUFFICIENT_UNITS in per-server) (sample in net\svcdlls\srvsvc\server\xsproc.c.
  16. - *** Test with a huge job, and make sure it will be OK to fail the HTTPExtensionProc call that only
  17. delivered a piece of the job (i.e. IIS may or may not accept the rest of the job
  18. but in either case delivers the IPP response to the client).
  19. - Test on NTW, verify IsNTW() works.
  20. -
  21. **********************************************************************************/
  22. #include "pch.h"
  23. #include "spool.h"
  24. #include <initguid.h> // To define the IIS Metabase GUIDs used in this file. Every other file defines GUIDs as extern, except here where we actually define them in 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];
  81. DWORD cbData = sizeof(szProductType);
  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. if( RegQueryValueEx( hKey, L"ProductType", NULL, NULL, (LPBYTE)szProductType, &cbData) == ERROR_SUCCESS ) {
  87. fProductTypeKnown = TRUE; // We will never check again...
  88. if( !lstrcmpi( L"ServerNT", szProductType ))
  89. bIsNTW = FALSE;
  90. }
  91. RegCloseKey( hKey );
  92. }
  93. }
  94. return bIsNTW;
  95. }
  96. //
  97. // if REMOTE_USER is empty it means anonymous, else, it has the username being impersonated
  98. //
  99. BOOL IsUserAnonymous(
  100. EXTENSION_CONTROL_BLOCK *pECB
  101. )
  102. {
  103. char szRemoteUser[ MAX_PATH ]; // ISAPI interface is ANSI
  104. ULONG uSize = sizeof( szRemoteUser );
  105. szRemoteUser[ 0 ] = 0; // Set it to empty ANSI string
  106. // Notice this is an ANSI call.
  107. pECB->GetServerVariable( pECB->ConnID, "REMOTE_USER", szRemoteUser, &uSize ) ;
  108. return( !(*szRemoteUser) ); // empty string should point to a zero char
  109. }
  110. //
  111. // Gets user name of the anonymous IIS account by reading it from
  112. // the Metabase's (IISADMIN) path w3svc/AnonymousUserName.
  113. //
  114. // - Returns TRUE if szAnonymousAccountUserName is filled in, FALSE if failed.
  115. // - Caller is supposed to make sure szAnonymousAccountName is MAX_PATH * sizeof(TCHAR) long.
  116. // - We get what "mdutil Get w3svc/AnonymousUserName" gets, e.g. IUSR_MACHINENAME.
  117. // I am ignoring the fact that the admin might have set the user name not per machine, rather
  118. // at a more granular level.
  119. //
  120. BOOL GetAnonymousAccountUserName(
  121. LPTSTR szAnonymousUserName,
  122. DWORD dwSize
  123. )
  124. {
  125. BOOL bRet = FALSE;
  126. HRESULT hr; // com error status
  127. METADATA_HANDLE hMeta = NULL; // handle to metabase
  128. CComPtr<IMSAdminBase> pIMeta; // ATL smart ptr
  129. DWORD dwMDRequiredDataLen;
  130. METADATA_RECORD mr;
  131. HANDLE hToken = NULL;
  132. // Need to revert to our service credential to be able to read IIS Metabase.
  133. hToken = RevertToPrinterSelf();
  134. if (hToken) {
  135. // Create a instance of the metabase object
  136. hr=::CoCreateInstance(CLSID_MSAdminBase,
  137. NULL,
  138. CLSCTX_ALL,
  139. IID_IMSAdminBase,
  140. (void **)&pIMeta);
  141. if( SUCCEEDED( hr )) {
  142. // open key to ROOT on website #1 (default)
  143. hr = pIMeta->OpenKey(METADATA_MASTER_ROOT_HANDLE,
  144. L"/LM",
  145. METADATA_PERMISSION_READ,
  146. MY_META_TIMEOUT,
  147. &hMeta);
  148. if( SUCCEEDED( hr )) {
  149. mr.dwMDIdentifier = MD_ANONYMOUS_USER_NAME;
  150. mr.dwMDAttributes = 0;
  151. mr.dwMDUserType = IIS_MD_UT_FILE;
  152. mr.dwMDDataType = STRING_METADATA;
  153. mr.dwMDDataLen = dwSize * sizeof(TCHAR);
  154. mr.pbMDData = reinterpret_cast<unsigned char *>(szAnonymousUserName);
  155. hr = pIMeta->GetData( hMeta, L"/W3svc", &mr, &dwMDRequiredDataLen );
  156. pIMeta->CloseKey( hMeta );
  157. if( SUCCEEDED( hr ))
  158. bRet = TRUE;
  159. }
  160. }
  161. if (!ImpersonatePrinterClient(hToken))
  162. bRet = FALSE;
  163. }
  164. return bRet;
  165. }
  166. //
  167. // Gets user name of the anonymous IIS account, combines with machine name g_szComputerName to
  168. // get the format LocalMachineName\IUSR_LocalMachineName
  169. //
  170. // - Returns TRUE if szAnonymousAccountName is filled in, FALSE if failed.
  171. // - Caller is supposed to make sure szAnonymousAccountName is MAX_PATH * sizeof(TCHAR) long.
  172. //
  173. BOOL GetAnonymousAccountName(
  174. LPTSTR szAnonymousAccountName,
  175. DWORD dwSize
  176. )
  177. {
  178. TCHAR szAnonymousUserName[MAX_PATH];
  179. ULONG uSize = dwSize;
  180. if( !GetComputerName( szAnonymousAccountName, &dwSize ))
  181. return FALSE;
  182. if( !GetAnonymousAccountUserName( szAnonymousUserName, MAX_PATH ))
  183. return FALSE;
  184. // Now add the user name to the machine name
  185. if (dwSize + 1 + lstrlen (szAnonymousUserName) < uSize ) {
  186. wsprintf( &szAnonymousAccountName[dwSize], L"\\%ws", szAnonymousUserName );
  187. return TRUE;
  188. }
  189. else {
  190. return FALSE;
  191. }
  192. }
  193. //
  194. // Enforce the Client Access Licensing
  195. //
  196. // Returns TRUE if license granted, FALSE otherwise. phLicense contains a license handle that needs
  197. // to be freed later, or NULL if no license was really needed, like NTW, or if non-Anonymous user
  198. // which will be enforced by IIS.
  199. //
  200. // NOTE: We only read the Anonymous account name once, for performance sake, even though it
  201. // could change during operation.
  202. //
  203. //
  204. BOOL RequestLicense(
  205. LS_HANDLE *phLicense,
  206. LPEXTENSION_CONTROL_BLOCK pECB
  207. )
  208. {
  209. static TCHAR szAnonymousAccountName[ MAX_PATH ];
  210. static BOOL fHaveReadAnonymousAccount = FALSE;
  211. if( !phLicense )
  212. return FALSE;
  213. *phLicense = NULL;
  214. if( IsNTW() || !IsUserAnonymous( pECB ))
  215. return TRUE;
  216. if( !fHaveReadAnonymousAccount )
  217. if( !GetAnonymousAccountName( szAnonymousAccountName , MAX_PATH ))
  218. return TRUE; // CAL-wise, we need to be forgiving
  219. else
  220. fHaveReadAnonymousAccount = TRUE;
  221. NT_LS_DATA NtLSData;
  222. NtLSData.DataType = NT_LS_USER_NAME;
  223. NtLSData.Data = szAnonymousAccountName;
  224. NtLSData.IsAdmin = FALSE; // why is this important to know ??
  225. return( NT_SUCCESS( NtLicenseRequest( TEXT("FilePrint"),
  226. TEXT("5.0"),
  227. phLicense,
  228. &NtLSData )));
  229. }
  230. void FreeLicense(
  231. LS_HANDLE hLicense
  232. )
  233. {
  234. // hLicense of NULL means no license was needed (like NTW,
  235. // or non-anonymous case where IIS will take care of it.
  236. if( hLicense )
  237. NtLSFreeHandle( hLicense );
  238. }