view cinst/main.c @ 831:747a48996c1f

(Issue13) Precompile uninstaller Create-dist-packge now creates a temporary installer that only writes the uninstaller. Then it excutes this installer (using wine) to create the uninstaller. That uninstaller is then packaged normaly and packaged instead of the written uninstaller.
author Andre Heinecke <andre.heinecke@intevation.de>
date Thu, 24 Jul 2014 15:59:00 +0200
parents fb69aef056ea
children 698b6a9bd75e
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 main.c
 * @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.
 *
 *  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>
 *  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.
 *
 **/
#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"

/* 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 = fopen (file_name, "rb");
  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;
}


int
main (int argc, char **argv)
{
  /* TODO handle wchar arguments on Windows or do conversion dance */
  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 || 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");
      return ERR_INVALID_PARAMS;
    }

  certificate_file_name = strchr(argv[1], '=') + 1;
  choices_file_name = strchr(argv[2], '=') + 1;

  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);

  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);
      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;
        }
    }

#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);
  free (certificate_list);

  return 0;
}

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