PageRenderTime 44ms CodeModel.GetById 7ms RepoModel.GetById 0ms app.codeStats 0ms

/trunk/thirdparties/ZetaLibNet/Core/Common/Impersonator.cs

http://lextudio.googlecode.com/
C# | 527 lines | 315 code | 55 blank | 157 comment | 26 complexity | 5f1d617e69d73788fd9c2b19e88c5575 MD5 | raw file
Possible License(s): CPL-1.0, Apache-2.0, GPL-2.0, LGPL-2.1
  1. namespace ZetaLib.Core.Common
  2. {
  3. #region Using directives.
  4. // ----------------------------------------------------------------------
  5. using System;
  6. using System.Security;
  7. using System.Security.Principal;
  8. using System.Runtime.InteropServices;
  9. using System.ComponentModel;
  10. using ZetaLib.Core.Logging;
  11. // ----------------------------------------------------------------------
  12. #endregion
  13. /////////////////////////////////////////////////////////////////////////
  14. /// <summary>
  15. /// Impersonation of a user. Allows to execute code under another
  16. /// user context.
  17. /// Please note that the account that instantiates the Impersonator class
  18. /// needs to have the 'Act as part of operating system' privilege set.
  19. /// </summary>
  20. /// <remarks>
  21. /// This class is based on the information in the Microsoft knowledge base
  22. /// article http://support.microsoft.com/default.aspx?scid=kb;en-us;Q306158
  23. ///
  24. /// Encapsulate an instance into a using-directive like e.g.:
  25. ///
  26. /// ...
  27. /// using ( new Impersonator( "myUsername", "myDomainname", "myPassword" ) )
  28. /// {
  29. /// ...
  30. /// [code that executes under the new context]
  31. /// ...
  32. /// }
  33. /// ...
  34. /// </remarks>
  35. public class Impersonator :
  36. IDisposable
  37. {
  38. #region Public methods.
  39. // ------------------------------------------------------------------
  40. /// <summary>
  41. /// Constructor. Starts the impersonation with the given credentials.
  42. /// Please note that the account that instantiates the Impersonator class
  43. /// needs to have the 'Act as part of operating system' privilege set.
  44. /// </summary>
  45. /// <param name="userName">The name of the user to act as.</param>
  46. /// <param name="domainName">The domain name of the user to act as.</param>
  47. /// <param name="password">The password of the user to act as.</param>
  48. public Impersonator(
  49. string userName,
  50. string domainName,
  51. string password )
  52. {
  53. ImpersonateValidUser(
  54. userName,
  55. domainName,
  56. password,
  57. LoginType.Interactive );
  58. }
  59. /// <summary>
  60. /// Constructor. Starts the impersonation with the given credentials.
  61. /// Please note that the account that instantiates the Impersonator class
  62. /// needs to have the 'Act as part of operating system' privilege set.
  63. /// </summary>
  64. /// <param name="userName">The name of the user to act as.</param>
  65. /// <param name="domainName">The domain name of the user to act as.</param>
  66. /// <param name="password">The password of the user to act as.</param>
  67. /// <param name="loginType">The login type.</param>
  68. public Impersonator(
  69. string userName,
  70. string domainName,
  71. string password,
  72. LoginType loginType )
  73. {
  74. ImpersonateValidUser(
  75. userName,
  76. domainName,
  77. password,
  78. loginType );
  79. }
  80. /// <summary>
  81. /// Constructor. Starts the impersonation with the given credentials.
  82. /// Please note that the account that instantiates the Impersonator class
  83. /// needs to have the 'Act as part of operating system' privilege set.
  84. /// </summary>
  85. /// <param name="userName">The name of the user to act as.</param>
  86. /// <param name="domainName">The domain name of the user to act as.</param>
  87. /// <param name="password">The password of the user to act as.</param>
  88. public Impersonator(
  89. string userName,
  90. string domainName,
  91. SecureString password )
  92. {
  93. ImpersonateValidUser(
  94. userName,
  95. domainName,
  96. password,
  97. LoginType.Interactive );
  98. }
  99. /// <summary>
  100. /// Constructor. Starts the impersonation with the given credentials.
  101. /// Please note that the account that instantiates the Impersonator class
  102. /// needs to have the 'Act as part of operating system' privilege set.
  103. /// </summary>
  104. /// <param name="userName">The name of the user to act as.</param>
  105. /// <param name="domainName">The domain name of the user to act as.</param>
  106. /// <param name="password">The password of the user to act as.</param>
  107. /// <param name="loginType">The login type.</param>
  108. public Impersonator(
  109. string userName,
  110. string domainName,
  111. SecureString password,
  112. LoginType loginType )
  113. {
  114. ImpersonateValidUser(
  115. userName,
  116. domainName,
  117. password,
  118. loginType );
  119. }
  120. // ------------------------------------------------------------------
  121. #endregion
  122. #region IDisposable member.
  123. // ------------------------------------------------------------------
  124. /// <summary>
  125. /// Performs application-defined tasks associated with freeing,
  126. /// releasing, or resetting unmanaged resources.
  127. /// </summary>
  128. public void Dispose()
  129. {
  130. UndoImpersonation();
  131. GC.SuppressFinalize( this );
  132. }
  133. /// <summary>
  134. /// Releases unmanaged resources and performs other cleanup operations before the
  135. /// <see cref="Impersonator"/> is reclaimed by garbage collection.
  136. /// </summary>
  137. ~Impersonator()
  138. {
  139. UndoImpersonation();
  140. }
  141. // ------------------------------------------------------------------
  142. #endregion
  143. #region P/Invoke.
  144. // ------------------------------------------------------------------
  145. /// <summary>
  146. /// Logons the user.
  147. /// </summary>
  148. /// <param name="lpszUserName">Name of the LPSZ user.</param>
  149. /// <param name="lpszDomain">The LPSZ domain.</param>
  150. /// <param name="lpszPassword">The LPSZ password.</param>
  151. /// <param name="dwLogonType">Type of the dw logon.</param>
  152. /// <param name="dwLogonProvider">The dw logon provider.</param>
  153. /// <param name="phToken">The ph token.</param>
  154. /// <returns></returns>
  155. [DllImport( @"advapi32.dll", CharSet = CharSet.Auto, SetLastError = true )]
  156. private static extern int LogonUser(
  157. string lpszUserName,
  158. string lpszDomain,
  159. string lpszPassword,
  160. int dwLogonType,
  161. int dwLogonProvider,
  162. ref IntPtr phToken );
  163. /// <summary>
  164. /// Logons the user2.
  165. /// </summary>
  166. /// <param name="lpszUserName">Name of the LPSZ user.</param>
  167. /// <param name="lpszDomain">The LPSZ domain.</param>
  168. /// <param name="Password">The password.</param>
  169. /// <param name="dwLogonType">Type of the dw logon.</param>
  170. /// <param name="dwLogonProvider">The dw logon provider.</param>
  171. /// <param name="phToken">The ph token.</param>
  172. /// <returns></returns>
  173. [DllImport( @"advapi32.dll", EntryPoint = @"LogonUser", CharSet = CharSet.Unicode, SetLastError = true )]
  174. private static extern int LogonUser2(
  175. string lpszUserName,
  176. string lpszDomain,
  177. IntPtr Password,
  178. int dwLogonType,
  179. int dwLogonProvider,
  180. ref IntPtr phToken );
  181. /// <summary>
  182. /// Duplicates the token.
  183. /// </summary>
  184. /// <param name="hToken">The h token.</param>
  185. /// <param name="impersonationLevel">The impersonation level.</param>
  186. /// <param name="hNewToken">The h new token.</param>
  187. /// <returns></returns>
  188. [DllImport( @"advapi32.dll", CharSet = CharSet.Auto, SetLastError = true )]
  189. private static extern int DuplicateToken(
  190. IntPtr hToken,
  191. int impersonationLevel,
  192. ref IntPtr hNewToken );
  193. /// <summary>
  194. /// Reverts to self.
  195. /// </summary>
  196. /// <returns></returns>
  197. [DllImport( @"advapi32.dll", CharSet = CharSet.Auto, SetLastError = true )]
  198. private static extern bool RevertToSelf();
  199. /// <summary>
  200. /// Closes the handle.
  201. /// </summary>
  202. /// <param name="handle">The handle.</param>
  203. /// <returns></returns>
  204. [DllImport( @"kernel32.dll", CharSet = CharSet.Auto, SetLastError = true )]
  205. private static extern bool CloseHandle(
  206. IntPtr handle );
  207. private const int LOGON32_LOGON_INTERACTIVE = 2;
  208. private const int LOGON32_PROVIDER_DEFAULT = 0;
  209. // ------------------------------------------------------------------
  210. #endregion
  211. #region Private methods.
  212. // ------------------------------------------------------------------
  213. /// <summary>
  214. /// Does the actual impersonation.
  215. /// </summary>
  216. /// <param name="userName">The name of the user to act as.</param>
  217. /// <param name="domainName">The domain name of the user to act as.</param>
  218. /// <param name="password">The password of the user to act as.</param>
  219. private void ImpersonateValidUser(
  220. string userName,
  221. string domainName,
  222. string password,
  223. LoginType loginType )
  224. {
  225. LogCentral.Current.LogInfo(
  226. string.Format(
  227. @"[Impersonation] About to impersonate as domain '{0}', user '{1}'.",
  228. domainName,
  229. userName ) );
  230. try
  231. {
  232. if ( domainName != null || domainName.Length <= 0 )
  233. {
  234. domainName = null;
  235. }
  236. WindowsIdentity tempWindowsIdentity = null;
  237. IntPtr token = IntPtr.Zero;
  238. IntPtr tokenDuplicate = IntPtr.Zero;
  239. try
  240. {
  241. if ( RevertToSelf() )
  242. {
  243. if ( LogonUser(
  244. userName,
  245. domainName,
  246. password,
  247. (int)loginType,
  248. LOGON32_PROVIDER_DEFAULT,
  249. ref token ) != 0 )
  250. {
  251. if ( DuplicateToken( token, 2, ref tokenDuplicate ) != 0 )
  252. {
  253. tempWindowsIdentity = new WindowsIdentity( tokenDuplicate );
  254. impersonationContext = tempWindowsIdentity.Impersonate();
  255. }
  256. else
  257. {
  258. int le = Marshal.GetLastWin32Error();
  259. throw new Win32Exception( le );
  260. }
  261. }
  262. else
  263. {
  264. int le = Marshal.GetLastWin32Error();
  265. throw new Win32Exception( le );
  266. }
  267. }
  268. else
  269. {
  270. int le = Marshal.GetLastWin32Error();
  271. throw new Win32Exception( le );
  272. }
  273. }
  274. finally
  275. {
  276. if ( token != IntPtr.Zero )
  277. {
  278. CloseHandle( token );
  279. }
  280. if ( tokenDuplicate != IntPtr.Zero )
  281. {
  282. CloseHandle( tokenDuplicate );
  283. }
  284. }
  285. }
  286. catch ( Exception x )
  287. {
  288. LogCentral.Current.LogError(
  289. string.Format(
  290. @"[Impersonation] Error impersonating as domain '{0}', user '{1}'.",
  291. domainName,
  292. userName ),
  293. x );
  294. throw;
  295. }
  296. LogCentral.Current.LogInfo(
  297. string.Format(
  298. @"[Impersonation] Successfully impersonated as domain '{0}', user '{1}'.",
  299. domainName,
  300. userName ) );
  301. }
  302. /// <summary>
  303. /// Does the actual impersonation.
  304. /// </summary>
  305. /// <param name="userName">The name of the user to act as.</param>
  306. /// <param name="domainName">The domain name of the user to act as.</param>
  307. /// <param name="password">The password of the user to act as.</param>
  308. private void ImpersonateValidUser(
  309. string userName,
  310. string domainName,
  311. SecureString password,
  312. LoginType loginType )
  313. {
  314. LogCentral.Current.LogInfo(
  315. string.Format(
  316. @"[Impersonation] About to impersonate as domain '{0}', user '{1}'.",
  317. domainName,
  318. userName ) );
  319. try
  320. {
  321. if ( domainName != null || domainName.Length <= 0 )
  322. {
  323. domainName = null;
  324. }
  325. WindowsIdentity tempWindowsIdentity = null;
  326. IntPtr token = IntPtr.Zero;
  327. IntPtr tokenDuplicate = IntPtr.Zero;
  328. IntPtr passwordPtr = IntPtr.Zero;
  329. try
  330. {
  331. if ( RevertToSelf() )
  332. {
  333. // Marshal the SecureString to unmanaged memory.
  334. passwordPtr =
  335. Marshal.SecureStringToGlobalAllocUnicode( password );
  336. if ( LogonUser2(
  337. userName,
  338. domainName,
  339. passwordPtr,
  340. (int)loginType,
  341. LOGON32_PROVIDER_DEFAULT,
  342. ref token ) != 0 )
  343. {
  344. if ( DuplicateToken( token, 2, ref tokenDuplicate ) != 0 )
  345. {
  346. tempWindowsIdentity = new WindowsIdentity( tokenDuplicate );
  347. impersonationContext = tempWindowsIdentity.Impersonate();
  348. }
  349. else
  350. {
  351. int le =Marshal.GetLastWin32Error();
  352. throw new Win32Exception( le );
  353. }
  354. }
  355. else
  356. {
  357. int le = Marshal.GetLastWin32Error();
  358. throw new Win32Exception( le );
  359. }
  360. }
  361. else
  362. {
  363. int le = Marshal.GetLastWin32Error();
  364. throw new Win32Exception( le );
  365. }
  366. }
  367. finally
  368. {
  369. if ( token != IntPtr.Zero )
  370. {
  371. CloseHandle( token );
  372. }
  373. if ( tokenDuplicate != IntPtr.Zero )
  374. {
  375. CloseHandle( tokenDuplicate );
  376. }
  377. // Zero-out and free the unmanaged string reference.
  378. Marshal.ZeroFreeGlobalAllocUnicode( passwordPtr );
  379. }
  380. }
  381. catch ( Exception x )
  382. {
  383. LogCentral.Current.LogError(
  384. string.Format(
  385. @"[Impersonation] Error impersonating as domain '{0}', user '{1}'.",
  386. domainName,
  387. userName ),
  388. x );
  389. throw;
  390. }
  391. LogCentral.Current.LogInfo(
  392. string.Format(
  393. @"[Impersonation] Successfully impersonated as domain '{0}', user '{1}'.",
  394. domainName,
  395. userName ) );
  396. }
  397. /// <summary>
  398. /// Reverts the impersonation.
  399. /// </summary>
  400. private void UndoImpersonation()
  401. {
  402. if ( impersonationContext != null )
  403. {
  404. LogCentral.Current.LogInfo(
  405. string.Format(
  406. @"[Impersonation] About to undo impersonation." ) );
  407. try
  408. {
  409. impersonationContext.Undo();
  410. impersonationContext = null;
  411. }
  412. catch ( Exception x )
  413. {
  414. LogCentral.Current.LogError(
  415. string.Format(
  416. @"[Impersonation] Error undoing impersonation." ),
  417. x );
  418. throw;
  419. }
  420. LogCentral.Current.LogInfo(
  421. string.Format(
  422. @"[Impersonation] Successfully undone impersonation." ) );
  423. }
  424. }
  425. // ------------------------------------------------------------------
  426. #endregion
  427. #region Private variables.
  428. // ------------------------------------------------------------------
  429. /// <summary>
  430. ///
  431. /// </summary>
  432. private WindowsImpersonationContext impersonationContext = null;
  433. // ------------------------------------------------------------------
  434. #endregion
  435. }
  436. /////////////////////////////////////////////////////////////////////////
  437. /// <summary>
  438. /// How to log in the user.
  439. /// </summary>
  440. public enum LoginType
  441. {
  442. #region Enum members.
  443. // ------------------------------------------------------------------
  444. /// <summary>
  445. /// Interactive. This is the default.
  446. /// </summary>
  447. Interactive = 2,
  448. /// <summary>
  449. ///
  450. /// </summary>
  451. Batch = 4,
  452. /// <summary>
  453. ///
  454. /// </summary>
  455. Network = 3,
  456. /// <summary>
  457. ///
  458. /// </summary>
  459. NetworkClearText = 0,
  460. /// <summary>
  461. ///
  462. /// </summary>
  463. Service = 5,
  464. /// <summary>
  465. ///
  466. /// </summary>
  467. Unlock = 7
  468. // ------------------------------------------------------------------
  469. #endregion
  470. }
  471. /////////////////////////////////////////////////////////////////////////
  472. }