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.

499 lines
12 KiB

  1. //+--------------------------------------------------------------------------
  2. // File: callback.cpp
  3. // Contents: access check callback
  4. //---------------------------------------------------------------------------
  5. #include <pch.cpp>
  6. #pragma hdrstop
  7. #include "csext.h"
  8. #include "certsd.h"
  9. #include <winldap.h>
  10. #include <limits.h>
  11. #include "csprop.h"
  12. #include "sid.h"
  13. #include <authzi.h>
  14. namespace CertSrv
  15. {
  16. HRESULT GetAccountSid(
  17. IN LPWSTR pwszName,
  18. PSID *ppSid)
  19. {
  20. HRESULT hr = S_OK;
  21. DWORD cbSid = 0;
  22. DWORD cbDomainName = 0;
  23. SID_NAME_USE use;
  24. LPWSTR pwszDomainName = NULL;
  25. *ppSid = NULL;
  26. if(!pwszName || L'\0'== pwszName[0])
  27. {
  28. hr = GetEveryoneSID(ppSid);
  29. _JumpIfError(hr, error, "GetEveryoneSID");
  30. }
  31. else
  32. {
  33. LookupAccountName(
  34. NULL,
  35. pwszName,
  36. NULL,
  37. &cbSid,
  38. NULL,
  39. &cbDomainName,
  40. &use);
  41. if(ERROR_INSUFFICIENT_BUFFER != GetLastError())
  42. {
  43. hr = myHError(GetLastError());
  44. _JumpError(hr, error, "LookupAccountName");
  45. }
  46. *ppSid = (PSID)LocalAlloc(LMEM_FIXED, cbSid);
  47. if(!*ppSid)
  48. {
  49. hr = E_OUTOFMEMORY;
  50. _JumpError(hr, error, "LocalAlloc");
  51. }
  52. pwszDomainName = (LPWSTR)LocalAlloc(LMEM_FIXED,
  53. cbDomainName*sizeof(WCHAR));
  54. if(!pwszDomainName)
  55. {
  56. hr = E_OUTOFMEMORY;
  57. _JumpError(hr, error, "LocalAlloc");
  58. }
  59. if(!LookupAccountName(
  60. NULL,
  61. pwszName,
  62. *ppSid,
  63. &cbSid,
  64. pwszDomainName,
  65. &cbDomainName,
  66. &use))
  67. {
  68. hr = myHError(GetLastError());
  69. _JumpError(hr, error, "LookupAccountName");
  70. }
  71. }
  72. hr = S_OK;
  73. error:
  74. if(S_OK!=hr)
  75. {
  76. if(*ppSid)
  77. {
  78. LocalFree(*ppSid);
  79. }
  80. if(pwszDomainName)
  81. {
  82. LocalFree(pwszDomainName);
  83. }
  84. }
  85. return hr;
  86. }
  87. BOOL
  88. CallbackAccessCheck(
  89. IN AUTHZ_CLIENT_CONTEXT_HANDLE pAuthzClientContext,
  90. IN PACE_HEADER pAce,
  91. IN PVOID pArgs OPTIONAL,
  92. IN OUT PBOOL pbAceApplicable)
  93. {
  94. HRESULT hr = S_OK;
  95. LPWSTR pwszSamName = (LPWSTR)pArgs;// requester name is passed in to
  96. // AuthzAccessCheck in NT4 style form
  97. // "DomainNetbiosName\RequesterSAMName"
  98. PSID pSid = NULL, pClientSid = NULL, pCallerSid = NULL;
  99. PSID pEveryoneSid = NULL;
  100. PTOKEN_GROUPS pGroups = NULL;
  101. ACCESS_ALLOWED_CALLBACK_ACE* pCallbackAce =
  102. (ACCESS_ALLOWED_CALLBACK_ACE*)pAce;
  103. PSID_LIST pSidList = (PSID_LIST) (((BYTE*)&pCallbackAce->SidStart)+
  104. GetLengthSid(&pCallbackAce->SidStart));
  105. DWORD cSids, cClientSids;
  106. CSASSERT(
  107. ACCESS_ALLOWED_CALLBACK_ACE_TYPE == pAce->AceType ||
  108. ACCESS_DENIED_CALLBACK_ACE_TYPE == pAce->AceType);
  109. CSASSERT(HeapValidate(GetProcessHeap(),0,NULL));
  110. SetLastError(ERROR_SUCCESS);
  111. // get the SID for the requester
  112. hr = GetAccountSid(pwszSamName, &pCallerSid);
  113. CSASSERT(HeapValidate(GetProcessHeap(),0,NULL));
  114. if(HRESULT_FROM_WIN32(ERROR_NONE_MAPPED)==hr)
  115. {
  116. // if name cannot be resolved, default to Everyone
  117. DWORD dwSize = sizeof(TOKEN_GROUPS);
  118. pGroups = (PTOKEN_GROUPS)LocalAlloc(LMEM_FIXED, sizeof(TOKEN_GROUPS));
  119. if(!pGroups)
  120. {
  121. hr = E_OUTOFMEMORY;
  122. _JumpError(hr, error, "LocalAlloc");
  123. }
  124. hr = GetEveryoneSID(&pEveryoneSid);
  125. _JumpIfError(hr, error, "GetEveryoneSID");
  126. CSASSERT(HeapValidate(GetProcessHeap(),0,NULL));
  127. pGroups->GroupCount=1;
  128. pGroups->Groups[0].Sid = pEveryoneSid;
  129. pGroups->Groups[0].Attributes = 0;
  130. }
  131. else
  132. {
  133. _JumpIfError(hr, error, "GetAccountSid");
  134. // get the list of groups this SID is member of
  135. hr = GetMembership(g_AuthzCertSrvRM, pCallerSid, &pGroups);
  136. _JumpIfError(hr, error, "GetMembership");
  137. CSASSERT(HeapValidate(GetProcessHeap(),0,NULL));
  138. }
  139. CSASSERT(HeapValidate(GetProcessHeap(),0,NULL));
  140. // traverse the SID list stored in the ACE and compare with the
  141. // client's membership
  142. for(pSid=(PSID)&pSidList->SidListStart, cSids=0; cSids<pSidList->dwSidCount;
  143. cSids++, pSid = (PSID)(((BYTE*)pSid)+GetLengthSid(pSid)))
  144. {
  145. CSASSERT(IsValidSid(pSid));
  146. // group membership doesn't include the user itself, so
  147. // compare with the user first
  148. if(pCallerSid && EqualSid(pSid, pCallerSid))
  149. {
  150. *pbAceApplicable = TRUE;
  151. goto error;
  152. }
  153. for(cClientSids=0; cClientSids<pGroups->GroupCount; cClientSids++)
  154. {
  155. pClientSid = pGroups->Groups[cClientSids].Sid;
  156. CSASSERT(IsValidSid(pClientSid));
  157. if(EqualSid(pSid, pClientSid))
  158. {
  159. *pbAceApplicable = TRUE;
  160. goto error;
  161. }
  162. }
  163. }
  164. *pbAceApplicable = FALSE;
  165. error:
  166. CSASSERT(HeapValidate(GetProcessHeap(),0,NULL));
  167. if(pEveryoneSid)
  168. {
  169. LocalFree(pEveryoneSid);
  170. }
  171. if(pCallerSid)
  172. {
  173. LocalFree(pCallerSid);
  174. }
  175. if(pGroups)
  176. {
  177. LocalFree(pGroups);
  178. }
  179. if(S_OK==hr)
  180. {
  181. return TRUE;
  182. }
  183. else
  184. {
  185. SetLastError(HRESULT_CODE(hr));
  186. return FALSE;
  187. }
  188. }
  189. HRESULT GetMembership(
  190. IN AUTHZ_RESOURCE_MANAGER_HANDLE AuthzRM,
  191. IN PSID pSid,
  192. PTOKEN_GROUPS *ppGroups)
  193. {
  194. HRESULT hr = S_OK;
  195. static LUID luid = {0,0};
  196. AUTHZ_CLIENT_CONTEXT_HANDLE AuthzCC = NULL;
  197. DWORD dwSizeRequired;
  198. *ppGroups = NULL;
  199. if(!AuthzInitializeContextFromSid(
  200. 0,
  201. pSid,
  202. AuthzRM,
  203. NULL,
  204. luid, //ignored
  205. NULL,
  206. &AuthzCC))
  207. {
  208. hr = myHError(GetLastError());
  209. _JumpError(hr, error, "AuthzInitializeContextFromSid");
  210. }
  211. if(!AuthzGetInformationFromContext(
  212. AuthzCC,
  213. AuthzContextInfoGroupsSids,
  214. 0,
  215. &dwSizeRequired,
  216. NULL))
  217. {
  218. if(ERROR_INSUFFICIENT_BUFFER!=GetLastError())
  219. {
  220. hr = myHError(GetLastError());
  221. _JumpError(hr, error, "AuthzGetContextInformation");
  222. }
  223. }
  224. *ppGroups = (PTOKEN_GROUPS)LocalAlloc(LMEM_FIXED, dwSizeRequired);
  225. if(!*ppGroups)
  226. {
  227. hr = E_OUTOFMEMORY;
  228. _JumpError(hr, error, "LocalAlloc");
  229. }
  230. if(!AuthzGetInformationFromContext(
  231. AuthzCC,
  232. AuthzContextInfoGroupsSids,
  233. dwSizeRequired,
  234. &dwSizeRequired,
  235. *ppGroups))
  236. {
  237. hr = myHError(GetLastError());
  238. _JumpError(hr, error, "AuthzGetContextInformation");
  239. }
  240. error:
  241. if(AuthzCC)
  242. {
  243. AuthzFreeContext(AuthzCC);
  244. }
  245. if(S_OK!=hr && *ppGroups)
  246. {
  247. LocalFree(*ppGroups);
  248. }
  249. return hr;
  250. }
  251. HRESULT GetRequesterName(DWORD dwRequestId, LPWSTR *ppwszName)
  252. {
  253. HRESULT hr = S_OK;
  254. ICertDBRow *prow = NULL;
  255. hr = g_pCertDB->OpenRow(
  256. PROPOPEN_READONLY | PROPTABLE_REQCERT,
  257. dwRequestId,
  258. NULL,
  259. &prow);
  260. _JumpIfError(hr, error, "OpenRow");
  261. hr = PKCSGetProperty(
  262. prow,
  263. g_wszPropRequesterName,
  264. PROPTYPE_STRING | PROPCALLER_SERVER | PROPTABLE_REQUEST,
  265. NULL,
  266. (BYTE **) ppwszName);
  267. _JumpIfError(hr, error, "PKCSGetProperty");
  268. error:
  269. if(prow)
  270. {
  271. prow->Release();
  272. }
  273. return hr;
  274. }
  275. HRESULT CheckOfficerRights(DWORD dwRequestID, CAuditEvent& event)
  276. {
  277. HRESULT hr = S_OK;
  278. LPWSTR pwszRequesterName = NULL;
  279. // officer rights disabled means every officer is allowed to manage requests
  280. // for everyone, so return ok
  281. if(!g_OfficerRightsSD.IsEnabled())
  282. return S_OK;
  283. hr = GetRequesterName(dwRequestID, &pwszRequesterName);
  284. if(CERTSRV_E_PROPERTY_EMPTY!=hr &&
  285. S_OK != hr)
  286. {
  287. _JumpError(hr, error, "GetRequesterName");
  288. }
  289. hr = CheckOfficerRights(pwszRequesterName, event);
  290. error:
  291. if(pwszRequesterName)
  292. {
  293. LocalFree(pwszRequesterName);
  294. }
  295. return hr;
  296. }
  297. // Verify if impersonated user has the rights over the specified request,
  298. // based on the officer rights defined in the global officer SD and the
  299. // requester name stored in the request
  300. // Return S_OK if allowed or if the officer rights feature is disabled
  301. // E_ACCESSDENIED if not allowed
  302. // E_* if failed to check the rights
  303. HRESULT CheckOfficerRights(LPCWSTR pwszRequesterName, CAuditEvent& event)
  304. {
  305. HRESULT hr = S_OK;
  306. IServerSecurity *pISS = NULL;
  307. HANDLE hThread = NULL;
  308. HANDLE hToken = NULL;
  309. PSECURITY_DESCRIPTOR pOfficerSD = NULL;
  310. static LUID luid = {0,0};
  311. AUTHZ_CLIENT_CONTEXT_HANDLE AuthzCC = NULL;
  312. AUTHZ_ACCESS_REQUEST AuthzRequest;
  313. AUTHZ_ACCESS_REPLY AuthzReply;
  314. ACCESS_MASK GrantedMask;
  315. DWORD dwError = 0;
  316. DWORD dwSaclEval = 0;
  317. bool fImpersonating = false;
  318. // officer rights disabled means every officer is allowed to manage requests
  319. // for everyone, so return ok
  320. if(!g_OfficerRightsSD.IsEnabled())
  321. return S_OK;
  322. hr = CoGetCallContext(IID_IServerSecurity, (void**)&pISS);
  323. _JumpIfError(hr, error, "CoGetCallContext");
  324. if (!pISS->IsImpersonating())
  325. {
  326. hr = pISS->ImpersonateClient();
  327. _JumpIfError(hr, error, "ImpersonateClient");
  328. }
  329. else
  330. {
  331. pISS->Release();
  332. pISS = NULL;
  333. }
  334. hThread = GetCurrentThread();
  335. if (NULL == hThread)
  336. {
  337. hr = myHLastError();
  338. _JumpIfError(hr, error, "GetCurrentThread");
  339. }
  340. if (!OpenThreadToken(hThread,
  341. TOKEN_QUERY,
  342. FALSE, // client impersonation
  343. &hToken))
  344. {
  345. hr = myHLastError();
  346. _JumpIfError(hr, error, "OpenThreadToken");
  347. }
  348. if(!AuthzInitializeContextFromToken(
  349. 0,
  350. hToken,
  351. g_AuthzCertSrvRM,
  352. NULL,
  353. luid,
  354. NULL,
  355. &AuthzCC))
  356. {
  357. hr = myHLastError();
  358. _JumpError(hr, error, "AuthzInitializeContextFromToken");
  359. }
  360. CloseHandle(hToken);
  361. hToken = NULL;
  362. if(pISS) // impersonating
  363. {
  364. pISS->RevertToSelf();
  365. pISS->Release();
  366. pISS = NULL;
  367. }
  368. AuthzRequest.DesiredAccess = DELETE;
  369. AuthzRequest.PrincipalSelfSid = NULL;
  370. AuthzRequest.ObjectTypeList = NULL;
  371. AuthzRequest.ObjectTypeListLength = 0;
  372. AuthzRequest.OptionalArguments = (void*)pwszRequesterName;
  373. AuthzReply.ResultListLength = 1;
  374. AuthzReply.GrantedAccessMask = &GrantedMask;
  375. AuthzReply.Error = &dwError;
  376. AuthzReply.SaclEvaluationResults = &dwSaclEval;
  377. hr = g_OfficerRightsSD.LockGet(&pOfficerSD);
  378. _JumpIfError(hr, error, "CProtectedSecurityDescriptor::LockGet");
  379. CSASSERT(IsValidSecurityDescriptor(pOfficerSD));
  380. if(!AuthzAccessCheck(
  381. 0,
  382. AuthzCC,
  383. &AuthzRequest,
  384. NULL, //no audit
  385. pOfficerSD,
  386. NULL,
  387. 0,
  388. &AuthzReply,
  389. NULL))
  390. {
  391. hr = myHLastError();
  392. _JumpError(hr, error, "AuthzAccessCheck");
  393. }
  394. hr = AuthzReply.Error[0]==ERROR_SUCCESS?S_OK:CERTSRV_E_RESTRICTEDOFFICER;
  395. error:
  396. if(AuthzCC)
  397. {
  398. AuthzFreeContext(AuthzCC);
  399. }
  400. if(pOfficerSD)
  401. {
  402. g_OfficerRightsSD.Unlock();
  403. }
  404. if (NULL != hThread)
  405. {
  406. CloseHandle(hThread);
  407. }
  408. if (NULL != hToken)
  409. {
  410. CloseHandle(hToken);
  411. }
  412. if (NULL != pISS)
  413. {
  414. pISS->RevertToSelf();
  415. pISS->Release();
  416. }
  417. // generate a failure audit event if restricted officer
  418. if(CERTSRV_E_RESTRICTEDOFFICER==hr)
  419. {
  420. HRESULT hrtemp = event.AccessCheck(
  421. CA_ACCESS_DENIED,
  422. event.m_gcNoAuditSuccess);
  423. if(S_OK!=hrtemp && E_ACCESSDENIED!=hrtemp)
  424. hr = hrtemp;
  425. }
  426. return hr;
  427. }
  428. } // namespace CertSrv