Convert SID's to usernames/groups? - c#

I'm looping through a network directory and trying to output the user/group names (permissions) associated with each file/folder. I'm getting the SID's back but I want the names like "group_test" and not "S-1-5-32-544". Here's my code -
var files = Directory.GetFiles(path, "*.*", SearchOption.TopDirectoryOnly);
foreach (var f in files2)
{
var fileInfo = new FileInfo(f);
var fs = fileInfo.GetAccessControl(AccessControlSections.Access);
foreach (FileSystemAccessRule rule in fs.GetAccessRules(true, true, typeof(System.Security.Principal.NTAccount)))
{
var value = rule.IdentityReference.Value;
Response.Write(string.Format("File: {0} \t Usergroup: {1} <br/>", fileInfo.Name, value));
} }
I get SID's from the above code but in the foreach loop, if I use this instead -
(NTAccount)((SecurityIdentifier)rule.IdentityReference).Translate(typeof(NTAccount)).Value
I get this exception -
Some or all identity references could not be translated.
It appears that the Translate method does not work on remote shares. How do I retrieve the real names of the SID's? The remote server does not have LDAP.
Thank you.

The problem is that you are trying to resolve a SID that is local to a remote machine. As the answer to this question states:
The SecurityReference object's Translate method does work on non-local SIDs but only for domain accounts...
This link provides an example for remotely resolving a SID using WMI which is probably the best method for accomplishing your task.

If you can use WMI you should be able to do it via the Win32_UserAccount class I think. It has a Name property and a SID property.
Or the Win32_Group class for the groups.
Here's an article for connecting to a remote pc using WMI that has C# code: How To: Connect to a Remote Computer

Related

Trying to check the friendly OS name of a different workstation

I am trying to find the friendly OS name of a different workstation from my workstation. This occurred when I used:
var name = (from x in new ManagementObjectSearcher("SELECT Caption FROM Win32_OperatingSystem").Get().Cast<ManagementObject>()
select x.GetPropertyValue("Caption")).FirstOrDefault();
it is returning my workstation's OS name. Can you please suggest me a better way to find out.
Thanks in advance!!
You can use the System.DirectoryServices to search on the directory 'WinNT' you can read more about it here:
https://learn.microsoft.com/en-us/dotnet/api/system.directoryservices.directoryentry.path?view=netframework-4.7.2
example solution - adding names to a list
// create list to add the names to
var pcnames = new List<string>();
// establish the domains in the local network
var directory = new DirectoryEntry("WinNT:");
// iterate through the children
foreach (DirectoryEntry workstation in directory.Children)
{
pcnames.Add(workstation.Name)
}

Is there a way to get the free space on an iSCSI drive mounted as a NTFS folder

