Exemple #1
0
/* Read the prefix of the keyblob and do some basic parsing.  On
   success returns an open estream file at R_FP and the length of the
   header at R_HEADERLEN.  */
static gpg_error_t
read_keyblob_prefix (const char *filename, estream_t *r_fp, size_t *r_headerlen)
{
  gpg_error_t err;
  estream_t fp;
  unsigned char packet[32];

  *r_fp = NULL;

  fp = es_fopen (filename, "rb");
  if (!fp)
    {
      err = gpg_error_from_syserror ();
      log_error ("error reading '%s': %s\n", filename, gpg_strerror (err));
      return err;
    }

  /* Read the header.  It is defined as 32 bytes thus we read it in one go.  */
  if (es_fread (packet, 32, 1, fp) != 1)
    {
      err = gpg_error_from_syserror ();
      log_error ("error reading the header of '%s': %s\n",
                 filename, gpg_strerror (err));
      es_fclose (fp);
      return err;
    }

  err = parse_header (filename, packet, 32, r_headerlen);
  if (err)
    es_fclose (fp);
  else
    *r_fp = fp;

  return err;
}
Exemple #2
0
static gpg_error_t
armor_data (char **r_string, const void *data, size_t datalen)
{
  gpg_error_t err;
  struct b64state b64state;
  estream_t fp;
  long length;
  char *buffer;
  size_t nread;

  *r_string = NULL;

  fp = es_fopenmem (0, "rw");
  if (!fp)
    return gpg_error_from_syserror ();

  if ((err=b64enc_start_es (&b64state, fp, "PGP PUBLIC KEY BLOCK"))
      || (err=b64enc_write (&b64state, data, datalen))
      || (err = b64enc_finish (&b64state)))
    {
      es_fclose (fp);
      return err;
    }

  /* FIXME: To avoid the extra buffer allocation estream should
     provide a function to snatch the internal allocated memory from
     such a memory stream.  */
  length = es_ftell (fp);
  if (length < 0)
    {
      err = gpg_error_from_syserror ();
      es_fclose (fp);
      return err;
    }

  buffer = xtrymalloc (length+1);
  if (!buffer)
    {
      err = gpg_error_from_syserror ();
      es_fclose (fp);
      return err;
    }

  es_rewind (fp);
  if (es_read (fp, buffer, length, &nread))
    {
      err = gpg_error_from_syserror ();
      es_fclose (fp);
      return err;
    }
  buffer[nread] = 0;
  es_fclose (fp);

  *r_string = buffer;
  return 0;
}
Exemple #3
0
/* Write an S-expression formatted key to our key storage.  With FORCE
   passed as true an existing key with the given GRIP will get
   overwritten.  */
int
agent_write_private_key (const unsigned char *grip,
                         const void *buffer, size_t length, int force)
{
  char *fname;
  estream_t fp;
  char hexgrip[40+4+1];

  bin2hex (grip, 20, hexgrip);
  strcpy (hexgrip+40, ".key");

  fname = make_filename (opt.homedir, GNUPG_PRIVATE_KEYS_DIR, hexgrip, NULL);

  /* FIXME: Write to a temp file first so that write failures during
     key updates won't lead to a key loss.  */

  if (!force && !access (fname, F_OK))
    {
      log_error ("secret key file '%s' already exists\n", fname);
      xfree (fname);
      return gpg_error (GPG_ERR_EEXIST);
    }

  fp = es_fopen (fname, force? "wb,mode=-rw" : "wbx,mode=-rw");
  if (!fp)
    {
      gpg_error_t tmperr = gpg_error_from_syserror ();
      log_error ("can't create '%s': %s\n", fname, gpg_strerror (tmperr));
      xfree (fname);
      return tmperr;
    }

  if (es_fwrite (buffer, length, 1, fp) != 1)
    {
      gpg_error_t tmperr = gpg_error_from_syserror ();
      log_error ("error writing '%s': %s\n", fname, gpg_strerror (tmperr));
      es_fclose (fp);
      gnupg_remove (fname);
      xfree (fname);
      return tmperr;
    }
  if (es_fclose (fp))
    {
      gpg_error_t tmperr = gpg_error_from_syserror ();
      log_error ("error closing '%s': %s\n", fname, gpg_strerror (tmperr));
      gnupg_remove (fname);
      xfree (fname);
      return tmperr;
    }
  bump_key_eventcounter ();
  xfree (fname);
  return 0;
}
Exemple #4
0
/* Retrieve keys from URL and write the result to the provided output
   stream OUTFP.  */
gpg_error_t
ks_action_fetch (ctrl_t ctrl, const char *url, estream_t outfp)
{
  gpg_error_t err = 0;
  estream_t infp;
  parsed_uri_t parsed_uri;  /* The broken down URI.  */

  if (!url)
    return gpg_error (GPG_ERR_INV_URI);

  err = http_parse_uri (&parsed_uri, url, 1);
  if (err)
    return err;

  if (parsed_uri->is_http)
    {
      err = ks_http_fetch (ctrl, url, &infp);
      if (!err)
        {
          err = copy_stream (infp, outfp);
          es_fclose (infp);
        }
    }
  else if (!parsed_uri->opaque)
    {
      err = gpg_error (GPG_ERR_INV_URI);
    }
  else if (!strcmp (parsed_uri->scheme, "finger"))
    {
      err = ks_finger_fetch (ctrl, parsed_uri, &infp);
      if (!err)
        {
          err = copy_stream (infp, outfp);
          es_fclose (infp);
        }
    }
  else if (!strcmp (parsed_uri->scheme, "kdns"))
    {
      err = ks_kdns_fetch (ctrl, parsed_uri, &infp);
      if (!err)
        {
          err = copy_stream (infp, outfp);
          es_fclose (infp);
        }
    }
  else
    err = gpg_error (GPG_ERR_INV_URI);

  http_release_parsed_uri (parsed_uri);
  return err;
}
Exemple #5
0
/* Set the status FD.  */
void
wks_set_status_fd (int fd)
{
  static int last_fd = -1;

  if (fd != -1 && last_fd == fd)
    return;

  if (statusfp && statusfp != es_stdout && statusfp != es_stderr)
    es_fclose (statusfp);
  statusfp = NULL;
  if (fd == -1)
    return;

  if (fd == 1)
    statusfp = es_stdout;
  else if (fd == 2)
    statusfp = es_stderr;
  else
    statusfp = es_fdopen (fd, "w");
  if (!statusfp)
    {
      log_fatal ("can't open fd %d for status output: %s\n",
                 fd, gpg_strerror (gpg_error_from_syserror ()));
    }
  last_fd = fd;
}
Exemple #6
0
/* Get the requested keys (matching PATTERNS) using all configured
   keyservers and write the result to the provided output stream.  */
