view cinst/nssstore_win.c @ 330:1e6d1eab8395

Fix NSS unit test for Windows and change how instructions are written This was supposed to fix the block on error. But it did not.
author Andre Heinecke <aheinecke@intevation.de>
date Tue, 08 Apr 2014 15:08:57 +0000
parents b1059360a0c7
children c0eac5c8c245
line wrap: on
line source
#ifdef WIN32

/* @file
   @brief Windows implementation of nssstore process control.
*/

#include <windows.h>
#include <stdio.h>
#include <strsafe.h>
#include <stdbool.h>
#include <userenv.h>
#include <io.h>

#include "logging.h"
#include "util.h"

#define PROCESS_TIMEOUT 30000 /* In milliseconds */

#define BUFSIZE 4096 /* used for reading childs stdout */

#define PRINTLASTERROR(msg) \
  char *my_error = getLastErrorMsg(); \
      if (my_error) { \
        DEBUGPRINTF(msg " : %s\n", my_error); \
        ERRORPRINTF(msg" : %s\n", my_error); \
        free (my_error); \
      } \
  DEBUGPRINTF ("Failed to get error information\n");

/**@brief Write strv of instructions to a handle
*
* Writes the null terminated list of instructions to
* the handle.
*
* @param [in] base64 encoded der certificates to write
* @param [in] write_handle to write to
* @param [in] remove weather the certificate should be installed or removed
*
* @returns true on success, false on failure
*/
static bool
write_instructions(char **certificates, HANDLE write_handle,
                   bool remove)
{
  int i = 0;
  int cHandle = -1;
  FILE *write_stream = NULL;

  if (!certificates)
    {
      return true;
    }

  cHandle = _open_osfhandle ((intptr_t)write_handle, 0);

  if (cHandle == -1)
    {
      ERRORPRINTF ("Failed to open write handle.\n");
    }

  write_stream = _fdopen(cHandle, "w");
  for (i = 0; certificates[i]; i++)
    {
      int ret = 0;
      DEBUGPRINTF("Writing \n");
      if (remove)
        ret = fprintf (write_stream, "R:%s\n", certificates[i]);
      else
        ret = fprintf (write_stream, "I:%s\n", certificates[i]);

      if (ret <= 0)
        {
          DEBUGPRINTF ("Failed to write everything.\n");
          break;
        }
    }

  DEBUGPRINTF("Write done\n");
  return true;
}

