(file) Return to HTTPAuthenticatorDelegator.cpp CVS log (file) (dir) Up to [Pegasus] / pegasus / src / Pegasus / Server

File: [Pegasus] / pegasus / src / Pegasus / Server / HTTPAuthenticatorDelegator.cpp (download)
Revision: 1.27, Wed Oct 22 13:26:13 2003 UTC (20 years, 8 months ago) by karl
Branch: MAIN
CVS Tags: RELEASE_2_3_0-root, RELEASE_2_3_0-branch, POST_LICENSE_UPDATE_2003
Changes since 1.26: +5 -3 lines
PEP 55 Update license on source files to current license text and date

//%2003////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2000, 2001, 2002  BMC Software, Hewlett-Packard Development
// Company, L. P., IBM Corp., The Open Group, Tivoli Systems.
// Copyright (c) 2003 BMC Software; Hewlett-Packard Development Company, L. P.;
// IBM Corp.; EMC Corporation, The Open Group.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// 
// THE ABOVE COPYRIGHT NOTICE AND THIS PERMISSION NOTICE SHALL BE INCLUDED IN
// ALL COPIES OR SUBSTANTIAL PORTIONS OF THE SOFTWARE. THE SOFTWARE IS PROVIDED
// "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
// LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
//==============================================================================
//
// Author:  Nag Boranna,   Hewlett-Packard Company(nagaraja_boranna@hp.com)
//
// Modified By: Dave Rosckes (rosckes@us.ibm.com)
//
//%/////////////////////////////////////////////////////////////////////////////

#include <Pegasus/Common/Constants.h>
#include <Pegasus/Common/HTTPAcceptor.h>
#include <Pegasus/Common/HTTPConnection.h>
#include <Pegasus/Common/HTTPMessage.h>
#include <Pegasus/Common/XmlWriter.h>
#include <Pegasus/Config/ConfigManager.h>
#include "HTTPAuthenticatorDelegator.h"
 
#ifdef PEGASUS_KERBEROS_AUTHENTICATION
#include <Pegasus/Common/CIMKerberosSecurityAssociation.h>
#endif 

PEGASUS_USING_STD;

PEGASUS_NAMESPACE_BEGIN

#ifdef PEGASUS_KERBEROS_AUTHENTICATION
/**
    Constant representing the Kerberos authentication challenge header.
*/
static const String KERBEROS_CHALLENGE_HEADER = "WWW-Authenticate: Negotiate ";
#endif

HTTPAuthenticatorDelegator::HTTPAuthenticatorDelegator(
    Uint32 operationMessageQueueId,
    Uint32 exportMessageQueueId)
   : Base(PEGASUS_QUEUENAME_HTTPAUTHDELEGATOR,
          MessageQueue::getNextQueueId()),
    _operationMessageQueueId(operationMessageQueueId),
    _exportMessageQueueId(exportMessageQueueId)
{
    PEG_METHOD_ENTER(TRC_HTTP,
        "HTTPAuthenticatorDelegator::HTTPAuthenticatorDelegator");

    _authenticationManager = new AuthenticationManager();

    PEG_METHOD_EXIT();
}

HTTPAuthenticatorDelegator::~HTTPAuthenticatorDelegator()
{
    PEG_METHOD_ENTER(TRC_HTTP,
        "HTTPAuthenticatorDelegator::~HTTPAuthenticatorDelegator");

    delete _authenticationManager;

    PEG_METHOD_EXIT();
}

void HTTPAuthenticatorDelegator::enqueue(Message* message) throw(IPCException)
{
    handleEnqueue(message);
}

void HTTPAuthenticatorDelegator::_sendResponse(
    Uint32 queueId,
    Array<Sint8>& message)
{
    PEG_METHOD_ENTER(TRC_HTTP,
        "HTTPAuthenticatorDelegator::_sendResponse");

    MessageQueue* queue = MessageQueue::lookup(queueId);

    if (queue)
    {
        HTTPMessage* httpMessage = new HTTPMessage(message);
	httpMessage->dest = queue->getQueueId();

        queue->enqueue(httpMessage);
    }

    PEG_METHOD_EXIT();
}

