view common/listutil.c @ 1071:fc4e1fe4e4d4

(issue116) Sign binaries with test certificate if RELESE_BUILD option is not used
author Andre Heinecke <andre.heinecke@intevation.de>
date Wed, 10 Sep 2014 17:52:11 +0200
parents 698b6a9bd75e
children edbf5e5e88f4
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>

#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 *f;
  long file_size;

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

  f = fopen(file_name, "rb");
  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;
    }

  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);

  /* 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/