/**************************************************************************
*** COPYRIGHT (c) 2002 by TransNexus, Inc.                              ***
***                                                                     ***
*** This software is property of TransNexus, Inc.                       ***
*** This software is freely available under license from TransNexus.    ***
*** The license terms and conditions for free use of this software by   ***
*** third parties are defined in the OSP Toolkit Software License       ***
*** Agreement (LICENSE.txt).  Any use of this software by third         ***
*** parties, which does not comply with the terms and conditions of the ***
*** OSP Toolkit Software License Agreement is prohibited without        ***
*** the prior, express, written consent of TransNexus, Inc.             ***
***                                                                     ***
*** Thank you for using the OSP ToolKit(TM).  Please report any bugs,   ***
*** suggestions or feedback to support@transnexus.com                   ***
***                                                                     ***
**************************************************************************/

#include "osp/ospcomm.h"
#include "osp/ospssl.h"
#include "osp/ospsecurity.h"
#include "osp/ospb64.h"

#include "osptnep.h"
#include "osptnepinit.h"
#include "osp/ospcryptowrap.h"

/*
 * Initialize the Enrollment Parameter object sent in. All of the
 * structures will have to be malloced in this case.
 *
 * Input: Pointer to an OSPTENROLLPARAMS object
 *
 * Errors:
 *     o the pointer to the OSPTENROLLPARAMS is null;
 *     o malloc or initialization fails on any member of the OSPTENROLLPARAMS.
 */
int OSPPInitEnrollParams(OSPTENROLLPARAMS *ospvEnrollParamsIn)
{
    int retVal = OSPC_ERR_NO_ERROR;

    OSPM_DBGENTER(("ENTER: OSPPInitEnrollParams\n"));

    /*
     * If ( the pointer to the enrollment parameter list is ok ) then
     *  o allocate memory for every string in the enrollment parameter list
     *    using default lengths. If a string is too short, too long, or needs
     *    to be allocated in another function for some other reason, then
     *    just take it out here.
     *  o if ( all of the pointers are ok ) then
     *      - initialize all of their values.
     */
    if (ospvEnrollParamsIn == OSPC_OSNULL) {
        retVal = OSPC_ERR_ENROLL_INVALID_ARG;
        OSPM_DBGERRORLOG(retVal, "Unable to initialize a null enrollment parameter set.\n");
    }

    if (retVal == OSPC_ERR_NO_ERROR) {
        ospvEnrollParamsIn->Verbose = 0;
        ospvEnrollParamsIn->Nonce = OSPC_OSNULL;
        ospvEnrollParamsIn->NonceLen = 0;

        ospvEnrollParamsIn->CACert = OSPC_OSNULL;
        ospvEnrollParamsIn->CACertLen = 0;
        ospvEnrollParamsIn->CACertB64 = OSPC_OSNULL;
        ospvEnrollParamsIn->CACertB64Len = 0;

        ospvEnrollParamsIn->Function = OSPC_OSNULL;
        ospvEnrollParamsIn->Username = OSPC_OSNULL;
        ospvEnrollParamsIn->Password = OSPC_OSNULL;
        ospvEnrollParamsIn->DeviceId = OSPC_OSNULL;
        ospvEnrollParamsIn->CustomerId = OSPC_OSNULL;
        ospvEnrollParamsIn->CertReq = OSPC_OSNULL;
        ospvEnrollParamsIn->SSLUrl = OSPC_OSNULL;
        ospvEnrollParamsIn->CAUrl = OSPC_OSNULL;
        ospvEnrollParamsIn->CAFprint = OSPC_OSNULL;
    }

    OSPM_DBGEXIT(("EXIT: OSPPFreeEnrollParams\n"));

    return retVal;
}

/*
 * This function is called for freeing up all the memory taken up
 * by an enrollment parameter object.
 *
 * Input:
 *     o pointer to an OSPTENROLLPARAMS;
 *
 * Errors:
 *     o if the input variable is null ( OSPC_ERR_ENROLL_ENROLL_PARAMS_FREE )
 */
int OSPPFreeEnrollParams(OSPTENROLLPARAMS *ospvEnrollParamsIn)
{
    int retVal = OSPC_ERR_NO_ERROR;

    OSPM_DBGENTER(("ENTER: OSPPFreeEnrollParams\n"));

    if (ospvEnrollParamsIn == OSPC_OSNULL) {
        retVal = OSPC_ERR_ENROLL_INVALID_ARG;
        OSPM_DBGERRORLOG(retVal, "The enrollment parameter list being freed was null.\n");
    }

    /*
     * Free all of the parameters in the enrollment list, just so long as
     * they're valid ( and the enrollment list itself isn't null, either ):
     */
    if (retVal == OSPC_ERR_NO_ERROR) {
        if (ospvEnrollParamsIn->Function != OSPC_OSNULL) {
            OSPM_FREE(ospvEnrollParamsIn->Function);
        }

        if (ospvEnrollParamsIn->Username != OSPC_OSNULL) {
            OSPM_FREE(ospvEnrollParamsIn->Username);
        }

        if (ospvEnrollParamsIn->Password != OSPC_OSNULL) {
            OSPM_FREE(ospvEnrollParamsIn->Password);
        }

        if (ospvEnrollParamsIn->DeviceId != OSPC_OSNULL) {
            OSPM_FREE(ospvEnrollParamsIn->DeviceId);
        }

        if (ospvEnrollParamsIn->CustomerId != OSPC_OSNULL) {
            OSPM_FREE(ospvEnrollParamsIn->CustomerId);
        }

        if (ospvEnrollParamsIn->CertReq != OSPC_OSNULL) {
            OSPM_FREE(ospvEnrollParamsIn->CertReq);
        }

        if (ospvEnrollParamsIn->CAFprint != OSPC_OSNULL) {
            OSPM_FREE(ospvEnrollParamsIn->CAFprint);
        }

        if (ospvEnrollParamsIn->SSLUrl != OSPC_OSNULL) {
            OSPM_FREE(ospvEnrollParamsIn->SSLUrl);
        }

        if (ospvEnrollParamsIn->CAUrl != OSPC_OSNULL) {
            OSPM_FREE(ospvEnrollParamsIn->CAUrl);
        }

        if (ospvEnrollParamsIn->CACertB64 != OSPC_OSNULL) {
            OSPM_FREE(ospvEnrollParamsIn->CACertB64);
        }

        if (ospvEnrollParamsIn->CACert != OSPC_OSNULL) {
            OSPM_FREE(ospvEnrollParamsIn->CACert);
        }

        if (ospvEnrollParamsIn->Nonce != OSPC_OSNULL) {
            OSPM_FREE(ospvEnrollParamsIn->Nonce);
        }

    }

    OSPM_DBGEXIT(("EXIT: OSPPFreeEnrollParams\n"));

    return retVal;
}

/*
 * This function will take a list of enrollment parameters and communications
 * parameters that specify how a communications manager will be set up.
 * The communications manager will first be created and then initialized
 * with all of the non-SSL-related parameters, followed by all of the
 * SSL-related parameters.
 *
 * Input: a pointer to the enrollment server parameters ( for the enrollment
 *        server's url ); a pointer to the communication parameters; and a
 *        pointer to the reference to the communication manager to be initialized.
 *        *ospvCommOut should be null ( but ospvCommOut should not. )
 *
 * Output: *ospvCommMgrOut should be initialized as a communications manager.
 *         If it can't be initialized, then it should be null and the
 *         return value will be non-zero. Otherwise, the return value should
 *         be zero.
 */
