/*********************************************************************** * * Copyright (C) 2006 Novell, Inc. All Rights Reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; version 2.1 * of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, Novell, Inc. * * To contact Novell about this file by physical or electronic mail, * you may find current contact information at www.novell.com. * * Author: Juan Carlos Luciani * ***********************************************************************/ //===[ Include files ]===================================================== #include "internal.h" //===[ Type definitions ]================================================== // // Parse states // #define AWAITING_ROOT_ELEMENT_START 0x0 #define AWAITING_ROOT_ELEMENT_END 0x1 #define AWAITING_STATUS_ELEMENT_START 0x2 #define AWAITING_STATUS_ELEMENT_END 0x3 #define AWAITING_STATUS_DATA 0x4 #define AWAITING_DESCRIPTION_ELEMENT_START 0x5 #define AWAITING_DESCRIPTION_ELEMENT_END 0x6 #define AWAITING_DESCRIPTION_DATA 0x7 #define AWAITING_SESSION_TOKEN_ELEMENT_START 0x8 #define AWAITING_SESSION_TOKEN_ELEMENT_END 0x9 #define AWAITING_SESSION_TOKEN_DATA 0xA #define AWAITING_LIFETIME_DATA 0xB #define AWAITING_LIFETIME_ELEMENT_START 0xC #define AWAITING_LIFETIME_ELEMENT_END 0xD #define AWAITING_AUTH_TOKEN_ELEMENT_START 0xE #define AWAITING_AUTH_TOKEN_ELEMENT_END 0xF #define AWAITING_AUTH_TOKEN_DATA 0x10 #define AWAITING_REALM_DATA 0x12 #define AWAITING_REALM_ELEMENT_END 0x13 #define DONE_PARSING 0x14 // // Authentication Response Parse Structure // typedef struct _AuthRespParse { XML_Parser p; int state; int elementDataProcessed; AuthenticateResp *pAuthenticateResp; CasaStatus status; } AuthRespParse, *PAuthRespParse; //===[ Function prototypes ]=============================================== //===[ Global variables ]================================================== //++======================================================================= char* BuildAuthenticateMsg( IN AuthContext *pAuthContext, IN char *pAuthMechToken) // // Arguments: // // Returns: // // Abstract: // // Notes: // // L2 //=======================================================================-- { char *pMsg = NULL; int bufferSize; DbgTrace(1, "-BuildAuthenticateMsg- Start\n", 0); /* * The format of the authentication request message is as follows: * * * * realm value * authentication mechanism token data * * */ // Determine the buffer size necessary to hold the msg bufferSize = strlen(XML_DECLARATION) + 2 // crlf + 1 // < + strlen(AUTH_REQUEST_ELEMENT_NAME) + 3 // >crlf + 1 // < + strlen(REALM_ELEMENT_NAME) + 1 // > + strlen(pAuthContext->pContext) + 2 // crlf + 1 // < + strlen(AUTH_MECH_TOKEN_ELEMENT_NAME) + 1 // > + strlen(pAuthMechToken) + 2 // crlf + 2 // null // Allocate the msg buffer pMsg = (char*) malloc(bufferSize); if (pMsg) { // Now build the message memset(pMsg, 0, bufferSize); strcat(pMsg, XML_DECLARATION); strcat(pMsg, "\r\n"); strcat(pMsg, "<"); strcat(pMsg, AUTH_REQUEST_ELEMENT_NAME); strcat(pMsg, ">\r\n"); strcat(pMsg, "<"); strcat(pMsg, REALM_ELEMENT_NAME); strcat(pMsg, ">"); strcat(pMsg, pAuthContext->pContext); strcat(pMsg, "\r\n"); strcat(pMsg, "<"); strcat(pMsg, AUTH_MECH_TOKEN_ELEMENT_NAME); strcat(pMsg, ">"); strcat(pMsg, pAuthMechToken); strcat(pMsg, "\r\n"); strcat(pMsg, ""); } else { DbgTrace(0, "-BuildAuthenticateMsg- Buffer allocation error\n", 0); } DbgTrace(1, "-BuildAuthenticateMsg- End, pMsg = %08X\n", pMsg); return pMsg; } //++======================================================================= static void XMLCALL AuthRespStartElementHandler( IN AuthRespParse *pAuthRespParse, IN const XML_Char *name, IN const XML_Char **atts) // // Arguments: // // Returns: // // Abstract: // // Notes: // // L2 //=======================================================================-- { DbgTrace(2, "-AuthRespStartElementHandler- Start\n", 0); // Proceed based on the state switch (pAuthRespParse->state) { case AWAITING_ROOT_ELEMENT_START: // In this state, we are only expecting the Authentication // Response Element. if (strcmp(name, AUTH_RESPONSE_ELEMENT_NAME) == 0) { // Good, advance to the next state. pAuthRespParse->state = AWAITING_STATUS_ELEMENT_START; } else { DbgTrace(0, "-AuthRespStartElementHandler- Un-expected start element\n", 0); XML_StopParser(pAuthRespParse->p, XML_FALSE); } break; case AWAITING_STATUS_ELEMENT_START: // In this state, we are only expecting the Status Element. if (strcmp(name, STATUS_ELEMENT_NAME) == 0) { // Good, advance to the next state. pAuthRespParse->state = AWAITING_DESCRIPTION_ELEMENT_START; } else { DbgTrace(0, "-AuthRespStartElementHandler- Un-expected start element\n", 0); XML_StopParser(pAuthRespParse->p, XML_FALSE); } break; case AWAITING_DESCRIPTION_ELEMENT_START: // In this state, we are only expecting the Description Element. if (strcmp(name, DESCRIPTION_ELEMENT_NAME) == 0) { // Good, advance to the next state. pAuthRespParse->state = AWAITING_DESCRIPTION_DATA; } else { DbgTrace(0, "-AuthRespStartElementHandler- Un-expected start element\n", 0); XML_StopParser(pAuthRespParse->p, XML_FALSE); } break; case AWAITING_SESSION_TOKEN_ELEMENT_START: // In this state, we are only expecting the Session Token Element. if (strcmp(name, SESSION_TOKEN_ELEMENT_NAME) == 0) { // Good, advance to the next state. pAuthRespParse->state = AWAITING_LIFETIME_ELEMENT_START; } else { DbgTrace(0, "-AuthRespStartElementHandler- Un-expected start element\n", 0); XML_StopParser(pAuthRespParse->p, XML_FALSE); } break; case AWAITING_LIFETIME_ELEMENT_START: // In this state, we are only expecting the Lifetime Element. if (strcmp(name, LIFETIME_ELEMENT_NAME) == 0) { // Good, advance to the next state. pAuthRespParse->state = AWAITING_LIFETIME_DATA; } else { DbgTrace(0, "-AuthRespStartElementHandler- Un-expected start element\n", 0); XML_StopParser(pAuthRespParse->p, XML_FALSE); } break; default: DbgTrace(0, "-AuthRespStartElementHandler- Un-expected state = %d\n", pAuthRespParse->state); XML_StopParser(pAuthRespParse->p, XML_FALSE); break; } DbgTrace(2, "-AuthRespStartElementHandler- End\n", 0); } //++======================================================================= static CasaStatus ConsumeElementData( IN AuthRespParse *pAuthRespParse, IN const XML_Char *s, IN int len, INOUT char **ppElementData, INOUT int *pElementDataLen) // // Arguments: // // Returns: // // Abstract: // // Notes: // // L2 //=======================================================================-- { CasaStatus retStatus = CASA_STATUS_SUCCESS; DbgTrace(3, "-ConsumeElementData- Start\n", 0); // Proceed based on whether or not we have already consumed data // for this element. if (*ppElementData == NULL) { // We have not yet consumed data for this element pAuthRespParse->elementDataProcessed = len; // Allocate a buffer to hold this element data (null terminated). *ppElementData = (char*) malloc(len + 1); if (*ppElementData) { memset(*ppElementData, 0, len + 1); memcpy(*ppElementData, s, len); // Return the length of the element data buffer *pElementDataLen = pAuthRespParse->elementDataProcessed + 1; } else { DbgTrace(0, "-ConsumeElementData- Buffer allocation failure\n", 0); retStatus = CasaStatusBuild(CASA_SEVERITY_ERROR, CASA_FACILITY_AUTHTOKEN, CASA_STATUS_INSUFFICIENT_RESOURCES); } } else { char *pNewBuf; // We have already received token data, append this data to it. pNewBuf = (char*) malloc(pAuthRespParse->elementDataProcessed + len + 1); if (pNewBuf) { memset(pNewBuf, 0, pAuthRespParse->elementDataProcessed + len + 1); memcpy(pNewBuf, *ppElementData, pAuthRespParse->elementDataProcessed); memcpy(pNewBuf + pAuthRespParse->elementDataProcessed, s, len); pAuthRespParse->elementDataProcessed += len; // Swap the buffers free(*ppElementData); *ppElementData = pNewBuf; // Return the length of the element data buffer *pElementDataLen = pAuthRespParse->elementDataProcessed + 1; } else { DbgTrace(0, "-ConsumeElementData- Buffer allocation failure\n", 0); retStatus = CasaStatusBuild(CASA_SEVERITY_ERROR, CASA_FACILITY_AUTHTOKEN, CASA_STATUS_INSUFFICIENT_RESOURCES); } } DbgTrace(3, "-ConsumeElementData- End, retStatus = %08X\n", retStatus); return retStatus; } //++======================================================================= static void XMLCALL AuthRespCharDataHandler( IN AuthRespParse *pAuthRespParse, IN const XML_Char *s, IN int len) // // Arguments: // // Returns: // // Abstract: // // Notes: // // L2 //=======================================================================-- { DbgTrace(2, "-AuthRespCharDataHandler- Start\n", 0); // Just exit if being called to process LF and CR characters if (len == 1 && ((*s == '\n') || (*s == '\r'))) { goto exit; } // Proceed based on the state switch (pAuthRespParse->state) { case AWAITING_DESCRIPTION_DATA: case AWAITING_DESCRIPTION_ELEMENT_END: // Ignore the status description data for now. // tbd // Advanced to the next state pAuthRespParse->state = AWAITING_DESCRIPTION_ELEMENT_END; break; case AWAITING_STATUS_DATA: // Set the appropriate status in the AuthenticationResp based on the // returned status. if (strncmp(HTTP_OK_STATUS_CODE, s, len) == 0) { pAuthRespParse->status = CASA_STATUS_SUCCESS; } else if (strncmp(HTTP_UNAUTHORIZED_STATUS_CODE, s, len) == 0) { pAuthRespParse->status = CasaStatusBuild(CASA_SEVERITY_ERROR, CASA_FACILITY_AUTHTOKEN, CASA_STATUS_AUTHENTICATION_FAILURE); } else if (strncmp(HTTP_SERVER_ERROR_STATUS_CODE, s, len) == 0) { pAuthRespParse->status = CasaStatusBuild(CASA_SEVERITY_ERROR, CASA_FACILITY_AUTHTOKEN, CASA_STATUS_SERVER_ERROR); } else { DbgTrace(0, "-AuthRespCharDataHandler- Un-expected status\n", 0); pAuthRespParse->status = CasaStatusBuild(CASA_SEVERITY_ERROR, CASA_FACILITY_AUTHTOKEN, CASA_STATUS_UNSUCCESSFUL); } // Advanced to the next state pAuthRespParse->state = AWAITING_STATUS_ELEMENT_END; break; case AWAITING_LIFETIME_DATA: // Convert the lifetime string to a numeric value pAuthRespParse->pAuthenticateResp->tokenLifetime = dtoul(s, len); // Advanced to the next state pAuthRespParse->state = AWAITING_LIFETIME_ELEMENT_END; break; case AWAITING_SESSION_TOKEN_DATA: case AWAITING_SESSION_TOKEN_ELEMENT_END: // Consume the data pAuthRespParse->status = ConsumeElementData(pAuthRespParse, s, len, &pAuthRespParse->pAuthenticateResp->pToken, &pAuthRespParse->pAuthenticateResp->tokenLen); if (CASA_SUCCESS(pAuthRespParse->status)) { // Advanced to the next state pAuthRespParse->state = AWAITING_SESSION_TOKEN_ELEMENT_END; } else { XML_StopParser(pAuthRespParse->p, XML_FALSE); } break; default: DbgTrace(0, "-AuthRespCharDataHandler- Un-expected state = %d\n", pAuthRespParse->state); XML_StopParser(pAuthRespParse->p, XML_FALSE); break; } exit: DbgTrace(2, "-AuthRespCharDataHandler- End\n", 0); } //++======================================================================= static void XMLCALL AuthRespEndElementHandler( IN AuthRespParse *pAuthRespParse, IN const XML_Char *name) // // Arguments: // // Returns: // // Abstract: // // Notes: // // L2 //=======================================================================-- { DbgTrace(2, "-AuthRespEndElementHandler- Start\n", 0); // Proceed based on the state switch (pAuthRespParse->state) { case AWAITING_ROOT_ELEMENT_END: // In this state, we are only expecting the Authentication // Response Element. if (strcmp(name, AUTH_RESPONSE_ELEMENT_NAME) == 0) { // Done. pAuthRespParse->state = DONE_PARSING; } else { DbgTrace(0, "-AuthRespEndHandler- Un-expected end element\n", 0); XML_StopParser(pAuthRespParse->p, XML_FALSE); } break; case AWAITING_DESCRIPTION_ELEMENT_END: // In this state, we are only expecting the Description Element. if (strcmp(name, DESCRIPTION_ELEMENT_NAME) == 0) { // Good, advance to the next state. pAuthRespParse->state = AWAITING_STATUS_DATA; } else { DbgTrace(0, "-AuthRespEndElementHandler- Un-expected end element\n", 0); XML_StopParser(pAuthRespParse->p, XML_FALSE); } break; case AWAITING_STATUS_ELEMENT_END: // In this state, we are only expecting the Status Element. if (strcmp(name, STATUS_ELEMENT_NAME) == 0) { // Good, advance to the next state based on the status code. if (CASA_SUCCESS(pAuthRespParse->status)) { // The request completed successfully pAuthRespParse->state = AWAITING_SESSION_TOKEN_ELEMENT_START; } else { pAuthRespParse->state = AWAITING_ROOT_ELEMENT_END; } } else { DbgTrace(0, "-AuthRespEndElementHandler- Un-expected start element\n", 0); XML_StopParser(pAuthRespParse->p, XML_FALSE); } break; case AWAITING_LIFETIME_ELEMENT_END: // In this state, we are only expecting the Lifetime Element. if (strcmp(name, LIFETIME_ELEMENT_NAME) == 0) { // Good, advance to the next state. pAuthRespParse->state = AWAITING_SESSION_TOKEN_DATA; } else { DbgTrace(0, "-AuthRespEndElementHandler- Un-expected start element\n", 0); XML_StopParser(pAuthRespParse->p, XML_FALSE); } break; case AWAITING_SESSION_TOKEN_ELEMENT_END: // In this state, we are only expecting the Session Token Element. if (strcmp(name, SESSION_TOKEN_ELEMENT_NAME) == 0) { // Good, advance to the next state. pAuthRespParse->state = AWAITING_ROOT_ELEMENT_END; } else { DbgTrace(0, "-AuthRespEndElementHandler- Un-expected start element\n", 0); XML_StopParser(pAuthRespParse->p, XML_FALSE); } break; default: DbgTrace(0, "-AuthRespEndElementHandler- Un-expected state = %d\n", pAuthRespParse->state); XML_StopParser(pAuthRespParse->p, XML_FALSE); break; } DbgTrace(2, "-AuthRespEndElementHandler- End\n", 0); } //++======================================================================= CasaStatus CreateAuthenticateResp( IN char *pRespMsg, IN int respLen, INOUT AuthenticateResp **ppAuthenticateResp) // // Arguments: // // Returns: // // Abstract: // // Notes: // // L2 //=======================================================================-- { CasaStatus retStatus = CASA_STATUS_SUCCESS; AuthRespParse authRespParse = {0}; AuthenticateResp *pAuthenticateResp; DbgTrace(1, "-CreateAuthenticateResp- Start\n", 0); /* * When an authentication request is processed successfully, the server replies to * the client with a message with the following format: * * * * ok200 * lifetime valuesession token data * * * When an authentication request fails to be successfully processed, the server * responds with an error and an error description string. The message format of * an unsuccessful reply is as follows: * * * * status descriptionstatus code * * * Plase note that the protocol utilizes the status codes defined * in the HTTP 1.1 Specification. * */ // Allocate AuthenticateResp object pAuthenticateResp = malloc(sizeof(*pAuthenticateResp)); if (pAuthenticateResp) { XML_Parser p; // Initialize the AuthenticateResp object and set it in the // authentication response parse oject. memset(pAuthenticateResp, 0, sizeof(*pAuthenticateResp)); authRespParse.pAuthenticateResp = pAuthenticateResp; // Create parser p = XML_ParserCreate(NULL); if (p) { // Keep track of the parser in our parse object authRespParse.p = p; // Initialize the status within the parse object authRespParse.status = CASA_STATUS_SUCCESS; // Set the start and end element handlers XML_SetElementHandler(p, AuthRespStartElementHandler, AuthRespEndElementHandler); // Set the character data handler XML_SetCharacterDataHandler(p, AuthRespCharDataHandler); // Set our user data XML_SetUserData(p, &authRespParse); // Parse the document if (XML_Parse(p, pRespMsg, respLen, 1) == XML_STATUS_OK) { // Verify that the parse operation completed successfully if (authRespParse.state == DONE_PARSING) { // The parse operation succeded, obtain the status returned // by the server. retStatus = authRespParse.status; } else { DbgTrace(0, "-CreateAuthenticateResp- Parse operation did not complete\n", 0); // Check if a status has been recorded if (authRespParse.status != CASA_STATUS_SUCCESS) { retStatus = authRespParse.status; } else { retStatus = CasaStatusBuild(CASA_SEVERITY_ERROR, CASA_FACILITY_AUTHTOKEN, CASA_STATUS_PROTOCOL_ERROR); } } } else { DbgTrace(0, "-CreateAuthenticateResp- Parse error %d\n", XML_GetErrorCode(p)); retStatus = CasaStatusBuild(CASA_SEVERITY_ERROR, CASA_FACILITY_AUTHTOKEN, CASA_STATUS_PROTOCOL_ERROR); } // Free the parser XML_ParserFree(p); } else { DbgTrace(0, "-CreateAuthenticateResp- Parser creation error\n", 0); retStatus = CasaStatusBuild(CASA_SEVERITY_ERROR, CASA_FACILITY_AUTHTOKEN, CASA_STATUS_INSUFFICIENT_RESOURCES); } // Return the AuthenticationResp object to the caller if necessary if (CASA_SUCCESS(retStatus)) { *ppAuthenticateResp = pAuthenticateResp; } else { free(pAuthenticateResp); } } else { DbgTrace(0, "-CreateAuthenticateResp- Memory allocation error\n", 0); retStatus = CasaStatusBuild(CASA_SEVERITY_ERROR, CASA_FACILITY_AUTHTOKEN, CASA_STATUS_INSUFFICIENT_RESOURCES); } DbgTrace(1, "-CreateAuthenticateResp- End, retStatus = %08X\n", retStatus); return retStatus; } //++======================================================================= void RelAuthenticateResp( IN AuthenticateResp *pAuthenticateResp) // // Arguments: // // Returns: // // Abstract: // // Notes: // // L2 //=======================================================================-- { DbgTrace(1, "-RelAuthenticateResp- Start\n", 0); // Free the resources associated with the object if (pAuthenticateResp->pToken) free(pAuthenticateResp->pToken); free(pAuthenticateResp); DbgTrace(1, "-RelAuthenticateResp- End\n", 0); } //++======================================================================= //++======================================================================= //++=======================================================================