#ifdef PEGASUS_KERBEROS_AUTHENTICATION
void HTTPAuthenticatorDelegator::_sendSuccess(
    Uint32 queueId,
    const String& authResponse)
{
    PEG_METHOD_ENTER(TRC_HTTP,
        "HTTPAuthenticatorDelegator::_sendSuccess");

    //
    // build OK (200) response message
    //

    Array<Sint8> message;
    XmlWriter::appendOKResponseHeader(message, authResponse);

    _sendResponse(queueId, message);

    PEG_METHOD_EXIT();
}
#endif

void HTTPAuthenticatorDelegator::_sendChallenge(
    Uint32 queueId,
    const String& authResponse)
{
    PEG_METHOD_ENTER(TRC_HTTP,
        "HTTPAuthenticatorDelegator::_sendChallenge");

    //
    // build unauthorized (401) response message
    //

    Array<Sint8> message;
    XmlWriter::appendUnauthorizedResponseHeader(message, authResponse);

    _sendResponse(queueId, message);

    PEG_METHOD_EXIT();
}


void HTTPAuthenticatorDelegator::_sendError(
    Uint32 queueId,
    const String errorMessage)
{
    PEG_METHOD_ENTER(TRC_HTTP,
        "HTTPAuthenticatorDelegator::_sendError");

    //
    // build error response message
    //

    Array<Sint8> message;
    //
    //ATTN: Need an ErrorResponseHeader() in XmlWriter
    //
    //message = XmlWriter::formatErrorResponseHeader(errorMessage);

    _sendResponse(queueId, message);

    PEG_METHOD_EXIT();
}


void HTTPAuthenticatorDelegator::handleEnqueue(Message *message)
{
    PEG_METHOD_ENTER(TRC_HTTP,
        "HTTPAuthenticatorDelegator::handleEnqueue");

    if (!message)
    {
        PEG_METHOD_EXIT();
        return;
    }

    // Flag indicating whether the message should be deleted after handling.
    // This should be set to false by handleHTTPMessage when the message is
    // passed as is to another queue.
    Boolean deleteMessage = true;
   
    if (message->getType() == HTTP_MESSAGE)
    {
        handleHTTPMessage((HTTPMessage*)message, deleteMessage);
    }

    if (deleteMessage)
    {
        delete message;
    }

    PEG_METHOD_EXIT();
}

void HTTPAuthenticatorDelegator::handleEnqueue()
{
    PEG_METHOD_ENTER(TRC_HTTP,
        "HTTPAuthenticatorDelegator::handleEnqueue");

    Message* message = dequeue();
    if(message)
       handleEnqueue(message);

    PEG_METHOD_EXIT();
}