int OSPPInitSecureCommMgr(OSPTENROLLPARAMS *ospvEnrollParamsIn,
    OSPTCOMMPARAMS *ospvCommParamsIn,
    OSPTCOMM **ospvCommOut)
{
    int retVal = OSPC_ERR_NO_ERROR;


    OSPM_DBGENTER(("ENTER: OSPPInitSecureCommMgr\n"));

    /*
     * If ( any of the parameters are null or
     *      if the url for the CA is null ) then
     *  o set an error code and complain.
     */
    if ((ospvEnrollParamsIn == OSPC_OSNULL) || (ospvCommParamsIn == OSPC_OSNULL) || (ospvCommOut == OSPC_OSNULL) ||
        (ospvEnrollParamsIn->SSLUrl == OSPC_OSNULL))
    {
        retVal = OSPC_ERR_ENROLL_INVALID_ARG;
        OSPM_DBGERRORLOG(retVal, "The parameters for initializing the secure comm manager are invalid.\n");
    }

    /*
     * Now create the communications manager. We should check the
     * pointer returned for the communications manager even if the return
     * value says otherwise.
     */
    if (retVal == OSPC_ERR_NO_ERROR) {
        retVal = OSPPCommNew(ospvCommOut);

        if (retVal != OSPC_ERR_NO_ERROR) {
            OSPM_DBGERRORLOG(retVal, "Unable to allocate the space for the communications manager.\n");
        } else if (*ospvCommOut == OSPC_OSNULL) {
            retVal = OSPC_ERR_COMM_NO_MEMORY;
            OSPM_DBGERRORLOG(retVal, "The communications manager returned was invalid.\n");
        }
    }

    /* Initialize the communications manager, which includes allocating
     * its memory.
     */
    if (retVal == OSPC_ERR_NO_ERROR) {
        retVal = OSPPInitNonSSLCommMgrParams(ospvEnrollParamsIn->SSLUrl, ospvCommParamsIn, *ospvCommOut);

        if (retVal != OSPC_ERR_NO_ERROR) {
            OSPM_DBGERRORLOG(retVal, "Unable to initialize the communication manager's non-SSL related parameters.\n");
        }
    }

    /* Now set up all of the communications parameters specific to SSL: */
    if (retVal == OSPC_ERR_NO_ERROR) {
        retVal = OSPPInitSSLCommMgrParams(ospvEnrollParamsIn, ospvCommParamsIn, *ospvCommOut);

        if (retVal != OSPC_ERR_NO_ERROR) {
            OSPM_DBGERRORLOG(retVal, "Unable to initialize the communication manager's SSL related parameters.\n");
        }
    }

    OSPM_DBGEXIT(("EXIT: OSPPInitSecureCommMgr\n"));

    return retVal;
}

/*
 * This is just a minimal wrapper for calling OSPPInitNonSecureCommMgrParams;
 * it just extracts the service point for the CA from the enrollment
 * parameters and passes it along as the service point to be explicitly
 * contacted by the communications manager.
 *
 * Input: a pointer to the enrollment server parameters ( for the CA's
 *        url ); a pointer to the communication parameters; and a pointer
 *        to the reference to the communication manager to be initialized.
 *
 * Output: *ospvCommMgrOut should be initialized as a communications manager.
 *         If it can't be initialized, then it should be null and the
 *         return value will be non-zero. Otherwise, the return value should
 *         be zero.
 */
int OSPPInitNonSecureCommMgr(OSPTENROLLPARAMS *ospvEnrollParamsIn,
    OSPTCOMMPARAMS *ospvCommParamsIn,
    OSPTCOMM **ospvCommOut)
{
    int retVal = OSPC_ERR_NO_ERROR;

    OSPM_DBGENTER(("OSPPInitNonSecureCommMgr\n"));

    /*
     * If ( any of the parameters are null or
     *      if the url for the CA is null ) then
     *  o set an error code and complain.
     */
    if ((ospvEnrollParamsIn == OSPC_OSNULL) || (ospvCommParamsIn == OSPC_OSNULL) || (ospvCommOut == OSPC_OSNULL) ||
        (ospvEnrollParamsIn->CAUrl == OSPC_OSNULL))
    {
        retVal = OSPC_ERR_ENROLL_INVALID_ARG;
        OSPM_DBGERRORLOG(retVal, "The parameters for initializing the CA's communications manager are invalid.\n");
    }

    /* Create the communications manager before we initialize it: */
    if (retVal == OSPC_ERR_NO_ERROR) {
        retVal = OSPPCommNew(ospvCommOut);

        if (retVal != OSPC_ERR_NO_ERROR) {
            OSPM_DBGERRORLOG(retVal, "Unable to create communications manager\n");
        }
        /*
         * For some reason we got a valid return value but an invalid
         * communications manager; set an error code and complain:
         */
        else if (*ospvCommOut == OSPC_OSNULL) {
            retVal = OSPC_ERR_COMM_NO_MEMORY;
            OSPM_DBGERRORLOG(retVal, "The communications manager returned was invalid.\n");
        }
    }

    /*
     * Now that the communications manager has been created, initialize
     * its basic parameters. We'll use the CAUrl from the enrollment
     * parameters as the service point:
     */
    if (retVal == OSPC_ERR_NO_ERROR) {
        retVal = OSPPInitNonSSLCommMgrParams(ospvEnrollParamsIn->CAUrl, ospvCommParamsIn, *ospvCommOut);

        if (retVal != OSPC_ERR_NO_ERROR) {
            OSPM_DBGERRORLOG(retVal, "Unable to initialize the communication manager's parameters for the CA.\n");
        }
    }

    OSPM_DBGEXIT(("OSPPInitNonSecureCommMgr\n"));

    return retVal;
}

/*
 * Initialize the communications manager's SSL-related parameters. In this
 * case, we'll need: the CA certificate ( given in the CACert field of
 * the enrollment parameters ); the service point to be accessed
 * ( given in the SSLUrl field of the enrollment parameters ); the
 * lifetime of an SSL session ( given in the SSLLifetime field of the
 * communications parameters ); and the communications manager itself.
 *
 * Input: a list of enrollment parameters ( for the CACert and SSLUrl fields of
 *        the OSPTENROLLPARAMS* ); a list of communications parameters
 *        ( for the SSLLifetime field of the OSPTCOMMPARAMS* ); and a
 *        reference to a communications manager.
 *
 * Output: the communications manager will have its parameters set, if
 *         possible. A return value of OSPC_ERR_NO_ERROR should indicate that
 *         there have been no problems. Any other return value will indicate
 *         an error.
 */
int OSPPInitSSLCommMgrParams(OSPTENROLLPARAMS *ospvEnrollParamsIn,
    OSPTCOMMPARAMS *ospvCommParamsIn,
    OSPTCOMM *ospvCommOut)
{
    int retVal = OSPC_ERR_NO_ERROR;

    OSPT_CERT caCert;
    const OSPT_CERT *caCerts[1];

    caCerts[0] = &caCert;

    OSPM_DBGENTER(("ENTER: OSPPInitSSLCommMgrParams\n"));

    /* Check the references to the parameters first. We'll be using
     * the SSLUrl field of the enrollment parameters list for the
     * service point, so we'll need to check that as well:
     */
    if ((ospvEnrollParamsIn == OSPC_OSNULL) || (ospvEnrollParamsIn->SSLUrl == OSPC_OSNULL) ||
        (ospvCommParamsIn == OSPC_OSNULL) || (ospvCommOut == OSPC_OSNULL))
    {
        retVal = OSPC_ERR_ENROLL_INVALID_ARG;
        OSPM_DBGERRORLOG(retVal, "The parameters for initializing the enrollment server communications were invalid.\n");
    }

    /*
     * If ( there were no problems ) then
     *  o set all of the communication manager's parameters, including
     *    connectivity parameters and the CA certificate.
     *    The CA certificate will be retrieved if necessary.
     */
    if (retVal == OSPC_ERR_NO_ERROR) {
        /*
         * Create a new security object. Note that we need the address
         * of the communication's manager's security object pointer, so
         * we need to dereference the ospvCommOut, access its security
         * object, and then take its address. Not pretty.
         */
        retVal = OSPPSecNew(&(ospvCommOut->Security));

        if (retVal != OSPC_ERR_NO_ERROR) {
            OSPM_DBGERRORLOG(retVal, "Unable to create security object for comm mgr.");
        }
    }

    /*
     * If ( there weren't any problems ) then
     *  o set the SSL lifetime and max number of connections
     *  o record any errors.
     */
    if (retVal == OSPC_ERR_NO_ERROR) {
        OSPPSecSetSSLLifetime(ospvCommOut->Security, ospvCommParamsIn->SSLLifetime);

        OSPM_DBGMISC(("CA cert: \n"));
        OSPPDumpHex(ospvEnrollParamsIn->CACert, ospvEnrollParamsIn->CACertLen);

        caCert.CertData = ospvEnrollParamsIn->CACert;
        caCert.CertDataLength = ospvEnrollParamsIn->CACertLen;

        retVal = OSPPSecSetAuthorityCertificates(ospvCommOut->Security, 1, caCerts);

        if (retVal != OSPC_ERR_NO_ERROR) {
            OSPM_DBGERRORLOG(retVal, "Unable to set the authority certs for the communications manager.\n");
        }
    }

    /*
     * Initialize the SSL context for the communication manager.
     * If we fail, then report the error and set an error code.
     */
    if (retVal == OSPC_ERR_NO_ERROR) {
        retVal = OSPPSSLWrapInit((void *)(ospvCommOut->Security));

        if (retVal != OSPC_ERR_NO_ERROR) {
            OSPM_DBGERRORLOG(retVal, "Unable to initialize SSL context.\n");
        }
    }

    OSPM_DBGMISC(("Return value: <%d>\n", retVal));
    OSPM_DBGEXIT(("EXIT: OSPPInitSSLCommMgrParams\n"));

    return retVal;
}

