Why does CredentialCache.DefaultCredential contain empty strings for domain, username, and password - iis

Does anyone have any ideas as to why CredentialCache.DefaultCredential would return an ICredential instance with empty strings for domain, username, and password? I'm running a WCF service on IIS 7.5. It works fine on one server but never works on another. I have verified that the IIS application has Windows Authentication enabled....
Here is how it's being used:
string url = string.Format("{0}/departments/finance/_vti_bin/listdata.svc", _IntranetAddress);
var financeDataContext = new FinanceDataContext(new Uri(url))
{
Credentials = CredentialCache.DefaultCredentials
};

I am not sure how it is working in one of your servers? I hope you already read this
http://msdn.microsoft.com/en-us/library/system.net.credentialcache.defaultcredentials.aspx
but it clearly says "The ICredentials instance returned by DefaultCredentials cannot be used to view the user name, password, or domain of the current security context."

The NetworkCredential returned from CredentialCache.DefaultCredential is just a placeholder. If you look at it using the Debugger, you'll see that it's of type SystemNetworkCredential. Internal API check for this type to see if integrated authentication should be used or not. There are other ways to get the current username (like WindowsIdentity.GetCurrent()).
EDIT:
To specify impersonation for a WCF operation, add this attribute to the method implementing a contract:
[OperationBehavior(Impersonation = ImpersonationOption.Required)]
public void SomeMethod()
{
// do something here
}

Related

Logout on ServiceStack v4

I have ServiceStack v4 service but when I call the auth/logout route (using either POST or GET) to logout the currently logged-in user, I get an error:
400 Not Empty
User Name cannot be empty
Password Cannot be empty
As I wouldn't expect users to enter credentials when logging out, I am surely missing something?
I have the AuthFeature registered during host initialisation, and I am using CredentialsAuthProvider. I have taken the code from Github so I can see how it works.
My Client Code:
var rest = Restangular.one('auth/logout').get();
//var result = rest.post({userName: userName});
this.requestTracker.addPromise(rest);
return rest;
After a lot of digging, this happens when you are using CredentialsAuthProvider. Within this class, a validator is defined that validates all instances of the Authenticate request. As the logout route uses the Authenticate request, this validator is fired.
I got round it by modifying the validator to:
RuleFor(x => x.UserName).NotEmpty().When(d => d.provider != "logout");
RuleFor(x => x.Password).NotEmpty().When(d => d.provider != "logout");
This is probably not the most elegant way of fixing long term, but got me up and running.
I know this question is old, but I recently have been struggling with the same thing. What occurs is that before the Authenticate.Post function is called, the validation cache is checked and the CredentialsAuthProvider which has the mentioned validator fails unless username and password are not empty.
Now, i'm not sure if it makes a difference if you only have that provider enabled or not - I've not tested. I actually have my own custom provider that subclasses CredentialsAuthProvider and it's the only one I register.
The only way currently is to either pass a non-empty (but useless) password and username, or modify your own custom provider, overriding the Authenticate function and using a modified version of the validator as mentioned above.

C# LDAP Authentication works for one DC, but not another

I have an interesting issue I've been trying to resolve for a few days.
I'm currently working with an Windows Server 2003 machine that is running a standard instance of Active Directory.
The directory contains two domain components (DCs) that both house users that are going to be authorizing against the directory, via my application.
I'm using :
The IP address of the server as the host name
An SSL connection via port 3269
The GSS Negotiate Auth Mechanism
A BaseDN that is a parentDN of both DC's
The sAMAccountName as the login name
The problem is, I cannot successfully authorize any users from DC1, yet all of the ones who belong to DC2 are completely fine and work great. I get this error on DC1 :
8009030C: LdapErr: DSID-0C09043E, comment: AcceptSecurityContext error, data 0, vece
System.DirectoryServices.Protocols.LdapException: The supplied credential is invalid.
However, using Softerra's LDAP Broswer, I can connect in and authorize the same exact user without any issue, so I know the credentials are correct.
From what I can tell, both of these DC's are configured the same... I've browsed both of them for something, anything that is different... but have found nothing that really stands out.
I posted something months ago about this particular setup, and the code I'm using is in that thread as well.
Set callback for System.DirectoryServices.DirectoryEntry to handle self-signed SSL certificate?
Any help here would be much appreciated.
Thanks!
I was able to get this working, but for the life of me I cannot figure out why this was the case. Basically, this error...
8009030C: LdapErr: DSID-0C09043E, comment: AcceptSecurityContext error, data 0, vece System.DirectoryServices.Protocols.LdapException: The supplied credential is invalid.
...was dead on. The issue was that users logging in under what I called DC2 needed to issue the bind with the domain AND sAMAccountName (Ex. LIB\JSmith), as opposed to DC1, which allowed just the sAMAccountName to be entered.
I figured the best way to make this programmatic was to use the principal binding account to query for the DN of the user. From that DN, using some crafty RegEx, I'm able to capture the domain they inherit from, and issue two separate binds.
SearchResultEntry ResultEntry = userResponse.Entries[0];
//Let's get the root domain of the user now using our DN RegEx and that search result
Regex RegexForBaseDN = new Regex(config.LdapAuth.LdapDnRegex);
Match match = RegexForBaseDN.Match(ResultEntry.DistinguishedName);
string domain = match.Groups[1].Value;
//Try binding the user with their domain\username
try
{
var thisUser = new NetworkCredential{
Domain = domain,
UserName = username,
Password = Pin
};
//If this goes well, we'll continue forward
ldapconn.Bind(thisUser);
}
//If that doesn't work, try biding them with the highest level domain
catch (LdapException ex)
{
if (ex.ErrorCode.Equals(LdapErrorCodes.LDAP_INVALID_CREDENTIALS))
{
var thisUserOnce = new NetworkCredential{
Domain = config.LdapAuth.LdapDomain,
UserName = username,
Password = Pin
};
//If this goes well, we'll continue forward
ldapconn.Bind(thisUserOnce);
}
}
It's not nearly as elegant as I would have wanted, but it does work for this particular scenario.
However, I'm still really interested in why the naming conventions are different depending on which DC the user inherit's from.

