LDAP search fails on server, not in Visual Studio - c#

I'm creating a service to search for users in LDAP. This should be fairly straightforward and probably done a thousand times, but I cannot seem to break through properly. I thought I had it, but then I deployed this to IIS and it all fell apart.
The following is setup as environment variables:
ldapController
ldapPort
adminUsername 🡒 Definitely a different user than the error reports
adminPassword
baseDn
And read in through my Startup.Configure method.
EDIT I know they are available to IIS, because I returned them in a REST endpoint.
This is my code:
// Connect to LDAP
LdapConnection conn = new LdapConnection();
conn.Connect(ldapController, ldapPort);
conn.Bind(adminUsername, adminPassword);
// Run search
LdapSearchResults lsc = conn.Search(
baseDn,
LdapConnection.SCOPE_SUB,
lFilter,
new string[] { /* lots of attributes to fetch */ },
false
);
// List out entries
var entries = new List<UserDto>();
while (lsc.hasMore() && entries.Count < 10) {
LdapEntry ent = lsc.next(); // <--- THIS FAILS!
// ...
}
return entries;
As I said, when debugging this in visual studio, it all works fine. When deployed to IIS, the error is;
Login failed for user 'DOMAIN\IIS_SERVER$'
Why? The user specified in adminUsername should be the user used to login (through conn.Bind(adminUsername, adminPassword);), right? So why does it explode stating that the IIS user is the one doing the login?
EDIT I'm using Novell.Directory.Ldap.NETStandard
EDIT The 'user' specified in the error above, is actually NOT a user at all. It is the AD registered name of the computer running IIS... If that makes any difference at all.
UPDATE After consulting with colleagues, I set up a new application pool on IIS, and tried to run the application as a specified user instead of the default passthrough. Exactly the same error message regardless of which user I set.

Try going via Network credentials that allows you to specify domain:
var networkCredential = new NetworkCredential(userName, password, domain);
conn.Bind(networkCredential);
If that does not work, specify auth type basic (not sure that the default is) before the call to bind.
conn.AuthType = AuthType.Basic;

Related

Not able to Set Password and Enable Account using C# and admin User

