LdapConnection Bind() always fails over SSL - c#

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.

Related

Generated WCF SOAP client uses current user for windows authentication instead of given credentials

I'm kind of new to the whole WCF and SOAP topic so please be kind.
I'm using a generated SOAP Client with .net6. In another project we successfully worked with the same Web Service using the old .net Framework 2.0 Web References and the same credentials.
Strange enough everything seemed to work fine at first. Until I realized, that it does not use the given credentials to authenticate. Instead it authenticates with my own domain user.
I also tried to get it to work with explicitly setting the binding with a BasicHttpBinding but I only could get the same broken logic to work or I got various authentication/protocol/security errors.
So it seems the authentication is basically working. It just doesn't use the provided credentials. So my question is: How can I configure it to work with the provided identity?
I also found out that it might have anything to do with a cached Windows token. But how can I get rid of it. How to prevent caching in the first place?
EDIT:
Specified the variable types explicitly.
string url = "http://someServer/AdministrationService.asmx";
AdministrationServiceSoapClient client = new AdministrationServiceSoapClient(
AdministrationServiceSoapClient.EndpointConfiguration.AdministrationServiceSoap,
url);
WindowsClientCredential credential = client.ClientCredentials.Windows;
credential.ClientCredential.UserName = "username";
credential.ClientCredential.Password = "password";
credential.ClientCredential.Domain = "DOMAIN";
GetServerInfoRequest getServerInfoRequest = new GetServerInfoRequest
{
// some stuff set here
};
GetServerInfoRequest getServerInfoReply = await client.GetServerInfoAsync(getServerInfoRequest);
As far as I know, BasicHttpBinding has security disabled by default, but can be added setting the BasicHttpSecurityMode to a value other than None in the constructor. It can be configured according to the instructions in BasicHttpBinding and BasicHttpBinding Constructors.
By default, setting up client credentials involves two steps: determining the type of client credential required by the service and specifying an actual client credential, as described in this document.
After waiting a day it is working. It seems that the cached credentials became invalid somehow.
Strange enough the simple service creation from above is not working anymore. Instead I have to use the following.
var client = new AdministrationServiceSoapClient(
new BasicHttpBinding()
{
Security = new BasicHttpSecurity()
{
Mode = BasicHttpSecurityMode.TransportCredentialOnly,
Message = new BasicHttpMessageSecurity()
{
ClientCredentialType = BasicHttpMessageCredentialType.UserName,
},
Transport = new HttpTransportSecurity()
{
ClientCredentialType = HttpClientCredentialType.Windows,
ProxyCredentialType = HttpProxyCredentialType.Windows,
}
},
},
new EndpointAddress(url));

LDAP search fails on server, not in Visual Studio

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;

CRM Context Object persists connection even when creating new instance

