Setting Server Bindings of IIS 6.0 Programmatically - c#

I'm trying to set up an installer to register a web site. Currently, I've got it creating an Application Pool and Web Site under Windows Server 2003. Unfortunately, whenever I try to modify the ServerBindings property to set the IP Address, it throws an exception at me. I first tried this because the documentation here told me to http://msdn.microsoft.com/en-us/library/ms525712%28VS.90%29.aspx. I'm currently using VB.NET, but C# answers are okay too as I need to switch it over to using C# anyway.
siteRootDE.Properties.Item("ServerBindings").Item(0) = "<address>"
This throws an ArgumentOutOfRangeException. I checked it, and server bindings is of size 0. When I tried to create a new entry in the list like this:
siteRootDE.Properties.Item("ServerBindings").Add("<address>")
I get a COMException when I try that.
I looked at the registered property keys, and ServerBindings is nowhere to be found. However, when I create the Web Site through IIS, it generates ServerBindings correctly and I can see it.
What do I need to do to get ServerBindings to appear?
EDIT: I moved the code over to C# and tried it. It seems for some reason, VB.NET crashes when given either the above, but C# doesn't. But that code still doesn't seem to do anything. It just silently fails. I'm trying it like this:
// WebPage is the folder where I created the website
DirectoryEntry siteRootDE = new DirectoryRoot("IIS://LocalHost/W3SVC/WebPage");
// www.mydomain.com is one of the IP addresses that shows up
// when I used the IIS administrative program
siteRootDE.Properties["ServerBindings"].Value = ":80:www.mydomain.com";
siteRootDE.CommitChanges();

In C# you should be able to do this:
webSite.Invoke("Put", "ServerBindings", ":80:www.mydomain.com");
or
webSite.Properties["ServerBindings"].Value = ":80:www.mydomain.com";
EDIT:
Here is the sample code I used.
public static void CreateNewWebSite(string siteID, string hostname)
{
DirectoryEntry webService = new DirectoryEntry("IIS://LOCALHOST/W3SVC");
DirectoryEntry website = new DirectoryEntry();
website = webService.Children.Add(siteID, "IIsWebServer");
website.CommitChanges();
website.Invoke("Put", "ServerBindings", ":80:" + hostname);
// Or website.Properties["ServerBindings"].Value = ":80:" + hostname;
website.Properties["ServerState"].Value = 2;
website.Properties["ServerComment"].Value = hostname;
website.CommitChanges();
DirectoryEntry rootDir = website.Children.Add("ROOT", "IIsWebVirtualDir");
rootDir.CommitChanges();
rootDir.Properties["AppIsolated"].Value = 2;
rootDir.Properties["Path"].Value = #"C:\Inetpub\wwwroot\MyRootDir";
rootDir.Properties["AuthFlags"].Value = 5;
rootDir.Properties["AccessFlags"].Value = 513;
rootDir.CommitChanges();
website.CommitChanges();
webService.CommitChanges();
}
Also, here is a good article for reference.

Related

Querying an Active Directory object property from ASP.NET application returns old results