void HTTPAuthenticatorDelegator::handleHTTPMessage(
    HTTPMessage* httpMessage,
    Boolean & deleteMessage)
{  
    PEG_METHOD_ENTER(TRC_HTTP,
        "HTTPAuthenticatorDelegator::handleHTTPMessage");

    Boolean authenticated = false;
    deleteMessage = true;

    // ATTN-RK-P3-20020408: This check probably shouldn't be necessary, but
    // we're getting an empty message when the client closes the connection
    if (httpMessage->message.size() == 0)
    {
        // The message is empty; just drop it
        PEG_METHOD_EXIT();
        return;
    }

    //
    // get the configured authentication flag
    //
    ConfigManager* configManager = ConfigManager::getInstance();

    Boolean enableAuthentication = false;

    if (String::equal(
        configManager->getCurrentValue("enableAuthentication"), "true"))
    {
        enableAuthentication = true;
    }

    //
    // Save queueId:
    //
    Uint32 queueId = httpMessage->queueId;

    //
    // Parse the HTTP message:
    //
    String startLine;
    Array<HTTPHeader> headers;
    Uint32 contentLength;

    httpMessage->parse(startLine, headers, contentLength);
    
    //
    // Parse the request line:
    //
    String methodName;
    String requestUri;
    String httpVersion;
    HttpMethod httpMethod = HTTP_METHOD__POST;

    HTTPMessage::parseRequestLine(
        startLine, methodName, requestUri, httpVersion);

    //
    //  Set HTTP method for the request
    //
    if (methodName == "M-POST")
    {
        httpMethod = HTTP_METHOD_M_POST;
    }

    if (methodName != "M-POST" && methodName != "POST")
    {
        // Only POST and M-POST are implemented by this server
        Array<Sint8> message;
        message = XmlWriter::formatHttpErrorRspMessage(
            HTTP_STATUS_NOTIMPLEMENTED);
        _sendResponse(queueId, message);
    }
    else if ((httpMethod == HTTP_METHOD_M_POST) &&
             (httpVersion == "HTTP/1.0"))
    {
        //
        //  M-POST method is not valid with version 1.0
        //
        Array<Sint8> message;
        message = XmlWriter::formatHttpErrorRspMessage(
            HTTP_STATUS_BADREQUEST);
        _sendResponse(queueId, message);
    }
    else
    {
        //
        // Process M-POST and POST messages:
        //
	Logger::put(Logger::STANDARD_LOG, System::CIMSERVER, Logger::TRACE,
	    "HTTPAuthenticatorDelegator - M-POST/POST processing start");

	httpMessage->message.append('\0');

        //
        // Search for Authorization header:
        //
        String authorization = String::EMPTY;

        if ( HTTPMessage::lookupHeader(
             headers, "PegasusAuthorization", authorization, false) &&
             enableAuthentication
           )
        {
            //
            // Do pegasus/local authentication
            //
            authenticated = 
                _authenticationManager->performPegasusAuthentication(
                    authorization,
                    httpMessage->authInfo);

            if (!authenticated)
            {
                String authChallenge = String::EMPTY;
                String authResp = String::EMPTY;

                authResp = _authenticationManager->getPegasusAuthResponseHeader(
                    authorization,
                    httpMessage->authInfo);

                if (!String::equal(authResp, String::EMPTY))
                {
                    _sendChallenge(queueId, authResp);
                }
                else
                {
                    _sendError(queueId, "Invalid Request");
                }

                PEG_METHOD_EXIT();
                return;
            }
        }

        if ( HTTPMessage::lookupHeader(
             headers, "Authorization", authorization, false) &&
             enableAuthentication
           )
        {
#ifdef PEGASUS_KERBEROS_AUTHENTICATION
	    // The presence of a Security Association indicates that Kerberos is being
	    // used.
	    CIMKerberosSecurityAssociation *sa = httpMessage->authInfo->getSecurityAssociation();
	    if (sa)
	    {
		sa->setClientSentAuthorization(true);
	    }
#endif	
            //
            // Do http authentication if not authenticated already
            //
            if (!authenticated)
            {
                authenticated =
                    _authenticationManager->performHttpAuthentication(
                        authorization,
                        httpMessage->authInfo);

                if (!authenticated)
                {
                    //ATTN: the number of challenges get sent for a 
                    //      request on a connection can be pre-set.
#ifdef PEGASUS_KERBEROS_AUTHENTICATION
                    // Kerberos authentication needs access to the AuthenticationInfo
                    // object for this session in order to set up the reference to the
                    // CIMKerberosSecurityAssociation object for this session.

                    String authResp =   
                        _authenticationManager->getHttpAuthResponseHeader(httpMessage->authInfo);
#else
                    String authResp =
                        _authenticationManager->getHttpAuthResponseHeader();
#endif
                    if (!String::equal(authResp, String::EMPTY))
                    {
                        _sendChallenge(queueId, authResp);
                    }
                    else
                    {
                        _sendError(queueId, "Invalid Request");
                    }

                    PEG_METHOD_EXIT();
                    return;
                }
	    }  // first not authenticated check
	}  // "Authorization" header check

#ifdef PEGASUS_KERBEROS_AUTHENTICATION
	// The presence of a Security Association indicates that Kerberos is being
	// used.
	CIMKerberosSecurityAssociation *sa = httpMessage->authInfo->getSecurityAssociation();

	// This will process a request with no content.
	if (sa && authenticated)
	{
	    if (sa->getServerToken().size())
	    {
		// This will handle the scenario where client did not send data (content) but
		// is authenticated.  After the client receives the success it should will
		// send the request.  For mutual authentication the client may choose not to
		// send request data until the context is fully established.
		// Note:  if mutual authentication wass not requested by the client then
		// no server token will be available.
		if (contentLength == 0)
		{
                    String authResp =   
                        _authenticationManager->getHttpAuthResponseHeader(httpMessage->authInfo);
                    if (!String::equal(authResp, String::EMPTY))
                    {
                        _sendSuccess(queueId, authResp);
                    }
                    else
                    {
                        _sendError(queueId, "Invalid Request");
                    }

                    PEG_METHOD_EXIT();
                    return;
		}
	    }
	}

	// This will process a request without an authorization record.
	if (sa && !authenticated)
	{
	    // Not authenticated can result from the client not sending an authorization
	    // record due to a previous authentication.  In this scenario the client
            // was previous authenticated but chose not to send an authorization
            // record.  The Security Association maintains state so a request will not
            // be processed unless the Security association thinks the client is authenticated.

	    // This will handle the scenario where the client was authenticated in the
	    // previous request but choose not to send an authorization record.
	    if (sa->getClientAuthenticated())
	    {	
		authenticated = true;
	    }
	}

	// The following is processing to unwrap (unencrypt) the message received from the
	// client when using kerberos authentication.
	// For Kerberos there should always be an "Authorization" record sent with the request
	// so the authenticated flag in the method should always be checked to verify that
	// the client is actually authenticated.  If no "Authoriztion" was sent then the
	// client can't be authenticated.  The "Authorization" record was processed above
	// and if the "Authorization" record was successfully processed then the client
	// is authenticated.
	if (sa  &&  authenticated)
	{
	    Uint32 rc;  // return code for the wrap
	    Array<Sint8> final_buffer;
	    final_buffer.clear();
	    Array<Sint8> header_buffer;
	    header_buffer.clear();
	    Array<Sint8> inWrappedMessage;
	    inWrappedMessage.clear();
	    Array<Sint8> outUnwrappedMessage;
	    outUnwrappedMessage.clear();

	    // The message needs to be parsed in order to distinguise between the
	    // headers and content. The message was parsed earlier in this method
	    // so we can use the contentLength set during that parse. When parsing
	    // the code breaks out of the loop as soon as it finds the double
	    // separator that terminates the headers so the headers and content
	    // can be easily separated.

	    // IMPORTANT - NOTE NOTE NOTE - IMPORTANT
	    // Need to increase the content size by one because earlier a
	    // NULL terminator was added causing the unwrap to think that the
	    // wrapped message has a bad signature.  Anything that was added
	    // to the content portion must be handled otherwise unwrap will
	    // return an error.  So if additional characters are added in the
	    // future the content size needs to be adjusted.
	    int charsAdded = 1; // increase by the number of chars added to content
	    contentLength = contentLength + charsAdded;

	    // Extract the unwrapped headers
	    for (Uint32 i = 0; i < (httpMessage->message.size()-contentLength); i++)
	    {
		header_buffer.append(httpMessage->message[i]);
	    }

	    // Extract the wrapped content
	    // Note: Need to decrease the message size by the number of characters
	    // added to content.  See important note above regarding content size.
	    for (Uint32 i = (httpMessage->message.size()-contentLength);
		 i < (httpMessage->message.size()-charsAdded); i++)
	    {
		inWrappedMessage.append(httpMessage->message[i]);
	    }

	    rc = sa->unwrap_message(inWrappedMessage,	  
				    outUnwrappedMessage);  // ASCII

	    if (rc) 
	    {
		// clear out the outUnwrappedMessage; if unwrapping is required
		// and it fails we need to send back a bad request.  A message
		// left in an wrapped state will be a severe failue later.  Reason
		// for unwrap failing may be due to a problem with the credentials
		// or context or some other failure may have occurred.
		outUnwrappedMessage.clear();
		// build a bad request.  We do not want to risk sending back
		// data that can't be authenticated.
		final_buffer = 
		  XmlWriter::formatHttpErrorRspMessage(HTTP_STATUS_BADREQUEST);
		//remove the last separater; the authentication record still
		// needs to be added.
		final_buffer.remove(final_buffer.size());  // "\n"
		final_buffer.remove(final_buffer.size());  // "\r"

		// Build the WWW_Authenticate record with token.
		String authResp =   
		  _authenticationManager->getHttpAuthResponseHeader(httpMessage->authInfo);
		// error occurred on wrap so the final_buffer needs to be built
		// differently
		final_buffer << authResp;
		// add double separaters to end
		final_buffer << "\r\n";
		final_buffer << "\r\n";

		_sendResponse(queueId, final_buffer);
		PEG_METHOD_EXIT();
		return;
	    }

            // If outUnwrappedMessage does not have any content data this is a result
            // of no data available.  This could be a result of the client not sending
            // any content data.  This is not unique to Kerberos so this will not be
            // handled here but it will be handled when the content is processed. Or,
	    // the client did not wrap message...this is okay.
            // If the unwrap resulted in no data when there should have been data then
            // this should have been caught above when the unwrap returned a bad return
            // code
            if (final_buffer.size() == 0)
            {
		// if outUnwrappedMessage is empty that indicates client did not
		// wrap the message.  The original message will be used.
		if (outUnwrappedMessage.size())
		{
		    final_buffer.appendArray(header_buffer);
		    final_buffer.appendArray(outUnwrappedMessage);
		    final_buffer.append('\0'); // add back char that was appended earlier
		    // Note: if additional characters added in future add back here
		}
            }
            else
            {
		// The final buffer should not have any data at this point. If it
		// does end the server because something bad happened.
		Logger::put(Logger::STANDARD_LOG, System::CIMSERVER, Logger::TRACE,
			    "HTTPAuthenticatorDelegator - the final buffer should not have data");
                PEGASUS_ASSERT(0);
            }

	    // replace the existing httpMessage with the headers and unwrapped message
	    // If the final buffer has not been set then the original httpMessage will be used.
	    if (final_buffer.size())
	    {
		    httpMessage->message.clear();
		    httpMessage->message = final_buffer;
	    }
	} // if not kerberos and client not authenticated
#endif

        if ( authenticated || !enableAuthentication )
        {
            //
            // Search for "CIMOperation" header:
            //
            String cimOperation;

            if (HTTPMessage::lookupHeader(
                headers, "CIMOperation", cimOperation, true))
            {
		Logger::put(Logger::STANDARD_LOG, System::CIMSERVER, Logger::TRACE,
			    "HTTPAuthenticatorDelegator - CIMOperation: $0 ",cimOperation);

                MessageQueue* queue =
                    MessageQueue::lookup(_operationMessageQueueId);

                if (queue)
                {
		   httpMessage->dest = queue->getQueueId();
		   
                    queue->enqueue(httpMessage);
                    deleteMessage = false;
                }
            }
            else if (HTTPMessage::lookupHeader(
                headers, "CIMExport", cimOperation, true))
            {
		Logger::put(Logger::STANDARD_LOG, System::CIMSERVER, Logger::TRACE,
			    "HTTPAuthenticatorDelegator - CIMExport: $0 ",cimOperation);

                MessageQueue* queue =
                    MessageQueue::lookup(_exportMessageQueueId);

                if (queue)
                {
		   httpMessage->dest = queue->getQueueId();

		   queue->enqueue(httpMessage);
		   deleteMessage = false;
                }
            }
            else
            {
                // We don't recognize this request message type

                // The Specification for CIM Operations over HTTP reads:
                //
                //     3.3.4. CIMOperation
                //
                //     If a CIM Server receives a CIM Operation request without
                //     this [CIMOperation] header, it MUST NOT process it as if
                //     it were a CIM Operation Request.  The status code
                //     returned by the CIM Server in response to such a request
                //     is outside of the scope of this specification.
                //
                //     3.3.5. CIMExport
                //
                //     If a CIM Listener receives a CIM Export request without
                //     this [CIMExport] header, it MUST NOT process it.  The
                //     status code returned by the CIM Listener in response to
                //     such a request is outside of the scope of this
                //     specification.
                //
                // The author has chosen to send a 400 Bad Request error, but
                // without the CIMError header since this request must not be
                // processed as a CIM request.

                Array<Sint8> message;
                message = XmlWriter::formatHttpErrorRspMessage(
                    HTTP_STATUS_BADREQUEST);
                _sendResponse(queueId, message);
                PEG_METHOD_EXIT();
                return;
            } // bad request
        } // authenticated and enableAuthentication check
        else
        {  // client not authenticated; send challenge
#ifdef PEGASUS_KERBEROS_AUTHENTICATION
            String authResp =    
                _authenticationManager->getHttpAuthResponseHeader(httpMessage->authInfo);
#else
            String authResp =
                _authenticationManager->getHttpAuthResponseHeader();
#endif

            if (!String::equal(authResp, String::EMPTY))
            {
                _sendChallenge(queueId, authResp);
            }
            else
            {
                _sendError(queueId, "Invalid Request");
            }
        }
    } // M-POST and POST processing

    PEG_METHOD_EXIT();
}

PEGASUS_NAMESPACE_END

No CVS admin address has been configured
Powered by
ViewCVS 0.9.2