Пример #1
0
static gpgme_error_t
build_fd_data_map (engine_spawn_t esp)
{
  struct datalist_s *a;
  size_t datac;
  int fds[2];

  for (datac = 0, a = esp->arglist; a; a = a->next)
    if (a->data)
      datac++;

  free_fd_data_map (esp->fd_data_map);
  esp->fd_data_map = calloc (datac + 1, sizeof *esp->fd_data_map);
  if (!esp->fd_data_map)
    return gpg_error_from_syserror ();

  for (datac = 0, a = esp->arglist; a; a = a->next)
    {
      assert (a->data);

      if (_gpgme_io_pipe (fds, a->inbound ? 1 : 0) == -1)
        {
          free (esp->fd_data_map);
          esp->fd_data_map = NULL;
          return gpg_error_from_syserror ();
        }
      if (_gpgme_io_set_close_notify (fds[0], close_notify_handler, esp)
          || _gpgme_io_set_close_notify (fds[1], close_notify_handler, esp))
        {
          /* FIXME: Need error cleanup.  */
          return gpg_error (GPG_ERR_GENERAL);
        }

      esp->fd_data_map[datac].inbound = a->inbound;
      if (a->inbound)
        {
          esp->fd_data_map[datac].fd       = fds[0];
          esp->fd_data_map[datac].peer_fd  = fds[1];
        }
      else
        {
          esp->fd_data_map[datac].fd       = fds[1];
          esp->fd_data_map[datac].peer_fd  = fds[0];
        }
      esp->fd_data_map[datac].data    = a->data;
      esp->fd_data_map[datac].dup_to  = a->dup_to;
      datac++;
    }

  return 0;
}
Пример #2
0
static gpgme_error_t
start (engine_gpgsm_t gpgsm, const char *command)
{
  gpgme_error_t err;
  assuan_fd_t afdlist[5];
  int fdlist[5];
  int nfds;
  int i;

  /* We need to know the fd used by assuan for reads.  We do this by
     using the assumption that the first returned fd from
     assuan_get_active_fds() is always this one.  */
  nfds = assuan_get_active_fds (gpgsm->assuan_ctx, 0 /* read fds */,
                                afdlist, DIM (afdlist));
  if (nfds < 1)
    return gpg_error (GPG_ERR_GENERAL);	/* FIXME */
  /* For now... */
  for (i = 0; i < nfds; i++)
    fdlist[i] = (int) afdlist[i];

  /* We "duplicate" the file descriptor, so we can close it here (we
     can't close fdlist[0], as that is closed by libassuan, and
     closing it here might cause libassuan to close some unrelated FD
     later).  Alternatively, we could special case status_fd and
     register/unregister it manually as needed, but this increases
     code duplication and is more complicated as we can not use the
     close notifications etc.  A third alternative would be to let
     Assuan know that we closed the FD, but that complicates the
     Assuan interface.  */

  gpgsm->status_cb.fd = _gpgme_io_dup (fdlist[0]);
  if (gpgsm->status_cb.fd < 0)
    return gpg_error_from_syserror ();

  if (_gpgme_io_set_close_notify (gpgsm->status_cb.fd,
				  close_notify_handler, gpgsm))
    {
      _gpgme_io_close (gpgsm->status_cb.fd);
      gpgsm->status_cb.fd = -1;
      return gpg_error (GPG_ERR_GENERAL);
    }

  err = add_io_cb (gpgsm, &gpgsm->status_cb, status_handler);
  if (!err && gpgsm->input_cb.fd != -1)
    err = add_io_cb (gpgsm, &gpgsm->input_cb, _gpgme_data_outbound_handler);
  if (!err && gpgsm->output_cb.fd != -1)
    err = add_io_cb (gpgsm, &gpgsm->output_cb, _gpgme_data_inbound_handler);
  if (!err && gpgsm->message_cb.fd != -1)
    err = add_io_cb (gpgsm, &gpgsm->message_cb, _gpgme_data_outbound_handler);

  if (!err)
    err = assuan_write_line (gpgsm->assuan_ctx, command);

  if (!err)
    gpgsm_io_event (gpgsm, GPGME_EVENT_START, NULL);

  return err;
}
Пример #3
0
static gpgme_error_t
start(engine_gpgsm_t gpgsm, const char *command)
{
    gpgme_error_t err;
    int fdlist[5];
    int nfds;

    /* We need to know the fd used by assuan for reads.  We do this by
       using the assumption that the first returned fd from
       assuan_get_active_fds() is always this one.  */
    nfds = assuan_get_active_fds(gpgsm->assuan_ctx, 0 /* read fds */,
                                 fdlist, DIM(fdlist));
    if(nfds < 1)
        return gpg_error(GPG_ERR_GENERAL);	/* FIXME */

    /* We duplicate the file descriptor, so we can close it without
       disturbing assuan.  Alternatively, we could special case
       status_fd and register/unregister it manually as needed, but this
       increases code duplication and is more complicated as we can not
       use the close notifications etc.  */
    gpgsm->status_cb.fd = dup(fdlist[0]);
    if(gpgsm->status_cb.fd < 0)
        return gpg_error_from_syserror();

    if(_gpgme_io_set_close_notify(gpgsm->status_cb.fd,
                                  close_notify_handler, gpgsm))
    {
        close(gpgsm->status_cb.fd);
        gpgsm->status_cb.fd = -1;
        return gpg_error(GPG_ERR_GENERAL);
    }

    err = add_io_cb(gpgsm, &gpgsm->status_cb, status_handler);
    if(!err && gpgsm->input_cb.fd != -1)
        err = add_io_cb(gpgsm, &gpgsm->input_cb, _gpgme_data_outbound_handler);
    if(!err && gpgsm->output_cb.fd != -1)
        err = add_io_cb(gpgsm, &gpgsm->output_cb, _gpgme_data_inbound_handler);
    if(!err && gpgsm->message_cb.fd != -1)
        err = add_io_cb(gpgsm, &gpgsm->message_cb, _gpgme_data_outbound_handler);

    if(!err)
        err = map_assuan_error(assuan_write_line(gpgsm->assuan_ctx, command));

    if(!err)
        (*gpgsm->io_cbs.event)(gpgsm->io_cbs.event_priv, GPGME_EVENT_START, NULL);

    return err;
}
Пример #4
0
static gpgme_error_t
gpgsm_set_fd (engine_gpgsm_t gpgsm, fd_type_t fd_type, const char *opt)
{
  gpg_error_t err = 0;
  char line[COMMANDLINELEN];
  char *which;
  iocb_data_t *iocb_data;
  int dir;

  switch (fd_type)
    {
    case INPUT_FD:
      which = "INPUT";
      iocb_data = &gpgsm->input_cb;
      break;

    case OUTPUT_FD:
      which = "OUTPUT";
      iocb_data = &gpgsm->output_cb;
      break;

    case MESSAGE_FD:
      which = "MESSAGE";
      iocb_data = &gpgsm->message_cb;
      break;

    default:
      return gpg_error (GPG_ERR_INV_VALUE);
    }

  dir = iocb_data->dir;

#if USE_DESCRIPTOR_PASSING
  /* We try to short-cut the communication by giving GPGSM direct
     access to the file descriptor, rather than using a pipe.  */
  iocb_data->server_fd = _gpgme_data_get_fd (iocb_data->data);
  if (iocb_data->server_fd < 0)
    {
      int fds[2];

      if (_gpgme_io_pipe (fds, dir) < 0)
	return gpg_error_from_syserror ();

      iocb_data->fd = dir ? fds[0] : fds[1];
      iocb_data->server_fd = dir ? fds[1] : fds[0];

      if (_gpgme_io_set_close_notify (iocb_data->fd,
				      close_notify_handler, gpgsm))
	{
	  err = gpg_error (GPG_ERR_GENERAL);
	  goto leave_set_fd;
	}
    }

  err = assuan_sendfd (gpgsm->assuan_ctx, iocb_data->server_fd);
  if (err)
    goto leave_set_fd;

  _gpgme_io_close (iocb_data->server_fd);
  iocb_data->server_fd = -1;

  if (opt)
    snprintf (line, COMMANDLINELEN, "%s FD %s", which, opt);
  else
    snprintf (line, COMMANDLINELEN, "%s FD", which);
#else
  if (opt)
    snprintf (line, COMMANDLINELEN, "%s FD=%s %s",
              which, iocb_data->server_fd_str, opt);
  else
    snprintf (line, COMMANDLINELEN, "%s FD=%s",
              which, iocb_data->server_fd_str);
#endif

  err = gpgsm_assuan_simple_command (gpgsm->assuan_ctx, line, NULL, NULL);

#if USE_DESCRIPTOR_PASSING
 leave_set_fd:
  if (err)
    {
      _gpgme_io_close (iocb_data->fd);
      iocb_data->fd = -1;
      if (iocb_data->server_fd != -1)
        {
          _gpgme_io_close (iocb_data->server_fd);
          iocb_data->server_fd = -1;
        }
    }
#endif

  return err;
}
Пример #5
0
static gpgme_error_t
gpgsm_new (void **engine, const char *file_name, const char *home_dir)
{
  gpgme_error_t err = 0;
  engine_gpgsm_t gpgsm;
  const char *argv[5];
  int argc;
#if !USE_DESCRIPTOR_PASSING
  int fds[2];
  int child_fds[4];
#endif
  char *dft_display = NULL;
  char dft_ttyname[64];
  char *dft_ttytype = NULL;
  char *optstr;

  gpgsm = calloc (1, sizeof *gpgsm);
  if (!gpgsm)
    return gpg_error_from_syserror ();

  gpgsm->status_cb.fd = -1;
  gpgsm->status_cb.dir = 1;
  gpgsm->status_cb.tag = 0;
  gpgsm->status_cb.data = gpgsm;

  gpgsm->input_cb.fd = -1;
  gpgsm->input_cb.dir = 0;
  gpgsm->input_cb.tag = 0;
  gpgsm->input_cb.server_fd = -1;
  *gpgsm->input_cb.server_fd_str = 0;
  gpgsm->output_cb.fd = -1;
  gpgsm->output_cb.dir = 1;
  gpgsm->output_cb.tag = 0;
  gpgsm->output_cb.server_fd = -1;
  *gpgsm->output_cb.server_fd_str = 0;
  gpgsm->message_cb.fd = -1;
  gpgsm->message_cb.dir = 0;
  gpgsm->message_cb.tag = 0;
  gpgsm->message_cb.server_fd = -1;
  *gpgsm->message_cb.server_fd_str = 0;

  gpgsm->status.fnc = 0;
  gpgsm->colon.fnc = 0;
  gpgsm->colon.attic.line = 0;
  gpgsm->colon.attic.linesize = 0;
  gpgsm->colon.attic.linelen = 0;
  gpgsm->colon.any = 0;

  gpgsm->inline_data = NULL;

  gpgsm->io_cbs.add = NULL;
  gpgsm->io_cbs.add_priv = NULL;
  gpgsm->io_cbs.remove = NULL;
  gpgsm->io_cbs.event = NULL;
  gpgsm->io_cbs.event_priv = NULL;

#if !USE_DESCRIPTOR_PASSING
  if (_gpgme_io_pipe (fds, 0) < 0)
    {
      err = gpg_error_from_syserror ();
      goto leave;
    }
  gpgsm->input_cb.fd = fds[1];
  gpgsm->input_cb.server_fd = fds[0];

  if (_gpgme_io_pipe (fds, 1) < 0)
    {
      err = gpg_error_from_syserror ();
      goto leave;
    }
  gpgsm->output_cb.fd = fds[0];
  gpgsm->output_cb.server_fd = fds[1];

  if (_gpgme_io_pipe (fds, 0) < 0)
    {
      err = gpg_error_from_syserror ();
      goto leave;
    }
  gpgsm->message_cb.fd = fds[1];
  gpgsm->message_cb.server_fd = fds[0];

  child_fds[0] = gpgsm->input_cb.server_fd;
  child_fds[1] = gpgsm->output_cb.server_fd;
  child_fds[2] = gpgsm->message_cb.server_fd;
  child_fds[3] = -1;
#endif

  argc = 0;
  argv[argc++] = "gpgsm";
  if (home_dir)
    {
      argv[argc++] = "--homedir";
      argv[argc++] = home_dir;
    }
  argv[argc++] = "--server";
  argv[argc++] = NULL;

  err = assuan_new_ext (&gpgsm->assuan_ctx, GPG_ERR_SOURCE_GPGME,
			&_gpgme_assuan_malloc_hooks, _gpgme_assuan_log_cb,
			NULL);
  if (err)
    goto leave;
  assuan_ctx_set_system_hooks (gpgsm->assuan_ctx, &_gpgme_assuan_system_hooks);

#if USE_DESCRIPTOR_PASSING
  err = assuan_pipe_connect
    (gpgsm->assuan_ctx, file_name ? file_name : _gpgme_get_gpgsm_path (),
     argv, NULL, NULL, NULL, ASSUAN_PIPE_CONNECT_FDPASSING);
#else
  {
    assuan_fd_t achild_fds[4];
    int i;

    /* For now... */
    for (i = 0; i < 4; i++)
      achild_fds[i] = (assuan_fd_t) child_fds[i];

    err = assuan_pipe_connect
      (gpgsm->assuan_ctx, file_name ? file_name : _gpgme_get_gpgsm_path (),
       argv, achild_fds, NULL, NULL, 0);

    /* For now... */
    for (i = 0; i < 4; i++)
      child_fds[i] = (int) achild_fds[i];
  }

  /* On Windows, handles are inserted in the spawned process with
     DuplicateHandle, and child_fds contains the server-local names
     for the inserted handles when assuan_pipe_connect returns.  */
  if (!err)
    {
      /* Note: We don't use _gpgme_io_fd2str here.  On W32 the
	 returned handles are real W32 system handles, not whatever
	 GPGME uses internally (which may be a system handle, a C
	 library handle or a GLib/Qt channel.  Confusing, yes, but
	 remember these are server-local names, so they are not part
	 of GPGME at all.  */
      snprintf (gpgsm->input_cb.server_fd_str,
		sizeof gpgsm->input_cb.server_fd_str, "%d", child_fds[0]);
      snprintf (gpgsm->output_cb.server_fd_str,
		sizeof gpgsm->output_cb.server_fd_str, "%d", child_fds[1]);
      snprintf (gpgsm->message_cb.server_fd_str,
		sizeof gpgsm->message_cb.server_fd_str, "%d", child_fds[2]);
    }
#endif
  if (err)
    goto leave;

  err = _gpgme_getenv ("DISPLAY", &dft_display);
  if (err)
    goto leave;
  if (dft_display)
    {
      if (asprintf (&optstr, "OPTION display=%s", dft_display) < 0)
        {
	  free (dft_display);
	  err = gpg_error_from_syserror ();
	  goto leave;
	}
      free (dft_display);

      err = assuan_transact (gpgsm->assuan_ctx, optstr, NULL, NULL, NULL,
			     NULL, NULL, NULL);
      free (optstr);
      if (err)
	goto leave;
    }

  if (isatty (1))
    {
      int rc;

      rc = ttyname_r (1, dft_ttyname, sizeof (dft_ttyname));
      if (rc)
	{
	  err = gpg_error_from_errno (rc);
	  goto leave;
	}
      else
	{
	  if (asprintf (&optstr, "OPTION ttyname=%s", dft_ttyname) < 0)
	    {
	      err = gpg_error_from_syserror ();
	      goto leave;
	    }
	  err = assuan_transact (gpgsm->assuan_ctx, optstr, NULL, NULL, NULL,
				 NULL, NULL, NULL);
	  free (optstr);
	  if (err)
	    goto leave;

	  err = _gpgme_getenv ("TERM", &dft_ttytype);
	  if (err)
	    goto leave;
	  if (dft_ttytype)
	    {
	      if (asprintf (&optstr, "OPTION ttytype=%s", dft_ttytype) < 0)
		{
		  free (dft_ttytype);
		  err = gpg_error_from_syserror ();
		  goto leave;
		}
	      free (dft_ttytype);

	      err = assuan_transact (gpgsm->assuan_ctx, optstr, NULL, NULL,
				     NULL, NULL, NULL, NULL);
	      free (optstr);
	      if (err)
		goto leave;
	    }
	}
    }

  /* Ask gpgsm to enable the audit log support.  */
  if (!err)
    {
      err = assuan_transact (gpgsm->assuan_ctx, "OPTION enable-audit-log=1",
                             NULL, NULL, NULL, NULL, NULL, NULL);
      if (gpg_err_code (err) == GPG_ERR_UNKNOWN_OPTION)
        err = 0; /* This is an optional feature of gpgsm.  */
    }


#ifdef HAVE_W32_SYSTEM
  /* Under Windows we need to use AllowSetForegroundWindow.  Tell
     gpgsm to tell us when it needs it.  */
  if (!err)
    {
      err = assuan_transact (gpgsm->assuan_ctx, "OPTION allow-pinentry-notify",
                             NULL, NULL, NULL, NULL, NULL, NULL);
      if (gpg_err_code (err) == GPG_ERR_UNKNOWN_OPTION)
        err = 0; /* This is a new feature of gpgsm.  */
    }
#endif /*HAVE_W32_SYSTEM*/

#if !USE_DESCRIPTOR_PASSING
  if (!err
      && (_gpgme_io_set_close_notify (gpgsm->input_cb.fd,
				      close_notify_handler, gpgsm)
	  || _gpgme_io_set_close_notify (gpgsm->output_cb.fd,
					 close_notify_handler, gpgsm)
	  || _gpgme_io_set_close_notify (gpgsm->message_cb.fd,
					 close_notify_handler, gpgsm)))
    {
      err = gpg_error (GPG_ERR_GENERAL);
      goto leave;
    }
#endif

 leave:
  /* Close the server ends of the pipes (because of this, we must use
     the stored server_fd_str in the function start).  Our ends are
     closed in gpgsm_release().  */
#if !USE_DESCRIPTOR_PASSING
  if (gpgsm->input_cb.server_fd != -1)
    _gpgme_io_close (gpgsm->input_cb.server_fd);
  if (gpgsm->output_cb.server_fd != -1)
    _gpgme_io_close (gpgsm->output_cb.server_fd);
  if (gpgsm->message_cb.server_fd != -1)
    _gpgme_io_close (gpgsm->message_cb.server_fd);
#endif

  if (err)
    gpgsm_release (gpgsm);
  else
    *engine = gpgsm;

  return err;
}
Пример #6
0
static gpgme_error_t
gpgsm_new(void **engine, const char *file_name, const char *home_dir)
{
    gpgme_error_t err = 0;
    engine_gpgsm_t gpgsm;
    const char *argv[5];
    int argc;
#if !USE_DESCRIPTOR_PASSING
    int fds[2];
    int child_fds[4];
#endif
    char *dft_display = NULL;
    char dft_ttyname[64];
    char *dft_ttytype = NULL;
    char *optstr;

    gpgsm = calloc(1, sizeof * gpgsm);
    if(!gpgsm)
        return gpg_error_from_errno(errno);

    gpgsm->status_cb.fd = -1;
    gpgsm->status_cb.dir = 1;
    gpgsm->status_cb.tag = 0;
    gpgsm->status_cb.data = gpgsm;

    gpgsm->input_cb.fd = -1;
    gpgsm->input_cb.dir = 0;
    gpgsm->input_cb.tag = 0;
    gpgsm->input_cb.server_fd = -1;
    gpgsm->output_cb.fd = -1;
    gpgsm->output_cb.dir = 1;
    gpgsm->output_cb.tag = 0;
    gpgsm->output_cb.server_fd = -1;
    gpgsm->message_cb.fd = -1;
    gpgsm->message_cb.dir = 0;
    gpgsm->message_cb.tag = 0;
    gpgsm->message_cb.server_fd = -1;

    gpgsm->status.fnc = 0;
    gpgsm->colon.fnc = 0;
    gpgsm->colon.attic.line = 0;
    gpgsm->colon.attic.linesize = 0;
    gpgsm->colon.attic.linelen = 0;
    gpgsm->colon.any = 0;

    gpgsm->io_cbs.add = NULL;
    gpgsm->io_cbs.add_priv = NULL;
    gpgsm->io_cbs.remove = NULL;
    gpgsm->io_cbs.event = NULL;
    gpgsm->io_cbs.event_priv = NULL;

#if !USE_DESCRIPTOR_PASSING
    if(_gpgme_io_pipe(fds, 0) < 0)
    {
        err = gpg_error_from_errno(errno);
        goto leave;
    }
    gpgsm->input_cb.fd = fds[1];
    gpgsm->input_cb.server_fd = fds[0];

    if(_gpgme_io_pipe(fds, 1) < 0)
    {
        err = gpg_error_from_errno(errno);
        goto leave;
    }
    gpgsm->output_cb.fd = fds[0];
    gpgsm->output_cb.server_fd = fds[1];

    if(_gpgme_io_pipe(fds, 0) < 0)
    {
        err = gpg_error_from_errno(errno);
        goto leave;
    }
    gpgsm->message_cb.fd = fds[1];
    gpgsm->message_cb.server_fd = fds[0];

    child_fds[0] = gpgsm->input_cb.server_fd;
    child_fds[1] = gpgsm->output_cb.server_fd;
    child_fds[2] = gpgsm->message_cb.server_fd;
    child_fds[3] = -1;
#endif

    argc = 0;
    argv[argc++] = "gpgsm";
    if(home_dir)
    {
        argv[argc++] = "--homedir";
        argv[argc++] = home_dir;
    }
    argv[argc++] = "--server";
    argv[argc++] = NULL;

#if USE_DESCRIPTOR_PASSING
    err = assuan_pipe_connect_ext
          (&gpgsm->assuan_ctx, file_name ? file_name : _gpgme_get_gpgsm_path(),
           argv, NULL, NULL, NULL, 1);
#else
    err = assuan_pipe_connect
          (&gpgsm->assuan_ctx, file_name ? file_name : _gpgme_get_gpgsm_path(),
           argv, child_fds);
#endif
    if(err)
        goto leave;

    err = _gpgme_getenv("DISPLAY", &dft_display);
    if(err)
        goto leave;
    if(dft_display)
    {
        if(asprintf(&optstr, "OPTION display=%s", dft_display) < 0)
        {
            free(dft_display);
            err = gpg_error_from_errno(errno);
            goto leave;
        }
        free(dft_display);

        err = assuan_transact(gpgsm->assuan_ctx, optstr, NULL, NULL, NULL,
                              NULL, NULL, NULL);
        free(optstr);
        if(err)
        {
            err = map_assuan_error(err);
            goto leave;
        }
    }

    if(isatty(1))
    {
        if(ttyname_r(1, dft_ttyname, sizeof(dft_ttyname)))
        {
            err = gpg_error_from_errno(errno);
            goto leave;
        }
        else
        {
            if(asprintf(&optstr, "OPTION ttyname=%s", dft_ttyname) < 0)
            {
                err = gpg_error_from_errno(errno);
                goto leave;
            }
            err = assuan_transact(gpgsm->assuan_ctx, optstr, NULL, NULL, NULL,
                                  NULL, NULL, NULL);
            free(optstr);
            if(err)
            {
                err = map_assuan_error(err);
                goto leave;
            }

            err = _gpgme_getenv("TERM", &dft_ttytype);
            if(err)
                goto leave;
            if(dft_ttytype)
            {
                if(asprintf(&optstr, "OPTION ttytype=%s", dft_ttytype) < 0)
                {
                    free(dft_ttytype);
                    err = gpg_error_from_errno(errno);
                    goto leave;
                }
                free(dft_ttytype);

                err = assuan_transact(gpgsm->assuan_ctx, optstr, NULL, NULL,
                                      NULL, NULL, NULL, NULL);
                free(optstr);
                if(err)
                {
                    err = map_assuan_error(err);
                    goto leave;
                }
            }
        }
    }

#if !USE_DESCRIPTOR_PASSING
    if(!err
            && (_gpgme_io_set_close_notify(gpgsm->input_cb.fd,
                                           close_notify_handler, gpgsm)
                || _gpgme_io_set_close_notify(gpgsm->output_cb.fd,
                        close_notify_handler, gpgsm)
                || _gpgme_io_set_close_notify(gpgsm->message_cb.fd,
                        close_notify_handler, gpgsm)))
    {
        err = gpg_error(GPG_ERR_GENERAL);
        goto leave;
    }
#endif

leave:
    /* Close the server ends of the pipes.  Our ends are closed in
       gpgsm_release().  */
#if !USE_DESCRIPTOR_PASSING
    if(gpgsm->input_cb.server_fd != -1)
        _gpgme_io_close(gpgsm->input_cb.server_fd);
    if(gpgsm->output_cb.server_fd != -1)
        _gpgme_io_close(gpgsm->output_cb.server_fd);
    if(gpgsm->message_cb.server_fd != -1)
        _gpgme_io_close(gpgsm->message_cb.server_fd);
#endif

    if(err)
        gpgsm_release(gpgsm);
    else
        *engine = gpgsm;

    return err;
}