gpg_error_t
ks_action_get (ctrl_t ctrl, strlist_t patterns, estream_t outfp)
{
  gpg_error_t err = 0;
  gpg_error_t first_err = 0;
  int any_server = 0;
  int any_data = 0;
  strlist_t sl;
  uri_item_t uri;
  estream_t infp;

  if (!patterns)
    return gpg_error (GPG_ERR_NO_USER_ID);

  /* FIXME: We only take care of the first keyserver.  To fully
     support multiple keyservers we need to track the result for each
     pattern and use the next keyserver if one key was not found.  The
     keyservers might not all be fully synced thus it is not clear
     whether the first keyserver has the freshest copy of the key.
     Need to think about a better strategy.  */
  for (uri = ctrl->keyservers; !err && uri; uri = uri->next)
    {
      if (uri->parsed_uri->is_http)
        {
          any_server = 1;
          for (sl = patterns; !err && sl; sl = sl->next)
            {
              err = ks_hkp_get (ctrl, uri->parsed_uri, sl->d, &infp);
              if (err)
                {
                  /* It is possible that a server does not carry a
                     key, thus we only save the error and continue
                     with the next pattern.  FIXME: It is an open
                     question how to return such an error condition to
                     the caller.  */
                  first_err = err;
                  err = 0;
                }
              else
                {
                  err = copy_stream (infp, outfp);
                  /* Reading from the keyserver should never fail, thus
                     return this error.  */
                  if (!err)
                    any_data = 1;
                  es_fclose (infp);
                  infp = NULL;
                }
            }
        }
      if (any_data)
        break; /* Stop loop after a keyserver returned something.  */
    }

  if (!any_server)
    err = gpg_error (GPG_ERR_NO_KEYSERVER);
  else if (!err && first_err && !any_data)
    err = first_err;
  return err;
}
Exemple #7
0
void
set_status_fd (int fd)
{
  static int last_fd = -1;

  if (fd != -1 && last_fd == fd)
    return;

  if (statusfp && statusfp != es_stdout && statusfp != es_stderr )
    es_fclose (statusfp);
  statusfp = NULL;
  if (fd == -1)
    return;

  if (fd == 1)
    statusfp = es_stdout;
  else if (fd == 2)
    statusfp = es_stderr;
  else
    statusfp = es_fdopen (fd, "w");
  if (!statusfp)
    {
      log_fatal ("can't open fd %d for status output: %s\n",
                 fd, strerror (errno));
    }
  last_fd = fd;

  gcry_set_progress_handler (progress_cb, NULL);
}
Exemple #8
0
static gpg_error_t
cmd_verify (assuan_context_t ctx, char *line)
{
  int rc;
  ctrl_t ctrl = assuan_get_pointer (ctx);
  int fd = translate_sys2libc_fd (assuan_get_input_fd (ctx), 0);
  int out_fd = translate_sys2libc_fd (assuan_get_output_fd (ctx), 1);
  estream_t out_fp = NULL;

  (void)line;

  if (fd == -1)
    return set_error (GPG_ERR_ASS_NO_INPUT, NULL);

  if (out_fd != -1)
    {
      out_fp = es_fdopen_nc (out_fd, "w");
      if (!out_fp)
        return set_error (gpg_err_code_from_syserror (), "fdopen() failed");
    }

  rc = start_audit_session (ctrl);
  if (!rc)
    rc = gpgsm_verify (assuan_get_pointer (ctx), fd,
                       ctrl->server_local->message_fd, out_fp);
  es_fclose (out_fp);

  /* Close and reset the fd.  */
  close_message_fd (ctrl);
  assuan_close_input_fd (ctx);
  assuan_close_output_fd (ctx);

  return rc;
}
Exemple #9
0
static gpg_error_t
cmd_decrypt (assuan_context_t ctx, char *line)
{
  ctrl_t ctrl = assuan_get_pointer (ctx);
  int inp_fd, out_fd;
  estream_t out_fp;
  int rc;

  (void)line;

  inp_fd = translate_sys2libc_fd (assuan_get_input_fd (ctx), 0);
  if (inp_fd == -1)
    return set_error (GPG_ERR_ASS_NO_INPUT, NULL);
  out_fd = translate_sys2libc_fd (assuan_get_output_fd (ctx), 1);
  if (out_fd == -1)
    return set_error (GPG_ERR_ASS_NO_OUTPUT, NULL);

  out_fp = es_fdopen_nc (out_fd, "w");
  if (!out_fp)
    return set_error (gpg_err_code_from_syserror (), "fdopen() failed");

  rc = start_audit_session (ctrl);
  if (!rc)
    rc = gpgsm_decrypt (ctrl, inp_fd, out_fp);
  es_fclose (out_fp);

  /* Close and reset the fds. */
  close_message_fd (ctrl);
  assuan_close_input_fd (ctx);
  assuan_close_output_fd (ctx);

  return rc;
}
Exemple #10
0
static gpg_error_t
cmd_sign (assuan_context_t ctx, char *line)
{
  ctrl_t ctrl = assuan_get_pointer (ctx);
  int inp_fd, out_fd;
  estream_t out_fp;
  int detached;
  int rc;

  inp_fd = translate_sys2libc_fd (assuan_get_input_fd (ctx), 0);
  if (inp_fd == -1)
    return set_error (GPG_ERR_ASS_NO_INPUT, NULL);
  out_fd = translate_sys2libc_fd (assuan_get_output_fd (ctx), 1);
  if (out_fd == -1)
    return set_error (GPG_ERR_ASS_NO_OUTPUT, NULL);

  detached = has_option (line, "--detached");

  out_fp = es_fdopen_nc (out_fd, "w");
  if (!out_fp)
    return set_error (GPG_ERR_ASS_GENERAL, "fdopen() failed");

  rc = start_audit_session (ctrl);
  if (!rc)
    rc = gpgsm_sign (assuan_get_pointer (ctx), ctrl->server_local->signerlist,
                     inp_fd, detached, out_fp);
  es_fclose (out_fp);

  /* close and reset the fd */
  close_message_fd (ctrl);
  assuan_close_input_fd (ctx);
  assuan_close_output_fd (ctx);

  return rc;
}
Exemple #11
0
/* Format NAME which is expected to be in rfc2253 format into a better
   human readable format. Caller must free the returned string.  NULL
   is returned in case of an error.  With TRANSLATE set to true the
   name will be translated to the native encoding.  Note that NAME is
   internally always UTF-8 encoded. */
