mirror of https://github.com/tongzx/nt5src
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.
558 lines
13 KiB
558 lines
13 KiB
/**********************************************************************/
|
|
/** Microsoft Windows NT **/
|
|
/** Copyright(c) Microsoft Corp., 1993 **/
|
|
/**********************************************************************/
|
|
|
|
/*
|
|
security.c
|
|
|
|
This module manages security for the W3 Service.
|
|
|
|
|
|
FILE HISTORY:
|
|
KeithMo 07-Mar-1993 Created.
|
|
|
|
*/
|
|
|
|
|
|
#include "w3p.hxx"
|
|
#include <lonsi.hxx>
|
|
|
|
DWORD DeniedFlagTable[] = { 0,
|
|
SF_DENIED_BY_CONFIG,
|
|
SF_DENIED_RESOURCE,
|
|
SF_DENIED_FILTER,
|
|
SF_DENIED_APPLICATION
|
|
};
|
|
|
|
DWORD
|
|
DeniedFlagsToSubStatus(
|
|
DWORD fFlags
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Map a set of denied flags to a substatus for use in custom error lookup.
|
|
|
|
Arguments:
|
|
|
|
fFlags - The flags to be mapped.
|
|
|
|
Return Value:
|
|
|
|
The substatus if we can map it, or 0 otherwise.
|
|
|
|
--*/
|
|
{
|
|
int i;
|
|
|
|
fFlags &= ~SF_DENIED_LOGON;
|
|
|
|
for (i = 0; i < sizeof(DeniedFlagTable)/sizeof(DWORD);i++)
|
|
{
|
|
if (DeniedFlagTable[i] == fFlags)
|
|
{
|
|
return i+1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// Public functions.
|
|
//
|
|
|
|
BOOL
|
|
HTTP_REQ_BASE::SendAuthNeededResp(
|
|
BOOL * pfFinished
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Sends an access denied HTTP server response with the accompanying
|
|
authentication schemes the server supports
|
|
|
|
Parameters:
|
|
|
|
pfFinished - If set to TRUE, indicates no further processing is needed
|
|
for this request
|
|
|
|
Return Value:
|
|
|
|
TRUE if successful, FALSE on error
|
|
|
|
--*/
|
|
{
|
|
CHAR * pszTail;
|
|
DWORD cbRespBufUsed;
|
|
DWORD cbRespBufLeft;
|
|
DWORD cbNeeded;
|
|
LPCSTR pszAccessDeniedMsg;
|
|
CHAR * pszMsgBody;
|
|
BYTE cMsgBuffer[128] ={ '\0' };
|
|
BUFFER bufMsg(cMsgBuffer, sizeof(cMsgBuffer));
|
|
DWORD dwSubStatus;
|
|
DWORD dwMsgSize;
|
|
DWORD dwMsgSizeNeeded;
|
|
BOOL bHaveCustom;
|
|
STR strAuthHdrs;
|
|
|
|
*pfFinished = FALSE;
|
|
|
|
if ( QueryRespBuf()->QuerySize() < MIN_BUFFER_SIZE_FOR_HEADERS )
|
|
{
|
|
if ( !QueryRespBuf()->Resize( MIN_BUFFER_SIZE_FOR_HEADERS ) )
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
if ( !HTTP_REQ_BASE::BuildStatusLine( QueryRespBuf(),
|
|
!IsProxyRequest() ? HT_DENIED :
|
|
HT_PROXY_AUTH_REQ,
|
|
NO_ERROR ))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// "Server: Microsoft/xxx
|
|
//
|
|
|
|
pszTail = (char*) QueryRespBuf()->QueryPtr() + QueryRespBufCB();
|
|
|
|
APPEND_VER_STR( pszTail );
|
|
|
|
//
|
|
// "Date: <GMT Time>" - Time the response was sent.
|
|
//
|
|
|
|
// build Date: uses Date/Time cache
|
|
pszTail += g_pDateTimeCache->GetFormattedCurrentDateTime( pszTail );
|
|
|
|
//
|
|
// See if we have a custom error message defined for this.
|
|
//
|
|
|
|
dwSubStatus = DeniedFlagsToSubStatus(_Filter.QueryDeniedFlags());
|
|
|
|
if (CheckCustomError(&bufMsg, !IsProxyRequest() ?
|
|
HT_DENIED : HT_PROXY_AUTH_REQ, dwSubStatus, pfFinished, &dwMsgSize, FALSE))
|
|
{
|
|
DBG_ASSERT(!*pfFinished);
|
|
|
|
pszAccessDeniedMsg = (CHAR *)bufMsg.QueryPtr();
|
|
dwMsgSizeNeeded = strlen(pszAccessDeniedMsg);
|
|
bHaveCustom = TRUE;
|
|
|
|
}
|
|
else
|
|
{
|
|
pszAccessDeniedMsg = QueryW3Instance()->QueryAccessDeniedMsg();
|
|
if ( memcmp( _strMethod.QueryStr(), "HEAD", 4 ))
|
|
{
|
|
dwMsgSize = strlen(pszAccessDeniedMsg);
|
|
}
|
|
else
|
|
{
|
|
dwMsgSize = 0;
|
|
}
|
|
dwMsgSizeNeeded = dwMsgSize;
|
|
bHaveCustom = FALSE;
|
|
}
|
|
|
|
//
|
|
// If this is not the first call, then return the current authentication
|
|
// data blob otherwise return the forms of authentication the server
|
|
// accepts
|
|
//
|
|
|
|
if ( IsAuthenticating() )
|
|
{
|
|
if ( !strAuthHdrs.Copy( IsProxyRequest() ? "Proxy-Authenticate" :
|
|
"WWW-Authenticate" ) ||
|
|
!strAuthHdrs.Append( ": " ) ||
|
|
!strAuthHdrs.Append( _strAuthInfo ) ||
|
|
!strAuthHdrs.Append( "\r\n" ) )
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( !AppendAuthenticationHdrs( &strAuthHdrs,
|
|
pfFinished ))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if ( *pfFinished )
|
|
{
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Make sure there's enough size for any ISAPI denial headers plus any
|
|
// admin specified access denied message
|
|
//
|
|
|
|
cbRespBufUsed = QueryRespBufCB();
|
|
cbRespBufLeft = QueryRespBuf()->QuerySize() - cbRespBufUsed;
|
|
|
|
cbNeeded = dwMsgSizeNeeded +
|
|
_strDenialHdrs.QueryCB() +
|
|
250 +
|
|
strAuthHdrs.QueryCB();
|
|
|
|
if ( cbNeeded > cbRespBufLeft )
|
|
{
|
|
if ( !QueryRespBuf()->Resize( cbNeeded + cbRespBufUsed ))
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
pszTail = QueryRespBufPtr() + cbRespBufUsed;
|
|
|
|
memcpy( pszTail,
|
|
strAuthHdrs.QueryStr(),
|
|
strAuthHdrs.QueryCB() );
|
|
|
|
pszTail += strAuthHdrs.QueryCB();
|
|
|
|
if ( IsKeepConnSet() )
|
|
{
|
|
if (!IsOneOne())
|
|
{
|
|
if ( !IsProxyRequest() )
|
|
{
|
|
APPEND_STRING( pszTail, "Connection: keep-alive\r\n" );
|
|
}
|
|
else
|
|
{
|
|
APPEND_STRING( pszTail, "Proxy-Connection: keep-alive\r\n" );
|
|
}
|
|
}
|
|
} else
|
|
{
|
|
if (IsOneOne())
|
|
{
|
|
if ( !IsProxyRequest() )
|
|
{
|
|
APPEND_STRING( pszTail, "Connection: close\r\n" );
|
|
} else
|
|
{
|
|
APPEND_STRING( pszTail, "Proxy-Connection: close\r\n" );
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Add any additional headers supplied by the filters plus the header
|
|
// termination
|
|
//
|
|
|
|
APPEND_NUMERIC_HEADER( pszTail, "Content-Length: ", dwMsgSize, "\r\n" );
|
|
|
|
|
|
if (!_strDenialHdrs.IsEmpty())
|
|
{
|
|
DWORD cb = _strDenialHdrs.QueryCCH();
|
|
CHAR *pszDenialHdr = _strDenialHdrs.QueryStr();
|
|
|
|
//
|
|
// We must always have CR-LF at the end
|
|
//
|
|
DBG_ASSERT( cb >= 2 && pszDenialHdr[cb - 2] == '\r' && pszDenialHdr[cb - 1] == '\n' );
|
|
|
|
APPEND_STR_HEADER( pszTail, "", _strDenialHdrs, "" );
|
|
|
|
}
|
|
|
|
if (!bHaveCustom)
|
|
{
|
|
APPEND_STRING( pszTail, "Content-Type: text/html\r\n\r\n" );
|
|
}
|
|
|
|
if ( memcmp( _strMethod.QueryStr(), "HEAD", 4 ))
|
|
{
|
|
|
|
APPEND_PSZ_HEADER( pszTail, "", pszAccessDeniedMsg, "" );
|
|
}
|
|
else
|
|
{
|
|
if (bHaveCustom)
|
|
{
|
|
DWORD dwBytesToCopy;
|
|
|
|
// Copy in only the content-type header, which is everything
|
|
// except for the message itself.
|
|
|
|
dwBytesToCopy = dwMsgSizeNeeded - dwMsgSize;
|
|
|
|
memcpy(pszTail, pszAccessDeniedMsg, dwBytesToCopy);
|
|
|
|
pszTail += dwBytesToCopy;
|
|
*pszTail++ = '\0';
|
|
}
|
|
}
|
|
|
|
DBG_ASSERT( QueryRespBuf()->QuerySize() > QueryRespBufCB() );
|
|
|
|
IF_DEBUG( PARSING )
|
|
{
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"[SendAuthNeededResp] Sending headers: %s",
|
|
QueryRespBufPtr() ));
|
|
}
|
|
|
|
//
|
|
// Add IO_FLAG_AND_RECV if we're doing a multi-leg authentication exchange and the
|
|
// connection is to be kept open for the duration of the exchange
|
|
//
|
|
|
|
return SendHeader( QueryRespBufPtr(),
|
|
(DWORD) -1,
|
|
(IO_FLAG_ASYNC | ( ( IsAuthenticating() && IsKeepConnSet() ) ?
|
|
IO_FLAG_AND_RECV :
|
|
0)),
|
|
pfFinished );
|
|
}
|
|
|
|
|
|
BOOL
|
|
HTTP_REQ_BASE::AppendAuthenticationHdrs(
|
|
STR * pstrAuthenticationHdrs,
|
|
BOOL * pfFinished
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This method adds the appropriate "WWW-Authenticate" strings to the passed
|
|
server response string
|
|
|
|
This routine assumes pRespBuf is large enough to hold the authentication
|
|
headers
|
|
|
|
Parameters:
|
|
|
|
pStrAuthenticationHdrs - buffer
|
|
pfFinished - Set to TRUE if no further processing is needed on this request
|
|
|
|
Return Value:
|
|
|
|
TRUE if successful, FALSE on error
|
|
|
|
--*/
|
|
{
|
|
CHAR * pchField;
|
|
DWORD dwAuth;
|
|
BOOL fDoBasic;
|
|
const LPSTR * apszNTProviders = NULL;
|
|
PW3_SERVER_INSTANCE pInstance = QueryW3Instance();
|
|
STACK_STR( strRealm, MAX_PATH); // make a local copy of the realm headers.
|
|
|
|
//
|
|
// If no realm was supplied in the registry, use the host name
|
|
//
|
|
|
|
if ( !_fBasicRealm )
|
|
{
|
|
fDoBasic = TRUE;
|
|
}
|
|
else
|
|
{
|
|
fDoBasic = FALSE;
|
|
}
|
|
|
|
//
|
|
// Notify any Access Denied filters the user has been denied access
|
|
//
|
|
|
|
if ( _Filter.IsNotificationNeeded( SF_NOTIFY_ACCESS_DENIED,
|
|
IsSecurePort() ))
|
|
{
|
|
if ( !_Filter.NotifyAccessDenied( _strURL.QueryStr(),
|
|
_strPhysicalPath.QueryStr(),
|
|
pfFinished ))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if ( *pfFinished )
|
|
{
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// We may not have read the metadata for this URL yet if a filter
|
|
// returned access denied during the initial read_raw notifications
|
|
//
|
|
|
|
if ( !QueryMetaData() )
|
|
{
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"[AppendAuthenticationHeaders] Warning - Metadata not read yet for NT auth list!\n"));
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// Send the correct header depending on if we're a proxy
|
|
//
|
|
|
|
if ( !IsProxyRequest() )
|
|
{
|
|
pchField = "WWW-Authenticate: ";
|
|
}
|
|
else
|
|
{
|
|
pchField = "Proxy-Authenticate: ";
|
|
}
|
|
|
|
dwAuth = QueryAuthentication();
|
|
apszNTProviders = QueryMetaData()->QueryNTProviders();
|
|
|
|
//
|
|
// generate the realm information for this request
|
|
//
|
|
|
|
strRealm.Copy( QueryMetaData()->QueryRealm()
|
|
? QueryMetaData()->QueryRealm()
|
|
: QueryHostAddr() );
|
|
|
|
//
|
|
// Append the appropriate authentication headers
|
|
//
|
|
|
|
if ( dwAuth & INET_INFO_AUTH_NT_AUTH )
|
|
{
|
|
DWORD i = 0;
|
|
|
|
//
|
|
// For each authentication package the server supports, add a
|
|
// WWW-Authenticate header
|
|
//
|
|
|
|
while ( apszNTProviders[i] )
|
|
{
|
|
if ( !pstrAuthenticationHdrs->Append( pchField ) ||
|
|
!pstrAuthenticationHdrs->Append( apszNTProviders[ i ] ) ||
|
|
!pstrAuthenticationHdrs->Append( "\r\n" ) )
|
|
{
|
|
return FALSE;
|
|
}
|
|
i++;
|
|
}
|
|
}
|
|
|
|
if ( fDoBasic && (dwAuth & INET_INFO_AUTH_CLEARTEXT) )
|
|
{
|
|
if ( !pstrAuthenticationHdrs->Append( pchField ) ||
|
|
!pstrAuthenticationHdrs->Append( "Basic realm=\"" ) ||
|
|
!pstrAuthenticationHdrs->Append( strRealm ) ||
|
|
!pstrAuthenticationHdrs->Append( "\"\r\n" ) )
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL
|
|
HTTP_REQ_BASE::ExtractClearNameAndPswd(
|
|
CHAR * pch,
|
|
STR * pstrUserName,
|
|
STR * pstrPassword,
|
|
BOOL fUUEncoded
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This method breaks a string in the form "username:password" and
|
|
places the components into pstrUserName and pstrPassword. If fUUEncoded
|
|
is TRUE, then the string is UUDecoded first.
|
|
|
|
Parameters:
|
|
|
|
pch - Pointer to <username>:<password>
|
|
|
|
Return Value:
|
|
|
|
TRUE if successful, FALSE on error
|
|
|
|
--*/
|
|
{
|
|
STACK_STR( strDecoded, MAX_PATH );
|
|
CHAR * pchtmp;
|
|
|
|
pch = SkipWhite( pch );
|
|
|
|
if ( fUUEncoded )
|
|
{
|
|
if ( !uudecode( pch,
|
|
&strDecoded ))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
pch = strDecoded.QueryStrA();
|
|
}
|
|
|
|
pchtmp = SkipTo( pch, TEXT(':') );
|
|
|
|
if ( *pchtmp == TEXT(':') )
|
|
{
|
|
*pchtmp = TEXT('\0');
|
|
|
|
if ( !_strUserName.Copy( pch ) ||
|
|
!_strPassword.Copy( pchtmp + 1 ))
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
HANDLE
|
|
HTTP_REQ_BASE::QueryPrimaryToken(
|
|
HANDLE * phDelete
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This method returns a non-impersonation user token handle that's
|
|
usable with CreateProcessAsUser.
|
|
|
|
Parameters:
|
|
|
|
phDelete - If returned as non-null, the caller is responsible for calling
|
|
CloseHandle on this value when done using the returned value.
|
|
|
|
Return Value:
|
|
|
|
The primary token handle if successful, FALSE otherwise
|
|
|
|
--*/
|
|
{
|
|
*phDelete = NULL;
|
|
|
|
if ( !UseVrAccessToken() )
|
|
{
|
|
return _tcpauth.QueryPrimaryToken();
|
|
}
|
|
|
|
return _pMetaData->QueryVrPrimaryAccessToken();
|
|
}
|
|
|
|
|
|
|