I have a system in place for a WCF Service, in which I take in some credentials from the client. I then try to authenticate with CRM using these credentials. If the authentication fails, I use a pre-defined service account, with the credentials stored in web.config.
What I have found is, no matter what, the first set of credentials used persists for any further requests, no matter how much I tear down the first object. I even instantiate new objects, wrap each context in a using statement, etc.
I have watered the code down into a simple 'connect, retry' block, and this suffers the same issue. The code is as follows:
try
{
var connection = new CrmConnection();
connection.ServiceUri = new Uri("https://my.crm.dynamics.com/");
connection.ClientCredentials = new ClientCredentials();
connection.ClientCredentials.UserName.UserName = "removed1";
connection.ClientCredentials.UserName.Password = "removed1";
using (var crm = new CrmOrganizationServiceContext(connection))
{
var req = new Microsoft.Crm.Sdk.Messages.WhoAmIRequest();
var resp = (Microsoft.Crm.Sdk.Messages.WhoAmIResponse)crm.Execute(req);
}
}
catch (Exception ex) { }
try
{
var connection = new CrmConnection();
connection.ServiceUri = new Uri("https://my.crm.dynamics.com/");
connection.ClientCredentials = new ClientCredentials();
connection.ClientCredentials.UserName.UserName = "removed2";
connection.ClientCredentials.UserName.Password = "removed2";
using (var crm = new CrmOrganizationServiceContext(connection))
{
var req = new Microsoft.Crm.Sdk.Messages.WhoAmIRequest();
var resp = (Microsoft.Crm.Sdk.Messages.WhoAmIResponse)crm.Execute(req);
}
}
catch (Exception ex) { }
Assume that removed1 is incorrect and removed2 is correct. The second call will fail instantly with a token exception, saying invalid credentials. If removed1 is correct, and removed2 is not, the first will authenticate and get the WhoAmIRequest fine. Then, removed2 should fail, but it does not, as it seems to still hold the connection using the old credentials. The invalid credentials still allows the service to make requests. Not good!
The bizarre thing is, the code for the authentication is in a separate project. I have included this project in a simple console application, and everything works fine. I can only assume this is something to do with the WCF service and the way it holds connections. I've tried manually disposing, calling garbage collection, setting to null, etc. I've also tried using web config connection strings called by name (hard coded 2 test ones), tried manually creating the connection string settings with unique names, using CrmConnection.Parse(), etc.
I have even copy pasted the code i'm using directly into a console application, and it works fine. Due to this, I am convinced it is to do with the behavior of a WCF service, and not the code itself. I set the class to have the behavior of
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
But no luck. If it is of any importance, this code is running in a message inspector class which implements IDispatchMessageInspector.
How can I ensure that I can get a fresh session? Thanks.
You are using the default constructor of the CrmConnection class. When doing that, your connection is cached by name. This name is supposed to be the name of the ConnectionStringSettings, but using this constructor that property is never being set and keeps its default value, thus always returning the first connection object created.
Just use another overload of the constructors, e.g. that using a connection string or accepting the service url, credentials etc.
The CrmConnection class was designed to offer an easy way to create connectionstrings in config files, similar to database connection strings. It had its issues and has been removed from the Dynamics CRM 2016 SDK.

Acumatica Web Services API Login

I am attempting to perform some basic integration using Acumatica's web services. Unfortunatly, I'm having problems logging in. According to their documentation, this process should look something like:
apitest.Screen context = new apitest.Screen();
context.CookieContainer = new System.Net.CookieContainer();
context.AllowAutoRedirect = true;
context.EnableDecompression = true;
context.Timeout = 1000000;
context.Url = "http://localhost/WebAPIVirtual/Soap/APITEST.asmx";
LoginResult result = context.Login("admin", "E618");
Simple enough. However, after creating and importing a WSDL file from Acumatica into Visual Studio, I found I don't have a Screen object. I do, however have a ScreenSoapClient object, which has a similar Login() method.
ScreenSoapClient context = new Acumatica.ScreenSoapClient("ScreenSoap");
LoginResult result = context.Login("username", "password");
That part works. In fact, the LoginResult give me a session ID. However, if I try to make any calls to the service, such as:
CR401000Content cr401000 = context.CR401000GetSchema();
I get an error: System.Web.Services.Protocols.SoapException: Server was unable to process request. ---> PX.Data.PXNotLoggedInException: Error #185: You are not currently logged in.
While the version of Acumatica we're using does appear to be slightly newer, I'm unsure why the Screen() object isn't available. Consequently, if I try a bad username/password, Login() does fail (as it should). From what I can the tell, the ScreenSoapClient class is using service model details from web.config, so it's getting the endpoint address and other details there.
Is there something I'm missing or doing wrong?
As i see, you use WCF to create your service reference.
So you should enable cookies in service binding:
var binding = new BasicHttpBinding()
{
AllowCookies = true
};
var address = new EndpointAddress("http://localhost/WebAPIVirtual/Soap/APITEST.asmx");
var c = new ServiceReference1.ScreenSoapClient(binding, address);
Or, you can use old asmx web service reference (http://msdn.microsoft.com/en-us/library/bb628649.aspx).
Then everything will be same as in Acumatica`s documentation.
As noted in a comment above, I was able to make contact with a representative from Acumatica. He had me remove then recreate the service references in our project and try again. That apparently did the trick and the "Error #185: You are not currently logged in" error went away.

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