Esempio n. 1
0
Pin::Pin() {
  gpg_error_t res;
  pid_t pid;
  int flags;
  const char *argv[2];

  gpg_err_init();
  res = assuan_new (&ctx);
  if(res)
    throw runtime_error(strprintf("pinentry initialisation: %s", gpg_strerror(res)));

  assuan_set_assuan_log_prefix("Pin: ");

  // needed esp. for ncurses
  lang = strprintf("OPTION lc-ctype=%s",getenv("LANG"));
  tty = strprintf("OPTION ttyname=%s",getenv("TTY"));

  flags = 0x0;
  argv[0] = "bitcoind"; // fake argv
  argv[1] = NULL;
  res = assuan_pipe_connect (ctx, SECURE_EXEC_PATH"/pinentry",
			     argv, NULL, NULL, NULL, flags);
  if(res)
    throw runtime_error(strprintf("pinentry pipe forking: %s", gpg_strerror(res)));

  pid = assuan_get_pid(ctx);
  if(pid == ASSUAN_INVALID_PID)
    throw runtime_error(strprintf("pinentry not running: %s", gpg_strerror(res)));

  cmd(tty.c_str());
  cmd(lang.c_str());
}
Esempio n. 2
0
static gpg_error_t
export_cert (char *fpr, struct cert *cert)
{
  gpg_error_t err;
  assuan_context_t ctx;
  const char *argv[] = { "gpgsm", "--server", NULL };
#define COMMANDLINELEN 80
  char cmd[COMMANDLINELEN];
  struct export_hook exp;

  err = assuan_new (&ctx);
  if (err)
    {
      DEBUG (DBG_CRIT, "failed to allocate assuan context: %s",
	     gpg_strerror (err));
      return err;
    }

  err = assuan_pipe_connect (ctx, get_gpgsm_path (), argv, NULL,
			     NULL, NULL, 128);
  if (err)
    {
      assuan_release (ctx);
      DEBUG (DBG_CRIT, "spawning %s\n", get_gpgsm_path ());
      return err;
    }

  exp.buffer = NULL;
  exp.buffer_len = 0;
  exp.buffer_size = 0;

  snprintf (cmd, sizeof (cmd), "EXPORT --data -- %s\n", cert->fpr);
  err = assuan_transact (ctx, cmd, export_cert_cb, &exp,
			 NULL, NULL, NULL, NULL);
  assuan_release (ctx);

  if (!err)
    {
      cert->cert_der = exp.buffer;
      cert->cert_der_len = exp.buffer_len;
    }
#ifdef COMPAT_FALLBACK
  else if (gpg_err_code (err) == GPG_ERR_ASS_NO_OUTPUT)
    {
      /* For compatibility with GPGSM 2.0.0, we fall back to a work
	 around in that case.  */
      if (cert->cert_der)
	{
	  free (cert->cert_der);
	  cert->cert_der = NULL;
	}
      err = export_cert_compat (fpr, cert);
    }
#endif

  if (!err)
    err = scute_agent_is_trusted (fpr, &cert->is_trusted);

  return err;
}
Pinentry::Pinentry() {
  gpg_error_t error;
  if ((error = assuan_new(&_pinentry)) != GPG_ERR_NO_ERROR)
    throw std::system_error(error, gpg_category());
  const char *argv[] = {"pinentry", nullptr};
  if ((error = assuan_pipe_connect(_pinentry, "/usr/bin/pinentry", argv, nullptr,
          nullptr, nullptr, 0)) != GPG_ERR_NO_ERROR)
    throw std::system_error(error, gpg_category());
}
Esempio n. 4
0
/* Invoke SEARCH_CB for each certificate found using assuan connection
   CTX to GPGSM.  */
gpg_error_t
scute_gpgsm_search_certs_by_fpr (const char *fpr,
				 cert_search_cb_t search_cb,
				 void *search_cb_hook)
{
  gpg_error_t err;
  assuan_context_t ctx;
  const char *argv[] = { "gpgsm", "--server", NULL };
  struct search_ctx_by_field search;

  err = assuan_new (&ctx);
  if (err)
    {
      DEBUG (DBG_CRIT, "failed to allocate assuan context: %s",
	     gpg_strerror (err));
      return err;
    }

  err = assuan_pipe_connect (ctx, get_gpgsm_path (), argv, NULL,
			     NULL, NULL, 128);
  if (err)
    {
      assuan_release (ctx);
      DEBUG (DBG_CRIT, "failed to spawn %s\n", get_gpgsm_path ());
      return err;
    }

  search.field = SEARCH_BY_FPR;
  search.pattern = fpr;
  search.search_cb = search_cb;
  search.search_cb_hook = search_cb_hook;

  err = scute_gpgsm_search_certs (ctx, &search_certs_by_field, &search);
  assuan_release (ctx);
  return err;
}
Esempio n. 5
0
int ask_and_verify_pin_code(struct sc_pkcs15_card *p15card,
			    struct sc_pkcs15_object *pin)
{
	int r;
	size_t len;
	const char *argv[3];
	const char *pgmname = PIN_ENTRY;
	ASSUAN_CONTEXT ctx;
	char buf[500];
	char errtext[100];
	struct entry_parm_s parm;
	struct sc_pkcs15_pin_info *pinfo = (struct sc_pkcs15_pin_info *) pin->data;
	
	argv[0] = pgmname;
	argv[1] = NULL;
	
	r = assuan_pipe_connect(&ctx, pgmname, (char **) argv, NULL);
	if (r) {
		printf("Can't connect to the PIN entry module: %s\n",
		       assuan_strerror((AssuanError) r));
		goto err;
	}
	sprintf(buf, "SETDESC Enter PIN [%s] for digital signing  ", pin->label);
	r = assuan_transact(ctx, buf, NULL, NULL, NULL, NULL, NULL, NULL);
	if (r) {
		printf("SETDESC: %s\n", assuan_strerror((AssuanError) r));
		goto err;
	}
	errtext[0] = 0;
	while (1) {
		if (errtext[0]) {
			sprintf(buf, "SETERROR %s", errtext);
			r = assuan_transact(ctx, buf, NULL, NULL, NULL, NULL, NULL, NULL);
			errtext[0] = 0;
		}
		parm.lines = 0;
		parm.size = sizeof(buf);
		parm.buffer = buf;
		r = assuan_transact(ctx, "GETPIN", getpin_cb, &parm, NULL, NULL, NULL, NULL);
		if (r == ASSUAN_Canceled) {
			assuan_disconnect(ctx);
			return -2;
		}
		if (r) {
			printf("GETPIN: %s\n", assuan_strerror((AssuanError) r));
			goto err;
		}
		len = strlen(buf);
		if (len < pinfo->min_length) {
			sprintf(errtext, "PIN code too short, min. %lu digits", (unsigned long) pinfo->min_length);
			continue;
		}
		if (len > pinfo->max_length) {
			sprintf(errtext, "PIN code too long, max. %lu digits", (unsigned long) pinfo->max_length);
			continue;
		}
		r = sc_pkcs15_verify_pin(p15card, pinfo, (const u8 *) buf, strlen(buf));
		switch (r) {
		case SC_ERROR_PIN_CODE_INCORRECT:
			sprintf(errtext, "PIN code incorrect (%d %s left)",
			       pinfo->tries_left, pinfo->tries_left == 1 ?
			       "try" : "tries");
			break;
		case 0:
			break;
		default:
			goto err;
		}
		if (r == 0)
			break;
	}

