559 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			559 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /**********************************************************************/
 | ||
| /**                       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();
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| 
 |