For quite a few days now I have been trying to get some custom Active Directory based authentication to work. It all works in theory but apparently my theory is wrong. Users who are logged on to a domain write a string token (e.g. PIN code) to their own property field in Active Directory (doesn't really matter which one, but I used primaryInternationISDNNumber for this) upon logging on to the ASP.NET application This PIN is always generated and written programmatically.
To explain it roughly, the web browser loads a Java applet which then loads a native DLL written in C++, which generates and writes the PIN to current user's Active Directory field. That DLL then returns the generated PIN to the applet which then passes it to the browser, which performs an AJAX call with the data returned to initiate the authentication. The application, which has got access to the AD, reads this field value for the connecting user object and checks if it matches with the one the user supplied. If PIN codes match, the user is successfully authenticated.
This is the sample code the ASP.NET application used to read the AD:
using (var de = new DirectoryEntry("LDAP://" + domainName))
{
using (var adSearch = new DirectorySearcher(de))
{
// Get user from active directory.
adSearch.Filter = "(sAMAccountName=" + userName.Trim().ToLower(CultureInfo.CurrentCulture) + ")";
var adSearchResult = adSearch.FindOne();
var entry = adSearchResult.GetDirectoryEntry();
var pinCodeProp = entry.Properties["primaryInternationISDNNumber"];
return pinCodeProp != null ? pinCodeProp.Value : string.Empty;
}
}
This works fine, often. But often is not acceptable. It needs to always be working.
The trouble is that the ASP.NET application sometimes gets the value which was previously in that field, not the actual value. As if there is some kind of caching. I have tried to add de.UsePropertyCache = false but that yielded the same results.
I have created two Win32 console applications for test purposes. One writes the PIN code, the other reads the PIN code. They always work fine!
I thought, this gotta be some problem with IIS application pool. So I have created a native DLL which gets loaded by the ASP.NET application using Platform Invoke. This DLL creates a new thread, calls CoInitialize and reads the PIN code. This is the code:
pszFqdn = argv[1];
pszUserName = argv[2];
pszPassword = argv[3];
IADs *pObject = NULL;
HRESULT hr = S_OK;
hr = CoInitialize(NULL);
if (SUCCEEDED(hr))
{
hr = ADsOpenObject(pszFqdn, pszUserName, pszPassword, ADS_SECURE_AUTHENTICATION, IID_IADs, (LPVOID*)&pObject);
if (SUCCEEDED(hr) && pObject)
{
VARIANT var;
VariantInit(&var);
hr = pObject->Get(CComBSTR("primaryInternationalISDNNumber"), &var);
if ((SUCCEEDED(hr) && var.bstrVal) || hr == 0x8000500d)
{
if (hr != 0x8000500d)
{
// convert the BSTR received to TCHAR array
std::wstring wsValue(var.bstrVal, SysStringLen(var.bstrVal));
// copy the received value to somewhere
// ... not relevant
}
VariantClear(&var);
}
pObject->Release();
}
}
CoUninitialize();
To my tremendous and unpleasant surprise, this code after a day of working properly, started returning the previous values, just like the managed code before!
So now I thought, alright, I wasn't able to escape the IIS application pool and since this gotta be a problem with IIS application pool, I will create a native Windows application which I will execute by using Process.Start method. I will return my PIN code by means of process exit code (since it's an integer anyway). The application uses the similar C++ code as the DLL above.
So I start my application, wait for it to finish, read the exit code. Returns the bad value!
But okay, I'd say, the process is started using the current user credentials, which is again IIS application pool. So I start the application under different credentials. And guess what..... it returns the old value again (?!?!?!).
And I thought Java was hell... Anyone has got any idea about what could possibly be going on here?
It was the replication indeed. As I didn't want to force the replication before reading the field (that would have been a time-expensive operation probably anyway), I came to an idea to read this field from each domain controller and check if any of them matches with the value supplied.
As it might prove helpful to someone, I did that using the following code.
var ctx = new DirectoryContext(
DirectoryContextType.DirectoryServer,
ipAddress,
userName, // in the form DOMAIN\UserName or else it would fail for a remote directory server
password);
var domain = Domain.GetDomain(ctx);
var values = new List<string>();
foreach (DomainController dc in domain.DomainControllers)
{
using (var entry =
new DirectoryEntry(
"LDAP://" + dc.IPAddress,
userName,
password))
{
using (var search = new DirectorySearcher(entry))
{
search.Filter = "(&(primaryInternationalISDNNumber=*)(sAMaccountName=" + userName + "))";
var result = search.FindOne();
var de = result.GetDirectoryEntry();
if (de.Properties["primaryInternationalISDNNumber"].Value != null)
{
values.Add(de.Properties["primaryInternationalISDNNumber"].Value.ToString());
}
}
}
}

Getting strange error code when programmatically adding a machine to a domain using C#

I am trying to add a Windows machine (server 2008 R2) to a domain programmatically using C#. I know I have the correct permissions to add the machine to the domain because I am able to add it manually through the windows UI. I also know that my ManagementScope is correct because when I create it I am able to query any WMI object that I want. I am trying to connect as follows:
ManagementClass computerSystem = new ManagementClass(scope, new ManagementPath("Win32_ComputerSystem"), new ObjectGetOptions());
ManagementObjectCollection computerSystemInstances = computerSystem.GetInstances();
ManagementObject baseObject = computerSystemInstances.ToList<ManagementObject>().First();
ManagementBaseObject inParams = baseObject.GetMethodParameters("JoinDomainOrWorkgroup");
inParams["Name"] = "my.domain.com";
inParams["Password"] = domainCredentials.FullUserName;
inParams["UserName"] = domainCredentials.Password;
inParams["FJoinOptions"] = 1;
var joinParams = baseObject.InvokeMethod("JoinDomainOrWorkgroup", inParams, null);
The method invoke does not throw any exceptions, but the error code value found at joinParams.Properties["ReturnValue"].Value is 1312. I can't find any documentation anywhere (even on Microsoft's MSDN page for the method) stating what this error code means. Does anyone know where to find what this error code is for?
In your code, you've transposed your username and password to the wrong variables. This may be the cause?
This might be a system error code, found http://msdn.microsoft.com/en-us/library/ms681385%28v=vs.85%29
ERROR_NO_SUCH_LOGON_SESSION
1312 (0x520)
A specified logon session does not exist. It may already have been terminated.

