ISAPI Filter LDAP Authentication Error on DMZ Server - ssl

I am writing an ISAPI filter for a web server that we have running in a DMZ. This ISAPI filter needs to connect to our internal domain controllers to authenticate against Active Directory. There is a rule in the firewall to allow traffic from the DMZ server to our domain controller on port 636 and the firewall shows that the traffic is passing through just fine. The problem lies in the ldap_connect() function. I am getting an error 0x51 Server Down when attempting to establish the connection. We use the domain controllers IP address instead of the DNS name since the web server's outside the domain.
ISAPI LDAP connection code:
// Set search criteria
strcpy(search, "(sAMAccountName=");
strcat(search, username);
strcat(search, ")");
// Set timeout
time.tv_sec = 30;
time.tv_usec = 30;
// Setup user authentication
AuthId.User = (unsigned char *) username;
AuthId.UserLength = strlen(username);
AuthId.Password = (unsigned char *) password;
AuthId.PasswordLength = strlen(password);
AuthId.Domain = (unsigned char *) domain;
AuthId.DomainLength = strlen(domain);
AuthId.Flags = SEC_WINNT_AUTH_IDENTITY_ANSI;
// Initialize LDAP connection
ldap = ldap_sslinit(servers, LDAP_SSL_PORT, 1);
if (ldap != NULL)
{
// Set LDAP options
ldap_set_option(ldap, LDAP_OPT_PROTOCOL_VERSION, (void *) &version);
ldap_set_option(ldap, LDAP_OPT_SSL, LDAP_OPT_ON);
// Make the connection
//
// FAILS HERE!
//
ldap_response = ldap_connect(ldap, &time);
if (ldap_response == LDAP_SUCCESS)
{
// Bind to LDAP connection
ldap_response = ldap_bind_s(ldap, (PCHAR) AuthId.User, (PCHAR) &AuthId, LDAP_AUTH_NTLM);
}
}
// Unbind LDAP connection if LDAP is established
if (ldap != NULL)
ldap_unbind(ldap);
// Return string
return valid_user;
servers = <DC IP Address>
I have tested this code on my local machine that is within the same domain as AD, and it works, both LDAP and LDAP over SSL. We have a server certificate installed on our domain controller from the Active Directory Enrollment Policy but I read elsewhere that I might need to install a client certificate as well (for our web server). Is this true?
Also, we have a separate wordpress site running on the same DMZ web server that connects to LDAP over SSL just fine. It uses OpenLDAP through PHP to connect and uses the IP address of our domain controllers to connect. We have an ldap.conf file that with a line of code: TLS_REQCERT never. Is there a way to mimic this effect in Visual C with what I'm trying to do for the ISAPI filter? Hoping this is a programming issue more than a certificate issue. If this is out of the realm of programming, please let me know or redirect me to a better place to post this.
Thanks!

Solved the problem by adding the CA to the certificate store on the web server. The CA was never copied over before.

Related

C# Cannot connect to AD using LDAPS

