![]() ![]() |
![]() |
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 |