/*
 * Initialize all of the non-SSL related parameters; we'll set the
 * retry delay, http timeout, persistence values, and so on. All of
 * the SSL-related parameters are set in the OSPPInitSSLCommMgrParams,
 * which won't be necessary for certain functions ( like retrieving the
 * CA certificate, which is currently done without SSL. )
 *
 * We'll be using this function for establishing basic parameters that
 * aren't dependent on the use of the communications manager, so we'll
 * need a generic service point specification, instead of assuming that
 * it will be in the CAUrl or SSLUrl fields of an OSPTENROLLPARAM*
 * structure. Doing the service point initialization here just eliminates
 * duplication of effort.
 *
 * Input: pointers to the communications parameters and the service point's
 *        url, as well as the outgoing communications manager.
 *
 * Output: The communication manager's parameters should be set up.
 *         A return value of OSPC_ERR_NO_ERROR is returned if everything
 *         worked out; otherwise, an error code is returned.
 */
int OSPPInitNonSSLCommMgrParams(unsigned char *ospvServicePtUrlIn,
    OSPTCOMMPARAMS *ospvCommParamsIn,
    OSPTCOMM *ospvCommOut)
{
    int retVal = OSPC_ERR_NO_ERROR;

    OSPM_DBGENTER(("ENTER: OSPPInitNonSSLCommMgrParams\n"));

    /* set an error code and complain if we can't use the input parameters: */
    if ((ospvCommParamsIn == OSPC_OSNULL) || (ospvCommOut == OSPC_OSNULL)) {
        retVal = OSPC_ERR_ENROLL_INVALID_ARG;
        OSPM_DBGERRORLOG(retVal, "At least one parameter is invalid (null)\n");
    }

    /*
     * If ( there weren't any problems ) then
     *  o set the HTTP persistence
     *  o record any errors.
     */
    if (retVal == OSPC_ERR_NO_ERROR) {
        retVal = OSPPCommSetPersistence(ospvCommOut, ospvCommParamsIn->HTTPPersistence);

        if (retVal != OSPC_ERR_NO_ERROR) {
            OSPM_DBGERRORLOG(retVal, "Unable to set HTTP persistence");
        }
    }

    /*
     * If ( there weren't any problems ) then
     *  o set the http retry delay
     *  o record any errors.
     */
    if (retVal == OSPC_ERR_NO_ERROR) {
        retVal = OSPPCommSetRetryDelay(ospvCommOut, ospvCommParamsIn->HTTPRetryDelay);

        if (retVal != OSPC_ERR_NO_ERROR) {
            OSPM_DBGERRORLOG(retVal, "Unable to set HTTP retry delay.");
        }
    }

    /*
     * If ( there weren't any problems ) then
     *  o set the http retry limit
     *  o record any errors.
     */
    if (retVal == OSPC_ERR_NO_ERROR) {
        retVal = OSPPCommSetRetryLimit(ospvCommOut, ospvCommParamsIn->HTTPRetryLimit);

        if (retVal != OSPC_ERR_NO_ERROR) {
            OSPM_DBGERRORLOG(retVal, "Unable to set HTTP Retry Limit.");
        }
    }

    /*
     * If ( there weren't any problems ) then
     *  o set the http max connections
     *  o record any errors.
     */
    if (retVal == OSPC_ERR_NO_ERROR) {
        retVal = OSPPCommSetMaxConnections(ospvCommOut, ospvCommParamsIn->HTTPMaxConnections);

        if (retVal != OSPC_ERR_NO_ERROR) {
            OSPM_DBGERRORLOG(retVal, "Unable to set HTTP Max connections.");
        }
    }

    /*
     * If ( there weren't any problems ) then
     *  o set the http timeout
     *  o record any errors.
     */
    if (retVal == OSPC_ERR_NO_ERROR) {
        retVal = OSPPCommSetTimeout(ospvCommOut, ospvCommParamsIn->HTTPTimeout);

        if (retVal != OSPC_ERR_NO_ERROR) {
            OSPM_DBGERRORLOG(retVal, "Unable to set HTTP timeout.");
        }
        retVal = OSPPCommSetConnSelectionTimeout(ospvCommOut, ospvCommParamsIn->HTTPTimeout);

        if (retVal != OSPC_ERR_NO_ERROR) {
            OSPM_DBGERRORLOG(retVal, "Unable to set Connection Selection timeout.");
        }
    }

    /* Now set the service point and record any errors: */
    if (retVal == OSPC_ERR_NO_ERROR) {
        OSPM_DBGMISC(("enrollment url: <%s>\n", ospvServicePtUrlIn));

        retVal = OSPPCommSetServicePoints(ospvCommOut, 1, (const char **)&(ospvServicePtUrlIn));

        retVal = OSPPCommMgrSetSPMessageCount(ospvCommOut, NULL);

        if (retVal != OSPC_ERR_NO_ERROR) {
            OSPM_DBGMISC(("Unable to set service points: <%d>\n", retVal));
        }
    }

    OSPM_DBGEXIT(("EXIT: OSPPInitNonSSLCommMgrParams\n"));

    return retVal;
}

/*
 * This function will: initialize a communications manager; construct
 * a message to be sent to a service point; parse the results; and validate
 * the contents of the certificate. We'll need the enrollment parameters
 * so that we know which CA to contact for  a certificate, and we'll
 * need the communications parameters for initializing the communications
 * manager. The enrollment parameters will also be used to store the
 * CA certificate that is returned ( accessed by
 * ospvEnrollParamsInOut->CACert ).
 *
 * Input: a pointer to the enrollment parameter list and a pointer to the
 *        communications parameter list.
 *
 * Output: the CA certificate ( if it was found ) in the enrollment parameter
 *         list ( under the CACert field ). OSPC_ERR_NO_ERROR is returned
 *         if everything went ok; otherwise, some other error code is returned.
 */