WebClient with credentials still not downloading file

I am trying to download files from a website with username/password. You need to pay for a registered account in order to download files - which we have done. I am attempting to pass in the username/password and download a file as follows:
if (docUrl != null)
{
if (!string.IsNullOrEmpty(username) && !string.IsNullOrEmpty(password))
this.WebClientInstance.Credentials = new NetworkCredential(username, password);
fileData = this.WebClientInstance.DownloadData(docUrl);
this.WebClientInstance.Dispose();
isDataDownloaded = true;
}
WebClientInstance is a System.Net.WebClient. I debugged and verified that it is hitting the line to set credentials. Instead of downloading the PDF, I end up with an HTML page that prompts me to log in to get access to the file. I have verified that the username/password is correct. I use the same credentials to scrape the website with WatiN.
Is there something else that I'm supposed to be doing here?
UPDATE
Okay, I've done some sniffing around and found some useful info on this issue. I still haven't gotten it to work, but I think I'm closer. First, you need to create a cookie aware WebClient that extends the WebClient class, as follows:
public class CookiesAwareWebClient : WebClient
{
public CookieContainer CookieContainer { get; private set; }
public CookiesAwareWebClient()
{
this.CookieContainer = new CookieContainer();
}
protected override WebRequest GetWebRequest(Uri address)
{
var webRequest = base.GetWebRequest(address);
if (webRequest is HttpWebRequest)
(webRequest as HttpWebRequest).CookieContainer = this.CookieContainer;
return webRequest;
}
}
Next is to use the WebClient.UploadValues() method to upload the login info to the target website. The full process of authenticating and downloading the target resource is as follows:
using (var webClient = new CookiesAwareWebClient())
{
var postData = new NameValueCollection()
{
{ "userId", username },
{ "password", password }
};
webClient.UploadValues(docUrl, postData);
fileData = webClient.DownloadData(docUrl);
}
I was wrong about the site using forms auth. It is a JSP website and uses a JSESSIONID. I have verified that I am getting a cookie back with what appears to be a valid 32-byte JSESSIONID value.
However, when I call WebClient.DownloadData() it is still only returning the redirected login page. I've tried to fix this by setting the AllowAutoRedirect property on the HttpWebRequest to false, but then it returns 0 bytes.
Is there something else that I need to do so it won't redirect and will take me to the resource once I have authenticated?
(Answered in a question edit. Converted to a community wiki answer. See Question with no answers, but issue solved in the comments (or extended in chat) )
The OP wrote:
Solved. So the problem was between my ears. I was passing in the URL for the secure resource to the .UploadValues() method, knowing that it would redirect to the login page. However, I really needed to pass in the URL from the login form (where it goes upon submitting) - not the login page itself. Once I did that, it worked correctly. I think I'm going to go find a career in food service now.
LINKS
There were already a few questions posted on SO that addressed this issue. I just didn't know what I was looking for at first so I didn't see those... Anywhere here are a couple good resources that I came across when working on this issue:
how to maintaine cookies in between two Url's in asp.net
Trying to get authentication cookie(s) using HttpWebRequest

Redirect loop when liferay integrated with OpenSSO