My requirement was to change the user password of AD. So, I created the LDAP SSL secure connection on the AD domain server by following https://bl.ocks.org/magnetikonline/0ccdabfec58eb1929c997d22e7341e45 successfully.
Using the ldp.exe tool (on the same AD server) I am able to connect with the SSL. This means LDAPS is enabled on the AD server.
Now I am trying to connect it from the ASP.NET Core application using the library Novell.Directory.Ldap which is on client-side using the following code:
public LdapConnection GetLDAPConnection(IOptions<ADConfiguration> _settings)
{
LdapConnection connection = new LdapConnection { SecureSocketLayer = true };
connection.Connect(_settings.Value.DomainIPAddress, _settings.Value.Port); //port is 636
connection.Bind(_settings.Value.AdminDn, _settings.Value.Password);
if (connection.Bound)
{
return connection;
}
return null;
}
The Connect method is throwing this error:
System.Security.Authentication.AuthenticationException: 'The remote certificate was rejected by the provided RemoteCertificateValidationCallback.'
Does the client machine also have settings for SSL? Or what else I am missing? Please help
I suspect your problem is using the IP address of the domain controller: _settings.Value.DomainIPAddress
SSL/TLS has two purposes: to encrypt the traffic, and to validate that the server is actually the server you want to be talking to. To address the second purpose, the domain name you use to connect must match the domain name in the certificate. In your case, when it validates the certificate, it sees that you connected to, let's say, 10.0.0.1, but the certificate it gets from the server says it is example.com and the validation fails because it doesn't match.
You will have to either:
Change _settings.Value.DomainIPAddress to the domain name used in the certificate. If you don't have DNS setup for that domain name, you could add an entry in your hosts file.
Tell LdapConnection to ignore certificate errors. The data will still be encrypted, but it won't validate the certificate (domain mismatch, expired cert, etc.). This is not recommended for a production application, but there is an example of that here: https://stackoverflow.com/a/67818854/1202807
Below code worked for me to connect to AD using LDAPS
ldapConnection = new LdapConnection(new LdapDirectoryIdentifier("your.LDAPSserver.com", 636));
var networkCredential = new NetworkCredential("UsernameWithoutDomain", "yourPassword", "AD.yourDOMAIN.com");
ldapConnection.SessionOptions.SecureSocketLayer = true;
ldapConnection.SessionOptions.ProtocolVersion = 3;
ldapConnection.SessionOptions.VerifyServerCertificate = new VerifyServerCertificateCallback(ServerCallback);
ldapConnection.AuthType = AuthType.Negotiate;
ldapConnection.Bind(networkCredential);
SearchRequest Srchrequest = new SearchRequest("CN=Users,DC=AD,DC=YOURCOMPANY,DC=COM", "mail=useremail#company.com", System.DirectoryServices.Protocols.SearchScope.Subtree);
SearchResponse SrchResponse = (SearchResponse)ldapConnection.SendRequest(Srchrequest);
// ServerCallback
private static bool ServerCallback(LdapConnection connection, X509Certificate certificate)
{
return true;
}
Surprisingly it is also working when I am not using networkCredential and just using ldapConnection.Bind(); Seems it is using my local credentials as default on my local machine.

.net core get RemoteIpAddress.MapToIPv4() that is behind cloudflare proxy

I need to extract the user IP address (v4).
I have the following code to do so:
HttpContext.Connection.RemoteIpAddress.MapToIPv4().ToString();
The problem is that in this case I am getting cloud flare ip address.
How can I get the forwarded v4 IP address?
Thanks
Cloudflare passes all HTTP request headers to your origin web server and adds additional headers.
The header:
CF-Connecting-IP
provides the client IP address connecting to Cloudflare to the origin
web server.
You can also use the header:
X-Forwarded-For
which maintains proxy server and original visitor IP addresses.
For more information about the CloudFlare headers you can refer to the documentation
Actually, you can created a method that tries to check all these headers and return the client IP address value.
private string getRemoteIpAddress(HttpContext accessor) {
// try reading the CloudFlare client IP address header
if (!string.IsNullOrEmpty(accessor.Request.Headers["CF-CONNECTING-IP"]))
return accessor.Request.Headers["CF-CONNECTING-IP"];
// try reading the proxy server and original visitor IP addresses header
var ipAddress = accessor.GetServerVariable("HTTP_X_FORWARDED_FOR");
if (!string.IsNullOrEmpty(ipAddress)) {
var addresses = ipAddress.Split(',');
if (addresses.Length != 0) return addresses.Last();
}
// try reading the remote IpAddress without a proxy
return accessor.Connection.RemoteIpAddress.ToString();
}

SSL redirect changes client IP address read from HTTPResponse