I have a bunch of iSCSI drives mounted as NFTS folders (to avoid exhausting all the drive letters) acting as a mini SAN, and I would like to get the information about their free space. The basic reason is to get warnings when space gets below a certain threshold, as a part of a scheduled task which does a bunch of other checks.
Is there a way to do it, preferably using C# (through WMI, P/Invoke, or whatever)? Of course, any scripting solution would also be great, as I can probably invoke it anyway (PowerShell)? I tried the optimistic route first, using DriveInfo initialized with using such a path, but it simply returns information about the root volume instead of the mount. I've also tried enumerating stuff like Win32_DiskPartition, Win32_LogicalDisk and Win32_MappedLogicalDisk but didn't get those drives at all.
As #FrédéricHamidi explained, Win32_Volume class in the WMI Storage Volume Provider shows correct space information about mounted volumes.
Usage example (C#) would be something like:
// iSCSI drive mounted in a NTFS folder
var ntfsPath = #"x:\iscsi\volume";
// it's good to know that backspaces must be escaped in WMI queries
var cmd = string.Format(
"SELECT * FROM Win32_Volume WHERE Name LIKE '{0}%'",
ntfsPath.Replace(#"\", #"\\"));
using (var searcher = new ManagementObjectSearcher(cmd))
{
foreach (ManagementObject queryObj in searcher.Get())
{
var name = (string)queryObj["Name"];
var freeSpaceInBytes = (ulong)queryObj["FreeSpace"];
}
}

Microsoft Exchange / Active Directory properties for mails to be used to read a GAL

I'm currently creating an application which is using outlook as well as the exchange server / active directory in my company to create mails (I've had a few other questions here thus already).
I'm currently trying to read the GAL for it to be used when sending mails over my application. From the solutions I've seen so far it seems to me that the variant where I read the mail addresses from the active directory instead of connecting to the exchange server (I first tried outlook but aside from getting only the account names with the type "EX" thus that they are stored on the exchange server I didn't get much info there).
What I've done so far is gtting access to teh active directory and reading all users from there
DirectorySearcher objsearch = new DirectorySearcher();
String strrootdse = objsearch.SearchRoot.Path;
DirectoryEntry objdirentry = new DirectoryEntry(strrootdse);
objsearch.Filter = "(& (mailnickname=*)(objectClass=user))";
objsearch.SearchScope = System.DirectoryServices.SearchScope.Subtree;
objsearch.PropertiesToLoad.Add("cn");
objsearch.PropertiesToLoad.Add("mail");
objsearch.PropertyNamesOnly = true;
objsearch.Sort.Direction = System.DirectoryServices.SortDirection.Ascending;
objsearch.Sort.PropertyName = "cn";
SearchResultCollection colresults = objsearch.FindAll();
List<String> arrGal = new List<String>();
foreach (SearchResult objresult in colresults)
{
arrGal.Add(objresult.GetDirectoryEntry().Properties["cn"].Value + ": " + objresult.GetDirectoryEntry().Properties["mail"].Value);
}
Now after looking at the active directory I saw that there are also proxies and that (at least at my company) the "mail" property is not necessarily one of the mail addresses listed in the proxies.
Thus I found these two attributes: msExchShadowProxyAddresses, proxyAddresses
From what I've seen so far from them by looking at samples they look like they are identical, but even searching I didn't find anything on the web so far there.
Thus my Question when I'm trying to get the GAL from active directory Can I use both of these properties (thus they are always identical) or should I only use the ShadowProxy property or is there something I need to take into special consideration there?
You need to use AddressEntry.GetExchangeuser method. See my reply to your other post.

"net use * /delete" equivalent in C#?

I'm using the following lines below to create a connection to a shared network location, but the problem is with any connections active (I think), network.MapNetworkDrive("..") will throw an error:
Multiple connections to a server or shared resource by the same user,
using more than one user name, are not allowed. Disconnect all previous
connections to the server or shared resource and try again.
I got pass this error by using net use * /delete from the command line, but is there an equivalent commands in C#?
IWshNetwork_Class network = new IWshNetwork_Class();
network.MapNetworkDrive("z:", #shared_path, Type.Missing, "Admin", "!QAZxsw2");
...
network.RemoveNetworkDrive("z:");
Just Use
System.Diagnostics.Process.Start("CMD.exe","/c net use * /delete /y");
Just use Process.Start
System.Diagnostics.Process.Start("CMD.exe","/c net use * /delete");
If you insist on a managed code approach (why?) you can do something like:
foreach(var letter in "ABCDEFGHIJKLMNOPQRSTUVWXYZ")
{
try {
network.RemoveNetworkDrive(letter + ":");
} catch {}
}
or better yet (depending on how flexible you are with the required behviour), iterate on this instead:
// assumes using System.IO
var networkDrives = DriveInfo.GetDrives().Where(x => x.DriveType == DriveType.Network))
foreach(var networkDrive in networkDrives)
I'd still opt for just using Process.Start as being far cleaner and more reliable. Once you start down the road of re-implementing 'trivial' functionality you often quickly find out how non-trivial it really is.
Further on why your question itself might need reconsidering - why would you insist on removing all network drive mappings anyway? If you are mapping Z: for example, you know what drive needs to be unmapped. You can have something like this:
public void MapDrive(char driveLetter, string networkPath, string userName, string password)
{
try { network.RemoveNetworkDrive(driveLetter + ":"); } catch {}
network.MapNetworkDrive(driveLetter + ":", #shared_path, Type.Missing, username, password);
}
because surely if you're creating the drive mappings you inherently know which drive letters need to be free as opposed to blowing all network drive mappings away.

How can I get tokenGroups from active directory on Windows Server 2003?

I'm trying to load tokenGroups from Active Directory but it isn't working once deployed to a Windows Server (2003). I cannot figure out why, since it works fine locally...
Here is my error:
There is no such object on the server.
And here is my code (the sid variable is the current users SecurityIdentifier pulled from HttpContext):
DirectoryEntry userDE = new DirectoryEntry(string.Format("LDAP://<SID={0}>", sid.Value))
userDE.RefreshCache(new[] { "tokenGroups" });
var tokenGroups = userDE.Properties["tokenGroups"] as CollectionBase;
groups = tokenGroups.Cast<byte[]>()
.Select(sid => new SecurityIdentifier(sid, 0)).ToArray();
Any ideas why I would get that error?
UPDATE: The error actually happens on the RefreshCache line
Do you have a valid value for userDE after the constructor call?? Does that user really exist? Or do you need to provide e.g. a server to use in your LDAP path??
The error message No such object on server seems to indicate the user just plain doesn't exist.... (or cannot be found, due to e.g. permissions)
Try this - not sure if that's the problem, but it's worth a try - it should work:
DirectoryEntry userDE = new DirectoryEntry(string.Format("LDAP://<SID={0}>", sid.Value))
userDE.RefreshCache(new string[] { "tokenGroups" });
Try using new string[] instead of just new[].

Categories