view common/listutil.c @ 1081:edbf5e5e88f4

(issue118) Extend verify_binary to carry an open file * binverify.c: Change result to a structure containing an open fptr Use in Memory data for windows verification. * mainwindow.cpp, selftest.c: Handle the returend structure * binverifytest.cpp: Test for the exclusive read and update signature. * listutil.c: Add optional fptr parameter to read_file
author Andre Heinecke <andre.heinecke@intevation.de>
date Thu, 11 Sep 2014 12:05:24 +0200
parents 698b6a9bd75e
children fd7d04bb37cb
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"

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

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

#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;
    }
#ifdef WIN32
    {
      wchar_t *wFilename = utf8_to_wchar(file_name, strlen(file_name));
      if (!wFilename)
        {
          return READ_FILE_UNREADABLE;
        }
      /* We open and write protect the file here so that
         as long as the file is open we can be sure that
         it was not modified and can use it in subsequent
         calls based on the filename. */
      f = _wfsopen(wFilename, L"rb", _SH_DENYWR);
      xfree(wFilename);
    }
#else
  f = fopen(file_name, "rb");
#endif
  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_KEY_SIZE / 8 * 4 / 3;
  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)
    {
      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)
    {
      /*        printf("Invalid data. Signature might be too long.\n"); */
      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)
    {
      /*        printf("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)
        {
          /* TODO: work with errno ? */
          /* errsv = errno; */
          /* perror("read_and_verify_list(), READ_FILE_UNREADABLE:");  */
          return SeekFailed;
        }
      if (ret == READ_FILE_READ_FAILED)
        {
          /* TODO: work with ferror() or feof() ? */
          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/