diff cinst/certificate-installer.c @ 1176:c8f698ca6355

(issue128) Rename cinst to trustbridge-certificate-installer
author Andre Heinecke <andre.heinecke@intevation.de>
date Mon, 22 Sep 2014 11:34:06 +0200
parents cinst/main.c@2a1206932f53
children 12ed0b72e9f5
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cinst/certificate-installer.c	Mon Sep 22 11:34:06 2014 +0200
@@ -0,0 +1,357 @@
+/* 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 DO_RELEASE_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/