view common/listutil.c @ 389:3be838c3e4d8

Handle installation success. Save / load last installed list. This also adds a feature that you can specifiy the download resource when release build is not set so that you can update a list without modifying the server data.
author Andre Heinecke <andre.heinecke@intevation.de>
date Tue, 15 Apr 2014 19:05:49 +0200
parents f6ce186cebc2
children 17e1c8f37d72
line wrap: on
line source
#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/