/**@brief Start the process to install / remove
*
* Creates a child process with the Security handle specified in hToken
* sends the instructions and then waits for the process to finish.
*
* If the process is not done in PROCESS_TIMEOUT seconds this assumes an
* unknown error happened.
*
* @param [in] to_install strv of DER encoded certificates to be added.
* @param [in] to_remove strv of DER encoded certificates to be remvoed.
* @param [in] hToken handle to the primary token that is used to install
* certificates.
*
* @returns true on success, false on error.
*/
static bool
start_procces_for_user (char **to_install, char **to_remove,
                        HANDLE hToken)
{
  HANDLE h_stdin_child_r = NULL,
         h_stdin_child_w = NULL,
         h_stdout_child_r = NULL,
         h_stdout_child_w = NULL;
  SECURITY_ATTRIBUTES saAttr = {0};
  /* TODO get this as absolute path based on current module location */
  LPWSTR lpApplicationName = L"mozilla.exe";
  PROCESS_INFORMATION piProcInfo = {0};
  STARTUPINFOW siStartInfo = {0};
  LPVOID lpEnvironment = NULL;
  BOOL success = FALSE;
  int retval = -1;

  saAttr.nLength = sizeof (SECURITY_ATTRIBUTES);
  saAttr.bInheritHandle = TRUE;
  saAttr.lpSecurityDescriptor = NULL; /* Use default */

  /* Create a pipe for the child process's STDIN. */
  if (!CreatePipe (&h_stdin_child_r, &h_stdin_child_w, &saAttr, 0))
    {
      PRINTLASTERROR ("Create pipe failed.\n");
      return -1;
    }

  /* Create a pipe for the child process's STDOUT. */
  if (!CreatePipe (&h_stdout_child_r, &h_stdout_child_w, &saAttr, 0))
    {
      PRINTLASTERROR ("Create pipe failed.\n");
      return -1;
    }

  /* Ensure that read/write is properly inherited */
  if (!SetHandleInformation (h_stdin_child_w, HANDLE_FLAG_INHERIT, 0) ||
      !SetHandleInformation (h_stdout_child_r, HANDLE_FLAG_INHERIT, 0))
    {
      PRINTLASTERROR ("SetHandleInformation failed.\n");
      goto closepipes;
    }

  /* Create the environment for the user */
  if (!CreateEnvironmentBlock (&lpEnvironment, hToken, FALSE))
    {
      PRINTLASTERROR ("Failed to create the environment.\n");
      goto closepipes;
    }

  /* set up handles. stdin and stdout go to the same stdout*/
  siStartInfo.cb = sizeof (STARTUPINFO);
  siStartInfo.hStdError = h_stdout_child_w;
  siStartInfo.hStdOutput = h_stdout_child_w;
  siStartInfo.hStdInput = h_stdin_child_r;
  siStartInfo.dwFlags = STARTF_USESTDHANDLES;

  success = CreateProcessAsUserW (hToken,
                                  lpApplicationName,
                                  NULL, /* Commandline */
                                  NULL, /* Process attributes. Take hToken */
                                  NULL, /* Thread attribues. Take hToken */
                                  TRUE, /* Inherit Handles */
                                  CREATE_UNICODE_ENVIRONMENT, /* Creation flags. */
                                  lpEnvironment,
                                  NULL, /* Current working directory */
                                  &siStartInfo,
                                  &piProcInfo);
  if (!success)
    {
      PRINTLASTERROR ("Failed to create process.\n");
      goto closepipes;
    }

  if (!write_instructions (to_install, h_stdin_child_w, false))
    {
      ERRORPRINTF ("Failed to write install instructions.\n");
      goto closepipes;
    }
  if (!write_instructions (to_remove, h_stdin_child_w, true))
    {
      ERRORPRINTF ("Failed to write remove instructions.\n");
      goto closepipes;
    }

  /* Close the Handle so that the child knows we are finished
     telling it what to do */
  CloseHandle (h_stdin_child_w);
  h_stdin_child_w = NULL;

#ifndef RELEASE_BUILD
  /* print childs stdout / stderr to parents stdout */
    {
      HANDLE stdout_handle = GetStdHandle(STD_OUTPUT_HANDLE);
      DWORD read_bytes = 0;
      DWORD written = 0;
      bool success;
      char buf[BUFSIZE];

      if (!stdout_handle || stdout_handle == INVALID_HANDLE_VALUE)
        {
          /* Should not happen */
          ERRORPRINTF("Failed to get stdout handle.\n");
          goto closeprocess;
        }

      for (;;)
        {
          success = ReadFile (h_stdout_child_r, buf, BUFSIZE, &read_bytes, NULL);
          if(!success || read_bytes == 0)
            break;

          success = WriteFile (stdout_handle, buf,
                               read_bytes, &written, NULL);
          if (!success || written != read_bytes)
            break;
        }
    }
#endif

  if (WaitForSingleObject (piProcInfo.hProcess, PROCESS_TIMEOUT) != WAIT_OBJECT_0)
    {
      /* Should not happen... */
      ERRORPRINTF ("Failed to wait for process.\n");
    }

closeprocess:
  if (piProcInfo.hProcess)
    CloseHandle (piProcInfo.hProcess);
  if (piProcInfo.hThread)
    CloseHandle (piProcInfo.hThread);

closepipes:
  if (lpEnvironment)
    DestroyEnvironmentBlock (lpEnvironment);
  if (h_stdin_child_w)
    CloseHandle (h_stdin_child_w);
  if (h_stdin_child_r)
    CloseHandle (h_stdin_child_r);
  if (h_stdout_child_w)
    CloseHandle (h_stdout_child_w);
  if (h_stdout_child_r)
    CloseHandle (h_stdout_child_r);

  return retval;
}

int
write_stores_nss (char **to_install, char **to_remove)
{
  HANDLE hToken = NULL;
  OpenProcessToken (GetCurrentProcess(), TOKEN_QUERY, &hToken);

  /* TODO loop over all users */
  if (!start_procces_for_user (to_install, to_remove, hToken))
    {
      ERRORPRINTF ("Failed to run NSS installation process.\n");
      return -1;
    }
  return 0;
}

#endif

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