A: 

Have you checked out adding impersonation to the web.config? That way your process doesn't run as an anonymous user, but instead runs as an authenticated network account:

<identity impersonate="true" userName="domain\username" password="goes@here"/>

It's how my company handles matters. Conversely, you could setup the app pool to run as a network user.

It seems like you're trying to use the DB to restrict access to functionality. I'm not sure that's the best path. Usually, the app has full access to the db, and then you restrict access using code.

UPDATE: As per your comment, it seems like the previous paragraph is relevant. I've never heard of an architecture where the ASP.NET framework handles impersonation on a per-logged in basis. I've seen systems where user was authenticated through the underlying network structure - aka kerberos authentication performed by IIS / Windows.

As before, traditionally you don't have every single end-user granted unique rights on the db server. The application has a login to the db, and then individual users are managed through the app.

Is there a reason you're not using kerberos and pushing the workload off to IIS?

JustLoren
You have misunderstood the question. I am familiar with this section of `web.config`. Setting impersonate here simply impersonates the `anonymous` account. My code looks up some other account associated with the user's cookie and impersonates that. I would like to make all network requests from the application use the account specified by the cookie.
Rice Flour Cookies
"Is there a reason you're not using Kerberos and pushing the workload off to IIS?" YES, there is a reason. I am using an existing log on infrastructure that we use to log users in to a number of applications. This log on system connects to a mainframe somewhere to validate the logins. My goal is to associate the credentials validated by the mainframe with local Active Directory accounts. IN SHORT, I wish to make it as if the user if logging in with Active Directory credentials, even though the user is not logging in with Active Directory credentials. Does that make sense?
Rice Flour Cookies
+1  A: 

Since Rising Star indicated a switch to Windows authentication isn't an option, what about doing impersonation in code? The one caveat is that you will need to know the users password. In fact, I would say that if you don't have access to the users password, impersonating the user is not possible.

I think the WindowsImpersonation class can provide what you need. An example of how to do this with a specific id and password can be found in MSDN.

Original Answer

Can you switch your application from your current authentication method to Windows authentication? In this case, if you enable impersonation without specifying an id/password, your application will impersonate the logged on user which I believe is what you are looking for.

A very thorough explanation of setting this up can be found here.

Jeff Siver
I absolutely cannot switch to Windows Authentication. I need to use the existing log on system that uses cookies.
Rice Flour Cookies
Right now, I am using impersonation in the code. However, I specifically stated that we wish to not use impersonation. As before, users cannot log in with Windows Authentication, but I want my log on application to identify them and log them in `as if they had used Windows Authentication`
Rice Flour Cookies
Sorry, I missed that part, on impersonation, the second time around. Honestly, at this point, i think your manager is asking for the impossible. You have to use AD security for your app without impersonation and without an AD authentication token available while using a logon system that does not use AD. My current take is that these two requirements contradict either other. Sorry I couldn't help more.
Jeff Siver
A: 

At some point, you're going to have to ask the users for credentials - this might be a one time request (or until the user changes their password anyway).

It sounds like you are trying to do Single Sign On - albeit it usually does the opposite (allow you to use your windows account to pass through non windows account credentials to other apps) but theres no reason you couldnt do the following:

  1. user logs in to app as normal
  2. if user has no windows credentials stored
    1. ask the user for credentials
  3. try to log in as user
  4. if fail login
    1. ask the user for credentials again
  5. store credentials

Once you have the credentials stored in your "SSO" cache you can use them to create new NetworkCredential objects for your network requests (i.e. your SQL connections, File copies etc).

You can read a description here http://aspalliance.com/1545_understanding_single_signon_in_aspnet_20.all

Mauro
This is unacceptable to the powers that be. I have an AD account for the user. I simply can't use Windows Authentication to log them in. I need to make it so that by presenting credentials to the log on system that verifies the user on the mainframe, they get logged in with their AD account.
Rice Flour Cookies
I think you are misunderstanding SSO. With SSO you still log in with your cookie based authentication, however you also store separate credentials which only that cookie based user can access. you then use these to handle the network requests.
Mauro
@Mauro, what you describe is similar to what I'm trying to do now. I have a cookie which contains the AD account I want to execute the SQL query as. My only question is, how do I make the asp.net process run the SQL query as the identity specified by the cookie?
Rice Flour Cookies
+1  A: 