Issue with Web Application and ISA Authentication

right now this code works by picking up the authenticated user (windows authentication). we are using active directory. however if you are off the network and try to log into this specific application you get redirected to our ISA 2006 server, which then passes login information into the application (in theory, lol)
when I access this app on my phone over 3g its pretty slow, but i dont get an error (the error just being a popup with an ok button, sometimes it says error, sometimes it doesnt). However when I use a faster better computer over the external it does give me this error. Unit testing by my colleagues revealed there to be some sort of recursive loop or some sort which was giving the error. any time you log into the web application while on the network, it picks up windows authentication with no problem and gives no error. I have spent 3 days trying to figure out my bug but cant find it, I hope another few set of eyes can help me track it down (I am using ASP.net 4.0 with C#)
public string IdentifyUser()
{
string retval = string.Empty;
try{
//System.Security.Principal.WindowsPrincipal p = System.Threading.Thread.CurrentPrincipal as System.Security.Principal.WindowsPrincipal;
//string UserIdentityName = p.Identity.Name;
string UserIdentityName = Request.ServerVariables["AUTH_USER"];
//string UserIdentityName = HttpContext.Current.User.Identity.Name.ToString();
string slash = #"\";
string Username = UserIdentityName.Substring(UserIdentityName.IndexOf(slash) + 1);
retval = Data_Access_Layers.SQL.GetUserID(Username);
}
catch (Exception ex)
{
throw ex;
}
return retval;
}
basically this fires off, pulls the username, it gets sent to "GetUserID" which looks up that username and sends back the user id attached to that person, which is then sent back out to the main page, and is stored in a javascript variable (to be used on other parts of the page)
Is the problem that AUTH_USER is empty?
What is the value of the AUTH_TYPE header when accessing the app from inside and outside the network?

Unable to access ArcGIS ServerObjectManager

I have a C# plugin for ArcGIS, and I'm trying to access ServerObjectManager off of an AGSServerConnection but it's coming out as null.
The block of code is:
string serverMachineName = adfMap.PrimaryMapResourceInstance.DataSource.DataSourceDefinition;
Identity adfIdentity = new Identity("Administrator", "password", "computername");
AGSServerConnection agsServerConnection = new AGSServerConnection(serverMachineName, adfIdentity, true);
IServerObjectManager serverObjectManager = agsServerConnection.ServerObjectManager;
IServerContext serverContext = serverObjectManager.CreateServerContext("TemporaryContext", "MapServer");
serverMachineName is something along the lines of http://computername/arcgis/services.
The Administrator account is an administrator on the system, as well as a member of agsadmin and agsusers (just in case).
The connection line succeeds.
serverObjectManager is null at this point, so the subsequent call to create a server context fails.
Any advice?
Looks like I shouldn't have taken a non-error on the connection for granted. Throwing in a check to see if it was actually connected showed that it really wasn't.

How can I get tokenGroups from active directory on Windows Server 2003?

I'm trying to load tokenGroups from Active Directory but it isn't working once deployed to a Windows Server (2003). I cannot figure out why, since it works fine locally...
Here is my error:
There is no such object on the server.
And here is my code (the sid variable is the current users SecurityIdentifier pulled from HttpContext):
DirectoryEntry userDE = new DirectoryEntry(string.Format("LDAP://<SID={0}>", sid.Value))
userDE.RefreshCache(new[] { "tokenGroups" });
var tokenGroups = userDE.Properties["tokenGroups"] as CollectionBase;
groups = tokenGroups.Cast<byte[]>()
.Select(sid => new SecurityIdentifier(sid, 0)).ToArray();
Any ideas why I would get that error?
UPDATE: The error actually happens on the RefreshCache line
Do you have a valid value for userDE after the constructor call?? Does that user really exist? Or do you need to provide e.g. a server to use in your LDAP path??
The error message No such object on server seems to indicate the user just plain doesn't exist.... (or cannot be found, due to e.g. permissions)
Try this - not sure if that's the problem, but it's worth a try - it should work:
DirectoryEntry userDE = new DirectoryEntry(string.Format("LDAP://<SID={0}>", sid.Value))
userDE.RefreshCache(new string[] { "tokenGroups" });
Try using new string[] instead of just new[].

Categories