How to grant Access on Files on a share drive?

179 views
Skip to first unread message

Jörgen Ahrens

unread,
Nov 8, 2005, 3:55:54 AM11/8/05
to
Hi All

We are trying to grant access on files on a share drive.

We have a client application running on a client machine where we
impersonate a technical user that has access to the server.
The actual user doesn't have access to the files located on the server. At
runtime we would like to grant access to the client user (on a specific file
on the server).

We managed to grant access by :
- mapping the shared folder from the server to the client machine
- we did not impersonate the technical user, but we ran the client
application under the profile of the technical user (what is not what we
need).

It seems as if the impersonated would not recongnize the mapped drives.(we
tried to implement the mapping on the client machine under the normal user
account and under the technical user account as well as on the server
machine)

Is it true, that we need to have a mapped shared drive to actually succeed?
or is it possible to access the shared folder directly without mapping the
networkdrive?

If we need to map the network drive, how can we assure that the impersonated
user can see the mapped network drives?

We attached our testprogram (Impersonation seemed to worked well and is not
included in the testprogram).

thanks for your help
j.ahrens

---------------------------------------------------------------------------------------------
Test scenario:

Server: S01
Client: W07
Shared Folder on the Server: \\S01\_KnowledgeBase (D:\Daten\_KnowledgeBase)
Mapped network drive: \\S01\_KnowledgeBase is mapped onto K:\ on the client
(would be nice if we could drop that and just use \\S01\_KnowledgeBase
directly)

---------------------------------------------------------------------------------------------
CODE
---------------------------------------------------------------------------------------------


using System;
using System.Management;
using System.Collections;