I am using Perfect Framework for my server side application running on an AWS EC2 instance. I am using the following code to get client IP address.
open static func someapi(request: HTTPRequest, _ response: HTTPResponse) {
var clientIP = request.remoteAddress.host }
This was working fine until I installed ssl certificate on my EC2 instance and start redirecting incoming traffic to port 443.
Now this code gives me the ip of my server, i think due to the redirect, Perfect somehow think request comes from itself.
Is there any other method to get client IP address? Or do i have to try something else?
Thanks!
For anyone struggling for the same problem, original client ip can be found in one of the header fields called "xForwardedFor" if there is a redirect, like the following:
var clientIP = request.remoteAddress.host
let forwardInfoResut = request.headers.filter { (item) -> Bool in
item.0 == HTTPRequestHeader.Name.xForwardedFor
}
if let forwardInfo = forwardInfoResut.first {
clientIP = forwardInfo.1
}
Hope this helps somebody, cheers!
Perhaps you should ask the people you are paying for support and whom manage the infrastructure how it works before asking us?
The convention, where an http connection is terminated elsewhere than the server is to inject an x-forwarded-for header. If there is already such a header, the intermediate server injects the client IP address at the front of the list.

Client certificate has different thumprint via ARR and AuthorizationContext

I am currently working on a prototype for a WCF service that will make use of client-certificate authentication. We would like to be able to directly publish our application to IIS, but also allow SSL offloading using IIS ARR (Application Request Routing).
After digging through the documentation, I have been able to test both configurations successfully. I am able to retrieve the client certificate used to authenticate from:
X-Arr-ClientCert - the header that contains the certificate when using ARR.
X509CertificateClaimSet - when published directly to IIS, this is how to retrieve the Client Certificate
To verify that the request is allowed, I match the thumbprint of the certificate to the expected thumbprint that is configured somewhere. To my surprise, when getting the certificate through different methods, the same certificate has different thumbprints.
To verify what is going on, I have converted the "RawData" property on both certificates to Base64 and found that it's the same, except that in the case of the X509CertificateClaimSet, there are spaces in the certificate data, while in the case of ARR, there are not. Otherwise, both strings are the same:
My question:
Has anyone else run into this, and can I actually rely on thumbprints? If not, my backup plan is to implement a check on Subject and Issuer, but I am open to other suggestions.
I have included some (simplified) sample code below:
string expectedThumbprint = "...";
if (OperationContext.Current.ServiceSecurityContext == null ||
OperationContext.Current.ServiceSecurityContext.AuthorizationContext.ClaimSets == null ||
OperationContext.Current.ServiceSecurityContext.AuthorizationContext.ClaimSets.Count <= 0)
{
// Claimsets not found, assume that we are reverse proxied by ARR (Application Request Routing). If this is the case, we expect the certificate to be in the X-ARR-CLIENTCERT header
IncomingWebRequestContext request = WebOperationContext.Current.IncomingRequest;
string certBase64 = request.Headers["X-Arr-ClientCert"];
if (certBase64 == null) return false;
byte[] bytes = Convert.FromBase64String(certBase64);
var cert = new System.Security.Cryptography.X509Certificates.X509Certificate2(bytes);
return cert.Thumbprint == expectedThumbprint;
}
// In this case, we are directly published to IIS with Certificate authentication.
else
{
bool correctCertificateFound = false;
foreach (var claimSet in OperationContext.Current.ServiceSecurityContext.AuthorizationContext.ClaimSets)
{
if (!(claimSet is X509CertificateClaimSet)) continue;
var cert = ((X509CertificateClaimSet)claimSet).X509Certificate;
// Match certificate thumbprint to expected value
if (cert.Thumbprint == expectedThumbprint)
{
correctCertificateFound = true;
break;
}
}
return correctCertificateFound;
}
Not sure what your exact scenario is, but I've always liked the Octopus Deploy approach to secure server <-> tentacle (client) communication. It is described in their Octopus Tentacle communication article. They essentially use the SslStream class, self-signed X.509 certificates and trusted thumbprints configured on both server and tentacle.
-Marco-
When setting up the test again for a peer review by colleagues, it appears that my issue has gone away. I'm not sure if I made a mistake (probably) or if rebooting my machine helped, but in any case, the Thumbprint now is reliable over both methods of authentication.

SignalR with Self-Signed SSL and Self-Host