int OSPPRetrieveCACert(OSPTENROLLPARAMS *ospvEnrollParamsInOut,
    OSPTCOMMPARAMS *ospvCommParamsIn)
{
    int retVal = OSPC_ERR_NO_ERROR;

    /* The structure used for encapsulating the request and response: */
    OSPT_MSG_INFO *certReqMsg = OSPC_OSNULL;

    /*
     * The request that we'll be building and sending to the CA via the
     * certReqMsg ( which will also hold the response. )
     */
    unsigned char *caRequest = OSPC_OSNULL;

    /*
     * The communications manager used for contacting the enrollment server
     * ( or its CA ) and requesting the CA certificate:
     */
    OSPTCOMM *caCommMgr = OSPC_OSNULL;

    /* The CA certificate response that will be null-terminated: */
    unsigned char *caCertResponse = OSPC_OSNULL;

    OSPM_DBGENTER(("ENTER: OSPPRetrieveCACert\n"));

    /* Check the input parameters for null values first: */
    if ((ospvCommParamsIn == OSPC_OSNULL) || (ospvEnrollParamsInOut == OSPC_OSNULL)) {
        retVal = OSPC_ERR_ENROLL_INVALID_ARG;
        OSPM_DBGERRORLOG(retVal, "Parameters were invalid; at least one was null.\n");
    }

    /* Now initialize the communications manager: */
    if (retVal == OSPC_ERR_NO_ERROR) {
        retVal = OSPPInitNonSecureCommMgr(ospvEnrollParamsInOut, ospvCommParamsIn, &caCommMgr);

        if (retVal != OSPC_ERR_NO_ERROR) {
            OSPM_DBGERRORLOG(retVal, "Unable to init communication with the CA.\n");
        }
    }

    /*
     * If ( the parameters were ok ) then
     *  o initialize the memory for the outbound message.
     *  o if ( we coldn't initialize it ) then
     *      - set an error code and complain.
     */
    if (retVal == OSPC_ERR_NO_ERROR) {
        retVal = OSPPMsgInfoNew(&certReqMsg);
        OSPM_MALLOC(certReqMsg->ContentType, unsigned char, OSPM_STRLEN(OSPC_COMM_TEXT_MSG) + 1);

        if (certReqMsg->ContentType == OSPC_OSNULL) {
            retVal = OSPC_ERR_ENROLL_NO_MEMORY;
            OSPM_DBGERRORLOG(retVal, "No memory is available for the CA certificate request.\n");
        }
    }

    /*
     * If ( we could create the message info structure ) then
     *  o create the retrieval request to be sent
     */
    if (retVal == OSPC_ERR_NO_ERROR) {
        retVal = OSPPCreateCARetrievalRequest(ospvEnrollParamsInOut, &caRequest);

        /* Print out an error if there was something wrong: */
        if (retVal != OSPC_ERR_NO_ERROR) {
            OSPM_DBGERRORLOG(retVal, "Unable to create CA retrieval request.");
        }
    }

    /*
     * If ( the request could be created ) then
     *  o set the request message and its size for the MessageInfo:
     *  o add the MessageInfo to the queue; the request should be
     *    transmitted to the enrollment server and a request received.
     *  o if ( sending the request failed ) then write an error message.
     */
    if (retVal == OSPC_ERR_NO_ERROR) {
        certReqMsg->RequestMsg = caRequest;
        certReqMsg->RequestSz = OSPM_STRLEN((const char *)caRequest);
        OSPM_STRCPY((char *)(certReqMsg->ContentType), OSPC_COMM_TEXT_MSG);

        OSPM_DBGMISC(("request msg: <%s>, <%d>\n", certReqMsg->RequestMsg, certReqMsg->RequestSz));

        retVal = OSPPCommAddTransaction(caCommMgr, certReqMsg);

        if (retVal != OSPC_ERR_NO_ERROR) {
            OSPM_DBGERRORLOG(retVal, "Unable to retrieve CA certificate.");
        }
    }

    /*
     * If ( sending the request was successful ) then
     *  o Get the CA certificate out of the response.
     *  o if ( there were problems getting it out ) then
     *      - print an error.
     */
    if (retVal == OSPC_ERR_NO_ERROR) {
        if (certReqMsg->ResponseMsg == OSPC_OSNULL) {
            retVal = OSPC_ERR_ENROLL_INVALID_RESPONSE;
            OSPM_DBGERRORLOG(retVal, "The response received for the CA certificate was empty.\n");
        }
    }

    /*
     * If ( the return value was ok ) then
     *  o check the error code returned as well; it's possible that the
     *    error code from the enrollment server ( at the HTTP level; this
     *    is not equivalent to the status of the enrollment request )
     *    didn't result in a bad return value. In this case, report an
     *    error.
     */
    if (retVal == OSPC_ERR_NO_ERROR) {
        if (certReqMsg->ErrorCode != OSPC_ERR_NO_ERROR) {
            retVal = OSPC_ERR_ENROLL_INVALID_RESPONSE;
            OSPM_DBGERRORLOG(retVal, "The error code received invalidates the CA's response.\n");
            OSPM_DBGMISC(("Error code received from server: <%d>\n", certReqMsg->ErrorCode));
        }
    }

    /*
     * Copy the response into something that's null-terminated; that way,
     * we won't have to worry about overwriting or reading other memory:
     */
    if (retVal == OSPC_ERR_NO_ERROR) {
        OSPM_MALLOC(caCertResponse, unsigned char, certReqMsg->ResponseSz + 1);
        if (caCertResponse == OSPC_OSNULL) {
            retVal = OSPC_ERR_ENROLL_NO_MEMORY;
            OSPM_DBGERRORLOG(retVal, "No memory for the null-terminated response.\n");
        }
    }

    /*
     * Everything looks alright with the response so far, so now let's get
     * the CA certificate ( base64 decoded ) from the response:
     */
    if (retVal == OSPC_ERR_NO_ERROR) {
        OSPM_MEMSET(caCertResponse, 0, certReqMsg->ResponseSz + 1);
        OSPM_STRNCPY(caCertResponse, certReqMsg->ResponseMsg, certReqMsg->ResponseSz);

        retVal = OSPPExtractCACertFromResponse(caCertResponse, certReqMsg->ResponseSz, &(ospvEnrollParamsInOut->CACert),
                    &(ospvEnrollParamsInOut->CACertLen), &(ospvEnrollParamsInOut->CACertB64), &(ospvEnrollParamsInOut-> CACertB64Len));

        if (retVal != OSPC_ERR_NO_ERROR) {
            OSPM_DBGERRORLOG(retVal, "Unable to extract CA certificate from response.");
        }
    }

    /*
     * If we got the binary string for the CA certificate successfully,
     * then we'll have to validate it. Here we'll turn it into an ASN.1
     * object, check the structure, check the signature ( the CA certificate
     * should be self-signed ), and check the fingerprint. The enrollment
     * parameters structure should contain the CA certificate and the acceptable
     * fingerprint for the certificate prior to the validation stage.
     */
    if (retVal == OSPC_ERR_NO_ERROR) {
        retVal = OSPPValidateCACertificate(ospvEnrollParamsInOut);
    }

    /* Now it's time to shutdown the communications manager: */
    if (caCommMgr != OSPC_OSNULL) {
        OSPM_DBGMISC(("Cleaning up communications manager...\n"));
        OSPPEnrollCleanupCommMgr(&caCommMgr);
    }

    /* Destroy the memory for the enrollment request: */
    OSPPMsgInfoDelete(&certReqMsg);

    /* Destroy the null-terminated CA certificate response if necessary: */
    if (caCertResponse != OSPC_OSNULL) {
        OSPM_FREE(caCertResponse);
    }

    OSPM_DBGEXIT(("EXIT: OSPPRetrieveCACert\n"));

    return retVal;
}

/*
 * Create the CA retrieval request. This function should create that
 * request and put it into the ospvCARequest that gets passed in. Note
 * that the memory alloced here for ospvCARequest will not be unmalloced
 * by this function; it will have to be freed by the calling function.
 *
 * Input: ospvCARequest ( also written to; should be empty initially. )
 *
 */
