IIS 7.5 and 2 Level Auth

We use a large vendor application at work.  We host all the infrastructure for the application inside the firewall, so there is absolutely no access from the Internet.

In IIS6 we configured 2 level authentication – NTLM and Forms Auth.  The vendor requires Forms Auth for the application.  Given the importance of this application and sensitive nature of the data; I also enabled NTLM and secured the site to only people in our division (about 450 people).  There are about 150 logins in the application meaning that 300 people have access to the site; even though they will not be able to actually see any screens until they login.

Through a series of discussions with different audiences; it was decided that there is still enough of a risk of those 300 people being infected with something that takes advantage of cross site scripting or other classic vulnerabilities.  So I further locked down the site using a more restrictive group.  While I feel like we are being a little paranoid about, I capitulated.

Enter IIS7…

images

Our standard for servers is Windows 2008r2 so we are on IIS7.5.  Doing this same 2 level authentication on IIS7.5 did not work.  Why?  Well because of the integrated pipeline…it simply cannot not do both at the “same time”.  One has to come first.  In IIS 6 NTLM always came first since that was done my IIS and then Forms Auth since that was done by ASP.NET.

There are a couple of hacks out there that describe how to work around this.  One of which I found posted here by Mike Volodarsky (formally of the IIS team).  Here he talks about a way to make this work by splitting up the authentication and forcing one to happen before the other.  I was up until well after midnight last night trying to consider how I would make this work given that the application is a vendor application and I don’t have the source code.  Not to mention that everything is precompiled, signed and obsuficated.  All of which add up to…this would be really hard to hack.

Finally, after a bit of chin rubbing…I came to the conclusion that the integrated pipeline may not be the problem at all.  Why do I even still need NTLM?  I mean if the only way for someone to access a web page on the site is to have a valid Forms Auth token then do I really need to force them to also have an NTLM token?  I went to bed content that I just need to leave NTLM behind in this case.

Now I just need to convince everyone that was pushing the original requirement for 2 level authentication that I don’t need it anymore.  Being that they don’t really understand the technology very well – that could be a challenge.  Since the way we got here was through a vulnerability scan of the web site in the first place – perhaps requesting another one will demonstrate my point and I won’t have to make them understand the why.

I will post an update on the outcome.

Old Problem – New Context

[Here is an old post I found that I had never published. I think it's because I could not get the formatting of the code to look "right". This time I published from Word and the formatting was still not preserved - rats! Also, the article is not as complete as I would like it but I wanted to get the code into the blogosphere.]

[There are a few technical things wrong with this post. The first is that using Thread Local Storage is risk (at best) in an ASP.NET application. See http://www.hanselman.com/blog/CommentView.aspx?guid=320. The second is that I would prefer to use a different pattern than the one below; something a little more elegant. Lastly, after using LINQ much more I don't know that this code is even a best practice; I need to noodle on this more.]

I keep thinking that there are like a dozen or so abstract problems that I solve and everything else is just another derivation from those. This is one of those dozen problems – what is context and why do we need it so often?

We have been using a lot of LINQ lately. While using LINQ to SQL I ran into a problem a few weeks ago and created a little hack to work around it. Well we are starting to roll LINQ out to the rest of the developers we need more than a hack for this issue. What is the issue, well it’s the same we have had in the last 4 ADO stacks – how do I reuse a connection when I have a transaction open. The only time I have not had to work around this issue was in MTS/MTX and when we used the Microsoft Enterprise Library (EntLib)

The way MTX and EntLib get around this is that they are a “layer” in between me (you) and the underlying infrastructure. I remember Bernie’s old saying – “Interesting things can happen when you can get in between things”. Another way of saying that layers or interception are something that are a key part of any design.


So here is what I want the client/consuming code to look like.


const
string CN = “Server=localhost;Database=AdventureWorks2008;Trusted_Connection=true”;

using (var aTrx = new TransactionScope())

{

var aCtx = new AdventureWorksDataContext(CN);

var x = from p in aCtx.Persons

where p.FirstName.StartsWith(“A”)

select p;

var y = x.First();

var aCtx2 = new AdventureWorksDataContext(CN);

var x2 = from p in aCtx2.Persons

where p.FirstName.StartsWith(“A”)

select p;

var y2 = x2.First();

}

So I wrote a little class that caches the connections so that they can be reused across LINQ statements. If we don’t do it this way then we are invoking the Distributed Transaction Coordinator (DTC). We only want to do this when the statements need to be in the same transaction.


public
static
class
TransactionalDataContext

{

[ThreadStatic]


private
static Hashtable mConnections;


public
static IDbConnection DataContext(string pCnStr)

{


var aTrx = Transaction.Current;


if (aTrx == null)

{


return CreateConnection(pCnStr);

}


if (mConnections == null)

{

mConnections = new Hashtable();

}


if (mConnections.ContainsKey(pCnStr))

{


return (IDbConnection)mConnections[pCnStr];

}


var aCn = CreateConnection(pCnStr);

mConnections[pCnStr] = aCn;

aTrx.TransactionCompleted += TransactionCompleted;


return aCn;

}


private
static IDbConnection CreateConnection(string pCnStr)

{


// cn str is name value pairs with ‘=’ and ‘;’ separating each pair


// get each of the pairs


var aCnStrPairs = pCnStr.Split(new[] { ‘;’ });


// find the pair with ‘Provider’


var aProvPairEnum = aCnStrPairs.Where(i => string.Compare(i, “Provider”, true) == 0);

IDbConnection aCn;


if (aProvPairEnum.Count() == 0)

{


// no provider specified so assume MS SQL Server

aCn = new SqlConnection(pCnStr);

}


else

{


// get the provider name


var aProvPair = aProvPairEnum.First();


var aProvNvp = aProvPair.Split(new[] { ‘=’ });


// create a connection


var aFactory = System.Data.Common.DbProviderFactories.GetFactory(aProvNvp[1]);

aCn = aFactory.CreateConnection();

aCn.ConnectionString = pCnStr;

}


// we open the cn so that LINQ does not – this will also signal to LINQ not to close the cn

aCn.Open();


return aCn;

}


// handler for when the trx is complete


private
static
void TransactionCompleted(object sender, TransactionEventArgs e)

{


try

{


// dispose of each connection


foreach (DictionaryEntry aEntry in mConnections)

{


try

{

((IDbConnection)aEntry.Value).Dispose();

}


catch { }

}

}


finally

{


// reset the collection, in case doing another on this thread

mConnections.Clear;

}

}

}


IIS 401.5 Error

Had a really weird problem today where I was getting a IIS 401.5 error on a subweb today. The problem shows up as a 401.2 error in the browser, but when you look at the IIS log it’s reported as a 401.5.

I was running through all my usual ways of debugging these types of issues and non of the usual things worked. Asked a collegue for help because I must be missing something obvious. Then I was verbally walking him through the configuration and I mentioned that the problem was only happening on this subweb. Something clicked on both our faces – it’s a subweb so there is more than one web.config in play here. I checked the web.config file and found that it was busted (the root site did not work either – but no one cares). I renamed the web.config file and bang, the site started working again.

Blogging this on the off chance it helps someone and to get it to stick in my own memory for next time.