char *
gpgsm_format_name2 (const char *name, int translate)
{
  estream_t fp;
  struct format_name_cookie cookie;
  es_cookie_io_functions_t io = { NULL };

  memset (&cookie, 0, sizeof cookie);

  io.func_write = format_name_writer;
  fp = es_fopencookie (&cookie, "w", io);
  if (!fp)
    {
      int save_errno = errno;
      log_error ("error creating memory stream: %s\n", strerror (save_errno));
      gpg_err_set_errno (save_errno);
      return NULL;
    }
  gpgsm_es_print_name2 (fp, name, translate);
  es_fclose (fp);
  if (cookie.error || !cookie.buffer)
    {
      xfree (cookie.buffer);
      gpg_err_set_errno (cookie.error);
      return NULL;
    }
  return cookie.buffer;
}
Exemple #12
0
/* Read the keyblob at FILENAME.  The caller should have acquired a
   lockfile and checked that the file exists.  */
static gpg_error_t
read_keyblob (const char *filename,
              void **r_enckeyblob, size_t *r_enckeybloblen)
{
  gpg_error_t err;
  estream_t fp = NULL;
  size_t headerlen = 0;
  size_t msglen;
  void *msg = NULL;

  *r_enckeyblob = NULL;
  *r_enckeybloblen = 0;

  err = read_keyblob_prefix (filename, &fp, &headerlen);
  if (err)
    goto leave;

  if (opt.verbose)
    log_info ("header length of '%s' is %zu\n", filename, headerlen);

  /* Read everything including the padding.  We should eventually do a
     regular OpenPGP parsing to detect the padding packet and pass
     only the actual used OpenPGP data to the engine.  This is in
     particular required when supporting CMS which will be
     encapsulated in an OpenPGP packet.  */
  assert (headerlen >= 32);
  msglen = headerlen - 32;
  if (!msglen)
    {
      err = gpg_error (GPG_ERR_NO_DATA);
      goto leave;
    }
  msg = xtrymalloc (msglen);
  if (!msglen)
    {
      err = gpg_error_from_syserror ();
      goto leave;
    }
  if (es_fread (msg, msglen, 1, fp) != 1)
    {
      err = gpg_error_from_syserror ();
      log_error ("error reading keyblob of '%s': %s\n",
                 filename, gpg_strerror (err));
      goto leave;
    }

  *r_enckeyblob = msg;
  msg = NULL;
  *r_enckeybloblen = msglen;

 leave:
  xfree (msg);
  es_fclose (fp);

  return err;
}
Exemple #13
0
/*  VERIFY

   This does a verify operation on the message send to the input-FD.
   The result is written out using status lines.  If an output FD was
   given, the signed text will be written to that.

   If the signature is a detached one, the server will inquire about
   the signed material and the client must provide it.
 */
static gpg_error_t
cmd_verify (assuan_context_t ctx, char *line)
{
  int rc;
#ifdef HAVE_W32_SYSTEM
  (void)ctx;
  (void)line;
  rc = gpg_error (GPG_ERR_NOT_IMPLEMENTED);
#else
  ctrl_t ctrl = assuan_get_pointer (ctx);
  gnupg_fd_t fd = assuan_get_input_fd (ctx);
  gnupg_fd_t out_fd = assuan_get_output_fd (ctx);
  estream_t out_fp = NULL;

  /* FIXME: Revamp this code it is nearly to 3 years old and was only
     intended as a quick test.  */

  (void)line;

  if (fd == GNUPG_INVALID_FD)
    return gpg_error (GPG_ERR_ASS_NO_INPUT);

  if (out_fd != GNUPG_INVALID_FD)
    {
      es_syshd_t syshd;

#ifdef HAVE_W32_SYSTEM
      syshd.type = ES_SYSHD_HANDLE;
      syshd.u.handle = out_fd;
#else
      syshd.type = ES_SYSHD_FD;
      syshd.u.fd = out_fd;
#endif
      out_fp = es_sysopen_nc (&syshd, "w");
      if (!out_fp)
        return set_error (gpg_err_code_from_syserror (), "fdopen() failed");
    }

  log_debug ("WARNING: The server mode is WORK "
             "IN PROGRESS and not ready for use\n");

  rc = gpg_verify (ctrl, fd, ctrl->server_local->message_fd, out_fp);

  es_fclose (out_fp);
  close_message_fd (ctrl);
  assuan_close_input_fd (ctx);
  assuan_close_output_fd (ctx);
#endif

  if (rc)
    log_error ("command '%s' failed: %s\n", "VERIFY", gpg_strerror (rc));
  return rc;
}
Exemple #14
0
/* Send the key in {DATA,DATALEN} to the keyserver identified by  URI.  */
gpg_error_t
ks_hkp_put (ctrl_t ctrl, parsed_uri_t uri, const void *data, size_t datalen)
{
  gpg_error_t err;
  char *hostport = NULL;
  char *request = NULL;
  estream_t fp = NULL;
  struct put_post_parm_s parm;
  char *armored = NULL;

  parm.datastring = NULL;

  err = armor_data (&armored, data, datalen);
  if (err)
    goto leave;

  parm.datastring = http_escape_string (armored, EXTRA_ESCAPE_CHARS);
  if (!parm.datastring)
    {
      err = gpg_error_from_syserror ();
      goto leave;
    }
  xfree (armored);
  armored = NULL;

  /* Build the request string.  */
  hostport = make_host_part (uri->scheme, uri->host, uri->port);
  if (!hostport)
    {
      err = gpg_error_from_syserror ();
      goto leave;
    }

  request = strconcat (hostport, "/pks/add", NULL);
  if (!request)
    {
      err = gpg_error_from_syserror ();
      goto leave;
    }

  /* Send the request.  */
  err = send_request (ctrl, request, hostport, put_post_cb, &parm, &fp);
  if (err)
    goto leave;

 leave:
  es_fclose (fp);
  xfree (parm.datastring);
  xfree (armored);
  xfree (request);
  xfree (hostport);
  return err;
}
Exemple #15
0
/* Ask the dirmngr for the policy flags and return them as an estream
 * memory stream.  If no policy flags are set, NULL is stored at
 * R_BUFFER.  */