namespace Ascami.LogExcellence.WinApp
{
// Access mask (see AccessMask property)
[Flags]
enum Mask : uint
{
FileReadData = 0x00000001,
FileWriteData = 0x00000002,
FileAppendData = 0x00000004,
FileReadEA = 0x00000008,
FileWriteEA = 0x00000010,
FileExecute = 0x00000020,
FileDeleteChild = 0x00000040,
FileReadAttributes = 0x00000080,
FileWriteAttributes= 0x00000100,


Delete = 0x00010000,
ReadControl = 0x00020000,
WriteDac = 0x00040000,
WriteOwner = 0x00080000,
Synchronize = 0x00100000,


AccessSystemSecurity = 0x01000000,
MaximumAllowed = 0x02000000,


GenericAll = 0x10000000,
GenericExecute= 0x20000000,
GenericWrite = 0x40000000,
GenericRead = 0x80000000
}

[Flags]
enum AceFlags : uint
{
NonInheritAce = 0,
ObjectInheritAce = 1,
ContainerInheritAce = 2,
NoPropagateInheritAce = 4,
InheritOnlyAce = 8,
InheritedAce = 16
}

[Flags]
enum AceType : uint
{
AccessAllowed = 0,
AccessDenied = 1,
Audit = 2
}

/// <summary>
/// The class offers two static functions used to allow or deny access to
files.
/// </summary>
class ManageFilePermissions
{
/// <summary>
/// This main is used to test this class.
/// </summary>
public static void Main()
{
//Start Impersonation
// Impersonation imp = new Impersonation();
// imp.LoadProfile("Ascami", "logexcellence", "pwd");
// imp.StartImpersonation();
//
//
System.Windows.Forms.MessageBox.Show(System.IO.Directory.Exists("K:\\1\\2").ToString());

bool success;
//Allow access for samrein to file K:\1\2\3.xls on server S01
success = ManageFilePermissions.AllowFileAccess("Ascami", "samrein",
"K:\\1\\2\\3.xls", "S01");
//Deny samrein
success = ManageFilePermissions.DenyFileAccess("Ascami", "samrein",
"K:\\1\\2\\3.xls", "S01");
//Allow wiederkehr
success = ManageFilePermissions.AllowFileAccess("Ascami", "swiederkehr",
"K:\\1\\2\\3.xls", "S01");
//Allow samrein
success = ManageFilePermissions.AllowFileAccess("Ascami", "samrein",
"K:\\1\\2\\3.xls", "S01");
//Deny mahrenes
success = ManageFilePermissions.DenyFileAccess("Ascami", "mahrens",
"K:\\1\\2\\3.xls", "S01");
//Deny wiederkehr
success = ManageFilePermissions.DenyFileAccess("Ascami", "swiederkehr",
"K:\\1\\2\\3.xls", "S01");
//Deny samrein
success = ManageFilePermissions.DenyFileAccess("Ascami", "samrein",
"K:\\1\\2\\3.xls", "S01");

}

/// <summary>
/// Allows the specified user to read and write the specified file.
/// </summary>
/// <param name="domain">The domain of the user</param>
/// <param name="username">The username of the user</param>
/// <param name="file">The file</param>
/// <param name="server">The server of the file. If it is placed on the
local machine use '.'</param>
/// <returns>true if the user is now allowed to read and write</returns>
public static bool AllowFileAccess(string domain, string username, string
file, string server)
{
ManagementObject lfs;
ManagementBaseObject descriptor; //The security descriptor
ManagementBaseObject[] dacl = FetchDACL(server, file, out lfs, out
descriptor);
if (dacl == null)
{
System.Windows.Forms.MessageBox.Show("Failed to fetch DACL.", "Error");
return false;
}
int aceIndex = GetACE(dacl, domain, username);
if (aceIndex == -1)
{
//ace does not exist -> create it, add it
ManagementBaseObject ace = CreateACE(domain, username, Mask.GenericRead
| Mask.GenericWrite);
if (ace == null)
{
System.Windows.Forms.MessageBox.Show("Could not create ACE for
domain\\username.", "Error");
return false;
}
dacl = AddACE(dacl, ace);
if (!WriteDACL(dacl, lfs, descriptor))
{
System.Windows.Forms.MessageBox.Show("Could not write DACL.", "Error");
return false;
}
return true;
}
else
{
//ace exists, modify it
ManagementBaseObject ace = dacl[aceIndex];
ModifyACE(ace, Mask.GenericRead | Mask.GenericWrite);
if (!WriteDACL(dacl, lfs, descriptor))
{
System.Windows.Forms.MessageBox.Show("Could not write DACL.", "Error");
return false;
}
return true;
}
}

/// <summary>
/// Denies the specified user to read and write the specified file.
/// </summary>
/// <param name="domain">The domain of the user</param>
/// <param name="username">The username of the user</param>
/// <param name="file">The file</param>
/// <param name="server">The server of the file. If it is placed on the
local machine use '.'</param>
/// <returns>true if the user is now denied to read and write</returns>
public static bool DenyFileAccess(string domain, string username, string
file, string server)
{
ManagementObject lfs;
ManagementBaseObject descriptor;
ManagementBaseObject[] dacl = FetchDACL(server, file, out lfs, out
descriptor);
if (dacl == null)
{
System.Windows.Forms.MessageBox.Show("Failed to fetch DACL.", "Error");
return false;
}
int aceIndex = GetACE(dacl, domain, username);
if (aceIndex == -1)
{
//No entry exists, return
return true;
}
else
{
dacl = RemoveACE(dacl, aceIndex);
if (!WriteDACL(dacl, lfs, descriptor))
{
System.Windows.Forms.MessageBox.Show("Could not write DACL.", "Error");
return false;
}
return true;
}
}

/// <summary>
/// Returns the index of the ACE from the DACL-list if it exists
/// </summary>
/// <param name="dacl">the discretionary access control list</param>
/// <param name="domain">The domain of the user</param>
/// <param name="username">The username of the user</param>
/// <returns>the index of the ace or -1 if none exists</returns>
private static int GetACE(ManagementBaseObject[] dacl, string domain,
string username)
{
ManagementBaseObject ace;
for (int i=0;i<dacl.Length;i++)
{
ace = dacl[i];

//Find out if it is an ace of the specified user
ManagementBaseObject trustee = (ManagementBaseObject)(ace["Trustee"]);
string trusteeDomain = trustee.Properties["Domain"].Value.ToString();
string trusteeUsername = trustee.Properties["Name"].Value.ToString();

if (string.Compare(domain, trusteeDomain, true)!= 0) continue;
if (string.Compare(username, trusteeUsername, true) != 0) continue;

//return the ace
return i;
}

return -1;
}

/// <summary>
/// Sets the ACE access mask.
/// </summary>
/// <param name="ace">The access control entry you would liko to
modify</param>
/// <param name="accessMask">The access mask</param>
private static void ModifyACE(ManagementBaseObject ace, Enum accessMask)
{
ace.Properties["AccessMask"].Value = accessMask;
}

/// <summary>
/// Creates a new Access Control Entry
/// </summary>
/// <param name="domain">The domain of the user</param>
/// <param name="username">The username of the user</param>
/// <param name="accessMask">The access mask</param>
/// <returns>The new Access Control Entry or null if the command
failed</returns>
private static ManagementBaseObject CreateACE(string domain, string
username, Enum accessMask)
{
ManagementBaseObject trustee = null;
ManagementBaseObject ace = null;

// Initialize new Trustee
try
{
trustee = new ManagementClass("Win32_Trustee");
trustee.Properties["Domain"].Value = domain;
trustee.Properties["Name"].Value = username;
}
catch (Exception)
{
//Happens if trustee does not exist
return null;
}
try
{
ace = new ManagementClass("Win32_ACE");
ace.Properties["AccessMask"].Value = accessMask;
ace.Properties["AceFlags"].Value = AceFlags.NoPropagateInheritAce;
ace.Properties["AceType"].Value = AceType.AccessAllowed;
ace.Properties["Trustee"].Value = trustee;
}
catch (Exception)
{
//No idea when this happens...
return null;
}

return ace;
}

/// <summary>
/// Writes the passed Discretionary Access Control List.
/// </summary>
/// <param name="dacl">The Discretionary Access Control List</param>
/// <param name="lfs">the object on which the dacl is written</param>
/// <param name="descriptor">the security descriptor</param>
/// <returns>true if successful, false if it failed</returns>
private static bool WriteDACL(ManagementBaseObject[] dacl,
ManagementObject lfs, ManagementBaseObject descriptor)
{
ManagementBaseObject inParams =
lfs.GetMethodParameters("SetSecurityDescriptor");
descriptor.Properties["Dacl"].Value = dacl;
inParams["Descriptor"] = descriptor;
ManagementBaseObject ret = lfs.InvokeMethod("SetSecurityDescriptor",
inParams, null);
if ((Convert.ToUInt32(ret.Properties["ReturnValue"].Value) == 0))
return true;
else
return false;
}

/// <summary>
/// Adds an ACE to a DACL
/// </summary>
/// <param name="dacl">The discretionary access control list</param>
/// <param name="ace">The access control entry</param>
/// <returns>the new discretionary access control list</returns>
private static ManagementBaseObject[] AddACE(ManagementBaseObject[] dacl,
ManagementBaseObject ace)
{
ManagementBaseObject[] newDacl = new ManagementBaseObject[dacl.Length+1];

dacl.CopyTo(newDacl, 0);
newDacl[newDacl.Length-1] = ace;

return newDacl;
}

/// <summary>
/// Removes an ACE of a DACL
/// </summary>
/// <param name="dacl">The discretionary access control list</param>
/// <param name="index">The index of the ACE you would like to
remove</param>
/// <returns>the new discretionary access control list</returns>
private static ManagementBaseObject[] RemoveACE(ManagementBaseObject[]
dacl, int index)
{
ManagementBaseObject[] newDacl = new ManagementBaseObject[dacl.Length-1];
int count = 0;
for (int i=0;i<dacl.Length;i++)
{
if (i==index) continue;

newDacl[count] = dacl[i];
count++;
}

return newDacl;
}

/// <summary>
/// Fetches the DACL for the specified file (non-inherited only!)
/// </summary>
/// <param name="server">The server the file is placed</param>
/// <param name="file">The path of the file</param>
/// <param name="lfs">out parameter: identifies the object(file)</param>
/// <param name="descriptor">out parameter: identifies the security
descriptor</param>
/// <returns>The discretionary access control list</returns>
private static ManagementBaseObject[] FetchDACL(string server, string
file, out ManagementObject lfs, out ManagementBaseObject descriptor)
{
lfs = null;
descriptor = null;

//The WMI Path of the file
try
{
ManagementPath path = new ManagementPath();
path.Server = server;
path.NamespacePath = @""; //for local machine @"root\cimv2" works, for
server not
//Replace backslashes by double blackslashes
file = file.Replace("\\", "\\\\");
path.RelativePath =
@"Win32_LogicalFileSecuritySetting.Path='"+file+"'";

//Fetch object
lfs = new ManagementObject(path);

// Get the security descriptor for this object
// bool EnablePrivileges = lfs.Scope.Options.EnablePrivileges;
lfs.Scope.Options.EnablePrivileges = true;
ManagementBaseObject outParams =
lfs.InvokeMethod("GetSecurityDescriptor", null, null);

if (((uint)(outParams.Properties["ReturnValue"].Value)) != 0)
{
System.Windows.Forms.MessageBox.Show("Failed to fetch object.",
"Error");
return null;
}

descriptor =
(ManagementBaseObject)(outParams.Properties["Descriptor"].Value);
//The DACL is an array of Win32_ACE objects.
ManagementBaseObject[] dacl =
(ManagementBaseObject[])(descriptor.Properties["Dacl"].Value);

//Remove all inherited aces
ArrayList nonInheritedAces = new ArrayList();
foreach(ManagementBaseObject ace in dacl)
{
if ((uint)ace["AceFlags"] == (uint)AceFlags.NonInheritAce)
{
nonInheritedAces.Add(ace);
}
}

dacl = new ManagementBaseObject[nonInheritedAces.Count];
nonInheritedAces.CopyTo(dacl);

return dacl;
}
catch (Exception e)
{
e.ToString();
return null;
}
}
}
}

Jeffrey Tan[MSFT]

unread,
Nov 9, 2005, 1:20:39 AM11/9/05
to
Hi jahrens,

I will spend a little more time in this issue, I will reply to you ASAP.
Thanks

Best regards,
Jeffrey Tan
Microsoft Online Partner Support
Get Secure! - www.microsoft.com/security
This posting is provided "as is" with no warranties and confers no rights.

Jeffrey Tan[MSFT]

unread,
Nov 9, 2005, 3:47:55 AM11/9/05
to
Hi jahrens,

Thanks for your post.

Based on my understanding, you want to access a shared folder resided on a
remote machine. The current logon user did not have the access right, but
you know a technical user account who have the access right on the shared
folder.

To do this, there is no need to do the driver mappinjg. Driver mapping is
just a convenience way of referring remote folder as local driver, we may
workaround it to use \\UNC\folder to access remote folder.

The critical point is establishing an active LAN session between your
client machine and remote share machine. Also, we must specific a
credential(the technical user account credential) other than current logon
session. We have 2 ways to do this:
1. Windows provided a tool named Net.exe, which provide the command line to
establish the LAN session between 2 machines, in .Net, code below shows how
to get this done:

//my test server: sha-user-718
//test share folder: \\sha-user-718\music
//available access account: SecurityTest with password: Password01!
// So we can use Net.exe to establish the Lan session like this:
//Net use \\sha-user-718\music /u:sha-user-718\securityTest Password01!
private void button1_Click(object sender, System.EventArgs e)
{
ProcessStartInfo psi=new ProcessStartInfo(@"c:\windows\system32\net",
@"use \\sha-user-718\music /u:sha-user-718\securityTest Password01!");
psi.UseShellExecute=false;
psi.CreateNoWindow=true;
Process p=new Process();
p.StartInfo=psi;
p.Start();
p.WaitForExit();

Process.Start(@"\\sha-user-718\music\1.wma");
}

2. Also, windows provided NetUseAdd API to establish the LAN session
programmatically, sample code listed below:

//test scenario the same as #1
[DllImport("NetApi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
internal static extern System.UInt32 NetUseAdd(
string UncServerName,
int Level,
ref USE_INFO_2 Buf,
out uint ParmError);
private void button2_Click(object sender, System.EventArgs e)
{
USE_INFO_2 useInfo = new USE_INFO_2();
useInfo.ui2_remote = @"\\sha-user-718\music";
useInfo.ui2_password = "Password01!";
useInfo.ui2_asg_type = 0; //disk drive
useInfo.ui2_usecount = 1;
useInfo.ui2_username = "securitytest";
useInfo.ui2_domainname= "sha-user-718";

uint paramErrorIndex;
uint returnCode = NetUseAdd(String.Empty, 2, ref useInfo, out
paramErrorIndex);
if (returnCode != 0)
{
throw new Win32Exception((int)returnCode);
}

Process.Start(@"\\sha-user-718\music\1.wma");
}

Both code works well in my winform application.

Hope this helps

Jeffrey Tan[MSFT]

unread,
Nov 9, 2005, 3:48:39 AM11/9/05
to
Sorry, missed the definition for USE_INFO_2 structure:

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
internal struct USE_INFO_2
{
internal string ui2_local;
internal string ui2_remote;
internal string ui2_password;
internal uint ui2_status;
internal uint ui2_asg_type;
internal uint ui2_refcount;
internal uint ui2_usecount;
internal string ui2_username;
internal string ui2_domainname;

Jörgen Ahrens

unread,
Nov 10, 2005, 10:15:06 AM11/10/05
to
Hi Jeffrey

The problem is a little bit more complex.


We have a server and a workstation.

Let's say we got a user running a client application on his machine. We don't
want this user to have any access to the server (except through our
application).


We figured, that we create a new account, that has access to the server. Let's
call him superuser.

Now when the client starts the application, we are starting impersonating
the superuser, by using (LogonUser, DuplicateToken and creating a new
instance of WindowsIdentity and finally calling Impersonate(). This works
fine and a call to System.Environment.UserName returns 'superuser'.


We already can delete/create files on the server using
System.IO.File.Delete(.). (without doing the netUseAdd stuff). Accessing the
filesystem on the server through a FileDialog works fine too.

The next step is to temporarely allow the normal user account to access a
file located at the server (to allow the user opening a file with a program,
that runs under his original account). Therefore we tried to use WMI. But
this is where our problems begin. We are not able to fetch the DACL under
the impersonated user account. Is there any work-around. Is it some kind of
security issue, that causes the problems?


Thanks in advance for your help.

J. Ahrens


Jeffrey Tan[MSFT]

unread,
Nov 11, 2005, 1:01:09 AM11/11/05
to
Hi jahrens,

Thanks for your feedback.

Yes, with your detailed feedback, your issue appears much clear to now. I
think what you want to do is temporarily grant access permission to the
normal user to access certain file on the server. Then in the closing event
of your program, you will again cancel this granting and restore the normal
user to the original state, yes?

First, for remote server access in Windows, there are 2 obstacles:
1. LAN session share permission(This is set in "Folder Properties"
dialog->"Sharing"->"Permissions" button)
2. DACL permission on certain secured objects(such as Files/Folders etc..)

Below is the further information I need to know:
1. How you restrict your normal user account access to the server machine?
Through the first LAN session sharing restriction or through certain DACL
permission restrict on server machine? This is critical information to
understand.
2. Is the normal user account a domain account or only a client workstation
local account?
3. Do you want to grant the normal user account access to the entire server
machine or only certain files on the server machines?

Ok, before you provide the above information, let me give you some more
information about this issue.
What I current know is that your server machine must know super user
account, that is the super user account is a domain account or local
account of server machine, so this account has the access right on server
machine.

Now, for the normal user account:
1. If it is a domain account(I assume that your workstation and server
machine both joined this domain, then your server machine really knows of
this normal user account):
1. If we want to allow this account to access certain file on the
server, we have to workaround the above 2 obstacles, I will list how to
break these 2 obstacles in the below statement.
2. If we want to allow this account to temporily access all the
files on the server, modifying the DACL and sharing perssion is too boring.
I think the easiest way is adding this normal account to the local
administrators group of server machine.
2. If it is only a local user account of workstation,then Server machine
know nothing about this account. This is somewhat difficult to allow it to
access Server machine, because Server machine does not know what this
account is. However, there is a trick to create a new user account in
Server machine with the same account name and the same password, then add
this new account to certain local group in Server machine which has enough
right. Then the authentication will succeed without any problem.

Ok, let's turn to how to break these 2 obstacles:
For #1 obstacle, we can use NetShareGetInfo API with PSHARE_INFO_502 to get
the Security Descriptor of the sharing permission. Then after addding a
positive allowed ACE into the Security Descriptor, we can use
NetShareSetInfo with PSHARE_INFO_502 to set the new Security Descriptor
into the sharing.
For #2, we can do the normal DACL programming to the NTFS folder, add a
positive ACE which grant the permission to the normal user account to the
DACL. Then re-set the new DACL into the NTFS folder security descriptor.
There are a lot of articles in the network about how to do DACL
programming, you can search for it. If you have trouble doing this, please
feel free to tell me. Note: many work of above requires your super user
account has Admin right on the Server machine. Thanks

Hope this helps

Jörgen Ahrens

unread,
Nov 11, 2005, 7:31:19 AM11/11/05
to
Hi Jeffrey

What you wrote is completely right.

Both users (the superuser, which has administrators rights on the server,
and the client, which has no rights at all concerning the server) are in the
same domain.

We thought about giving the client no rights at all, not even for the shared
drive (but this is something we could discuss (read-only rights on the
shared folder).

At runtime, we would like to use the superuser to modify the DACL of a file
and the parent folder, in order to allow the client access to that file when
he opens it. Once the file has been opened, the ACE is again removed to keep
the user from accessing the file using the Explorer.
Again, shortly before the saving operation is done, we will add the ACE and
remove it when the saving is done.


So here are the answers to your 3 questions:

1. Client sees the Shared Folder 'MySharedFolder' on the server machine, but
has no rights granted, not even read-only.

2. All accounts are domain accounts and in the same domain.

3. We would like to grant the client Read or Write permission on one file
(which means that we will have to grant rights on the file and the parent
directory)


Again: The problem that we are currently not able to solve is the
modification of the DACL of the file. I post the code below, so that you can
see, what it does.
Keep in mind, that we were able to do it as long as the program ran under
the superuser. But if we switched the profile, using the client's account
and impersonated the superuser, we were no longer able to access the DACL on
the server.

Is the problem the impersonation, or is the problem the insufficient access
to the 'MySharedFolder' althogh we are impersonatinng the superuser?
You may have to try the things out doing the impersonation...


Thanks for your advice
J. Ahrens

-----------------------------------------------------------------------------------------------------------------------------------------------
using System;

using System.Management;

using System.Collections;

namespace Test

{

/// <summary>

/// The class offers two static functions used to allow or deny access to
files.

/// </summary>

private class ManageFilePermissions

{


// Access mask (see AccessMask property)

[Flags]

private enum Mask : uint

{

FileReadData = 0x00000001,

FileWriteData = 0x00000002,

FileAppendData = 0x00000004,

FileReadEA = 0x00000008,

FileWriteEA = 0x00000010,

FileExecute = 0x00000020,

FileDeleteChild = 0x00000040,

FileReadAttributes = 0x00000080,

FileWriteAttributes= 0x00000100,

Delete = 0x00010000,

ReadControl = 0x00020000,

WriteDac = 0x00040000,

WriteOwner = 0x00080000,

Synchronize = 0x00100000,

AccessSystemSecurity = 0x01000000,

MaximumAllowed = 0x02000000,

GenericAll = 0x10000000,

GenericExecute= 0x20000000,

GenericWrite = 0x40000000,

GenericRead = 0x80000000

}

[Flags]

private enum AceFlags : uint

{

NonInheritAce = 0,

ObjectInheritAce = 1,

ContainerInheritAce = 2,

NoPropagateInheritAce = 4,

InheritOnlyAce = 8,

InheritedAce = 16

}

[Flags]

private enum AceType : uint

{

AccessAllowed = 0,

AccessDenied = 1,

Audit = 2

}

/// <summary>

/// Allows the specified user to read and write the specified file.

/// </summary>

/// <param name="domain">The domain of the user</param>

/// <param name="username">The username of the user</param>

/// <param name="file">The file</param>

/// <param name="server">The server of the file. If it is placed on the
local machine use '.'</param>

/// <returns>true if the user is now allowed to read and write</returns>

public static bool AllowFileAccess(string domain, string username, string

file, string server, Mask accessMask)

{

ManagementObject lfs;

ManagementBaseObject descriptor; //The security descriptor

ManagementBaseObject[] dacl = FetchDACL(server, file, out lfs, out
descriptor);

if (dacl == null)

{

return false;

}

int aceIndex = GetACE(dacl, domain, username);

if (aceIndex == -1)

{

//ace does not exist -> create it, add it

ManagementBaseObject ace = CreateACE(domain, username, accessMask);

if (ace == null)

{

return false;

}

dacl = AddACE(dacl, ace);

if (!WriteDACL(dacl, lfs, descriptor))

{

return false;

}

return true;

}

else

{

//ace exists, modify it

ManagementBaseObject ace = dacl[aceIndex];

ModifyACE(ace, accessMask);

if (!WriteDACL(dacl, lfs, descriptor))

{

return false;

}

return true;

}

}

/// <summary>

/// Denies the specified user to read and write the specified file.

/// </summary>

/// <param name="domain">The domain of the user</param>

/// <param name="username">The username of the user</param>

/// <param name="file">The file</param>

/// <param name="server">The server of the file. If it is placed on the
local machine use '.'</param>

/// <returns>true if the user is now denied to read and write</returns>

public static bool DenyFileAccess(string domain, string username, string
file, string server)

{

ManagementObject lfs;

ManagementBaseObject descriptor;

ManagementBaseObject[] dacl = FetchDACL(server, file, out lfs, out
descriptor);

if (dacl == null)

{

return false;

}

int aceIndex = GetACE(dacl, domain, username);

if (aceIndex == -1)

{

//No entry exists, return

return true;

}

else

{

dacl = RemoveACE(dacl, aceIndex);

if (!WriteDACL(dacl, lfs, descriptor))

{

return false;

}

return true;

}

}

/// <summary>

/// </summary>

{

ManagementBaseObject ace;

{

ace = dacl[i];

//return the ace

return i;

}

return -1;

}

/// <summary>

/// </summary>

{

}

/// <summary>

/// </summary>

{

ManagementBaseObject trustee = null;

ManagementBaseObject ace = null;

// Initialize new Trustee

try

{

}

catch (Exception)

{

return null;

}

try

{

}

catch (Exception)

{

return null;

}


return ace;

}

/// <summary>

/// </summary>

{

inParams["Descriptor"] = descriptor;

return true;

else

return false;

}

/// <summary>

/// </summary>

{


dacl.CopyTo(newDacl, 0);

newDacl[newDacl.Length-1] = ace;

return newDacl;

}

/// <summary>

/// </summary>

{

int count = 0;

{

if (i==index) continue;

count++;

}

return newDacl;

}

/// <summary>

/// </summary>

{

// ManagementPath path = new
ManagementPath("\\\\S01\\root\\cimv2:Win32_LogicalDisk.DeviceID=\"D:\"");

// path.RelativePath =
@"Win32_LogicalFileSecuritySetting.Path='Daten\\_KnowledgeBase\\1\\2\\3.xls'";

lfs = null;

descriptor = null;

//The WMI Path of the file

try

{

ManagementPath path = new ManagementPath();

path.Server = server;

path.NamespacePath = @""; //for local machine @"root\cimv2" works, for
server not

//Replace backslashes by double blackslashes

file = file.Replace("\\", "\\\\");

path.RelativePath = @"Win32_LogicalFileSecuritySetting.Path='"+file+"'";

//Fetch object

lfs = new ManagementObject(path);


// Get the security descriptor for this object

// bool EnablePrivileges = lfs.Scope.Options.EnablePrivileges;

lfs.Scope.Options.EnablePrivileges = true;

ManagementBaseObject outParams = lfs.InvokeMethod("GetSecurityDescriptor",
null, null);

if (((uint)(outParams.Properties["ReturnValue"].Value)) != 0)

{

return null;

}

descriptor =
(ManagementBaseObject)(outParams.Properties["Descriptor"].Value);

//The DACL is an array of Win32_ACE objects.

ManagementBaseObject[] dacl =
(ManagementBaseObject[])(descriptor.Properties["Dacl"].Value);


//Remove all inherited aces

ArrayList nonInheritedAces = new ArrayList();

foreach(ManagementBaseObject ace in dacl)

{

if ((uint)ace["AceFlags"] == (uint)AceFlags.NonInheritAce)

{

nonInheritedAces.Add(ace);

}

}

dacl = new ManagementBaseObject[nonInheritedAces.Count];

nonInheritedAces.CopyTo(dacl);

return dacl;

}

catch (Exception)

{

return null;

}

}

}

}

--------------------------------------------------------------------------------------------------------------------------------------------------------------


Jeffrey Tan[MSFT]

unread,
Nov 14, 2005, 4:00:15 AM11/14/05
to
Hi jahrens,

Thanks for your detailed feedback.

Yes, with your further information, your problem appears much clear to me
now. Now, your current problem is the DACL programming on the files/folders
in .Net, yes?

To save the time, can you point out a little accurate that what the problem
with your code snippet? Which functions in the code snippet is the problem?
Thanks

Also, if you are not sure if the problem related with the impsonated
account, you can try this code snippet under and active admin account to
see if it works well.

Finally, which version of .Net you are using? .Net 2.0 has some build-in
support for ACL programming, and it is much easier than WMI, for more
information, please refer to:
"How To Program AC Ls "
http://pluralsight.com/wiki/default.aspx/Keith.GuideBook/HowToProgramACLs.ht
ml

Also, in .Net1.1, there are also some already writen libraries, see below:
"GotDotNet User Sample: ACLs in .NET "
http://www.gotdotnet.com/community/usersamples/details.aspx?sampleguid=e6098
575-dda0-48b8-9abf-e0705af065d9

Anyway, I will wait for your further feedback. Thanks

Jörgen Ahrens

unread,
Nov 14, 2005, 4:31:34 AM11/14/05
to
Hi Jeffrey

Yes, the current problem is the DACL programming on the files/folders.

Our Code fails with this call:


ManagementBaseObject outParams = lfs.InvokeMethod("GetSecurityDescriptor",
null, null);

Under an active admin account it works well.

thanks for your help
J. Ahrens


""Jeffrey Tan[MSFT]"" <v-j...@online.microsoft.com> wrote in message
news:Be21enP6...@TK2MSFTNGXA02.phx.gbl...

Jörgen Ahrens

unread,
Nov 14, 2005, 5:12:37 AM11/14/05
to
Hi Jeffrey

Its me again. We just tested it once again.

Our program works only if we map the shared folder to a drive( L:\) and if
we don't use impersonation and if we are logged in with the admin account.

if we do not map the shared folder it does not work.
if we use impersonation, it does not work even if we map the shared folder
and even if we run the impersonation under the admin account.
so it seems that using impersonation causes problems to access a mapped or
unmapped shared folder.

thanks for helping us.
J. Ahrens

our impersonation class

public class Impersonation

{

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

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);

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

public extern static bool DuplicateToken(IntPtr ExistingTokenHandle,

int SECURITY_IMPERSONATION_LEVEL, ref IntPtr DuplicateTokenHandle);

//The impersonation context is set by the function 'StartImpersonation'.

private WindowsImpersonationContext impersonatedUser = null;

//The windows identy is loaded when the function 'LoadProfile' is called.

private WindowsIdentity newWindowsIdentity = null;

/// <summary>

/// LoadProfile retrieves the tokenHandle for the specified user and
initialises the

/// windows identity. Note that this function only has to be called once.

/// You may call it multiple times to impersonate different users.

///

/// If any impersonation is currently running, it is stopped!

/// </summary>

/// <param name="domain">Domain of the user</param>

/// <param name="username">Username of the user</param>

/// <param name="password">Password of the user</param>

/// <returns>true if successful, false if not</returns>

public bool LoadProfile(string domain, string username, string password)

{

//Stop current impersonation

this.StopImpersonation();

//Token management

IntPtr tokenHandle = new IntPtr(0);

IntPtr duplicatedTokenHandle = new IntPtr(0);

tokenHandle = IntPtr.Zero;

duplicatedTokenHandle = IntPtr.Zero;

try

{

//Set logon parameters

const int LOGON32_PROVIDER_DEFAULT = 0;

//This parameter causes LogonUser to create a primary token.

const int LOGON32_LOGON_INTERACTIVE = 2;

const int SecurityImpersonation = 3;

//Call LogonUser to obtain a handle to an access token.

bool successful = LogonUser(username, domain, password,

LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT,

ref tokenHandle);


//return if Logon failed.

if (!successful)

{

return false;

}

//Duplicate the token.

successful = DuplicateToken(tokenHandle, SecurityImpersonation, ref
duplicatedTokenHandle);

//return if duplication failed.

if (!successful)

{

CloseHandle(tokenHandle);

return false;

}


// The token that is passed to the following constructor must

// be a primary token in order to use it for impersonation.

newWindowsIdentity = new WindowsIdentity(duplicatedTokenHandle);

// Free the tokens.

if (tokenHandle != IntPtr.Zero)

CloseHandle(tokenHandle);

if (duplicatedTokenHandle != IntPtr.Zero)

CloseHandle(duplicatedTokenHandle);

impersonatedUser = newWindowsIdentity.Impersonate();

//Success

return true;

}

catch(Exception)

{

// Free the tokens.

if (tokenHandle != IntPtr.Zero)

CloseHandle(tokenHandle);

if (duplicatedTokenHandle != IntPtr.Zero)

CloseHandle(duplicatedTokenHandle);

//Show message, return

return false;

}

}

}


Jeffrey Tan[MSFT]

unread,
Nov 14, 2005, 7:52:08 AM11/14/05
to
Hi jahrens,

Thanks for your feedback.

Yes, for local Admin issue, this is because
Win32_LogicalFileSecuritySetting does not accept UNC path, we may have to
use Win32_LogicalShareSecuritySetting to access UNC share path. This is
confirmed by Willy Denoyette [MVP] in the link below:
http://groups.google.com/group/microsoft.public.dotnet.languages.csharp/brow
se_thread/thread/82f104008c3e407c/e6949b24ff431688?lnk=st&q=Win32_LogicalFil
eSecuritySetting+path+shared&rnum=1&hl=zh-CN#e6949b24ff431688

So to use Win32_LogicalFileSecuritySetting , we have to do the driver
mapping first. For impersonate, why we can not access the local mapped
driver? I think this should be caused by the profile issue. When you are
doing driver mapping interactively, this is only done under your
interactive logon session profile, the impersonated logon session profile
did not have the mapped local driver. So we must do the driver mapping
programmatically. We can leverage net.exe to do the driver mapping like
this:
net use x: \\bobsmachine\c$ /p:yes /u:domain\superuser password01!

In .Net, after doing the impersonate, you can use the Process.Start to run
the above command, the same code in my first reply. If this still does not
work, please feel free to tell me, thanks

Jörgen Ahrens

unread,
Nov 14, 2005, 9:42:01 AM11/14/05
to
THANK YOU JEFFREY!!

it works!
but we had to use the NetUseAdd API to successfully connect to the shared
folder.
using the Process.start() did not run under the impersonated account and
mapped the drive to the normal user.

i hope we will succeed now. if not we will post again;-)

thanks again and all the best
J. Ahrens


""Jeffrey Tan[MSFT]"" <v-j...@online.microsoft.com> wrote in message

news:HDENDpR...@TK2MSFTNGXA02.phx.gbl...

Jeffrey Tan[MSFT]

unread,
Nov 15, 2005, 3:51:36 AM11/15/05
to
Hi jahrens,

I am glad my reply can help you.

Oh, yes, the new created process will inherit the security context from the
calling process's account, not the impersonated thread's account,
programmatically doing this should be a correct way. If you have problem to
implement this, please feel free to tell me, thanks

Jörgen Ahrens

unread,
Nov 17, 2005, 8:32:54 AM11/17/05
to
Hi Jeffrey

It's me again.

I have another problem...

I use the impersonation feature of .net. But i just noticed that this
doesn't work for windows 2000...

So i went looking for a impersonation that also works on 2000 and found the
code attached on that mail.

The impersonation of a user works fine. And on the local computer everything
is accessible. But when i then try to connect to a shared server
folder(Windows 2003 or Windows 2000). I get file not found exception or path
not found exception.

Do you have any idea what it could be?
I'm quite in a hurry and would be very happy if you could answer fast.

I also posted this in the microsoft.public.dotnet.security Forum.

Thanks a lot for your help

J.Ahrens

---------------------Code----------------------------------

Imports System.Runtime.InteropServices


Public Class SSPIImpersonation


Private Const HEAP_ZERO_MEMORY As Integer = &H8


Private Const SEC_WINNT_AUTH_IDENTITY_ANSI As Integer = &H1


Private Const SECBUFFER_TOKEN As Integer = &H2


Private Const SECURITY_NATIVE_DREP As Integer = &H10


Private Const SECPKG_CRED_INBOUND As Integer = &H1
Private Const SECPKG_CRED_OUTBOUND As Integer = &H2


Private Const SEC_I_CONTINUE_NEEDED As Integer = &H90312
Private Const SEC_I_COMPLETE_NEEDED As Integer = &H90313
Private Const SEC_I_COMPLETE_AND_CONTINUE As Integer = &H90314


Private Const VER_PLATFORM_WIN32_NT As Integer = &H2


Private intClientBuf As Integer
Private intServerBuf As Integer
Private asClient As AUTH_SEQ = Nothing
Private asServer As AUTH_SEQ = Nothing


Private Structure SecPkgInfo
Dim fCapabilities As Integer
Dim wVersion As Short
Dim wRPCID As Short
Dim cbMaxToken As Integer
Dim Name As Integer
Dim Comment As Integer
End Structure


Private Structure SecHandle
Dim dwLower As Integer
Dim dwUpper As Integer
End Structure


Private Structure AUTH_SEQ
Dim fInitialized As Boolean
Dim fHaveCredHandle As Boolean
Dim fHaveCtxtHandle As Boolean
Dim hcred As SecHandle
Dim hctxt As SecHandle
End Structure


Private Structure SEC_WINNT_AUTH_IDENTITY
Dim User As String
Dim UserLength As Integer
Dim Domain As String
Dim DomainLength As Integer
Dim Password As String
Dim PasswordLength As Integer
Dim Flags As Integer
End Structure


Private Structure TimeStamp
Dim LowPart As Integer
Dim HighPart As Integer
End Structure


Private Structure SecBuffer
Dim cbBuffer As Integer
Dim BufferType As Integer
Dim pvBuffer As Integer
End Structure


Private Structure SecBufferDesc
Dim ulVersion As Integer
Dim cBuffers As Integer
Dim pBuffers As Integer
End Structure


Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory"
(ByVal Destination As Integer, ByRef Source As SecBuffer, ByVal Length As
Integer)
Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory"
(ByRef Destination As SecBuffer, ByVal Source As Integer, ByVal Length As
Integer)
Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory"
(ByRef Destination As SecPkgInfo, ByVal Source As Integer, ByVal Length As
Integer)


Private Declare Function NT4QuerySecurityPackageInfo Lib "security"
Alias "QuerySecurityPackageInfoA" (ByVal PackageName As String, ByRef
pPackageInfo As Integer) As Integer
Private Declare Function QuerySecurityPackageInfo Lib "secur32" Alias
"QuerySecurityPackageInfoA" (ByVal PackageName As String, ByRef pPackageInfo
As Integer) As Integer


Private Declare Function NT4FreeContextBuffer Lib "security" Alias
"FreeContextBuffer" (ByVal pvContextBuffer As Integer) As Integer
Private Declare Function FreeContextBuffer Lib "secur32" (ByVal
pvContextBuffer As Integer) As Integer


Private Declare Function NT4InitializeSecurityContext Lib "security"
Alias "InitializeSecurityContextA" _
(ByRef phCredential As SecHandle, ByRef phContext As SecHandle, _
ByVal pszTargetName As Integer, ByVal fContextReq As Integer, _
ByVal Reserved1 As Integer, ByVal TargetDataRep As Integer, _
ByRef pInput As SecBufferDesc, ByVal Reserved2 As Integer, _
ByRef phNewContext As SecHandle, ByRef pOutput As SecBufferDesc, _
ByRef pfContextAttr As Integer, ByRef ptsExpiry As TimeStamp) As Integer


Private Declare Function InitializeSecurityContext Lib "secur32" _
Alias "InitializeSecurityContextA" _
(ByRef phCredential As SecHandle, ByRef phContext As SecHandle, _
ByVal pszTargetName As Integer, ByVal fContextReq As Integer, _
ByVal Reserved1 As Integer, ByVal TargetDataRep As Integer, _
ByRef pInput As SecBufferDesc, ByVal Reserved2 As Integer, _
ByRef phNewContext As SecHandle, ByRef pOutput As SecBufferDesc, _
ByRef pfContextAttr As Integer, ByRef ptsExpiry As TimeStamp) As Integer


Private Declare Function NT4InitializeSecurityContext2 Lib "security"
Alias "InitializeSecurityContextA" _
(ByRef phCredential As SecHandle, ByVal phContext As Integer, _
ByVal pszTargetName As Integer, ByVal fContextReq As Integer, _
ByVal Reserved1 As Integer, ByVal TargetDataRep As Integer, _
ByVal pInput As Integer, ByVal Reserved2 As Integer, _
ByRef phNewContext As SecHandle, ByRef pOutput As SecBufferDesc, _
ByRef pfContextAttr As Integer, ByRef ptsExpiry As TimeStamp) As Integer


Private Declare Function InitializeSecurityContext2 Lib "secur32" Alias
"InitializeSecurityContextA" _
(ByRef phCredential As SecHandle, ByVal phContext As Integer, _
ByVal pszTargetName As Integer, ByVal fContextReq As Integer, _
ByVal Reserved1 As Integer, ByVal TargetDataRep As Integer, _
ByVal pInput As Integer, ByVal Reserved2 As Integer, _
ByRef phNewContext As SecHandle, ByRef pOutput As SecBufferDesc, _
ByRef pfContextAttr As Integer, ByRef ptsExpiry As TimeStamp) As Integer


Private Declare Function NT4AcquireCredentialsHandle Lib "security"
Alias "AcquireCredentialsHandleA" _
(ByVal pszPrincipal As Integer, _
ByVal pszPackage As String, ByVal fCredentialUse As Integer, _
ByVal pvLogonId As Integer, _
ByRef pAuthData As SEC_WINNT_AUTH_IDENTITY, _
ByVal pGetKeyFn As Integer, ByVal pvGetKeyArgument As Integer, _
ByRef phCredential As SecHandle, ByRef ptsExpiry As TimeStamp) As
Integer


Private Declare Function AcquireCredentialsHandle Lib "secur32" Alias
"AcquireCredentialsHandleA" _
(ByVal pszPrincipal As Integer, _
ByVal pszPackage As String, ByVal fCredentialUse As Integer, _
ByVal pvLogonId As Integer, _
ByRef pAuthData As SEC_WINNT_AUTH_IDENTITY, _
ByVal pGetKeyFn As Integer, ByVal pvGetKeyArgument As Integer, _
ByRef phCredential As SecHandle, ByRef ptsExpiry As TimeStamp) As
Integer


Private Declare Function NT4AcquireCredentialsHandle2 Lib "security"
Alias "AcquireCredentialsHandleA" _
(ByVal pszPrincipal As Integer, _
ByVal pszPackage As String, ByVal fCredentialUse As Integer, _
ByVal pvLogonId As Integer, ByVal pAuthData As Integer, _
ByVal pGetKeyFn As Integer, ByVal pvGetKeyArgument As Integer, _
ByRef phCredential As SecHandle, ByRef ptsExpiry As TimeStamp) As
Integer


Private Declare Function AcquireCredentialsHandle2 Lib "secur32" Alias
"AcquireCredentialsHandleA" _
(ByVal pszPrincipal As Integer, _
ByVal pszPackage As String, ByVal fCredentialUse As Integer, _
ByVal pvLogonId As Integer, ByVal pAuthData As Integer, _
ByVal pGetKeyFn As Integer, ByVal pvGetKeyArgument As Integer, _
ByRef phCredential As SecHandle, ByRef ptsExpiry As TimeStamp) As
Integer


Private Declare Function NT4AcceptSecurityContext Lib "security" Alias
"AcceptSecurityContext" _
(ByRef phCredential As SecHandle, _
ByRef phContext As SecHandle, ByRef pInput As SecBufferDesc, _
ByVal fContextReq As Integer, ByVal TargetDataRep As Integer, _
ByRef phNewContext As SecHandle, ByRef pOutput As SecBufferDesc, _
ByRef pfContextAttr As Integer, ByRef ptsExpiry As TimeStamp) As Integer


Private Declare Function AcceptSecurityContext Lib "secur32" _
(ByRef phCredential As SecHandle, _
ByRef phContext As SecHandle, ByRef pInput As SecBufferDesc, _
ByVal fContextReq As Integer, ByVal TargetDataRep As Integer, _
ByRef phNewContext As SecHandle, ByRef pOutput As SecBufferDesc, _
ByRef pfContextAttr As Integer, ByRef ptsExpiry As TimeStamp) As Integer


Private Declare Function NT4ImpersonateSecurityContext Lib "security"
Alias "ImpersonateSecurityContext" _
(ByRef phContext As SecHandle) As Integer


Private Declare Function ImpersonateSecurityContext Lib "secur32" _
(ByRef phContext As SecHandle) As Integer


Private Declare Function NT4RevertSecurityContext Lib "security" Alias
"RevertSecurityContext" _
(ByRef phContext As SecHandle) As Integer


Private Declare Function RevertSecurityContext Lib "secur32" _
(ByRef phContext As SecHandle) As Integer


Private Declare Function NT4AcceptSecurityContext2 Lib "security" Alias
"AcceptSecurityContext" _
(ByRef phCredential As SecHandle, _
ByVal phContext As Integer, ByRef pInput As SecBufferDesc, _
ByVal fContextReq As Integer, ByVal TargetDataRep As Integer, _
ByRef phNewContext As SecHandle, ByRef pOutput As SecBufferDesc, _
ByRef pfContextAttr As Integer, ByRef ptsExpiry As TimeStamp) As Integer


Private Declare Function AcceptSecurityContext2 Lib "secur32" Alias
"AcceptSecurityContext" _
(ByRef phCredential As SecHandle, _
ByVal phContext As Integer, ByRef pInput As SecBufferDesc, _
ByVal fContextReq As Integer, ByVal TargetDataRep As Integer, _
ByRef phNewContext As SecHandle, ByRef pOutput As SecBufferDesc, _
ByRef pfContextAttr As Integer, ByRef ptsExpiry As TimeStamp) As Integer


Private Declare Function NT4CompleteAuthToken Lib "security" Alias
"CompleteAuthToken" _
(ByRef phContext As SecHandle, _
ByRef pToken As SecBufferDesc) As Integer
Private Declare Function CompleteAuthToken Lib "secur32" _
(ByRef phContext As SecHandle, _
ByRef pToken As SecBufferDesc) As Integer


Private Declare Function NT4DeleteSecurityContext Lib "security" _
Alias "DeleteSecurityContext" (ByRef phContext As SecHandle) _
As Integer
Private Declare Function DeleteSecurityContext Lib "secur32" _
(ByRef phContext As SecHandle) _
As Integer


Private Declare Function NT4FreeCredentialsHandle Lib "security" _
Alias "FreeCredentialsHandle" (ByRef phContext As SecHandle) _
As Integer
Private Declare Function FreeCredentialsHandle Lib "secur32" _
(ByRef phContext As SecHandle) _
As Integer


Private Declare Function GetProcessHeap Lib "kernel32" () As Integer


Private Declare Function HeapAlloc Lib "kernel32" _
(ByVal hHeap As Integer, ByVal dwFlags As Integer, _
ByVal dwBytes As Integer) As Integer


Private Declare Function HeapFree Lib "kernel32" (ByVal hHeap As
Integer, _
ByVal dwFlags As Integer, ByVal lpMem As Integer) As Integer


Dim g_NT4 As Boolean

Public Sub New(ByVal accountname As String, ByVal password As String,
ByVal domain As String)


If SSPValidateUser(accountname, domain, password) Then
Console.Write("User Credential are valid" + vbCrLf)
Else
Console.Write("User Credential couldn't be validated" + vbCrLf)
End If
End Sub


Public Overridable Sub Dispose()
' Clean up resources
If asClient.fHaveCtxtHandle Then
If g_NT4 Then
NT4DeleteSecurityContext(asClient.hctxt)
Else
DeleteSecurityContext(asClient.hctxt)
End If
End If


If asClient.fHaveCredHandle Then
If g_NT4 Then
NT4FreeCredentialsHandle(asClient.hcred)
Else
FreeCredentialsHandle(asClient.hcred)
End If
End If


If asServer.fHaveCtxtHandle Then
If g_NT4 Then
NT4DeleteSecurityContext(asServer.hctxt)
Else
DeleteSecurityContext(asServer.hctxt)
End If
End If


If asServer.fHaveCredHandle Then
If g_NT4 Then
NT4FreeCredentialsHandle(asServer.hcred)
Else
FreeCredentialsHandle(asServer.hcred)
End If
End If


If intClientBuf <> 0 Then
HeapFree(GetProcessHeap(), 0, intClientBuf)
End If


If intServerBuf <> 0 Then
HeapFree(GetProcessHeap(), 0, intServerBuf)
End If
End Sub


Private Function GenClientContext(ByRef AuthSeq As AUTH_SEQ, _
ByRef AuthIdentity As SEC_WINNT_AUTH_IDENTITY, _
ByVal pIn As Integer, ByVal cbIn As Integer, _
ByVal pOut As Integer, ByRef cbOut As Integer, _
ByRef fDone As Boolean) As Boolean


Dim ss As Integer
Dim tsExpiry As TimeStamp
Dim sbdOut As SecBufferDesc
Dim sbOut As SecBuffer
Dim sbdIn As SecBufferDesc
Dim sbIn As SecBuffer
Dim fContextAttr As Integer


GenClientContext = False


If Not AuthSeq.fInitialized Then
If g_NT4 Then
ss = NT4AcquireCredentialsHandle(0&, "NTLM", _
SECPKG_CRED_OUTBOUND, 0&, AuthIdentity, 0&, 0&, _
AuthSeq.hcred, tsExpiry)
Else
ss = AcquireCredentialsHandle(0&, "NTLM", _
SECPKG_CRED_OUTBOUND, 0&, AuthIdentity, 0&, 0&, _
AuthSeq.hcred, tsExpiry)
End If


If ss < 0 Then
Exit Function
End If


AuthSeq.fHaveCredHandle = True
End If


' Prepare output buffer
sbdOut.ulVersion = 0
sbdOut.cBuffers = 1
sbdOut.pBuffers = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
Marshal.SizeOf(sbOut))


sbOut.cbBuffer = cbOut
sbOut.BufferType = SECBUFFER_TOKEN
sbOut.pvBuffer = pOut


CopyMemory(sbdOut.pBuffers, sbOut, Marshal.SizeOf(sbOut))


' Prepare input buffer
If AuthSeq.fInitialized Then
sbdIn.ulVersion = 0
sbdIn.cBuffers = 1
sbdIn.pBuffers = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
Marshal.SizeOf(sbIn))


sbIn.cbBuffer = cbIn
sbIn.BufferType = SECBUFFER_TOKEN
sbIn.pvBuffer = pIn


CopyMemory(sbdIn.pBuffers, sbIn, Marshal.SizeOf(sbIn))
End If


If AuthSeq.fInitialized Then
If g_NT4 Then
ss = NT4InitializeSecurityContext(AuthSeq.hcred, _
AuthSeq.hctxt, 0&, 0, 0, SECURITY_NATIVE_DREP, sbdIn, _
0, AuthSeq.hctxt, sbdOut, fContextAttr, tsExpiry)
Else
ss = InitializeSecurityContext(AuthSeq.hcred, _
AuthSeq.hctxt, 0&, 0, 0, SECURITY_NATIVE_DREP, sbdIn, _
0, AuthSeq.hctxt, sbdOut, fContextAttr, tsExpiry)
End If
Else
If g_NT4 Then
ss = NT4InitializeSecurityContext2(AuthSeq.hcred, 0&, 0&, _
0, 0, SECURITY_NATIVE_DREP, 0&, 0, AuthSeq.hctxt, _
sbdOut, fContextAttr, tsExpiry)
Else
ss = InitializeSecurityContext2(AuthSeq.hcred, 0&, 0&, _
0, 0, SECURITY_NATIVE_DREP, 0&, 0, AuthSeq.hctxt, _
sbdOut, fContextAttr, tsExpiry)
End If
End If


If ss < 0 Then
GoTo FreeResourcesAndExit
End If


AuthSeq.fHaveCtxtHandle = True


' If necessary, complete token
If ss = SEC_I_COMPLETE_NEEDED Or ss = SEC_I_COMPLETE_AND_CONTINUE
Then
If g_NT4 Then
ss = NT4CompleteAuthToken(AuthSeq.hctxt, sbdOut)
Else
ss = CompleteAuthToken(AuthSeq.hctxt, sbdOut)
End If


If ss < 0 Then
GoTo FreeResourcesAndExit
End If
End If


CopyMemory(sbOut, sbdOut.pBuffers, Marshal.SizeOf(sbOut))
cbOut = sbOut.cbBuffer


If Not AuthSeq.fInitialized Then
AuthSeq.fInitialized = True
End If


fDone = Not (ss = SEC_I_CONTINUE_NEEDED Or ss =
SEC_I_COMPLETE_AND_CONTINUE)


GenClientContext = True


FreeResourcesAndExit:


If sbdOut.pBuffers <> 0 Then
HeapFree(GetProcessHeap(), 0, sbdOut.pBuffers)
End If


If sbdIn.pBuffers <> 0 Then
HeapFree(GetProcessHeap(), 0, sbdIn.pBuffers)
End If
End Function


Private Function GenServerContext(ByRef AuthSeq As AUTH_SEQ, _
ByVal pIn As Integer, ByVal cbIn As Integer, _
ByVal pOut As Integer, ByRef cbOut As Integer, _
ByRef fDone As Boolean) As Boolean


Dim ss As Integer
Dim tsExpiry As TimeStamp
Dim sbdOut As SecBufferDesc
Dim sbOut As SecBuffer
Dim sbdIn As SecBufferDesc
Dim sbIn As SecBuffer
Dim fContextAttr As Integer


GenServerContext = False


If Not AuthSeq.fInitialized Then
If g_NT4 Then
ss = NT4AcquireCredentialsHandle2(0&, "NTLM", _
SECPKG_CRED_INBOUND, 0&, 0&, 0&, 0&, AuthSeq.hcred, _
tsExpiry)
Else
ss = AcquireCredentialsHandle2(0&, "NTLM", _
SECPKG_CRED_INBOUND, 0&, 0&, 0&, 0&, AuthSeq.hcred, _
tsExpiry)
End If


If ss < 0 Then
Exit Function
End If


AuthSeq.fHaveCredHandle = True
End If


' Prepare output buffer
sbdOut.ulVersion = 0
sbdOut.cBuffers = 1
sbdOut.pBuffers = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
Marshal.SizeOf(sbOut))


sbOut.cbBuffer = cbOut
sbOut.BufferType = SECBUFFER_TOKEN
sbOut.pvBuffer = pOut


CopyMemory(sbdOut.pBuffers, sbOut, Marshal.SizeOf(sbOut))


' Prepare input buffer
sbdIn.ulVersion = 0
sbdIn.cBuffers = 1
sbdIn.pBuffers = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
Marshal.SizeOf(sbIn))