I am still a bit confused with the terminology here. But I think you are in trouble :)

In my opinion/understanding the following two statements already tell you that what you want is impossible:

I've been told that for each page that connects to our SQL server, I need to make it so that the user connects using an Active Directory account.

versus

However, the powers that be here don't like impersonation;

I would say the first statement implies impersonation. I believe this to be a very common requirement by the way. Mainly for exactly the reason you stated: it lets you see who is doing what on your database. It might even be (or become) requested by law... You usually do not have to give permissions to individuals though, I tend to use groups/roles.

Now in your comment to @Mauro you write

I have a cookie which contains the AD account I want to execute the SQL query as. My only question is, how do I make the asp.net process run the SQL query as the identity specified by the cookie?

What do you mean by "contains the AD account"? Just the username or username and password? If you have both you can (must?) follow the MSDN article (WindowsImpersonationContext) mentioned by @Jeff Siver (or is this what you and your management describe as "cluttering the code"?).
If you do not have the password you are out of luck. How would you possibly run something with the privileges of a certain user without providing the system any clue (but a username) that you are allowed to do so? I cannot see at the moment how a cookie could help here. Unless - hmm - maybe you could roll your own Security Principal? That is above me, though.

In case we do not (yet) have the same understanding of some terms: this would be the (or rather my) list of "usual suspects" capable of producing misunderstandings.

  • Windows Authentication: the option in IIS vs. Windows Integrated Authentication in SQL Server
  • Impersonation: the ASP.NET option (as stated by @JustLoren) vs. impersonation in code ("cluttered code"?)
  • Delegation: the passing on of credentials from IIS to SQL Server (for example) vs. triple-hop (passing of credentials from IE (or browser) to IIS to SQL Server; also a sort of delegation)

UPDATE
WIF seems to bring certain complexity, see Figure 7 and Figure 8 in Overview of the Claims Based Architecture which can be found on Windows Identity Foundation Simplifies User Access for Developers. And I have to admit that I do not know WIF. From what I see it might really be able to help you; or it might just "shift the problem"...

BUT from your comment

I have a security principal with the users AD account

I think you might be able to switch the identity of the user "running the web app" (i.e. making the calls to the database) using the mechanism that can also be used to roll your own "security principal-solution".

I am unsure if you then still need the impersonate in Web.config as suggested by @JustLoren. But it might well be.

You would start by replacing the current security principal with "your own" (the one you get through C2WTS) in Global.asaxs Application_AuthenticateRequest-method (hopefully this will still be called if you allow anonymous access?!?). But beware I would say this is by no means trivial.

You can find more information (and warnings) here:

UPDATE2

So.... You're saying that the answer to my question may be to somehow replace the current security principal in this method?

That is what I meant, yes. Here comes some example code (excerpts of something without WIF I know to work).

Custom Principle Class (which you would not need):

using System;
using System.Collections.Generic;
using System.Web;
using System.Security.Principal;

namespace PrincipalTest
{
    [Serializable]
    public class MyPrincipal : WindowsPrincipal
    {
        public MyPrincipal(WindowsIdentity ntIdentity) : base(ntIdentity)
        {
        }

        public bool IsAdmin
        {
            get
            {
                bool isAdmin = (string.Compare(this.Identity.Name,
                        @"<domain>\<user>", StringComparison.OrdinalIgnoreCase)
                        == 0);
                return isAdmin;
            }
        }
    }
}

In Global.asax (you would do your WIF-magic here):

protected void Application_AuthenticateRequest(object sender, EventArgs e)
{
    DotnetReportingPrincipal principal =
            new MyPrincipal(HttpContext.Current.User.Identity
            as WindowsIdentity);

    // Attach the new principal object to the current HttpContext object
    HttpContext.Current.User = principal;
    System.Threading.Thread.CurrentPrincipal
            = System.Web.HttpContext.Current.User;
}

You can then do things like this (again not needed in your case I assume):

protected void Page_Load(object sender, EventArgs e)
{
    IPrincipal current = HttpContext.Current.User;
    if (current is MyPrincipal)
    {
        if (current as DotnetReportingPrincipal).IsAdmin) 
        {
            ...
        }
    }
}