gpg_error_t
wkd_get_policy_flags (const char *addrspec, estream_t *r_buffer)
{
  gpg_error_t err;
  assuan_context_t ctx;
  struct wkd_get_parm_s parm;
  char *line = NULL;
  char *buffer = NULL;

  memset (&parm, 0, sizeof parm);
  *r_buffer = NULL;

  err = connect_dirmngr (&ctx);
  if (err)
    return err;

  line = es_bsprintf ("WKD_GET --policy-flags -- %s", addrspec);
  if (!line)
    {
      err = gpg_error_from_syserror ();
      goto leave;
    }
  if (strlen (line) + 2 >= ASSUAN_LINELENGTH)
    {
      err = gpg_error (GPG_ERR_TOO_LARGE);
      goto leave;
    }

  parm.memfp = es_fopenmem (0, "rwb");
  if (!parm.memfp)
    {
      err = gpg_error_from_syserror ();
      goto leave;
    }
  err = assuan_transact (ctx, line, wkd_get_data_cb, &parm,
                         NULL, NULL, wkd_get_status_cb, &parm);
  if (err)
    goto leave;

  es_rewind (parm.memfp);
  *r_buffer = parm.memfp;
  parm.memfp = 0;

 leave:
  es_free (buffer);
  es_fclose (parm.memfp);
  xfree (line);
  assuan_release (ctx);
  return err;
}
Exemple #16
0
static gpg_error_t
cmd_genkey (assuan_context_t ctx, char *line)
{
  ctrl_t ctrl = assuan_get_pointer (ctx);
  int inp_fd, out_fd;
  estream_t in_stream, out_stream;
  int rc;

  (void)line;

  inp_fd = translate_sys2libc_fd (assuan_get_input_fd (ctx), 0);
  if (inp_fd == -1)
    return set_error (GPG_ERR_ASS_NO_INPUT, NULL);
  out_fd = translate_sys2libc_fd (assuan_get_output_fd (ctx), 1);
  if (out_fd == -1)
    return set_error (GPG_ERR_ASS_NO_OUTPUT, NULL);

  in_stream = es_fdopen_nc (inp_fd, "r");
  if (!in_stream)
    return set_error (GPG_ERR_ASS_GENERAL, "es_fdopen failed");

  out_stream = es_fdopen_nc (out_fd, "w");
  if (!out_stream)
    {
      es_fclose (in_stream);
      return set_error (gpg_err_code_from_syserror (), "fdopen() failed");
    }
  rc = gpgsm_genkey (ctrl, in_stream, out_stream);
  es_fclose (out_stream);
  es_fclose (in_stream);

  /* close and reset the fds */
  assuan_close_input_fd (ctx);
  assuan_close_output_fd (ctx);

  return rc;
}
Exemple #17
0
/* Test whether the container with name FILENAME is a suitable G13
   container.  This function may even be called on a mounted
   container.  */
