Re: IIS Folder and file security. Impersonation does not work.

From: David Wang [Msft] (someone_at_online.microsoft.com)
Date: 07/08/04


Date: Thu, 8 Jul 2004 00:18:19 -0700

You are trying to implement two things:
1. Custom URL navigation.
2. Custom authentication

First -- what you want to do does NOT need the impersonation DLL at all.
Let me explain a possible implementation shortly.

Second -- you are muddling HTML and IIS concepts together and hoping for the
best. You have to understand that sending a <img src> causes the client to
make a separate request which has NOTHING to do with this current request,
while making a FileSystemObject or ADO call on this request to stream a file
DOES use this current request's identity.

What you observe is absolutely by-design. Yes, the ActiveX Object is able
to change the impersonation token for the thread that is executing the ASP
page which ends up sending the <img src...> entity body to the client.
However, please realize that the client makes ANOTHER SEPARATE request for
the <img src> URL, which is handled by ANOTHER thread on IIS -- IIS is
multi-threaded. Thus, the impersonation that you did for this ASP page does
absolutely nothing for that <img src> request -- hence you see the password
dialog.

Meanwhile, if you chose to use FileSystemObject or ADO to open a file and
stream it to the client, then the impersonation token WILL be used on that
FileSystemObject/ADO call.

Finally -- it is NOT possible to change the user identity that IIS uses to
execute a request with ASP -- if you want IIS to serve up "files/00001.jpg"
as your UserX, you need to either:
1. Browser must authenticate as UserX when requesting files/00001.jpg
2. Only anonymous is allowed, and Anonymous User is configured as UserX (so
all anonymous users impersonate UserX)
3. Use an ISAPI Filter or ISAPI Extension to change the impersonation token
that IIS uses

None of these are possible with anything you do in ASP. You simply cannot
change the impersonation token that IIS uses with ASP -- as soon as your
page returns control to IIS, IIS will strip off the impersonation token on
the thread. So, you can only change the impersonation token of the thread
while that ASP page is running -- which is what the sample code does.

Now, with IIS6, we have a custom authentication sample ISAPI that should
work for you after you write a little bit of C code to customize it for your
specific needs.

Download the Platform SDK and get the IIS portion. The sample code is
called CustomAuth, and the Platform SDK includes necessary tools/compiler to
build it (if you do not have Visual Studio or comparable development tool).

http://www.microsoft.com/msdownload/platformsdk/sdkupdate/default.htm

It can be configured for any URL namespace, and it implements custom
authentication where you enter username/password in an HTML page. So, you
can easily configure CustomAuth to protect only content under the /Files
vdir and no-where else. The CustomAuth.INI file explains how to do this,
conceptually. Basically, the /Files vdir will have only anonymous
authentication enabled and this CustomAuth module loaded to implement custom
authentication.

What you'd need to do with this sample code is to implement verification of
the username/password entered from the HTML page as well as set the user
token for IIS to impersonate -- which is EXACTLY all you're trying to do in
the ASP page.

You will find in CustomAuth.cpp a single line containing "LogonUser", and
this is where you'll inject your code.
- pstrUser->QueryStr() contains the username from the form
- strPassword.QueryStr() contains the password from the form
- The hToken that is created by the LogonUser call is the impersonation
token that will be used by IIS to execute the subsequent request.

So, all you need to do is:
1. implement a username/password validation scheme using the values from
pstrUser and strPassword. If there's not a lot of users, I suggest using an
INI file -- one single call to GetPrivateProfileString should be sufficient
to lookup a username/password inside a plain-text INI file.
2. hardcode the LogonUser call to log in UserX
3. If username/password authenticates, then make the LogonUser call in #2
let the rest of the code use the hToken for UserX. If the username/password
fails to authenticate, then set fResult to FALSE and you're done.

Here's a code snippet for you to get started in CustomAuth.cpp. I haven't
compiled nor tested it, but it should get you most of the way there.