	assuan_disconnect(ctx);	
	return 0;
err:	
	assuan_disconnect(ctx);
	return -1;
}
Esempio n. 6
0
/* 
 
     M A I N

*/
int 
main (int argc, char **argv)
{
  int last_argc = -1;
  assuan_context_t ctx;
  gpg_error_t err;
  int no_close_fds[2];
  const char *arglist[10];
  int is_server = 0;
  int with_exec = 0;
  char *fname = prepend_srcdir ("motd");

  if (argc)
    {
      log_set_prefix (*argv);
      argc--; argv++;
    }
  while (argc && last_argc != argc )
    {
      last_argc = argc;
      if (!strcmp (*argv, "--help"))
        {
          puts (
"usage: ./fdpassing [options]\n"
"\n"
"Options:\n"
"  --verbose      Show what is going on\n"
"  --with-exec    Exec the child.  Default is just a fork\n"
);
          exit (0);
        }
      if (!strcmp (*argv, "--verbose"))
        {
          verbose = 1;
          argc--; argv++;
        }
      else if (!strcmp (*argv, "--debug"))
        {
          verbose = debug = 1;
          argc--; argv++;
        }
      else if (!strcmp (*argv, "--server"))
        {
          is_server = 1;
          argc--; argv++;
        }
      else if (!strcmp (*argv, "--with-exec"))
        {
          with_exec = 1;
          argc--; argv++;
        }
    }


  assuan_set_assuan_log_prefix (log_prefix);

  if (is_server)
    {
      server ();
      log_info ("server finished\n");
    }
  else
    {
      const char *loc;

      no_close_fds[0] = 2;
      no_close_fds[1] = -1;
      if (with_exec)
        {
          arglist[0] = "fdpassing";
          arglist[1] = "--server";
          arglist[2] = verbose? "--verbose":NULL;
          arglist[3] = NULL;
        }

      err = assuan_new (&ctx);
      if (err)
	log_fatal ("assuan_new failed: %s\n", gpg_strerror (err));

      err = assuan_pipe_connect (ctx, with_exec? "./fdpassing":NULL,
				 with_exec ? arglist : &loc,
				 no_close_fds, NULL, NULL, 1);
      if (err)
        {
          log_error ("assuan_pipe_connect failed: %s\n", gpg_strerror (err));
          return 1;
        }
      
      if (!with_exec && loc[0] == 's')
        {
          server ();
          log_info ("server finished\n");
        }
      else
        {
          if (client (ctx, fname)) 
            {
              log_info ("waiting for server to terminate...\n");
              assuan_release (ctx);
            }
          log_info ("client finished\n");
        }
    }

  return errorcount ? 1 : 0;
}
Esempio n. 7
0
/* Try to connect to the dirmngr via socket or fork it off and work by
   pipes.  Handle the server's initial greeting */
static assuan_context_t
start_dirmngr (int only_daemon)
{
  int rc;
  char *infostr, *p;
  assuan_context_t ctx;
  int try_default = 0;

  infostr = opt.force_pipe_server? NULL : getenv ("DIRMNGR_INFO");
  if (only_daemon && (!infostr || !*infostr))
    {
      infostr = xstrdup (dirmngr_socket_name ());
      try_default = 1;
    }

  rc = assuan_new (&ctx);
  if (rc)
    {
      log_error (_("failed to allocate assuan context: %s\n"),
                 gpg_strerror (rc));
      return NULL;
    }

  if (!infostr || !*infostr)
    {
      const char *pgmname;
      const char *argv[3];
      int no_close_list[3];
      int i;

      if (only_daemon)
        {
          log_error (_("apparently no running dirmngr\n"));
          return NULL;
        }

      if (opt.verbose)
        log_info (_("no running dirmngr - starting one\n"));

      if (!opt.dirmngr_program || !*opt.dirmngr_program)
        opt.dirmngr_program = "./dirmngr";
      if ( !(pgmname = strrchr (opt.dirmngr_program, '/')))
        pgmname = opt.dirmngr_program;
      else
        pgmname++;

      argv[0] = pgmname;
      argv[1] = "--server";
      argv[2] = NULL;

      i=0;
      if (log_get_fd () != -1)
        no_close_list[i++] = assuan_fd_from_posix_fd (log_get_fd ());
      no_close_list[i++] = assuan_fd_from_posix_fd (es_fileno (es_stderr));
      no_close_list[i] = -1;

      /* Connect to the agent and perform initial handshaking.  */
      rc = assuan_pipe_connect (ctx, opt.dirmngr_program, argv,
                                no_close_list, NULL, NULL, 0);
    }
  else /* Connect to a daemon.  */
    {
      int prot;
      int pid;

      infostr = xstrdup (infostr);
      if (!try_default && *infostr)
        {
          if ( !(p = strchr (infostr, ':')) || p == infostr)
            {
              log_error (_("malformed DIRMNGR_INFO environment variable\n"));
              xfree (infostr);
              if (only_daemon)
                return NULL;
              /* Try again by starting a new instance.  */
              opt.force_pipe_server = 1;
              return start_dirmngr (0);
            }
          *p++ = 0;
          pid = atoi (p);
          while (*p && *p != ':')
            p++;
          prot = *p? atoi (p+1) : 0;
          if (prot != 1)
            {
              log_error (_("dirmngr protocol version %d is not supported\n"),
                         prot);
              xfree (infostr);
              if (only_daemon)
                return NULL;
              opt.force_pipe_server = 1;
              return start_dirmngr (0);
            }
        }
      else
        pid = -1;

      rc = assuan_socket_connect (ctx, infostr, pid, 0);
      xfree (infostr);
      if (gpg_err_code(rc) == GPG_ERR_ASS_CONNECT_FAILED && !only_daemon)
        {
          log_error (_("can't connect to the dirmngr - trying fall back\n"));
          opt.force_pipe_server = 1;
          return start_dirmngr (0);
        }
    }

  if (rc)
    {
      assuan_release (ctx);
      log_error (_("can't connect to the dirmngr: %s\n"),
                 gpg_strerror (rc));
      return NULL;
    }

  return ctx;
}
Esempio n. 8
0
/* Fire up a new GPG.  Handle the server's initial greeting.  Returns
   0 on success and stores the assuan context at R_CTX.  */
