view common/listutil.c @ 1395:a2574a029322

Fix Base 64 signature size calculation. If the signature byte size is not equally dividable by three the base 64 encoding needs three additional bytes. The value is now fixed to avoid such errors in the future.
author Andre Heinecke <andre.heinecke@intevation.de>
date Mon, 26 Jan 2015 13:17:32 +0100
parents 8d27c6d226cd
children
line wrap: on
line source
/* Copyright (C) 2014 by Bundesamt für Sicherheit in der Informationstechnik
 * Software engineering by Intevation GmbH
 *
 * This file is Free Software under the GNU GPL (v>=2)
 * and comes with ABSOLUTELY NO WARRANTY!
 * See LICENSE.txt for details.
 */
#include "listutil.h"

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>

#ifdef WIN32
#include <share.h>
#endif

#include "strhelp.h"
#include "logging.h"
#include "portpath.h"

#ifdef RELEASE_BUILD
#include "pubkey-release.h"
#else
#include "pubkey-test.h"
#endif

#ifndef __clang__
#pragma GCC diagnostic ignored "-Wconversion"
#endif
/* Polarssl mh.h contains a conversion which gcc warns about */
#include <polarssl/pk.h>
#include <polarssl/base64.h>
#include <polarssl/sha256.h>
#ifndef __clang__
#pragma GCC diagnostic pop
#endif

#define MAX_FILESIZE (MAX_LINE_LENGTH * MAX_LINES)

#define READ_FILE_UNREADABLE -1
#define READ_FILE_TOO_LARGE -2
#define READ_FILE_NO_MEMORY -3
#define READ_FILE_READ_FAILED -4
#define READ_FILE_INVALID_CALL -5
int
read_file(const char *file_name, char **data, size_t *size,
          const size_t max_size, FILE **fptr)
{
  FILE *f;
  long file_size;

  if (!file_name || !data || !size || !max_size)
    {
      return READ_FILE_INVALID_CALL;
    }

  f = port_fopen_rb(file_name, true);
  if (f == NULL)
    return READ_FILE_UNREADABLE;

  fseek(f, 0, SEEK_END);
  file_size = ftell(f);
  if (file_size <= 0)
    {
      fclose(f);
      return READ_FILE_UNREADABLE;
    }

  fseek(f, 0, SEEK_SET);

  if (file_size + 1 == 0)
    {
      fclose(f);
      return READ_FILE_TOO_LARGE;
    }
  *size = (size_t) file_size;

  if (*size > max_size)
    {
      fclose(f);
      return READ_FILE_TOO_LARGE;
    }

  *data = (char *) malloc( *size + 1 );
  if (*data == NULL)
    {
      fclose(f);
      return READ_FILE_NO_MEMORY;
    }

  if (fread(*data, 1, *size, f) != *size)
    {
      free(*data);
      fclose(f);
      return READ_FILE_READ_FAILED;
    }

  if (fptr)
    {
      *fptr = f;
    }
  else
    {
      fclose(f);
    }

  (*data)[*size] = '\0';

  return 0;
}

int verify_list(const char *data, const size_t size)
{
  int ret = -1;
  pk_context pub_key_ctx;
  char *p;

  /* Modulus / 8 are the necessary bytes. */
#ifndef TRUSTBRIDGE_RSA_KEY_SIZE
# error "Key size undefined"
#endif
  const size_t sig_b64_size = TRUSTBRIDGE_RSA_B64_SIZE;
  size_t sig_size = TRUSTBRIDGE_RSA_KEY_SIZE / 8;

  char signature_b64[sig_b64_size + 1];
  unsigned char signature[sig_size];
  /* Hash algroithm is sha256 */
  unsigned char hash[32];

  if (!data || !size)
    {
      ERRORPRINTF ("Invalid call.\n");
      return -1;
    }

  /* Fetch the signature from the first line od data */
  p = strchr(data, '\r');
  if (p == 0 || (unsigned int)(p - (data + 2)) != sig_b64_size)
    {
      DEBUGPRINTF("Invalid data. Signature might be too long.\n");
      DEBUGPRINTF("Should: %u is: %u\n", (unsigned int) sig_b64_size, (unsigned int)(p - (data + 2)));
      return -1;
    }
  strncpy(signature_b64, data + 2, sig_b64_size);
  signature_b64[sig_b64_size] = '\0';

  ret = base64_decode(signature, &sig_size,
                      (unsigned char *)signature_b64, sig_b64_size);

  if (ret != 0 || sig_size != TRUSTBRIDGE_RSA_KEY_SIZE / 8)
    {
      DEBUGPRINTF("failed to decode signature\n");
      return -1;
    }

  /* Hash is calculated over the data without the first line.
   * linebreaks are \r\n so the first char of the new line is
   * p+2 */
  p += 2;
  /* Size of the data to hash is the size - signature line
   * signature line is sig_b64_size - "S:" and - "\r\n" so -4*/
  sha256((unsigned char *)p, size - sig_b64_size - 4, hash, 0);

  pk_init(&pub_key_ctx);

  ret = pk_parse_public_key(&pub_key_ctx, public_key_pem,
                            public_key_pem_size);
  if (ret != 0)
    {
      ERRORPRINTF ("pk_parse_public_key failed with -0x%04x\n\n", -ret);
      pk_free(&pub_key_ctx);
      return ret;
    }

  ret = pk_verify(&pub_key_ctx, POLARSSL_MD_SHA256, hash, 0,
                  signature, sig_size);

  if (ret != 0)
    {
      ERRORPRINTF ("pk_verify failed with -0x%04x\n\n", -ret);
    }
  pk_free(&pub_key_ctx);

  return ret;
}

list_status_t read_and_verify_list(const char *file_name, char **data,
                                   size_t *size)
{
  list_status_t retval = UnknownError;
  *data = NULL;
  *size = 0;
  int ret = 0;

  ret = read_file(file_name, data, size, MAX_FILESIZE, NULL);

  /* printf ("Ret: %i \n", ret); */
  if (ret != 0)
    {
      if (ret == READ_FILE_TOO_LARGE)
        {
          return TooLarge;
        }
      if (ret == READ_FILE_UNREADABLE)
        {
          return SeekFailed;
        }
      if (ret == READ_FILE_READ_FAILED)
        {
          return ReadFailed;
        }
      return UnknownError;
    }

  if (!*data || !*size)
    {
      /* File is probably empty */
      return UnknownError;
    }

  if (**data != 'S')
    {
      retval = InvalidFormat;
    }
  else
    {
      ret = verify_list (*data, *size);
      if (ret == 0)
        {
          /* Hooray */
          return Valid;
        }
      if (ret == -1)
        {
          /* our error */
          retval = InvalidFormat;
        }
      else
        {
          retval = InvalidSignature;
        }
    }

  if (retval != Valid && *data)
    {
      free(*data);
      *data = NULL;
      *size = 0;
    }
  return retval;
}

char **
get_certs_from_list (char *data, const size_t size)
{
  char *cur = data;
  char **retval = NULL;

  if (!data || !size)
    {
      ERRORPRINTF ("Invalid call to get_certs_to_remove \n");
      return NULL;
    }

  while (cur)
    {
      char *next = strchr(cur, '\n');
      if (strlen(cur) > 3 && (cur[0] == 'I' || cur[0] == 'R') &&
          next - cur > 4)
        {
          size_t len = (size_t) (next - cur - 3);
          /* Remove I: or R: at the beginning and \r\n at the end */
          strv_append(&retval, cur + 2, len);
        }
      cur = next ? (next + 1) : NULL;
    }
  return retval;
}

http://wald.intevation.org/projects/trustbridge/