fwknop/lib/fko_hmac.c
Michael Rash ffde9c3f1a [libfko] bug fix to check strdup() return value
Using the 'fiu-run' fault injection binary, a couple of cases were
turned up with libfko does not properly check the strdup() return value.
This commit fixes these issues, and here is an illustration of the stack
trace for one such issue:

  Core was generated by `../client/.libs/fwknop -A tcp/22 -a 127.0.0.2 -D
  127.0.0.1 --get-key local_spa.'.
  Program terminated with signal 11, Segmentation fault.
  #0  __strnlen_sse2 () at ../sysdeps/x86_64/multiarch/../strnlen.S:34
  34      ../sysdeps/x86_64/multiarch/../strnlen.S: No such file or directory.
  (gdb) where
  #0  __strnlen_sse2 () at ../sysdeps/x86_64/multiarch/../strnlen.S:34
  #1  0x00007effa38189bc in _rijndael_encrypt (enc_key_len=<optimized out>, enc_key=<optimized out>, ctx=0x7effa5945750) at fko_encryption.c:141
  #2  fko_encrypt_spa_data (ctx=0x7effa5945750, enc_key=<optimized out>, enc_key_len=<optimized out>) at fko_encryption.c:605
  #3  0x00007effa381a2d6 in fko_spa_data_final (ctx=0x7effa5945750, enc_key=enc_key@entry=0x7fff3ff4aa10 "fwknoptest", enc_key_len=<optimized out>, hmac_key=hmac_key@entry=0x7fff3ff4aaa0 "", hmac_key_len=0) at fko_funcs.c:489
  #4  0x00007effa405f2fb in main (argc=<optimized out>, argv=<optimized out>) at fwknop.c:449
2014-06-08 23:09:55 -04:00

322 lines
8.8 KiB
C

/*
*****************************************************************************
*
* File: fko_hmac.c
*
* Purpose: Provide HMAC support to SPA communications
*
* Fwknop is developed primarily by the people listed in the file 'AUTHORS'.
* Copyright (C) 2009-2014 fwknop developers and contributors. For a full
* list of contributors, see the file 'CREDITS'.
*
* License (GNU General Public License):
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
* USA
*
*****************************************************************************
*/
#include "fko_common.h"
#include "fko.h"
#include "cipher_funcs.h"
#include "hmac.h"
#include "base64.h"
int
fko_verify_hmac(fko_ctx_t ctx,
const char * const hmac_key, const int hmac_key_len)
{
char *hmac_digest_from_data = NULL;
char *tbuf = NULL;
int res = FKO_SUCCESS;
int hmac_b64_digest_len = 0, zero_free_rv = FKO_SUCCESS;
/* Must be initialized
*/
if(!CTX_INITIALIZED(ctx))
return(FKO_ERROR_CTX_NOT_INITIALIZED);
if(hmac_key == NULL)
return(FKO_ERROR_INVALID_DATA);
if (! is_valid_encoded_msg_len(ctx->encrypted_msg_len))
return(FKO_ERROR_INVALID_DATA_HMAC_MSGLEN_VALIDFAIL);
if(hmac_key_len < 0 || hmac_key_len > MAX_DIGEST_BLOCK_LEN)
return(FKO_ERROR_INVALID_HMAC_KEY_LEN);
if(ctx->hmac_type == FKO_HMAC_MD5)
hmac_b64_digest_len = MD5_B64_LEN;
else if(ctx->hmac_type == FKO_HMAC_SHA1)
hmac_b64_digest_len = SHA1_B64_LEN;
else if(ctx->hmac_type == FKO_HMAC_SHA256)
hmac_b64_digest_len = SHA256_B64_LEN;
else if(ctx->hmac_type == FKO_HMAC_SHA384)
hmac_b64_digest_len = SHA384_B64_LEN;
else if(ctx->hmac_type == FKO_HMAC_SHA512)
hmac_b64_digest_len = SHA512_B64_LEN;
else
return(FKO_ERROR_UNSUPPORTED_HMAC_MODE);
if((ctx->encrypted_msg_len - hmac_b64_digest_len)
< MIN_SPA_ENCODED_MSG_SIZE)
return(FKO_ERROR_INVALID_DATA_HMAC_ENCMSGLEN_VALIDFAIL);
/* Get digest value
*/
hmac_digest_from_data = strndup((ctx->encrypted_msg
+ ctx->encrypted_msg_len - hmac_b64_digest_len),
hmac_b64_digest_len);
if(hmac_digest_from_data == NULL)
return(FKO_ERROR_MEMORY_ALLOCATION);
/* Now we chop the HMAC digest off of the encrypted msg
*/
tbuf = strndup(ctx->encrypted_msg,
ctx->encrypted_msg_len - hmac_b64_digest_len);
if(tbuf == NULL)
{
if(zero_free(hmac_digest_from_data, strnlen(hmac_digest_from_data,
MAX_SPA_ENCODED_MSG_SIZE)) == FKO_SUCCESS)
return(FKO_ERROR_MEMORY_ALLOCATION);
else
return(FKO_ERROR_ZERO_OUT_DATA);
}
if(zero_free(ctx->encrypted_msg, ctx->encrypted_msg_len) != FKO_SUCCESS)
zero_free_rv = FKO_ERROR_ZERO_OUT_DATA;
ctx->encrypted_msg = tbuf;
ctx->encrypted_msg_len -= hmac_b64_digest_len;
if(ctx->encryption_mode == FKO_ENC_MODE_ASYMMETRIC)
{
/* See if we need to add the "hQ" string to the front of the
* encrypted data.
*/
if(! ctx->added_gpg_prefix)
{
res = add_gpg_prefix(ctx);
}
}
else
{
/* See if we need to add the "Salted__" string to the front of the
* encrypted data.
*/
if(! ctx->added_salted_str)
{
res = add_salted_str(ctx);
}
}
if (res != FKO_SUCCESS)
{
if(zero_free(hmac_digest_from_data, strnlen(hmac_digest_from_data,
MAX_SPA_ENCODED_MSG_SIZE)) != FKO_SUCCESS)
zero_free_rv = FKO_ERROR_ZERO_OUT_DATA;
if(zero_free_rv == FKO_SUCCESS)
return(res);
else
return(zero_free_rv);
}
/* Calculate the HMAC from the encrypted data and then
* compare
*/
res = fko_set_spa_hmac_type(ctx, ctx->hmac_type);
if(res == FKO_SUCCESS)
{
res = fko_set_spa_hmac(ctx, hmac_key, hmac_key_len);
if(res == FKO_SUCCESS)
{
if(constant_runtime_cmp(hmac_digest_from_data,
ctx->msg_hmac, hmac_b64_digest_len) != 0)
{
res = FKO_ERROR_INVALID_DATA_HMAC_COMPAREFAIL;
}
}
}
if(zero_free(hmac_digest_from_data, strnlen(hmac_digest_from_data,
MAX_SPA_ENCODED_MSG_SIZE)) != FKO_SUCCESS)
zero_free_rv = FKO_ERROR_ZERO_OUT_DATA;
if(res == FKO_SUCCESS)
return(zero_free_rv);
else
return(res);
}
/* Return the fko HMAC data
*/
int
fko_get_spa_hmac(fko_ctx_t ctx, char **hmac_data)
{
/* Must be initialized
*/
if(!CTX_INITIALIZED(ctx))
return(FKO_ERROR_CTX_NOT_INITIALIZED);
if(hmac_data == NULL)
return(FKO_ERROR_INVALID_DATA);
*hmac_data = ctx->msg_hmac;
return(FKO_SUCCESS);
}
/* Set the HMAC type
*/
int
fko_set_spa_hmac_type(fko_ctx_t ctx, const short hmac_type)
{
/* Must be initialized
*/
if(!CTX_INITIALIZED(ctx))
return(FKO_ERROR_CTX_NOT_INITIALIZED);
if(hmac_type < 0 || hmac_type >= FKO_LAST_HMAC_MODE)
return(FKO_ERROR_INVALID_DATA_HMAC_TYPE_VALIDFAIL);
ctx->hmac_type = hmac_type;
ctx->state |= FKO_HMAC_MODE_MODIFIED;
return(FKO_SUCCESS);
}
/* Return the fko HMAC type
*/
int
fko_get_spa_hmac_type(fko_ctx_t ctx, short *hmac_type)
{
/* Must be initialized
*/
if(!CTX_INITIALIZED(ctx))
return(FKO_ERROR_CTX_NOT_INITIALIZED);
if(hmac_type == NULL)
return(FKO_ERROR_INVALID_DATA);
*hmac_type = ctx->hmac_type;
return(FKO_SUCCESS);
}
int fko_set_spa_hmac(fko_ctx_t ctx,
const char * const hmac_key, const int hmac_key_len)
{
unsigned char hmac[SHA512_DIGEST_STR_LEN] = {0};
char *hmac_base64 = NULL;
int hmac_digest_str_len = 0;
int hmac_digest_len = 0;
/* Must be initialized
*/
if(!CTX_INITIALIZED(ctx))
return(FKO_ERROR_CTX_NOT_INITIALIZED);
if(hmac_key == NULL)
return(FKO_ERROR_INVALID_DATA);
if(hmac_key_len < 0 || hmac_key_len > MAX_DIGEST_BLOCK_LEN)
return(FKO_ERROR_INVALID_HMAC_KEY_LEN);
if(ctx->hmac_type == FKO_HMAC_MD5)
{
hmac_md5(ctx->encrypted_msg,
ctx->encrypted_msg_len, hmac, hmac_key, hmac_key_len);
hmac_digest_len = MD5_DIGEST_LEN;
hmac_digest_str_len = MD5_DIGEST_STR_LEN;
}
else if(ctx->hmac_type == FKO_HMAC_SHA1)
{
hmac_sha1(ctx->encrypted_msg,
ctx->encrypted_msg_len, hmac, hmac_key, hmac_key_len);
hmac_digest_len = SHA1_DIGEST_LEN;
hmac_digest_str_len = SHA1_DIGEST_STR_LEN;
}
else if(ctx->hmac_type == FKO_HMAC_SHA256)
{
hmac_sha256(ctx->encrypted_msg,
ctx->encrypted_msg_len, hmac, hmac_key, hmac_key_len);
hmac_digest_len = SHA256_DIGEST_LEN;
hmac_digest_str_len = SHA256_DIGEST_STR_LEN;
}
else if(ctx->hmac_type == FKO_HMAC_SHA384)
{
hmac_sha384(ctx->encrypted_msg,
ctx->encrypted_msg_len, hmac, hmac_key, hmac_key_len);
hmac_digest_len = SHA384_DIGEST_LEN;
hmac_digest_str_len = SHA384_DIGEST_STR_LEN;
}
else if(ctx->hmac_type == FKO_HMAC_SHA512)
{
hmac_sha512(ctx->encrypted_msg,
ctx->encrypted_msg_len, hmac, hmac_key, hmac_key_len);
hmac_digest_len = SHA512_DIGEST_LEN;
hmac_digest_str_len = SHA512_DIGEST_STR_LEN;
}
hmac_base64 = calloc(1, MD_HEX_SIZE(hmac_digest_len)+1);
if (hmac_base64 == NULL)
return(FKO_ERROR_MEMORY_ALLOCATION);
b64_encode(hmac, hmac_base64, hmac_digest_len);
strip_b64_eq(hmac_base64);
if(ctx->msg_hmac != NULL)
free(ctx->msg_hmac);
ctx->msg_hmac = strdup(hmac_base64);
free(hmac_base64);
if(ctx->msg_hmac == NULL)
return(FKO_ERROR_MEMORY_ALLOCATION);
ctx->msg_hmac_len = strnlen(ctx->msg_hmac, hmac_digest_str_len);
switch(ctx->msg_hmac_len)
{
case MD5_B64_LEN:
break;
case SHA1_B64_LEN:
break;
case SHA256_B64_LEN:
break;
case SHA384_B64_LEN:
break;
case SHA512_B64_LEN:
break;
default:
return(FKO_ERROR_INVALID_DATA_HMAC_LEN_VALIDFAIL);
}
return FKO_SUCCESS;
}
/***EOF***/