In your situation I would try (hope :)) you can just set ASP.NET to use impersonation (again as suggested by @JustLoren). Hopefully IIS will then pass the "new" user down to SQL Server for querying.

If this is not the case, make sure your configuration allows for delegation. Here would be some hints:

Please remember that I am on very thin ice here. But - who knows...
You might be able to achieve something that has been shown in a Microsoft demo, not bad at all :)
Good luck!

Anotherupdate
Hmm - I am not sure it I like the way things go here ;) I honestly hope I did not point you in the wrong direction...

As you said, there is the WindowsClaimsIdentity class:

there is a class in WIF that I'm not familiar with called WindowsClaimsIdentity which seems to be a hybrid Windows/Claims Identity. I'm trying to find how to set up my impersonation using this class.

But it says:

If client is using Windows authentication, create a WindowsClaimsPrincipal. This principal allows downcasting to WindowsPrincipal and WindowsIdentity (to access things like impersonation and other Windows security specific features).

And your client is not using Windows authentication, I believe. So there will not be a WindowsPrincipal; which is bad news.

For your scenario to work you absolutely need a WindowsPrincipal in the end, I am quite convinced. This is because I believe this to be the only one you can pass to SQL Server. So the question is: how do you get a WindowsPrincipal from "your cookie"?

The way you are doing thing now you might already get a WindowsPrincpal by the way; but the wrong one, I fear. Why? Because you do not have <authentication mode="Windows" /> in your Web.config. Or if you have it and allow anonymous access (which you do) you will end up with the NETWORK SERVICE or machine account Windows Principal (you could easily check this and tell me if I am wrong though).

By the way: do you have any <authentication mode="..."> in Web.config? I do not think so!?!

So again: how can you get a WindowsPrincipal for the current user from the cookie information you have?

Here are my thoughts (I have to admit that they leave a bit of a foul smell in my mouth; it feels like reinventing the (SSO) wheel...).

  1. Configure your app to allow anonymous access in IIS (this is a requirement, as I understand).
  2. Set your app to use impersonation (in Web.config, see @JustLoren's answer)
  3. Use the code under "Obtain a Windows Token for the Original Caller" in How To: Use Protocol Transition and Constrained Delegation in ASP.NET 2.0 to obtain a WindowsIdentity in Global.asaxs Application_AuthenticateRequest-method (using the username/password information you can hopefully retrieve from "the cookie system").
  4. Create a WindowsPrincipal from this WindowsIdentity as described under "To create a WindowsPrincipal object for a single validation", step 2 in How to: Create a WindowsPrincipal Object ; essentially by doing WindowsPrincipal MyPrincipal = new WindowsPrincipal(MyIdentity);.
  5. Attach you newly created WindowsPrincipal to the current HttpContext and thread (as proposed in "Update2").
  6. Cross your fingers :) But I have certain confidence in this. The shaky part seems to be to get the correct WindowsIdentity...

Yetanontherupdate
In section "Impersonate a Specific User in Code" of the article How to implement impersonation in an ASP.NET application still another way to get a WindowsIdentity from username/password is used: LogonUserA imported from advapi32.dll.

Maybefinalupdate
After your latest update I can see clearer now. You are absolutely right with you question. As far as I know, changing (or taking influence on) what WindowsPrincipal.GetCurrent returns is only possible by activating Windows Authentication and disabling anonymous access in IIS. Which is what you cannot do.

But wait!

Wow, this is getting dirty. But as I saw the tag "kludge" on your question I dare write the following... But beware: I am pretty sure this is not what Microsoft showed in this demo you mentioned :) Neither if this has any unwanted side effects!

What you should be able to do (my quick and dirty test showed it seems to work) is

  1. Add a System.Security.Principal.WindowsImpersonationContext field in Global.asax
  2. In Global.asaxs Application_AuthenticateRequest get a WindowsIdentity for the user you want to impersonate (you seem to be able to do this as you could impersonate the user in code)
  3. Still in Global.asaxs Application_AuthenticateRequest impersonate the user (in code as you did before or as described in the article(s) cited above; e.g. here), do not call impersonationContext.Undo()...
  4. In Global.asaxs Application_EndRequest call impersonationContext.Undo() on the field you created two steps before
  5. Keep <identity impersonate="true" /> in Web.config
  6. Cross all your fingers and toes :)