For example, suppose your INI file containing usernames/passwords are in
C:\passwords.txt (make sure that this file is accessible to the anonymous
user configured in IIS, since it's the user identity used to execute the
CustomAuth code.

passwords.txt looks like:
[passwords]
user1=password1
user2=password2
...

The code snippet looks something like this (I've added leading/trailing
comments so it is clear where to inject this bit of code in CustomAuth.cpp):

//
// Log on the user
//

DWORD dwPasswordSize;
CHAR szStoredPassword[MAX_PATH];

// lookup the username key under the "passwords" section
// user passwords are limited to MAX_PATH-1 in length. Read MSDN to figure
out how to allow larger passwords
dwPasswordSize = GetPrivateProfileString(
    "passwords",
    pstrUser->QueryStr(),
    "",
    szStoredPassword,
    MAX_PATH,
    "C:\\passwords.txt" );

if ( dwPasswordSize == 0 )
{
    // Did not find username in password.txt, so deny access
    fResult = FALSE;
    SetLastError( ERROR_NO_SUCH_USER );
}
else if ( strcmp( strPassword.QueryStr(), szStoredPassword ) == 0 )
{
    // The password from the form matches password for username in
password.txt,
    // so allow access with impersonation token of UserX
    fResult = LogonUser( "UserX",
                     NULL,
                     "UserX's password",
                     g_LogonType,
                     LOGON32_PROVIDER_DEFAULT,
                     &hToken );
}
else
{

    // username was found, but password did not match, so deny access
    fResult = FALSE;
    SetLastError( ERROR_INVALID_PASSWORD );
}

//
// Regardless of the result, we are done with the password
//

Good luck.

-- 
//David
IIS
This posting is provided "AS IS" with no warranties, and confers no rights.
//
"Razak" <razak@mmsc.com.my> wrote in message
news:%23wYzi%23IZEHA.3752@TK2MSFTNGP12.phx.gbl...
I'm using WIndows server 2003 with IIS 6.0. My web consists of a folder
named 'Files' which I would like to restrict access only to members. The
folder contains various types of files such as images (jpg, png, bmp), flash
animation (swf), videos (avi, mov), and audios (wav, mp3, aif).
What I'm trying to achieve is that members will be alowed to view and
download those files in 'Files' folder only through an ASP page since I want
to track some statistics. So I have to avoid the files from being leeched or
simply opened by typing the url to the file in the address bar. The only way
I can think to achieve that is by securing the folder since it is on an NTFS
drive. I don't want to create each user a Windows account on the server,
instead I created a general user account 'Xuser' which is given the
previllege to access the 'File' folder. BTW, the folder is set to allow only
Administrators group and Xuser.
For each succesfully logged in user will have access to the asp page (which
displays the file, or play the movie/animation, so on), by linking to the
requested file in the 'Files' folder using Impersonation method I created
using VB (as sampled at
http://support.microsoft.com/support/kb/articles/q248/1/87.asp ) and on the
ASP page is as follows:-
<%
Dim objLogon
Set objLogon = Server.CreateObject("LoginAdmin.ImpersonateUser")
objLogon.Logon "Xuser", "1234", ""
%>
.
.
.
.
<IMG Src="files/00001.jpg">
.
.
.
.
<%
objLogon.Logoff
Set objLogon = Nothing
%>
The problem is, the above impersonation does not work. Everytime I open that
page, IE still prompt for user/pwd. I know that the impersonation dll is OK
since in other part of the website, I use it to manipulate
(modifying/writing) the files in the 'Files' folder which otherwise I'm not
allowed to (since 'IUSR_machinename' is not given the privillege at all to
access/modify the 'File' folder). And the Impersonation works just fine.
It just that it doesn't work when linking from a web page. I opted out
locating the secured folder 'File' somewhere outside the web root and stream
it using
<img src="stream.asp?file=00001.jpg"> because it only works for image files,
but doesn't work with shockwave flash, large audio and video files, etc.
Hope that someone has done something similar to this to share their
knowledge. Thanks.


Relevant Pages

  • Re: impersonating a user
    ... > authentication is what determines the context of the thread. ... > applications, IIS will read the HTTP, and when anonymous is selected IIS ... > Local System account (which is the default account for Services that are ... > impersonation and authentication very clearly. ...
    (microsoft.public.inetserver.iis.security)
  • Re: 401 Unauthorized trying to read SPList Attachment - owssrv.dll
    ... Client-side impersonation isn't one of them. ... NOTHING on the server unless you negotiate HTTP authentication of some sort. ... be called by the first which actually opens the attachment. ... Unauthorized error from IIS on http://localhost/_vti_bin/owssrv.dll. ...
    (microsoft.public.inetserver.iis.security)
  • Re: IIS impersonation problems
    ... Are you using Basic or Integrated Windows Authentication? ... then IIS never has the user's password - so it can't ... permissions to logon to remote resources. ... Is there a maximum number of hops impersonation can make? ...
    (microsoft.public.inetserver.iis.security)
  • Re: custom page for user credentials?
    ... custom HTML page to enter username/password, transmit it in some fashion, ... and do the verification on IIS. ... you MUST give at least one impersonation ... the equivalent of Windows Integrated Authentication everywhere. ...
    (microsoft.public.inetserver.iis.security)
  • Re: SP2003 w/o AD
    ... You can use some (possibly custom) tool to walk the user list in the ... IIS does all of the authentication for SPS, so you also have to figure ...
    (microsoft.public.sharepoint.portalserver)