sbIn.cbBuffer = cbIn
sbIn.BufferType = SECBUFFER_TOKEN
sbIn.pvBuffer = pIn


CopyMemory(sbdIn.pBuffers, sbIn, Marshal.SizeOf(sbIn))


If AuthSeq.fInitialized Then
If g_NT4 Then
ss = NT4AcceptSecurityContext(AuthSeq.hcred, AuthSeq.hctxt,
_
sbdIn, 0, SECURITY_NATIVE_DREP, AuthSeq.hctxt, sbdOut, _
fContextAttr, tsExpiry)
Else
ss = AcceptSecurityContext(AuthSeq.hcred, AuthSeq.hctxt, _
sbdIn, 0&, SECURITY_NATIVE_DREP, AuthSeq.hctxt, sbdOut, _
fContextAttr, tsExpiry)
End If
Else
If g_NT4 Then
ss = NT4AcceptSecurityContext2(AuthSeq.hcred, 0&, sbdIn, 0,
_
SECURITY_NATIVE_DREP, AuthSeq.hctxt, sbdOut, _
fContextAttr, tsExpiry)
Else
ss = AcceptSecurityContext2(AuthSeq.hcred, 0&, sbdIn, 0, _
SECURITY_NATIVE_DREP, AuthSeq.hctxt, sbdOut, _
fContextAttr, tsExpiry)
End If
End If