True, you would then still impersonate the user in code (i.e. "clutter your code with impersonation") but only slightly :) Meaning only in one place (Global.asax).

What do you reckon?

@Rising Star: by the way: in your comment from 2010-05-31 14:03:31Z you write "He says that you can uncheck that box and then...". Is this by intention or did you mean to write "check"? Because if you uncheck that box you have to check something else to still allow access to anyone. What would you (or he) check instead? Forms Authentication (as there is no "magic flute authentication" as you correctly noted here :))? Sorry, I (automatically) misread it as "check" until now...

Update (by Rising Star) I see you've converted this thread to community Wiki so I no longer have to manually update the question. This is going to be a mess, but once the problem is solved, we can clean it up a bit.

scherand asked how I attach a Windows Identity to a cookie. This is done using the WIF framework; specifically, I do this by returning a WindowsClaimsPrincipal from the ClaimsAuthenticationManager as described in the "least privilege" link that I provided. Also, yes, the boss did say to uncheck the "anonymous access" box in the IIS settings; that seems to break everything.

I think you might be on the right track with your answer. I was just looking into doing my own custom code in global.asax myself.

scherand
Thanks for your response. I feel like this is killing me. I have a supervisor who swears up and down that what I'm describing is possible and he wants me to find out how to do it. He says that he saw it done at a Microsoft demo.
Rice Flour Cookies
Yes, one of the three suspects is `Windows Authentication`. My supervisor pointed to the screen in the Virtual Directory security settings where it says "Allow Anonymous Access". He says that you can uncheck that box and then somehow, using WIF, log users in to the application just as if they had used `Windows Authentication` although it is impossible to use `Windows Authentication`.
Rice Flour Cookies
Once a user is logged in as if by `Windows Authentication`, you can make a setting in the `web.config` that makes it so that it is not necessary to programmatically impersonate the user with each database request.
Rice Flour Cookies
Using a component of WIF called `C2WTS`, I can obtain a SecurityPrincipal I can use for impersonation (and I can do that with with just the AD username, without the password). You have to be a system administrator to allow any application to use C2WTS for obvious security reasons.So, where I have a gap in my knowledge is, I have a security principal with the users AD account. I now wish to make it as if the user logged in with that AD account so that the database will recognize the user without having to do `impersonation in code`
Rice Flour Cookies
I updated my answer. Maybe it is more useful now?
scherand
scherand, thank you very much for that last bit of information. It looks like that may be exactly what I need to get this job done.In answer to your speculation, about global.asax, YES, the AuthenticateRequest method gets called with WIF. So.... You're saying that the answer to my question may be to somehow replace the current security principal in this method? I will look into this tomorrow.
Rice Flour Cookies
Another update. I am now *very* curious about your findings :)
scherand
Update: I tried replacing the `HttpContext.Current.User` with a `WindowsIdentity` constructed using the identity returned by `C2WTS` in the `AuthenticateRequest` method in `global.asax`. The method executes normally, but after execution, it throws exception:
Rice Flour Cookies
@Rising Star: did you intend to ditch the information what exception is being thrown? I would be interesting to know/hear :)
scherand
I put the text of the exception in my question. After giving this some more thought, I found this page: http://www.leastprivilege.com/GenevaHTTPModulesClaimsPrincipalHttpModule.aspx. The author points out that there is a class in WIF that I'm not familiar with called `WindowsClaimsIdentity` which seems to be a hybrid Windows/Claims Identity. I'm trying to find how to set up my impersonation using this class. See my updated question for more information about what I've found.
Rice Flour Cookies
@Rising Star: Sorry, I missed that (but found it now :)).
scherand
I've updated the question in response to your update. I have no trouble getting the `WindowsPrincipal`. I can even attach it to the `Thread`, but impersonation in `web.config` still causes weird things to happen; it impersonates the account specified by the "Anonymous Access" section of the IIS Configuration.
Rice Flour Cookies
@scherand, "Magic Flute", as I described in the other thread you mentioned, was meant to be a pseudonym for the proprietary authentication system that I must use. However, it seemed to cause so much confusion that I haven't used it again.
Rice Flour Cookies
@Rising Star: too bad, I like it a lot! Reminds me of [Zelda](http://de.wikipedia.org/wiki/The_Legend_of_Zelda) somehow; but that is a completely different story...
scherand