Mercurial > trustbridge
view cinst/certificate-installer.c @ 1301:320ee276f5ff
Merge.
author | Bernhard Reiter <bernhard@intevation.de> |
---|---|
date | Mon, 29 Sep 2014 16:31:13 +0200 |
parents | c8f698ca6355 |
children | 12ed0b72e9f5 |
line wrap: on
line source
/* 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; }