If ss < 0 Then
GoTo FreeResourcesAndExit
End If


AuthSeq.fHaveCtxtHandle = True


' If necessary, complete token
If ss = SEC_I_COMPLETE_NEEDED Or ss = SEC_I_COMPLETE_AND_CONTINUE
Then
If g_NT4 Then
ss = NT4CompleteAuthToken(AuthSeq.hctxt, sbdOut)
Else
ss = CompleteAuthToken(AuthSeq.hctxt, sbdOut)
End If


If ss < 0 Then
GoTo FreeResourcesAndExit
End If
End If


CopyMemory(sbOut, sbdOut.pBuffers, Marshal.SizeOf(sbOut))
cbOut = sbOut.cbBuffer


If Not AuthSeq.fInitialized Then
AuthSeq.fInitialized = True
End If


fDone = Not (ss = SEC_I_CONTINUE_NEEDED Or ss =
SEC_I_COMPLETE_AND_CONTINUE)


GenServerContext = True


FreeResourcesAndExit:


If sbdOut.pBuffers <> 0 Then
HeapFree(GetProcessHeap(), 0, sbdOut.pBuffers)
End If


If sbdIn.pBuffers <> 0 Then
HeapFree(GetProcessHeap(), 0, sbdIn.pBuffers)
End If


