tags:

views:

306

answers:

2

This has to be obtained from a remote machine. The following query works not for SIDs, but for group and account names.

"SELECT GroupComponent FROM Win32_GroupUser WHERE PartComponent = \"Win32_UserAccount.Domain='" + accountDomain + "',Name='" + accountName + "'\""

The Win32_Group objects it returns come in the forms of strings, and they only have domain and name (even though Win32_Group has a SID property).

I have this sinking feeling I'll have to:

  1. Turn the SID into an account name by querying Win32_SID;
  2. Perform the query above;
  3. Turn each of the resulting group names into SIDs by querying Win32_Group.
+2  A: 

Can you use the System.DirectoryServices.AccountManagement namespace classes?

using (var context = new PrincipalContext( ContextType.Domain ))
{
    using (var user = UserPrincipal.FindByIdentity( context, accountName ))
    {
        var groups = user.GetAuthorizationGroups();
        ...iterate through groups and find SIDs for each one
    }
}

It should work with ContextType.Machine, though you'd need to specify the machine name and have appropriate privileges.

using (var context = new PrincipalContext( ContextType.Machine,
                                           "MyComputer",
                                           userid,
                                           password ))
{
   ...
}

There's a nice MSDN article (longish, though) on using the new .NET 3.5 account management namespace.

tvanfosson
I need to be able to do this with local users and groups too.
JCCyC
ContextType.Machine should enumerate users and groups local to the machine specified by name when creating the PrincipalContext.
tvanfosson
I imagine, if I connect to \\MACHINENAME with WNetUseConnection() from mpr.dll with the proper remote credentials, I should be able to get the info even if I and the remote machine are not part of the same domain, right?
JCCyC
I believe that there is a constructor for PrincipalContext that allows you to specify the credentials (id/password) to use when connecting: http://msdn.microsoft.com/en-us/library/bb341016.aspx
tvanfosson
Got it to work. Now I wish there was a Principal.FindBySID() method.
JCCyC
No, wait... there is! Principal.FindByIdentity(ctx, IdentityType.Sid ,sid)
JCCyC
+2  A: 

Yes there is but some methods depend on having a domain.

  1. See this page for how to convert a SID to a user id using P/Invoke and the Windows API, or with .NET 2.0+ and no P/Invoke.

    using System.Security.Principal;

    // convert the user sid to a domain\name string account = new SecurityIdentifier(stringSid).Translate(typeof(NTAccount)).ToString();

  2. If you have AD and the user id in there then use the DirectorySearcher method or Account Management APIs to find the groups. Otherwise use the method outlined in this article to get local groups.

  3. Now use the API suggested by @tvanfosson to iterate the groups and get the SIDs. Or follow the info below.

In an ASP.NET application it is possible to use code like this to access group info provided a user is authenticated by Windows and not Forms authentication. In this example I've left an interesting note about exceptions that are thrown in that environment but it may apply to other users:

public List<string> GetGroupsFromLogonUserIdentity()
{
    List<string> groups = new List<string>();
    HttpRequest request = HttpContext.Current.Request;

    if (request.LogonUserIdentity.Groups != null)
    {
        foreach (IdentityReference group in request.LogonUserIdentity.Groups)
        {
            try
            {
                groups.Add(group.Translate(typeof(NTAccount)).ToString());
            }
            catch (IdentityNotMappedException)
            {
                // Swallow these exceptions without throwing an error. They are
                // the result of dead objects in AD which are associated with
                // user accounts. In this application users may have a group
                // name associated with their AD profile which cannot be
                // resolved in the Active Directory.
            }
        }
    }

    return groups;
}

LogonUserIdentity is based on the WindowsIdentity class. You could modify my code sample to use WindowsIdentity and function in a non-Web application. Once you iterate over a group you should be able to do something like this to get the SecurityIdentifier:

SecurityIdentifier secid = group as SecurityIdentifier;
BrianLy