You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
510 lines
11 KiB
510 lines
11 KiB
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
//
|
|
// METHOD.CPP
|
|
//
|
|
// Copyright 1986-1997 Microsoft Corporation, All Rights Reserved
|
|
//
|
|
|
|
#include "_davprs.h"
|
|
|
|
#include <statcode.h>
|
|
#include "ecb.h"
|
|
#include "instdata.h"
|
|
|
|
|
|
// ------------------------------------------------------------------------
|
|
//
|
|
// DAVUnsupported()
|
|
//
|
|
// Execute an unsupported method --> Return "501 Not Supported" to client
|
|
//
|
|
void
|
|
DAVUnsupported( LPMETHUTIL pmu )
|
|
{
|
|
// Get our access perms
|
|
//
|
|
SCODE sc = S_OK;
|
|
// Do ISAPI application and IIS access bits checking
|
|
//
|
|
sc = pmu->ScIISCheck (pmu->LpwszRequestUrl());
|
|
if (FAILED(sc))
|
|
{
|
|
// Either the request has been forwarded, or some bad error occurred.
|
|
// In either case, quit here and map the error!
|
|
//
|
|
goto ret;
|
|
}
|
|
|
|
ret:
|
|
pmu->SetResponseCode( FAILED(sc)
|
|
? HscFromHresult(sc)
|
|
: HSC_NOT_IMPLEMENTED,
|
|
NULL,
|
|
0 );
|
|
}
|
|
|
|
// ========================================================================
|
|
//
|
|
// STRUCT SMethod
|
|
//
|
|
// Encapsulates DAV method execution information.
|
|
//
|
|
// This is represented as a structure rather than a class since the method
|
|
// objects are all const globals and MSVC won't initialize global objects
|
|
// in a DLL to anything but 0-filled memory without an explicit call to
|
|
// _CRT_INIT at process attach. _CRT_INIT is too expensive to call just
|
|
// to initialize globals with constant data values which are known
|
|
// at compile time.
|
|
//
|
|
typedef struct SMethod
|
|
{
|
|
//
|
|
// Verb ("GET", "PUT", etc.)
|
|
//
|
|
//
|
|
LPCSTR lpszVerb;
|
|
LPCWSTR pwszVerb;
|
|
|
|
//
|
|
// Method ID
|
|
//
|
|
METHOD_ID mid;
|
|
|
|
//
|
|
// Implementation execution function
|
|
//
|
|
DAVMETHOD * Execute;
|
|
|
|
} SMethod;
|
|
|
|
const SMethod g_rgMethods[] =
|
|
{
|
|
//
|
|
// For best performance, entries in this array should be
|
|
// ordered by relative frequency.
|
|
//
|
|
//$OPT Right now, they obviously are not.
|
|
//
|
|
{
|
|
"OPTIONS",
|
|
L"OPTIONS",
|
|
MID_OPTIONS,
|
|
DAVOptions
|
|
},
|
|
{
|
|
"GET",
|
|
L"GET",
|
|
MID_GET,
|
|
DAVGet
|
|
},
|
|
{
|
|
"HEAD",
|
|
L"HEAD",
|
|
MID_HEAD,
|
|
DAVHead
|
|
},
|
|
{
|
|
"PUT",
|
|
L"PUT",
|
|
MID_PUT,
|
|
DAVPut
|
|
},
|
|
{
|
|
"POST",
|
|
L"POST",
|
|
MID_POST,
|
|
DAVPost
|
|
},
|
|
{
|
|
"MOVE",
|
|
L"MOVE",
|
|
MID_MOVE,
|
|
DAVMove
|
|
},
|
|
{
|
|
"COPY",
|
|
L"COPY",
|
|
MID_COPY,
|
|
DAVCopy
|
|
},
|
|
{
|
|
"DELETE",
|
|
L"DELETE",
|
|
MID_DELETE,
|
|
DAVDelete
|
|
},
|
|
{
|
|
"MKCOL",
|
|
L"MKCOL",
|
|
MID_MKCOL,
|
|
DAVMkCol
|
|
},
|
|
{
|
|
"PROPFIND",
|
|
L"PROPFIND",
|
|
MID_PROPFIND,
|
|
DAVPropFind
|
|
},
|
|
{
|
|
"PROPPATCH",
|
|
L"PROPPATCH",
|
|
MID_PROPPATCH,
|
|
DAVPropPatch
|
|
},
|
|
{
|
|
"SEARCH",
|
|
L"SEARCH",
|
|
MID_SEARCH,
|
|
DAVSearch
|
|
},
|
|
{
|
|
"LOCK",
|
|
L"LOCK",
|
|
MID_LOCK,
|
|
DAVLock
|
|
},
|
|
{
|
|
"UNLOCK",
|
|
L"UNLOCK",
|
|
MID_UNLOCK,
|
|
DAVUnlock
|
|
},
|
|
{
|
|
NULL,
|
|
NULL,
|
|
MID_UNKNOWN,
|
|
DAVUnsupported
|
|
}
|
|
|
|
};
|
|
|
|
METHOD_ID
|
|
MidMethod (LPCSTR pszMethod)
|
|
{
|
|
const SMethod * pMethod;
|
|
|
|
for ( pMethod = g_rgMethods; pMethod->lpszVerb != NULL; pMethod++ )
|
|
if ( !strcmp( pszMethod, pMethod->lpszVerb ) )
|
|
break;
|
|
|
|
return pMethod->mid;
|
|
}
|
|
|
|
METHOD_ID
|
|
MidMethod (LPCWSTR pwszMethod)
|
|
{
|
|
const SMethod * pMethod;
|
|
|
|
for ( pMethod = g_rgMethods; pMethod->pwszVerb != NULL; pMethod++ )
|
|
if ( !wcscmp( pwszMethod, pMethod->pwszVerb ) )
|
|
break;
|
|
|
|
return pMethod->mid;
|
|
}
|
|
|
|
|
|
// Debug SID vs Name ---------------------------------------------------------
|
|
//
|
|
#ifdef DBG
|
|
VOID
|
|
SpitUserNameAndSID (CHAR * rgch)
|
|
{
|
|
enum { TOKENBUFFSIZE = (256*6) + sizeof(TOKEN_USER)};
|
|
|
|
auto_handle<HANDLE> hTok;
|
|
BYTE tokenbuff[TOKENBUFFSIZE];
|
|
TOKEN_USER *ptu = reinterpret_cast<TOKEN_USER *>(tokenbuff);
|
|
ULONG ulcbTok = sizeof(tokenbuff);
|
|
|
|
*rgch = '\0';
|
|
|
|
// Open the process and the process token, and get out the
|
|
// security ID.
|
|
//
|
|
if (!OpenThreadToken (GetCurrentThread(),
|
|
TOKEN_QUERY,
|
|
TRUE, //$ TRUE for Process security!
|
|
hTok.load()))
|
|
{
|
|
if (ERROR_NO_TOKEN != GetLastError())
|
|
{
|
|
DebugTrace( "OpenThreadToken() failed %d\n", GetLastError() );
|
|
return;
|
|
}
|
|
|
|
if (!OpenProcessToken (GetCurrentProcess(),
|
|
TOKEN_QUERY,
|
|
hTok.load()))
|
|
{
|
|
DebugTrace( "OpenProcessToken() failed %d\n", GetLastError() );
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (GetTokenInformation (hTok,
|
|
TokenUser,
|
|
ptu,
|
|
ulcbTok,
|
|
&ulcbTok))
|
|
{
|
|
ULONG IdentifierAuthority;
|
|
BYTE * pb = (BYTE*)&IdentifierAuthority;
|
|
SID * psid = reinterpret_cast<SID *>(ptu->User.Sid);
|
|
|
|
for (INT i = 0; i < sizeof(ULONG); i++)
|
|
{
|
|
*pb++ = psid->IdentifierAuthority.Value[5-i];
|
|
}
|
|
wsprintfA (rgch, "S-%d-%d",
|
|
psid->Revision,
|
|
IdentifierAuthority);
|
|
|
|
for (i = 0; i < psid->SubAuthorityCount; i++)
|
|
{
|
|
// The SubAuthority is a PDWORD which can be 64 bits
|
|
// at the most in the forseable future, 2^64 = 10^20,
|
|
// so we should use 23 (20 for the SubAuthority, a terminating
|
|
// NULL plus the "- ". If snprintf
|
|
// is not able to print the NULL, we will add it ourselves.
|
|
//
|
|
CHAR rgchT[23];
|
|
_snprintf (rgchT, sizeof(rgchT), "-%d", psid->SubAuthority[i]);
|
|
rgchT[CElems(rgchT) - 1] = '\0';
|
|
lstrcatA (rgch, rgchT);
|
|
}
|
|
|
|
if (1 == psid->Revision)
|
|
{
|
|
if (0 == IdentifierAuthority)
|
|
lstrcatA (rgch, " (Null)");
|
|
if (1 == IdentifierAuthority)
|
|
lstrcatA (rgch, " (World)");
|
|
if (2 == IdentifierAuthority)
|
|
lstrcatA (rgch, " (Local)");
|
|
if (3 == IdentifierAuthority)
|
|
lstrcatA (rgch, " (Creator)");
|
|
if (4 == IdentifierAuthority)
|
|
lstrcatA (rgch, " (Non-Unique)");
|
|
if (5 == IdentifierAuthority)
|
|
lstrcatA (rgch, " (NT)");
|
|
}
|
|
|
|
CHAR rgchAccount[MAX_PATH];
|
|
CHAR rgchDomain[MAX_PATH];
|
|
DWORD cbAccount = sizeof(rgchAccount) - 1;
|
|
DWORD cbDomain = sizeof(rgchDomain) - 1;
|
|
SID_NAME_USE snu;
|
|
LookupAccountSidA (NULL,
|
|
psid,
|
|
rgchAccount,
|
|
&cbAccount,
|
|
rgchDomain,
|
|
&cbDomain,
|
|
&snu);
|
|
lstrcatA (rgch, " ");
|
|
lstrcatA (rgch, rgchDomain);
|
|
lstrcatA (rgch, "\\");
|
|
lstrcatA (rgch, rgchAccount);
|
|
DavprsDbgHeadersTrace ("Dav: header: x-Dav-Debug-SID: %hs\n", rgch);
|
|
}
|
|
}
|
|
|
|
VOID DebugAddSIDHeader( IMethUtil& mu )
|
|
{
|
|
CHAR rgch[4096];
|
|
|
|
if (!DEBUG_TRACE_TEST(DavprsDbgHeaders))
|
|
return;
|
|
|
|
SpitUserNameAndSID (rgch);
|
|
mu.SetResponseHeader ("x-Dav-Debug-SID", rgch);
|
|
}
|
|
|
|
#else
|
|
#define DebugAddSIDHeader(_mu)
|
|
#endif // DBG
|
|
|
|
// ----------------------------------------------------------------------------
|
|
//
|
|
// CDAVExt::DwMain()
|
|
//
|
|
// Invokes a DAV method. This is THE function called by our IIS entrypoint
|
|
// DwDavXXExtensionProc() to start processing a request.
|
|
//
|
|
// If MINIMAL_ISAPI is defined, this function is implemented in another
|
|
// file (.\appmain.cpp). See the implementation there for what MINIMAL_ISAPI
|
|
// does.
|
|
//
|
|
#ifndef MINIMAL_ISAPI
|
|
DWORD
|
|
CDAVExt::DwMain( LPEXTENSION_CONTROL_BLOCK pecbRaw,
|
|
BOOL fUseRawUrlMappings /* = FALSE */ )
|
|
{
|
|
#ifdef DBG
|
|
CHAR rgch[1024];
|
|
DWORD cch;
|
|
|
|
cch = sizeof(rgch);
|
|
if (pecbRaw->GetServerVariable (pecbRaw->ConnID, "REQUEST_METHOD", rgch, &cch))
|
|
EcbTrace ("CDAVExt::DwMain() called via method: %hs\n", rgch);
|
|
|
|
cch = sizeof(rgch);
|
|
if (pecbRaw->GetServerVariable (pecbRaw->ConnID, "ALL_RAW", rgch, &cch))
|
|
EcbTrace ("CDAVExt::DwMain() called with RAW:\n%hs\n", rgch);
|
|
|
|
cch = sizeof(rgch);
|
|
if (pecbRaw->GetServerVariable (pecbRaw->ConnID, "ALL_HTTP", rgch, &cch))
|
|
EcbTrace ("CDAVExt::DwMain() called with HTTP:\n%hs\n", rgch);
|
|
#endif // DBG
|
|
|
|
auto_ref_ptr<IEcb> pecb;
|
|
DWORD dwHSEStatusRet = 0;
|
|
BOOL fCaughtException = FALSE;
|
|
HANDLE hitUser = INVALID_HANDLE_VALUE;
|
|
|
|
try
|
|
{
|
|
//
|
|
// Don't let hardware exceptions (AVs, etc.)
|
|
// leave this try block
|
|
//
|
|
CWin32ExceptionHandler win32ExceptionHandler;
|
|
|
|
pecb.take_ownership(NewEcb(*pecbRaw, fUseRawUrlMappings, &dwHSEStatusRet));
|
|
|
|
//
|
|
// If for whatever reason we failed to create a CEcb then bail
|
|
// and return whatever status we were told to return.
|
|
//
|
|
// Note: return HSE_STATUS_SUCCESS here, not HSE_STATUS_ERROR.
|
|
// We have sent back a 500 Server Error response to the client
|
|
// so we don't need to send back any kind of error to IIS.
|
|
//
|
|
if ( !pecb.get() )
|
|
{
|
|
// All valid HSE status codes are non-zero (how convenient!)
|
|
// so we can make sure that we are returning a valid HSE
|
|
// status code here.
|
|
//
|
|
Assert( dwHSEStatusRet != 0 );
|
|
return dwHSEStatusRet;
|
|
}
|
|
|
|
const SMethod * pMethod;
|
|
|
|
//
|
|
// Lookup the method object for this verb
|
|
//
|
|
for ( pMethod = g_rgMethods; pMethod->lpszVerb != NULL; pMethod++ )
|
|
if ( !strcmp( pecb->LpszMethod(), pMethod->lpszVerb ) )
|
|
break;
|
|
|
|
//
|
|
// Build request and response objects.
|
|
//
|
|
auto_ref_ptr<IRequest> prequest( NewRequest( *pecb ) );
|
|
auto_ref_ptr<IResponse> presponse( NewResponse( *pecb ) );
|
|
|
|
//
|
|
// If impersonation is required, do it here
|
|
//
|
|
hitUser = pecb->HitUser();
|
|
if ((NULL == hitUser) || (INVALID_HANDLE_VALUE == hitUser))
|
|
{
|
|
//$ REVIEW: SECURITY: If HitUser() returns any
|
|
// value of NULL or INVALID_HANDLE_VALUE, then a call
|
|
// to augment the user's token to include USG
|
|
// group membership failed. Since this token
|
|
// is not augmented with the additional group
|
|
// that may be included in any/all deny ACL's
|
|
// we want to fail this request immediately.
|
|
//
|
|
// We treat the failure as a 500 level error.
|
|
//
|
|
pecb->SendAsyncErrorResponse (500,
|
|
gc_szDefErrStatusLine,
|
|
gc_cchszDefErrStatusLine,
|
|
gc_szUsgErrBody,
|
|
gc_cchszUsgErrBody);
|
|
|
|
//
|
|
// Return HSE_STATUS_PENDING on success. We will call
|
|
// HSE_REQ_DONE_WITH_SESSION when the CEcb is destroyed.
|
|
//
|
|
dwHSEStatusRet = HSE_STATUS_PENDING;
|
|
//
|
|
//$ REVIEW: SECURITY: end.
|
|
|
|
}
|
|
else
|
|
{
|
|
safe_impersonation si( hitUser );
|
|
|
|
// If we failed to impersonate, we should not process any
|
|
// portion of the the request.
|
|
//
|
|
if (!si.FImpersonated())
|
|
throw CHresultException(E_FAIL);
|
|
|
|
// Let the implementation handle the request.
|
|
//
|
|
{
|
|
auto_ref_ptr<CMethUtil> pmu( CMethUtil::NewMethUtil( *pecb,
|
|
*prequest,
|
|
*presponse,
|
|
pMethod->mid ) );
|
|
|
|
DebugAddSIDHeader( *pmu );
|
|
|
|
//
|
|
// Execute the method
|
|
//
|
|
pMethod->Execute( pmu.get() );
|
|
}
|
|
|
|
//
|
|
// Call the method completion function on the response.
|
|
// This does all work necessary to finish handling the
|
|
// response as far as the method is concerned, including
|
|
// sending the response if it was not deferred by the impl.
|
|
//
|
|
presponse->FinishMethod();
|
|
|
|
//
|
|
// Return HSE_STATUS_PENDING on success. We will call
|
|
// HSE_REQ_DONE_WITH_SESSION when the CEcb is destroyed.
|
|
//
|
|
dwHSEStatusRet = HSE_STATUS_PENDING;
|
|
}
|
|
}
|
|
catch ( CDAVException& )
|
|
{
|
|
fCaughtException = TRUE;
|
|
}
|
|
|
|
//
|
|
// If we caught an exception then handle it as best we can
|
|
//
|
|
if ( fCaughtException )
|
|
{
|
|
//
|
|
// If we have a CEcb then use it to handle the server error.
|
|
// If we don't have one (i.e. we threw an exception trying
|
|
// to allocate/build one) then return an error to IIS
|
|
// and let it handle it.
|
|
//
|
|
dwHSEStatusRet =
|
|
pecb.get() ? pecb->HSEHandleException() :
|
|
HSE_STATUS_ERROR;
|
|
}
|
|
|
|
//
|
|
// All valid HSE status codes are non-zero (how convenient!)
|
|
// so we can make sure that we are returning a valid HSE
|
|
// status code here.
|
|
//
|
|
Assert( dwHSEStatusRet != 0 );
|
|
|
|
return dwHSEStatusRet;
|
|
}
|
|
#endif // !defined(MINIMAL_ISAPI)
|