End Function


Private Function SSPValidateUser(ByVal User As String, ByVal Domain As
String, ByVal Password As String) As Boolean
Dim pSPI As Integer
Dim SPI As SecPkgInfo
Dim cbMaxToken As Integer


Dim ai As SEC_WINNT_AUTH_IDENTITY


asClient = Nothing
asServer = Nothing
intClientBuf = 0
intServerBuf = 0


Dim cbIn As Integer
Dim cbOut As Integer
Dim fDone As Boolean


SSPValidateUser = False


' Determine if system is Windows NT (version 4.0 or earlier)
g_NT4 = (System.Environment.OSVersion.Platform =
VER_PLATFORM_WIN32_NT And System.Environment.OSVersion.Version.Major <= 4)


' Get max token size
If g_NT4 Then
NT4QuerySecurityPackageInfo("NTLM", pSPI)
Else
QuerySecurityPackageInfo("NTLM", pSPI)
End If


CopyMemory(SPI, pSPI, Marshal.SizeOf(SPI))
cbMaxToken = SPI.cbMaxToken


If g_NT4 Then
NT4FreeContextBuffer(pSPI)
Else
FreeContextBuffer(pSPI)
End If


' Allocate buffers for client and server messages
intClientBuf = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
cbMaxToken)
If intClientBuf = 0 Then
GoTo FreeResourcesAndExit
End If


