C# LDAP Authentication works for one DC, but not another - c#-4.0

I have an interesting issue I've been trying to resolve for a few days.
I'm currently working with an Windows Server 2003 machine that is running a standard instance of Active Directory.
The directory contains two domain components (DCs) that both house users that are going to be authorizing against the directory, via my application.
I'm using :
The IP address of the server as the host name
An SSL connection via port 3269
The GSS Negotiate Auth Mechanism
A BaseDN that is a parentDN of both DC's
The sAMAccountName as the login name
The problem is, I cannot successfully authorize any users from DC1, yet all of the ones who belong to DC2 are completely fine and work great. I get this error on DC1 :
8009030C: LdapErr: DSID-0C09043E, comment: AcceptSecurityContext error, data 0, vece
System.DirectoryServices.Protocols.LdapException: The supplied credential is invalid.
However, using Softerra's LDAP Broswer, I can connect in and authorize the same exact user without any issue, so I know the credentials are correct.
From what I can tell, both of these DC's are configured the same... I've browsed both of them for something, anything that is different... but have found nothing that really stands out.
I posted something months ago about this particular setup, and the code I'm using is in that thread as well.
Set callback for System.DirectoryServices.DirectoryEntry to handle self-signed SSL certificate?
Any help here would be much appreciated.
Thanks!

I was able to get this working, but for the life of me I cannot figure out why this was the case. Basically, this error...
8009030C: LdapErr: DSID-0C09043E, comment: AcceptSecurityContext error, data 0, vece System.DirectoryServices.Protocols.LdapException: The supplied credential is invalid.
...was dead on. The issue was that users logging in under what I called DC2 needed to issue the bind with the domain AND sAMAccountName (Ex. LIB\JSmith), as opposed to DC1, which allowed just the sAMAccountName to be entered.
I figured the best way to make this programmatic was to use the principal binding account to query for the DN of the user. From that DN, using some crafty RegEx, I'm able to capture the domain they inherit from, and issue two separate binds.
SearchResultEntry ResultEntry = userResponse.Entries[0];
//Let's get the root domain of the user now using our DN RegEx and that search result
Regex RegexForBaseDN = new Regex(config.LdapAuth.LdapDnRegex);
Match match = RegexForBaseDN.Match(ResultEntry.DistinguishedName);
string domain = match.Groups[1].Value;
//Try binding the user with their domain\username
try
{
var thisUser = new NetworkCredential{
Domain = domain,
UserName = username,
Password = Pin
};
//If this goes well, we'll continue forward
ldapconn.Bind(thisUser);
}
//If that doesn't work, try biding them with the highest level domain
catch (LdapException ex)
{
if (ex.ErrorCode.Equals(LdapErrorCodes.LDAP_INVALID_CREDENTIALS))
{
var thisUserOnce = new NetworkCredential{
Domain = config.LdapAuth.LdapDomain,
UserName = username,
Password = Pin
};
//If this goes well, we'll continue forward
ldapconn.Bind(thisUserOnce);
}
}
It's not nearly as elegant as I would have wanted, but it does work for this particular scenario.
However, I'm still really interested in why the naming conventions are different depending on which DC the user inherit's from.

Related

Has azure user ids changed their format?

