|
|
/**********************************************************************/ /** 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(); }
|