Victor Vogelpoel [Macaw]

"Accomplishing the impossible means only that the boss will add it to your regular duties."

<November 2008>
SuMoTuWeThFrSa
2627282930311
2345678
9101112131415
16171819202122
23242526272829
30123456


Navigation

Good Stuff

Blogs I read

Cool Gadgets

Sites I visit regularly

Subscriptions

Post Categories



Security (RSS)

Security
Impersonation and SharePoint 3

Part1: http://dotnetjunkies.com/WebLog/victorv/archive/2005/04/20/69524.aspx
Part2: http://dotnetjunkies.com/WebLog/victorv/archive/2005/04/20/69519.aspx

The last impersonation code was reverted back to the Application Pool account but still could not access SharePoint objects. Can the cause be as described in Q892866 (http://support.microsoft.com/?kbid=892866) where "Impersonation does not work when you use some elements of the SharePoint Portal Server object model or the Windows SharePoint Services object model to impersonate an authenticated user"?

In a desperate attempt I added another logon and impersonation level to the last impersonation code. The next code seems to work for us. It reverts the identity to the Application Pool account and reimpersonates the administrator account. It's the best of all worlds: the DOMAIN\SPAdmin account can be impersonated by both our developer accounts and DOMAIN\jane.doe.

public sealed class ImpersonationUtility

{

private static string ADMINDOMAINACCOUNT = @"DOMAIN\SPadmin";

private static string ADMINDOMAIN = "DOMAIN";

private static string ADMINACCOUNT = "SPadmin";

private static string ADMINPASSWORD = "password";

private WindowsImpersonationContext _wiContext;

/// <summary>

/// Private ctor.

/// </summary>

private ImpersonationUtility()

{}

/// <summary>

/// Start impersonating the administrator.

/// </summary>

/// <returns>an ImpersonationUtility instance.</returns>

public static ImpersonationUtility ImpersonateAdmin()

{

ImpersonationUtility imp = new ImpersonationUtility();

imp._ImpersonateAdmin();

return imp;

}

/// <summary>

/// Undo the impersonation.

/// </summary>

public void Undo()

{

if (this._wiContext != null)

{

this._wiContext.Undo();

this._wiContext = null;

}

}

private void _ImpersonateAdmin()

{

IntPtr token = IntPtr.Zero;

IntPtr tokenDuplicate = IntPtr.Zero;

try

{

// Only start admin impersonation if we're not the admin...

if (String.Compare(ADMINDOMAINACCOUNT, WindowsIdentity.GetCurrent().Name, true, CultureInfo.InvariantCulture) != 0)

{

// Temporarily stop the impersonation started by Web.Config.

// WindowsIdentity.Impersonate() will store the current identity (the

// account of the requestor) and return to the account of the ApplicationPool

// which is "DOMAIN\SPAdmin".

this._wiContext = WindowsIdentity.Impersonate(IntPtr.Zero);

// But somehow the reverted account "DOMAIN\SPAdmin" still does

// not have enough privileges to access SharePoint objects, so

// we're logging in DOMAIN\SPAdmin again...

if (NativeMethods.LogonUserA(ADMINACCOUNT, ADMINDOMAIN, ADMINPASSWORD, NativeMethods.LOGON32_LOGON_INTERACTIVE,

NativeMethods.LOGON32_PROVIDER_DEFAULT, ref token) != 0)

{

if (NativeMethods.DuplicateToken(token, 2, ref tokenDuplicate) != 0)

{

WindowsIdentity wi = new WindowsIdentity(tokenDuplicate);

// NOTE: Impersonate may fail if account that tries to impersonate does

// not hold the "Impersonate after Authentication" privilege

// See local security policy - user rights assignment.

// Note that the ImpersonationContext from the Impersonate() call

// is ignored. Upon the Undo() call, the original account

// will be reinstated.

wi.Impersonate();

}

else

{

throw new Win32Exception(Marshal.GetLastWin32Error(),

"Impersonation: Error duplicating token after logon for user \"DOMAIN\\SPAdmin\"");

}

}

else

{

throw new Win32Exception(Marshal.GetLastWin32Error(),

"Impersonation: Error logging on user \"DOMAIN\\SPAdmin\"");

}

}

}

finally

{

if (token != IntPtr.Zero)

Kernel32.NativeMethods.CloseHandle(token);

if (tokenDuplicate != IntPtr.Zero)

Kernel32.NativeMethods.CloseHandle(tokenDuplicate);

}

}

}

Now, can anyone explain why the DOMAIN\SPAdmin account needs to impersonate itself (!) to access SharePoint objects in code? Is the level of the reverted account token too low to do any serious SharePoint work?

References:

posted Wednesday, April 20, 2005 7:34 PM by victorv with 2 Comments

Impersonation and SharePoint 2

Part1: http://dotnetjunkies.com/WebLog/victorv/archive/2005/04/20/69524.aspx
part3: http://dotnetjunkies.com/WebLog/victorv/archive/2005/04/20/69521.aspx

The next iteration of the impersionation code uses a trick I have learned from Keith Brown's Security Wiki: temporarily undoing the impersonation by the impersonation by <identity impersonate='true'/> in the web.config. This way we're back at the Application Pool account, which is the administrator. The next iteration of the impersonation code worked correctly in changing the identity to DOMAIN\SPAdmin and returning it to the user doing the page request, but still failed to access SharePoint objects :-(

public sealed class ImpersonationUtility

{

private static string ADMINDOMAINACCOUNT = @"DOMAIN\SPadmin";

private static string ADMINDOMAIN = "DOMAIN";

private static string ADMINACCOUNT = "SPadmin";

private static string ADMINPASSWORD = "password";

 

private WindowsImpersonationContext _wiContext;

 

/// <summary>

/// Private ctor.

/// </summary>

private ImpersonationUtility()

{}

 

/// <summary>

/// Start impersonating the administrator.

/// </summary>

/// <returns>an ImpersonationUtility instance.</returns>

public static ImpersonationUtility ImpersonateAdmin()

{

ImpersonationUtility imp = new ImpersonationUtility();

imp._ImpersonateAdmin();

return imp;

}

 

/// <summary>

/// Undo the impersonation.

/// </summary>

public void Undo()

{

if (this._wiContext != null)

{

this._wiContext.Undo();

this._wiContext = null;

}

}

 

private void _ImpersonateAdmin()

{

// Only start admin impersonation if we're not the admin...

if (String.Compare(ADMINDOMAINACCOUNT, WindowsIdentity.GetCurrent().Name, true, CultureInfo.InvariantCulture) != 0)

{

// Temporarily stop the impersonation started by Web.Config.

// WindowsIdentity.Impersonate() will store the current identity (the

// account of the requestor) and return to the account of the ApplicationPool

// which is "DOMAIN\SPAdmin".

this._wiContext = WindowsIdentity.Impersonate(IntPtr.Zero);

}

}

}

The key in this code is WindowsIdentity.Impersonate(IntPtr.Zero). Remember I told you about the Impersonation code from Q306158 which uses RevertToSelf() API call to return the identity to the Application Pool account; Impersonate(IntPtr.Zero) uses this RevertToSelf() internally. The fun part is that the Undo() does return the identity back to the requestor account!

As I said, unfortunately, the SPAdmin account after impersonation still has no access to SharePoint objects...

posted Wednesday, April 20, 2005 7:28 PM by victorv with 3 Comments

Impersonation and SharePoint

[note: sorry about the order of the three Impersonation And SharePoint posts. I've almost seen hell in trying to post the story in one message on dotnetjunkies]

part2: http://dotnetjunkies.com/WebLog/victorv/archive/2005/04/20/69519.aspx
part3: http://dotnetjunkies.com/WebLog/victorv/archive/2005/04/20/69521.aspx

I have been working in a team on a pretty large SharePoint 2003 based intranet where we met a problem with impersonation. I think we found a solution, but I don't fully understand why it works. Perhaps one of you can explain it to me.

But let me start with explaining the environment. Our dev workstations are Windows 2003 Server with Windows SharePoint Services and SharePoint Portal with a shared central SharePoint database. The application pools run under the intranet administrator account, say "DOMAIN\SPAdmin".

The intranet application we develop mostly uses standard SharePoint security, but some actions require administrative rights. For example, settings are stored in a list in a special configuration site and can only be retrieved with admin rights. The current user needs to have elevated rights to access these settings and the intranet admin account has these rights. The current user needs to impersonate the admin.

This is what should happen when we request a page from the intranet:

  1. IIS/ASP.NET receives the request. The current identity is the one from the Application Pool: DOMAIN\SPAdmin.
  2. Because the web.config contains , Anonymous Authentication is off and Windows Integrated Authentication is on, ASP.NET impersonates the user, making the current identity DOMAIN\victorv.
  3. Work is done, but configuration settings need to be retrieved. The administrator account is impersonated. The current identity is (again) DOMAIN\SPAdmin.
  4. Settings are retrieved and the impersonation is undone. The current identity is returned to DOMAIN\victorv.

The impersonation C# code we used initially was from Microsoft support article Q306158 (http://support.microsoft.com/?kbid=306158), but this code has one nasty shortcoming for SharePoint use: the impersonated account (step 2) is not reinstated with the undo: the identity is left DOMAIN\SPAdmin, while it should return to DOMAIN\victorv! This is because the RevertToSelf() call sets the baseline for the impersonation context to be undone. And the code executed after the undone impersonation runs under the wrong identity and will confuse SharePoint security.

This is the impersonation code I derived from Q306158:

public sealed class ImpersonationUtility

{

private static string ADMINDOMAINACCOUNT = @"DOMAIN\SPAdmin";

private static string ADMINPASSWORD = "password";

private WindowsImpersonationContext _wiContext;

///

/// Private ctor.

///

private ImpersonationUtility()

{}

public static ImpersonationUtility ImpersonateAdmin()

{

ImpersonationUtility imp = new ImpersonationUtility();

 

if (imp.Impersonate(ADMINDOMAINACCOUNT, ADMINPASSWORD))

return imp;

 

return null;

}

 

public void Undo()

{

if (this._wiContext != null)

{

this._wiContext.Undo();

this._wiContext = null;

}

}

 

///

/// Impersonate the designated user.

///

/// Account of format "DOMAIN\user"

/// password of the user

/// True if the impersonation succeeded, false if not.

private bool Impersonate(string domainAccount, string password)

{

IntPtr token = IntPtr.Zero;

IntPtr tokenDuplicate = IntPtr.Zero;

 

try

{

string[] accountparts = domainAccount.Split(new char[]{'\\'});

string domain = accountparts[0];

string username = accountparts[1];

 

// If the requested domainAccount is different from the thread identity,

// then revert to self first before attempting the logon.

if (String.Compare(domainAccount, WindowsIdentity.GetCurrent().Name, true, CultureInfo.InvariantCulture) != 0)

{

if (NativeMethods.LogonUserA(username, domain, password, NativeMethods.LOGON32_LOGON_NETWORK,

NativeMethods.LOGON32_PROVIDER_DEFAULT, ref token) != 0)

{

if (NativeMethods.DuplicateToken(token, 2, ref tokenDuplicate) != 0)

{

WindowsIdentity wi = new WindowsIdentity(tokenDuplicate);

// NOTE: Impersonate may fail if account that tries to impersonate does

// not hold the "Impersonate after Authentication" privilege

// See local security policy - user rights assignment.

this._wiContext = wi.Impersonate();

}

else

{

throw new Win32Exception(Marshal.GetLastWin32Error(),

String.Format(CultureInfo.InvariantCulture,

"Inpersonation: Error duplicating token after logon for user \"{0}\"", domainAccount));

}

}

else

{

throw new Win32Exception(Marshal.GetLastWin32Error(),

String.Format(CultureInfo.InvariantCulture,

"Inpersonation: Error logging on user \"{0}\"", domainAccount));

}

}

}

finally

{

if (token != IntPtr.Zero)

Kernel32.NativeMethods.CloseHandle(token);

if (tokenDuplicate != IntPtr.Zero)

Kernel32.NativeMethods.CloseHandle(tokenDuplicate);

}

 

return (this._wiContext != null);

}

}

 

internal sealed class NativeMethods

{

private NativeMethods() { }

 

[DllImport("kernel32.dll", CharSet=CharSet.Auto)]

public static extern bool CloseHandle(IntPtr handle);

 

public const int LOGON32_PROVIDER_DEFAULT = 0;

public const int LOGON32_LOGON_INTERACTIVE = 2;

public const int LOGON32_LOGON_NETWORK = 3;

 

[DllImport("advapi32.dll")]

public static extern int LogonUserA(String lpszUserName,

String lpszDomain,

String lpszPassword,

int dwLogonType,

int dwLogonProvider,

ref IntPtr phToken);

 

[DllImport("advapi32.dll", CharSet=CharSet.Auto, SetLastError=true)]

public static extern int DuplicateToken(IntPtr hToken,

int impersonationLevel,

ref IntPtr hNewToken);

 

[DllImport("advapi32.dll", CharSet=CharSet.Auto, SetLastError=true)]

public static extern bool RevertToSelf();

}

And this is how the impersonation code is used:

ImpersonationUtility imp = null;

 

try

{

imp = ImpersonationUtility.ImpersonateAdmin();

// do admin work...

}

finally

{

// Unlogon Admin

if (imp != null)

imp.Undo();

}

The impersonation code above worked fine for us developers, but not on another 'Persona' account we use to test the intranet: "DOMAIN\jane.doe"; this account represents a generic intranet user with limited rights and this account failed to impersonate the admin.

It took a lot of Googling and reading, and I think DOMAIN\jane.doe is failing impersonation because this account cannot create an impersonation level token for DOMAIN\SPAdmin (remember we are on a Windows 2003 server and security is tight on this OS). This would limit the account to check group membership and such. Read about this by Joe Kaplan in news post http://groups-beta.google.com/group/microsoft.public.dotnet.framework.aspnet.security/browse_thread/thread/d9af678e4e9738a0/2e3795fe90f7abea?q=joe+kaplan+impersonate+windowsidentity&rnum=3#2e3795fe90f7abea

A tip from my coworker Serge v/d Oever (http://weblogs.asp.net/soever) led me to Keith Browns online security Wiki book "The .NET Developer's Guide to Windows Security", where he describes temporarily undoing the impersonation by <identity impersonate='true'/> in the web.config. This way we're back at the Application Pool account, which is the administrator. The next iteration of the impersonation code worked correctly in changing the identity to DOMAIN\SPAdmin and returning it to the user doing the page request, but failed to access SharePoint objects...  More about that later.

posted Wednesday, April 20, 2005 7:38 PM by victorv with 2 Comments

Encrypted drive on flash disk using Cryptainer LE

I finally found a decent application for securing data on my USB flash disk. Don't you have some sensitive data on your flash disk that you'd preferred to have stored encrypted on your drive?
My Sony 256MB MicroVault came with "Password Lock", which creates a large encrypted file on the flash drive and exposes this file as a drive in Explorer. "Password Lock" looks like a Windows 3.1 application and I did not like having to install the application on every PC I wanted to access this virtual drive. Also, "FD.ExE" seems to be loaded all the time, whether I have the flash disk connected or not.

A pleasant surprise was the free "Cryptainer LE", with the capability to create an encrypted file up to 25MB (upgrades store up to 50GB). The encrypted file, or "volume" as Cryptainer calls it, is exposed as a drive, just like Sony's Password Lock. The Cryptainer User Interface is very modern although somewhat confusing because of multiple windows. Once the volume is loaded (mounted), a drive letter is created and the application can be dismissed to the system tray bar and use the volume like a drive from Windows Explorer.

Another plus is that Cryptainer supports more than one volume and even "mobile volumes", like read-only volumes on CD-ROM/RW disks and, to my liking: flash disk. A mobile version of the Cryptainer is stored on the disk and runs without installing:

  1. Insert the flashdisk in a USB port and go to the flash driveletter in Explorer
  2. double-click “cryptainerlemobile.exe“ in the root of the drive and Cryptainer starts without installing!
  3. The default volume is loaded and you have to type the password for the volume.
  4. Cryptainer creates a drive letter for the volume. yeah!

Note that a system driver is installed silently in the background which can be de-installed when unmounting.

I installed Cryptainer initially on my laptop to use it for storing sensitive data like registrations, subscriptions and passwords, which I usually store in separate text files. I quickly found "Install Cryptainer Mobile" in the Tools menu and hoped this to be my solution for carrying sensitive files with me on my flash disk.
"Install Cryptainer Mobile" installs a "cryptainerlemobile.exe" stub application in the root and additional application files in subfolder "CryptainerMobileFiles" of the flash disk. This is all you need to create and mount a volume on the flash disk and access it on any PC through a drive letter.

Kewl.

Cryptainer LE can be downloaded from the company's (Cypherix) website: http://www.cypherix.com/cryptainerle/

posted Tuesday, February 15, 2005 8:39 PM by victorv with 0 Comments




Powered by Dot Net Junkies, by Telligent Systems