int OSPPCreateCARetrievalRequest(OSPTENROLLPARAMS *ospvEnrollParamsIn,
    unsigned char **ospvCARequestOut)
{
    int retVal = OSPC_ERR_NO_ERROR;

    /* Length of the request being built: */
    int requestStrLen = -1;

    /* The request being built: */
    unsigned char *requestStr = OSPC_OSNULL;

    OSPM_DBGENTER(("ENTER: OSPPCreateCARetrievalRequest\n"));

    /* If ( the output request is null ) then set an error code */
    if (ospvCARequestOut == OSPC_OSNULL) {
        retVal = OSPC_ERR_ENROLL_INVALID_ARG;
    }

    if (retVal == OSPC_ERR_NO_ERROR)

        /*
         * If ( everything has gone ok so far ) then
         *  o calculate the length of the request: the length of
         *    "operation=GetCACert".
         *  o allocate that much memory, plus one byte ( for null )
         *  o if ( we couldn't malloc memory ) then
         *      o something really bad happened, so set the return value to
         *        an error code and get out.
         */
        if (retVal == OSPC_ERR_NO_ERROR) {
            requestStrLen = OSPM_STRLEN(OSPC_ENROLL_OPERATION_REQ_PARAM) +
                OSPM_STRLEN(OSPC_ENROLL_NAME_VALUE_DELIMITER) +
                OSPM_STRLEN(OSPC_ENROLL_OP_CA_CERT_REQ_PARAM);

            OSPM_MALLOC(requestStr, unsigned char, requestStrLen + 1);
            if (requestStr == OSPC_OSNULL) {
                OSPM_DBGERRORLOG(retVal, "Unable to allocate space for request\n");
            }
        }

    /*
     * If ( the parameters and the memory setting were ok ) then
     *   o copy the request parameters into the request
     *   o set the CA certificate request to be the string that gets built.
     */
    if (retVal == OSPC_ERR_NO_ERROR) {
        OSPM_MEMSET(requestStr, 0, requestStrLen + 1);
        OSPM_SPRINTF((char *)requestStr, "%s%s%s", OSPC_ENROLL_OPERATION_REQ_PARAM, OSPC_ENROLL_NAME_VALUE_DELIMITER, OSPC_ENROLL_OP_CA_CERT_REQ_PARAM);

        *ospvCARequestOut = requestStr;
        OSPM_DBGMISC(("request string: <%s>\n", requestStr));
    }

    OSPM_DBGEXIT(("EXIT: OSPPCreateCARetrievalRequest\n"));

    return retVal;
}

/*
 * Extract the CA certificate from the response and base64-decode it.
 * We'll also use the base64-encoded certificate to be passed back to
 * the user so that they can use it again on input ( instead of having
 * to redo the base64 encoding. )
 *
 * Input: a character string representing the response to be scanned,
 *        along with outgoing parameters for the binary BER-encoding
 *        of the CA certificate, its length, the base64 encoding of the
 *        certificate, and its length as well.
 *
 * Output: Along with the values of the outgoing parameters, a return value
 *         of OSPC_ERR_NO_ERROR is returned if the variables could be
 *         allocated and assigned properly. Otherwise, a return value of
 *         something other than OSPC_ERR_NO_ERROR is returned.
 */
int OSPPExtractCACertFromResponse(unsigned char *ospvResponseIn,
    unsigned ospvResponseLenIn,
    unsigned char **ospvCACertOut,
    unsigned *ospvCACertLenOut,
    unsigned char **ospvCACertB64Out,
    unsigned *ospvCACertB64LenOut)
{
    int retVal = OSPC_ERR_NO_ERROR;
    int caCertB64Len = 0;
    unsigned char *caCertB64 = OSPC_OSNULL;

    OSPM_DBGENTER(("ENTER: OSPPExtractCACertFromResponse\n"));

    /* Check the input parameters first: */
    if ((ospvResponseIn == OSPC_OSNULL) || (ospvCACertOut == OSPC_OSNULL) || (ospvCACertLenOut == OSPC_OSNULL) ||
        (ospvCACertB64Out == OSPC_OSNULL) || (ospvCACertB64LenOut == OSPC_OSNULL))
    {
        retVal = OSPC_ERR_ENROLL_INVALID_ARG;
        OSPM_DBGERRORLOG(retVal, "The arguments for extracting a CA certificate are invalid.\n");
    }

    /*
     * Retrieve the base64-encoded CA certificate from the response;
     * the memory should be allocated for the caCertB64 inside of
     * OSPPExtractFieldFromResponse.
     */
    if (retVal == OSPC_ERR_NO_ERROR) {
        retVal = OSPPExtractFieldFromResponse(ospvResponseIn, ospvResponseLenIn, (unsigned char *)OSPC_ENROLL_CA_CERT_RSP_PARAM, &caCertB64);

        /* Complain if we couldn't find the CA certificate: */
        if (retVal != OSPC_ERR_NO_ERROR) {
            OSPM_DBGERRORLOG(retVal, "Unable to find the base64-encoded CA certificate in the response.\n");
        }

        /* Set an error code and complain if the return value was null: */
        else if (caCertB64 == OSPC_OSNULL) {
            retVal = OSPC_ERR_ENROLL_NO_MEMORY;
            OSPM_DBGERRORLOG(retVal, "The base64-encoded CA certificate returned was null.\n");
        }
    }

    /*
     * If ( we could find the CA certificate ) then
     *  o get its length
     *  o set an error code and complain if it's empty
     */
    if (retVal == OSPC_ERR_NO_ERROR) {
        caCertB64Len = OSPM_STRLEN((char *)caCertB64);
        if (caCertB64Len <= 0) {
            OSPM_DBGERRORLOG(retVal, "The CA certificate returned was empty.\n");
        }
    }

    /*
     * IF ( it looks like the CA certificate might be ok ) then
     *  o allocate memory for the BER-encoded CA certificate ( that is, the
     *    one that's been base64-decoded ); we'll use the pointer returned
     *    to caCertB64 for the outgoing base64-encoding of the cert, so we
     *    don't need any space for it here
     *  o complain if the space couldn't be allocated.
     */
    if (retVal == OSPC_ERR_NO_ERROR) {
        OSPM_MALLOC(*ospvCACertOut, unsigned char, 2 * caCertB64Len + 1);
        if (*ospvCACertOut == OSPC_OSNULL) {
            retVal = OSPC_ERR_ENROLL_NO_MEMORY;
            OSPM_DBGERRORLOG(retVal, "Unable to allocate memory for the certificate buffers.\n");
        }
    }

    /*
     * If ( the memory could be allocated ) then
     *  o initialize it
     *  o set the maximum size of the base64-decoded cert to be twice the
     *    length of the base64-encoded cert. This isn't a very tight
     *    space allocation ( in fact, the decoding of a base64-encoded message
     *    is 4 * ceil( caCertB64Len/3 ) ), but it's required for the
     *    base64 decoding mechanism.
     *  o complain if we couldn't get it
     */
    if (retVal == OSPC_ERR_NO_ERROR) {
        OSPM_MEMSET(*ospvCACertOut, 0, 2 * caCertB64Len + 1);
        *ospvCACertLenOut = 2 * caCertB64Len;

        retVal = OSPPBase64Decode((char *)caCertB64, caCertB64Len, *ospvCACertOut, ospvCACertLenOut);

        if (retVal != OSPC_ERR_NO_ERROR) {
            OSPM_DBGERRORLOG(retVal, "Bad base 64 encoding for CA certificate\n");
        }
    }

    /*
     * If ( we could successfully base64-decode the certificate ) then
     *  o copy the base64-encoded CA certificate and set its length.
     */
    if (retVal == OSPC_ERR_NO_ERROR) {
        *ospvCACertB64Out = caCertB64;
        *ospvCACertB64LenOut = caCertB64Len;
    }

    OSPM_DBGEXIT(("EXIT: OSPPExtractCACertFromResponse\n"));

    return retVal;
}

/*
 * Now take the enrollment parameter's CA certificate fingerprint in hex
 * ( which should have been taken from a secure channel, such as paper
 * [ if ubiquity can be considered security ] or an SSL session with the
 * enrollment server's accompanying web site ( if available. ) If the
 * fingerprint is missing, then an error will not be generated; we'll assume
 * for now ( although this is debatable ) that the fingerprint isn't
 * necessary. If any of the other parameters are otherwise null, then
 * this function will fail. If the CA certificate is missing, then this
 * function will fail regardless of whether or not we care about checking
 * for the fingerprint. This may be changed too ( just rearrange the ordering
 * of the statements for checking the null values. )
 *
 * Input: a pointer to the enrollment parameters, which contains the CA
 *        certificate and the CA's intended fingerprint.
 *
 * Output: an error code if anything is null or if the fingerprint cannot be
 *         taken or doesn't match up to what's expected.
 */
