view cinst/nssstore_win.c @ 328:18b31e2498a3

Build / dependency notes for nss
author Andre Heinecke <aheinecke@intevation.de>
date Tue, 08 Apr 2014 14:50:31 +0000
parents eff8e7ce4dae
children b1059360a0c7
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 "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) { \
        DEBUGMSG(msg " :"); \
        DEBUGMSG(my_error); \
        ERRORPRINTF(msg" : %s\n", my_error); \
        free (my_error); \
      } \
  DEBUGMSG ("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] instructions instructions to write
* @param [in] write_handle to write to
*
* @returns true on success, false on failure
*/
static bool
write_instructions(char **instructions, HANDLE write_handle)
{
  bool retval = false;
  int i = 0;
  const char *line_end = "\n\0";

  if (!instructions)
    {
      return true;
    }

  for (i = 0; instructions[i]; i++)
    {
      DWORD written = 0;
      DWORD inst_len = strlen (instructions[i]);
      retval = WriteFile (write_handle, (LPCVOID) instructions[i], inst_len, &written, NULL);
      if (!retval)
        {
          PRINTLASTERROR ("Failed to write\n");
          return false;
        }
      if (inst_len != written)
        {
          ERRORPRINTF ("Failed to write everything\n");
          retval = false;
          return false;
        }
      written = 0;
      retval = WriteFile (write_handle, (LPCVOID) line_end, 2, &written, NULL);
      if (!retval)
        {
          PRINTLASTERROR ("Failed to write line end\n");
          return false;
        }
      if (inst_len != written)
        {
          ERRORPRINTF ("Failed to write full line end\n");
          retval = false;
          return false;
        }
    }
  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_w;
  siStartInfo.dwFlags = STARTF_USESTDHANDLES;

  success = CreateProcessAsUserW (hToken,
                                  lpApplicationName,
                                  NULL, /* Commandline */
                                  NULL, /* Process attributes. Take hToken */
                                  NULL, /* Thread attribues. Take hToken */
                                  TRUE, /* Inherit Handles */
                                  0, /* Creation flags. Use default */
                                  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))
    {
      ERRORPRINTF ("Failed to write install instructions.\n");
      goto closepipes;
    }
  if (!write_instructions (to_remove, h_stdin_child_w))
    {
      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 (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/