C# or Powershell Web Request: Force Kerberos, UseDefaultCredentials - c#

I'd like to pass-through autenticate with a kerberized web application written in GO (the GOKRB5 implementation is used). The server and the client are both running on windows.
It works when I invoke my webrequest in powershell using -UseDefaultCredentials switch from a remote machine, but it fails when i run the command from the machine where the web application is installed.
As it turns out, the GOKRB5 implementation fails, if the first entry of the mechTypes collection in the SPNEGO part of the Auth Header is not of type KRB5.
see: code on github.
My wireshark says that when invoking the web request from the machine where the web application is installed, the first entry is of type NTLMSSP
Simple Protected Negotiation
    negTokenInit
        mechTypes: 4 items
            MechType: 1.3.6.1.4.1.311.2.2.10 (NTLMSSP - Microsoft NTLM Security Support Provider)
...the webapp fails then as expected with error SPNEGO OID of MechToken is not of type KRB5 and returns 401.
Is there a way to send a webrequest using c# or powershell and force Kerberos instead of NTLM?
Setting SPN for NetBiosName (HTTP/MyApp:666), playing with System.Net.WebRequest properties such as ImpersonationLevel, AuthenticationLevel, PreAuthenticate... no success.

Related

How can I make API requests between 2 internal APIs in one Visual Studio solution?

