view cinst/main.c @ 242:809eaca3898c

Added helper functions to handle lists of NSS SECItems.
author Sascha Wilde <wilde@intevation.de>
date Fri, 28 Mar 2014 18:37:59 +0100
parents 4a20bab3f806
children 4de97f74d038
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/