Using WPF & C#, I can set all the attributes in Active Directory, but can't do the following :
1) Can't Set User Password
2) Can't Enable User
However, I can do the same thing manually!
Approach Tried:
1.
DirectoryEntry directoryEntry=
directoryEntry.Invoke("SetPassword", new object[] {myPass#x6712}); // To set password
directoryEntry.Properties["userAcountControl"].Value=0x0200; //To Enable User
2.
DirectoryEntry uEntry = new DirectoryEntry(userDn);
uEntry.Invoke("SetPassword", new object[] { password });
uEntry.Properties["LockOutTime"].Value = 0; //unlock account
3.
using (var context = new PrincipalContext( ContextType.Domain ))
{
using (var user = UserPrincipal.FindByIdentity( context, IdentityType.SamAccountName, userName ))
{
user.SetPassword( "newpassword" );
// or
user.ChangePassword( "oldPassword", "newpassword" );
user.Save();
}
}
ERROR ON PASSWORD SET: Exception has been thrown by the target invocation.
ERROR ON ENABLE USER: Access is denied.
NOTE: I'm using a Domain Admin User.
The program gives the exception in these above lines.
Please, Advice! Thanks in Advance !!
Maybe this is just a mistake in your question, but the code you show in your first example wouldn't compile because the password is not in quotes. It should be:
directoryEntry.Invoke("SetPassword", new object[] {"myPass#x6712"});
That code invokes IADsUser.SetPassword. The 'Remarks' in the documentation point to some prerequisites for it to work, namely, that it must be a secure connection. So it may have failed in setting up a secure connection. It would usually try Kerberos to do that, so something might have gone wrong there.
You can try specifically connecting via LDAPS (LDAP over SSL) by pointing it at port 636 (new DirectoryEntry("LDAP://example.com:636/CN=whatever,DC=example,DC=com")), but that requires that you trust the certificate that is served up. Sometimes it's a self-signed cert, so you would need to add the cert to the trusted certs on whichever computer you run this from.
Or, the account you are running it with does not have the 'Reset Password' permission on the account.
For enabling, the userAccountControl attribute is a bit flag, so you don't want to set it to 2, mostly because 2 (or more accurately, the second bit) means that it's disabled. So you want to unset the second bit. You would do that like this:
directoryEntry.Properties["userAcountControl"].Value =
(int) directoryEntry.Properties["userAcountControl"].Value & ~2;
Most of the time that will result in a value of 512 (NORMAL_ACCOUNT), but not necessarily. The account could have other bits set that you don't want to inadvertently unset.
You also need to call .CommitChanges() for the changes to userAcountControl to take effect:
directoryEntry.CommitChanges();

Unable to get some user account information in UWP app - In-premise active directory (Not Azure AD)

In a UWP app, I have enabled the User Account Information capability.
I need to get the username and the domain name (each of them separately) of the currently logged on user (The users are logged on with an in-premise Active Directory account - Not Azure AD).
For example, the user would log in to the Active Directory domain domain1 using the username user1. i.e. domain1\user1.
I am using the following code to try to get the required details:
IReadOnlyList<User> users = await User.FindAllAsync();
var user = users.FirstOrDefault();
// get domain
var data1 = await user.GetPropertyAsync(KnownUserProperties.DomainName);
string strDomainName = (string)data1;
// get username
var data2 = await user.GetPropertyAsync(KnownUserProperties.AccountName);
string strUserName = (string)data2;
Issues:
strDomainName returns domain1.com\user1. Why does this include the .com part for all our domains? On c# winforms applications we can easily get domain1\user1 without any issue.
strUserName returns an empty string. i.e. "". Why does this not return any value?
I also checked the following:
KnownUserProperties.FirstName returns an empty string. i.e. ""
KnownUserProperties.LastName returns an empty string. i.e. ""
KnownUserProperties.PrincipalName returns an empty string. i.e. ""
KnownUserProperties.ProviderName returns an empty string. i.e. ""
KnownUserProperties.GuestHost returns an empty string. i.e. ""
Is there anything else I need to enable similar to the User Account Information capability? Or are there any other permissions that need to be granted to the app to get this information?
I understand that I can get the value of strDomainName and perform string functions to get what I need. But I want to know if there is any way to get this information directly. Also curious why KnownUserProperties.AccountName and other properties listed above such as FirstName, LastName etc. just returns an empty string.
I am running the following version of Windows:
I have the following set as the Target version and Min Version:
To verify, I also tested with the UserInfo sample project by Microsoft from GitHub and I got the following output:
The following was automatically enabled in Settings > Privacy > Account Info.
TestApp is the app I tried with and User Info C# Sample is the sample app from GitHub:
Update:
After also enabling the Enterprise Authentication capability, KnownUserProperties.PrincipalName does return the expected value. i.e. user1#domain1.com.
However, other properties listed above such as FirstName, LastName etc. just returns an empty string and I am still unable to find any property that returns domain1\user1 (without the .com part)
The Information you are trying to access are not reliable, as they (as you mentioned) do not have to be set and also they can be restricted access to via privacy settings in general.
I had a similar problem and would advise you to use the UWP OneDrive API
using Microsoft.OneDrive.Sdk;
and then request wl.basic scope. This scope contains at least a reliable username.
public static async Task<bool> GetAuthenticatedClient()
{
string oneDriveConsumerBaseUrl = "https://api.onedrive.com/v1.0";
var scopes = new List<string>
{
"wl.signin",
"wl.basic",
};
Task authTask;
var onlineIdAuthProvider = new OnlineIdAuthenticationProvider(scopes.ToArray());
authTask = onlineIdAuthProvider.RestoreMostRecentFromCacheOrAuthenticateUserAsync();
oneDriveClient = new OneDriveClient(oneDriveConsumerBaseUrl, onlineIdAuthProvider);
AuthProvider = onlineIdAuthProvider;
try
{
await authTask;
if (!AuthProvider.IsAuthenticated)
{
return false;
}
}
catch (ServiceException exception)
{
// Swallow the auth exception but write message for debugging.
//Debug.WriteLine(exception.Error.Message);
return false;
}
return true;
}
As for the domain, I'm not sure, but you could try to access it via Environment.UserDomainName like described on MSDN or with Windows.Networking.Connectivity.NetworkInformation.GetHostNames() like described here.
I found another possible solution to this. If you are still debugging this locally or the app was already installed on the target machine, I could enable the capabality User Account Information but this was not propagated to the actual installed app:
when your search for your app name in the start menu and then right click the entry and select App settings you get something like this:
As soon as I enabled the highlighted option (basically it says 'Account information') it worked.

LdapConnection Bind() always fails over SSL

I need to query some information with Active Directory that is only accessible when authenticated over SSL. I can make anonymous connections without issue, but I always get an "LDAP server is unavailable error" when trying to use SSL. There's a lot of forum topics about this, and I've reviewed them but have not found a solution. This code is an ASP.NET MVC app being run on IIS Express.
LdapConnection conn = new LdapConnection("ldap.xxx.com:636/OU=xxx,DC=xxx,DC=xxx,DC=xxx");
//conn.AutoBind = false;
conn.SessionOptions.ProtocolVersion = 3;
conn.AuthType = AuthType.Basic; //Tried with Negotiate as well
conn.Credential = new NetworkCredential(#"domain\user", "userPW", "domain");
conn.SessionOptions.SecureSocketLayer = true;
conn.SessionOptions.VerifyServerCertificate = new VerifyServerCertificateCallback((dev, cer) => true);
//conn.Timeout = new TimeSpan(1, 0, 0);
conn.Bind();
I use that container string with a PrincipalContext to validate the credentials for this account, that validation is successful (that takes place before this code). The container places me into the correct node and the account credentials are a match. Which is why this error puzzles me. Protocol version is correct as well. I've set the verifycert callback to return true regardless of the certificate.
The error is being thrown on the first line, if I remove the container it creates the connection, but then hangs indefinitely when I call .Bind(). If I specify the domain after the connection is made, the "unavailable error" is thrown on .Bind(). I don't understand why it fails with the container because it works when passed in with a PrincipalContext.
Thank you for any help.

Silverlight: Preventing authentication window / login prompt while connecting SharePoint with different credentials using DataContext (REST)

I'm programming a Silverlight client to consume a list in Sharepoint 2010 using REST. It's supposed to work as a gadget on users Windows desktop.
My requirement is, logging into Sharepoint with specific credentials instead of current logged user. It works fine with the source code I pasted down and I'm able to fetch the list content as expected, however, everytime I run the software, Windows shows a login box - authentication window to user before estabilishing a connection to Sharepoint.
If user skips it by clicking "cancel" the rest of software works normally.
So I need to prevent this login box.
ObservableCollection<ShoutBoxItem> allItems = new ObservableCollection<ShoutBoxItem>();
ShoutsProxy.TwitterDataContext context = new TwitterDataContext(new Uri(webServiceUrl));
context.HttpStack = HttpStack.ClientHttp;
context.Credentials = new System.Net.NetworkCredential(username, password, domain);
context.UseDefaultCredentials = false;
DataServiceQuery<ShoutBoxItem> query = DataServiceQuery<ShoutBoxItem>)context.ShoutBox;
query.BeginExecute(onGetShoutBoxItemsComplete, query);
So at exactly "query.beginexecute" line, a login box comes up immediately.
Any suggestions?
Greetings from Duisburg,
Alper Barkmaz
Well, I have found a workaround for this.
Instead of filling out a NetworkCredential object with login information, I post username and password at web service URL, http://username:password#domain.com/service.svc
And voila, there's no authentication prompt. The point was, my silverlight application was hosted in local html file with address starting with file://, thus transferring network credentials to target domain was having problems. So in this case, instead of dealing this inside Silverlight, I directly modified the URL and the result was brilliant.
Security: I believe httpclient breaks in, does authentication and removes the login information from URL, so the login information is not transferred over network as plain text. However, double checking this is better.
The new form of solution is,
ObservableCollection<ShoutBoxItem> allItems = new ObservableCollection<ShoutBoxItem>();
ShoutsProxy.TwitterDataContext context = new TwitterDataContext(new Uri("http://username:password#domain.com/service.svc"));
context.HttpStack = HttpStack.ClientHttp;
context.Credentials = new System.Net.NetworkCredential();
context.UseDefaultCredentials = false;
DataServiceQuery<ShoutBoxItem> query = DataServiceQuery<ShoutBoxItem>)context.ShoutBox;
query.BeginExecute(onGetShoutBoxItemsComplete, query);

Exchange Server Login Credentials Setup

I just want to pass this by you and make sure that I am doing this right. My code:
ExchangeService _mailService = new ExchangeService(ExchangeVersion.Exchange2007_SP1);
_mailService.Credentials = new System.Net.NetworkCredential(#"userName", #"password", #"mydomainInfo");
//_mailService.UseDefaultCredentials = true;
_mailService.Url = new Uri("https://webmail.mydomain.com/ews/exchange.asmx");
try
{
ItemView allItems = new ItemView(100);
SearchFilter searchFilterInbox = new SearchFilter.IsEqualTo(EmailMessageSchema.IsRead, false);
Folder _inbox = Folder.Bind(_mailService, WellKnownFolderName.Inbox);
// <SNIP>
So I get this far and the Bind returns an Authentication Fail (401). If I comment out the '.Credentials' and uncomment '.UseDefaultCredentials', I log in just fine. But I need to access a different inbox folder. I pulled the UserName and Domain Name from my account on Outlook. Even if I use my own login information, I still get this error. To me that says I'm just not providing the right information. Thoughts? I can log into the web side of the service just fine.
Well I solved my own problem. It was what I was suspecting. I just didn't have the correct data in the "domain" and "username" fields. I was actually calling the server the Exchange Server was on. So I changed it to the domain that my computer is logged into and changed the user id from "me#email.com" to "me" and it works! So I had it all setup right, just wrong information.

Categories