I created an API .NET CORE project and it runs fine as my startup project, and has assigned 5001 as the port e.g. one of the calls is:
https://localhost:5001/api/Values/TESTME
I added a site cors policy in startup like so:
services.AddCors(options =>
{
options.AddPolicy("SiteCors", builder =>
{
builder.AllowAnyHeader();
builder.AllowAnyMethod();
builder.AllowAnyOrigin();
});
});
Now I added another project (which runs on a different local host port) and set it as the startup project - it tries to do a WebRequest:
string url = "https://localhost:5001/api/Values/TESTME";
try
{
WebRequest request = WebRequest.Create(url);
WebResponse response = request.GetResponse();
response.Close();
}
catch (Exception ex)
{
}
which is rejected because No connection could be made because the target machine actively refused it.
I think adding a Firewall rule may be in order but 1) how exactly do I add a firewall rule for this? and 2) would this even work? Would I need both projects to be "running" in VS at the same time?
Make sure you run both in separate instances on separate ports & it should be fine (if you haven't explicitly blocked anything in your firewall).
You could also run one API in IIS Server or self-hosted & the other running in Visual Studio.

Production Application Crashes when accessing X509Certificate2 Key Data

Really hoping you can help me with the following.
I have created an .Net Core Application that is run as a windows service.
I'm trying to open a certificate in order to decrypt the app settings.
The certificate I am using on my local machine and the test server is the same certificate.
The Public key for the certificate in question is an X5092.
I have the following code to read the Certificate from the store to get the values.
public void Start()
{
using (var store = new X509Store(Location))
{
store.Open(OpenFlags.ReadOnly);
var signingCert = store.Certificates.Find(CriteriaType, _thumb, false);
_logger.Warning($"Number of certs found {signingCert.Count}");
var firstCert = signingCert[0];
_logger.Warning($"Subject - {firstCert.Subject}");
Thread.Sleep(1000);
try
{
_logger.Warning($"Private Key - {firstCert.PrivateKey}");
_logger.Warning($"Get RSA Private Key Method {firstCert.GetRSAPrivateKey()}");
}
catch (Exception ex)
{
_logger.Error(ex.ToString());
}
}
}
The application finds the certificate using the ThumbPrint enum, the thumbprint and doesn't care if the certificate is valid or not.
The value of firstCert is the cert I am after.
The "Subject" prints to the log file correctly as well as any of the other props apart from the Public and Private Key props.
My application crashes as soon as I call any of the following
firstCert.PrivateKey
firstCert.GetRSAPrivateKey()
firstCert.PublicKey.{anotherProp}
The crashes happen under the following conditions.
The server is rebooting.
The application is set to automatic start up.
The application is run as a service.
The Logon is Local System Account.
When the application does crash, there are no logs after I try to access key data of the cert.
No exception caught, the application fails to start, I only find this out by looking at the services to which is says stopped.
The application works correctly in the following conditions
When debugging on my local machine.
Running as a service, on automatic start, as the Local System Account on my local machine.
As above but started from the services feature.
On the test server but with a delayed start up.
On the test server but started from the services feature.
The app written is .Net5
My local machine is windows 10 enterprise.
The server is 2012 R2.

Programmatically creating a remote site in IIS7 using Microsoft.Web.Administration: Invalid index. (Exception from HRESULT: 0x80070585)

I'm trying to write a provisioning tool for our web application. I am attempting to create a web site remotely in IIS7.5 using Microsoft.Web.Administration.ServerManager.
CODE:
using (ServerManager serverManager = ServerManager.OpenRemote("MyRemoteHost")) {
serverManager.Sites.Add("Contoso", "http", "*:80:www.contoso.com", #"C:\inetpub\wwwroot\Contoso");
serverManager.CommitChanges();
}
The above code continuously fails with error: Invalid index. (Exception from HRESULT: 0x80070585)
NOTES:
If I run this code locally on the host, I do not encounter the error and the web site is created successfully.
My user has administrative privileges on the remote host.
I am able to perform other operations on the remote host such as reading the listing of sites.
My workstation is running Windows 8
I've managed to successfully execute the above mentioned code against a Server 2012 host. There appears to be some sort of incompatibility when running this code on a Windows 8 client against a Server 2008R2 host.
I'm using Microsoft.Web.Administration.7.0.0.0.
Any help in resolving this issue would be greatly appreciated.
Thank you
I was having the same issue too by using a remote serverManager object connected to my target server on a very large, heavily regulated corporate network.
After some investigation however, it turned out that the website WAS being created on that specific instance of server manager and, despite the error comming back, using the .CommitChanges(); method on that serverManager object would succesfully save the new website.
var serverManager = ServerManager.OpenRemote("ServerName");
var sites = string.Join(";", serverManager.Sites.Select(o => o.ToString()));
Console.WriteLine(sites);
try
{
serverManager.Sites.Add("newWebSite", #"D:\TestSite", 80);
}
catch ( Exception e )
{
Console.WriteLine(e);
}
Console.WriteLine(string.Join(";", serverManager.Sites.Select(o => o.ToString())));
Console.WriteLine(string.Join(";", ServerManager.OpenRemote("ServerName").Sites.Select(o => o.ToString())));
serverManager.CommitChanges();
Console.WriteLine(string.Join(";", ServerManager.OpenRemote("ServerName").Sites.Select(o => o.ToString())));
Console.ReadLine();
Using this sample code, the catch was not hit, indicating that the program still worked and I can clearly see the website added to the remote server in both the original instance of the serverManager and the new one I created.
The Console returned this:
CopyOfLive;Default Web Site
CopyOfLive;Default Web Site;newWebSite
CopyOfLive;Default Web Site
CopyOfLive;Default Web Site;newWebSite
Furthermore, checking IIS on the server proved that the website had been created.
I hope this helps!

Control IIS 7 server from Windows 2003 server programmatically

We run various jobs using a Windows 2003 server. Some of these jobs send app pool commands to web servers running IIS 6 (recycle, start, stop). Now we have a Windows 2008 web server running IIS 7, and we want to send the same commands. This is all done using C#.
This is the code we use to send commands for IIS 6:
var methodToInvoke = "Stop"; // could be "Stop", "Start", or "Recycle"
var co = new ConnectionOptions
{
Impersonation = ImpersonationLevel.Impersonate,
Authentication = AuthenticationLevel.PacketPrivacy
};
var objPath = string.Format("IISApplicationPool.Name='W3SVC/AppPools/{0}'", appPoolName);
var scope = new ManagementScope(string.Format(#"\\{0}\root\MicrosoftIISV2", machineName), co);
using (var mc = new ManagementObject(objPath))
{
mc.Scope = scope;
mc.InvokeMethod(methodToInvoke, null, null);
}
This code doesn't work for IIS 7 due to underlying changes, so we're currently trying this:
using (ServerManager serverManager = ServerManager.OpenRemote(machineName))
{
var appPool = serverManager.ApplicationPools[appPoolName];
if (appPool != null)
{
appPool.Stop(); // or app.Start() or app.Recycle()
serverManager.CommitChanges();
}
}
The above code works fine on my workstation, which runs Windows 7 (and, thus, IIS 7.5). However, it does not work when I deploy this code to our application server. It get this error:
System.InvalidCastException:
Unable to cast COM object of type 'System.__ComObject' to interface type
'Microsoft.Web.Administration.Interop.IAppHostWritableAdminManager'.
This operation failed because the QueryInterface call on the COM component for the
interface with IID '{FA7660F6-7B3F-4237-A8BF-ED0AD0DCBBD9}' failed due to the following error:
Interface not registered (Exception from HRESULT: 0x80040155).
From my research, this is due to the fact that IIS 7 is not available on the Windows Server 2003 server. (I did include the Microsoft.Web.Administration.dll file.)
So my questions are:
Is it possible for the above code for IIS 7 to work at all from a Windows 2003 server?
If no to #1, is there a better way of doing this?
From reading around it doesn't appear to be possible to do what you're looking for. It's not enough to include the dll files.
According to http://forums.iis.net/t/1149274.aspx..
In order to use Microsoft.Web.Administration you need to have IIS installed, at the bare minimum you need to install the Configuration API's which are brought through installing the Management Tools.
Unfortunately there is no SDK that enables this and it has several dependencies on other components that wouldn't let you just take it to another machine and make it work (such as COM objects, DLL's, etc).
I'd be interested in knowing if you've found a way round this.
Thanks
Try controlling the IIS pool with DirectoryEntry instead.
See this topic:
Check the status of an application pool (IIS 6) with C#
Microsoft.Web.Administration, it relies on System.Web.dll which was provided by framework 4, not client profile.

"Access Denied" when trying to connect to remote IIS server - C#

I receive an "Access Deined" COMException when I try to connect to a remote IIS 6 server from my C# application that is running under IIS 5.1.
Any ideas? I am experiencing all the same issues with the original questions.
Update - 4/1/09
I found this solution (http://www.codeproject.com/KB/cs/Start_Stop_IIS_Website.aspx) that consists of a window application connecting to an IIS server to start and stop web sites. I am able to run it on my workstation and connect to the IIS server.
Ugh....why can I run this stand alone application but not my ASP.NET application?
Original
I receive an "Access Denied" COMException when I try to connect to IIS from a remote machine using the DirectoryEntry.Exist method to check to see if the IIS server is valid.
string path = string.Format("IIS://{0}/W3SVC", server);
if(DirectoryEntry.Exist(path))
{
//do something is valid....
}
I am a member of an active directory group that has been added to the Administrators groups to the IIS server I am trying to connect to.
Has anyone experience this issue and know how to resolve it?
UPDATE:
#Kev - It is an ASP.NET application. Also, I can connect without an username and password to the remote server through IIS6 Manager.
#Chris - I am trying to connect to the remote server to display the number of virtual directorys and determine the .NET framework version of each directory. See this SO question.
#dautzenb - My ASP.NET application is running under IIS 5.1 trying to connect to an IIS 6 server. I can see fault audits in the security log for my local ASPNET account on the remote server. When I try to debug the application, I am running under my domain account and still get the Access is denied.
UPDATE 2:
#Kev - I was able to establish to create a DirectoryEntry object using the following overload:
public DirectoryEntry
(
string path,
string username,
string password
)
But, all of the properties contain a " threw an exception of type 'System.Runtime.InteropServices.COMException'" while I debug the app.
Also, the AuthenticationType property is set to Secure.
UPDATE 3:
The following two failure audit entries were in the remote IIS server's security event log every time I tried to establish a connection:
First event:
Event Category: Account Logon
Event ID: 680
Log attempt by: MICROSOFT_AUTHENTICATION_PACKAGE_V1_0
Logon account: ASPNET
Source Workstation:
Error Code: 0xC0000234
Second event:
Event Category: Logon/Logoff
Event ID: 529
Logon Failure:
Reason: Unknown user name or bad password
User Name: ASPNET
Domain: (MyDomain)
Logon Type: 3
Logon Process: NtLmSsp
Authentication Package: NTLM
Workstation Name: (MyWorkstationId)
Caller User Name: -
Caller Domain: -
Caller Logon ID: -
Caller Process ID: -
Transited Services: -
Source Network Address: 10.12.13.35
Source Port: 1708
Impersonation is set to true and the username and password are blank. It is using the ASPNET account on the remote IIS server.
If it is an identity problem, you could try setting your IIS 5.1 application to use Integrated Windows Authentication, and then add the following to you web.config on your IIS5.1 web site under system.web to enable impersonation.
<identity impersonate="true"/>
<authentication mode="Windows" />
Since this is an ASP.NET application, it runs in an Application Pool of IIS. This Application Pool runs using a specific user("Local System", "Network Service" or another user).
Does this user have enough rights to connect to a remote server ?
See MSDN for more info.
This looks like it may be a double-hop issue. If you are impersonating the current user of a website using NTLM, that impersonation is only valid on that server (your IIS 5.1 server in this case). If you try to connect to another server using the web site, you are actually going to have issues as it cannot pass the token to another server that was used during impersonation. The same is true if you are debugging your site through your machine, going to another box. Your local machine is authenticating you, but it cannot impersonate you to another server.
All of the solutions I have used in the past require you to hard code the app pool to use an account that has permissions, set the annony. account to a domain account with permissions on the other machine, or use a windows service running on the IIS 5.1 machine, under a domain account, to connect to the other server.
If you are using Kerberos, this wouldn't apply, but AD uses NTLM by default.
Where exactly are you trying to read too? Is it in under the same path as your application?
When I had this problem, I found that simply authenticating my self on a Windows file share solved the problem. From experience, I think that WMI/ADSI/COM doesn't have great support for not-already-authenticated users. I believe this issue occurs when you're not associated with a Windows domain.
If it is indeed a NTLM doublehop issue you could use the SETSPN utility to create service principal named instances for your target IIS servers.
Then you could go into Active Directory, and then allow the computer object (basically the NETWORK SERVICE or LOCAL SERVICE principals) to delegate its credentials to a correctly registered SPN.
Then you could hop-hop-hop all over the place! But, be warned! People can hurt themselves on sharp pointy things when you enable double-hop!
Good KB articles to read:
http://support.microsoft.com/kb/929650
I believe that DirectoryEntry.Exists silently ignores any credentials supplied and uses the creds of the authenticated user. This seems to match the behaviour you've described. For AD work, we never use it for this reason.
I'm sort of stumped at the moment as to why you can't get this working. There is a temporary work around you could try. When instantiating the DirectoryEntry object you could use one of the following constructor overloads:
public DirectoryEntry(
string path,
string username,
string password
)
Documented at: MSDN: DirectoryEntry Constructor (String, String, String)
...or...
public DirectoryEntry(
string path,
string username,
string password,
AuthenticationTypes authenticationType
)
Documented at: MSDN: DirectoryEntry Constructor (String, String, String, AuthenticationTypes)
As it happens I'm building a test AD environment on my virtual server box for a new project to do similar stuff. When I get it up and running I'll have a play around to see if I can reproduce the problem you're encountering. In the meantime let us know what happens if you try these constructor overloads referenced above.
Update (In answer to Michaels comment):
For reasons that evade me just now, we couldn't use DirectoryEntry.Exists() in a particular scenario, there is this snippet of code that gets called now and again in one of our apps:
public static bool MetabasePathExists(string metabasePath)
{
try
{
using(DirectoryEntry site = new DirectoryEntry(metabasePath))
{
if(site.Name != String.Empty)
{
return true;
}
return false;
}
}
catch(COMException ex)
{
if(ex.Message.StartsWith("The system cannot find the path specified"))
{
return false;
}
LogError(ex, String.Format("metabasePath={0}", metabasePath));
throw;
}
catch(Exception ex)
{
LogError(ex, String.Format("metabasePath={0}", metabasePath));
throw;
}
}
You could replace the constructor with one of the ones from above. Admittedly it's a stab in the dark :).

Categories