PageRenderTime 27ms CodeModel.GetById 31ms RepoModel.GetById 1ms app.codeStats 0ms

/mcs/class/corlib/System.Security.Principal/corefx/WindowsIdentity.cs

https://github.com/madewokherd/mono
C# | 1161 lines | 870 code | 167 blank | 124 comment | 154 complexity | 1c7ac3ca42261ece02ee783811706690 MD5 | raw file
  1. // Licensed to the .NET Foundation under one or more agreements.
  2. // The .NET Foundation licenses this file to you under the MIT license.
  3. // See the LICENSE file in the project root for more information.
  4. using Microsoft.Win32.SafeHandles;
  5. using System.Collections.Generic;
  6. using System.Diagnostics;
  7. using System.Globalization;
  8. using System.Runtime.InteropServices;
  9. using System.Security.Claims;
  10. using System.Security.Permissions;
  11. using System.Text;
  12. using System.Threading;
  13. using KERB_LOGON_SUBMIT_TYPE = Interop.SspiCli.KERB_LOGON_SUBMIT_TYPE;
  14. using KERB_S4U_LOGON = Interop.SspiCli.KERB_S4U_LOGON;
  15. using KerbS4uLogonFlags = Interop.SspiCli.KerbS4uLogonFlags;
  16. using LUID = Interop.LUID;
  17. using LSA_STRING = Interop.SspiCli.LSA_STRING;
  18. using QUOTA_LIMITS = Interop.SspiCli.QUOTA_LIMITS;
  19. using SECURITY_LOGON_TYPE = Interop.SspiCli.SECURITY_LOGON_TYPE;
  20. using TOKEN_SOURCE = Interop.SspiCli.TOKEN_SOURCE;
  21. using System.Runtime.Serialization;
  22. namespace System.Security.Principal
  23. {
  24. [Serializable]
  25. public class WindowsIdentity : ClaimsIdentity, IDisposable, ISerializable, IDeserializationCallback
  26. {
  27. private string _name = null;
  28. private SecurityIdentifier _owner = null;
  29. private SecurityIdentifier _user = null;
  30. private IdentityReferenceCollection _groups = null;
  31. private SerializationInfo _info;
  32. private SafeAccessTokenHandle _safeTokenHandle = SafeAccessTokenHandle.InvalidHandle;
  33. private string _authType = null;
  34. private int _isAuthenticated = -1;
  35. private volatile TokenImpersonationLevel _impersonationLevel;
  36. private volatile bool _impersonationLevelInitialized;
  37. public new const string DefaultIssuer = @"AD AUTHORITY";
  38. private string _issuerName = DefaultIssuer;
  39. private object _claimsIntiailizedLock = new object();
  40. private volatile bool _claimsInitialized;
  41. private List<Claim> _deviceClaims;
  42. private List<Claim> _userClaims;
  43. private static bool s_ignoreWindows8Properties;
  44. //
  45. // Constructors.
  46. //
  47. private WindowsIdentity()
  48. : base(null, null, null, ClaimTypes.Name, ClaimTypes.GroupSid)
  49. { }
  50. /// <summary>
  51. /// Initializes a new instance of the WindowsIdentity class for the user represented by the specified User Principal Name (UPN).
  52. /// </summary>
  53. /// <remarks>
  54. /// Unlike the desktop version, we connect to Lsa only as an untrusted caller. We do not attempt to exploit Tcb privilege or adjust the current
  55. /// thread privilege to include Tcb.
  56. /// </remarks>
  57. public WindowsIdentity(string sUserPrincipalName)
  58. : base(null, null, null, ClaimTypes.Name, ClaimTypes.GroupSid)
  59. {
  60. // Desktop compat: See comments below for why we don't validate sUserPrincipalName.
  61. using (SafeLsaHandle lsaHandle = ConnectToLsa())
  62. {
  63. int packageId = LookupAuthenticationPackage(lsaHandle, Interop.SspiCli.AuthenticationPackageNames.MICROSOFT_KERBEROS_NAME_A);
  64. // 8 byte or less name that indicates the source of the access token. This choice of name is visible to callers through the native GetTokenInformation() api
  65. // so we'll use the same name the CLR used even though we're not actually the "CLR."
  66. byte[] sourceName = { (byte)'C', (byte)'L', (byte)'R', (byte)0 };
  67. TOKEN_SOURCE sourceContext;
  68. if (!Interop.Advapi32.AllocateLocallyUniqueId(out sourceContext.SourceIdentifier))
  69. throw new SecurityException(string.Format(CultureInfo.InvariantCulture, "AllocateLocallyUniqueId returned error {0}", Marshal.GetLastWin32Error()));
  70. sourceContext.SourceName = new byte[TOKEN_SOURCE.TOKEN_SOURCE_LENGTH];
  71. Buffer.BlockCopy(sourceName, 0, sourceContext.SourceName, 0, sourceName.Length);
  72. // Desktop compat: Desktop never null-checks sUserPrincipalName. Actual behavior is that the null makes it down to Encoding.Unicode.GetBytes() which then throws
  73. // the ArgumentNullException (provided that the prior LSA calls didn't fail first.) To make this compat decision explicit, we'll null check ourselves
  74. // and simulate the exception from Encoding.Unicode.GetBytes().
  75. if (sUserPrincipalName == null)
  76. throw new ArgumentNullException("s");
  77. byte[] upnBytes = Encoding.Unicode.GetBytes(sUserPrincipalName);
  78. if (upnBytes.Length > ushort.MaxValue)
  79. {
  80. // Desktop compat: LSA only allocates 16 bits to hold the UPN size. We should throw an exception here but unfortunately, the desktop did an unchecked cast to ushort,
  81. // effectively truncating upnBytes to the first (N % 64K) bytes. We'll simulate the same behavior here (albeit in a way that makes it look less accidental.)
  82. Array.Resize(ref upnBytes, upnBytes.Length & ushort.MaxValue);
  83. }
  84. unsafe
  85. {
  86. //
  87. // Build the KERB_S4U_LOGON structure. Note that the LSA expects this entire
  88. // structure to be contained within the same block of memory, so we need to allocate
  89. // enough room for both the structure itself and the UPN string in a single buffer
  90. // and do the marshalling into this buffer by hand.
  91. //
  92. int authenticationInfoLength = checked(sizeof(KERB_S4U_LOGON) + upnBytes.Length);
  93. using (SafeLocalAllocHandle authenticationInfo = Interop.Kernel32.LocalAlloc(0, new UIntPtr(checked((uint)authenticationInfoLength))))
  94. {
  95. if (authenticationInfo.IsInvalid)
  96. throw new OutOfMemoryException();
  97. KERB_S4U_LOGON* pKerbS4uLogin = (KERB_S4U_LOGON*)(authenticationInfo.DangerousGetHandle());
  98. pKerbS4uLogin->MessageType = KERB_LOGON_SUBMIT_TYPE.KerbS4ULogon;
  99. pKerbS4uLogin->Flags = KerbS4uLogonFlags.None;
  100. pKerbS4uLogin->ClientUpn.Length = pKerbS4uLogin->ClientUpn.MaximumLength = checked((ushort)upnBytes.Length);
  101. IntPtr pUpnOffset = (IntPtr)(pKerbS4uLogin + 1);
  102. pKerbS4uLogin->ClientUpn.Buffer = pUpnOffset;
  103. Marshal.Copy(upnBytes, 0, pKerbS4uLogin->ClientUpn.Buffer, upnBytes.Length);
  104. pKerbS4uLogin->ClientRealm.Length = pKerbS4uLogin->ClientRealm.MaximumLength = 0;
  105. pKerbS4uLogin->ClientRealm.Buffer = IntPtr.Zero;
  106. ushort sourceNameLength = checked((ushort)(sourceName.Length));
  107. using (SafeLocalAllocHandle sourceNameBuffer = Interop.Kernel32.LocalAlloc(0, new UIntPtr(sourceNameLength)))
  108. {
  109. if (sourceNameBuffer.IsInvalid)
  110. throw new OutOfMemoryException();
  111. Marshal.Copy(sourceName, 0, sourceNameBuffer.DangerousGetHandle(), sourceName.Length);
  112. LSA_STRING lsaOriginName = new LSA_STRING(sourceNameBuffer.DangerousGetHandle(), sourceNameLength);
  113. SafeLsaReturnBufferHandle profileBuffer;
  114. int profileBufferLength;
  115. LUID logonId;
  116. SafeAccessTokenHandle accessTokenHandle;
  117. QUOTA_LIMITS quota;
  118. int subStatus;
  119. int ntStatus = Interop.SspiCli.LsaLogonUser(
  120. lsaHandle,
  121. ref lsaOriginName,
  122. SECURITY_LOGON_TYPE.Network,
  123. packageId,
  124. authenticationInfo.DangerousGetHandle(),
  125. authenticationInfoLength,
  126. IntPtr.Zero,
  127. ref sourceContext,
  128. out profileBuffer,
  129. out profileBufferLength,
  130. out logonId,
  131. out accessTokenHandle,
  132. out quota,
  133. out subStatus);
  134. if (ntStatus == unchecked((int)Interop.StatusOptions.STATUS_ACCOUNT_RESTRICTION) && subStatus < 0)
  135. ntStatus = subStatus;
  136. if (ntStatus < 0) // non-negative numbers indicate success
  137. throw GetExceptionFromNtStatus(ntStatus);
  138. if (subStatus < 0) // non-negative numbers indicate success
  139. throw GetExceptionFromNtStatus(subStatus);
  140. if (profileBuffer != null)
  141. profileBuffer.Dispose();
  142. _safeTokenHandle = accessTokenHandle;
  143. }
  144. }
  145. }
  146. }
  147. }
  148. internal WindowsIdentity (ClaimsIdentity claimsIdentity, IntPtr userToken)
  149. : base (claimsIdentity)
  150. {
  151. if (userToken != IntPtr.Zero && userToken.ToInt64() > 0)
  152. {
  153. CreateFromToken (userToken);
  154. }
  155. }
  156. private static SafeLsaHandle ConnectToLsa()
  157. {
  158. SafeLsaHandle lsaHandle;
  159. int ntStatus = Interop.SspiCli.LsaConnectUntrusted(out lsaHandle);
  160. if (ntStatus < 0) // non-negative numbers indicate success
  161. throw GetExceptionFromNtStatus(ntStatus);
  162. return lsaHandle;
  163. }
  164. private static int LookupAuthenticationPackage(SafeLsaHandle lsaHandle, string packageName)
  165. {
  166. Debug.Assert(!string.IsNullOrEmpty(packageName));
  167. unsafe
  168. {
  169. int packageId;
  170. byte[] asciiPackageName = Encoding.ASCII.GetBytes(packageName);
  171. fixed (byte* pAsciiPackageName = &asciiPackageName[0])
  172. {
  173. LSA_STRING lsaPackageName = new LSA_STRING((IntPtr)pAsciiPackageName, checked((ushort)(asciiPackageName.Length)));
  174. int ntStatus = Interop.SspiCli.LsaLookupAuthenticationPackage(lsaHandle, ref lsaPackageName, out packageId);
  175. if (ntStatus < 0) // non-negative numbers indicate success
  176. throw GetExceptionFromNtStatus(ntStatus);
  177. }
  178. return packageId;
  179. }
  180. }
  181. public WindowsIdentity(IntPtr userToken) : this(userToken, null, -1) { }
  182. public WindowsIdentity(IntPtr userToken, string type) : this(userToken, type, -1) { }
  183. private WindowsIdentity(IntPtr userToken, string authType, int isAuthenticated)
  184. : base(null, null, null, ClaimTypes.Name, ClaimTypes.GroupSid)
  185. {
  186. CreateFromToken(userToken);
  187. _authType = authType;
  188. _isAuthenticated = isAuthenticated;
  189. }
  190. public WindowsIdentity(IntPtr userToken, string type, WindowsAccountType acctType) : this(userToken, type, -1)
  191. {
  192. // FIXME: Are we supposed to use acctType in construction?
  193. if (GetAccountType() != acctType)
  194. {
  195. throw new SecurityException("userToken does not match acctType");
  196. }
  197. }
  198. public WindowsIdentity(IntPtr userToken, string type, WindowsAccountType acctType, bool isAuthenticated) :
  199. this(userToken, type, acctType)
  200. {
  201. _isAuthenticated = isAuthenticated ? 1 : 0;
  202. }
  203. public WindowsIdentity(string sUserPrincipalName, string type) : this(sUserPrincipalName)
  204. {
  205. _authType = type;
  206. }
  207. private static SafeAccessTokenHandle DuplicateAccessToken(IntPtr accessToken)
  208. {
  209. if (accessToken == IntPtr.Zero)
  210. {
  211. throw new ArgumentException(SR.Argument_TokenZero);
  212. }
  213. // Find out if the specified token is a valid.
  214. uint dwLength = sizeof(uint);
  215. if (!Interop.Advapi32.GetTokenInformation(
  216. accessToken,
  217. (uint)TokenInformationClass.TokenType,
  218. IntPtr.Zero,
  219. 0,
  220. out dwLength) &&
  221. Marshal.GetLastWin32Error() == Interop.Errors.ERROR_INVALID_HANDLE)
  222. {
  223. throw new ArgumentException(SR.Argument_InvalidImpersonationToken);
  224. }
  225. SafeAccessTokenHandle duplicateAccessToken = SafeAccessTokenHandle.InvalidHandle;
  226. IntPtr currentProcessHandle = Interop.Kernel32.GetCurrentProcess();
  227. if (!Interop.Kernel32.DuplicateHandle(
  228. currentProcessHandle,
  229. accessToken,
  230. currentProcessHandle,
  231. ref duplicateAccessToken,
  232. 0,
  233. true,
  234. Interop.DuplicateHandleOptions.DUPLICATE_SAME_ACCESS))
  235. {
  236. throw new SecurityException(string.Format(CultureInfo.InvariantCulture, "DuplicateHandle returned error {0}", Marshal.GetLastWin32Error()));
  237. }
  238. return duplicateAccessToken;
  239. }
  240. private static SafeAccessTokenHandle DuplicateAccessToken(SafeAccessTokenHandle accessToken)
  241. {
  242. if (accessToken.IsInvalid)
  243. {
  244. return accessToken;
  245. }
  246. bool refAdded = false;
  247. try
  248. {
  249. accessToken.DangerousAddRef(ref refAdded);
  250. return DuplicateAccessToken(accessToken.DangerousGetHandle());
  251. }
  252. finally
  253. {
  254. if (refAdded)
  255. {
  256. accessToken.DangerousRelease();
  257. }
  258. }
  259. }
  260. private void CreateFromToken(IntPtr userToken)
  261. {
  262. _safeTokenHandle = DuplicateAccessToken(userToken);
  263. }
  264. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2229", Justification = "Public API has already shipped.")]
  265. public WindowsIdentity(SerializationInfo info, StreamingContext context)
  266. {
  267. _info = info;
  268. }
  269. private WindowsAccountType GetAccountType()
  270. {
  271. if (IsAnonymous)
  272. return WindowsAccountType.Anonymous;
  273. if (IsGuest)
  274. return WindowsAccountType.Guest;
  275. if (IsSystem)
  276. return WindowsAccountType.System;
  277. return WindowsAccountType.Normal;
  278. }
  279. void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
  280. {
  281. info.AddValue ("m_userToken", Token);
  282. // can be null when not resolved
  283. info.AddValue ("m_name", _name);
  284. info.AddValue ("m_type", _authType);
  285. info.AddValue ("m_acctType", GetAccountType());
  286. info.AddValue ("m_isAuthenticated", IsAuthenticated);
  287. }
  288. void IDeserializationCallback.OnDeserialization(object sender)
  289. {
  290. CreateFromToken((IntPtr) _info.GetValue ("m_userToken", typeof (IntPtr)));
  291. // can't trust this alone - we must validate the token
  292. string m_name = _info.GetString ("m_name");
  293. if (m_name != null) {
  294. // validate token by comparing names
  295. if (GetName() != m_name)
  296. throw new SerializationException ("Token-Name mismatch.");
  297. }
  298. else {
  299. // validate token by getting name
  300. GetName();
  301. }
  302. _authType = _info.GetString ("m_type");
  303. // ignoring m_acctType
  304. _isAuthenticated = _info.GetBoolean ("m_isAuthenticated") ? 1 : 0;
  305. }
  306. //
  307. // Factory methods.
  308. //
  309. public static WindowsIdentity GetCurrent()
  310. {
  311. return GetCurrentInternal(TokenAccessLevels.MaximumAllowed, false);
  312. }
  313. public static WindowsIdentity GetCurrent(bool ifImpersonating)
  314. {
  315. return GetCurrentInternal(TokenAccessLevels.MaximumAllowed, ifImpersonating);
  316. }
  317. public static WindowsIdentity GetCurrent(TokenAccessLevels desiredAccess)
  318. {
  319. return GetCurrentInternal(desiredAccess, false);
  320. }
  321. // GetAnonymous() is used heavily in ASP.NET requests as a dummy identity to indicate
  322. // the request is anonymous. It does not represent a real process or thread token so
  323. // it cannot impersonate or do anything useful. Note this identity does not represent the
  324. // usual concept of an anonymous token, and the name is simply misleading but we cannot change it now.
  325. public static WindowsIdentity GetAnonymous()
  326. {
  327. return new WindowsIdentity();
  328. }
  329. //
  330. // Properties.
  331. //
  332. // this is defined 'override sealed' for back compat. Il generated is 'virtual final' and this needs to be the same.
  333. public override sealed string AuthenticationType
  334. {
  335. get
  336. {
  337. // If this is an anonymous identity, return an empty string
  338. if (_safeTokenHandle.IsInvalid)
  339. return String.Empty;
  340. if (_authType == null)
  341. {
  342. Interop.LUID authId = GetLogonAuthId(_safeTokenHandle);
  343. if (authId.LowPart == Interop.LuidOptions.ANONYMOUS_LOGON_LUID)
  344. return String.Empty; // no authentication, just return an empty string
  345. SafeLsaReturnBufferHandle pLogonSessionData = SafeLsaReturnBufferHandle.InvalidHandle;
  346. try
  347. {
  348. int status = Interop.SspiCli.LsaGetLogonSessionData(ref authId, ref pLogonSessionData);
  349. if (status == unchecked((int)(0xC0000002))) // not implemented in Wine
  350. return "Negotiate";
  351. if (status < 0) // non-negative numbers indicate success
  352. throw GetExceptionFromNtStatus(status);
  353. pLogonSessionData.Initialize((uint)Marshal.SizeOf<Interop.SECURITY_LOGON_SESSION_DATA>());
  354. Interop.SECURITY_LOGON_SESSION_DATA logonSessionData = pLogonSessionData.Read<Interop.SECURITY_LOGON_SESSION_DATA>(0);
  355. return Marshal.PtrToStringUni(logonSessionData.AuthenticationPackage.Buffer);
  356. }
  357. finally
  358. {
  359. if (!pLogonSessionData.IsInvalid)
  360. pLogonSessionData.Dispose();
  361. }
  362. }
  363. return _authType;
  364. }
  365. }
  366. public TokenImpersonationLevel ImpersonationLevel
  367. {
  368. get
  369. {
  370. // In case of a race condition here here, both threads will set m_impersonationLevel to the same value,
  371. // which is ok.
  372. if (!_impersonationLevelInitialized)
  373. {
  374. TokenImpersonationLevel impersonationLevel = TokenImpersonationLevel.None;
  375. // If this is an anonymous identity
  376. if (_safeTokenHandle.IsInvalid)
  377. {
  378. impersonationLevel = TokenImpersonationLevel.Anonymous;
  379. }
  380. else
  381. {
  382. TokenType tokenType = (TokenType)GetTokenInformation<int>(TokenInformationClass.TokenType);
  383. if (tokenType == TokenType.TokenPrimary)
  384. {
  385. impersonationLevel = TokenImpersonationLevel.None; // primary token;
  386. }
  387. else
  388. {
  389. /// This is an impersonation token, get the impersonation level
  390. int level = GetTokenInformation<int>(TokenInformationClass.TokenImpersonationLevel);
  391. impersonationLevel = (TokenImpersonationLevel)level + 1;
  392. }
  393. }
  394. _impersonationLevel = impersonationLevel;
  395. _impersonationLevelInitialized = true;
  396. }
  397. return _impersonationLevel;
  398. }
  399. }
  400. public override bool IsAuthenticated
  401. {
  402. get
  403. {
  404. if (_isAuthenticated == -1)
  405. {
  406. // This approach will not work correctly for domain guests (will return false
  407. // instead of true). This is a corner-case that is not very interesting.
  408. _isAuthenticated = CheckNtTokenForSid(new SecurityIdentifier(IdentifierAuthority.NTAuthority,
  409. new int[] { Interop.SecurityIdentifier.SECURITY_AUTHENTICATED_USER_RID })) ? 1 : 0;
  410. }
  411. return _isAuthenticated == 1;
  412. }
  413. }
  414. private bool CheckNtTokenForSid(SecurityIdentifier sid)
  415. {
  416. // special case the anonymous identity.
  417. if (_safeTokenHandle.IsInvalid)
  418. return false;
  419. // CheckTokenMembership expects an impersonation token
  420. SafeAccessTokenHandle token = SafeAccessTokenHandle.InvalidHandle;
  421. TokenImpersonationLevel til = ImpersonationLevel;
  422. bool isMember = false;
  423. try
  424. {
  425. if (til == TokenImpersonationLevel.None)
  426. {
  427. if (!Interop.Advapi32.DuplicateTokenEx(_safeTokenHandle,
  428. (uint)TokenAccessLevels.Query,
  429. IntPtr.Zero,
  430. (uint)TokenImpersonationLevel.Identification,
  431. (uint)TokenType.TokenImpersonation,
  432. ref token))
  433. throw new SecurityException(string.Format(CultureInfo.InvariantCulture, "DuplicateTokenEx returned error {0}", Marshal.GetLastWin32Error()));
  434. }
  435. // CheckTokenMembership will check if the SID is both present and enabled in the access token.
  436. #if uap
  437. if (!Interop.Kernel32.CheckTokenMembershipEx((til != TokenImpersonationLevel.None ? _safeTokenHandle : token),
  438. sid.BinaryForm,
  439. Interop.Kernel32.CTMF_INCLUDE_APPCONTAINER,
  440. ref isMember))
  441. throw new SecurityException(new Win32Exception().Message);
  442. #else
  443. if (!Interop.Advapi32.CheckTokenMembership((til != TokenImpersonationLevel.None ? _safeTokenHandle : token),
  444. sid.BinaryForm,
  445. ref isMember))
  446. throw new SecurityException(string.Format(CultureInfo.InvariantCulture, "CheckTokenMembership returned error {0}", Marshal.GetLastWin32Error()));
  447. #endif
  448. }
  449. finally
  450. {
  451. if (token != SafeAccessTokenHandle.InvalidHandle)
  452. {
  453. token.Dispose();
  454. }
  455. }
  456. return isMember;
  457. }
  458. //
  459. // IsGuest, IsSystem and IsAnonymous are maintained for compatibility reasons. It is always
  460. // possible to extract this same information from the User SID property and the new
  461. // (and more general) methods defined in the SID class (IsWellKnown, etc...).
  462. //
  463. public virtual bool IsGuest
  464. {
  465. get
  466. {
  467. // special case the anonymous identity.
  468. if (_safeTokenHandle.IsInvalid)
  469. return false;
  470. return CheckNtTokenForSid(new SecurityIdentifier(IdentifierAuthority.NTAuthority,
  471. new int[] { Interop.SecurityIdentifier.SECURITY_BUILTIN_DOMAIN_RID, (int)WindowsBuiltInRole.Guest }));
  472. }
  473. }
  474. public virtual bool IsSystem
  475. {
  476. get
  477. {
  478. // special case the anonymous identity.
  479. if (_safeTokenHandle.IsInvalid)
  480. return false;
  481. SecurityIdentifier sid = new SecurityIdentifier(IdentifierAuthority.NTAuthority,
  482. new int[] { Interop.SecurityIdentifier.SECURITY_LOCAL_SYSTEM_RID });
  483. return (this.User == sid);
  484. }
  485. }
  486. public virtual bool IsAnonymous
  487. {
  488. get
  489. {
  490. // special case the anonymous identity.
  491. if (_safeTokenHandle.IsInvalid)
  492. return true;
  493. SecurityIdentifier sid = new SecurityIdentifier(IdentifierAuthority.NTAuthority,
  494. new int[] { Interop.SecurityIdentifier.SECURITY_ANONYMOUS_LOGON_RID });
  495. return (this.User == sid);
  496. }
  497. }
  498. public override string Name
  499. {
  500. get
  501. {
  502. return GetName();
  503. }
  504. }
  505. public virtual WindowsImpersonationContext Impersonate ()
  506. {
  507. return new WindowsImpersonationContext (GetTokenInternal ());
  508. }
  509. [SecurityPermission (SecurityAction.Demand, ControlPrincipal=true)]
  510. public static WindowsImpersonationContext Impersonate (IntPtr userToken)
  511. {
  512. return new WindowsImpersonationContext (userToken);
  513. }
  514. internal String GetName()
  515. {
  516. // special case the anonymous identity.
  517. if (_safeTokenHandle.IsInvalid)
  518. return String.Empty;
  519. if (_name == null)
  520. {
  521. // revert thread impersonation for the duration of the call to get the name.
  522. RunImpersonated(SafeAccessTokenHandle.InvalidHandle, delegate
  523. {
  524. NTAccount ntAccount = this.User.Translate(typeof(NTAccount)) as NTAccount;
  525. _name = ntAccount.ToString();
  526. });
  527. }
  528. return _name;
  529. }
  530. public SecurityIdentifier Owner
  531. {
  532. get
  533. {
  534. // special case the anonymous identity.
  535. if (_safeTokenHandle.IsInvalid)
  536. return null;
  537. if (_owner == null)
  538. {
  539. using (SafeLocalAllocHandle tokenOwner = GetTokenInformation(_safeTokenHandle, TokenInformationClass.TokenOwner))
  540. {
  541. _owner = new SecurityIdentifier(tokenOwner.Read<IntPtr>(0), true);
  542. }
  543. }
  544. return _owner;
  545. }
  546. }
  547. public SecurityIdentifier User
  548. {
  549. get
  550. {
  551. // special case the anonymous identity.
  552. if (_safeTokenHandle.IsInvalid)
  553. return null;
  554. if (_user == null)
  555. {
  556. using (SafeLocalAllocHandle tokenUser = GetTokenInformation(_safeTokenHandle, TokenInformationClass.TokenUser))
  557. {
  558. _user = new SecurityIdentifier(tokenUser.Read<IntPtr>(0), true);
  559. }
  560. }
  561. return _user;
  562. }
  563. }
  564. public IdentityReferenceCollection Groups
  565. {
  566. get
  567. {
  568. // special case the anonymous identity.
  569. if (_safeTokenHandle.IsInvalid)
  570. return null;
  571. if (_groups == null)
  572. {
  573. IdentityReferenceCollection groups = new IdentityReferenceCollection();
  574. using (SafeLocalAllocHandle pGroups = GetTokenInformation(_safeTokenHandle, TokenInformationClass.TokenGroups))
  575. {
  576. uint groupCount = pGroups.Read<uint>(0);
  577. Interop.TOKEN_GROUPS tokenGroups = pGroups.Read<Interop.TOKEN_GROUPS>(0);
  578. Interop.SID_AND_ATTRIBUTES[] groupDetails = new Interop.SID_AND_ATTRIBUTES[tokenGroups.GroupCount];
  579. pGroups.ReadArray((uint)Marshal.OffsetOf<Interop.TOKEN_GROUPS>("Groups").ToInt32(),
  580. groupDetails,
  581. 0,
  582. groupDetails.Length);
  583. foreach (Interop.SID_AND_ATTRIBUTES group in groupDetails)
  584. {
  585. // Ignore disabled, logon ID, and deny-only groups.
  586. uint mask = Interop.SecurityGroups.SE_GROUP_ENABLED | Interop.SecurityGroups.SE_GROUP_LOGON_ID | Interop.SecurityGroups.SE_GROUP_USE_FOR_DENY_ONLY;
  587. if ((group.Attributes & mask) == Interop.SecurityGroups.SE_GROUP_ENABLED)
  588. {
  589. groups.Add(new SecurityIdentifier(group.Sid, true));
  590. }
  591. }
  592. }
  593. Interlocked.CompareExchange(ref _groups, groups, null);
  594. }
  595. return _groups;
  596. }
  597. }
  598. public SafeAccessTokenHandle AccessToken
  599. {
  600. get
  601. {
  602. return _safeTokenHandle;
  603. }
  604. }
  605. public virtual IntPtr Token
  606. {
  607. get
  608. {
  609. return _safeTokenHandle.DangerousGetHandle();
  610. }
  611. }
  612. internal IntPtr GetTokenInternal()
  613. {
  614. return _safeTokenHandle.DangerousGetHandle();
  615. }
  616. //
  617. // Public methods.
  618. //
  619. public static void RunImpersonated(SafeAccessTokenHandle safeAccessTokenHandle, Action action)
  620. {
  621. if (action == null)
  622. throw new ArgumentNullException(nameof(action));
  623. RunImpersonatedInternal(safeAccessTokenHandle, action);
  624. }
  625. public static T RunImpersonated<T>(SafeAccessTokenHandle safeAccessTokenHandle, Func<T> func)
  626. {
  627. if (func == null)
  628. throw new ArgumentNullException(nameof(func));
  629. T result = default(T);
  630. RunImpersonatedInternal(safeAccessTokenHandle, () => result = func());
  631. return result;
  632. }
  633. protected virtual void Dispose(bool disposing)
  634. {
  635. if (disposing)
  636. {
  637. if (_safeTokenHandle != null && !_safeTokenHandle.IsClosed)
  638. _safeTokenHandle.Dispose();
  639. }
  640. _name = null;
  641. _owner = null;
  642. _user = null;
  643. }
  644. public void Dispose()
  645. {
  646. Dispose(true);
  647. }
  648. //
  649. // internal.
  650. //
  651. private static AsyncLocal<SafeAccessTokenHandle> s_currentImpersonatedToken = new AsyncLocal<SafeAccessTokenHandle>(CurrentImpersonatedTokenChanged);
  652. private static void RunImpersonatedInternal(SafeAccessTokenHandle token, Action action)
  653. {
  654. token = DuplicateAccessToken(token);
  655. bool isImpersonating;
  656. int hr;
  657. SafeAccessTokenHandle previousToken = GetCurrentToken(TokenAccessLevels.MaximumAllowed, false, out isImpersonating, out hr);
  658. if (previousToken == null || previousToken.IsInvalid)
  659. throw new SecurityException(string.Format(CultureInfo.InvariantCulture, "GetCurrentToken returned error {0}", Marshal.GetLastWin32Error()));
  660. s_currentImpersonatedToken.Value = isImpersonating ? previousToken : null;
  661. ExecutionContext currentContext = ExecutionContext.Capture();
  662. // Run everything else inside of ExecutionContext.Run, so that any EC changes will be undone
  663. // on the way out.
  664. ExecutionContext.Run(
  665. currentContext,
  666. delegate
  667. {
  668. if (!Interop.Advapi32.RevertToSelf())
  669. Environment.FailFast(string.Format(CultureInfo.InvariantCulture, "RevertToSelf returned error {0}", Marshal.GetLastWin32Error()));
  670. s_currentImpersonatedToken.Value = null;
  671. if (!token.IsInvalid && !Interop.Advapi32.ImpersonateLoggedOnUser(token))
  672. throw new SecurityException(SR.Argument_ImpersonateUser);
  673. s_currentImpersonatedToken.Value = token;
  674. action();
  675. },
  676. null);
  677. }
  678. private static void CurrentImpersonatedTokenChanged(AsyncLocalValueChangedArgs<SafeAccessTokenHandle> args)
  679. {
  680. if (!args.ThreadContextChanged)
  681. return; // we handle explicit Value property changes elsewhere.
  682. if (!Interop.Advapi32.RevertToSelf())
  683. Environment.FailFast(string.Format(CultureInfo.InvariantCulture, "RevertToSelf returned error {0}", Marshal.GetLastWin32Error()));
  684. if (args.CurrentValue != null && !args.CurrentValue.IsInvalid)
  685. {
  686. if (!Interop.Advapi32.ImpersonateLoggedOnUser(args.CurrentValue))
  687. Environment.FailFast(string.Format(CultureInfo.InvariantCulture, "ImpersonateLoggedOnUser returned error {0}", Marshal.GetLastWin32Error()));
  688. }
  689. }
  690. internal static WindowsIdentity GetCurrentInternal(TokenAccessLevels desiredAccess, bool threadOnly)
  691. {
  692. int hr = 0;
  693. bool isImpersonating;
  694. SafeAccessTokenHandle safeTokenHandle = GetCurrentToken(desiredAccess, threadOnly, out isImpersonating, out hr);
  695. if (safeTokenHandle == null || safeTokenHandle.IsInvalid)
  696. {
  697. // either we wanted only ThreadToken - return null
  698. if (threadOnly && !isImpersonating)
  699. return null;
  700. // or there was an error
  701. throw new SecurityException(string.Format(CultureInfo.InvariantCulture, "GetCurrentToken returned error {0}", Marshal.GetLastWin32Error()));
  702. }
  703. WindowsIdentity wi = new WindowsIdentity();
  704. wi._safeTokenHandle.Dispose();
  705. wi._safeTokenHandle = safeTokenHandle;
  706. return wi;
  707. }
  708. //
  709. // private.
  710. //
  711. private static int GetHRForWin32Error(int dwLastError)
  712. {
  713. if ((dwLastError & 0x80000000) == 0x80000000)
  714. return dwLastError;
  715. else
  716. return (dwLastError & 0x0000FFFF) | unchecked((int)0x80070000);
  717. }
  718. private static Exception GetExceptionFromNtStatus(int status)
  719. {
  720. if ((uint)status == Interop.StatusOptions.STATUS_ACCESS_DENIED)
  721. return new UnauthorizedAccessException();
  722. if ((uint)status == Interop.StatusOptions.STATUS_INSUFFICIENT_RESOURCES || (uint)status == Interop.StatusOptions.STATUS_NO_MEMORY)
  723. return new OutOfMemoryException();
  724. return new SecurityException(string.Format(CultureInfo.InvariantCulture, "NT error {0}", status));
  725. }
  726. private static SafeAccessTokenHandle GetCurrentToken(TokenAccessLevels desiredAccess, bool threadOnly, out bool isImpersonating, out int hr)
  727. {
  728. isImpersonating = true;
  729. SafeAccessTokenHandle safeTokenHandle;
  730. hr = 0;
  731. bool success = Interop.Advapi32.OpenThreadToken(desiredAccess, WinSecurityContext.Both, out safeTokenHandle);
  732. if (!success)
  733. hr = GetHRForWin32Error(Marshal.GetLastWin32Error());
  734. if (!success && hr == GetHRForWin32Error(Interop.Errors.ERROR_NO_TOKEN))
  735. {
  736. // No impersonation
  737. isImpersonating = false;
  738. if (!threadOnly)
  739. safeTokenHandle = GetCurrentProcessToken(desiredAccess, out hr);
  740. }
  741. return safeTokenHandle;
  742. }
  743. private static SafeAccessTokenHandle GetCurrentProcessToken(TokenAccessLevels desiredAccess, out int hr)
  744. {
  745. hr = 0;
  746. SafeAccessTokenHandle safeTokenHandle;
  747. if (!Interop.Advapi32.OpenProcessToken(Interop.Kernel32.GetCurrentProcess(), desiredAccess, out safeTokenHandle))
  748. hr = GetHRForWin32Error(Marshal.GetLastWin32Error());
  749. return safeTokenHandle;
  750. }
  751. /// <summary>
  752. /// Get a property from the current token
  753. /// </summary>
  754. private T GetTokenInformation<T>(TokenInformationClass tokenInformationClass) where T : struct
  755. {
  756. Debug.Assert(!_safeTokenHandle.IsInvalid && !_safeTokenHandle.IsClosed, "!m_safeTokenHandle.IsInvalid && !m_safeTokenHandle.IsClosed");
  757. using (SafeLocalAllocHandle information = GetTokenInformation(_safeTokenHandle, tokenInformationClass))
  758. {
  759. Debug.Assert(information.ByteLength >= (ulong)Marshal.SizeOf<T>(),
  760. "information.ByteLength >= (ulong)Marshal.SizeOf(typeof(T))");
  761. return information.Read<T>(0);
  762. }
  763. }
  764. private static Interop.LUID GetLogonAuthId(SafeAccessTokenHandle safeTokenHandle)
  765. {
  766. using (SafeLocalAllocHandle pStatistics = GetTokenInformation(safeTokenHandle, TokenInformationClass.TokenStatistics))
  767. {
  768. Interop.TOKEN_STATISTICS statistics = pStatistics.Read<Interop.TOKEN_STATISTICS>(0);
  769. return statistics.AuthenticationId;
  770. }
  771. }
  772. private static SafeLocalAllocHandle GetTokenInformation(SafeAccessTokenHandle tokenHandle, TokenInformationClass tokenInformationClass, bool nullOnInvalidParam=false)
  773. {
  774. SafeLocalAllocHandle safeLocalAllocHandle = SafeLocalAllocHandle.InvalidHandle;
  775. uint dwLength = (uint)sizeof(uint);
  776. bool result = Interop.Advapi32.GetTokenInformation(tokenHandle,
  777. (uint)tokenInformationClass,
  778. safeLocalAllocHandle,
  779. 0,
  780. out dwLength);
  781. int dwErrorCode = Marshal.GetLastWin32Error();
  782. switch (dwErrorCode)
  783. {
  784. case Interop.Errors.ERROR_BAD_LENGTH:
  785. // special case for TokenSessionId. Falling through
  786. case Interop.Errors.ERROR_INSUFFICIENT_BUFFER:
  787. // ptrLength is an [In] param to LocalAlloc
  788. UIntPtr ptrLength = new UIntPtr(dwLength);
  789. safeLocalAllocHandle.Dispose();
  790. safeLocalAllocHandle = Interop.Kernel32.LocalAlloc(0, ptrLength);
  791. if (safeLocalAllocHandle == null || safeLocalAllocHandle.IsInvalid)
  792. throw new OutOfMemoryException();
  793. safeLocalAllocHandle.Initialize(dwLength);
  794. result = Interop.Advapi32.GetTokenInformation(tokenHandle,
  795. (uint)tokenInformationClass,
  796. safeLocalAllocHandle,
  797. dwLength,
  798. out dwLength);
  799. if (!result)
  800. throw new SecurityException(string.Format(CultureInfo.InvariantCulture, "GetTokenInformation returned error {0}", Marshal.GetLastWin32Error()));
  801. break;
  802. case Interop.Errors.ERROR_INVALID_HANDLE:
  803. throw new ArgumentException(SR.Argument_InvalidImpersonationToken);
  804. case Interop.Errors.ERROR_INVALID_PARAMETER:
  805. case Interop.Errors.ERROR_INVALID_FUNCTION: /* Not implemented in Wine */
  806. if (nullOnInvalidParam)
  807. {
  808. safeLocalAllocHandle.Dispose();
  809. return null;
  810. }
  811. // Throw the exception.
  812. goto default;
  813. default:
  814. throw new SecurityException(string.Format(CultureInfo.InvariantCulture, "GetTokenInformation returned error {0}", dwErrorCode));
  815. }
  816. return safeLocalAllocHandle;
  817. }
  818. protected WindowsIdentity(WindowsIdentity identity)
  819. : base(identity, null, GetAuthType(identity), null, null)
  820. {
  821. bool mustDecrement = false;
  822. try
  823. {
  824. if (!identity._safeTokenHandle.IsInvalid && identity._safeTokenHandle != SafeAccessTokenHandle.InvalidHandle && identity._safeTokenHandle.DangerousGetHandle() != IntPtr.Zero)
  825. {
  826. identity._safeTokenHandle.DangerousAddRef(ref mustDecrement);
  827. if (!identity._safeTokenHandle.IsInvalid && identity._safeTokenHandle.DangerousGetHandle() != IntPtr.Zero)
  828. CreateFromToken(identity._safeTokenHandle.DangerousGetHandle());
  829. _authType = identity._authType;
  830. _isAuthenticated = identity._isAuthenticated;
  831. }
  832. }
  833. finally
  834. {
  835. if (mustDecrement)
  836. identity._safeTokenHandle.DangerousRelease();
  837. }
  838. }
  839. private static string GetAuthType(WindowsIdentity identity)
  840. {
  841. if (identity == null)
  842. {
  843. throw new ArgumentNullException(nameof(identity));
  844. }
  845. return identity._authType;
  846. }
  847. internal ClaimsIdentity CloneAsBase ()
  848. {
  849. return base.Clone();
  850. }
  851. /// <summary>
  852. /// Returns a new instance of <see cref="WindowsIdentity"/> with values copied from this object.
  853. /// </summary>
  854. public override ClaimsIdentity Clone()
  855. {
  856. return new WindowsIdentity(this);
  857. }
  858. /// <summary>
  859. /// Gets the 'User Claims' from the NTToken that represents this identity
  860. /// </summary>
  861. public virtual IEnumerable<Claim> UserClaims
  862. {
  863. get
  864. {
  865. InitializeClaims();
  866. return _userClaims.ToArray();
  867. }
  868. }
  869. /// <summary>
  870. /// Gets the 'Device Claims' from the NTToken that represents the device the identity is using
  871. /// </summary>
  872. public virtual IEnumerable<Claim> DeviceClaims
  873. {
  874. get
  875. {
  876. InitializeClaims();
  877. return _deviceClaims.ToArray();
  878. }
  879. }
  880. /// <summary>
  881. /// Gets the claims as <see cref="IEnumerable{Claim}"/>, associated with this <see cref="WindowsIdentity"/>.
  882. /// Includes UserClaims and DeviceClaims.
  883. /// </summary>
  884. public override IEnumerable<Claim> Claims
  885. {
  886. get
  887. {
  888. if (!_claimsInitialized)
  889. {
  890. InitializeClaims();
  891. }
  892. foreach (Claim claim in base.Claims)
  893. yield return claim;
  894. foreach (Claim claim in _userClaims)
  895. yield return claim;
  896. foreach (Claim claim in _deviceClaims)
  897. yield return claim;
  898. }
  899. }
  900. /// <summary>
  901. /// Internal method to initialize the claim collection.
  902. /// Lazy init is used so claims are not initialized until needed
  903. /// </summary>
  904. private void InitializeClaims()
  905. {
  906. if (!_claimsInitialized)
  907. {
  908. lock (_claimsIntiailizedLock)
  909. {
  910. if (!_claimsInitialized)
  911. {
  912. _userClaims = new List<Claim>();
  913. _deviceClaims = new List<Claim>();
  914. if (!String.IsNullOrEmpty(Name))
  915. {
  916. //
  917. // Add the name claim only if the WindowsIdentity.Name is populated
  918. // WindowsIdentity.Name will be null when it is the fake anonymous user
  919. // with a token value of IntPtr.Zero
  920. //
  921. _userClaims.Add(new Claim(NameClaimType, Name, ClaimValueTypes.String, _issuerName, _issuerName, this));
  922. }
  923. // primary sid
  924. AddPrimarySidClaim(_userClaims);
  925. // group sids
  926. AddGroupSidClaims(_userClaims);
  927. if (!s_ignoreWindows8Properties)
  928. {
  929. // Device group sids (may cause s_ignoreWindows8Properties to be set to true, so must be first in this block)
  930. AddDeviceGroupSidClaims(_deviceClaims, TokenInformationClass.TokenDeviceGroups);
  931. if (!s_ignoreWindows8Properties)
  932. {
  933. // User token claims
  934. AddTokenClaims(_userClaims, TokenInformationClass.TokenUserClaimAttributes, ClaimTypes.WindowsUserClaim);
  935. // Device token claims
  936. AddTokenClaims(_deviceClaims, TokenInformationClass.TokenDeviceClaimAttributes, ClaimTypes.WindowsDeviceClaim);
  937. }
  938. }
  939. _claimsInitialized = true;
  940. }
  941. }
  942. }
  943. }
  944. /// <summary>
  945. /// Creates a collection of SID claims that represent the users groups.
  946. /// </summary>
  947. private void AddGroupSidClaims(List<Claim> instanceClaims)
  948. {
  949. // special case the anonymous identity.
  950. if (_safeTokenHandle.IsInvalid)
  951. return;
  952. SafeLocalAllocHandle safeAllocHandle = SafeLocalAllocHandle.InvalidHandle;
  953. SafeLocalAllocHandle safeAllocHandlePrimaryGroup = SafeLocalAllocHandle.InvalidHandle;
  954. try
  955. {
  956. // Retrieve the primary group sid
  957. safeAllocHandlePrimaryGroup = GetTokenInformation(_safeTokenHandle, TokenInformationClass.TokenPrimaryGroup);
  958. Interop.TOKEN_PRIMARY_GROUP primaryGroup = (Interop.TOKEN_PRIMARY_GROUP)Marshal.PtrToStructure<Interop.TOKEN_PRIMARY_GROUP>(safeAllocHandlePrimaryGroup.DangerousGetHandle());
  959. SecurityIdentifier primaryGroupSid = new SecurityIdentifier(primaryGroup.PrimaryGroup, true);
  960. // only add one primary group sid
  961. bool foundPrimaryGroupSid = false;
  962. // Retrieve all group sids, primary group sid is one of them
  963. safeAllocHandle = GetTokenInformation(_safeTokenHandle, TokenInformationClass.TokenGroups);
  964. int count = Marshal.ReadInt32(safeAllocHandle.DangerousGetHandle());
  965. IntPtr pSidAndAttributes = new IntPtr((long)safeAllocHandle.DangerousGetHandle() + (long)Marshal.OffsetOf<Interop.TOKEN_GROUPS>("Groups"));
  966. Claim claim;
  967. for (int i = 0; i < count; ++i)
  968. {
  969. Interop.SID_AND_ATTRIBUTES group = (Interop.SID_AND_ATTRIBUTES)Marshal.PtrToStructure<Interop.SID_AND_ATTRIBUTES>(pSidAndAttributes);
  970. uint mask = Interop.SecurityGroups.SE_GROUP_ENABLED | Interop.SecurityGroups.SE_GROUP_LOGON_ID | Interop.SecurityGroups.SE_GROUP_USE_FOR_DENY_ONLY;
  971. SecurityIdentifier groupSid = new SecurityIdentifier(group.Sid, true);
  972. if ((group.Attributes & mask) == Interop.SecurityGroups.SE_GROUP_ENABLED)
  973. {
  974. if (!foundPrimaryGroupSid && StringComparer.Ordinal.Equals(groupSid.Value, primaryGroupSid.Value))
  975. {
  976. claim = new Claim(ClaimTypes.PrimaryGroupSid, groupSid.Value, ClaimValueTypes.String, _issuerName, _issuerName, this);
  977. claim.Properties.Add(ClaimTypes.WindowsSubAuthority, groupSid.IdentifierAuthority.ToString());
  978. instanceClaims.Add(claim);
  979. foundPrimaryGroupSid = true;
  980. }
  981. //Primary group sid generates both regular groupsid claim and primary groupsid claim
  982. claim = new Claim(ClaimTypes.GroupSid, groupSid.Value, ClaimValueTypes.String, _issuerName, _issuerName, this);
  983. claim.Properties.Add(ClaimTypes.WindowsSubAuthority, groupSid.IdentifierAuthority.ToString());
  984. instanceClaims.Add(claim);
  985. }
  986. else if ((group.Attributes & mask) == Interop.SecurityGroups.SE_GROUP_USE_FOR_DENY_ONLY)
  987. {
  988. if (!foundPrimaryGroupSid && StringComparer.Ordinal.Equals(groupSid.Value, primaryGroupSid.Value))
  989. {
  990. claim = new Claim(ClaimTypes.DenyOnlyPrimaryGroupSid, groupSid.Value, ClaimValueTypes.String, _issuerName, _issuerName, this);
  991. claim.Properties.Add(ClaimTypes.WindowsSubAuthority, groupSid.IdentifierAuthority.ToString());
  992. instanceClaims.Add(claim);
  993. foundPrimaryGroupSid = true;
  994. }