int OSPPValidateCACertificate(OSPTENROLLPARAMS *ospvEnrollParams)
{
    int retVal = OSPC_ERR_NO_ERROR;
    unsigned char *caFprint = OSPC_OSNULL;
    OSPTASN1OBJECT *caCertASN1Object = OSPC_OSNULL;
    int caFprintLen = -1;

    /* The binary version of the input fingerprint: */
    unsigned char *inputFprintBin = OSPC_OSNULL;
    int inputFprintBinLen = 0;

    /*
     * This is the index of the CA certificate's parent ( itself ) within the
     * given list. It's ignored because it should be self-signed.
     */
    int ignoreIndex = -1;

    OSPM_DBGENTER(("ENTER: OSPPValidateCACertificate\n"));

    /* If ( the input parameters are bad ) then
     *  o there's nothing we can do; set the errorcode and complain.
     */
    if (ospvEnrollParams == OSPC_OSNULL) {
        retVal = OSPC_ERR_ENROLL_INVALID_ARG;
        OSPM_DBGERRORLOG(retVal, "Invalid enrollment parameters passed in.\n");
    }

    /*
     * Now check the CA certificate's value; if the CA certificate is null
     * or empty, then we can't check it. In this case, set an errorcode
     * and complain.
     */
    if ((ospvEnrollParams->CACert == OSPC_OSNULL) ||
        (ospvEnrollParams->CACertLen <= 0)) {
        retVal = OSPC_ERR_ENROLL_INVALID_ARG;
        OSPM_DBGERRORLOG(retVal, "The CA certificate being validated is empty.\n");
    }

    /* Create an ASN1 object out of the CA certificate received. If we cannot
     * form a certificate, then we know that there's something wrong with the
     * general format of the certificate.
     */
    if (retVal == OSPC_ERR_NO_ERROR) {
        retVal = OSPPX509CertCreate(ospvEnrollParams->CACert, &caCertASN1Object);
        if (retVal != OSPC_ERR_NO_ERROR) {
            OSPM_DBGERRORLOG(retVal, "The CA certificate received is not a valid X.509 cert.\n");
        }
    }

    /*
     * Then we successfully parsed the CA certificate's structure; now let's
     * check the signature. The signature must be self-signed; if it isn't,
     * then we'll need some other way of authenticating the certificate
     * ( such as assuming that the CA certificate entered by hand is valid.
     */
    if (retVal == OSPC_ERR_NO_ERROR) {
        OSPM_DBGMISC(("CA cert:\n"));
        OSPPDumpHex(caCertASN1Object->ElementInfo->Content, caCertASN1Object->ElementInfo->ContentLength);
        retVal = OSPPX509CertValidateCertificate(caCertASN1Object, &caCertASN1Object, 1, &ignoreIndex);

        if (retVal != OSPC_ERR_NO_ERROR) {
            OSPM_DBGERRORLOG(retVal, "The CA certificate chain is not self-signed.\n");
        }
    }

    /*
     * If the fingerprint passed in was null, then we're done; we don't
     * care about the CA fingerprint. The security policy here may be
     * changed if we absolutely require the CA's fingerprint. We won't
     * return right away if there's no other processing to be done,
     * simply for the sake of having a single entry/exit point.
     */
    if ((ospvEnrollParams->CAFprint != OSPC_OSNULL) &&
        (OSPM_STRLEN((char *)(ospvEnrollParams->CAFprint)) > 0)) {
        OSPM_DBGMISC(("fingerprint on input: \n"));
        OSPPDumpHex(ospvEnrollParams->CAFprint, 40);

        if (ospvEnrollParams->CACert == OSPC_OSNULL) {
            retVal = OSPC_ERR_ENROLL_INVALID_ARG;
            OSPM_DBGERRORLOG(retVal, "The CA certificate is missing.\n");
        }

        /* Create the memory for the hash: */
        if (retVal == OSPC_ERR_NO_ERROR) {
            OSPM_MALLOC(caFprint, unsigned char, OSPC_ENROLL_MAX_FPRINT_SIZE + 1);

            OSPM_MALLOC(inputFprintBin, unsigned char, 2 * OSPC_ENROLL_MAX_FPRINT_SIZE + 1);

            /*
             * If ( the memory allocation didn't work ) then
             *  o set an error code and complain; we'll clean up any
             *    memory allocated at the end of this block anyway.
             */
            if ((caFprint == OSPC_OSNULL) || (inputFprintBin == OSPC_OSNULL)) {
                retVal = OSPC_ERR_ENROLL_NO_MEMORY;
                OSPM_DBGERRORLOG(retVal, "Unable to create memory for the fingerprint.\n");
            }
        }

        /*
         * Now turn the hexadecimal ASCII into binary; this is better than
         * comparing two hex encodings that may have different capitalization
         * schemes.
         */
        if (retVal == OSPC_ERR_NO_ERROR) {
            inputFprintBinLen = 2 * OSPC_ENROLL_MAX_FPRINT_SIZE;
            retVal = OSPPHexToBinary(ospvEnrollParams->CAFprint, OSPM_STRLEN((char *)(ospvEnrollParams-> CAFprint)), inputFprintBin,
                (unsigned *)(&inputFprintBinLen));

            if (retVal != OSPC_ERR_NO_ERROR) {
                OSPM_DBGERRORLOG(retVal, "The fingerprint passed in could not be decoded to binary.\n");
                OSPM_DBGMISC(("fingerprint passed in: <%s>\n", ospvEnrollParams->CAFprint));
            }
        }

        /*
         * If ( we could initialize for the hash ) then
         *  o initialize the memory;
         *  o take the hash;
         *  o check the return value for calculating the fingerprint.
         */
        if (retVal == OSPC_ERR_NO_ERROR) {
            OSPM_MEMSET(caFprint, 0, OSPC_ENROLL_MAX_FPRINT_SIZE + 1);
            OSPPDumpHex(caCertASN1Object->ElementInfo->Content, caCertASN1Object->ElementInfo->ContentLength);

            /*
             * Use the CA certificate and its length as the parameters for
             * the fingerprint/digest; put the digest in caFprint and its
             * length in caFprintLen. We'll use SHA 1 for the digesting
             * algorithm; the other two paramters( OSPC_OSNULL and 0 ) are
             * superfluous values; ignore them and their green curtain.
             */
            retVal = OSPPCryptoWrapDigest(caFprint, (unsigned *)(&caFprintLen), OSPC_OSNULL, 0, ospvEnrollParams->CACert,
                ospvEnrollParams->CACertLen, OSPC_CRYPTO_FLAG_USE_SHA1_DIGEST);

            OSPM_DBGMISC(("retVal for digesting: <%d>\n", retVal));
            if (retVal == OSPC_ERR_NO_ERROR) {
                OSPM_DBGMISC(("CA fingerprint:\n"));
                OSPPDumpHex(caFprint, caFprintLen);
            }
        }

        /*
         * Now check the fingerprint against what we were given. Translate
         * the fingerprint that was taken into hex and compare it against the
         * hex fingerprint given on input.
         */
        if (retVal == OSPC_ERR_NO_ERROR) {
            OSPM_DBGMISC(("input fingerprint: \n"));
            OSPPDumpHex(inputFprintBin, caFprintLen);
            if (caFprintLen != inputFprintBinLen) {
                retVal = OSPC_ERR_ENROLL_BAD_FPRINT;
            } else if (OSPM_MEMCMP(inputFprintBin, caFprint, caFprintLen) != 0) {
                retVal = OSPC_ERR_ENROLL_BAD_FPRINT;
            }

            if (retVal == OSPC_ERR_NO_ERROR) {
                OSPM_DBGMISC(("The CA fingerprint matches the one passed in.\n"));
            } else {
                OSPM_DBGERRORLOG(retVal, "The CA certificate received had a bad fingerprint.\n");
            }
        }

        /* Free up everything that we've allocated, but only if the
         * space exists:
         */
        if (caFprint != OSPC_OSNULL) {
            OSPM_FREE(caFprint);
        }

        if (inputFprintBin != OSPC_OSNULL) {
            OSPM_FREE(inputFprintBin);
        }
    }

    /* This object is allocated outside the scope of when we check
     * the CA certificate's fingerpring, so save the freeing of this
     * object until the end.
     */
    if (caCertASN1Object != OSPC_OSNULL) {
        OSPPASN1ObjectDelete(&caCertASN1Object);
    }

    OSPM_DBGEXIT(("EXIT: OSPPValidateCACertificate\n"));

    return retVal;
}