static gpg_error_t
start_gpg (ctrl_t ctrl, const char *gpg_program, strlist_t gpg_arguments,
           int input_fd, int output_fd, assuan_context_t *r_ctx)
{
  gpg_error_t err;
  assuan_context_t ctx = NULL;
  const char *pgmname;
  const char **argv;
  assuan_fd_t no_close_list[5];
  int i;
  char line[ASSUAN_LINELENGTH];

  (void)ctrl;

  *r_ctx = NULL;

  err = assuan_new (&ctx);
  if (err)
    {
      log_error ("can't allocate assuan context: %s\n", gpg_strerror (err));
      return err;
    }

  /* The first time we are used, intialize the gpg_program variable.  */
  if ( !gpg_program || !*gpg_program )
    gpg_program = gnupg_module_name (GNUPG_MODULE_NAME_GPG);

  /* Compute argv[0].  */
  if ( !(pgmname = strrchr (gpg_program, '/')))
    pgmname = gpg_program;
  else
    pgmname++;

  if (fflush (NULL))
    {
      err = my_error_from_syserror ();
      log_error ("error flushing pending output: %s\n", gpg_strerror (err));
      return err;
    }

  argv = xtrycalloc (strlist_length (gpg_arguments) + 3, sizeof *argv);
  if (argv == NULL)
    {
      err = my_error_from_syserror ();
      return err;
    }
  i = 0;
  argv[i++] = pgmname;
  argv[i++] = "--server";
  for (; gpg_arguments; gpg_arguments = gpg_arguments->next)
    argv[i++] = gpg_arguments->d;
  argv[i++] = NULL;

  i = 0;
  if (log_get_fd () != -1)
    no_close_list[i++] = assuan_fd_from_posix_fd (log_get_fd ());
  no_close_list[i++] = assuan_fd_from_posix_fd (fileno (stderr));
  if (input_fd != -1)
    no_close_list[i++] = assuan_fd_from_posix_fd (input_fd);
  if (output_fd != -1)
    no_close_list[i++] = assuan_fd_from_posix_fd (output_fd);
  no_close_list[i] = ASSUAN_INVALID_FD;

  /* Connect to GPG and perform initial handshaking.  */
  err = assuan_pipe_connect (ctx, gpg_program, argv, no_close_list,
			     NULL, NULL, 0);
  if (err)
    {
      assuan_release (ctx);
      log_error ("can't connect to GPG: %s\n", gpg_strerror (err));
      return gpg_error (GPG_ERR_NO_ENGINE);
    }

  if (input_fd != -1)
    {
      snprintf (line, sizeof line, "INPUT FD=%d", input_fd);
      err = assuan_transact (ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
      if (err)
        {
          assuan_release (ctx);
          log_error ("error sending INPUT command: %s\n", gpg_strerror (err));
          return err;
        }
    }

  if (output_fd != -1)
    {
      snprintf (line, sizeof line, "OUTPUT FD=%d", output_fd);
      err = assuan_transact (ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
      if (err)
        {
          assuan_release (ctx);
          log_error ("error sending OUTPUT command: %s\n", gpg_strerror (err));
          return err;
        }
    }

  *r_ctx = ctx;
  return 0;
}
Esempio n. 9
0
static gpgme_error_t
g13_new (void **engine, const char *file_name, const char *home_dir)
{
  gpgme_error_t err = 0;
  engine_g13_t g13;
  const char *pgmname;
  int argc;
  const char *argv[5];
  char *dft_display = NULL;
  char dft_ttyname[64];
  char *dft_ttytype = NULL;
  char *optstr;

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

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

  pgmname = file_name ? file_name : _gpgme_get_default_g13_name ();
  argc = 0;
  argv[argc++] = _gpgme_get_basename (pgmname);
  if (home_dir)
    {
      argv[argc++] = "--homedir";
      argv[argc++] = home_dir;
    }
  argv[argc++] = "--server";
  argv[argc++] = NULL;

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

#if USE_DESCRIPTOR_PASSING
  err = assuan_pipe_connect (g13->assuan_ctx, pgmname, argv,
                             NULL, NULL, NULL, ASSUAN_PIPE_CONNECT_FDPASSING);
#else
  err = assuan_pipe_connect (g13->assuan_ctx, pgmname, argv,
                             NULL, NULL, NULL, 0);
#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 (g13->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));

      /* Even though isatty() returns 1, ttyname_r() may fail in many
	 ways, e.g., when /dev/pts is not accessible under chroot.  */
      if (!rc)
	{
	  if (asprintf (&optstr, "OPTION ttyname=%s", dft_ttyname) < 0)
	    {
	      err = gpg_error_from_syserror ();
	      goto leave;
	    }
	  err = assuan_transact (g13->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 (g13->assuan_ctx, optstr, NULL, NULL,
				     NULL, NULL, NULL, NULL);
	      free (optstr);
	      if (err)
		goto leave;
	    }
	}
    }

#ifdef HAVE_W32_SYSTEM
  /* Under Windows we need to use AllowSetForegroundWindow.  Tell
     g13 to tell us when it needs it.  */
  if (!err)
    {
      err = assuan_transact (g13->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 g13.  */
    }
#endif /*HAVE_W32_SYSTEM*/

 leave:

  if (err)
    g13_release (g13);
  else
    *engine = g13;

  return err;
}
Esempio n. 10
0
/* Fork off the pin entry if this has not already been done.  Note,
   that this function must always be used to aquire the lock for the
   pinentry - we will serialize _all_ pinentry calls.
 */
static int
start_pinentry (ctrl_t ctrl)
{
  int rc = 0;
  const char *pgmname;
  assuan_context_t ctx;
  const char *argv[5];
  int no_close_list[3];
  int i;
  const char *tmpstr;
  unsigned long pinentry_pid;
  const char *value;
  struct timespec abstime;
  int err;

  npth_clock_gettime (&abstime);
  abstime.tv_sec += LOCK_TIMEOUT;
  err = npth_mutex_timedlock (&entry_lock, &abstime);
  if (err)
    {
      if (err == ETIMEDOUT)
	rc = gpg_error (GPG_ERR_TIMEOUT);
      else
	rc = gpg_error_from_errno (rc);
      log_error (_("failed to acquire the pinentry lock: %s\n"),
                 gpg_strerror (rc));
      return rc;
    }

  entry_owner = ctrl;

  if (entry_ctx)
    return 0;

  if (opt.verbose)
    log_info ("starting a new PIN Entry\n");

#ifdef HAVE_W32_SYSTEM
  fflush (stdout);
  fflush (stderr);
#endif
  if (fflush (NULL))
    {
#ifndef HAVE_W32_SYSTEM
      gpg_error_t tmperr = gpg_error (gpg_err_code_from_errno (errno));
#endif
      log_error ("error flushing pending output: %s\n", strerror (errno));
      /* At least Windows XP fails here with EBADF.  According to docs
         and Wine an fflush(NULL) is the same as _flushall.  However
         the Wime implementaion does not flush stdin,stdout and stderr
         - see above.  Lets try to ignore the error. */
#ifndef HAVE_W32_SYSTEM
      return unlock_pinentry (tmperr);
#endif
    }

  if (!opt.pinentry_program || !*opt.pinentry_program)
    opt.pinentry_program = gnupg_module_name (GNUPG_MODULE_NAME_PINENTRY);
  pgmname = opt.pinentry_program;
  if ( !(pgmname = strrchr (opt.pinentry_program, '/')))
    pgmname = opt.pinentry_program;
  else
    pgmname++;

  /* OS X needs the entire file name in argv[0], so that it can locate
     the resource bundle.  For other systems we stick to the usual
     convention of supplying only the name of the program.  */
#ifdef __APPLE__
  argv[0] = opt.pinentry_program;
#else /*!__APPLE__*/
  argv[0] = pgmname;
#endif /*__APPLE__*/

  if (!opt.keep_display
      && (value = session_env_getenv (ctrl->session_env, "DISPLAY")))
    {
      argv[1] = "--display";
      argv[2] = value;
      argv[3] = NULL;
    }
  else
    argv[1] = NULL;

  i=0;
  if (!opt.running_detached)
    {
      if (log_get_fd () != -1)
        no_close_list[i++] = assuan_fd_from_posix_fd (log_get_fd ());
      no_close_list[i++] = assuan_fd_from_posix_fd (fileno (stderr));
    }
  no_close_list[i] = -1;

  rc = assuan_new (&ctx);
  if (rc)
    {
      log_error ("can't allocate assuan context: %s\n", gpg_strerror (rc));
      return rc;
    }
  /* We don't want to log the pinentry communication to make the logs
     easier to read.  We might want to add a new debug option to enable
     pinentry logging.  */
#ifdef ASSUAN_NO_LOGGING
  assuan_set_flag (ctx, ASSUAN_NO_LOGGING, 1);
#endif

  /* Connect to the pinentry and perform initial handshaking.  Note
     that atfork is used to change the environment for pinentry.  We
     start the server in detached mode to suppress the console window
     under Windows.  */
  rc = assuan_pipe_connect (ctx, opt.pinentry_program, argv,
			    no_close_list, atfork_cb, ctrl,
			    ASSUAN_PIPE_CONNECT_DETACHED);
  if (rc)
    {
      log_error ("can't connect to the PIN entry module '%s': %s\n",
                 opt.pinentry_program, gpg_strerror (rc));
      assuan_release (ctx);
      return unlock_pinentry (gpg_error (GPG_ERR_NO_PIN_ENTRY));
    }
  entry_ctx = ctx;

  if (DBG_ASSUAN)
    log_debug ("connection to PIN entry established\n");

  rc = assuan_transact (entry_ctx,
                        opt.no_grab? "OPTION no-grab":"OPTION grab",
                        NULL, NULL, NULL, NULL, NULL, NULL);
  if (rc)
    return unlock_pinentry (rc);

  value = session_env_getenv (ctrl->session_env, "GPG_TTY");
  if (value)
    {
      char *optstr;
      if (asprintf (&optstr, "OPTION ttyname=%s", value) < 0 )
	return unlock_pinentry (out_of_core ());
      rc = assuan_transact (entry_ctx, optstr, NULL, NULL, NULL, NULL, NULL,
			    NULL);
      xfree (optstr);
      if (rc)
	return unlock_pinentry (rc);
    }
  value = session_env_getenv (ctrl->session_env, "TERM");
  if (value)
    {
      char *optstr;
      if (asprintf (&optstr, "OPTION ttytype=%s", value) < 0 )
	return unlock_pinentry (out_of_core ());
      rc = assuan_transact (entry_ctx, optstr, NULL, NULL, NULL, NULL, NULL,
			    NULL);
      xfree (optstr);
      if (rc)
	return unlock_pinentry (rc);
    }
  if (ctrl->lc_ctype)
    {
      char *optstr;
      if (asprintf (&optstr, "OPTION lc-ctype=%s", ctrl->lc_ctype) < 0 )
	return unlock_pinentry (out_of_core ());
      rc = assuan_transact (entry_ctx, optstr, NULL, NULL, NULL, NULL, NULL,
			    NULL);
      xfree (optstr);
      if (rc)
	return unlock_pinentry (rc);
    }
  if (ctrl->lc_messages)
    {
      char *optstr;
      if (asprintf (&optstr, "OPTION lc-messages=%s", ctrl->lc_messages) < 0 )
	return unlock_pinentry (out_of_core ());
      rc = assuan_transact (entry_ctx, optstr, NULL, NULL, NULL, NULL, NULL,
			    NULL);
      xfree (optstr);
      if (rc)
	return unlock_pinentry (rc);
    }

  {
    /* Provide a few default strings for use by the pinentries.  This
       may help a pinentry to avoid implementing localization code.  */
    static struct { const char *key, *value; } tbl[] = {
      /* TRANSLATORS: These are labels for buttons etc used in
         Pinentries.  An underscore indicates that the next letter
         should be used as an accelerator.  Double the underscore for
         a literal one.  The actual to be translated text starts after
         the second vertical bar.  */
      { "ok",     N_("|pinentry-label|_OK") },
      { "cancel", N_("|pinentry-label|_Cancel") },
      { "prompt", N_("|pinentry-label|PIN:") },
      { NULL, NULL}
    };
    char *optstr;
    int idx;
    const char *s, *s2;

    for (idx=0; tbl[idx].key; idx++)
      {
        s = _(tbl[idx].value);
        if (*s == '|' && (s2=strchr (s+1,'|')))
          s = s2+1;
        if (asprintf (&optstr, "OPTION default-%s=%s", tbl[idx].key, s) < 0 )
          return unlock_pinentry (out_of_core ());
        assuan_transact (entry_ctx, optstr, NULL, NULL, NULL, NULL, NULL,
                         NULL);
        xfree (optstr);
      }
  }


  /* Tell the pinentry the name of a file it shall touch after having
     messed with the tty.  This is optional and only supported by
     newer pinentries and thus we do no error checking. */
  tmpstr = opt.pinentry_touch_file;
  if (tmpstr && !strcmp (tmpstr, "/dev/null"))
    tmpstr = NULL;
  else if (!tmpstr)
    tmpstr = get_agent_socket_name ();
  if (tmpstr)
    {
      char *optstr;

      if (asprintf (&optstr, "OPTION touch-file=%s", tmpstr ) < 0 )
        ;
      else
        {
          assuan_transact (entry_ctx, optstr, NULL, NULL, NULL, NULL, NULL,
                           NULL);
          xfree (optstr);
        }
    }


  /* Now ask the Pinentry for its PID.  If the Pinentry is new enough
     it will send the pid back and we will use an inquire to notify
     our client.  The client may answer the inquiry either with END or
     with CAN to cancel the pinentry. */
  rc = assuan_transact (entry_ctx, "GETINFO pid",
                        getinfo_pid_cb, &pinentry_pid,
                        NULL, NULL, NULL, NULL);
  if (rc)
    {
      log_info ("You may want to update to a newer pinentry\n");
      rc = 0;
    }
  else if (!rc && (pid_t)pinentry_pid == (pid_t)(-1))
    log_error ("pinentry did not return a PID\n");
  else
    {
      rc = agent_inq_pinentry_launched (ctrl, pinentry_pid);
      if (gpg_err_code (rc) == GPG_ERR_CANCELED
          || gpg_err_code (rc) == GPG_ERR_FULLY_CANCELED)
        return unlock_pinentry (gpg_err_make (GPG_ERR_SOURCE_DEFAULT,
                                              gpg_err_code (rc)));
      rc = 0;
    }

  return 0;
}
Esempio n. 11
0
int ask_user_consent(sc_card_t *card) {
    int res;
    struct stat buf;
    const char *argv[3];
    assuan_fd_t noclosefds[2];
    assuan_context_t ctx; 
    if ( (card==NULL) || (card->ctx==NULL)) return SC_ERROR_INVALID_ARGUMENTS;
    get_user_consent_env(card->ctx);
    res=stat(user_consent_app,&buf);
    if (res!=0) {
      /* TODO: check that pinentry file is executable */
      sc_debug(card->ctx,SC_LOG_DEBUG_NORMAL,"Invalid pinentry application: %s\n",user_consent_app);
      SC_FUNC_RETURN(card->ctx,SC_LOG_DEBUG_NORMAL,SC_ERROR_INVALID_ARGUMENTS);
    }
    argv[0]=user_consent_app;
    argv[1]=NULL;
    argv[2]=NULL;
    noclosefds[0]= fileno(stderr);
    noclosefds[1]= ASSUAN_INVALID_FD;
#ifdef HAVE_ASSUAN_2
    res = assuan_new(&ctx);
    if (res!=0) {
      sc_debug(card->ctx,SC_LOG_DEBUG_NORMAL,"Can't create the User Consent environment: %s\n",_gpg_error(res));
      SC_FUNC_RETURN(card->ctx,SC_LOG_DEBUG_NORMAL,SC_ERROR_INVALID_ARGUMENTS);
    }
    res = assuan_pipe_connect(ctx,user_consent_app,argv,noclosefds,NULL,NULL,0);
#else 
    res = assuan_pipe_connect(&ctx,user_consent_app,argv,0);
#endif
    if (res!=0) {
        sc_debug(card->ctx,SC_LOG_DEBUG_NORMAL,"Can't connect to the User Consent module: %s\n",_gpg_error(res));
        res=SC_ERROR_INVALID_ARGUMENTS; /* invalid or not available pinentry */
        goto exit;
    }
    res = assuan_transact(
       ctx, 
       "SETDESC Está a punto de realizar una firma electrónica con su clave de FIRMA del DNI electrónico.¿Desea permitir esta operación?", 
       NULL, NULL, NULL, NULL, NULL, NULL);
    if (res!=0) {
       sc_debug(card->ctx,SC_LOG_DEBUG_NORMAL,"SETDESC: %s\n", _gpg_error(res));
       res=SC_ERROR_CARD_CMD_FAILED; /* perhaps should use a better errcode */
       goto exit;
    }
    res = assuan_transact(ctx,"CONFIRM",NULL,NULL,NULL,NULL,NULL,NULL);
#ifdef HAVE_ASSUAN_1
    if (res == ASSUAN_Canceled) {
       sc_debug(card->ctx,SC_LOG_DEBUG_VERBOSE,"Sign cancelled by user");
       res= SC_ERROR_NOT_ALLOWED;
       goto exit;
    }
#endif
    if (res) {
       sc_debug(card->ctx,SC_LOG_DEBUG_NORMAL,"SETERROR: %s\n",_gpg_error(res));
       res=SC_ERROR_SECURITY_STATUS_NOT_SATISFIED;
     } else {
       res=SC_SUCCESS;
     }
exit:
#ifdef HAVE_ASSUAN_2
    assuan_release(ctx);
#else
    assuan_disconnect(ctx);
#endif
    SC_FUNC_RETURN(card->ctx,SC_LOG_DEBUG_NORMAL,res);
}
Esempio n. 12
0
/* Try to connect to the agent via socket or fork it off and work by
   pipes.  Handle the server's initial greeting.  Returns a new assuan
   context at R_CTX or an error code. */
gpg_error_t
start_new_gpg_agent (assuan_context_t *r_ctx,
                     gpg_err_source_t errsource,
                     const char *homedir,
                     const char *agent_program,
                     const char *opt_lc_ctype,
                     const char *opt_lc_messages,
                     session_env_t session_env,
                     int verbose, int debug,
                     gpg_error_t (*status_cb)(ctrl_t, int, ...),
                     ctrl_t status_cb_arg)
{
  /* If we ever failed to connect via a socket we will force the use
     of the pipe based server for the lifetime of the process.  */
  static int force_pipe_server = 0;

  gpg_error_t err = 0;
  char *infostr, *p;
  assuan_context_t ctx;
  int did_success_msg = 0;

  *r_ctx = NULL;

  err = assuan_new (&ctx);
  if (err)
    {
      log_error ("error allocating assuan context: %s\n", gpg_strerror (err));
      return err;
    }

 restart:
  infostr = force_pipe_server? NULL : getenv ("GPG_AGENT_INFO");
  if (!infostr || !*infostr)
    {
      char *sockname;
      const char *argv[3];
      pid_t pid;
      int excode;

      /* First check whether we can connect at the standard
         socket.  */
      sockname = make_filename (homedir, "S.gpg-agent", NULL);
      err = assuan_socket_connect (ctx, sockname, 0, 0);

      if (err)
        {
          /* With no success start a new server.  */
          if (!agent_program || !*agent_program)
            agent_program = gnupg_module_name (GNUPG_MODULE_NAME_AGENT);

          if (verbose)
            log_info (_("no running gpg-agent - starting '%s'\n"),
                      agent_program);

          if (status_cb)
            status_cb (status_cb_arg, STATUS_PROGRESS,
                       "starting_agent ? 0 0", NULL);

          if (fflush (NULL))
            {
              gpg_error_t tmperr = gpg_err_make (errsource,
                                                 gpg_err_code_from_syserror ());
              log_error ("error flushing pending output: %s\n",
                         strerror (errno));
              xfree (sockname);
	      assuan_release (ctx);
              return tmperr;
            }

          argv[0] = "--use-standard-socket-p";
          argv[1] = NULL;
          err = gnupg_spawn_process_fd (agent_program, argv, -1, -1, -1, &pid);
          if (err)
            log_debug ("starting '%s' for testing failed: %s\n",
                       agent_program, gpg_strerror (err));
          else if ((err = gnupg_wait_process (agent_program, pid, 1, &excode)))
            {
              if (excode == -1)
                log_debug ("running '%s' for testing failed (wait): %s\n",
                           agent_program, gpg_strerror (err));
            }
          gnupg_release_process (pid);

          if (!err && !excode)
            {
              /* If the agent has been configured for use with a
                 standard socket, an environment variable is not
                 required and thus we we can savely start the agent
                 here.  */
              lock_spawn_t lock;

              argv[0] = "--daemon";
              argv[1] = "--use-standard-socket";
              argv[2] = NULL;

              if (!(err = lock_spawning (&lock, homedir, "agent", verbose))
                  && assuan_socket_connect (ctx, sockname, 0, 0))
                {
                  err = gnupg_spawn_process_detached (agent_program, argv,NULL);
                  if (err)
                    log_error ("failed to start agent '%s': %s\n",
                               agent_program, gpg_strerror (err));
                  else
                    {
                      int i;

                      for (i=0; i < SECS_TO_WAIT_FOR_AGENT; i++)
                        {
                          if (verbose)
                            log_info (_("waiting for the agent "
                                        "to come up ... (%ds)\n"),
                                      SECS_TO_WAIT_FOR_AGENT - i);
                          gnupg_sleep (1);
                          err = assuan_socket_connect (ctx, sockname, 0, 0);
                          if (!err)
                            {
                              if (verbose)
                                {
                                  log_info (_("connection to agent "
                                              "established\n"));
                                  did_success_msg = 1;
                                }
                              break;
                            }
                        }
                    }
                }

              unlock_spawning (&lock, "agent");
            }
          else
            {
              /* If using the standard socket is not the default we
                 start the agent as a pipe server which gives us most
                 of the required features except for passphrase
                 caching etc.  */
              const char *pgmname;
              int no_close_list[3];
              int i;

              if ( !(pgmname = strrchr (agent_program, '/')))
                pgmname = agent_program;
              else
                pgmname++;

              argv[0] = pgmname;
              argv[1] = "--server";
              argv[2] = NULL;

              i=0;
              if (log_get_fd () != -1)
                no_close_list[i++] = assuan_fd_from_posix_fd (log_get_fd ());
              no_close_list[i++] = assuan_fd_from_posix_fd (fileno (stderr));
              no_close_list[i] = -1;

              /* Connect to the agent and perform initial handshaking. */
              err = assuan_pipe_connect (ctx, agent_program, argv,
                                         no_close_list, NULL, NULL, 0);
            }
        }
      xfree (sockname);
    }
  else
    {
      int prot;
      int pid;

      infostr = xstrdup (infostr);
      if ( !(p = strchr (infostr, PATHSEP_C)) || p == infostr)
        {
          log_error (_("malformed GPG_AGENT_INFO environment variable\n"));
          xfree (infostr);
          force_pipe_server = 1;
          goto restart;
        }
      *p++ = 0;
      pid = atoi (p);
      while (*p && *p != PATHSEP_C)
        p++;
      prot = *p? atoi (p+1) : 0;
      if (prot != 1)
        {
          log_error (_("gpg-agent protocol version %d is not supported\n"),
                     prot);
          xfree (infostr);
          force_pipe_server = 1;
          goto restart;
        }

      err = assuan_socket_connect (ctx, infostr, pid, 0);
      xfree (infostr);
      if (gpg_err_code (err) == GPG_ERR_ASS_CONNECT_FAILED)
        {
          log_info (_("can't connect to the agent - trying fall back\n"));
          force_pipe_server = 1;
          goto restart;
        }
    }

  if (err)
    {
      log_error ("can't connect to the agent: %s\n", gpg_strerror (err));
      assuan_release (ctx);
      return gpg_err_make (errsource, GPG_ERR_NO_AGENT);
    }

  if (debug && !did_success_msg)
    log_debug (_("connection to agent established\n"));

  err = assuan_transact (ctx, "RESET",
                        NULL, NULL, NULL, NULL, NULL, NULL);
  if (!err)
    err = send_pinentry_environment (ctx, errsource,
                                    opt_lc_ctype, opt_lc_messages,
                                    session_env);
  if (err)
    {
      assuan_release (ctx);
      return err;
    }

  *r_ctx = ctx;
  return 0;
}
Esempio n. 13
0
int ask_user_auth()
{
  int r;
  const char *argv[3];
  const char *pgmname = PIN_ENTRY; 
  ASSUAN_CONTEXT ctx;
  gchar buf[500];
  gsize buflen = sizeof(buf);
  gchar *buf_conv_ptr = NULL;
  const char *local_charset, *default_charset;
  gsize bytes_read=0, bytes_written=0;  
  gboolean is_utf8;

  memset(buf, 0, buflen);
  default_charset = setlocale(LC_CTYPE, "");

  setlocale(LC_ALL, "");
  bindtextdomain(PACKAGE, "/usr/share/locale");
  textdomain(PACKAGE);

  argv[0] = pgmname;
  argv[1] = NULL;
	
  r = assuan_pipe_connect(&ctx, pgmname, (char **) argv, 0);
  if (r) {
    printf(i18n("Can't connect to the PIN entry module: %s\n"),
	   assuan_strerror((AssuanError) r));
    goto err;
  }
	
  sprintf(buf, i18n("SETDESC Está a punto de realizar una firma electrónica con su clave de FIRMA del DNI electrónico. ¿Desea permitir esta operación?"));
  
  is_utf8 = g_get_charset(&local_charset);  
  buf_conv_ptr = g_convert_with_fallback(buf, buflen, local_charset, "UTF-8", 
					 NULL, &bytes_read, &bytes_written, NULL);
  if(!buf_conv_ptr) {
    printf(i18n("Error converting string to locale charset.\n"));
    goto err;
  }

  r = assuan_transact(ctx, buf_conv_ptr, NULL, NULL, NULL, NULL, NULL, NULL);
  if (r) {
    printf("SETDESC: %s\n", assuan_strerror((AssuanError) r));
    goto err;
  }
  while (1) {
    r = assuan_transact(ctx, "CONFIRM", NULL, NULL, NULL, NULL, NULL, NULL);
    if (r == ASSUAN_Canceled) {
      assuan_disconnect(ctx);
      return -2;
    }
    if (r) {
      printf("SETERROR: %s\n", assuan_strerror((AssuanError) r));
      goto err;
    }
    if (r == 0)
      break;
  }

  assuan_disconnect(ctx);	
  return 0;
 err:	
  assuan_disconnect(ctx);
  return -1;
}
Esempio n. 14
0
/* This is a compatibility function for GPGSM 2.0.0, which does not
   support the --data option with the EXPORT command.  */
static gpg_error_t
export_cert_compat (char *fpr, struct cert *cert)
{
  gpg_error_t err;
  assuan_context_t ctx;
  const char *argv[] = { "gpgsm", "--server", NULL };
  int got;
#define COMMANDLINELEN 80
  char cmd[COMMANDLINELEN];
  int output_fds[2];
  int child_fds[2];

#define MAX_CERT_SIZE 4096
  cert->cert_der = malloc (MAX_CERT_SIZE);
  if (!cert->cert_der)
    return gpg_error_from_syserror ();

  if(pipe (output_fds) < 0)
    return gpg_error_from_syserror ();

  child_fds[0] = assuan_fd_from_posix_fd (output_fds[1]);
  child_fds[1] = -1;

  err = assuan_new (&ctx);
  if (err)
    {
      close (output_fds[0]);
      close (output_fds[1]);
      DEBUG (DBG_CRIT, "failed to allocate assuan context: %s\n",
	     gpg_strerror (err));
      return err;
    }

  err = assuan_pipe_connect (ctx, get_gpgsm_path (), argv, child_fds,
			     NULL, NULL, 128);
  close (output_fds[1]);
  if (err)
    {
      close (output_fds[0]);
      assuan_release (ctx);
      DEBUG (DBG_CRIT, "failed to spawn %s\n", get_gpgsm_path ());
      return err;
    }

  snprintf (cmd, sizeof (cmd), "OUTPUT FD=%i", output_fds[1]);
  err = assuan_transact (ctx, cmd, NULL, NULL, NULL, NULL, NULL, NULL);
  if (err)
    goto export_out;

  /* FIXME: This will only work if the certificate is small and fits
     into the pipe buffer completely!!!  */
  snprintf (cmd, sizeof (cmd), "EXPORT %s\n", cert->fpr);
  err = assuan_transact (ctx, cmd, NULL, NULL, NULL, NULL, NULL, NULL);
  if (err)
    goto export_out;

  do
    {
      got = read (output_fds[0], cert->cert_der + cert->cert_der_len,
		  MAX_CERT_SIZE - cert->cert_der_len);
      if (got > 0)
	cert->cert_der_len += got;
    }
  while (!err && got > 0 && cert->cert_der_len < MAX_CERT_SIZE);
  
  if (got < 0 || cert->cert_der_len == MAX_CERT_SIZE)
    err = gpg_error (GPG_ERR_GENERAL);

 export_out:
  assuan_release (ctx);
  close (output_fds[0]);
  return err;
}
Esempio n. 15
0
/* Try to connect to the agent via socket or fork it off and work by
   pipes.  Handle the server's initial greeting */
static int
start_dirmngr_ext (ctrl_t ctrl, assuan_context_t *ctx_r)
{
  int rc;
  char *infostr, *p;
  assuan_context_t ctx = NULL;
  int try_default = 0;

  if (opt.disable_dirmngr)
    return gpg_error (GPG_ERR_NO_DIRMNGR);

  if (*ctx_r)
    return 0;

  /* Note: if you change this to multiple connections, you also need
     to take care of the implicit option sending caching. */

#ifdef HAVE_W32_SYSTEM
  infostr = NULL;
  opt.prefer_system_dirmngr = 1;
#else
  infostr = force_pipe_server? NULL : getenv ("DIRMNGR_INFO");
#endif /*HAVE_W32_SYSTEM*/
  if (infostr && !*infostr)
    infostr = NULL;
  else if (infostr)
    infostr = xstrdup (infostr);

  if (opt.prefer_system_dirmngr && !force_pipe_server && !infostr)
    {
      infostr = xstrdup (dirmngr_socket_name ());
      try_default = 1;
    }

  rc = assuan_new (&ctx);
  if (rc)
    {
      log_error ("can't allocate assuan context: %s\n", gpg_strerror (rc));
      return rc;
    }

  if (!infostr)
    {
      const char *pgmname;
      const char *argv[3];
      int no_close_list[3];
      int i;

      if (!opt.dirmngr_program || !*opt.dirmngr_program)
        opt.dirmngr_program = gnupg_module_name (GNUPG_MODULE_NAME_DIRMNGR);
      if ( !(pgmname = strrchr (opt.dirmngr_program, '/')))
        pgmname = opt.dirmngr_program;
      else
        pgmname++;

      if (opt.verbose)
        log_info (_("no running dirmngr - starting `%s'\n"),
                  opt.dirmngr_program);
      
      if (fflush (NULL))
        {
          gpg_error_t tmperr = gpg_error (gpg_err_code_from_errno (errno));
          log_error ("error flushing pending output: %s\n", strerror (errno));
          return tmperr;
        }

      argv[0] = pgmname;
      argv[1] = "--server";
      argv[2] = NULL;

      i=0;
      if (log_get_fd () != -1)
        no_close_list[i++] = assuan_fd_from_posix_fd (log_get_fd ());
      no_close_list[i++] = assuan_fd_from_posix_fd (fileno (stderr));
      no_close_list[i] = -1;

      /* connect to the agent and perform initial handshaking */
      rc = assuan_pipe_connect (ctx, opt.dirmngr_program, argv,
                                no_close_list, NULL, NULL, 0);
    }
  else
    {
      int prot;
      int pid;

      if (!try_default)
        {
          if ( !(p = strchr (infostr, PATHSEP_C)) || p == infostr)
            {
              log_error (_("malformed DIRMNGR_INFO environment variable\n"));
              xfree (infostr);
              force_pipe_server = 1;
              return start_dirmngr_ext (ctrl, ctx_r);
            }
          *p++ = 0;
          pid = atoi (p);
          while (*p && *p != PATHSEP_C)
            p++;
          prot = *p? atoi (p+1) : 0;
          if (prot != 1)
            {
              log_error (_("dirmngr protocol version %d is not supported\n"),
                         prot);
              xfree (infostr);
              force_pipe_server = 1;
              return start_dirmngr_ext (ctrl, ctx_r);
            }
        }
      else
        pid = -1;

      rc = assuan_socket_connect (ctx, infostr, pid, 0);
#ifdef HAVE_W32_SYSTEM
      if (rc)
        log_debug ("connecting dirmngr at `%s' failed\n", infostr);
#endif

      xfree (infostr);
#ifndef HAVE_W32_SYSTEM
      if (gpg_err_code (rc) == GPG_ERR_ASS_CONNECT_FAILED)
        {
          log_info (_("can't connect to the dirmngr - trying fall back\n"));
          force_pipe_server = 1;
          return start_dirmngr_ext (ctrl, ctx_r);
        }
#endif /*!HAVE_W32_SYSTEM*/
    }

  prepare_dirmngr (ctrl, ctx, rc);

  if (rc)
    {
      assuan_release (ctx);
      log_error ("can't connect to the dirmngr: %s\n", gpg_strerror (rc));
      return gpg_error (GPG_ERR_NO_DIRMNGR);
    }
  *ctx_r = ctx;

  if (DBG_ASSUAN)
    log_debug ("connection to dirmngr established\n");
  return 0;
}
Esempio n. 16
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;
}
Esempio n. 17
0
/* Fire up a new GPG.  Handle the server's initial greeting.  Returns
   0 on success and stores the assuan context at R_CTX.  */
static gpg_error_t
start_gpg (ctrl_t ctrl, int input_fd, int output_fd, assuan_context_t *r_ctx)
{
  gpg_error_t err;
  assuan_context_t ctx = NULL;
  const char *pgmname;
  const char *argv[10];
  int no_close_list[5];
  int i;
  char line[ASSUAN_LINELENGTH];

  (void)ctrl;

  *r_ctx = NULL;

  err = assuan_new (&ctx);
  if (err)
    {
      log_error ("can't allocate assuan context: %s\n", gpg_strerror (err));
      return err;
    }

  /* The first time we are used, intialize the gpg_program variable.  */
  if ( !opt.gpg_program || !*opt.gpg_program )
    opt.gpg_program = gnupg_module_name (GNUPG_MODULE_NAME_GPG);

  if (opt.verbose)
    log_info (_("no running gpg - starting '%s'\n"), opt.gpg_program);

  /* Compute argv[0].  */
  if ( !(pgmname = strrchr (opt.gpg_program, '/')))
    pgmname = opt.gpg_program;
  else
    pgmname++;

  if (fflush (NULL))
    {
      err = gpg_error_from_syserror ();
      log_error ("error flushing pending output: %s\n", gpg_strerror (err));
      return err;
    }

  i = 0;
  argv[i++] = pgmname;
  argv[i++] = "--server";
  if ((opt.debug & 1024))
    argv[i++] = "--debug=1024";
  argv[i++] = "-z";
  argv[i++] = "0";
  argv[i++] = "--trust-model";
  argv[i++] = "always";
  argv[i++] = NULL;

  i = 0;
  if (log_get_fd () != -1)
    no_close_list[i++] = assuan_fd_from_posix_fd (log_get_fd ());
  no_close_list[i++] = assuan_fd_from_posix_fd (fileno (stderr));
  if (input_fd != -1)
    no_close_list[i++] = assuan_fd_from_posix_fd (input_fd);
  if (output_fd != -1)
    no_close_list[i++] = assuan_fd_from_posix_fd (output_fd);
  no_close_list[i] = -1;

  /* Connect to GPG and perform initial handshaking.  */
  err = assuan_pipe_connect (ctx, opt.gpg_program, argv, no_close_list,
			     NULL, NULL, 0);
  if (err)
    {
      assuan_release (ctx);
      log_error ("can't connect to GPG: %s\n", gpg_strerror (err));
      return gpg_error (GPG_ERR_NO_ENGINE);
    }

  if (input_fd != -1)
    {
      snprintf (line, sizeof line, "INPUT FD=%d", input_fd);
      err = assuan_transact (ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
      if (err)
        {
          assuan_release (ctx);
          log_error ("error sending INPUT command: %s\n", gpg_strerror (err));
          return err;
        }
    }

  if (output_fd != -1)
    {
      snprintf (line, sizeof line, "OUTPUT FD=%d", output_fd);
      err = assuan_transact (ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
      if (err)
        {
          assuan_release (ctx);
          log_error ("error sending OUTPUT command: %s\n", gpg_strerror (err));
          return err;
        }
    }

  *r_ctx = ctx;

  if (DBG_ASSUAN)
    log_debug ("connection to GPG established\n");
  return 0;
}
Esempio n. 18
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;
}
Esempio n. 19
0
/* Fork off the SCdaemon if this has not already been done.  Lock the
   daemon and make sure that a proper context has been setup in CTRL.
   This function might also lock the daemon, which means that the
   caller must call unlock_scd after this fucntion has returned
   success and the actual Assuan transaction been done. */
static int
start_scd (ctrl_t ctrl)
{
  gpg_error_t err = 0;
  const char *pgmname;
  assuan_context_t ctx = NULL;
  const char *argv[3];
  assuan_fd_t no_close_list[3];
  int i;
  int rc;

  if (opt.disable_scdaemon)
    return gpg_error (GPG_ERR_NOT_SUPPORTED);

  /* If this is the first call for this session, setup the local data
     structure. */
  if (!ctrl->scd_local)
    {
      ctrl->scd_local = xtrycalloc (1, sizeof *ctrl->scd_local);
      if (!ctrl->scd_local)
        return gpg_error_from_syserror ();
      ctrl->scd_local->ctrl_backlink = ctrl;
      ctrl->scd_local->next_local = scd_local_list;
      scd_local_list = ctrl->scd_local;
    }


  /* Assert that the lock count is as expected. */
  if (ctrl->scd_local->locked)
    {
      log_error ("start_scd: invalid lock count (%d)\n",
                 ctrl->scd_local->locked);
      return gpg_error (GPG_ERR_INTERNAL);
    }
  ctrl->scd_local->locked++;

  if (ctrl->scd_local->ctx)
    return 0; /* Okay, the context is fine.  We used to test for an
                 alive context here and do an disconnect.  Now that we
                 have a ticker function to check for it, it is easier
                 not to check here but to let the connection run on an
                 error instead. */


  /* We need to protect the following code. */
  rc = npth_mutex_lock (&start_scd_lock);
  if (rc)
    {
      log_error ("failed to acquire the start_scd lock: %s\n",
                 strerror (rc));
      return gpg_error (GPG_ERR_INTERNAL);
    }

  /* Check whether the pipe server has already been started and in
     this case either reuse a lingering pipe connection or establish a
     new socket based one. */
  if (primary_scd_ctx && primary_scd_ctx_reusable)
    {
      ctx = primary_scd_ctx;
      primary_scd_ctx_reusable = 0;
      if (opt.verbose)
        log_info ("new connection to SCdaemon established (reusing)\n");
      goto leave;
    }

  rc = assuan_new (&ctx);
  if (rc)
    {
      log_error ("can't allocate assuan context: %s\n", gpg_strerror (rc));
      err = rc;
      goto leave;
    }

  if (socket_name)
    {
      rc = assuan_socket_connect (ctx, socket_name, 0, 0);
      if (rc)
        {
          log_error ("can't connect to socket '%s': %s\n",
                     socket_name, gpg_strerror (rc));
          err = gpg_error (GPG_ERR_NO_SCDAEMON);
          goto leave;
        }

      if (opt.verbose)
        log_info ("new connection to SCdaemon established\n");
      goto leave;
    }

  if (primary_scd_ctx)
    {
      log_info ("SCdaemon is running but won't accept further connections\n");
      err = gpg_error (GPG_ERR_NO_SCDAEMON);
      goto leave;
    }

  /* Nope, it has not been started.  Fire it up now. */
  if (opt.verbose)
    log_info ("no running SCdaemon - starting it\n");

  if (fflush (NULL))
    {
#ifndef HAVE_W32_SYSTEM
      err = gpg_error_from_syserror ();
#endif
      log_error ("error flushing pending output: %s\n", strerror (errno));
      /* At least Windows XP fails here with EBADF.  According to docs
         and Wine an fflush(NULL) is the same as _flushall.  However
         the Wime implementaion does not flush stdin,stdout and stderr
         - see above.  Lets try to ignore the error. */
#ifndef HAVE_W32_SYSTEM
      goto leave;
#endif
    }

  if (!opt.scdaemon_program || !*opt.scdaemon_program)
    opt.scdaemon_program = gnupg_module_name (GNUPG_MODULE_NAME_SCDAEMON);
  if ( !(pgmname = strrchr (opt.scdaemon_program, '/')))
    pgmname = opt.scdaemon_program;
  else
    pgmname++;

  argv[0] = pgmname;
  argv[1] = "--multi-server";
  argv[2] = NULL;

  i=0;
  if (!opt.running_detached)
    {
      if (log_get_fd () != -1)
        no_close_list[i++] = assuan_fd_from_posix_fd (log_get_fd ());
      no_close_list[i++] = assuan_fd_from_posix_fd (fileno (stderr));
    }
  no_close_list[i] = ASSUAN_INVALID_FD;

  /* Connect to the scdaemon and perform initial handshaking.  Use
     detached flag so that under Windows SCDAEMON does not show up a
     new window.  */
  rc = assuan_pipe_connect (ctx, opt.scdaemon_program, argv,
			    no_close_list, atfork_cb, NULL,
                            ASSUAN_PIPE_CONNECT_DETACHED);
  if (rc)
    {
      log_error ("can't connect to the SCdaemon: %s\n",
                 gpg_strerror (rc));
      err = gpg_error (GPG_ERR_NO_SCDAEMON);
      goto leave;
    }

  if (opt.verbose)
    log_debug ("first connection to SCdaemon established\n");


  /* Get the name of the additional socket opened by scdaemon. */
  {
    membuf_t data;
    unsigned char *databuf;
    size_t datalen;

    xfree (socket_name);
    socket_name = NULL;
    init_membuf (&data, 256);
    assuan_transact (ctx, "GETINFO socket_name",
                     membuf_data_cb, &data, NULL, NULL, NULL, NULL);

    databuf = get_membuf (&data, &datalen);
    if (databuf && datalen)
      {
        socket_name = xtrymalloc (datalen + 1);
        if (!socket_name)
          log_error ("warning: can't store socket name: %s\n",
                     strerror (errno));
        else
          {
            memcpy (socket_name, databuf, datalen);
            socket_name[datalen] = 0;
            if (DBG_IPC)
              log_debug ("additional connections at '%s'\n", socket_name);
          }
      }
    xfree (databuf);
  }

  /* Tell the scdaemon we want him to send us an event signal.  We
     don't support this for W32CE.  */
#ifndef HAVE_W32CE_SYSTEM
  if (opt.sigusr2_enabled)
    {
      char buf[100];

#ifdef HAVE_W32_SYSTEM
      snprintf (buf, sizeof buf, "OPTION event-signal=%lx",
                (unsigned long)get_agent_scd_notify_event ());
#else
      snprintf (buf, sizeof buf, "OPTION event-signal=%d", SIGUSR2);
#endif
      assuan_transact (ctx, buf, NULL, NULL, NULL, NULL, NULL, NULL);
    }
#endif /*HAVE_W32CE_SYSTEM*/

  primary_scd_ctx = ctx;
  primary_scd_ctx_reusable = 0;

 leave:
  if (err)
    {
      unlock_scd (ctrl, err);
      if (ctx)
	assuan_release (ctx);
    }
  else
    {
      ctrl->scd_local->ctx = ctx;
    }
  rc = npth_mutex_unlock (&start_scd_lock);
  if (rc)
    log_error ("failed to release the start_scd lock: %s\n", strerror (rc));
  return err;
}