view common/listutil.c @ 289:9ad00a3255f4

Change cinst from stdin input to use arguments. As we have to execute this process on Windows over the shell a stdin / stdout communication is not really possible without some major hacks. So you now have to supply an instructions file and the path to the certificatelist as arguments when this process is called
author Andre Heinecke <aheinecke@intevation.de>
date Wed, 02 Apr 2014 13:52:02 +0000
parents 881ce5126f07
children 57867a523dcf
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
#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 - 4);
          /* 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/