gpg_error_t
g13_is_container (ctrl_t ctrl, const char *filename)
{
  gpg_error_t err;
  estream_t fp = NULL;
  size_t dummy;

  (void)ctrl;

  /* Read just the prefix of the header.  */
  err = read_keyblob_prefix (filename, &fp, &dummy);
  if (!err)
    es_fclose (fp);
  return err;
}
Exemple #18
0
static gpg_error_t
cmd_encrypt (assuan_context_t ctx, char *line)
{
  ctrl_t ctrl = assuan_get_pointer (ctx);
  certlist_t cl;
  int inp_fd, out_fd;
  estream_t out_fp;
  int rc;

  (void)line;

  inp_fd = translate_sys2libc_fd (assuan_get_input_fd (ctx), 0);
  if (inp_fd == -1)
    return set_error (GPG_ERR_ASS_NO_INPUT, NULL);
  out_fd = translate_sys2libc_fd (assuan_get_output_fd (ctx), 1);
  if (out_fd == -1)
    return set_error (GPG_ERR_ASS_NO_OUTPUT, NULL);

  out_fp = es_fdopen_nc (out_fd, "w");
  if (!out_fp)
    return set_error (gpg_err_code_from_syserror (), "fdopen() failed");

  /* Now add all encrypt-to marked recipients from the default
     list. */
  rc = 0;
  if (!opt.no_encrypt_to && !ctrl->server_local->no_encrypt_to)
    {
      for (cl=ctrl->server_local->default_recplist; !rc && cl; cl = cl->next)
        if (cl->is_encrypt_to)
          rc = gpgsm_add_cert_to_certlist (ctrl, cl->cert,
                                           &ctrl->server_local->recplist, 1);
    }
  if (!rc)
    rc = ctrl->audit? 0 : start_audit_session (ctrl);
  if (!rc)
    rc = gpgsm_encrypt (assuan_get_pointer (ctx),
                        ctrl->server_local->recplist,
                        inp_fd, out_fp);
  es_fclose (out_fp);

  gpgsm_release_certlist (ctrl->server_local->recplist);
  ctrl->server_local->recplist = NULL;
  /* Close and reset the fd */
  close_message_fd (ctrl);
  assuan_close_input_fd (ctx);
  assuan_close_output_fd (ctx);
  return rc;
}
Exemple #19
0
static gpg_error_t
cmd_getauditlog (assuan_context_t ctx, char *line)
{
  ctrl_t ctrl = assuan_get_pointer (ctx);
  int  out_fd;
  estream_t out_stream;
  int opt_data, opt_html;
  int rc;

  opt_data = has_option (line, "--data");
  opt_html = has_option (line, "--html");
  line = skip_options (line);

  if (!ctrl->audit)
    return gpg_error (GPG_ERR_NO_DATA);

  if (opt_data)
    {
      out_stream = es_fopencookie (ctx, "w", data_line_cookie_functions);
      if (!out_stream)
        return set_error (GPG_ERR_ASS_GENERAL,
                          "error setting up a data stream");
    }
  else
    {
      out_fd = translate_sys2libc_fd (assuan_get_output_fd (ctx), 1);
      if (out_fd == -1)
        return set_error (GPG_ERR_ASS_NO_OUTPUT, NULL);

      out_stream = es_fdopen_nc (out_fd, "w");
      if (!out_stream)
        {
          return set_error (GPG_ERR_ASS_GENERAL, "es_fdopen() failed");
        }
    }

  audit_print_result (ctrl->audit, out_stream, opt_html);
  rc = 0;

  es_fclose (out_stream);

  /* Close and reset the fd. */
  if (!opt_data)
    assuan_close_output_fd (ctx);
  return rc;
}
Exemple #20
0
/* This thread feeds data to the given stream.  */
static THREAD_RET_TYPE
producer_thread (void *argaddr)
{
  struct thread_arg *arg = argaddr;
  int i = 0;

  (void)arg;

  while (!arg->stop_me && i++ < 3)
    {
      show ("thread '%s' about to write\n", arg->name);
      es_fprintf (arg->stream, "This is '%s' count=%d\n", arg->name, i);
      es_fflush (arg->stream);
    }
  es_fclose (arg->stream);
  return THREAD_RET_VALUE;
}
Exemple #21
0
int
keyserver_fetch (ctrl_t ctrl, strlist_t urilist)
{
  gpg_error_t err;
  strlist_t sl;
  estream_t datastream;
  unsigned int options = opt.keyserver_options.import_options;

  /* Switch on fast-import, since fetch can handle more than one
     import and we don't want each set to rebuild the trustdb.
     Instead we do it once at the end. */
  opt.keyserver_options.import_options |= IMPORT_FAST;

  for (sl=urilist; sl; sl=sl->next)
    {
      if (!opt.quiet)
        log_info (_("requesting key from '%s'\n"), sl->d);

      err = gpg_dirmngr_ks_fetch (ctrl, sl->d, &datastream);
      if (!err)
        {
          void *stats_handle;

          stats_handle = import_new_stats_handle();
          import_keys_es_stream (ctrl, datastream, stats_handle, NULL, NULL,
                                 opt.keyserver_options.import_options);

          import_print_stats (stats_handle);
          import_release_stats_handle (stats_handle);
        }
      else
        log_info (_("WARNING: unable to fetch URI %s: %s\n"),
                  sl->d, gpg_strerror (err));
      es_fclose (datastream);
    }

  opt.keyserver_options.import_options = options;

  /* If the original options didn't have fast import, and the trustdb
     is dirty, rebuild. */
  if (!(opt.keyserver_options.import_options&IMPORT_FAST))
    trustdb_check_or_update ();

  return 0;
}
Exemple #22
0
/* The public release function. */
void
runner_release (runner_t runner)
{
  gpg_error_t err;

  if (!runner)
    return;

  if (!--runner->refcount)
    return;

  err = mountinfo_del_mount (NULL, NULL, runner->identifier);
  if (err)
    log_error ("failed to remove mount with rid %u from mtab: %s\n",
               runner->identifier, gpg_strerror (err));

  es_fclose (runner->status_fp);
  if (runner->in_fd != -1)
    close (runner->in_fd);
  if (runner->out_fd != -1)
    close (runner->out_fd);

  /* Fixme: close the process. */

  /* Tell the engine to release its data.  */
  if (runner->handler_cleanup)
    runner->handler_cleanup (runner->handler_data);

  if (runner->pid != (pid_t)(-1))
    {
      /* The process has not been cleaned up - do it now.  */
      gnupg_kill_process (runner->pid);
      /* (Actually we should use the program name and not the
          arbitrary NAME of the runner object.  However it does not
          matter because that information is only used for
          diagnostics.)  */
      gnupg_wait_process (runner->name, runner->pid, 1, NULL);
      gnupg_release_process (runner->pid);
    }

  xfree (runner->name);
  xfree (runner);
}
Exemple #23
0
/* This thread eats data from the given stream.  */
static THREAD_RET_TYPE
consumer_thread (void *argaddr)
{
  struct thread_arg *arg = argaddr;
  char buf[15];

  (void)arg;

  while (!arg->stop_me)
    {
      show ("thread '%s' ready to read\n", arg->name);
      if (!es_fgets (buf, sizeof buf, arg->stream))
        {
          show ("Thread '%s' received EOF or error\n", arg->name);
          break;
        }
      show ("Thread '%s' got: '%s'\n", arg->name, buf);
    }
  es_fclose (arg->stream);
  return THREAD_RET_VALUE;
}
Exemple #24
0
/* Helper to write mail to the output(s).  */
gpg_error_t
wks_send_mime (mime_maker_t mime)
{
  gpg_error_t err;
  estream_t mail;

  /* Without any option we take a short path.  */
  if (!opt.use_sendmail && !opt.output)
    {
      es_set_binary (es_stdout);
      return mime_maker_make (mime, es_stdout);
    }


  mail = es_fopenmem (0, "w+b");
  if (!mail)
    {
      err = gpg_error_from_syserror ();
      return err;
    }

  err = mime_maker_make (mime, mail);

  if (!err && opt.output)
    {
      es_rewind (mail);
      err = send_mail_to_file (mail, opt.output);
    }

  if (!err && opt.use_sendmail)
    {
      es_rewind (mail);
      err = send_mail (mail);
    }

  es_fclose (mail);
  return err;
}
Exemple #25
0
/*  VERIFY

   This does a verify operation on the message send to the input-FD.
   The result is written out using status lines.  If an output FD was
   given, the signed text will be written to that.

   If the signature is a detached one, the server will inquire about
   the signed material and the client must provide it.
 */
static gpg_error_t
cmd_verify (assuan_context_t ctx, char *line)
{
  int rc;
  ctrl_t ctrl = assuan_get_pointer (ctx);
  gnupg_fd_t fd = assuan_get_input_fd (ctx);
  gnupg_fd_t out_fd = assuan_get_output_fd (ctx);
  estream_t out_fp = NULL;

  /* FIXME: Revamp this code it is nearly to 3 years old and was only
     intended as a quick test.  */

  (void)line;

  if (fd == GNUPG_INVALID_FD)
    return gpg_error (GPG_ERR_ASS_NO_INPUT);

  if (out_fd != GNUPG_INVALID_FD)
    {
      out_fp = es_fdopen_nc (out_fd, "w");
      if (!out_fp)
        return set_error (gpg_err_code_from_syserror (), "fdopen() failed");
    }

  log_debug ("WARNING: The server mode is WORK "
             "iN PROGRESS and not ready for use\n");

  rc = gpg_verify (ctrl, fd, ctrl->server_local->message_fd, out_fp);

  es_fclose (out_fp);
  close_message_fd (ctrl);
  assuan_close_input_fd (ctx);
  assuan_close_output_fd (ctx);

  if (rc)
    log_error ("command '%s' failed: %s\n", "VERIFY", gpg_strerror (rc));
  return rc;
}
Exemple #26
0
/* Search all configured keyservers for keys matching PATTERNS and
   write the result to the provided output stream.  */