Tried my luck at research, but so far no joy.
I would like to connect a SignalR javascript client to a self-hosted SignalR Windows Service binding to a self-signed SSL certificate.
My application works quite well over http, but the client repetitively disconnects when the Owin WebApplication starts using https.
Here is what I've done to configure SignalR with SSL.
Created a Self-Signed certificate using IIS
Imported the certificate into the Trusted Root Certification Authorities in the mmc (not sure if that helped)
Ran NETSH command to bind SSL to port 8080
netsh http add sslcert ipport=0.0.0.0:8080 certhash=123456f6790a35f4b017b55d09e28f7ebe001bd appid={12345678-db90-4b66-8b01-88f7af2e36bf}
Added code in self-hosted HubConnection instances to add exported SSL like this (though this shouldn't matter because it's the client that cannot connect):
if (File.Exists("MyCert.cer")
&& Settings.GetSetting(Settings.Setting.SrProtocol).Equals("https", StringComparison.InvariantCultureIgnoreCase))
connection.AddClientCertificate(X509Certificate.CreateFromCertFile("MyCert.cer"));
Starting Owin WebApplication using https (this should create the binding in http.sys)
string registerUrl = string.Format("{0}://SOME.WHERE.COM:{1}", Service.Server.SrProtocol, Service.Server.SrPort);
WebApp.Start<StartUp>(registerUrl);
In the SignalR 2.0 documentation, it says:
To start the web server, call WebApplication.Start(endpoint). You should now be able to navigate to endpoint/signalr/hubs in your browser.
When I browse to the URL http://SOME.WHERE.COM:8080/signalr/hubs I am successful receiving the javascript that drives SignalR.
When I browse to the URL https://SOME.WHERE.COM:8080/signalr/hubs I am unsuccessful and I receive "The connection to the server was reset" using FF.
Some additional points I've considered:
NETSH SHOW indicates the url is registered
URL group ID: E300000240000022
State: Active
Request queue name: Request queue is unnamed.
Properties:
Max bandwidth: inherited
Max connections: inherited
Timeouts:
Timeout values inherited
Number of registered URLs: 1
Registered URLs: HTTPS://SOME.WHERE.COM:8080/
NETSH SHOW indicates the SSL certificate is bound to 8080:
IP:port : 0.0.0.0:8080
Certificate Hash : 123456f6790a35f4b017b55d09e28f7ebe001bd
Application ID : {12345678-db90-4b66-8b01-88f7af2e36bf}
Certificate Store Name : (null)
Verify Client Certificate Revocation : Enabled
Verify Revocation Using Cached Client Certificate Only : Disabled
Usage Check : Enabled
Revocation Freshness Time : 0
URL Retrieval Timeout : 0
Ctl Identifier : (null)
Ctl Store Name : (null)
DS Mapper Usage : Disabled
Negotiate Client Certificate : Disabled
Any help is greatly appreciated!
I believe its all working for me now. Here is a run down of the steps I took to get things flowing:
SSL NOTES
SSL & SignalR (Owin WebApplication) requires binding a certificate to a port.
Use IIS to generate an self-signed cert, this should place the certificate into the LOCAL COMPUTER > Personal > Certificates folder in CERTMGR
In CERTMGR shift+drag certificate to LOCAL COMPUTER > Trusted Root Certification Authorities > Certificates folder, which should make a copy of it there
Run the following command to bind the SSL certificate to 0.0.0.0:8080
netsh http add sslcert ipport=0.0.0.0:8080 certhash=123456f6790a35f4b017b55d09e28f7ebe001bd appid={12345678-db90-4b66-8b01-88f7af2e36bf}
netsh http show urlacl > D:\urlacl.txt
Output:
Reserved URL : https://*:8080/
User: SOMEWHERE\Administrator
Listen: Yes
Delegate: No
SDDL: D:(A;;GX;;;S-1-5-21-138209071-46972887-2260295844-1106)
Run the following NETSH command to reserve all IP addresses for port 8080 to the My Service application ID and service account
netsh http add urlacl url=https://*:8080/ user=SOMEWHERE\Administrator listen=yes
netsh http show sslcert > D:\sslcert.txt
Output:
IP:port : 0.0.0.0:8080
Certificate Hash : 123456f6790a35f4b017b55d09e28f7ebe001bd
Application ID : {12345678-db90-4b66-8b01-88f7af2e36bf}
Certificate Store Name : (null)
Verify Client Certificate Revocation : Enabled
Verify Revocation Using Cached Client Certificate Only : Disabled
Usage Check : Enabled
Revocation Freshness Time : 0
URL Retrieval Timeout : 0
Ctl Identifier : (null)
Ctl Store Name : (null)
DS Mapper Usage : Disabled
Negotiate Client Certificate : Disabled
Update the MyServices.exe.config file to use https protocol (These are appSetting keys used to dynamically set the protocol and port of SignalR when My Service starts)
<add key="SrProtocol" value="https" />
<add key="SrPort" value="8080" />
Start the My Service using the NETSTAT START command
Run the following NETSH command to show the service state is occupying the registered url
netsh http show servicestate > D:\servicestate.txt
Output:
Server session ID: C300000320000039
Version: 2.0
State: Active
Properties:
Max bandwidth: 4294967295
Timeouts:
Entity body timeout (secs): 120
Drain entity body timeout (secs): 120
Request queue timeout (secs): 120
Idle connection timeout (secs): 120
Header wait timeout (secs): 120
Minimum send rate (bytes/sec): 150
URL groups:
URL group ID: C600000340000138
State: Active
Request queue name: Request queue is unnamed.
Properties:
Max bandwidth: inherited
Max connections: inherited
Timeouts:
Timeout values inherited
Number of registered URLs: 1
Registered URLs:
HTTPS://*:8080/
My application does NOT depend on IIS, but once I used IIS to temporarily create a port binding to my SSL certificate, my application started to work, and I was able to inspect the NETSH servicestate to see how IIS does it. I have since dropped the IIS binding and ran through the setup notes, and still have success.
My Owing startup looks somethign like this:
private void configureMessaging()
{
string registerUrl = string.Format("{0}://*:{1}", Service.Server.SrProtocol, Service.Server.SrPort);
try
{
#if DEBUG
//System.Diagnostics.Debugger.Launch();
#endif
// Starts an owin web application to host SignalR, using the protocol and port defined.
WebApp.Start<StartUp>(registerUrl);
}
catch (Exception ex)
{
Logger.Logs.Log(string.Format("Failed to configure messaging. Exception: {0}", ex.RecurseInnerException()), LogType.Error);
if (ex is HttpListenerException || ex.InnerException is HttpListenerException)
{
try
{
Process p = new Process();
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.FileName = "netsh.exe";
p.StartInfo.Arguments = string.Format("netsh http delete urlacl url={0}"
, registerUrl
);
p.Start();
p.StandardOutput.ReadToEnd();
p.WaitForExit();
}
catch (Exception exP)
{
Logger.Logs.Log(string.Format("Failed to delete urlacl {0}. Exception: {1}"
, registerUrl
, exP.RecurseInnerException()
)
, LogType.Error
)
;
retries = 5;
}
}
if (retries < 5)
{
retries++;
Logger.Logs.Log(string.Format("Attempting to configure messaging again. Attempt No. {0}", retries), LogType.Warn);
Thread.Sleep(1000);
configureMessaging();
}
else
Logger.Logs.Log(string.Format("Exceeded total number of retries to configure messaging.", retries), LogType.Error);
}
}
And self-hosted HubConnetion instances look like this:
public IHubProxy MyHubProxy
{
get
{
if (this._MyHubProxy == null)
{
var connection = new HubConnection(string.Format("{0}://{1}:{2}/"
, Settings.GetSetting(Settings.Setting.SrProtocol)
, MyHub.GetLocalhostFqdn(null)
, Settings.GetSetting(Settings.Setting.SrPort)
)
)
;
this._MyHubProxy = connection.CreateHubProxy("MyHub");
if (File.Exists("My.cer")
&& Settings.GetSetting(Settings.Setting.SrProtocol).Equals("https", StringComparison.InvariantCultureIgnoreCase))
connection.AddClientCertificate(X509Certificate.CreateFromCertFile("My.cer"));
connection.Start().Wait();
}
return this._MyHubProxy;
}
}
There is a little more code here than relevant, but hopefully it may be of help!