PageRenderTime 52ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 0ms

/mcs/class/referencesource/System.ServiceModel/System/ServiceModel/ComIntegration/ComPlusAuthorization.cs

https://github.com/pruiz/mono
C# | 618 lines | 528 code | 70 blank | 20 comment | 82 complexity | d2dd39af189550930e971cdb6dedce93 MD5 | raw file
Possible License(s): LGPL-2.0, MPL-2.0-no-copyleft-exception, CC-BY-SA-3.0, GPL-2.0
  1. //----------------------------------------------------------------------------
  2. // Copyright (c) Microsoft Corporation. All rights reserved.
  3. //----------------------------------------------------------------------------
  4. namespace System.ServiceModel.ComIntegration
  5. {
  6. using System;
  7. using System.Collections.Generic;
  8. using System.ComponentModel;
  9. using System.Runtime;
  10. using System.Runtime.InteropServices;
  11. using System.Security;
  12. using System.Security.AccessControl;
  13. using System.Security.Permissions;
  14. using System.Security.Principal;
  15. using System.ServiceModel;
  16. using System.ServiceModel.Diagnostics;
  17. using SafeCloseHandle = System.IdentityModel.SafeCloseHandle;
  18. using SafeHGlobalHandle = System.IdentityModel.SafeHGlobalHandle;
  19. static class SecurityUtils
  20. {
  21. static WindowsIdentity anonymousIdentity;
  22. static WindowsIdentity processIdentity;
  23. static object lockObject = new object();
  24. [Fx.Tag.SecurityNote(Critical = "Uses critical type SafeHGlobalHandle.",
  25. Safe = "Performs a Demand for full trust.")]
  26. [SecuritySafeCritical]
  27. [SecurityPermission(SecurityAction.Demand, Unrestricted = true)]
  28. public static SafeHandle GetTokenInformation(SafeCloseHandle token, TOKEN_INFORMATION_CLASS infoClass)
  29. {
  30. uint length;
  31. if (!SafeNativeMethods.GetTokenInformation(token, infoClass, SafeHGlobalHandle.InvalidHandle, 0, out length))
  32. {
  33. int error = Marshal.GetLastWin32Error();
  34. if (error != (int)Win32Error.ERROR_INSUFFICIENT_BUFFER)
  35. {
  36. throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new Win32Exception(error, SR.GetString(SR.GetTokenInfoFailed, error)));
  37. }
  38. }
  39. SafeHandle buffer = SafeHGlobalHandle.AllocHGlobal(length);
  40. try
  41. {
  42. if (!SafeNativeMethods.GetTokenInformation(token, infoClass, buffer, length, out length))
  43. {
  44. int error = Marshal.GetLastWin32Error();
  45. throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new Win32Exception(error, SR.GetString(SR.GetTokenInfoFailed, error)));
  46. }
  47. }
  48. catch
  49. {
  50. buffer.Dispose();
  51. throw;
  52. }
  53. return buffer;
  54. }
  55. internal static bool IsAtleastImpersonationToken(SafeCloseHandle token)
  56. {
  57. using (SafeHandle buffer =
  58. GetTokenInformation(token, TOKEN_INFORMATION_CLASS.TokenImpersonationLevel))
  59. {
  60. int level = Marshal.ReadInt32(buffer.DangerousGetHandle());
  61. if (level < (int)SecurityImpersonationLevel.Impersonation)
  62. return false;
  63. else
  64. return true;
  65. }
  66. }
  67. internal static bool IsPrimaryToken(SafeCloseHandle token)
  68. {
  69. using (SafeHandle buffer =
  70. GetTokenInformation(token, TOKEN_INFORMATION_CLASS.TokenType))
  71. {
  72. int level = Marshal.ReadInt32(buffer.DangerousGetHandle());
  73. return (level == (int)TokenType.TokenPrimary);
  74. }
  75. }
  76. internal static LUID GetModifiedIDLUID(SafeCloseHandle token)
  77. {
  78. using (SafeHandle buffer =
  79. GetTokenInformation(token, TOKEN_INFORMATION_CLASS.TokenStatistics))
  80. {
  81. TOKEN_STATISTICS tokenStats = (TOKEN_STATISTICS)
  82. Marshal.PtrToStructure(buffer.DangerousGetHandle(), typeof(TOKEN_STATISTICS));
  83. return tokenStats.ModifiedId;
  84. }
  85. }
  86. public static WindowsIdentity GetAnonymousIdentity()
  87. {
  88. SafeCloseHandle tokenHandle = null;
  89. bool isImpersonating = false;
  90. lock (lockObject)
  91. {
  92. if (anonymousIdentity == null)
  93. {
  94. try
  95. {
  96. try
  97. {
  98. if (!SafeNativeMethods.ImpersonateAnonymousUserOnCurrentThread(SafeNativeMethods.GetCurrentThread()))
  99. {
  100. int error = Marshal.GetLastWin32Error();
  101. throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new Win32Exception(error, SR.GetString(SR.ImpersonateAnonymousTokenFailed, error)));
  102. }
  103. isImpersonating = true;
  104. bool revertSuccess;
  105. bool isSuccess = SafeNativeMethods.OpenCurrentThreadToken(SafeNativeMethods.GetCurrentThread(), TokenAccessLevels.Query, true, out tokenHandle);
  106. if (!isSuccess)
  107. {
  108. int error = Marshal.GetLastWin32Error();
  109. revertSuccess = SafeNativeMethods.RevertToSelf();
  110. if (false == revertSuccess)
  111. {
  112. error = Marshal.GetLastWin32Error();
  113. //this requires a failfast since failure to revert impersonation compromises security
  114. DiagnosticUtility.FailFast("RevertToSelf() failed with " + error);
  115. }
  116. isImpersonating = false;
  117. Utility.CloseInvalidOutSafeHandle(tokenHandle);
  118. throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new Win32Exception(error, SR.GetString(SR.OpenThreadTokenFailed, error)));
  119. }
  120. revertSuccess = SafeNativeMethods.RevertToSelf();
  121. if (false == revertSuccess)
  122. {
  123. int error = Marshal.GetLastWin32Error();
  124. //this requires a failfast since failure to revert impersonation compromises security
  125. DiagnosticUtility.FailFast("RevertToSelf() failed with " + error);
  126. }
  127. isImpersonating = false;
  128. using (tokenHandle)
  129. {
  130. anonymousIdentity = new WindowsIdentity(tokenHandle.DangerousGetHandle());
  131. }
  132. }
  133. finally
  134. {
  135. if (isImpersonating)
  136. {
  137. bool revertSuccess = SafeNativeMethods.RevertToSelf();
  138. if (false == revertSuccess)
  139. {
  140. int error = Marshal.GetLastWin32Error();
  141. //this requires a failfast since failure to revert impersonation compromises security
  142. DiagnosticUtility.FailFast("RevertToSelf() failed with " + error);
  143. }
  144. }
  145. }
  146. }
  147. catch
  148. {
  149. // Force the finally to run before leaving the method.
  150. throw;
  151. }
  152. }
  153. }
  154. return anonymousIdentity;
  155. }
  156. public static WindowsIdentity GetProcessIdentity()
  157. {
  158. SafeCloseHandle tokenHandle = null;
  159. lock (lockObject)
  160. {
  161. try
  162. {
  163. bool isSuccess = SafeNativeMethods.GetCurrentProcessToken(SafeNativeMethods.GetCurrentProcess(), TokenAccessLevels.Query, out tokenHandle);
  164. if (!isSuccess)
  165. {
  166. int error = Marshal.GetLastWin32Error();
  167. Utility.CloseInvalidOutSafeHandle(tokenHandle);
  168. throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new Win32Exception(error, SR.GetString(SR.OpenProcessTokenFailed, error)));
  169. }
  170. processIdentity = new WindowsIdentity(tokenHandle.DangerousGetHandle());
  171. }
  172. finally
  173. {
  174. if (tokenHandle != null)
  175. tokenHandle.Dispose();
  176. }
  177. }
  178. return processIdentity;
  179. }
  180. }
  181. internal sealed class ComPlusAuthorization
  182. {
  183. string[] serviceRoleMembers = null;
  184. string[] contractRoleMembers = null;
  185. string[] operationRoleMembers = null;
  186. CommonSecurityDescriptor securityDescriptor = null;
  187. static SecurityIdentifier sidAdministrators = new SecurityIdentifier(WellKnownSidType.BuiltinAdministratorsSid, null);
  188. Dictionary<LUID, bool> accessCheckCache = new Dictionary<LUID, bool>();
  189. public ComPlusAuthorization(string[] serviceRoleMembers, string[] contractRoleMembers, string[] operationRoleMembers)
  190. {
  191. this.serviceRoleMembers = serviceRoleMembers;
  192. this.contractRoleMembers = contractRoleMembers;
  193. this.operationRoleMembers = operationRoleMembers;
  194. }
  195. private void BuildSecurityDescriptor()
  196. {
  197. Fx.Assert((null == securityDescriptor), "SecurityDescriptor must be NULL");
  198. NTAccount name;
  199. SecurityIdentifier sid;
  200. CommonAce ace;
  201. RawAcl acl = new RawAcl(GenericAcl.AclRevision, 1);
  202. int index = 0;
  203. if (operationRoleMembers != null)
  204. {
  205. foreach (string userName in operationRoleMembers)
  206. {
  207. name = new NTAccount(userName);
  208. sid = (SecurityIdentifier)name.Translate(typeof(SecurityIdentifier));
  209. ace = new CommonAce(AceFlags.None, AceQualifier.AccessAllowed, (int)ComRights.EXECUTE, sid, false, null);
  210. acl.InsertAce(index, ace);
  211. index++;
  212. }
  213. }
  214. if (contractRoleMembers != null)
  215. {
  216. foreach (string userName in contractRoleMembers)
  217. {
  218. name = new NTAccount(userName);
  219. sid = (SecurityIdentifier)name.Translate(typeof(SecurityIdentifier));
  220. ace = new CommonAce(AceFlags.None, AceQualifier.AccessAllowed, (int)ComRights.EXECUTE, sid, false, null);
  221. acl.InsertAce(index, ace);
  222. index++;
  223. }
  224. }
  225. if (serviceRoleMembers != null)
  226. {
  227. foreach (string userName in serviceRoleMembers)
  228. {
  229. name = new NTAccount(userName);
  230. sid = (SecurityIdentifier)name.Translate(typeof(SecurityIdentifier));
  231. ace = new CommonAce(AceFlags.None, AceQualifier.AccessAllowed, (int)ComRights.EXECUTE, sid, false, null);
  232. acl.InsertAce(index, ace);
  233. index++;
  234. }
  235. }
  236. DiscretionaryAcl dacl = new DiscretionaryAcl(true, false, acl);
  237. securityDescriptor = new CommonSecurityDescriptor(true, false, ControlFlags.DiscretionaryAclPresent, sidAdministrators, sidAdministrators, null, dacl);
  238. }
  239. private bool IsAccessCached(LUID luidModifiedID, out bool isAccessAllowed)
  240. {
  241. if (null == accessCheckCache)
  242. {
  243. throw Fx.AssertAndThrowFatal("AcessCheckCache must not be NULL");
  244. }
  245. bool retValue = false;
  246. lock (this)
  247. {
  248. retValue = accessCheckCache.TryGetValue(luidModifiedID, out isAccessAllowed);
  249. }
  250. return retValue;
  251. }
  252. private void CacheAccessCheck(LUID luidModifiedID, bool isAccessAllowed)
  253. {
  254. if (null == accessCheckCache)
  255. {
  256. throw Fx.AssertAndThrowFatal("AcessCheckCache must not be NULL");
  257. }
  258. lock (this)
  259. {
  260. accessCheckCache[luidModifiedID] = isAccessAllowed;
  261. }
  262. }
  263. private void CheckAccess(WindowsIdentity clientIdentity, out bool IsAccessAllowed)
  264. {
  265. if (null == securityDescriptor)
  266. {
  267. throw Fx.AssertAndThrowFatal("Security Descriptor must not be NULL");
  268. }
  269. IsAccessAllowed = false;
  270. byte[] BinaryForm = new byte[securityDescriptor.BinaryLength];
  271. securityDescriptor.GetBinaryForm(BinaryForm, 0);
  272. SafeCloseHandle ImpersonationToken = null;
  273. SafeCloseHandle clientIdentityToken = new SafeCloseHandle(clientIdentity.Token, false);
  274. try
  275. {
  276. if (SecurityUtils.IsPrimaryToken(clientIdentityToken))
  277. {
  278. if (!SafeNativeMethods.DuplicateTokenEx(clientIdentityToken,
  279. TokenAccessLevels.Query,
  280. IntPtr.Zero,
  281. SecurityImpersonationLevel.Identification,
  282. TokenType.TokenImpersonation,
  283. out ImpersonationToken))
  284. {
  285. int error = Marshal.GetLastWin32Error();
  286. Utility.CloseInvalidOutSafeHandle(ImpersonationToken);
  287. throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new Win32Exception(error, SR.GetString(SR.DuplicateTokenExFailed, error)));
  288. }
  289. }
  290. GENERIC_MAPPING GenericMapping = new GENERIC_MAPPING();
  291. PRIVILEGE_SET PrivilegeSet = new PRIVILEGE_SET();
  292. uint PrivilegeSetLength = (uint)Marshal.SizeOf(PrivilegeSet);
  293. uint GrantedAccess = 0;
  294. if (!SafeNativeMethods.AccessCheck(BinaryForm, (ImpersonationToken != null) ? ImpersonationToken : clientIdentityToken,
  295. (int)ComRights.EXECUTE, GenericMapping, out PrivilegeSet,
  296. ref PrivilegeSetLength, out GrantedAccess, out IsAccessAllowed))
  297. {
  298. int error = Marshal.GetLastWin32Error();
  299. throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new Win32Exception(error, SR.GetString(SR.AccessCheckFailed, error)));
  300. }
  301. }
  302. finally
  303. {
  304. if (ImpersonationToken != null)
  305. ImpersonationToken.Dispose();
  306. }
  307. }
  308. public string[] ServiceRoleMembers
  309. {
  310. get
  311. {
  312. return serviceRoleMembers;
  313. }
  314. }
  315. public string[] ContractRoleMembers
  316. {
  317. get
  318. {
  319. return contractRoleMembers;
  320. }
  321. }
  322. public string[] OperationRoleMembers
  323. {
  324. get
  325. {
  326. return operationRoleMembers;
  327. }
  328. }
  329. public CommonSecurityDescriptor SecurityDescriptor
  330. {
  331. get
  332. {
  333. return securityDescriptor;
  334. }
  335. }
  336. public bool IsAuthorizedForOperation(WindowsIdentity clientIdentity)
  337. {
  338. bool IsAccessAllowed = false;
  339. if (null == clientIdentity)
  340. {
  341. throw Fx.AssertAndThrow("NULL Identity");
  342. }
  343. if (IntPtr.Zero == clientIdentity.Token)
  344. {
  345. throw Fx.AssertAndThrow("Token handle cannot be zero");
  346. }
  347. lock (this)
  348. {
  349. if (securityDescriptor == null)
  350. {
  351. BuildSecurityDescriptor();
  352. }
  353. }
  354. LUID luidModified = SecurityUtils.GetModifiedIDLUID(new SafeCloseHandle(clientIdentity.Token, false));
  355. if (IsAccessCached(luidModified, out IsAccessAllowed))
  356. return IsAccessAllowed;
  357. CheckAccess(clientIdentity, out IsAccessAllowed);
  358. CacheAccessCheck(luidModified, IsAccessAllowed);
  359. return IsAccessAllowed;
  360. }
  361. }
  362. internal sealed class ComPlusServerSecurity : IContextSecurityPerimeter, IServerSecurity, IDisposable
  363. {
  364. WindowsIdentity clientIdentity = null;
  365. IntPtr oldSecurityObject = IntPtr.Zero;
  366. WindowsImpersonationContext impersonateContext = null;
  367. bool isImpersonating = false;
  368. bool shouldUseCallContext = false;
  369. const uint RPC_C_AUTHN_GSS_NEGOTIATE = 9;
  370. const uint RPC_C_AUTHN_WINNT = 10;
  371. const uint RPC_C_AUTHN_GSS_KERBEROS = 16;
  372. const uint RPC_C_AUTHN_DEFAULT = unchecked((uint)0xFFFFFFFF);
  373. const uint RPC_C_AUTHZ_NONE = 0;
  374. const uint RPC_C_AUTHN_LEVEL_DEFAULT = 0;
  375. const uint RPC_C_AUTHN_LEVEL_NONE = 1;
  376. const uint RPC_C_AUTHN_LEVEL_CONNECT = 2;
  377. const uint RPC_C_AUTHN_LEVEL_CALL = 3;
  378. const uint RPC_C_AUTHN_LEVEL_PKT = 4;
  379. const uint RPC_C_AUTHN_LEVEL_PKT_INTEGRITY = 5;
  380. const uint RPC_C_AUTHN_LEVEL_PKT_PRIVACY = 6;
  381. public ComPlusServerSecurity(WindowsIdentity clientIdentity, bool shouldUseCallContext)
  382. {
  383. if (null == clientIdentity)
  384. {
  385. throw Fx.AssertAndThrow("NULL Identity");
  386. }
  387. if (IntPtr.Zero == clientIdentity.Token)
  388. {
  389. throw Fx.AssertAndThrow("Token handle cannot be zero");
  390. }
  391. this.shouldUseCallContext = shouldUseCallContext;
  392. this.clientIdentity = clientIdentity;
  393. IntPtr secCtx = Marshal.GetIUnknownForObject(this);
  394. try
  395. {
  396. oldSecurityObject = SafeNativeMethods.CoSwitchCallContext(secCtx);
  397. }
  398. catch
  399. {
  400. Marshal.Release(secCtx);
  401. throw;
  402. }
  403. }
  404. ~ComPlusServerSecurity()
  405. {
  406. Dispose(false);
  407. }
  408. public bool GetPerimeterFlag()
  409. {
  410. return shouldUseCallContext;
  411. }
  412. public void SetPerimeterFlag(bool flag)
  413. {
  414. shouldUseCallContext = flag;
  415. }
  416. public void QueryBlanket
  417. (
  418. IntPtr authnSvc,
  419. IntPtr authzSvc,
  420. IntPtr serverPrincipalName,
  421. IntPtr authnLevel,
  422. IntPtr impLevel,
  423. IntPtr clientPrincipalName,
  424. IntPtr Capabilities
  425. )
  426. {
  427. // Convert to RPC'isms.
  428. if (authnSvc != IntPtr.Zero)
  429. {
  430. uint tempAuthnSvc = RPC_C_AUTHN_DEFAULT;
  431. // Try to convert the clientIdentity.AuthenticationType to an RPC constant.
  432. // This is a best case attempt.
  433. string authenticationType = clientIdentity.AuthenticationType;
  434. if (authenticationType.ToUpperInvariant() == "NTLM")
  435. tempAuthnSvc = RPC_C_AUTHN_WINNT;
  436. else if (authenticationType.ToUpperInvariant() == "KERBEROS")
  437. tempAuthnSvc = RPC_C_AUTHN_GSS_KERBEROS;
  438. else if (authenticationType.ToUpperInvariant() == "NEGOTIATE")
  439. tempAuthnSvc = RPC_C_AUTHN_GSS_NEGOTIATE;
  440. Marshal.WriteInt32(authnSvc, (int)tempAuthnSvc);
  441. }
  442. if (authzSvc != IntPtr.Zero)
  443. {
  444. Marshal.WriteInt32(authzSvc, (int)RPC_C_AUTHZ_NONE);
  445. }
  446. if (serverPrincipalName != IntPtr.Zero)
  447. {
  448. IntPtr str = Marshal.StringToCoTaskMemUni(SecurityUtils.GetProcessIdentity().Name);
  449. Marshal.WriteIntPtr(serverPrincipalName, str);
  450. }
  451. // There is no equivalent for the RPC authn level. It can only be
  452. // approximated, in the best case. Use default.
  453. if (authnLevel != IntPtr.Zero)
  454. {
  455. Marshal.WriteInt32(authnLevel, (int)RPC_C_AUTHN_LEVEL_DEFAULT);
  456. }
  457. if (impLevel != IntPtr.Zero)
  458. {
  459. Marshal.WriteInt32(impLevel, 0);
  460. }
  461. if (clientPrincipalName != IntPtr.Zero)
  462. {
  463. IntPtr str = Marshal.StringToCoTaskMemUni(clientIdentity.Name);
  464. Marshal.WriteIntPtr(clientPrincipalName, str);
  465. }
  466. if (Capabilities != IntPtr.Zero)
  467. {
  468. Marshal.WriteInt32(Capabilities, 0);
  469. }
  470. }
  471. public int ImpersonateClient()
  472. {
  473. // We want to return known COM hresults here rather than random CLR-Exception mapped HRESULTS. Also,
  474. // we don't want CLR to set the ErrorInfo object.
  475. int hresult = HR.E_FAIL;
  476. try
  477. {
  478. impersonateContext = WindowsIdentity.Impersonate(clientIdentity.Token);
  479. isImpersonating = true;
  480. hresult = HR.S_OK;
  481. }
  482. catch (SecurityException)
  483. {
  484. // Special case anonymous impersonation failure.
  485. // Unmanaged callers to ImpersonateClient expect this hresult.
  486. hresult = HR.RPC_NT_BINDING_HAS_NO_AUTH;
  487. }
  488. catch (Exception e)
  489. {
  490. if (Fx.IsFatal(e))
  491. throw;
  492. }
  493. return hresult;
  494. }
  495. public int RevertToSelf()
  496. {
  497. // We want to return known COM hresults here rather than random CLR-Exception mapped HRESULTS. Also,
  498. // we don't want CLR to set the ErrorInfo object.
  499. int hresult = HR.E_FAIL;
  500. if (isImpersonating)
  501. {
  502. try
  503. {
  504. impersonateContext.Undo();
  505. isImpersonating = false;
  506. hresult = HR.S_OK;
  507. }
  508. catch (Exception e)
  509. {
  510. if (Fx.IsFatal(e))
  511. throw;
  512. }
  513. }
  514. return hresult;
  515. }
  516. public bool IsImpersonating()
  517. {
  518. return isImpersonating;
  519. }
  520. void IDisposable.Dispose()
  521. {
  522. Dispose(true);
  523. GC.SuppressFinalize(this);
  524. }
  525. public void Dispose(bool disposing)
  526. {
  527. RevertToSelf();
  528. IntPtr secCtx = SafeNativeMethods.CoSwitchCallContext(oldSecurityObject);
  529. if (IntPtr.Zero == secCtx)
  530. {
  531. // this has to be a failfast since not having a security context can compromise security
  532. DiagnosticUtility.FailFast("Security Context was should not be null");
  533. }
  534. if (Marshal.GetObjectForIUnknown(secCtx) != this)
  535. {
  536. // this has to be a failfast since being in the wrong security context can compromise security
  537. DiagnosticUtility.FailFast("Security Context was modified from underneath us");
  538. }
  539. Marshal.Release(secCtx);
  540. if (disposing)
  541. {
  542. clientIdentity = null;
  543. if (impersonateContext != null)
  544. impersonateContext.Dispose();
  545. }
  546. }
  547. }
  548. }