gpg_error_t
ks_action_search (ctrl_t ctrl, strlist_t patterns, estream_t outfp)
{
  gpg_error_t err = 0;
  int any_server = 0;
  uri_item_t uri;
  estream_t infp;

  if (!patterns)
    return gpg_error (GPG_ERR_NO_USER_ID);

  /* FIXME: We only take care of the first pattern.  To fully support
     multiple patterns we might either want to run several queries in
     parallel and merge them.  We also need to decide what to do with
     errors - it might not be the best idea to ignore an error from
     one server and silently continue with another server.  For now we
     stop at the first error. */
  for (uri = ctrl->keyservers; !err && uri; uri = uri->next)
    {
      if (uri->parsed_uri->is_http)
        {
          any_server = 1;
          err = ks_hkp_search (ctrl, uri->parsed_uri, patterns->d, &infp);
          if (!err)
            {
              err = copy_stream (infp, outfp);
              es_fclose (infp);
              break;
            }
        }
    }

  if (!any_server)
    err = gpg_error (GPG_ERR_NO_KEYSERVER);
  return err;
}
Exemple #27
0
/* The thread spawned by runner_spawn.  */
static void *
runner_thread (void *arg)
{
  runner_t runner = arg;
  gpg_error_t err = 0;

  log_debug ("starting runner thread\n");
  /* If a status_fp is available, the thread's main task is to read
     from that stream and invoke the backend's handler function.  This
     is done on a line by line base and the line length is limited to
     a reasonable value (about 1000 characters). Other work will
     continue either due to an EOF of the stream or by demand of the
     engine.  */
  if (runner->status_fp)
    {
      int c, cont_line;
      unsigned int pos;
      char buffer[1024];
      estream_t fp = runner->status_fp;

      pos = 0;
      cont_line = 0;
      while (!err && !runner->cancel_flag && (c=es_getc (fp)) != EOF)
        {
          buffer[pos++] = c;
          if (pos >= sizeof buffer - 5 || c == '\n')
            {
              buffer[pos - (c == '\n')] = 0;
              if (opt.verbose)
                log_info ("%s%s: %s\n",
                          runner->name, cont_line? "(cont)":"", buffer);
              /* We handle only complete lines and ignore any stuff we
                 possibly had to truncate.  That is - at least for the
                 encfs engine - not an issue because our changes to
                 the tool make sure that only relatively short prompt
                 lines are of interest.  */
              if (!cont_line && runner->handler)
                err = runner->handler (runner->handler_data,
                                       runner, buffer);
              pos = 0;
              cont_line = (c != '\n');
            }
        }
      if (!err && runner->cancel_flag)
        log_debug ("runner thread noticed cancel flag\n");
      else
        log_debug ("runner thread saw EOF\n");
      if (pos)
        {
          buffer[pos] = 0;
          if (opt.verbose)
            log_info ("%s%s: %s\n",
                      runner->name, cont_line? "(cont)":"", buffer);
          if (!cont_line && !err && runner->handler)
            err = runner->handler (runner->handler_data,
                                          runner, buffer);
        }
      if (!err && es_ferror (fp))
        {
          err = gpg_error_from_syserror ();
          log_error ("error reading from %s: %s\n",
                     runner->name, gpg_strerror (err));
        }

      runner->status_fp = NULL;
      es_fclose (fp);
      log_debug ("runner thread closed status fp\n");
    }

  /* Now wait for the process to finish.  */
  if (!err && runner->pid != (pid_t)(-1))
    {
      int exitcode;

      log_debug ("runner thread waiting ...\n");
      err = gnupg_wait_process (runner->name, runner->pid, 1, &exitcode);
      gnupg_release_process (runner->pid);
      runner->pid = (pid_t)(-1);
      if (err)
        log_error ("running '%s' failed (exitcode=%d): %s\n",
                   runner->name, exitcode, gpg_strerror (err));
      log_debug ("runner thread waiting finished\n");
    }

  /* Get rid of the runner object (note: it is refcounted).  */
  log_debug ("runner thread releasing runner ...\n");
  {
    runner_t r, rprev;

    for (r = running_threads, rprev = NULL; r; rprev = r, r = r->next_running)
      if (r == runner)
        {
          if (!rprev)
            running_threads = r->next_running;
          else
            rprev->next_running = r->next_running;
          r->next_running = NULL;
          break;
        }
  }
  runner_release (runner);
  log_debug ("runner thread runner released\n");

  return NULL;
}
Exemple #28
0
/* Fork and exec the PGMNAME, see exechelp.h for details.  */
gpg_error_t
gnupg_spawn_process (const char *pgmname, const char *argv[],
                     int *except, void (*preexec)(void), unsigned int flags,
                     estream_t *r_infp,
                     estream_t *r_outfp,
                     estream_t *r_errfp,
                     pid_t *pid)
{
  gpg_error_t err;
  int inpipe[2] = {-1, -1};
  int outpipe[2] = {-1, -1};
  int errpipe[2] = {-1, -1};
  estream_t infp = NULL;
  estream_t outfp = NULL;
  estream_t errfp = NULL;
  int nonblock = !!(flags & GNUPG_SPAWN_NONBLOCK);

  if (r_infp)
    *r_infp = NULL;
  if (r_outfp)
    *r_outfp = NULL;
  if (r_errfp)
    *r_errfp = NULL;
  *pid = (pid_t)(-1); /* Always required.  */

  if (r_infp)
    {
      err = create_pipe_and_estream (inpipe, &infp, 1, nonblock);
      if (err)
        return err;
    }

  if (r_outfp)
    {
      err = create_pipe_and_estream (outpipe, &outfp, 0, nonblock);
      if (err)
        {
          if (infp)
            es_fclose (infp);
          else if (inpipe[1] != -1)
            close (inpipe[1]);
          if (inpipe[0] != -1)
            close (inpipe[0]);

          return err;
        }
    }

  if (r_errfp)
    {
      err = create_pipe_and_estream (errpipe, &errfp, 0, nonblock);
      if (err)
        {
          if (infp)
            es_fclose (infp);
          else if (inpipe[1] != -1)
            close (inpipe[1]);
          if (inpipe[0] != -1)
            close (inpipe[0]);

          if (outfp)
            es_fclose (outfp);
          else if (outpipe[0] != -1)
            close (outpipe[0]);
          if (outpipe[1] != -1)
            close (outpipe[1]);

          return err;
        }
    }


  *pid = fork ();
  if (*pid == (pid_t)(-1))
    {
      err = my_error_from_syserror ();
      log_error (_("error forking process: %s\n"), gpg_strerror (err));

      if (infp)
        es_fclose (infp);
      else if (inpipe[1] != -1)
        close (inpipe[1]);
      if (inpipe[0] != -1)
        close (inpipe[0]);

      if (outfp)
        es_fclose (outfp);
      else if (outpipe[0] != -1)
        close (outpipe[0]);
      if (outpipe[1] != -1)
        close (outpipe[1]);

      if (errfp)
        es_fclose (errfp);
      else if (errpipe[0] != -1)
        close (errpipe[0]);
      if (errpipe[1] != -1)
        close (errpipe[1]);
      return err;
    }

  if (!*pid)
    {
      /* This is the child. */
      gcry_control (GCRYCTL_TERM_SECMEM);
      es_fclose (outfp);
      es_fclose (errfp);
      do_exec (pgmname, argv, inpipe[0], outpipe[1], errpipe[1],
               except, preexec);
      /*NOTREACHED*/
    }

  /* This is the parent. */
  if (inpipe[0] != -1)
    close (inpipe[0]);
  if (outpipe[1] != -1)
    close (outpipe[1]);
  if (errpipe[1] != -1)
    close (errpipe[1]);

  if (r_infp)
    *r_infp = infp;
  if (r_outfp)
    *r_outfp = outfp;
  if (r_errfp)
    *r_errfp = errfp;

  return 0;
}
/* Fork and exec the PGMNAME, see exechelp.h for details.  */
gpg_error_t
gnupg_spawn_process (const char *pgmname, const char *argv[],
                     gpg_err_source_t errsource,
                     void (*preexec)(void), unsigned int flags,
                     estream_t infp,
                     estream_t *r_outfp,
                     estream_t *r_errfp,
                     pid_t *pid)
{
  gpg_error_t err;
  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 cr_flags;
  char *cmdline;
  HANDLE inhandle = INVALID_HANDLE_VALUE;
  HANDLE outpipe[2] = {INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE};
  HANDLE errpipe[2] = {INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE};
  estream_t outfp = NULL;
  estream_t errfp = NULL;
  HANDLE nullhd[3] = {INVALID_HANDLE_VALUE,
                      INVALID_HANDLE_VALUE,
                      INVALID_HANDLE_VALUE};
  int i;
  es_syshd_t syshd;

  if (r_outfp)
    *r_outfp = NULL;
  if (r_errfp)
    *r_errfp = NULL;
  *pid = (pid_t)(-1); /* Always required.  */

  if (infp)
    {
      es_fflush (infp);
      es_rewind (infp);
      es_syshd (infp, &syshd);
      switch (syshd.type)
        {
        case ES_SYSHD_FD:
          inhandle = (HANDLE)_get_osfhandle (syshd.u.fd);
          break;
        case ES_SYSHD_SOCK:
          inhandle = (HANDLE)_get_osfhandle (syshd.u.sock);
          break;
        case ES_SYSHD_HANDLE:
          inhandle = syshd.u.handle;
          break;
        default:
          inhandle = INVALID_HANDLE_VALUE;
          break;
        }
      if (inhandle == INVALID_HANDLE_VALUE)
        return gpg_err_make (errsource, GPG_ERR_INV_VALUE);
      /* FIXME: In case we can't get a system handle (e.g. due to
         es_fopencookie we should create a piper and a feeder
         thread.  */
    }

  if (r_outfp)
    {
      if (create_inheritable_pipe (outpipe, 1))
        {
          err = gpg_err_make (errsource, GPG_ERR_GENERAL);
          log_error (_("error creating a pipe: %s\n"), gpg_strerror (err));
          return err;
        }

      syshd.type = ES_SYSHD_HANDLE;
      syshd.u.handle = outpipe[0];
      outfp = es_sysopen (&syshd, "r");
      if (!outfp)
        {
          err = gpg_err_make (errsource, gpg_err_code_from_syserror ());
          log_error (_("error creating a stream for a pipe: %s\n"),
                     gpg_strerror (err));
          CloseHandle (outpipe[0]);
          CloseHandle (outpipe[1]);
          outpipe[0] = outpipe[1] = INVALID_HANDLE_VALUE;
          return err;
        }
    }

  if (r_errfp)
    {
      if (create_inheritable_pipe (errpipe, 1))
        {
          err = gpg_err_make (errsource, GPG_ERR_GENERAL);
          log_error (_("error creating a pipe: %s\n"), gpg_strerror (err));
          return err;
        }

      syshd.type = ES_SYSHD_HANDLE;
      syshd.u.handle = errpipe[0];
      errfp = es_sysopen (&syshd, "r");
      if (!errfp)
        {
          err = gpg_err_make (errsource, gpg_err_code_from_syserror ());
          log_error (_("error creating a stream for a pipe: %s\n"),
                     gpg_strerror (err));
          CloseHandle (errpipe[0]);
          CloseHandle (errpipe[1]);
          errpipe[0] = errpipe[1] = INVALID_HANDLE_VALUE;
          if (outfp)
            es_fclose (outfp);
          else if (outpipe[0] != INVALID_HANDLE_VALUE)
            CloseHandle (outpipe[0]);
          if (outpipe[1] != INVALID_HANDLE_VALUE)
            CloseHandle (outpipe[1]);
          return err;
        }
    }

  /* Prepare security attributes.  */
  memset (&sec_attr, 0, sizeof sec_attr );
  sec_attr.nLength = sizeof sec_attr;
  sec_attr.bInheritHandle = FALSE;

  /* Build the command line.  */
  err = build_w32_commandline (pgmname, argv, &cmdline);
  if (err)
    return err;

  if (inhandle != INVALID_HANDLE_VALUE)
    nullhd[0] = w32_open_null (0);
  if (outpipe[1] != INVALID_HANDLE_VALUE)
    nullhd[1] = w32_open_null (0);
  if (errpipe[1] != INVALID_HANDLE_VALUE)
    nullhd[2] = w32_open_null (0);

  /* Start the process.  Note that we can't run the PREEXEC function
     because this might change our own environment. */
  (void)preexec;

  memset (&si, 0, sizeof si);
  si.cb = sizeof (si);
  si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
  si.wShowWindow = DEBUG_W32_SPAWN? SW_SHOW : SW_MINIMIZE;
  si.hStdInput  =   inhandle == INVALID_HANDLE_VALUE? nullhd[0] : inhandle;
  si.hStdOutput = outpipe[1] == INVALID_HANDLE_VALUE? nullhd[1] : outpipe[1];
  si.hStdError  = errpipe[1] == INVALID_HANDLE_VALUE? nullhd[2] : errpipe[1];

  cr_flags = (CREATE_DEFAULT_ERROR_MODE
              | ((flags & 128)? DETACHED_PROCESS : 0)
              | GetPriorityClass (GetCurrentProcess ())
              | CREATE_SUSPENDED);
/*   log_debug ("CreateProcess, path=`%s' cmdline=`%s'\n", pgmname, cmdline); */
  if (!CreateProcess (pgmname,       /* Program to start.  */
                      cmdline,       /* Command line arguments.  */
                      &sec_attr,     /* Process security attributes.  */
                      &sec_attr,     /* Thread security attributes.  */
                      TRUE,          /* Inherit handles.  */
                      cr_flags,      /* Creation flags.  */
                      NULL,          /* Environment.  */
                      NULL,          /* Use current drive/directory.  */
                      &si,           /* Startup information. */
                      &pi            /* Returns process information.  */
                      ))
    {
      log_error ("CreateProcess failed: %s\n", w32_strerror (-1));
      xfree (cmdline);
      if (outfp)
        es_fclose (outfp);
      else if (outpipe[0] != INVALID_HANDLE_VALUE)
        CloseHandle (outpipe[0]);
      if (outpipe[1] != INVALID_HANDLE_VALUE)
        CloseHandle (outpipe[1]);
      if (errfp)
        es_fclose (errfp);
      else if (errpipe[0] != INVALID_HANDLE_VALUE)
        CloseHandle (errpipe[0]);
      if (errpipe[1] != INVALID_HANDLE_VALUE)
        CloseHandle (errpipe[1]);
      return gpg_err_make (errsource, GPG_ERR_GENERAL);
    }
  xfree (cmdline);
  cmdline = NULL;

  /* Close the inherited handles to /dev/null.  */
  for (i=0; i < DIM (nullhd); i++)
    if (nullhd[i] != INVALID_HANDLE_VALUE)
      CloseHandle (nullhd[i]);

  /* Close the inherited ends of the pipes.  */
  if (outpipe[1] != INVALID_HANDLE_VALUE)
    CloseHandle (outpipe[1]);
  if (errpipe[1] != INVALID_HANDLE_VALUE)
    CloseHandle (errpipe[1]);

  /* log_debug ("CreateProcess ready: hProcess=%p hThread=%p" */
  /*            " dwProcessID=%d dwThreadId=%d\n", */
  /*            pi.hProcess, pi.hThread, */
  /*            (int) pi.dwProcessId, (int) pi.dwThreadId); */
  /* log_debug ("                     outfp=%p errfp=%p\n", outfp, errfp); */

  /* Fixme: For unknown reasons AllowSetForegroundWindow returns an
     invalid argument error if we pass it the correct processID.  As a
     workaround we use -1 (ASFW_ANY).  */
  if ( (flags & 64) )
    gnupg_allow_set_foregound_window ((pid_t)(-1)/*pi.dwProcessId*/);

  /* Process has been created suspended; resume it now. */
  ResumeThread (pi.hThread);
  CloseHandle (pi.hThread);

  if (r_outfp)
    *r_outfp = outfp;
  if (r_errfp)
    *r_errfp = errfp;

  *pid = handle_to_pid (pi.hProcess);
  return 0;

}
/* Fork and exec the PGMNAME, see exechelp.h for details.  */
gpg_error_t
gnupg_spawn_process (const char *pgmname, const char *argv[],
                     gpg_err_source_t errsource,
                     void (*preexec)(void), unsigned int flags,
                     estream_t infp,
                     estream_t *r_outfp,
                     estream_t *r_errfp,
                     pid_t *pid)
{
    gpg_error_t err;
    int infd = -1;
    int outpipe[2] = {-1, -1};
    int errpipe[2] = {-1, -1};
    estream_t outfp = NULL;
    estream_t errfp = NULL;

    (void)flags; /* Currently not used.  */

    if (r_outfp)
        *r_outfp = NULL;
    if (r_errfp)
        *r_errfp = NULL;
    *pid = (pid_t)(-1); /* Always required.  */

    if (infp)
    {
        es_fflush (infp);
        es_rewind (infp);
        infd = es_fileno (infp);
        if (infd == -1)
            return gpg_err_make (errsource, GPG_ERR_INV_VALUE);
    }

    if (r_outfp)
    {
        err = create_pipe_and_estream (outpipe, &outfp, errsource);
        if (err)
            return err;
    }

    if (r_errfp)
    {
        err = create_pipe_and_estream (errpipe, &errfp, errsource);
        if (err)
        {
            if (outfp)
                es_fclose (outfp);
            else if (outpipe[0] != -1)
                close (outpipe[0]);
            if (outpipe[1] != -1)
                close (outpipe[1]);
            return err;
        }
    }


    *pid = fork ();
    if (*pid == (pid_t)(-1))
    {
        err = gpg_err_make (errsource, gpg_err_code_from_syserror ());
        log_error (_("error forking process: %s\n"), gpg_strerror (err));

        if (outfp)
            es_fclose (outfp);
        else if (outpipe[0] != -1)
            close (outpipe[0]);
        if (outpipe[1] != -1)
            close (outpipe[1]);

        if (errfp)
            es_fclose (errfp);
        else if (errpipe[0] != -1)
            close (errpipe[0]);
        if (errpipe[1] != -1)
            close (errpipe[1]);
        return err;
    }

    if (!*pid)
    {
        /* This is the child. */
        gcry_control (GCRYCTL_TERM_SECMEM);
        es_fclose (outfp);
        es_fclose (errfp);
        do_exec (pgmname, argv, infd, outpipe[1], errpipe[1], preexec);
        /*NOTREACHED*/
    }

    /* This is the parent. */
    if (outpipe[1] != -1)
        close (outpipe[1]);
    if (errpipe[1] != -1)
        close (errpipe[1]);

    if (r_outfp)
        *r_outfp = outfp;
    if (r_errfp)
        *r_errfp = errfp;

    return 0;
}