views:

367

answers:

3

I need to gain access to the files and directories that the current IPrincipal has access to via the Directory.GetDirectories() and Directory.GetFiles() methods, without listing the other files. The process itself is running as NETWORK SERVICE, so it must change the principal to the current user (via IPrincipal) for the duration of these calls.

I have attempted to change Thread.CurrentPrincipal to the new IPrincipal prior to the file access part, but it doesn't appear to make a difference.

Is there something else I can do, or am I missing something?

A: 

I don't think you're doing this the right way. You should look into using impersonation. For example take a look at this tutorial on how to do this.

James
A: 

You can use DllImport and the LogonUser win32 API to impersonate another user.

Arnshea
+4  A: 

Windows impersonation solves this problem by using the login details to acquire a user token. This token can then be used to obtain a WindowsIdentity, which is then used to generate an impersonation context. Within this context scope, you can then access the file system as the impersonated user.

Of course, you will need to store the user name and password for this approach to work.

First, define the Windows APIs required to obtain a user token from Windows:

internal class WindowsAPI 
{
    public const int LOGON32_PROVIDER_DEFAULT = 0;
    public const int LOGON32_LOGON_INTERACTIVE = 2;

    [DllImport( "advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode )]
    public static extern bool LogonUser( String lpszUsername, 
        String lpszDomain, String lpszPassword,
        int dwLogonType, int dwLogonProvider, ref IntPtr phToken 
    );

    [DllImport( "kernel32.dll", CharSet = CharSet.Auto )]
    public extern static bool CloseHandle( IntPtr handle );
}

Then, use these APIs to aquire a WindowsIdentity:

private WindowsIdentity GetIdentity( string userName, string password )
{
    _userToken = IntPtr.Zero;

    if ( !WindowsAPI.LogonUser(
        userName,
        AbbGrainDomain,
        password,
        WindowsAPI.LOGON32_LOGON_INTERACTIVE, WindowsAPI.LOGON32_PROVIDER_DEFAULT,
        ref _userToken
    ) )
    {
        int errorCode = Marshal.GetLastWin32Error();
        throw new System.ComponentModel.Win32Exception( errorCode );
    }

    return new WindowsIdentity( _userToken );
}

And finally, use this identity to generate an impersonation context:

public List<string> GetDirectories( string searchPath )
{
    using ( WindowsImpersonationContext wic = GetIdentity().Impersonate() )
    {
        var directories = new List<string>();

        var di = new DirectoryInfo( searchPath );
        directories.AddRange( di.GetDirectories().Select( d => d.FullName ) );

        return directories;
    }
}

Finally it is important to clean up the windows handle using the IDisposable pattern, using the stored _userToken:

if ( _userToken != IntPtr.Zero )
    WindowsAPI.CloseHandle( _userToken );
Ben Laan