/*
 * Translate the given hex string into binary. We'll be given the
 * length of the hex string and the length of the binary string as
 * well. We'll expect that the binary string already has enough
 * memory allocated ( at least twice as much as the hex encoding. )
 *
 * The length of the hex string dictates how much memory will be copied.
 * For example, if the length of the hex string is 200 bytes ( or 100 bytes
 * in its full binary representation ), then the number of octets to
 * be copied from the hex string can be anywhere from 2 to 200.
 *
 * The length of the binary string initially refers to the amount of
 * space that the binary string can contain. It will be set to the
 * number of bytes that were copied into the binary string.
 *
 * Input: the hex string, the length of the hex string, the binary
 *        representation, and the length of the binary representation
 *
 * Output: the binary representation and its length will be output.
 *         A return code of OSPC_ERR_NO_ERRRO ( 0 ) will be returned
 *         if everything went alright; otherwise, a non-zero code
 *         will be returned.
 */
int OSPPHexToBinary(unsigned char *ospvHexStr,
    unsigned ospvHexStrLen,
    unsigned char *ospvBinaryStr,
    unsigned *ospvBinaryStrLen)
{
    int retVal = OSPC_ERR_NO_ERROR;

    /* Index for looping through the hex string contents: */
    unsigned hexStrIndex = 0;

    /* The next two bytes from the hex string that will compose the next byte in the binary string: */
    unsigned char *nextByte = OSPC_OSNULL;

    /* The next word of data to be placed in the binary string; this will be the output of a strtol on nextByte: */
    long nextWord = 0;

    /* If ( any of the values input are null ) then  complain and set an error code. */
    if ((ospvHexStr == OSPC_OSNULL) ||
        (ospvBinaryStr == OSPC_OSNULL) || (ospvBinaryStrLen == OSPC_OSNULL)) {
        retVal = OSPC_ERR_ENROLL_INVALID_ARG;
        OSPM_DBGERRORLOG(retVal, "A null pointer was passed in.\n");
    }

    /*
     * If ( the hex string is not as long as what the caller wants
     *      to copy from it ) then
     *  o set an error code and complain.
     */
    if (retVal == OSPC_ERR_NO_ERROR) {
        if (OSPM_STRLEN((char *)ospvHexStr) < (size_t)ospvHexStrLen) {
            retVal = OSPC_ERR_ENROLL_INVALID_ARG;
            OSPM_DBGERRORLOG(retVal, "The requested length of the hex string to be translated is longer than the string itself.\n");
        }
    }

    /*
     * If ( the caller wants more bytes from the binary string than are
     *      allocated to it or if the binary string is empty ) then
     *  o set an error code and complain.
     */
    if (retVal == OSPC_ERR_NO_ERROR) {
        if ((*ospvBinaryStrLen <= 0) ||
            (*ospvBinaryStrLen < 2 * ospvHexStrLen)) {
            retVal = OSPC_ERR_ENROLL_INVALID_ARG;
            OSPM_DBGERRORLOG(retVal, "The given binary string is too small.\n");
            OSPM_DBGMISC(("%d < 2 * %d\n", *ospvBinaryStrLen, ospvHexStrLen));
        }
    }

    /*
     * For ( each character )
     *  o if ( the character isn't a hex digit ) then
     *      - set an error code and return.
     */
    for (hexStrIndex = 0; hexStrIndex < ospvHexStrLen; hexStrIndex++) {
        if (!OSPM_ISXDIGIT(ospvHexStr[hexStrIndex])) {
            retVal = OSPC_ERR_ENROLL_INVALID_ARG;
            OSPM_DBGERRORLOG(retVal, "The hex string on input is not entirely hex characters.\n");
            OSPM_DBGMISC(("character %c at offset %d of %s is not a hex character\n", ospvHexStr[hexStrIndex], hexStrIndex, ospvHexStr));
        }
    }

    /*
     * Try to allocate memory for the chunk of data that will be used for
     * translating the hex to binary. Set an error code and complain
     * if we can't.
     */
    if (retVal == OSPC_ERR_NO_ERROR) {
        OSPM_MALLOC(nextByte, unsigned char, 3);
        if (nextByte == OSPC_OSNULL) {
            retVal = OSPC_ERR_ENROLL_NO_MEMORY;
            OSPM_DBGERRORLOG(retVal, "Unable to allocate memory for translating hex ascii strings to binary.\n");
        }
    }

    /*
     * If ( all of the parameters are ok ) then
     *  o reset the length of the binary output string.
     *  o initialize the memory for the binary string.
     *  o for ( every octet in the hex  string that we want to copy ) do
     *      - copy the next octet into some other space
     *      - turn it into a long int.
     *      - take the last 8 bits ( wherever they may be ) and put them
     *        at the end of the binary string; increment the length of the
     *        binary string by one.
     */
    if (retVal == OSPC_ERR_NO_ERROR) {
        *ospvBinaryStrLen = 0;
        OSPM_MEMSET(nextByte, 0, 3);

        for (hexStrIndex = 0; (hexStrIndex < ospvHexStrLen); hexStrIndex += 2) {
            OSPM_SPRINTF((char *)nextByte, "%.2s", ospvHexStr + hexStrIndex);
            nextWord = OSPM_STRTOL((char *)nextByte, NULL, 16);

            OSPM_DBGMISC(("nextWord: %ld, %d\n", nextWord, (nextWord && OSPC_LONG_TO_BYTE_MASK)));

            ospvBinaryStr[(*ospvBinaryStrLen)++] = (unsigned char)(nextWord & OSPC_LONG_TO_BYTE_MASK);

        }
        OSPM_DBGMISC(("final binary encoding:\n"));
        OSPPDumpHex(ospvBinaryStr, *ospvBinaryStrLen);
    }

    /* Free up any memory we allocated: */
    if (nextByte != OSPC_OSNULL) {
        OSPM_FREE(nextByte);
    }

    return retVal;
}

/*
 * Given the response to scan and the field to search for, extract the
 * contents of that field into the outgoing value. We'll be careful
 * not to accidentally use names that look similar but which have different
 * lengths. For example, if we're looking for the field "def" and we're
 * given "abcdef=ghi&def=jkl", then we want to return "jkl" and not
 * "ghi". If we find an appropriate field, then we'll save it in
 * the outgoing pointer ( which we'll also allocate. )
 *
 * Input: the response to be scanned, the field to look for, and a pointer
 *        to a string that will contain the field's value ( if we find it. )
 *
 * Output: *ospvValueOut will be non-null and will contain the contents of
 *         the field if the field can be found and is non-empty. Otherwise,
 *         if the field is empty or can't be found, then an error code
 *         other than OSPC_ERR_NO_ERROR will be returned and *ospvValueOut
 *         will be untouched.
 */
