I'm trying to determine the last domain connection made by a user from a remote machine, regardless of if the machine is currently connected to the domain or not.
The closest I can get is using the System.DirectoryServices.ActiveDirectory and System.DirectoryServices.AccountManagement namespaces that do something similar to this:
Domain d = Domain.GetComputerDomain();
PrincipalContext c = new PrincipalContext(ContextType.Domain, d.Name);
UserPrincipal uc = UserPrincipal.FindByIdentity(c, "johndoe");
And then using the LastLogon property of the UserPrincipal.
This works fine, as long as the machine my application is running on is connected to the domain. If it isn't, Domain.GetComputerDomain() returns null, and I'm out of luck (even if I hardcode the domain name to the PrincipalContext constructor, it throws an exception when not connected to the domain). Is there some other AD property or registry key that gets stored locally on the remote machine when it makes an AD connection to the server that I could use?
The following link describing the LSA Cache seems promising, but, to my knowledge, there is nothing regarding domain connection timestamps that gets cached.
Determine User Active Directory Groups from Local Machine off Network
Related
Using these 2 examples:
http://blogs.technet.com/b/brad_rutkowski/archive/2008/04/15/c-getting-members-of-a-group-the-easy-way-with-net-3-5-discussion-groups-nested-recursive-security-groups-etc.aspx
or
Get members of Active Directory Group and check if they are enabled or disabled
I was able to get users from "Domain Users" when running the them on the Domain controller.
However, I was not able to on member machine that belong to the same domain.
I even logon to the member machine as the Domain Administrator
The errors messages:
Example 1
Unhandled Exception: System.Runtime.InteropServices.COMException: The specified domain either does not exist or could not be contacted.
Example 2
Unhandled Exception: System.DirectoryServices.AccountManagement.PrincipalServerDownException: The server could not be contacted. ---> System.DirectoryServices.Protocols.LdapException: The LDAP server is unavailable.
Can somebody please point me to an example or how to fix this problem ?
Thanks.
Accounts that are local to a workstation or server are not Active Directory accounts, even if the machine is itself a member of an Active Directory domain. Active Directory APIs generally use LDAP to connect to a domain controller (DC), which will not work for local accounts since there is no DC involved.
Assuming you are working with local users and groups, you can still use the System.DirectoryServices.AccountManagement API to get local users and groups. The DirectoryContext class provides a ContextType property, which you will set to Machine to access local users and groups.
The below code is a simple example that will list all the users on the provided workstation:
string workstationName = null; // null --> localhost
PrincipalContext cxt = new PrincipalContext(ContextType.Machine, workstationName);
foreach (var u in new PrincipalSearcher(new UserPrincipal(cxt)).FindAll())
{
var userPrincipal = u as UserPrincipal;
Console.WriteLine(u.Name);
}
Samba3 uses SID's in the range S-1-22-1 for users and S-1-22-2 for groups. For instance, S-1-22-1-1-10042 is the UNIX user with uid 10042.
I would like to be either able to map such a SID into a name, like 'myunixaccount', similar to this functionality for Windows account mapping:
SecurityIdentifier sid = ...; // For instance from FileSystemAccessRule.
name = sid.Translate(typeof(NTAccount)).Value;
Windows itself is able to make this mapping, but I seem unable to find a mapping algorithm.
ADDED: Environment Description
Tested proposed solution on Convert SID to Username in C#. It did not help. Therefore some extra environment description:
Windows PC, either joined to a domain or standalone, running W7 Professional, x86.
File located on Samba-based drive. Samba authenticates to AD controller of domain.
Samba version: 4.0.3, running on Linux 2.6.18-238, x64.
PAM for Samba, interactive sessions etc.
AD controller is W2012 with some default UNIX extension attribute in the directory to allow mapping UID etc.
.NET Framework libraries 4.5.2.
ldap.conf:
piece of ldap.conf
nss_base_passwd=OU=nl,OU=xxx,dc=yyy,dc=local?sub(objectCategory=user)
nss_map_objectclass posixAccount User
nss_map_objectclass shadowAccount User
nss_map_attribute uid sAMAccountName
nss_map_attribute uidNumber uidNumber
nss_map_attribute gidNumber gidNumber
nss_map_attribute cn sAMAccountName
nss_map_attribute uniqueMember member
nss_map_attribute userPassword msSFUPassword
nss_map_attribute homeDirectory unixHomeDirectory
nss_map_attribute loginShell loginShell
nss_map_attribute gecos cn
nss_map_objectclass posixGroup Group
nss_map_attribute shadowLastChange pwdLastSet
Interactive logons on UNIX with Windows authentication work fine, dito for Samba shares. When using a PC on the domain, it doesn't ask for credentials.
Some samples, the user gle3 (highlighted in 1) also exists in the domain but with a different SID. The SID used here is the Samba SID, like S-1-22-1-1-10001.
In (2) you can see that the user exists in the used passwd configuration. The following of course yields no results: grep gle3 /etc/passwd, since the entries are used from remote server. The remote server maps the user SID of gle3 to UNIX uid 10001 and default group 10003.
In (3) you can see that the default group does not exist, and that is why the permissions can not resolve it to a name.
So obviously Windows somehow asks the file server: "give me the data on these SIDs" and the Samba file server respons somehow: Ok, that is "Unix User\gle3" and "Unix Group\10003" but I do not have a group name for that last one.
I've been toying around with this some time ago for building a local LAN crawler on a 2000+ computer network. I'm pretty sure that what you're asking is not part of the SMB protocol. You can actually see that: if Windows cannot resolve the credentials, it will show the SID's in the security properties.
Basically what happens is that a SID is an object ID (like a username / group) that's mapped to a unique ID. They work like GUID's. Usually PC's communicate in SID's, not in usernames.
Now, there's different kinds of SID you need to consider:
You have the Active Directory SID that you can resolve using the standard methods. Note that these include group and user SID's.
There's the local PC SID's that you can resolve using the standard methods. Again, group and user SID's. This probably works for Samba as well as Windows, although I've only tested it on Windows in the past.
There's SID's on remote PC's that normally cannot be resolved. Basically this is what happens if you get a NTLM token in any different way.
There's actually a lot more than this... certificate credentials, reserved users, etc are all also object ID's which can be used for logging in - but I'll just keep it simple. The key takeaway from this comment is that while all usernames have a SID, it's not true that all SID's also have a username.
If you have an AD somewhere (you seem to do), a proper setup contains all users here. The easiest way to get the complete mapping is to simply enumerate the complete active directory. That should contain all the mappings. Basically that works like this:
DirectoryEntry root = new DirectoryEntry("LDAP://dc=MyCompany,dc=com");
DirectorySearcher search = new DirectorySearcher(root);
search.Filter = "(objectCategory=Person)";
search.SearchScope = SearchScope.Subtree;
search.PropertiesToLoad.Add("objectSid");
search.PropertiesToLoad.Add("displayName");
foreach(SearchResult result in search.FindAll())
{
// grab the data - if present
if(result.Properties["objectSid"] != null && result.Properties["objectSid"].Count > 1)
{
var sid = result.Properties["objectSid"][0];
}
if(result.Properties["displayName"] != null && result.Properties["displayName"].Count > 0)
{
var userName = result.Properties["displayName"][0].ToString();
}
}
I am sending request via WCF from Machine A to Machine B.
The domain and users are same on both machines.
In Machine B, using a given parameters, I want to create a WindowsIdentity for that user that invoked the operation from Machine A.
I know there is technology to do this via WCF infrastructure - but this technology is not enabled in my project.
So i need to send OperationContext.Current.ServiceSecurityContext.WindowsIdentity.User.Value from Machine A to Machine B.
My question is how do I create a WindowsIdentity using a SID ? Or do i need to send different parameters ?
Notes:
I was trying this:
PrincipalContext ctx = new PrincipalContext(ContextType.Domain, "My_Domain");
GroupPrincipal grp = GroupPrincipal.FindByIdentity(ctx, "userSID");
ofcourse with the right parameters, and then iterated all the GRP.GetMembers(true) to seek for mine - but it was not there.
Update:
The User is: DOMAIN_NT/MyUser123 -> If i change it to MyUser123#DOMAIN_NT and create instance of WindowsIdentity identity = new WindowsIdentitiy("MyUser123#DOMAIN_NT"); - It will work, but its an ugly way.
I basically have three questions here: I need to use .Net 3.5.
I have enabled SSL on my active directory. I have exported the certificate and imported on a different machine. Now when I try to access the active directory using port 389, it allows me to connect . Is this an expected behavior?
Many places I found to use "LDAPS" in my directory path when using SSL. But when I use this I get Unknown COM Exception. Here on MSDN I found there is nothing such "LDAPS"
https://social.msdn.microsoft.com/Forums/vstudio/en-US/723c3908-5806-4515-a5b2-b565e0131a2b/active-directory-connection-ldap-over-ssl
Do I really need to provide the domain name before username (domain\user)? I am able to connect without specifying the domain name this way. All I need to provide the FQDN or the name to which the SSL certificate is issued.
I am using DirectoryEntry class for my implementation.
string path = "LDAP://hostname:port/SearchBase";
DirectoryEntry _directoryEntryObj = new DirectoryEntry(path, userName, password);
if(IsSSL)
_directoryEntryObj.AuthenticationType = AuthenticationTypes.SecureSocketsLayer;
object obj = _directoryEntryObj.NativeObject;
If you don't deactivate plain LDAP, it is an expected behavior, since LDAPS uses a different port, see: https://en.wikipedia.org/wiki/Lightweight_Directory_Access_Protocol
In general I don't believe you will get far with an AD that only runs on LDAPS (port 636), with plain LDAP (389) blocked. I don't think that most appliations implement LDAPS, but I could be wrong.
On top, the certificate has to valid. If you just export to another machine (with another hostname), the certificate won't be trusted. What kind of certificate do you use?
this could be linked with my concern regarding the validity of your certificate
When I played around with ldap/ad in .Net I found that you can also just use the current user. If you don't want to use that user, you'll have to go with domain\user. I also think that you consider that best practice, since you can always determine if the specified user is a local account or a domain account.
--
Hope I could help,
regards
We have a WPF application that runs in full trust.
Part of the application checks the membership of a Windows AD group.
This works fine on a Windows 7 machine, but not on a Windows XP machine.
The error occurs on the following line:
PrincipalContext ctx = new PrincipalContext(ContextType.Domain, "domain name");
According to the article Managing Directory Security Principals in the .NET Framework 3.5, the "domain name" variable might not be needed. That is, if you are accessing an Active Directory in the same domain as your application, the domain name is not needed.
You use the name parameter on the PrincipalContext constructor in
order to provide the name of the specific directory to connect to.
This can be the name of a specific server, machine, or domain. It's
important to note that if this parameter is null, AccountManagement
will attempt to determine a default machine or domain for the
connection based on your current security context.
The solution or workaround to the problem (at least what worked for me on both XP and W7) is the following change:
PrincipalContext ctx = new PrincipalContext(ContextType.Domain, null);