view common/listutil.c @ 50:a7b8cb29f1f6

Rename random files to garbage to clarify what they contain
author Andre Heinecke <aheinecke@intevation.de>
date Mon, 17 Mar 2014 11:36:42 +0000
parents 7055a79abe02
children 3f6378647371
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>

#ifdef RELEASE
#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 1048576 /* 1024*1024 */

/**
 *  @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
static int read_file(const char *file_name, char **data, size_t *size,
                     const size_t max_size)
{
    FILE *f;
    long file_size;

    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) {
        fclose(f);
        free(*data);
        return READ_FILE_READ_FAILED;
    }

    fclose(f);

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

    return 0;
}

/** @brief verify the certificate list
 *
 * The public key to verify against is the static publicKeyPEM data defined
 * in the pubkey header.
 *
 *  @param [in] data the list data
 *  @param [in] size the size of the data
 *
 *  @returns 0 if the list is valid a polarssl error or -1 otherwise
 */
int verify_list(char *data, 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];

    /* 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) {
            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 {
            return InvalidSignature;
        }
    }

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

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