view cinst/main.c @ 92:4980b0deb773

Fix memleak in case of invalid signature
author Andre Heinecke <aheinecke@intevation.de>
date Fri, 21 Mar 2014 09:47:54 +0000
parents 80ab2168760f
children c602d8cfa619
line wrap: on
line source
/** @brief Main entry point for the cinst process.
 *
 *  The cinst process may or may not be run with elevated
 *  privileges. When run with elevated privileges this
 *  process will modify system wide certificate stores.
 *  Otherwise only the users certificate stores are modified.
 *
 *  It expects a certificatelist on stdin enclosed in a
 *  -----BEGIN CERTIFICATE LIST-----
 *  ...
 *  -----END CERTIFICATE LIST-----
 *
 *  Followed by additional instruction lines of:
 *  I:<certificate>
 *  R:<certificate>
 *
 *  It will only execute the instructions if the
 *  I and R instructions are also part of the signed
 *  certificate list. The signature is validated with the
 *  built in key.
 *
 *  The special instruction "UNINSTALL" will cause the installer
 *  to remove all certificates (Even those marked with I) that
 *  are part of the list to be removed.
 *
 **/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>

#include <polarssl/base64.h>

#include "strhelp.h"
#include "listutil.h"
#include "errorcodes.h"

#ifdef WIN32
#include <windows.h>
#include <wincrypt.h>
#endif

/* The certificate list + instructions may only be so long as
 * twice the accepted certificatelist size */
#define MAX_INPUT_SIZE MAX_LINE_LENGTH * MAX_LINES * 2

/* @brief Read stdin into data structures.
 *
 * Reads from stdin and sorts the input into the respective
 * variables. The pointers returned need to be freed by the caller.
 * Terminates in OOM conditions.
 *
 * The caller needs to free the memory allocated by this function
 * even when an error is returned.
 *
 * @returns: 0 on success. An error code otherwise.
 */
int readInput(char **certificate_list, char ***to_install,
              char ***to_remove)
{
    int lines_read = 0;
    int readingList = 0;
    size_t list_size = 0;
    char buf[MAX_LINE_LENGTH + 1];

    if (*certificate_list || *to_install || *to_remove) {
        printf("Error invalid parameters\n");
        return -1;
    }

    while (fgets(buf, MAX_LINE_LENGTH + 1, stdin)) {
        size_t len = strlen(buf); /* fgets ensures buf is terminated */
        if (len < 2) {
            printf("Line to short.\n");
            return ERR_INVALID_INPUT;
        }
        if (lines_read ++ > MAX_LINES) {
            printf("Too many lines\n");
            return ERR_TOO_MUCH_INPUT;
        }
        if (strcmp("-----BEGIN CERTIFICATE LIST-----\r\n", buf) == 0){
            readingList = 1;
            continue;
        }
        if (strcmp("-----END CERTIFICATE LIST-----\r\n", buf) == 0){
            readingList = 0;
            continue;
        }
        if (readingList) {
            str_append_str(certificate_list, &list_size, buf, len);
            continue;
        }
        if (*buf == 'I') {
            /* Remove leading I: and trailing \r\n */
            array_append_str(to_install, buf+2, len - 4);
            continue;
        }
        if (*buf == 'R') {
            /* Remove leading R: and trailing \r\n */
            array_append_str(to_remove, buf+2, len - 4);
            continue;
        }
        if (strcmp("UNINSTALL", buf) == 0) {
            /* Remove trailing \r\n */
            array_append_str(to_remove, buf, len - 2);
        }
    }

    return 0;
}
/*
int validate_instructions(const char *certificate_list,
                          const size_t list_len,
                          const char **to_install,
                          const char **to_remove)
{
     TODO 
    (void *) certificate_list;
    (void **) to_install;
    (void **) to_remove;
    (void) list_len;

    return 0;
}
*/

#ifdef WIN32
/** @brief Install certificates into Windows store
 *
 * @param [in] to_install NULL terminated array of base64 encoded certificates.
 * @param [in] user_store set to True if the certificates shoudl be installed
 *             only for the current user. O for system wide installation.
 * @returns 0 on success an errorcode otherwise.
 */
int install_certificates_win(const char **to_install, int user_store)
{
    int i = 0;
    HCERTSTORE hStore = NULL;

    if (!user_store) {
        // Access user store
        hStore = CertOpenStore(CERT_STORE_PROV_SYSTEM, 0,
                               0, CERT_SYSTEM_STORE_CURRENT_USER, L"Root");
    } else {
        // Access machine store
        hStore = CertOpenStore(CERT_STORE_PROV_SYSTEM, 0,
                               0, CERT_SYSTEM_STORE_LOCAL_MACHINE, L"Root");
    }

    if (!hStore) {
        return ERR_STORE_ACCESS_DENIED;
    }

    while (to_install[i]) {
        size_t needed_len = 0;
        size_t cert_len = strnlen(to_install[i], MAX_LINE_LENGTH);
        int ret = -1;
        unsigned char *buf;
        /* Check the needed size for the buffer */
        ret = base64_decode(NULL, &needed_len,
                            (unsigned char *)to_install[i], cert_len);

        if (ret != 0 && ret != POLARSSL_ERR_BASE64_BUFFER_TOO_SMALL) {
            return ERR_INVALID_INSTRUCTIONS;
        }

        buf = xmalloc(needed_len);
        memset (buf, 0, needed_len);

        ret = base64_decode(buf, &needed_len,
                            (unsigned char *)to_install[i], cert_len);

        if (ret != 0) {
            return ERR_INVALID_INSTRUCTIONS;
        }

        ret = CertAddEncodedCTLToStore (hStore,
                                        X509_ASN_ENCODING,
                                        (PBYTE)buf,
                                        needed_len,
                                        CERT_STORE_ADD_ALWAYS,
                                        NULL);

        if (ret != 0) {
            printf("Failed to add certificate\n");
            free(buf);
            return ret;
        }
        free(buf);
    }
    if(hStore) {
        CertCloseStore(hStore, 0);
    }
    return 0;
}
#endif

int main() {
    char **to_install = NULL;
    char **to_remove = NULL;
    char *certificate_list = NULL;
    size_t list_len = 0;
    int ret = -1;
    /*
        i = 0 ,
        uninstall = 0;
    */
    ret = readInput(&certificate_list, &to_install, &to_remove);

    if (ret != 0) {
        return ret;
    }

    if (!certificate_list) {
        return ERR_INVALID_INPUT_NO_LIST;
    }

    list_len = strnlen(certificate_list, MAX_INPUT_SIZE);

    ret = verify_list(certificate_list, list_len);

    if (ret != 0) {
        return ERR_INVALID_SIGNATURE;
    }

    if (!strv_length(to_install) && !strv_length(to_remove)) {
        return ERR_NO_INSTRUCTIONS;
    }


    /* Check that the instructions are ok to execute  
    ret = validate_instructions(certificate_list, list_len, to_install,
                                to_remove);
    if (ret != 0) {
        return ERR_INVALID_INSTRUCTIONS;
    }

    if (to_remove) {
        for (i=0; to_remove[i]; i++) {
            if (strncmp("UNINSTALL", to_remove[i], MAX_LINE_LENGTH)) {
                uninstall = 1;
                break;
            }
        }
    }

    if (uninstall) {
        
    }
*/

#ifdef WIN32
    install_certificates_win((const char**) to_install, 1);
    //remove_certificates_win((const char**) to_remove, 1);
#endif

    /* Make valgrind happy */
    strfreev(to_install);
    strfreev(to_remove);
    free(certificate_list);

    return 0;
}

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