intServerBuf = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
cbMaxToken)
If intServerBuf = 0 Then
GoTo FreeResourcesAndExit
End If


' Initialize auth identity structure
ai.Domain = Domain
ai.DomainLength = Domain.Length
ai.User = User
ai.UserLength = User.Length
ai.Password = Password
ai.PasswordLength = Password.Length
ai.Flags = SEC_WINNT_AUTH_IDENTITY_ANSI


' Prepare client message (negotiate) .
cbOut = cbMaxToken
If Not GenClientContext(asClient, ai, 0, 0, intClientBuf, cbOut,
fDone) Then
GoTo FreeResourcesAndExit
End If


' Prepare server message (challenge) .
cbIn = cbOut
cbOut = cbMaxToken
If Not GenServerContext(asServer, intClientBuf, cbIn, intServerBuf,
cbOut, fDone) Then
GoTo FreeResourcesAndExit
End If


' Prepare client message (authenticate) .
cbIn = cbOut
cbOut = cbMaxToken
If Not GenClientContext(asClient, ai, intServerBuf, cbIn,
intClientBuf, cbOut, fDone) Then
GoTo FreeResourcesAndExit
End If


' Prepare server message (authentication) .
cbIn = cbOut
cbOut = cbMaxToken
If Not GenServerContext(asServer, intClientBuf, cbIn, intServerBuf,
cbOut, fDone) Then
GoTo FreeResourcesAndExit
End If


