示例#1
0
文件: fko_decode.c 项目: mrash/fwknop
static int
parse_username(char *tbuf, char **ndx, int *t_size, fko_ctx_t ctx)
{
    if((*t_size = strcspn(*ndx, ":")) < 1)
        return(FKO_ERROR_INVALID_DATA_DECODE_USERNAME_MISSING);

    if (*t_size > MAX_SPA_USERNAME_SIZE)
        return(FKO_ERROR_INVALID_DATA_DECODE_USERNAME_TOOBIG);

    strlcpy(tbuf, *ndx, *t_size+1);

    if(ctx->username != NULL)
        free(ctx->username);

    ctx->username = calloc(1, *t_size+1); /* Yes, more than we need */
    if(ctx->username == NULL)
        return(FKO_ERROR_MEMORY_ALLOCATION);

    if(b64_decode(tbuf, (unsigned char*)ctx->username) < 0)
        return(FKO_ERROR_INVALID_DATA_DECODE_USERNAME_DECODEFAIL);

    if(validate_username(ctx->username) != FKO_SUCCESS)
        return(FKO_ERROR_INVALID_DATA_DECODE_USERNAME_VALIDFAIL);

    *ndx += *t_size + 1;

    return FKO_SUCCESS;
}
示例#2
0
文件: fko_user.c 项目: alteman/fwknop
/* Get or Set the username for the fko context spa data.
*/
int
fko_set_username(fko_ctx_t ctx, const char * const spoof_user)
{
    char   *username = NULL;
    int     res = FKO_SUCCESS, is_user_heap_allocated=0;

#if HAVE_LIBFIU
    fiu_return_on("fko_set_username_init", FKO_ERROR_CTX_NOT_INITIALIZED);
#endif

    /* Must be initialized
    */
    if(!CTX_INITIALIZED(ctx))
        return FKO_ERROR_CTX_NOT_INITIALIZED;

    /* If spoof_user was not passed in, check for a SPOOF_USER enviroment
     * variable.  If it is set, use its value.
    */
    if(spoof_user != NULL && spoof_user[0] != '\0')
    {
#if HAVE_LIBFIU
        fiu_return_on("fko_set_username_strdup", FKO_ERROR_MEMORY_ALLOCATION);
#endif
        username = strdup(spoof_user);
        if(username == NULL)
            return(FKO_ERROR_MEMORY_ALLOCATION);
        is_user_heap_allocated = 1;
    }
    else
        username = getenv("SPOOF_USER");

    /* Try to get the username from the system.
    */
    if(username == NULL)
    {
        /* Since we've already tried looking at an env variable, try
         * LOGNAME next (and the cuserid() man page recommends this)
        */
        if((username = getenv("LOGNAME")) == NULL)
        {
#ifdef _XOPEN_SOURCE
            /* cuserid will return the effective user (i.e. su or setuid).
            */
            username = cuserid(NULL);
#else
            username = getlogin();
#endif
            /* if we still didn't get a username, continue falling back
            */
            if(username == NULL)
            {
                if((username = getenv("USER")) == NULL)
                {
                    username = strdup("NO_USER");
                    if(username == NULL)
                        return(FKO_ERROR_MEMORY_ALLOCATION);
                    is_user_heap_allocated = 1;
                }
            }
        }
    }

    /* Truncate the username if it is too long.
    */
    if(strnlen(username, MAX_SPA_USERNAME_SIZE) == MAX_SPA_USERNAME_SIZE)
        *(username + MAX_SPA_USERNAME_SIZE - 1) = '\0';

    if((res = validate_username(username)) != FKO_SUCCESS)
    {
        if(is_user_heap_allocated == 1)
            free(username);
#if HAVE_LIBFIU
        fiu_return_on("fko_set_username_valuser", FKO_ERROR_INVALID_DATA);
#endif
        return res;
    }

    /* Just in case this is a subsquent call to this function.  We
     * do not want to be leaking memory.
    */
    if(ctx->username != NULL)
        free(ctx->username);

    ctx->username = strdup(username);

    ctx->state |= FKO_DATA_MODIFIED;

    if(is_user_heap_allocated == 1)
        free(username);

    if(ctx->username == NULL)
        return(FKO_ERROR_MEMORY_ALLOCATION);

    return(FKO_SUCCESS);
}
示例#3
0
文件: fko_decode.c 项目: blair/fwknop
/* Decode the encoded SPA data.
*/
int
fko_decode_spa_data(fko_ctx_t ctx)
{
    char       *tbuf, *ndx, *tmp;
    int         t_size, i, is_err;

    if (! is_valid_encoded_msg_len(ctx->encoded_msg_len))
        return(FKO_ERROR_INVALID_DATA_DECODE_MSGLEN_VALIDFAIL);

    /* Make sure there are no non-ascii printable chars
    */
    for (i=0; i < (int)strnlen(ctx->encoded_msg, MAX_SPA_ENCODED_MSG_SIZE); i++)
        if(isprint(ctx->encoded_msg[i]) == 0)
            return(FKO_ERROR_INVALID_DATA_DECODE_NON_ASCII);

    /* Make sure there are enough fields in the SPA packet
     * delimited with ':' chars
    */
    ndx = ctx->encoded_msg;
    for (i=0; i < MAX_SPA_FIELDS; i++)
    {
        if ((tmp = strchr(ndx, ':')) == NULL)
            break;

        ndx = tmp;
        ndx++;
    }

    if (i < MIN_SPA_FIELDS)
        return(FKO_ERROR_INVALID_DATA_DECODE_LT_MIN_FIELDS);

    t_size = strnlen(ndx, SHA512_B64_LEN+1);

    switch(t_size)
    {
        case MD5_B64_LEN:
            ctx->digest_type = FKO_DIGEST_MD5;
            ctx->digest_len  = MD5_B64_LEN;
            break;

        case SHA1_B64_LEN:
            ctx->digest_type = FKO_DIGEST_SHA1;
            ctx->digest_len  = SHA1_B64_LEN;
            break;

        case SHA256_B64_LEN:
            ctx->digest_type = FKO_DIGEST_SHA256;
            ctx->digest_len  = SHA256_B64_LEN;
            break;

        case SHA384_B64_LEN:
            ctx->digest_type = FKO_DIGEST_SHA384;
            ctx->digest_len  = SHA384_B64_LEN;
            break;

        case SHA512_B64_LEN:
            ctx->digest_type = FKO_DIGEST_SHA512;
            ctx->digest_len  = SHA512_B64_LEN;
            break;

        default: /* Invalid or unsupported digest */
            return(FKO_ERROR_INVALID_DIGEST_TYPE);
    }

    if (ctx->encoded_msg_len - t_size < 0)
        return(FKO_ERROR_INVALID_DATA_DECODE_ENC_MSG_LEN_MT_T_SIZE);

    if(ctx->digest != NULL)
        free(ctx->digest);

    /* Copy the digest into the context and terminate the encoded data
     * at that point so the original digest is not part of the
     * encoded string.
    */
    ctx->digest = strdup(ndx);
    if(ctx->digest == NULL)
        return(FKO_ERROR_MEMORY_ALLOCATION);

    /* Zero out the rest of the encoded_msg bucket...
    */
    bzero((ndx-1), t_size);

    ctx->encoded_msg_len -= t_size+1;

    /* Make a tmp bucket for processing base64 encoded data and
     * other general use.
    */
    tbuf = malloc(FKO_ENCODE_TMP_BUF_SIZE);
    if(tbuf == NULL)
        return(FKO_ERROR_MEMORY_ALLOCATION);

    /* Can now verify the digest.
    */
    switch(ctx->digest_type)
    {
        case FKO_DIGEST_MD5:
            md5_base64(tbuf, (unsigned char*)ctx->encoded_msg, ctx->encoded_msg_len);
            break;

        case FKO_DIGEST_SHA1:
            sha1_base64(tbuf, (unsigned char*)ctx->encoded_msg, ctx->encoded_msg_len);
            break;

        case FKO_DIGEST_SHA256:
            sha256_base64(tbuf, (unsigned char*)ctx->encoded_msg, ctx->encoded_msg_len);
            break;

        case FKO_DIGEST_SHA384:
            sha384_base64(tbuf, (unsigned char*)ctx->encoded_msg, ctx->encoded_msg_len);
            break;

        case FKO_DIGEST_SHA512:
            sha512_base64(tbuf, (unsigned char*)ctx->encoded_msg, ctx->encoded_msg_len);
            break;
    }

    /* We give up here if the computed digest does not match the
     * digest in the message data.
    */
    if(constant_runtime_cmp(ctx->digest, tbuf, t_size) != 0)
    {
        free(tbuf);
        return(FKO_ERROR_DIGEST_VERIFICATION_FAILED);
    }

    /* Now we will work through the encoded data and extract (and base64-
     * decode where necessary), the SPA data fields and populate the context.
    */
    ndx = ctx->encoded_msg;

    /* The rand val data */
    if((t_size = strcspn(ndx, ":")) < FKO_RAND_VAL_SIZE)
    {
        free(tbuf);
        return(FKO_ERROR_INVALID_DATA_DECODE_RAND_MISSING);
    }

    if(ctx->rand_val != NULL)
        free(ctx->rand_val);

    ctx->rand_val = calloc(1, FKO_RAND_VAL_SIZE+1);
    if(ctx->rand_val == NULL)
    {
        free(tbuf);
        return(FKO_ERROR_MEMORY_ALLOCATION);
    }
    ctx->rand_val = strncpy(ctx->rand_val, ndx, FKO_RAND_VAL_SIZE);

    /* Jump to the next field (username).  We need to use the temp buffer
     * for the base64 decode step.
    */
    ndx += t_size + 1;
    if((t_size = strcspn(ndx, ":")) < 1)
    {
        free(tbuf);
        return(FKO_ERROR_INVALID_DATA_DECODE_USERNAME_MISSING);
    }

    if (t_size > MAX_SPA_USERNAME_SIZE)
    {
        free(tbuf);
        return(FKO_ERROR_INVALID_DATA_DECODE_USERNAME_TOOBIG);
    }

    strlcpy(tbuf, ndx, t_size+1);

    if(ctx->username != NULL)
        free(ctx->username);

    ctx->username = malloc(t_size+1); /* Yes, more than we need */
    if(ctx->username == NULL)
    {
        free(tbuf);
        return(FKO_ERROR_MEMORY_ALLOCATION);
    }

    if(b64_decode(tbuf, (unsigned char*)ctx->username) < 0)
    {
        free(tbuf);
        return(FKO_ERROR_INVALID_DATA_DECODE_USERNAME_DECODEFAIL);
    }
    if(validate_username(ctx->username) != FKO_SUCCESS)
    {
        free(tbuf);
        return(FKO_ERROR_INVALID_DATA_DECODE_USERNAME_VALIDFAIL);
    }

    /* Extract the timestamp value.
    */
    ndx += t_size + 1;
    if((t_size = strcspn(ndx, ":")) < 1)
    {
        free(tbuf);
        return(FKO_ERROR_INVALID_DATA_DECODE_TIMESTAMP_MISSING);
    }

    if (t_size > MAX_SPA_TIMESTAMP_SIZE)
    {
        free(tbuf);
        return(FKO_ERROR_INVALID_DATA_DECODE_TIMESTAMP_TOOBIG);
    }

    strlcpy(tbuf, ndx, t_size+1);

    ctx->timestamp = (unsigned int) strtol_wrapper(tbuf,
            0, -1, NO_EXIT_UPON_ERR, &is_err);
    if(is_err != FKO_SUCCESS)
    {
        free(tbuf);
        return(FKO_ERROR_INVALID_DATA_DECODE_TIMESTAMP_DECODEFAIL);
    }

    /* Extract the version string.
    */
    ndx += t_size + 1;
    if((t_size = strcspn(ndx, ":")) < 1)
    {
        free(tbuf);
        return(FKO_ERROR_INVALID_DATA_DECODE_VERSION_MISSING);
    }

    if (t_size > MAX_SPA_VERSION_SIZE)
    {
        free(tbuf);
        return(FKO_ERROR_INVALID_DATA_DECODE_VERSION_TOOBIG);
    }

    if(ctx->version != NULL)
        free(ctx->version);

    ctx->version = malloc(t_size+1);
    if(ctx->version == NULL)
    {
        free(tbuf);
        return(FKO_ERROR_MEMORY_ALLOCATION);
    }

    strlcpy(ctx->version, ndx, t_size+1);

    /* Extract the message type value.
    */
    ndx += t_size + 1;
    if((t_size = strcspn(ndx, ":")) < 1)
    {
        free(tbuf);
        return(FKO_ERROR_INVALID_DATA_DECODE_MSGTYPE_MISSING);
    }

    if (t_size > MAX_SPA_MESSAGE_TYPE_SIZE)
    {
        free(tbuf);
        return(FKO_ERROR_INVALID_DATA_DECODE_MSGTYPE_TOOBIG);
    }

    strlcpy(tbuf, ndx, t_size+1);

    ctx->message_type = strtol_wrapper(tbuf, 0,
            FKO_LAST_MSG_TYPE, NO_EXIT_UPON_ERR, &is_err);
    if(is_err != FKO_SUCCESS)
    {
        free(tbuf);
        return(FKO_ERROR_INVALID_DATA_DECODE_MSGTYPE_DECODEFAIL);
    }

    /* Extract the SPA message string.
    */
    ndx += t_size + 1;
    if((t_size = strcspn(ndx, ":")) < 1)
    {
        free(tbuf);
        return(FKO_ERROR_INVALID_DATA_DECODE_MESSAGE_MISSING);
    }

    if (t_size > MAX_SPA_MESSAGE_SIZE)
    {
        free(tbuf);
        return(FKO_ERROR_INVALID_DATA_DECODE_MESSAGE_TOOBIG);
    }

    strlcpy(tbuf, ndx, t_size+1);

    if(ctx->message != NULL)
        free(ctx->message);

    ctx->message = malloc(t_size+1); /* Yes, more than we need */
    if(ctx->message == NULL)
    {
        free(tbuf);
        return(FKO_ERROR_MEMORY_ALLOCATION);
    }

    if(b64_decode(tbuf, (unsigned char*)ctx->message) < 0)
    {
        free(tbuf);
        return(FKO_ERROR_INVALID_DATA_DECODE_MESSAGE_DECODEFAIL);
    }

    if(ctx->message_type == FKO_COMMAND_MSG)
    {
        /* Require a message similar to: 1.2.3.4,<command>
        */
        if(validate_cmd_msg(ctx->message) != FKO_SUCCESS)
        {
            free(tbuf);
            return(FKO_ERROR_INVALID_DATA_DECODE_MESSAGE_VALIDFAIL);
        }
    }
    else
    {
        /* Require a message similar to: 1.2.3.4,tcp/22
        */
        if(validate_access_msg(ctx->message) != FKO_SUCCESS)
        {
            free(tbuf);
            return(FKO_ERROR_INVALID_DATA_DECODE_ACCESS_VALIDFAIL);
        }
    }

    /* Extract nat_access string if the message_type indicates so.
    */
    if(  ctx->message_type == FKO_NAT_ACCESS_MSG
      || ctx->message_type == FKO_LOCAL_NAT_ACCESS_MSG
      || ctx->message_type == FKO_CLIENT_TIMEOUT_NAT_ACCESS_MSG
      || ctx->message_type == FKO_CLIENT_TIMEOUT_LOCAL_NAT_ACCESS_MSG)
    {
        ndx += t_size + 1;
        if((t_size = strcspn(ndx, ":")) < 1)
        {
            free(tbuf);
            return(FKO_ERROR_INVALID_DATA_DECODE_NATACCESS_MISSING);
        }

        if (t_size > MAX_SPA_MESSAGE_SIZE)
        {
            free(tbuf);
            return(FKO_ERROR_INVALID_DATA_DECODE_NATACCESS_TOOBIG);
        }

        strlcpy(tbuf, ndx, t_size+1);

        if(ctx->nat_access != NULL)
            free(ctx->nat_access);

        ctx->nat_access = malloc(t_size+1); /* Yes, more than we need */
        if(ctx->nat_access == NULL)
        {
            free(tbuf);
            return(FKO_ERROR_MEMORY_ALLOCATION);
        }

        if(b64_decode(tbuf, (unsigned char*)ctx->nat_access) < 0)
        {
            free(tbuf);
            return(FKO_ERROR_INVALID_DATA_DECODE_NATACCESS_DECODEFAIL);
        }

        if(validate_nat_access_msg(ctx->nat_access) != FKO_SUCCESS)
        {
            free(tbuf);
            return(FKO_ERROR_INVALID_DATA_DECODE_NATACCESS_VALIDFAIL);
        }
    }

    /* Now look for a server_auth string.
    */
    ndx += t_size + 1;
    if((t_size = strlen(ndx)) > 0)
    {
        if (t_size > MAX_SPA_MESSAGE_SIZE)
        {
            free(tbuf);
            return(FKO_ERROR_INVALID_DATA_DECODE_SRVAUTH_MISSING);
        }

        /* There is data, but what is it?
         * If the message_type does not have a timeout, assume it is a
         * server_auth field.
        */
        if(  ctx->message_type != FKO_CLIENT_TIMEOUT_ACCESS_MSG
          && ctx->message_type != FKO_CLIENT_TIMEOUT_NAT_ACCESS_MSG
          && ctx->message_type != FKO_CLIENT_TIMEOUT_LOCAL_NAT_ACCESS_MSG)
        {
            strlcpy(tbuf, ndx, t_size+1);

            if(ctx->server_auth != NULL)
                free(ctx->server_auth);

            ctx->server_auth = malloc(t_size+1); /* Yes, more than we need */
            if(ctx->server_auth == NULL)
            {
                free(tbuf);
                return(FKO_ERROR_MEMORY_ALLOCATION);
            }

            if(b64_decode(tbuf, (unsigned char*)ctx->server_auth) < 0)
            {
                free(tbuf);
                return(FKO_ERROR_INVALID_DATA_DECODE_SRVAUTH_DECODEFAIL);
            }

            /* At this point we should be done.
            */
            free(tbuf);

            /* Call the context initialized.
            */
            ctx->initval = FKO_CTX_INITIALIZED;
            FKO_SET_CTX_INITIALIZED(ctx);

            return(FKO_SUCCESS);
        }

        /* If we are here then we may still have a server_auth string,
         * or a timeout, or both. So we look for a ':' delimiter.  If
         * it is there we have both, if not we check the message_type
         * again.
        */
        if(strchr(ndx, ':'))
        {
            t_size = strcspn(ndx, ":");

            if (t_size > MAX_SPA_MESSAGE_SIZE)
            {
                free(tbuf);
                return(FKO_ERROR_INVALID_DATA_DECODE_EXTRA_TOOBIG);
            }

            /* Looks like we have both, so assume this is the 
            */
            strlcpy(tbuf, ndx, t_size+1);

            if(ctx->server_auth != NULL)
                free(ctx->server_auth);

            ctx->server_auth = malloc(t_size+1); /* Yes, more than we need */
            if(ctx->server_auth == NULL)
            {
                free(tbuf);
                return(FKO_ERROR_MEMORY_ALLOCATION);
            }

            if(b64_decode(tbuf, (unsigned char*)ctx->server_auth) < 0)
            {
                free(tbuf);
                return(FKO_ERROR_INVALID_DATA_DECODE_EXTRA_DECODEFAIL);
            }

            ndx += t_size + 1;
        }

        /* Now we look for a timeout value if one is supposed to be there.
        */
        if(  ctx->message_type == FKO_CLIENT_TIMEOUT_ACCESS_MSG
          || ctx->message_type == FKO_CLIENT_TIMEOUT_NAT_ACCESS_MSG
          || ctx->message_type == FKO_CLIENT_TIMEOUT_LOCAL_NAT_ACCESS_MSG)
        {
            if((t_size = strlen(ndx)) < 1)
            {
                free(tbuf);
                return(FKO_ERROR_INVALID_DATA_DECODE_TIMEOUT_MISSING);
            }
            if (t_size > MAX_SPA_MESSAGE_SIZE)
            {
                free(tbuf);
                return(FKO_ERROR_INVALID_DATA_DECODE_TIMEOUT_TOOBIG);
            }

            /* Should be a number only.
            */
            if(strspn(ndx, "0123456789") != t_size)
            {
                free(tbuf);
                return(FKO_ERROR_INVALID_DATA_DECODE_TIMEOUT_VALIDFAIL);
            }

            ctx->client_timeout = (unsigned int) strtol_wrapper(ndx, 0,
                    (2 << 15), NO_EXIT_UPON_ERR, &is_err);
            if(is_err != FKO_SUCCESS)
            {
                free(tbuf);
                return(FKO_ERROR_INVALID_DATA_DECODE_TIMEOUT_DECODEFAIL);
            }
        }
    }

    /* Done with the tmp buffer.
    */
    free(tbuf);

    /* Call the context initialized.
    */
    ctx->initval = FKO_CTX_INITIALIZED;
    FKO_SET_CTX_INITIALIZED(ctx);

    return(FKO_SUCCESS);
}
示例#4
0
/* Set the SPA encryption type.
*/
int
fko_encode_spa_data(fko_ctx_t ctx)
{
    int     res, offset = 0;
    char   *tbuf;

    /* Must be initialized
    */
    if(!CTX_INITIALIZED(ctx))
        return(FKO_ERROR_CTX_NOT_INITIALIZED);

    /* Check prerequisites.
     * --DSS XXX:  Needs review.  Also, we could make this more robust (or
     *             (at leaset expand the error reporting for the missing
     *             data).
    */
    if(  validate_username(ctx->username) != FKO_SUCCESS
      || ctx->version  == NULL || strnlen(ctx->version, MAX_SPA_VERSION_SIZE)  == 0
      || ctx->message  == NULL || strnlen(ctx->message, MAX_SPA_MESSAGE_SIZE)  == 0)
    {
        return(FKO_ERROR_INCOMPLETE_SPA_DATA);
    }

    if(ctx->message_type == FKO_NAT_ACCESS_MSG)
    {
        if(ctx->nat_access == NULL || strnlen(ctx->nat_access, MAX_SPA_MESSAGE_SIZE) == 0)
            return(FKO_ERROR_INCOMPLETE_SPA_DATA);
    }

    /* Allocate our initial tmp buffer.
    */
    tbuf = calloc(1, FKO_ENCODE_TMP_BUF_SIZE);
    if(tbuf == NULL)
        return(FKO_ERROR_MEMORY_ALLOCATION);

    /* Put it together a piece at a time, starting with the rand val.
    */
    strlcpy(tbuf, ctx->rand_val, FKO_ENCODE_TMP_BUF_SIZE);

    /* Add the base64-encoded username.
    */
    strlcat(tbuf, ":", FKO_ENCODE_TMP_BUF_SIZE);
    if((res = append_b64(tbuf, ctx->username)) != FKO_SUCCESS)
    {
        free(tbuf);
        return(res);
    }

    /* Add the timestamp.
    */
    offset = strlen(tbuf);
    sprintf(((char*)tbuf+offset), ":%u:", (unsigned int) ctx->timestamp);

    /* Add the version string.
    */
    strlcat(tbuf, ctx->version, FKO_ENCODE_TMP_BUF_SIZE);

    /* Before we add the message type value, we will once again
     * check for whether or not a client_timeout was specified
     * since the message_type was set.  If this is the case, then
     * we want to adjust the message_type first.  The easy way
     * to do this is simply call fko_set_spa_client_timeout and set
     * it to its current value.  This will force a re-check and
     * possible reset of the message type.
     *
    */
    fko_set_spa_client_timeout(ctx, ctx->client_timeout);

    /* Add the message type value.
    */
    offset = strlen(tbuf);
    sprintf(((char*)tbuf+offset), ":%i:", ctx->message_type);

    /* Add the base64-encoded SPA message.
    */
    if((res = append_b64(tbuf, ctx->message)) != FKO_SUCCESS)
    {
        free(tbuf);
        return(res);
    }

    /* If a nat_access message was given, add it to the SPA
     * message.
    */
    if(ctx->nat_access != NULL)
    {
        strlcat(tbuf, ":", FKO_ENCODE_TMP_BUF_SIZE);
        if((res = append_b64(tbuf, ctx->nat_access)) != FKO_SUCCESS)
        {
            free(tbuf);
                return(res);
        }
    }

    /* If we have a server_auth field set.  Add it here.
     *
    */
    if(ctx->server_auth != NULL)
    {
        strlcat(tbuf, ":", FKO_ENCODE_TMP_BUF_SIZE);
        if((res = append_b64(tbuf, ctx->server_auth)) != FKO_SUCCESS)
        {
            free(tbuf);
                return(res);
        }
    }

    /* If a client timeout is specified and we are not dealing with a
     * SPA command message, add the timeout here.
    */
    if(ctx->client_timeout > 0 && ctx->message_type != FKO_COMMAND_MSG)
    {
        offset = strlen(tbuf);
        sprintf(((char*)tbuf+offset), ":%i", ctx->client_timeout);
    }

    /* If encoded_msg is not null, then we assume it needs to
     * be freed before re-assignment.
    */
    if(ctx->encoded_msg != NULL)
        free(ctx->encoded_msg);

    /* Copy our encoded data into the context.
    */
    ctx->encoded_msg = strdup(tbuf);
    if(ctx->encoded_msg == NULL)
    {
        free(tbuf);
        return(FKO_ERROR_MEMORY_ALLOCATION);
    }

    /* At this point we can compute the digest for this SPA data.
    */
    if((res = fko_set_spa_digest(ctx)) != FKO_SUCCESS)
    {
        free(tbuf);
        return(res);
    }

    /* Here we can clear the modified flags on the SPA data fields.
    */
    FKO_CLEAR_SPA_DATA_MODIFIED(ctx);

    free(tbuf);

    return(FKO_SUCCESS);
}