Mercurial > trustbridge
diff cinst/certificate-installer.c @ 1176:c8f698ca6355
(issue128) Rename cinst to trustbridge-certificate-installer
author | Andre Heinecke <andre.heinecke@intevation.de> |
---|---|
date | Mon, 22 Sep 2014 11:34:06 +0200 |
parents | cinst/main.c@2a1206932f53 |
children | 12ed0b72e9f5 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/cinst/certificate-installer.c Mon Sep 22 11:34:06 2014 +0200 @@ -0,0 +1,357 @@ +/* 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. + */ +/** + * @file certificate-installer.c + * @brief Main entry point for the certificate install process. + * + * The certificate installer 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. + * + * The first parameter to this process should be list=\<file_name\> + * of the certificate list to work on. The second parameter should + * be choices=\<choices_file_name\>|uninstall + * + * choices_file_name should be the absolute path to an + * choices file formatted as: + * + * I:\<certificate\><BR> + * R:\<certificate\> + * + * Line breaks can be system dependent in the Choices file. + * + * It will only execute the choices if the + * I and R choices 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. + * + * For more verbose debug output add --debug to the call. + * + **/ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <assert.h> +#include <stdbool.h> + +#include "strhelp.h" +#include "listutil.h" +#include "logging.h" +#include "errorcodes.h" +#include "windowsstore.h" +#include "nssstore.h" +#include "portpath.h" + +/* The certificate list + choices 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 choices from an input file into the to_install + * and to_remove buffers. + * + * Lines starting with I: are treated as install choices. + * Lines starting with R: are treated as remove choices. + * Other lines are ignored. + * + * Terminates in OOM conditions. + * + * The caller needs to free the memory allocated by this function + * even when an error is returned. + * + * @param[in] file_name absolute path to the choices file. + * @param[out] to_install strv of installation choices or NULL + * @param[out] to_remove strv of remove choices or NULL + * + * @returns: 0 on success. An error code otherwise. + */ +static int +read_choices_file (char *file_name, char ***to_install, + char ***to_remove) +{ + int lines_read = 0; + char buf[MAX_LINE_LENGTH + 2]; + FILE *f = NULL; + long file_size; + + if (*to_install || *to_remove) + { + ERRORPRINTF ("Error invalid parameters.\n"); + return -1; + } + + f = port_fopen_rb(file_name, false); + if (f == NULL) + { + ERRORPRINTF ("Failed to open file: %s\n", file_name); + return ERR_NO_INSTRUCTIONS; + } + + fseek (f, 0, SEEK_END); + file_size = ftell (f); + if (file_size <= 0) + { + fclose (f); + ERRORPRINTF ("File size error: %s\n", file_name); + return ERR_NO_INSTRUCTIONS; + } + + fseek (f, 0, SEEK_SET); + + if (file_size + 1 == 0) + { + fclose (f); + ERRORPRINTF ("File seek error: %s\n", file_name); + return ERR_INVALID_INSTRUCTIONS; + } + + while (fgets (buf, MAX_LINE_LENGTH + 1, f) ) + { + size_t len = strlen (buf); /* fgets ensures buf is terminated */ + if (len <= 3) + { + ERRORPRINTF ("Line too short.\n"); + fclose (f); + return ERR_INVALID_INPUT; + } + if (lines_read++ > MAX_LINES) + { + ERRORPRINTF ("Too many lines\n"); + fclose (f); + return ERR_TOO_MUCH_INPUT; + } + if (*buf == 'I') + { + char *trimmed = buf+2; + /* Remove leading I: and trailing whitespace */ + str_trim(&trimmed); + strv_append (to_install, trimmed, strlen(trimmed)); + continue; + } + if (*buf == 'R') + { + char *trimmed = buf+2; + /* Remove leading R: and trailing whitespace */ + str_trim(&trimmed); + strv_append (to_remove, trimmed, strlen(trimmed)); + continue; + } + } + + fclose (f); + return 0; +} + +/** @brief Check that the insturctions match to the list + * + * Only certificates part of the certificate_list are allowed + * for installation. + * + * @param[in] all_certs strv of all valid certificates in a list + * @param[in] to_validate strv of choices + * + * @returns 0 on success, an error otherwise + */ +int +validate_choices (char **all_certs, char **to_validate) +{ + int i = 0, j = 0; + + if (!all_certs || strv_length (all_certs) < 1) + { + /* Invalid parameters */ + return -1; + } + + if (to_validate == NULL) + { + /* Nothing is valid */ + return 0; + } + + for (i = 0; to_validate[i]; i++) + { + bool found = false; + for (j = 0; all_certs[j]; j++) + { + if (strncmp (to_validate[i], all_certs[j], MAX_LINE_LENGTH) == + 0) + { + found = true; + break; + } + } + if (!found) + { + DEBUGPRINTF ("Failed to find certificate; \n%s\n", to_validate[i]); + return ERR_INVALID_INSTRUCTIONS; + } + } + + return 0; +} + +#ifdef DO_RELEASE_BUILD +bool g_debug = false; +#else +bool g_debug = true; +#endif + +int +main (int argc, char **argv) +{ + char **to_install = NULL, + **to_remove = NULL, + **all_valid_certs = NULL; + int ret = -1; + + char *certificate_list = NULL, + *certificate_file_name = NULL, + *choices_file_name = NULL; + size_t list_len = 0; + list_status_t list_status; + bool do_uninstall = false; + + /* Some very static argument parsing. list= and choices= is only + added to make it more transparent how this programm is called if + a user looks at the detailed uac dialog. */ + if ((argc != 3 && argc != 4) || strncmp(argv[1], "list=", 5) != 0 || + strncmp(argv[2], "choices=", 8) != 0) + { + ERRORPRINTF ("Invalid arguments.\n" + "Expected arguments: list=<certificate_list> \n" + " choices=<choices_file>|uninstall\n" + "Optional: --debug\n"); + return ERR_INVALID_PARAMS; + } + + certificate_file_name = strchr(argv[1], '=') + 1; + choices_file_name = strchr(argv[2], '=') + 1; + + if (argc == 4 && strncmp(argv[3], "--debug", 7) == 0) + { + g_debug = true; + } + + if (!certificate_file_name || !choices_file_name) + { + ERRORPRINTF ("Invalid arguments.\n" + "Expected arguments: list=<certificate_list> \n" + " choices=<choices_file>|uninstall\n"); + return ERR_INVALID_PARAMS; + } + + if (strncmp(choices_file_name, "uninstall", 9) == 0) + { + do_uninstall = true; + choices_file_name = NULL; + } + + list_status = read_and_verify_list (certificate_file_name, &certificate_list, + &list_len); + + if (list_status != Valid) + { + if (list_status == InvalidSignature) + { + ERRORPRINTF ("Failed to verify signature.\n"); + return ERR_INVALID_SIGNATURE; + } + + ERRORPRINTF ("Failed to read certificate list.\n"); + return ERR_INVALID_INPUT_NO_LIST; + } + + all_valid_certs = get_certs_from_list (certificate_list, list_len); + free (certificate_list); + + if (!all_valid_certs) + { + /* Impossible */ + return -1; + } + + + /* For uninstall we are done now */ + if (do_uninstall) + { +#ifdef WIN32 + ret = write_stores_win (NULL, all_valid_certs); + if (ret != 0) + { + ERRORPRINTF ("Failed to write windows stores retval: %i\n", ret); + } +#endif + ret = write_stores_nss (NULL, all_valid_certs); + strv_free (all_valid_certs); + return ret; + } + + ret = read_choices_file (choices_file_name, &to_install, + &to_remove); + + if (ret) + { + ERRORPRINTF ("Failed to read choices file\n"); + return ret; + } + + if (!strv_length (to_install) && !strv_length (to_remove) ) + { + ERRORPRINTF ("Failed to read choices file\n"); + return ERR_NO_INSTRUCTIONS; + } + + /* Check that the choices are ok to execute */ + if (to_install) + { + ret = validate_choices (all_valid_certs, to_install); + if (ret) + { + ERRORPRINTF ("Failed to validate choices\n"); + return ret; + } + } + + if (to_remove) + { + ret = validate_choices (all_valid_certs, to_remove); + if (ret) + { + ERRORPRINTF ("Failed to validate removal choices\n"); + return ret; + } + } + + strv_free (all_valid_certs); + +#ifdef WIN32 + ret = write_stores_win (to_install, to_remove); + if (ret != 0) + { + ERRORPRINTF ("Failed to write windows stores retval: %i\n", ret); + } +#endif + ret = write_stores_nss (to_install, to_remove); + if (ret != 0) + { + ERRORPRINTF ("Failed to write nss stores"); + } + + /* Make valgrind happy */ + strv_free (to_install); + strv_free (to_remove); + + return 0; +}