int OSPPExtractFieldFromResponse(unsigned char *ospvResponse,
    unsigned ospvResponseLen,
    unsigned char *ospvSearchField,
    unsigned char **ospvValueOut)
{
    int retVal = OSPC_ERR_NO_ERROR;

    /* This will point to the value that we find, if we find it: */
    unsigned char *beginValuePtr = OSPC_OSNULL;

    /* This points to the name that we're trying to match on: */
    unsigned char *nextNamePtr = OSPC_OSNULL;

    /* Flag for determining whether or not we found a value: */
    OSPTBOOL valueFound = OSPC_FALSE;

    OSPM_DBGENTER(("ENTER: OSPPExtractFieldFromResponse\n"));

    /*
     * Check for any null or empty parameters passed in first.
     * We'll be allocating *ospvValueOut, so don't check its contents
     * ( just check that ospvValueOut isn't null. )
     */
    if ((ospvResponse == OSPC_OSNULL) || (ospvResponseLen <= 0) || (ospvSearchField == OSPC_OSNULL) ||
        (ospvValueOut == OSPC_OSNULL) || (OSPM_STRLEN((char *)ospvSearchField) <= 0))
    {
        retVal = OSPC_ERR_ENROLL_INVALID_ARG;
        OSPM_DBGERRORLOG(retVal, "Invalid arguments for searching for a field\n");
    }

    /*
     * We know that the parameters are valid, so allocate enough memory
     * to store the value that we find. The maximum possible size is
     * OSPM_STRLEN( ospvResponse ) - 2, which would happen if the response
     * was a one-field value with a one-character name ( such as
     * "a=1073741824" .) So, go ahead and allocate the size of the string.
     */
    if (retVal == OSPC_ERR_NO_ERROR) {
        OSPM_MALLOC(beginValuePtr, unsigned char, ospvResponseLen);

        if (beginValuePtr == OSPC_OSNULL) {
            retVal = OSPC_ERR_ENROLL_NO_MEMORY;
            OSPM_DBGERRORLOG(retVal, "Unable to allocate memory for saving the field's value.\n");
        }
    }

    /*
     * If ( we could allocate the memory ) then
     *  o get the beginning of the string
     *  o do:
     *      - find the first occurrence of a name in the string.
     *      - if ( the occurrence is null ) then
     *          o we're done; we couldn't find a name-value pair for the
     *            given name.
     *      - otherwise:
     *          o check the name to see if it matches a name-value pair
     *            ( that is, we don't want to use "abcd=efgh" if we're looking
     *              for the name "bc" )
     *          o if ( we matched on one string ) then we must have found
     *            something for a name and a value.
     *              - if ( the value is null ) then
     *                  o the value we found here is empty; we can't use it.
     *              - else if ( the name was at the beginning of the response )
     *                     OR ( the previous character is the field delimiter )
     *                  o then we know that we've actually found the name
     *                    and hence the correct value. We know that we
     *                    haven't matched on "abcd=efgh" if we're looking
     *                    for the name "bc", since we compare the name we
     *                    found to the beginning of the string. Likewise,
     *                    cases like "abcd=efgh&ijkl=mnop" will fall through
     *                    the cracks if we're looking for "jk" ( since
     *                    'i' != '&' )
     *       - if ( we didn't find anything on this iteration ) then
     *           o increment the pointer so that we don't get the same case.
     *     while ( we haven't found anything ) and
     *           ( we haven't run into any problems )
     */
    if (retVal == OSPC_ERR_NO_ERROR) {
        nextNamePtr = ospvResponse;

        do {
            nextNamePtr = (unsigned char *)OSPM_STRSTR((char *)nextNamePtr, (char *)ospvSearchField);

            if (nextNamePtr == OSPC_OSNULL) {
                retVal = OSPC_ERR_ENROLL_SEARCH_FIELD_NOT_FOUND;
            }

            if (retVal == OSPC_ERR_NO_ERROR) {
                OSPM_DBGMISC(("nextNamePtr: <%s>\n", nextNamePtr));
                if (OSPM_SSCANF(nextNamePtr, OSPC_ENROLL_SEARCH_PATTERN, beginValuePtr) > 0) {
                    if (beginValuePtr == OSPC_OSNULL) {
                        /* Do nothing; the field value was empty. */
                        OSPM_DBGMISC(("The value for this field is empty; ignonring current value.\n"));
                    } else if ((nextNamePtr == ospvResponse) || (OSPM_STRCHR (OSPC_ENROLL_URL_FIELD_DELIMITERS, *(nextNamePtr - 1)))) {
                        valueFound = OSPC_TRUE;
                    } else {
                        OSPM_DBGMISC(("False hit.\n"));
                    }
                } else {
                    OSPM_DBGMISC(("The given attribute was a false hit.\n"));
                }
            }

            /* Make sure that we don't get the same case again: */
            if (valueFound != OSPC_TRUE) {
                nextNamePtr++;
            }
        } while ((valueFound == OSPC_FALSE) && (retVal == OSPC_ERR_NO_ERROR));
    }

    if ((valueFound != OSPC_TRUE) || (beginValuePtr == OSPC_OSNULL)) {
        retVal = OSPC_ERR_ENROLL_SEARCH_FIELD_NOT_FOUND;
        OSPM_DBGERRORLOG(retVal, "Unable to find the field requested.\n");
        OSPM_DBGMISC(("Unable to find field <%s> in <%s>\n", ospvSearchField, ospvResponse));
    }

    /* Copy the contents of the string found into the outgoing string if we found something we can use: */
    if (retVal == OSPC_ERR_NO_ERROR) {
        retVal = OSPPCopyString(ospvValueOut, beginValuePtr);
        if (retVal != OSPC_ERR_NO_ERROR) {
            OSPM_DBGERRORLOG(retVal, "Unable to copy value found into the outgoing pointer.\n");
        }
    }

    /* Now free up the pointer we found, regardless of whether or not we were successful: */
    if (beginValuePtr != OSPC_OSNULL) {
        OSPM_FREE(beginValuePtr);
    }

    OSPM_DBGEXIT(("EXIT: OSPPExtractFieldFromResponse\n"));

    return retVal;
}

/*
 * copy the source string to the destination string; allocate and initialize
 * enough space in the destination string that we can fit everything in
 * the source string.
 *
 * Input: pointer to the destination string and the source string to be
 *        copied.
 *
 * Output: *ospvDestStr should be a replica of ospvSrcStr if everything goes
 *         well; in this case, OSPC_ERR_NO_ERROR is returned. Otherwise,
 *         an error code other than OSPC_ERR_NO_ERROR is returned.
 */
int OSPPCopyString(unsigned char **ospvDestStr, unsigned char *ospvSrcStr)
{
    int retVal = OSPC_ERR_NO_ERROR;

    /*
     * Don't assign the source string to the destination string
     * until the end; we'll perform all of our operations on this
     * string instead:
     */
    unsigned char *tmpDestStr = OSPC_OSNULL;

    /* This is the length of the source string: */
    int srcStrLen = 0;

    OSPM_DBGENTER(("ENTER OSPPCopyString\n"));

    /* Check for valid inputs first: */
    if ((ospvDestStr == OSPC_OSNULL) || (ospvSrcStr == OSPC_OSNULL)) {
        retVal = OSPC_ERR_ENROLL_INVALID_ARG;
        OSPM_DBGERRORLOG(retVal, "The parameters for copying a string were invalid.\n");
    }

    /*
     * if ( the parameters look ok ) then
     *  o get the length of the source string
     *  o if ( the source is empty ) then
     *      - set an error code and complain.
     */
    if (retVal == OSPC_ERR_NO_ERROR) {
        srcStrLen = OSPM_STRLEN((char *)ospvSrcStr);
        if (srcStrLen <= 0) {
            retVal = OSPC_ERR_ENROLL_SOURCE_STRING_EMPTY;
            OSPM_DBGERRORLOG(retVal, "Unable to copy empty source string.\n");
        }
    }

    /*
     * If ( the source string is ok ) then
     *  o allocate enough memory for the output string
     *  o if ( we couldn't do it ) then
     *      - complain
     */
    if (retVal == OSPC_ERR_NO_ERROR) {
        OSPM_MALLOC(tmpDestStr, unsigned char, srcStrLen + 1);

        if (tmpDestStr == OSPC_OSNULL) {
            retVal = OSPC_ERR_ENROLL_NO_MEMORY;
            OSPM_DBGERRORLOG(retVal, "Unable to allocate memory for destination string.\n");
        }
    }

    /*
     * If ( we could allocate the memory ) then
     *  o initialize the memory
     *  o copy the strings
     *  o assign the string created to the outgoing pointer.
     */
    if (retVal == OSPC_ERR_NO_ERROR) {
        OSPM_MEMSET(tmpDestStr, 0, srcStrLen + 1);
        OSPM_STRCPY((char *)tmpDestStr, (char *)ospvSrcStr);
        *ospvDestStr = tmpDestStr;
    }
    /* Else we had some problem so
     *  o free up any memory we may have allocated:
     */
    else {
        if (tmpDestStr != OSPC_OSNULL) {
            OSPM_FREE(tmpDestStr);
        }
    }

    OSPM_DBGEXIT(("EXIT OSPPCopyString\n"));

    return retVal;
}