Good evening ppl at Microsoft!
I have an Mobile App Service at Microsoft Azure Located at South Central US named CeneamApp.
My backend is configured in a way so that my user can access only the data they capture, by making use of stable user ids.
so I had followed Adrian Hall book to create an a user id (https://adrianhall.github.io/develop-mobile-apps-with-csharp-and-azure/chapter2/authorization/)with the following format sid:{identifier}as described here: (https://github.com/Azure/azure-mobile-apps-net-server/wiki/Understanding-User-Ids).
now all my userid had been changed and my user cant access their previous data capture by them, because somehow the provider or issuer or whatever is going on, doesnt let me retrieve a user id as described by the github project team wiki (in the previous link). so instead i receive a new userid but seem to be a random number:
I'm adding screenshot of the essential part of my code at my backend project which i debugged so i could understand whats going on and my dummy database where you can see an stable_id save on it and the new suppose stable_ids the next two rows.
Debugged code retrieving apparently a new userid from FACEBOOK could you confirm about this change? because I havent been able to understand this change.
Dummy Database with the lost userid and the new ones from other accounts database screenshot
if anyone has information about this odd behavior i would appreciate to enlight me, because this line of code:
principal.Claims.FirstOrDefault(c => c.Type == ClaimTypes.NameIdentifier)?.Value;
used to give me a user id with this format: "sid:{identifier}", now the format is a the screenshot shows.
Same situation here. About to go live and suddenly this. Interesting that only one Mobile App based in the UK datacenter is affected. Other 2 apps which are in production and plus another Web App are still fine.
The full solution can only be provided by Azure team. But I have a workaround and and idea:
1. Workaround.
In the base controller I read and parse the token from the header. The sid is in the subject of the token. The code is simple:
string _userSid;
public string UserSid
{
get
{
if (_userSid == null)
{
KeyValuePair<string, IEnumerable<string>> auth_header = Request.Headers.FirstOrDefault(h => h.Key.Equals("x-zumo-auth", StringComparison.InvariantCultureIgnoreCase));
string token = auth_header.Value.FirstOrDefault();
if (!string.IsNullOrEmpty(token))
{
var jwtToken = new JwtSecurityToken(token);
if (jwtToken != null)
{
_userSid = jwtToken.Subject;
}
}
}
return _userSid;
}
}
I use it instead of getting this from ClaimPrinciple as per manual. I checked the Mobile App Server code does a very similar thing.
2. Idea.
Azure Mobile Apps have this parameter:
MobileAppsManagement_EXTENSION_VERSION it is recommended to set to latest. I think if we downgraded it to previous version it would work until Microsoft finds and solves the problem. The only issue is that I do not know and could not find the version number. May be someone knows and can post here?

ASP.NET Windows Authentication returns wrong user to the application

I have two domains A and B, with a user Administrator in each. Both admins have different objectGuids, SIDs and passwords. There's an IIS 8.5 in B configured with Windows Authentication (Methods Negotiate/NTLM, Extended Security disabled, Kernel mode auth enabled). Domain B trusts domain A (one-way trust).
When I now open IE as A\administrator, and connect to the IIS in B, the IIS returns to me that the user logged in is B\administrator (should be A\administrator).
This is my code:
public class UserController : ApiController
{
// GET api/<controller>
public User Get()
{
var usr = ((WindowsIdentity)User.Identity).User;
return new User() {
Name = User.Identity.Name,
SID = usr==null?"":usr.ToString()
};
}
}
Also, the same is in the IIS log:
2018-05-07 09:19:10 172.17.41.31 GET /winauthtest/User - 80 B\Administrator 172.17.42.11 Mozilla/4.0+(compatible;+MSIE+7.0;+Windows+NT+10.0;+WOW64;+Trident/7.0;+.NET4.0C;+.NET4.0E) - 404 0 2 31
Is this intended behaviour or a bug, and if it's a bug, where to report the bug?
Can I fix it by changing Windows Authentication settings, or what else could I do about it?
Do you know any other possibility to get the true SID of the user that is accessing my IIS?
OMG, I had the same problem and your problem gave me a hint as to why this is happening. So, running my project in Chrome gives me the wrong domain, while opening in Edge gives me the correct domain. I have my project set up using Windows Authentication and it seems Chrome is in some way blocking Windows Authentication.
This link gives more info.
https://specopssoft.com/blog/configuring-chrome-and-firefox-for-windows-integrated-authentication/
In the end, another of my coworker had the same issue and we couldn't fix it properly. It would misbehave randomly. The only solution that seemed to work was restarting the PC.

How To Get List Of Active Directory Domain Groups When Not Connected To Domain

At my company we have a custom HMI. This HMI can login using the domain of the computer if the user would like to. Here's the scenario we're currently trying to solve
With user logged into Windows as a domain user, they login to the HMI using the same windows credentials - everything goes smoothly.
Network connection goes down and the user restarts the computer.
The user can login to Windows as before using the same domain logon (with the Windows cached user allowing this)
The user tries to login to our HMI and they can't because I haven't figured out how to access these cached active directory users.
Here's the code I'm using right now to detect if the user is member of one of the groups allowed to login to the HMI. These groups are stored in our SQL database, and are stored as Strings like SERVER\BUILDER for example
If DuplicateToken(token, 2, tokenDuplicate) <> 0 Then
tempWindowsIdentity = New WindowsIdentity(tokenDuplicate)
AppDomain.CurrentDomain.SetPrincipalPolicy(PrincipalPolicy.WindowsPrincipal)
WinPrincipal = New WindowsPrincipal(tempWindowsIdentity)
End If
If WinPrincipal.IsInRole(user.Group) Then
'Success - obviously there's more code after this
End If
The problem is that when the user is not connected to the domain I get an exception on the IsInRole method. I took a look at the overloading in the method and tried using the SID equivalent of the user.Group instead of the literal String. This seems to work, but now another problem comes up. When the user is not on the network I can't translate the Group Name (string from SQL) into these SIDs.
My current workaround is that when the user is connected to the domain, I save the SIDs and the groups into a lookup table (encrypted), then when the network is not connected, I take the group name from the database and literally lookup the SID string in the table.
The code I'm using to convert the group name to SID is the following
'' Get all groups available to the user
For i As Integer = 0 To WindowsIdentity.GetCurrent.Groups.Count - 1
Dim ir As IdentityReference = WindowsIdentity.GetCurrent.Groups(i)
sid(i) = New SecurityIdentifier(ir.Value)
sidName(i) = CType(ir.Translate(GetType(NTAccount)), NTAccount).Value
' Write to file as csv
writer.WriteLine(sidName(i) + "," + sid(i).ToString)
Next
Then I lookup the group name like so
If File.Exists(FILEPATH) = True Then
Dim reader As New StreamReader(FILEPATH)
While Not reader.EndOfStream
Dim sTemp() As String = reader.ReadLine.Split(",")
If groupName.Equals(sTemp(0)) Then
Return New SecurityIdentifier(sTemp(1))
End If
End While
End If
The reason I can't translate the group name to a SID when not on the network is because the IdentityReference.Translate throws an exception because it can't see the domain.
So, finally after all this explanation, here's what I'm looking for. I'm thinking that there must be access somewhere for me to get the SID from a group name, because windows MUST be storing it somewhere, otherwise I wouldn't be able to login to windows when not connected to the network.
I've done a lot of searching and I haven't been able to figure out where these cached credentials are stored. Any help would be appreciated and if I haven't been clear enough let me know and I'll try to explain as best I can.
I know it is old, but it was actual for me.
As a familiar post I will link this: Determine User Active Directory Groups from Local Machine off Network
The proposed solution was to store the SID in a local space which seems to be the best idea.

Why does CredentialCache.DefaultCredential contain empty strings for domain, username, and password

Does anyone have any ideas as to why CredentialCache.DefaultCredential would return an ICredential instance with empty strings for domain, username, and password? I'm running a WCF service on IIS 7.5. It works fine on one server but never works on another. I have verified that the IIS application has Windows Authentication enabled....
Here is how it's being used:
string url = string.Format("{0}/departments/finance/_vti_bin/listdata.svc", _IntranetAddress);
var financeDataContext = new FinanceDataContext(new Uri(url))
{
Credentials = CredentialCache.DefaultCredentials
};
I am not sure how it is working in one of your servers? I hope you already read this
http://msdn.microsoft.com/en-us/library/system.net.credentialcache.defaultcredentials.aspx
but it clearly says "The ICredentials instance returned by DefaultCredentials cannot be used to view the user name, password, or domain of the current security context."
The NetworkCredential returned from CredentialCache.DefaultCredential is just a placeholder. If you look at it using the Debugger, you'll see that it's of type SystemNetworkCredential. Internal API check for this type to see if integrated authentication should be used or not. There are other ways to get the current username (like WindowsIdentity.GetCurrent()).
EDIT:
To specify impersonation for a WCF operation, add this attribute to the method implementing a contract:
[OperationBehavior(Impersonation = ImpersonationOption.Required)]
public void SomeMethod()
{
// do something here
}

How to use a logged in person's credentials to search Active Directory?

I have a web app (.NET 3.5) which is sending notifications by email to users. In order to do this, I search Active Directory to find each person's email.
At the moment, I am hardcoding my own username and password like so in order to search AD:
Dim entry As New DirectoryEntry("LDAP://companyad", "myUsername", "myPassword", AuthenticationTypes.Secure)
Dim srch As New DirectorySearcher(entry)
srch.Filter = [String].Format("(&(objectClass=person)(sAMAccountName={0}))", "someOtherUsername")
Dim result As SearchResult = srch.FindOne()
Now, obviously, this is not ideal and I don't want those credentials hardcoded. My web app is using Windows Authentication. It also uses impersonation (as the logged in user) to access files and SQL Server. Is there also a way for me to "impersonate" the logged in user in order to search AD?
EDIT 1
I thought I'd better explain why I chose this answer. The problem turned out to not be the multi-hop issue or kerberos as it seems I have set these up correctly.
I had recently changed my app to only allow access to a certain group through the web.config settings. I had previously been only allowing access to myself. I set up the group and added myself to it. I then removed the hardcoded credentials and attempted to run the app WITHOUT RESTARTING my computer.
According to my network admin, I would not be logged on under that new group until I restarted my computer which I think is what was causing my problem. So, Preet's answer is actually the most accurate as I just needed to pass the LDAP path to DirectoryEntry.
EDIT 2
I also needed to register a Service Principal Name.
I ran this:
setspn -A HTTP/[dns name of the site] [machine name]
on my development machine.
Thanks to everyone else for their answers.
Doesn't
Dim entry As New DirectoryEntry("LDAP://companyad")
work?
Why not create a new user for this purpose alone? A user with only searching rights.
I set <identity impersonate="true"/> in my web.config and added the following code to my my page load event handler. It worked fine. Are you sure you are not dealing with a multi hop situation? In that case your app pool account needs to be configured for kerberos authentication to support impersonation in a multihop scenario. More info on this is here: http://support.microsoft.com/kb/329986
Response.Write(User.Identity.Name);
DirectoryEntry entry = new DirectoryEntry("LDAP://[mydomain.net]");
DirectorySearcher srch = new DirectorySearcher(entry);
srch.Filter = string.Format("(&(objectClass=person)(sAMAccountName={0}))", "[user]");
SearchResult result = srch.FindOne();
Response.Write(result.Path);
If you wish to use the Windows logged in user account as the credentials against AD, you have to use the following:
public bool IsExistingUser() {
DirectoryEntry de = new DirectoryEntry(Environment.UserDomainName)
DirectorySearcher ds = new DirectorySearcher(de)
ds.Filter = string.Format("((objectClass=user)(SAMAccountName={0}))", Environment.UserName)
try
SearchResult sr = ds.FindOne();
if (sr != null && sr.DirectoryEntry.Name.Contains(Environment.UserName))
return true;
catch (DirectoryServicesCOMException ex)
catch (COMException ex)
throw new Exception("Can't find logged in user in AD", ex);
return false;
}
Assuming this code would compile and run, it will verify whether the existing logged in user is known by your Domain Controller.
Impersonation is discouraged as it lets litteral password strings travel in your network. So, try to avoid it as much as possible.
EDIT Here's a pretty useful link for AD: Howto: (Almost) Everything In Active Directory via C# I found this post awesome!

Resources