My softwares are:
Liferay 6.0.6 with Tomocat 6.0.29, OpenSSO 9.5.2_RC1 Build 563 with tomcat 6.0.35, CentOS 6.2 Operating system
Setup:
I have setup both liferay and opensso on the same CenOS machine, making sure that both of its tomcat run on very different port, I have installed and configured OpenSSO with Liferay as per the guidelines availaible on liferay forums
Problem:
when i hit my application URL i get redirected to Opensso login page which is what i want, when i login with proper authentication details it trys to redirect to my application which is exactly how it should behave, however this redirect goes in a loop and i don't see my application dashboard. The conclusion i come to is that the redirect is trying to authenticate in liferay but somehow it does not get what it is looking for and goes back to opensso and this repeats infinitely. I can find similar issues been reported here. Unfortunetly, it did not work.
Later i decided to debug the liferay code and i put a break point on com.liferay.portal.servlet.filters.sso.opensso.OpenSSOUtil and com.liferay.portal.servlet.filters.sso.opensso.OpenSSOFilter. The way i understand this code is written is it first goes to the OpenSSOUtil.processFilter() method which get's the openSSO setting information that i have configured on liferay and later checks if it is authenticated by calling the method OpenSSOUtil.isAuthenticated(). This particular implementation basically reads the cookie information sent and tries to set the cookie property on liferay by calling the method OpenSSOUtil._setCookieProperty(). This is where it fails, it tries to read the cookie with name [iPlanetDirectoryPro] from the liferay class com.liferay.util.CookieUtil using the HttpServletRequest object but all it get's a NULL. this value set's the authenticate status to false and hence the loop executes.
Following is the code from class com.liferay.util.CookieUtil
public static String get(HttpServletRequest request, String name) {
Cookie[] cookies = request.getCookies();
if (cookies == null) {
return null;
}
for (int i = 0; i < cookies.length; i++) {
Cookie cookie = cookies;
String cookieName = GetterUtil.getString(cookie.getName());
if (cookieName.equalsIgnoreCase(name)) {
return cookie.getValue();
}
}
return null;
}
Can anyone please let me know why liferay is not able to find the cookie that opensso sent. If its related to Opensso setting about enable cookie value, then i have done that already which is here
In OpenSSO go to: Configuration -> Servers and Sites -> -> Security -> Cookie -> check Encode Cookie Value (set to Yes)
What works:
when this loop is executing i open another tab and login to my application explicitly, from my application when i signout it get's signout from opensso also. This is strange to me.
For more information, while this redirect loop happens, following URL's give me these set of information
http://opensso.ple.com:9090/openam/identity/getCookieNameForToken
string=iPlanetDirectoryPro
http://opensso.ple.com:9090/openam/identity/isTokenValid
boolean=true

How to use a logged in person's credentials to search Active Directory?

I have a web app (.NET 3.5) which is sending notifications by email to users. In order to do this, I search Active Directory to find each person's email.
At the moment, I am hardcoding my own username and password like so in order to search AD:
Dim entry As New DirectoryEntry("LDAP://companyad", "myUsername", "myPassword", AuthenticationTypes.Secure)
Dim srch As New DirectorySearcher(entry)
srch.Filter = [String].Format("(&(objectClass=person)(sAMAccountName={0}))", "someOtherUsername")
Dim result As SearchResult = srch.FindOne()
Now, obviously, this is not ideal and I don't want those credentials hardcoded. My web app is using Windows Authentication. It also uses impersonation (as the logged in user) to access files and SQL Server. Is there also a way for me to "impersonate" the logged in user in order to search AD?
EDIT 1
I thought I'd better explain why I chose this answer. The problem turned out to not be the multi-hop issue or kerberos as it seems I have set these up correctly.
I had recently changed my app to only allow access to a certain group through the web.config settings. I had previously been only allowing access to myself. I set up the group and added myself to it. I then removed the hardcoded credentials and attempted to run the app WITHOUT RESTARTING my computer.
According to my network admin, I would not be logged on under that new group until I restarted my computer which I think is what was causing my problem. So, Preet's answer is actually the most accurate as I just needed to pass the LDAP path to DirectoryEntry.
EDIT 2
I also needed to register a Service Principal Name.
I ran this:
setspn -A HTTP/[dns name of the site] [machine name]
on my development machine.
Thanks to everyone else for their answers.
Doesn't
Dim entry As New DirectoryEntry("LDAP://companyad")
work?
Why not create a new user for this purpose alone? A user with only searching rights.
I set <identity impersonate="true"/> in my web.config and added the following code to my my page load event handler. It worked fine. Are you sure you are not dealing with a multi hop situation? In that case your app pool account needs to be configured for kerberos authentication to support impersonation in a multihop scenario. More info on this is here: http://support.microsoft.com/kb/329986
Response.Write(User.Identity.Name);
DirectoryEntry entry = new DirectoryEntry("LDAP://[mydomain.net]");
DirectorySearcher srch = new DirectorySearcher(entry);
srch.Filter = string.Format("(&(objectClass=person)(sAMAccountName={0}))", "[user]");
SearchResult result = srch.FindOne();
Response.Write(result.Path);
If you wish to use the Windows logged in user account as the credentials against AD, you have to use the following:
public bool IsExistingUser() {
DirectoryEntry de = new DirectoryEntry(Environment.UserDomainName)
DirectorySearcher ds = new DirectorySearcher(de)
ds.Filter = string.Format("((objectClass=user)(SAMAccountName={0}))", Environment.UserName)
try
SearchResult sr = ds.FindOne();
if (sr != null && sr.DirectoryEntry.Name.Contains(Environment.UserName))
return true;
catch (DirectoryServicesCOMException ex)
catch (COMException ex)
throw new Exception("Can't find logged in user in AD", ex);
return false;
}
Assuming this code would compile and run, it will verify whether the existing logged in user is known by your Domain Controller.
Impersonation is discouraged as it lets litteral password strings travel in your network. So, try to avoid it as much as possible.
EDIT Here's a pretty useful link for AD: Howto: (Almost) Everything In Active Directory via C# I found this post awesome!

Resources