view cinst/certificate-installer.c @ 1395:a2574a029322

Fix Base 64 signature size calculation. If the signature byte size is not equally dividable by three the base 64 encoding needs three additional bytes. The value is now fixed to avoid such errors in the future.
author Andre Heinecke <andre.heinecke@intevation.de>
date Mon, 26 Jan 2015 13:17:32 +0100
parents 12ed0b72e9f5
children
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 IS_TAG_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;
}

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