/*********************************************************************** * * 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_AUTH_POLICY_ELEMENT_START 0x2 #define AWAITING_AUTH_POLICY_ELEMENT_END 0x3 #define AWAITING_AUTH_POLICY_DATA 0x4 #define AWAITING_AUTH_SOURCE_ELEMENT_START 0x5 #define AWAITING_AUTH_SOURCE_ELEMENT_END 0x6 #define AWAITING_AUTH_SOURCE_CHILD_START 0x7 #define AWAITING_REALM_DATA 0x8 #define AWAITING_REALM_ELEMENT_END 0x9 #define AWAITING_MECHANISM_DATA 0xA #define AWAITING_MECHANISM_ELEMENT_END 0xB #define AWAITING_MECHANISM_INFO_DATA 0xC #define AWAITING_MECHANISM_INFO_ELEMENT_END 0xD #define AWAITING_UNKNOWN_DATA 0xE #define AWAITING_UNKNOWN_ELEMENT_END 0xF #define DONE_PARSING 0x10 // // Authentication Policy Parse Structure // typedef struct _AuthPolicyParse { XML_Parser p; int state; int elementDataProcessed; AuthPolicy *pAuthPolicy; CasaStatus status; } AuthPolicyParse, *PAuthPolicyParse; //===[ Function prototypes ]=============================================== //===[ Global variables ]================================================== //++======================================================================= static void XMLCALL AuthPolicyStartElementHandler( IN AuthPolicyParse *pAuthPolicyParse, IN const XML_Char *name, IN const XML_Char **atts) // // Arguments: // // Returns: // // Abstract: // // Notes: // // L2 //=======================================================================-- { DbgTrace(2, "-AuthPolicyStartElementHandler- Start\n", 0); // Proceed based on the state switch (pAuthPolicyParse->state) { case AWAITING_ROOT_ELEMENT_START: // In this state, we are only expecting the Authentication // Policy Element. if (strcmp(name, AUTH_POLICY_ELEMENT_NAME) == 0) { // Good, advance to the next state. pAuthPolicyParse->state = AWAITING_AUTH_SOURCE_ELEMENT_START; } else { DbgTrace(0, "-AuthPolicyStartElementHandler- Un-expected start element\n", 0); XML_StopParser(pAuthPolicyParse->p, XML_FALSE); } break; case AWAITING_AUTH_SOURCE_ELEMENT_START: case AWAITING_ROOT_ELEMENT_END: // In this state, we are only expecting the start of an Authentication // Source Element. if (strcmp(name, AUTH_SOURCE_ELEMENT_NAME) == 0) { AuthContext *pAuthContext; // Create an authentication context structure pAuthContext = (AuthContext*) malloc(sizeof(AuthContext)); if (pAuthContext) { // Initialize the allocated AuthContext structure and associate it // with the AuthPolicy structure. memset(pAuthContext, 0, sizeof(*pAuthContext)); InsertTailList(&pAuthPolicyParse->pAuthPolicy->authContextListHead, &pAuthContext->listEntry); // Good, advance to the next state. pAuthPolicyParse->state = AWAITING_AUTH_SOURCE_CHILD_START; } else { DbgTrace(0, "-AuthPolicyStartElementHandler- Buffer allocation error\n", 0); XML_StopParser(pAuthPolicyParse->p, XML_FALSE); } } else { DbgTrace(0, "-AuthPolicyStartElementHandler- Un-expected start element\n", 0); XML_StopParser(pAuthPolicyParse->p, XML_FALSE); } break; case AWAITING_AUTH_SOURCE_CHILD_START: // Proceed based on the name of the element if (strcmp(name, REALM_ELEMENT_NAME) == 0) { // Advance to the next state. pAuthPolicyParse->state = AWAITING_REALM_DATA; } else if (strcmp(name, MECHANISM_ELEMENT_NAME) == 0) { // Advance to the next state. pAuthPolicyParse->state = AWAITING_MECHANISM_DATA; } else if (strcmp(name, MECHANISM_INFO_ELEMENT_NAME) == 0) { // Advance to the next state. pAuthPolicyParse->state = AWAITING_MECHANISM_INFO_DATA; } else if (strcmp(name, AUTH_SOURCE_ELEMENT_NAME) == 0) { // We are starting a new auth source entry, create an authentication // context structure to hold its information. AuthContext *pAuthContext; // Create an authentication context structure pAuthContext = (AuthContext*) malloc(sizeof(AuthContext)); if (pAuthContext) { // Initialize the allocated AuthContext structure and associate it // with the AuthPolicy structure. memset(pAuthContext, 0, sizeof(*pAuthContext)); InsertTailList(&pAuthPolicyParse->pAuthPolicy->authContextListHead, &pAuthContext->listEntry); } else { DbgTrace(0, "-AuthPolicyStartElementHandler- Buffer allocation error\n", 0); XML_StopParser(pAuthPolicyParse->p, XML_FALSE); } } else { // Advance to the next state. pAuthPolicyParse->state = AWAITING_UNKNOWN_DATA; } break; default: DbgTrace(0, "-AuthPolicyStartElementHandler- Un-expected state = %d\n", pAuthPolicyParse->state); XML_StopParser(pAuthPolicyParse->p, XML_FALSE); break; } DbgTrace(2, "-AuthPolicyStartElementHandler- End\n", 0); } //++======================================================================= static CasaStatus ConsumeElementData( IN AuthPolicyParse *pAuthPolicyParse, 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 pAuthPolicyParse->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 = pAuthPolicyParse->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(pAuthPolicyParse->elementDataProcessed + len + 1); if (pNewBuf) { memset(pNewBuf, 0, pAuthPolicyParse->elementDataProcessed + len + 1); memcpy(pNewBuf, *ppElementData, pAuthPolicyParse->elementDataProcessed); memcpy(pNewBuf + pAuthPolicyParse->elementDataProcessed, s, len); pAuthPolicyParse->elementDataProcessed += len; // Swap the buffers free(*ppElementData); *ppElementData = pNewBuf; // Return the length of the element data buffer *pElementDataLen = pAuthPolicyParse->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 AuthPolicyCharDataHandler( IN AuthPolicyParse *pAuthPolicyParse, IN const XML_Char *s, IN int len) // // Arguments: // // Returns: // // Abstract: // // Notes: // // L2 //=======================================================================-- { AuthContext *pAuthContext; DbgTrace(2, "-AuthPolicyCharDataHandler- Start\n", 0); // Just exit if being called to process white space if (*s == '\n' || *s == '\r' || *s == '\t' || *s == ' ') { goto exit; } // Proceed based on the state switch (pAuthPolicyParse->state) { case AWAITING_REALM_DATA: // Get access to the AuthContext at the tail of the list pAuthContext = CONTAINING_RECORD(pAuthPolicyParse->pAuthPolicy->authContextListHead.Blink, AuthContext, listEntry); // Consume the data pAuthPolicyParse->status = ConsumeElementData(pAuthPolicyParse, s, len, &pAuthContext->pContext, &pAuthContext->contextLen); if (CASA_SUCCESS(pAuthPolicyParse->status)) { // Advanced to the next state pAuthPolicyParse->state = AWAITING_REALM_ELEMENT_END; } else { XML_StopParser(pAuthPolicyParse->p, XML_FALSE); } break; case AWAITING_MECHANISM_DATA: case AWAITING_MECHANISM_ELEMENT_END: // Get access to the AuthContext at the tail of the list pAuthContext = CONTAINING_RECORD(pAuthPolicyParse->pAuthPolicy->authContextListHead.Blink, AuthContext, listEntry); // Consume the data pAuthPolicyParse->status = ConsumeElementData(pAuthPolicyParse, s, len, &pAuthContext->pMechanism, &pAuthContext->mechanismLen); if (CASA_SUCCESS(pAuthPolicyParse->status)) { // Advanced to the next state pAuthPolicyParse->state = AWAITING_MECHANISM_ELEMENT_END; } else { XML_StopParser(pAuthPolicyParse->p, XML_FALSE); } break; case AWAITING_MECHANISM_INFO_DATA: case AWAITING_MECHANISM_INFO_ELEMENT_END: // Get access to the AuthContext at the tail of the list pAuthContext = CONTAINING_RECORD(pAuthPolicyParse->pAuthPolicy->authContextListHead.Blink, AuthContext, listEntry); // Consume the data pAuthPolicyParse->status = ConsumeElementData(pAuthPolicyParse, s, len, &pAuthContext->pMechInfo, &pAuthContext->mechInfoLen); if (CASA_SUCCESS(pAuthPolicyParse->status)) { // Advanced to the next state pAuthPolicyParse->state = AWAITING_MECHANISM_INFO_ELEMENT_END; } else { XML_StopParser(pAuthPolicyParse->p, XML_FALSE); } break; case AWAITING_UNKNOWN_DATA: case AWAITING_UNKNOWN_ELEMENT_END: // Just advance the state pAuthPolicyParse->state = AWAITING_UNKNOWN_ELEMENT_END; break; default: DbgTrace(0, "-AuthPolicyCharDataHandler- Un-expected state = %d\n", pAuthPolicyParse->state); XML_StopParser(pAuthPolicyParse->p, XML_FALSE); break; } exit: DbgTrace(2, "-AuthPolicyCharDataHandler- End\n", 0); } //++======================================================================= static void XMLCALL AuthPolicyEndElementHandler( IN AuthPolicyParse *pAuthPolicyParse, IN const XML_Char *name) // // Arguments: // // Returns: // // Abstract: // // Notes: // // L2 //=======================================================================-- { AuthContext *pAuthContext; DbgTrace(2, "-AuthPolicyEndElementHandler- Start\n", 0); // Proceed based on the state switch (pAuthPolicyParse->state) { case AWAITING_ROOT_ELEMENT_END: // In this state, we are only expecting the Authentication // Policy Element. if (strcmp(name, AUTH_POLICY_ELEMENT_NAME) == 0) { // Done. pAuthPolicyParse->state = DONE_PARSING; } else { DbgTrace(0, "-AuthPolicyEndElementHandler- Un-expected end element\n", 0); XML_StopParser(pAuthPolicyParse->p, XML_FALSE); } break; case AWAITING_AUTH_SOURCE_CHILD_START: // In this state, we are only expecting the Authentication // Source Response Element. if (strcmp(name, AUTH_SOURCE_ELEMENT_NAME) == 0) { // Good, advance to the next state. pAuthPolicyParse->state = AWAITING_ROOT_ELEMENT_END; } else { DbgTrace(0, "-AuthPolicyEndHandler- Un-expected end element\n", 0); XML_StopParser(pAuthPolicyParse->p, XML_FALSE); } break; case AWAITING_REALM_ELEMENT_END: // In this state, we are only expecting the Realm Element. if (strcmp(name, REALM_ELEMENT_NAME) == 0) { // Good, advance to the next state. pAuthPolicyParse->state = AWAITING_AUTH_SOURCE_CHILD_START; } else { DbgTrace(0, "-AuthPolicyEndElementHandler- Un-expected end element\n", 0); XML_StopParser(pAuthPolicyParse->p, XML_FALSE); } break; case AWAITING_MECHANISM_ELEMENT_END: // In this state, we are only expecting the Mechanism Element. if (strcmp(name, MECHANISM_ELEMENT_NAME) == 0) { // Good, advance to the next state. pAuthPolicyParse->state = AWAITING_AUTH_SOURCE_CHILD_START; } else { DbgTrace(0, "-AuthPolicyEndElementHandler- Un-expected end element\n", 0); XML_StopParser(pAuthPolicyParse->p, XML_FALSE); } break; case AWAITING_MECHANISM_INFO_DATA: // Get access to the AuthContext at the tail of the list pAuthContext = CONTAINING_RECORD(pAuthPolicyParse->pAuthPolicy->authContextListHead.Blink, AuthContext, listEntry); // There was no mechanism info data. Set it to an empty string. pAuthContext->pMechInfo = (char*) malloc(1); if (pAuthContext->pMechInfo) { *pAuthContext->pMechInfo = '\0'; } else { DbgTrace(0, "-AuthPolicyEndElementHandler- Buffer allocation failure\n", 0); pAuthPolicyParse->status = CasaStatusBuild(CASA_SEVERITY_ERROR, CASA_FACILITY_AUTHTOKEN, CASA_STATUS_INSUFFICIENT_RESOURCES); XML_StopParser(pAuthPolicyParse->p, XML_FALSE); break; } // Fall through case AWAITING_MECHANISM_INFO_ELEMENT_END: // In this state, we are only expecting the Mechanism Info Element. if (strcmp(name, MECHANISM_INFO_ELEMENT_NAME) == 0) { // Good, advance to the next state. pAuthPolicyParse->state = AWAITING_AUTH_SOURCE_CHILD_START; } else { DbgTrace(0, "-AuthPolicyEndElementHandler- Un-expected end element\n", 0); XML_StopParser(pAuthPolicyParse->p, XML_FALSE); } break; case AWAITING_UNKNOWN_ELEMENT_END: // Advance to the next state. pAuthPolicyParse->state = AWAITING_AUTH_SOURCE_CHILD_START; break; default: DbgTrace(0, "-AuthPolicyEndElementHandler- Un-expected state = %d\n", pAuthPolicyParse->state); XML_StopParser(pAuthPolicyParse->p, XML_FALSE); break; } DbgTrace(2, "-AuthPolicyEndElementHandler- End\n", 0); } //++======================================================================= CasaStatus CreateAuthPolicy( IN char *pEncodedData, IN int encodedDataLen, INOUT AuthPolicy **ppAuthPolicy) // // Arguments: // // Returns: // // Abstract: // // Notes: // // L2 //=======================================================================-- { CasaStatus retStatus; AuthPolicy *pAuthPolicy = NULL; AuthPolicyParse authPolicyParse = {0}; char *pData = NULL; int dataLen = 0; DbgTrace(1, "-CreateAuthPolicy- Start\n", 0); /* * An authentication policy document has the following format: * * * * * realm name * authentication mechanism type * authentication mechanism context data * * ... * * * The authentication policy document can contain multiple auth_source * elements. These auth_source elements can be for different authentication * sources or for the same authentication source but specifying a different * authentication mechanism. The mechanism_info element is optional. * * The following is a sample authentication policy document: * * * * * Corp_eDirTree * Krb5Authenticate * host/hostname * * * Corp_eDirTree * PwdAuthenticate * * * * * This authentication policy would tell the CASA client that it can * authenticate to the CASA Authentication Token Service using * credentials for the Corp_eDirTree and utilizing either the * Krb5 authentication mechanism or the Pwd authentication mechanism. * The Krb5 authentication mechanism context data specifies the * name of the Kerberos service principal. * */ // Initialize output parameter *ppAuthPolicy = NULL; // Decode the data retStatus = DecodeData(pEncodedData, encodedDataLen, (void**) &pData, &dataLen); if (CASA_SUCCESS(retStatus)) { // Allocate space for the AuthPolicy structure pAuthPolicy = (AuthPolicy*) malloc(sizeof(*pAuthPolicy)); if (pAuthPolicy) { XML_Parser p; // Initialize the AuthPolicy object memset(pAuthPolicy, 0, sizeof(*pAuthPolicy)); InitializeListHead(&pAuthPolicy->authContextListHead); // Set the AuthPolicy object in the parse object authPolicyParse.pAuthPolicy = pAuthPolicy; // Create parser p = XML_ParserCreate(NULL); if (p) { // Keep track of the parser in our parse object authPolicyParse.p = p; // Initialize the status within the parse object authPolicyParse.status = CASA_STATUS_SUCCESS; // Set the start and end element handlers XML_SetElementHandler(p, (XML_StartElementHandler) AuthPolicyStartElementHandler, (XML_EndElementHandler) AuthPolicyEndElementHandler); // Set the character data handler XML_SetCharacterDataHandler(p, (XML_CharacterDataHandler) AuthPolicyCharDataHandler); // Set our user data XML_SetUserData(p, &authPolicyParse); // Parse the document if (XML_Parse(p, pData, dataLen, 1) == XML_STATUS_OK) { // Verify that the parse operation completed successfully if (authPolicyParse.state == DONE_PARSING) { // The parse operation succeded retStatus = CASA_STATUS_SUCCESS; } else { DbgTrace(0, "-CreateAuthPolicy- Parse operation did not complete\n", 0); retStatus = CasaStatusBuild(CASA_SEVERITY_ERROR, CASA_FACILITY_AUTHTOKEN, CASA_STATUS_PROTOCOL_ERROR); } } else { DbgTrace(0, "-CreateAuthPolicy- 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, "-CreateAuthPolicy- Parser creation error\n", 0); retStatus = CasaStatusBuild(CASA_SEVERITY_ERROR, CASA_FACILITY_AUTHTOKEN, CASA_STATUS_INSUFFICIENT_RESOURCES); } // Return the AuthPolicy object to the caller if necessary if (CASA_SUCCESS(retStatus)) { *ppAuthPolicy = pAuthPolicy; // Forget about the AuthPolicy object so that it is not release down below pAuthPolicy = NULL; } } else { DbgTrace(0, "-CreateAuthPolicy- Buffer allocation error\n", 0); retStatus = CasaStatusBuild(CASA_SEVERITY_ERROR, CASA_FACILITY_AUTHTOKEN, CASA_STATUS_INSUFFICIENT_RESOURCES); } } else { DbgTrace(0, "-CreateAuthPolicy- Buffer allocation error\n", 0); retStatus = CasaStatusBuild(CASA_SEVERITY_ERROR, CASA_FACILITY_AUTHTOKEN, CASA_STATUS_INSUFFICIENT_RESOURCES); } // Release necessary allocated resources if (pAuthPolicy) RelAuthPolicy(pAuthPolicy); if (pData) free(pData); DbgTrace(1, "-CreateAuthPolicy- End, retStatus = %08X\n", retStatus); return retStatus; } //++======================================================================= void RelAuthPolicy( IN AuthPolicy *pAuthPolicy) // // Arguments: // // Returns: // // Abstract: // // Notes: // // L2 //=======================================================================-- { LIST_ENTRY *pListEntry; DbgTrace(1, "-RelAuthPolicy- Start\n", 0); // Free all of the associated AuthContexts pListEntry = pAuthPolicy->authContextListHead.Flink; while (pListEntry != &pAuthPolicy->authContextListHead) { AuthContext *pAuthContext; // Get pointer to AuthContext structure pAuthContext = CONTAINING_RECORD(pListEntry, AuthContext, listEntry); // Free associated buffers if (pAuthContext->pContext) free(pAuthContext->pContext); if (pAuthContext->pMechanism) free(pAuthContext->pMechanism); if (pAuthContext->pMechInfo) free(pAuthContext->pMechInfo); // Remove the entry from the list RemoveEntryList(&pAuthContext->listEntry); // Free the AuthContext free(pAuthContext); // Advance to the next entry pListEntry = pAuthPolicy->authContextListHead.Flink; } // Free the AuthPolicy free(pAuthPolicy); DbgTrace(1, "-RelAuthPolicy- End\n", 0); } //++======================================================================= //++======================================================================= //++=======================================================================