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?
Related
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());
}
}
}
}
I have a C# WPF applicaiton that I am trying to perform a light check with the Active Directory Server and am running into serious performance issues of 20-30 seconds for the funciton to run. Using the identical code, I can place it in a Winforms application and it takes about 1 second or less. Since it is a big AD, I am guessing it is pulling all properties for the end user, but I really only want the first and last name of the person (for other purposes), and to ensure the user is in Active Directory.
Here is the code:
public string DomainUserNameGet(string ActiveDirectoryServer, string WindowsUserID) {
/// queries AD to get logged on user's name
string results = "";
try {
// create your domain context
PrincipalContext oPrincipalContext = new PrincipalContext(
ContextType.Domain
, ActiveDirectoryServer
);
UserPrincipal oUserPrincipal = UserPrincipal.FindByIdentity(
oPrincipalContext
, IdentityType.SamAccountName
, ActiveDirectoryServer + #"\" + WindowsUserID
);
results =
oUserPrincipal.GivenName.ToString()
+ " "
+ oUserPrincipal.Surname.ToString();
} catch { }
return results;
}
The crazy thing is I can do the following via command line and get the response in about 1 second:
NET USER /DOMAIN LANID | find "LANID" /c
Any ideas on how I can improve performance?
RenniePet had it right; Turns out there was a DNS issue; I am unsure why this showed up in WPF vs. win forms though.
Recently two users in our system started getting this error when trying to add them to a role.
System.Runtime.InteropServices.COMException: Cannot create a file when that file already exists. (Exception from HRESULT: 0x800700B7)
What is interesting is that the same error occurs regardless of the configuration, running locally we use an XML store and in the test environment it uses SQL Server.
Here is code where is blows up - AddMemberName() - as you can see this is pretty straightforward stuff and it's worked well for a while, it's just these two users all of the sudden
public void AddUserToRole(string roleName, string userName, bool upn)
{
string uName = userName;
if (upn)
uName = getAltUserNames(userName).First();
AzAuthorizationStore store = new AzAuthorizationStoreClass();
store.Initialize(2, _provider.StoreLocation, null);
IAzApplication app = store.OpenApplication(_provider.ApplicationName, null);
IAzRole role = app.OpenRole(roleName, null);
role.AddMemberName(uName, null);
role.Submit(0, null);
Marshal.FinalReleaseComObject(role);
}
I tried googling various different terms but can't find much of anything regarding this. I did read this post but it doesn't seem to be the issue.
Thanks
Check you Active Directory usernames and the underlying OU name especially. Check for duplicates and mismatches.
I had an issue once where a user got married and her name changed.
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.
I have a windows service which runs under system account and executes some programs from time to time (yeah,yeah, I know that's a bad practice, but that's not my decision). I need to set the "interact with desktop" check, to see the gui of that executed programs, after the service is installed. I've tried several ways, putting the code below in AfterInstall or OnCommited event handlers of my service installer:
ConnectionOptions coOptions = new ConnectionOptions();
coOptions.Impersonation = ImpersonationLevel.Impersonate;
ManagementScope mgmtScope = new System.Management.ManagementScope(#"root\CIMV2", coOptions);
mgmtScope.Connect();
ManagementObject wmiService = new ManagementObject("Win32_Service.Name='" + ServiceMonitorInstaller.ServiceName + "'");
ManagementBaseObject InParam = wmiService.GetMethodParameters("Change");
InParam["DesktopInteract"] = true;
ManagementBaseObject OutParam = wmiService.InvokeMethod("Change", InParam, null);
or
RegistryKey ckey = Registry.LocalMachine.OpenSubKey(
#"SYSTEM\CurrentControlSet\Services\WindowsService1", true);
if(ckey != null)
{
if(ckey.GetValue("Type") != null)
{
ckey.SetValue("Type", ((int)ckey.GetValue("Type") | 256));
}
}
both of these methods "work". They set the check, but after I start the service it launches the exe - and gui isn't shown! So, if I stop the service, recheck and start it again - bingo! everything starts and is shown. The second way to achieve the result is to reboot - after it the gui is also shown.
So the question is: Is there a correct way to set "interact with desktop" check, so it'll start working without rechecks and reboots?
OS: Windows XP (haven't tried Vista and 7 yet...)
private static void SetInterActWithDeskTop()
{
var service = new System.Management.ManagementObject(
String.Format("WIN32_Service.Name='{0}'", "YourServiceName"));
try
{
var paramList = new object[11];
paramList[5] = true;
service.InvokeMethod("Change", paramList);
}
finally
{
service.Dispose();
}
}
And finally after searching the internet for a week - I've found a great working solution:
http://asprosys.blogspot.com/2009/03/allow-service-to-interact-with-desktop.html
Find the desktop to launch into. This
may seem facetious but it isn't as
simple as it seems. With Terminal
Services and Fast User Switching there
can be multiple interactive users
logged on to the computer at the same
time. If you want the user that is
currently sitting at the physical
console then you're in luck, the
Terminal Services API call
WTSGetActiveConsoleSessionId will get
you the session ID you need. If your
needs are more complex (i.e. you need
to interact with a specific user on a
TS server or you need the name of the
window station in a non-interactive
session) you'll need to enumerate the
Terminal Server sessions with
WTSEnumerateSessions and check the
session for the information you need
with WTSGetSessionInformation.
Now you know what session you need to
interact with and you have its ID.
This is the key to the whole process,
using WTSQueryUserToken and the
session ID you can now retrieve the
token of the user logged on to the
target session. This completely
mitigates the security problem of the
'interact with the desktop' setting,
the launched process will not be
running with the LOCAL SYSTEM
credentials but with the same
credentials as the user that is
already logged on to that session! No
privilege elevation.
Using CreateProcessAsUser and the
token we have retrieved we can launch
the process in the normal way and it
will run in the target session with
the target user's credentials. There
are a couple of caveats, both
lpCurrentDirectory and lpEnvironment
must point to valid values - the
normal default resolution methods for
these parameters don't work for
cross-session launching. You can use
CreateEnvironmentBlock to create a
default environment block for the
target user.
There is source code of the working project attached.
Same as Heisa but with WMI. (code is Powershell, but can be easily ported to C#)
if ($svc = gwmi win32_service|?{$_.name -eq $svcname})
{
try {
$null = $svc.change($svc.displayname,$svc.pathname,16,1,`
"Manual",$false,$svc.startname,$null,$null,$null,$null)
write-host "Change made"
catch { throw "Error: $_" }
} else
{ throw "Service $svcname not installed" }
See MSDN: Service Change() method for param description.