view common/listutil.c @ 502:e551de11d8b6

Properly handle the case that the file does not exist. TRUNCATE makes create file fail if the file does not exist but we need TRUNCATE in the case that the file already exists
author Andre Heinecke <aheinecke@intevation.de>
date Mon, 28 Apr 2014 09:18:07 +0000
parents 17e1c8f37d72
children f595fcbe3e76
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"

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

/**
 *  @brief Read a file into memory.
 *
 * The caller needs to free data
 *
 * @param[in] fileName Name of the file.
 * @param[out] data the file content
 * @param[out] size size in bytes of the file content.
 * @param[in] max_size the maximum amount of bytes to read.
 *
 * @return 0 on success an error code otherwise.
 */
#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
static 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;
    /* Fixed key size of 3072 implies the sizes*/
    const size_t sig_b64_size = 512;
    size_t sig_size = 384;

    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 != 384) {
/*        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);
#if 0
    {
        int i;
        FILE *foo = fopen("/tmp/testdump", "w");
        FILE *foo2 = fopen("/tmp/rawdump", "w");
        for (i=0; i< (int)(size - sig_b64_size - 2); i++)
            fprintf (foo, "%c", p[i]);
        for (i=0; i< (int)(size); i++)
            fprintf (foo2, "%c", data[i]);
        fclose(foo);
        printf ("Hash: \n");
        for (i=0; i<32; i++) {
            printf ("%x", hash[i]);
        }
        printf("\n");
    }
#endif

    ret = pk_parse_public_key(&pub_key_ctx, public_key_pem,
                              public_key_pem_size);
    if (ret != 0) {
        printf("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) {
        printf("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)
    {
      printf ("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/