SSPValidateUser = True

FreeResourcesAndExit:
'Code moved to destructor
End Function


' Impersonates the actual user by the user
' given in the constuctor. Use the function RevertUser
' to log off the impersonated user and use the actual user again.
' Returns SEC_E_OK, if no error occured, SEC_E_INVALID_HANDLE, if
' no impersonation could take place.
Function ImpersonateUser() As Integer
Dim ss As Integer

If g_NT4 Then
ss = NT4ImpersonateSecurityContext(asServer.hctxt)
Else
ss = ImpersonateSecurityContext(asServer.hctxt)
End If

ImpersonateUser = ss
End Function


' Logs off the impersonated user and uses the actual user again.
' Use the function ImpersonateUser for impersonating.
' Returns SEC_E_OK, if no error occured, SEC_E_INVALID_HANDLE, if
' no revertion could take place.
Public Function RevertUser() As Integer
Dim ss As Integer

If g_NT4 Then
ss = NT4RevertSecurityContext(asServer.hctxt)
Else
ss = RevertSecurityContext(asServer.hctxt)
End If

RevertUser = ss
End Function


End Class


Jeffrey Tan[MSFT]

unread,
Nov 18, 2005, 6:17:02 AM11/18/05
to
Hi jahrens,

For impersonation does not work on Win2000, I think this is because of the
requirement of LogonUser API. On Win2000, calling LogonUser API requires
SeTcbPrivilige, which is by default only given to SYSTEM account.

