view cinst/main.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 4de97f74d038
children b460d2cf088d
line wrap: on
line source
/**
 * @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 instruction=<instruction_file_name>|uninstall
 *
 *  instruction_file_name should be the absolute path to an
 *  instructions file formatted as:
 *
 *  I:<certificate>
 *  R:<certificate>
 *
 *  Line breaks can be system dependent in the Instructions file.
 *
 *  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.
 *
 **/
#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"

/* 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 instructions from an input file into the to_install
 * and to_remove buffers.
 *
 * Lines starting with I: are treated as install instructions.
 * Lines starting with R: are treated as remove instructions.
 * 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 instructions file.
 * @param[out] to_install strv of installation instructions or NULL
 * @param[out] to_remove strv of remove instructions or NULL
 *
 * @returns: 0 on success. An error code otherwise.
 */
static int
read_instructions_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)
    {
      printf ("Error invalid parameters.\n");
      return -1;
    }

  f = fopen (file_name, "rb");
  if (f == NULL)
    return ERR_NO_INSTRUCTIONS;

  fseek (f, 0, SEEK_END);
  file_size = ftell (f);
  if (file_size <= 0)
    {
      fclose (f);
      return ERR_NO_INSTRUCTIONS;
    }

  fseek (f, 0, SEEK_SET);

  if (file_size + 1 == 0)
    {
      fclose (f);
      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)
        {
          printf ("Line too short.\n");
          return ERR_INVALID_INPUT;
        }
      if (lines_read++ > MAX_LINES)
        {
          printf ("Too many lines\n");
          return ERR_TOO_MUCH_INPUT;
        }
      if (*buf == 'I')
        {
          /* Remove leading I: and trailing \n */
          strv_append (to_install, buf + 2, len - 3);
          continue;
        }
      if (*buf == 'R')
        {
          /* Remove leading R: and trailing \n */
          strv_append (to_remove, buf + 2, len - 3);
          continue;
        }
    }

  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 instructions
 *
 * @returns 0 on success, an error otherwise
 */
int
validate_instructions (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 - 2) ==
              0)
            {
              found = true;
              break;
            }
        }
      if (!found)
        {
          printf ("Install instruction with invalid certificate\n.");
          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,
       *instruction_file_name = NULL;
  size_t list_len = 0;
  list_status_t list_status;
  bool do_uninstall = false;

  /* Some very static argument parsing. list= and instructions= 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], "instructions=", 13) != 0)
    {
      ERRORPRINTF ("Invalid arguments.\n"
                   "Expected arguments: list=<certificate_list> \n"
                   "                    instructions=<instructions_file>|uninstall\n");
      return ERR_INVALID_PARAMS;
    }

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

  if (!certificate_file_name || !instruction_file_name)
    {
      ERRORPRINTF ("Invalid arguments.\n"
                   "Expected arguments: list=<certificate_list> \n"
                   "                    instructions=<instructions_file>|uninstall\n");
      return ERR_INVALID_PARAMS;
    }

  if (strncmp(instruction_file_name, "uninstall", 9) == 0)
    {
      do_uninstall = true;
      instruction_file_name = NULL;
    }

  list_status = read_and_verify_list (certificate_file_name, &certificate_list,
                                      &list_len);

  if (list_status != Valid)
    {
      if (list_status == InvalidSignature)
        {
          return ERR_INVALID_SIGNATURE;
        }

      return ERR_INVALID_INPUT_NO_LIST;
    }

  all_valid_certs = get_certs_from_list (certificate_list, list_len);

  if (!all_valid_certs)
    {
      /* Impossible */
      return -1;
    }


#ifdef WIN32
  /* For uninstall we are done now */
  if (do_uninstall)
    {
      return write_stores_win (NULL, all_valid_certs);
    }
#endif

  ret = read_instructions_file (instruction_file_name, &to_install,
                                &to_remove);

  if (ret)
    {
      return ret;
    }

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

  /* Check that the instructions are ok to execute */
  if (to_install)
    {
      ret = validate_instructions (all_valid_certs, to_install);
      if (ret)
        {
          return ret;
        }
    }

  if (to_remove)
    {
      ret = validate_instructions (all_valid_certs, to_remove);
      if (ret)
        {
          return ret;
        }
    }

#ifdef WIN32
  return write_stores_win (to_install, to_remove);
#endif

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

  return 0;
}

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