view cinst/main.c @ 214:aab742690bee

Fix check for selected items and wait for bytes written. According to the documentation closing the write channel should suffice. But in testing it did not sent over everything.
author Andre Heinecke <andre.heinecke@intevation.de>
date Wed, 26 Mar 2014 17:17:19 +0100
parents 8cfcd38a9bb3
children 4a20bab3f806
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.
 *
 *  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 <stdbool.h>

#include "strhelp.h"
#include "listutil.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 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.
 *
 * Uninstall certificates are all certificates that are pa
 *
 * @param[out] certificate_list the parsed certificate list
 * @param[out] to_install strv of installation instructions or NULL
 * @param[out] to_remove strv of remove instructions or NULL
 * @param[out] all_certs strv of uninstallation instructions or NULL
 *
 * @returns: 0 on success. An error code otherwise.
 */
int
readInput (char **certificate_list, char ***to_install,
           char ***to_remove, char ***all_certs)
{
  int lines_read = 0;
  int readingList = 0;
  size_t list_size = 0;
  char buf[MAX_LINE_LENGTH + 2];

  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 <= 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[len - 2] != '\r')
        {
          if (buf[len - 1] != '\n')
            {
              printf ("Line too long.\n");
              return ERR_INVALID_INPUT;
            }
          buf[len - 1] = '\r';
          buf[len] = '\n';
          buf[len + 1] = '\0';
          len++;
        }

      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);
        }
      else if (strcmp ("UNINSTALL\r\n", buf) == 0)
        {
          /* Remove trailing \r\n */
          strv_append (to_remove, buf, len - 2);
          continue;
        }
      if (*buf == 'I')
        {
          /* Remove leading I: and trailing \r\n */
          strv_append (readingList ? all_certs : to_install,
                       buf + 2, len - 4);
          continue;
        }
      if (*buf == 'R')
        {
          /* Remove leading R: and trailing \r\n */
          strv_append (readingList ? all_certs : to_remove, buf + 2, len - 4);
          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 ()
{
  char **to_install = NULL;
  char **to_remove = NULL;
  char **all_certs = NULL;
  char *certificate_list = NULL;
  size_t list_len = 0;
  int ret = -1;
  bool uninstall = false;

  ret = readInput (&certificate_list, &to_install, &to_remove, &all_certs);

  if (ret)
    {
      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)
    {
      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 */
  if (to_install)
    {
      ret = validate_instructions (all_certs, to_install);
      if (ret)
        {
          return ret;
        }
    }

  if (to_remove)
    {
      if (to_remove[0]
          && strncmp ("UNINSTALL", to_remove[0], MAX_LINE_LENGTH) == 0)
        {
          uninstall = true;
          strv_free (to_remove);
          to_remove = NULL;
        }
      else
        {
          ret = validate_instructions (all_certs, to_remove);
          if (ret)
            {
              return ret;
            }
        }
    }

  if (uninstall)
    {
      /* To uninstall does not have to be verified as it part of the
       * signed list.*/
      to_remove = all_certs;
    }
  else
    {
      strv_free (all_certs);
      all_certs = NULL;
    }

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

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

  return 0;
}

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