So, you should first assign SeTcbPrivilige to your current interactive user
account, then the impersonation will succeed without any problem.

To assign SeTcbPrivilige to certain user account, we have 2 ways:
1. Directly go to Administrative Tools-> Local Security Policy, then
navigate to "Local Policies"->"Local Rights Assignment". In right panel,
click "Act as part of the operating system Properties" and add your current
user account. At last, you must log-off and re-login to let this setting
take effect.
2. Use LSA API to do this programmatically, however, this still requires
log-off and re-login to let this setting take effect, code listed below:
#ifndef UNICODE
#define UNICODE
#endif

#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <Ntsecapi.h>

#ifndef STATUS_SUCCESS
#define STATUS_SUCCESS ((NTSTATUS)0x00000000L)
#endif

#ifdef _DEBUG
#define JASSERT(x) printf x
#else
#define JASSERT(x)
#endif

#define RET_OK 1
#define RET_ERR 2
#define RET_UKN 0

#define MAX_NAME_LEN 200
//////////////////////////////////////////////////////////////////////////

void
InitUnicodeString( OUT PLSA_UNICODE_STRING LsaUnicodeString, IN LPWSTR
pwszString)
{
memset(LsaUnicodeString, 0, sizeof(LsaUnicodeString));

LsaUnicodeString->Length = wcslen(pwszString) * sizeof(WCHAR);
LsaUnicodeString->MaximumLength = LsaUnicodeString->Length +
sizeof(UNICODE_NULL);
LsaUnicodeString->Buffer = pwszString;
}

//////////////////////////////////////////////////////////////////////////

LSA_HANDLE
WINAPI
OpenPolicy(DWORD DesiredAccess, LPWSTR ServerName)
{
LSA_HANDLE PolicyHandle = NULL;
LSA_OBJECT_ATTRIBUTES ObjectAttributes;
LSA_UNICODE_STRING ServerNameString;
NTSTATUS Status;

memset(&ObjectAttributes, 0, sizeof(LSA_OBJECT_ATTRIBUTES));

InitUnicodeString(&ServerNameString, ServerName);

Status = LsaOpenPolicy( &ServerNameString,
&ObjectAttributes,
DesiredAccess,
&PolicyHandle);
if(Status != STATUS_SUCCESS)
{
JASSERT(("OpenPolicy->LsaOpenPolicy error %X\n", Status));
}
return PolicyHandle;
}

//////////////////////////////////////////////////////////////////////////

BOOL
WINAPI
GetAccountSid(LPTSTR AccountName,
PSID *Sid)
{
PSID pSID = NULL;
DWORD cbSid = 0;
LPTSTR DomainName = NULL;
DWORD cbDomainName = 0;
SID_NAME_USE SIDNameUse;
BOOL bDone = FALSE;

__try
{
if(!LookupAccountName(NULL, // we only concertrate local machine.
AccountName,
pSID,
&cbSid,
DomainName,
&cbDomainName,
&SIDNameUse))
{
pSID = (PSID)malloc(cbSid);
DomainName = (LPTSTR)malloc(cbDomainName * sizeof(TCHAR));
if(!pSID || !DomainName)
{
JASSERT(("Not enough memory! failed...\n"));
__leave;
}
if(!LookupAccountName(NULL,
AccountName,
pSID,
&cbSid,
DomainName,
&cbDomainName,
&SIDNameUse))
{
JASSERT(("Not enough memory! failed...\n"));
__leave;
}
bDone = TRUE;
}
}
__finally
{
if(DomainName)
free(DomainName);
if(!bDone && pSID)
free(pSID);
}
if(bDone)
*Sid = pSID;

return bDone;
}

//////////////////////////////////////////////////////////////////////////

BOOL
WINAPI
AdjustUserPolicy(LSA_HANDLE PolicyHandle,
PSID pAccountSid,
LPWSTR lpwSePrivilege,
BOOL fRemove)
{
LSA_UNICODE_STRING SePrivilegeString;
NTSTATUS Status;

InitUnicodeString(&SePrivilegeString, lpwSePrivilege);

if(fRemove)
{
Status = LsaRemoveAccountRights(PolicyHandle,
pAccountSid,
FALSE,
&SePrivilegeString,
1);
}
else
{
Status = LsaAddAccountRights(PolicyHandle,
pAccountSid,
&SePrivilegeString,
1);
}
return (Status == STATUS_SUCCESS);
}

//////////////////////////////////////////////////////////////////////////

int main(int argc, char** argv)
{
LSA_HANDLE PolicyHandle = NULL;
PSID pSid = NULL;
DWORD cbAccountName = MAX_NAME_LEN;
TCHAR AccountName[MAX_NAME_LEN];
TCHAR wComputerName[] = L"";
int nRet = RET_ERR;

__try
{
if(!GetUserName(AccountName, &cbAccountName))
{
printf("ft..... How long are your name?\n");
__leave;
}

PolicyHandle = OpenPolicy(POLICY_CREATE_ACCOUNT | POLICY_LOOKUP_NAMES,
wComputerName);
if(!PolicyHandle)
{
printf("OpenPolicy return NULL\n");
__leave;
}

if(!GetAccountSid(AccountName, &pSid))
{
printf("GetAccountSid return false\n");
__leave;
}

if(!AdjustUserPolicy(PolicyHandle, pSid, SE_TCB_NAME, FALSE))
{
printf("AdjustUserPolicy return false\n");
__leave;
}
getchar();
nRet = RET_OK;
}
__finally
{
if(pSid)
free(pSid);
if(PolicyHandle)
LsaClose(PolicyHandle);
}

return nRet;
}

After enabling SeTcbPrivilige, we should can do impersonation without any
problem, my impersonation code listed below:
HANDLE hTok;
BOOL fResult=LogonUser(TEXT("SecurityTest"), TEXT("."),
TEXT("Password01!"),
LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, &hTok);
if(!fResult)
{
printf("LogonUser failed with %d", GetLastError());
return -1;
}

fResult=ImpersonateLoggedOnUser(hTok);
if(!fResult)
{
printf("ImpersonateLoggedOnUser failed with %d", GetLastError());
return -1;
}

fResult=CreateDirectory(TEXT("C:\\Security"), NULL);
if(!fResult)
{
printf("CreateDirectory failed with %d", GetLastError());
return -1;
}

fResult=RevertToSelf();
if(!fResult)
{
printf("RevertToSelf failed with %d", GetLastError());
return -1;
}
CloseHandle(hTok);

This works well on my test Win2k VPC. Hope it helps

Jeffrey Tan[MSFT]

unread,
Nov 23, 2005, 12:17:19 AM11/23/05
to
Hi jahrens,

Have you tried my suggestion of turning on the SeTcbPrivilege? Is your
problem resolved? Please feel free to tell me, thanks

Reply all
Reply to author
Forward
0 new messages