diff options
Diffstat (limited to '')
| -rw-r--r-- | assuan/assuan-pipe-connect.c | 577 | 
1 files changed, 444 insertions, 133 deletions
diff --git a/assuan/assuan-pipe-connect.c b/assuan/assuan-pipe-connect.c index f79884cf..4ce3862a 100644 --- a/assuan/assuan-pipe-connect.c +++ b/assuan/assuan-pipe-connect.c @@ -30,7 +30,11 @@  #include <errno.h>  #include <fcntl.h>  #include <sys/types.h> +#ifndef HAVE_W32_SYSTEM  #include <sys/wait.h> +#else +#include <windows.h> +#endif  #include "assuan-defs.h" @@ -40,11 +44,47 @@  #define MAX_OPEN_FDS 20  #endif -#define LOG(format, args...) \ -	fprintf (assuan_get_assuan_log_stream (), \ -	         assuan_get_assuan_log_prefix (), \ -	         "%s" format , ## args) +#ifdef HAVE_W32_SYSTEM +/* We assume that a HANDLE can be represented by an int which should +   be true for all i386 systems (HANDLE is defined as void *) and +   these are the only systems for which Windows is available.  Further +   we assume that -1 denotes an invalid handle.  */ +#define fd_to_handle(a)  ((HANDLE)(a)) +#define handle_to_fd(a)  ((int)(a)) +#define pid_to_handle(a) ((HANDLE)(a)) +#define handle_to_pid(a) ((int)(a)) +#endif /*HAVE_W32_SYSTEM*/ + + +/* This should be called to make sure that SIGPIPE gets ignored.  */ +static void +fix_signals (void) +{ +#ifndef _ASSUAN_NO_FIXED_SIGNALS +#ifndef HAVE_DOSISH_SYSTEM  /* No SIGPIPE for these systems.  */ +  static int fixed_signals; + +  if (!fixed_signals) +    {  +      struct sigaction act; +         +      sigaction (SIGPIPE, NULL, &act); +      if (act.sa_handler == SIG_DFL) +	{ +	  act.sa_handler = SIG_IGN; +	  sigemptyset (&act.sa_mask); +	  act.sa_flags = 0; +	  sigaction (SIGPIPE, &act, NULL); +        } +      fixed_signals = 1; +      /* FIXME: This is not MT safe */ +    } +#endif /*HAVE_DOSISH_SYSTEM*/ +#endif /*!_ASSUAN_NO_FIXED_SIGNALS*/ +} + +#ifndef HAVE_W32_SYSTEM  static int  writen (int fd, const char *buffer, size_t length)  { @@ -63,75 +103,328 @@ writen (int fd, const char *buffer, size_t length)      }    return 0;  /* okay */  } - +#endif  static int -do_finish (ASSUAN_CONTEXT ctx) +do_finish (assuan_context_t ctx)  {    if (ctx->inbound.fd != -1)      { -      close (ctx->inbound.fd); +      _assuan_close (ctx->inbound.fd);        ctx->inbound.fd = -1;      }    if (ctx->outbound.fd != -1)      { -      close (ctx->outbound.fd); +      _assuan_close (ctx->outbound.fd);        ctx->outbound.fd = -1;      } -  if (ctx->pid != -1) +  if (ctx->pid != -1 && ctx->pid)      { -#if 0 -      /* This is already done by the double fork.  */ -      waitpid (ctx->pid, NULL, 0);  /* FIXME Check return value.  */ +#ifndef HAVE_W32_SYSTEM +#ifndef _ASSUAN_USE_DOUBLE_FORK +      if (!ctx->flags.no_waitpid) +        waitpid (ctx->pid, NULL, 0);         ctx->pid = -1;  #endif +#endif /*!HAVE_W32_SYSTEM*/      }    return 0;  }  static void -do_deinit (ASSUAN_CONTEXT ctx) +do_deinit (assuan_context_t ctx)  {    do_finish (ctx);  } +#ifdef HAVE_W32_SYSTEM +/* Build a command line for use with W32's CreateProcess.  On success +   CMDLINE gets the address of a newly allocated string.  */ +static int +build_w32_commandline (char * const *argv, char **cmdline) +{ +  int i, n; +  const char *s; +  char *buf, *p; + +  *cmdline = NULL; +  n = 0; +  for (i=0; (s=argv[i]); i++) +    { +      n += strlen (s) + 1 + 2;  /* (1 space, 2 quoting */ +      for (; *s; s++) +        if (*s == '\"') +          n++;  /* Need to double inner quotes.  */ +    } +  n++; + +  buf = p = xtrymalloc (n); +  if (!buf) +    return -1; + +  for (i=0; argv[i]; i++)  +    { +      if (i) +        p = stpcpy (p, " "); +      if (!*argv[i]) /* Empty string. */ +        p = stpcpy (p, "\"\""); +      else if (strpbrk (argv[i], " \t\n\v\f\"")) +        { +          p = stpcpy (p, "\""); +          for (s=argv[i]; *s; s++) +            { +              *p++ = *s; +              if (*s == '\"') +                *p++ = *s; +            } +          *p++ = '\"'; +          *p = 0; +        } +      else +        p = stpcpy (p, argv[i]); +    } + +  *cmdline= buf; +  return 0; +} +#endif /*HAVE_W32_SYSTEM*/ + + +#ifdef HAVE_W32_SYSTEM +/* Create pipe where one end end is inheritable.  */ +static int +create_inheritable_pipe (int filedes[2], int for_write) +{ +  HANDLE r, w, h; +  SECURITY_ATTRIBUTES sec_attr; + +  memset (&sec_attr, 0, sizeof sec_attr ); +  sec_attr.nLength = sizeof sec_attr; +  sec_attr.bInheritHandle = FALSE; +     +  if (!CreatePipe (&r, &w, &sec_attr, 0)) +    { +      _assuan_log_printf ("CreatePipe failed: %s\n", w32_strerror (-1)); +      return -1; +    } + +  if (!DuplicateHandle (GetCurrentProcess(), for_write? r : w, +                        GetCurrentProcess(), &h, 0, +                        TRUE, DUPLICATE_SAME_ACCESS )) +    { +      _assuan_log_printf ("DuplicateHandle failed: %s\n", w32_strerror (-1)); +      CloseHandle (r); +      CloseHandle (w); +      return -1; +    } +  if (for_write) +    { +      CloseHandle (r); +      r = h; +    } +  else +    { +      CloseHandle (w); +      w = h; +    } + +  filedes[0] = handle_to_fd (r); +  filedes[1] = handle_to_fd (w); +  return 0; +} +#endif /*HAVE_W32_SYSTEM*/ +  /* Connect to a server over a pipe, creating the assuan context and     returning it in CTX.  The server filename is NAME, the argument     vector in ARGV.  FD_CHILD_LIST is a -1 terminated list of file -   descriptors not to close in the child.  */ -AssuanError -assuan_pipe_connect (ASSUAN_CONTEXT *ctx, const char *name, char *const argv[], -		     int *fd_child_list) +   descriptors not to close in the child.  ATFORK is called in the +   child right after the fork; ATFORKVALUE is passed as the first +   argument and 0 is passed as the second argument. The ATFORK +   function should only act if the second value is 0. */ +assuan_error_t +assuan_pipe_connect2 (assuan_context_t *ctx, +                      const char *name, char *const argv[], +                      int *fd_child_list, +                      void (*atfork) (void *opaque, int reserved), +                      void *atforkvalue)  { -#ifndef _ASSUAN_IN_GPGME -  static int fixed_signals = 0; -#endif -  AssuanError err; +#ifdef HAVE_W32_SYSTEM +  assuan_error_t err;    int rp[2];    int wp[2]; +  char mypidstr[50]; +  char *cmdline; +  SECURITY_ATTRIBUTES sec_attr; +  PROCESS_INFORMATION pi =  +    { +      NULL,      /* Returns process handle.  */ +      0,         /* Returns primary thread handle.  */ +      0,         /* Returns pid.  */ +      0          /* Returns tid.  */ +    }; +  STARTUPINFO si; +  int fd, *fdp; +  HANDLE nullfd = INVALID_HANDLE_VALUE;    if (!ctx || !name || !argv || !argv[0])      return ASSUAN_Invalid_Value; -#ifndef _ASSUAN_IN_GPGME -  if (!fixed_signals) -    {  -      struct sigaction act; -         -      sigaction (SIGPIPE, NULL, &act); -      if (act.sa_handler == SIG_DFL) -	{ -	  act.sa_handler = SIG_IGN; -	  sigemptyset (&act.sa_mask); -	  act.sa_flags = 0; -	  sigaction (SIGPIPE, &act, NULL); +  fix_signals (); + +  sprintf (mypidstr, "%lu", (unsigned long)getpid ()); + +  /* Build the command line.  */ +  if (build_w32_commandline (argv, &cmdline)) +    return ASSUAN_Out_Of_Core; + +  /* Create thew two pipes. */ +  if (create_inheritable_pipe (rp, 0)) +    { +      xfree (cmdline); +      return ASSUAN_General_Error; +    } +   +  if (create_inheritable_pipe (wp, 1)) +    { +      CloseHandle (fd_to_handle (rp[0])); +      CloseHandle (fd_to_handle (rp[1])); +      xfree (cmdline); +      return ASSUAN_General_Error; +    } + +   +  err = _assuan_new_context (ctx); +  if (err) +    { +      CloseHandle (fd_to_handle (rp[0])); +      CloseHandle (fd_to_handle (rp[1])); +      CloseHandle (fd_to_handle (wp[0])); +      CloseHandle (fd_to_handle (wp[1])); +      xfree (cmdline); +      return ASSUAN_General_Error; +    } + +  (*ctx)->pipe_mode = 1; +  (*ctx)->inbound.fd  = rp[0];  /* Our inbound is read end of read pipe. */ +  (*ctx)->outbound.fd = wp[1];  /* Our outbound is write end of write pipe. */ +  (*ctx)->deinit_handler = do_deinit; +  (*ctx)->finish_handler = do_finish; + + +  /* fixme: Actually we should set the "_assuan_pipe_connect_pid" env +     variable.  However this requires us to write a full environment +     handler, because the strings are expected in sorted order.  The +     suggestion given in the MS Reference Library, to save the old +     value, changeit, create proces and restore it, is not thread +     safe.  */ + +  /* Start the process.  */ +  memset (&sec_attr, 0, sizeof sec_attr ); +  sec_attr.nLength = sizeof sec_attr; +  sec_attr.bInheritHandle = FALSE; +   +  memset (&si, 0, sizeof si); +  si.cb = sizeof (si); +  si.dwFlags = STARTF_USESTDHANDLES; +  si.hStdInput  = fd_to_handle (wp[0]); +  si.hStdOutput = fd_to_handle (rp[1]); + +  /* Dup stderr to /dev/null unless it is in the list of FDs to be +     passed to the child. */ +  fd = fileno (stderr); +  fdp = fd_child_list; +  if (fdp) +    { +      for (; *fdp != -1 && *fdp != fd; fdp++) +        ; +    } +  if (!fdp || *fdp == -1) +    { +      nullfd = CreateFile ("nul", GENERIC_WRITE, +                           FILE_SHARE_READ | FILE_SHARE_WRITE, +                           NULL, OPEN_EXISTING, 0, NULL); +      if (nullfd == INVALID_HANDLE_VALUE) +        { +          _assuan_log_printf ("can't open `nul': %s\n", w32_strerror (-1)); +          CloseHandle (fd_to_handle (rp[0])); +          CloseHandle (fd_to_handle (rp[1])); +          CloseHandle (fd_to_handle (wp[0])); +          CloseHandle (fd_to_handle (wp[1])); +          xfree (cmdline); +          _assuan_release_context (*ctx);  +          return -1;          } -      fixed_signals = 1; -      /* FIXME: This is not MT safe */ +      si.hStdError = nullfd;      } -#endif +  else +    si.hStdError = fd_to_handle (_get_osfhandle (fd)); + + +  /* Note: We inherit all handles flagged as inheritable.  This seems +     to be a security flaw but there seems to be no way of selecting +     handles to inherit. */ +  /*   _assuan_log_printf ("CreateProcess, path=`%s' cmdline=`%s'\n", */ +  /*                       name, cmdline); */ +  if (!CreateProcess (name,                 /* Program to start.  */ +                      cmdline,              /* Command line arguments.  */ +                      &sec_attr,            /* Process security attributes.  */ +                      &sec_attr,            /* Thread security attributes.  */ +                      TRUE,                 /* Inherit handles.  */ +                      (CREATE_DEFAULT_ERROR_MODE +                       | GetPriorityClass (GetCurrentProcess ()) +                       | CREATE_SUSPENDED), /* Creation flags.  */ +                      NULL,                 /* Environment.  */ +                      NULL,                 /* Use current drive/directory.  */ +                      &si,                  /* Startup information. */ +                      &pi                   /* Returns process information.  */ +                      )) +    { +      _assuan_log_printf ("CreateProcess failed: %s\n", w32_strerror (-1)); +      CloseHandle (fd_to_handle (rp[0])); +      CloseHandle (fd_to_handle (rp[1])); +      CloseHandle (fd_to_handle (wp[0])); +      CloseHandle (fd_to_handle (wp[1])); +      if (nullfd != INVALID_HANDLE_VALUE) +        CloseHandle (nullfd); +      xfree (cmdline); +      _assuan_release_context (*ctx);  +      return ASSUAN_General_Error; +    } +  xfree (cmdline); +  cmdline = NULL; +  if (nullfd != INVALID_HANDLE_VALUE) +    { +      CloseHandle (nullfd); +      nullfd = INVALID_HANDLE_VALUE; +    } + +  CloseHandle (fd_to_handle (rp[1])); +  CloseHandle (fd_to_handle (wp[0])); + +  /*   _assuan_log_printf ("CreateProcess ready: hProcess=%p hThread=%p" */ +  /*                       " dwProcessID=%d dwThreadId=%d\n", */ +  /*                       pi.hProcess, pi.hThread, */ +  /*                       (int) pi.dwProcessId, (int) pi.dwThreadId); */ + +  ResumeThread (pi.hThread); +  CloseHandle (pi.hThread);  +  (*ctx)->pid = 0;  /* We don't use the PID. */ +  CloseHandle (pi.hProcess); /* We don't need to wait for the process. */ + +#else /*!HAVE_W32_SYSTEM*/ +  assuan_error_t err; +  int rp[2]; +  int wp[2]; +  char mypidstr[50]; + +  if (!ctx || !name || !argv || !argv[0]) +    return ASSUAN_Invalid_Value; + +  fix_signals (); + +  sprintf (mypidstr, "%lu", (unsigned long)getpid ());    if (pipe (rp) < 0)      return ASSUAN_General_Error; @@ -158,8 +451,8 @@ assuan_pipe_connect (ASSUAN_CONTEXT *ctx, const char *name, char *const argv[],    (*ctx)->deinit_handler = do_deinit;    (*ctx)->finish_handler = do_finish; -  /* FIXME: Use _gpgme_io_spawn.  The PID stored here is actually -     soon useless.  */ +  /* FIXME: For GPGME we should better use _gpgme_io_spawn.  The PID +     stored here is actually soon useless.  */    (*ctx)->pid = fork ();    if ((*ctx)->pid < 0)      { @@ -173,111 +466,131 @@ assuan_pipe_connect (ASSUAN_CONTEXT *ctx, const char *name, char *const argv[],    if ((*ctx)->pid == 0)      { -      /* Intermediate child to prevent zombie processes.  */ -      pid_t pid; - +#ifdef _ASSUAN_USE_DOUBLE_FORK              if ((pid = fork ()) == 0) +#endif  	{ -	  /* Child.  */ - -	  int i, n; -	  char errbuf[512]; -	  int *fdp; - -	  /* Dup handles to stdin/stdout. */ -	  if (rp[1] != STDOUT_FILENO) -	    { -	      if (dup2 (rp[1], STDOUT_FILENO) == -1) -		{ -		  LOG ("dup2 failed in child: %s\n", strerror (errno)); -		  _exit (4); -		} -	    } -	  if (wp[0] != STDIN_FILENO) -	    { -	      if (dup2 (wp[0], STDIN_FILENO) == -1) -		{ -		  LOG ("dup2 failed in child: %s\n", strerror (errno)); -		  _exit (4); -		} -	    } -	   -	  /* Dup stderr to /dev/null unless it is in the list of FDs to be -	     passed to the child. */ -	  fdp = fd_child_list; -	  if (fdp) -	    { -	      for (; *fdp != -1 && *fdp != STDERR_FILENO; fdp++) -		; -	    } -	  if (!fdp || *fdp == -1) -	    { -	      int fd = open ("/dev/null", O_WRONLY); -	      if (fd == -1) -		{ -		  LOG ("can't open `/dev/null': %s\n", strerror (errno)); -		  _exit (4); -		} -	      if (dup2 (fd, STDERR_FILENO) == -1) -		{ -		  LOG ("dup2(dev/null, 2) failed: %s\n", strerror (errno)); -		  _exit (4); -		} -	    } - - -	  /* Close all files which will not be duped and are not in the -	     fd_child_list. */ -	  n = sysconf (_SC_OPEN_MAX); -	  if (n < 0) -	    n = MAX_OPEN_FDS; -	  for (i=0; i < n; i++) -	    { -	      if ( i == STDIN_FILENO || i == STDOUT_FILENO || i == STDERR_FILENO) -		continue; -	      fdp = fd_child_list; -	      if (fdp) -		{ -		  while (*fdp != -1 && *fdp != i) -		    fdp++; -		} -	       -	      if (!(fdp && *fdp != -1)) -		close(i); -	    } - -	  errno = 0; - -	  execv (name, argv);  -	  /* oops - use the pipe to tell the parent about it */ -	  snprintf (errbuf, sizeof(errbuf)-1, "ERR %d can't exec `%s': %.50s\n", -		    ASSUAN_Problem_Starting_Server, name, strerror (errno)); -	  errbuf[sizeof(errbuf)-1] = 0; -	  writen (1, errbuf, strlen (errbuf)); -	  _exit (4); -	} /* End child.  */ +          int i, n; +          char errbuf[512]; +          int *fdp; +           +          if (atfork) +            atfork (atforkvalue, 0); + +          /* Dup handles to stdin/stdout. */ +          if (rp[1] != STDOUT_FILENO) +            { +              if (dup2 (rp[1], STDOUT_FILENO) == -1) +                { +                  _assuan_log_printf ("dup2 failed in child: %s\n", +                                      strerror (errno)); +                  _exit (4); +                } +            } +          if (wp[0] != STDIN_FILENO) +            { +              if (dup2 (wp[0], STDIN_FILENO) == -1) +                { +                  _assuan_log_printf ("dup2 failed in child: %s\n", +                                      strerror (errno)); +                  _exit (4); +                } +            } + +          /* Dup stderr to /dev/null unless it is in the list of FDs to be +             passed to the child. */ +          fdp = fd_child_list; +          if (fdp) +            { +              for (; *fdp != -1 && *fdp != STDERR_FILENO; fdp++) +                ; +            } +          if (!fdp || *fdp == -1) +            { +              int fd = open ("/dev/null", O_WRONLY); +              if (fd == -1) +                { +                  _assuan_log_printf ("can't open `/dev/null': %s\n", +                                      strerror (errno)); +                  _exit (4); +                } +              if (dup2 (fd, STDERR_FILENO) == -1) +                { +                  _assuan_log_printf ("dup2(dev/null, 2) failed: %s\n", +                                      strerror (errno)); +                  _exit (4); +                } +            } + + +          /* Close all files which will not be duped and are not in the +             fd_child_list. */ +          n = sysconf (_SC_OPEN_MAX); +          if (n < 0) +            n = MAX_OPEN_FDS; +          for (i=0; i < n; i++) +            { +              if ( i == STDIN_FILENO || i == STDOUT_FILENO +                   || i == STDERR_FILENO) +                continue; +              fdp = fd_child_list; +              if (fdp) +                { +                  while (*fdp != -1 && *fdp != i) +                    fdp++; +                } + +              if (!(fdp && *fdp != -1)) +                close(i); +            } +          errno = 0; + +          /* We store our parents pid in the environment so that the +             execed assuan server is able to read the actual pid of the +             client.  The server can't use getppid becuase it might have +             been double forked before the assuan server has been +             initialized. */ +          setenv ("_assuan_pipe_connect_pid", mypidstr, 1); + +          execv (name, argv);  +          /* oops - use the pipe to tell the parent about it */ +          snprintf (errbuf, sizeof(errbuf)-1, +                    "ERR %d can't exec `%s': %.50s\n", +                    ASSUAN_Problem_Starting_Server, name, strerror (errno)); +          errbuf[sizeof(errbuf)-1] = 0; +          writen (1, errbuf, strlen (errbuf)); +          _exit (4); +        } +#ifdef _ASSUAN_USE_DOUBLE_FORK        if (pid == -1)  	_exit (1);        else  	_exit (0); +#endif      } +#ifdef _ASSUAN_USE_DOUBLE_FORK    waitpid ((*ctx)->pid, NULL, 0);    (*ctx)->pid = -1; +#endif    close (rp[1]);    close (wp[0]); +#endif /*!HAVE_W32_SYSTEM*/ +    /* initial handshake */    {      int okay, off;      err = _assuan_read_from_server (*ctx, &okay, &off);      if (err) -      LOG ("can't connect server: %s\n", assuan_strerror (err)); +      _assuan_log_printf ("can't connect server: %s\n", +                          assuan_strerror (err));      else if (okay != 1)        { -	LOG ("can't connect server: `%s'\n", (*ctx)->inbound.line); +	_assuan_log_printf ("can't connect server: `%s'\n", +                            (*ctx)->inbound.line);  	err = ASSUAN_Connect_Failed;        }    } @@ -292,15 +605,13 @@ assuan_pipe_connect (ASSUAN_CONTEXT *ctx, const char *name, char *const argv[],  } - - - - - - - - - - - - +/* Connect to a server over a pipe, creating the assuan context and +   returning it in CTX.  The server filename is NAME, the argument +   vector in ARGV.  FD_CHILD_LIST is a -1 terminated list of file +   descriptors not to close in the child.  */ +assuan_error_t +assuan_pipe_connect (assuan_context_t *ctx, const char *name, char *const argv[], +		     int *fd_child_list) +{ +  return assuan_pipe_connect2 (ctx, name, argv, fd_child_list, NULL, NULL); +}  | 
