Example #1
0
File: gpgtar.c Project: gpg/gnupg
/* gpgtar main. */
int
main (int argc, char **argv)
{
    gpg_error_t err;
    const char *fname;
    ARGPARSE_ARGS pargs;

    assert (sizeof (struct ustar_raw_header) == 512);

    gnupg_reopen_std (GPGTAR_NAME);
    set_strusage (my_strusage);
    log_set_prefix (GPGTAR_NAME, 1);

    /* Make sure that our subsystems are ready.  */
    i18n_init();
    init_common_subsystems (&argc, &argv);

    /* Parse the command line. */
    pargs.argc  = &argc;
    pargs.argv  = &argv;
    pargs.flags = ARGPARSE_FLAG_KEEP;
    parse_arguments (&pargs, opts);

    if ((files_from && !null_names) || (!files_from && null_names))
        log_error ("--files-from and --null may only be used in conjunction\n");
    if (files_from && strcmp (files_from, "-"))
        log_error ("--files-from only supports argument \"-\"\n");

    if (log_get_errorcount (0))
        exit (2);

    /* Print a warning if an argument looks like an option.  */
    if (!opt.quiet && !(pargs.flags & ARGPARSE_FLAG_STOP_SEEN))
    {
        int i;

        for (i=0; i < argc; i++)
            if (argv[i][0] == '-' && argv[i][1] == '-')
                log_info (_("NOTE: '%s' is not considered an option\n"), argv[i]);
    }

    if (! opt.gpg_program)
        opt.gpg_program = gnupg_module_name (GNUPG_MODULE_NAME_GPG);

    if (opt.verbose > 1)
        opt.debug_level = 1024;

    switch (cmd)
    {
    case aList:
        if (argc > 1)
            usage (1);
        fname = argc ? *argv : NULL;
        if (opt.filename)
            log_info ("note: ignoring option --set-filename\n");
        if (files_from)
            log_info ("note: ignoring option --files-from\n");
        err = gpgtar_list (fname, !skip_crypto);
        if (err && log_get_errorcount (0) == 0)
            log_error ("listing archive failed: %s\n", gpg_strerror (err));
        break;

    case aEncrypt:
    case aSign:
    case aSignEncrypt:
        if ((!argc && !null_names)
                || (argc && null_names))
            usage (1);
        if (opt.filename)
            log_info ("note: ignoring option --set-filename\n");
        err = gpgtar_create (null_names? NULL :argv,
                             !skip_crypto
                             && (cmd == aEncrypt || cmd == aSignEncrypt),
                             cmd == aSign || cmd == aSignEncrypt);
        if (err && log_get_errorcount (0) == 0)
            log_error ("creating archive failed: %s\n", gpg_strerror (err));
        break;

    case aDecrypt:
        if (argc != 1)
            usage (1);
        if (opt.outfile)
            log_info ("note: ignoring option --output\n");
        if (files_from)
            log_info ("note: ignoring option --files-from\n");
        fname = argc ? *argv : NULL;
        err = gpgtar_extract (fname, !skip_crypto);
        if (err && log_get_errorcount (0) == 0)
            log_error ("extracting archive failed: %s\n", gpg_strerror (err));
        break;

    default:
        log_error (_("invalid command (there is no implicit command)\n"));
        break;
    }

    return log_get_errorcount (0)? 1:0;
}
Example #2
0
/* Try to connect to the dirmngr via a socket.  On platforms
   supporting it, start it up if needed.  Returns a new assuan context
   at R_CTX or an error code. */
gpg_error_t
start_new_dirmngr (assuan_context_t *r_ctx,
                   gpg_err_source_t errsource,
                   const char *homedir,
                   const char *dirmngr_program,
                   int verbose, int debug,
                   gpg_error_t (*status_cb)(ctrl_t, int, ...),
                   ctrl_t status_cb_arg)
{
  gpg_error_t err;
  assuan_context_t ctx;
  const char *sockname;
  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;
    }

  sockname = dirmngr_socket_name ();
  err = assuan_socket_connect (ctx, sockname, 0, 0);
#ifdef USE_DIRMNGR_AUTO_START
  if (err)
    {
      lock_spawn_t lock;
      const char *argv[2];

      /* With no success try start a new Dirmngr.  On most systems
         this will fail because the Dirmngr is expected to be a system
         service.  However on Wince we don't distinguish users and
         thus we can start it.  A future extension might be to use the
         userv system to start the Dirmngr as a system service.  */
      if (!dirmngr_program || !*dirmngr_program)
        dirmngr_program = gnupg_module_name (GNUPG_MODULE_NAME_DIRMNGR);

      if (verbose)
        log_info (_("no running Dirmngr - starting '%s'\n"),
                  dirmngr_program);

      if (status_cb)
        status_cb (status_cb_arg, STATUS_PROGRESS,
                   "starting_dirmngr ? 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));
          assuan_release (ctx);
          return tmperr;
        }

      argv[0] = "--daemon";
      argv[1] = NULL;

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

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

      unlock_spawning (&lock, "dirmngr");
    }
#else
  (void)homedir;
  (void)dirmngr_program;
  (void)verbose;
  (void)status_cb;
  (void)status_cb_arg;
#endif /*USE_DIRMNGR_AUTO_START*/

  if (err)
    {
      log_error ("connecting dirmngr at '%s' failed: %s\n",
                 sockname, gpg_strerror (err));
      assuan_release (ctx);
      return gpg_err_make (errsource, GPG_ERR_NO_DIRMNGR);
    }

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

  *r_ctx = ctx;
  return 0;
}
Example #3
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;
}
Example #4
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;
}
Example #5
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;
}
Example #6
0
int
main (int argc, char **argv )
{
  ARGPARSE_ARGS pargs;
  assuan_context_t ctx;
  gpg_error_t err;
  unsigned char *certbuf;
  size_t certbuflen = 0;
  int cmd_ping = 0;
  int cmd_cache_cert = 0;
  int cmd_validate = 0;
  int cmd_lookup = 0;
  int cmd_loadcrl = 0;
  int cmd_squid_mode = 0;

  early_system_init ();
  set_strusage (my_strusage);
  log_set_prefix ("dirmngr-client",
                  GPGRT_LOG_WITH_PREFIX);

  /* For W32 we need to initialize the socket subsystem.  Because we
     don't use Pth we need to do this explicit. */
#ifdef HAVE_W32_SYSTEM
 {
   WSADATA wsadat;

   WSAStartup (0x202, &wsadat);
 }
#endif /*HAVE_W32_SYSTEM*/

  /* Init Assuan.  */
  assuan_set_assuan_log_prefix (log_get_prefix (NULL));
  assuan_set_gpg_err_source (GPG_ERR_SOURCE_DEFAULT);

  /* Setup I18N. */
  i18n_init();

  /* Parse the command line.  */
  pargs.argc = &argc;
  pargs.argv = &argv;
  pargs.flags= 1;  /* Do not remove the args. */
  while (arg_parse (&pargs, opts) )
    {
      switch (pargs.r_opt)
        {
        case oVerbose: opt.verbose++; break;
        case oQuiet: opt.quiet++; break;

        case oOCSP: opt.use_ocsp++; break;
        case oPing: cmd_ping = 1; break;
        case oCacheCert: cmd_cache_cert = 1; break;
        case oValidate: cmd_validate = 1; break;
        case oLookup: cmd_lookup = 1; break;
        case oUrl: opt.url = 1; break;
        case oLocal: opt.local = 1; break;
        case oLoadCRL: cmd_loadcrl = 1; break;
        case oPEM: opt.pem = 1; break;
        case oSquidMode:
          opt.pem = 1;
          opt.escaped_pem = 1;
          cmd_squid_mode = 1;
          break;
        case oForceDefaultResponder: opt.force_default_responder = 1; break;

        default : pargs.err = 2; break;
	}
    }
  if (log_get_errorcount (0))
    exit (2);

  if (cmd_ping)
    err = 0;
  else if (cmd_lookup || cmd_loadcrl)
    {
      if (!argc)
        usage (1);
      err = 0;
    }
  else if (cmd_squid_mode)
    {
      err = 0;
      if (argc)
        usage (1);
    }
  else if (!argc)
    {
      err = read_certificate (NULL, &certbuf, &certbuflen);
      if (err)
        log_error (_("error reading certificate from stdin: %s\n"),
                   gpg_strerror (err));
    }
  else if (argc == 1)
    {
      err = read_certificate (*argv, &certbuf, &certbuflen);
      if (err)
        log_error (_("error reading certificate from '%s': %s\n"),
                   *argv, gpg_strerror (err));
    }
  else
    {
      err = 0;
      usage (1);
    }

  if (log_get_errorcount (0))
    exit (2);

  if (certbuflen > 20000)
    {
      log_error (_("certificate too large to make any sense\n"));
      exit (2);
    }

  err = start_new_dirmngr (&ctx,
                           GPG_ERR_SOURCE_DEFAULT,
                           opt.dirmngr_program
                             ? opt.dirmngr_program
                             : gnupg_module_name (GNUPG_MODULE_NAME_DIRMNGR),
                           ! cmd_ping,
                           opt.verbose,
                           0,
                           NULL, NULL);
  if (err)
    {
      log_error (_("can't connect to the dirmngr: %s\n"), gpg_strerror (err));
      exit (2);
    }

  if (cmd_ping)
    ;
  else if (cmd_squid_mode)
    {
      while (!(err = squid_loop_body (ctx)))
        ;
      if (gpg_err_code (err) == GPG_ERR_EOF)
        err = 0;
    }
  else if (cmd_lookup)
    {
      int last_err = 0;

      for (; argc; argc--, argv++)
        {
          err = do_lookup (ctx, *argv);
          if (err)
            {
              log_error (_("lookup failed: %s\n"), gpg_strerror (err));
              last_err = err;
            }
        }
      err = last_err;
    }
  else if (cmd_loadcrl)
    {
      int last_err = 0;

      for (; argc; argc--, argv++)
        {
          err = do_loadcrl (ctx, *argv);
          if (err)
            {
              log_error (_("loading CRL '%s' failed: %s\n"),
                         *argv, gpg_strerror (err));
              last_err = err;
            }
        }
      err = last_err;
    }
  else if (cmd_cache_cert)
    {
      err = do_cache (ctx, certbuf, certbuflen);
      xfree (certbuf);
    }
  else if (cmd_validate)
    {
      err = do_validate (ctx, certbuf, certbuflen);
      xfree (certbuf);
    }
  else
    {
      err = do_check (ctx, certbuf, certbuflen);
      xfree (certbuf);
    }

  assuan_release (ctx);

  if (cmd_ping)
    {
      if (!opt.quiet)
        log_info (_("a dirmngr daemon is up and running\n"));
      return 0;
    }
  else if (cmd_lookup|| cmd_loadcrl || cmd_squid_mode)
    return err? 1:0;
  else if (cmd_cache_cert)
    {
      if (err && gpg_err_code (err) == GPG_ERR_DUP_VALUE )
        {
          if (!opt.quiet)
            log_info (_("certificate already cached\n"));
        }
      else if (err)
        {
          log_error (_("error caching certificate: %s\n"),
                     gpg_strerror (err));
          return 1;
        }
      return 0;
    }
  else if (cmd_validate && err)
    {
      log_error (_("validation of certificate failed: %s\n"),
                 gpg_strerror (err));
      return 1;
    }
  else if (!err)
    {
      if (!opt.quiet)
        log_info (_("certificate is valid\n"));
      return 0;
    }
  else if (gpg_err_code (err) == GPG_ERR_CERT_REVOKED )
    {
      if (!opt.quiet)
        log_info (_("certificate has been revoked\n"));
      return 1;
    }
  else
    {
      log_error (_("certificate check failed: %s\n"), gpg_strerror (err));
      return 2;
    }
}
Example #7
0
/* Assume that the reader is at a pkcs#12 message and try to import
   certificates from that stupid format.  We will also store secret
   keys.  All of the pkcs#12 parsing and key storing is handled by the
   gpg-protect-tool, we merely have to take care of receiving the
   certificates. On success RETFP returns a temporary file with
   certificates. */
static gpg_error_t
parse_p12 (ctrl_t ctrl, ksba_reader_t reader,
           FILE **retfp, struct stats_s *stats)
{
  const char *pgmname;
  gpg_error_t err = 0, child_err = 0;
  int c, cont_line;
  unsigned int pos;
  FILE *tmpfp, *certfp = NULL, *fp = NULL;
  char buffer[1024];
  size_t nread;
  pid_t pid = -1;
  int bad_pass = 0;

  if (!opt.protect_tool_program || !*opt.protect_tool_program)
    pgmname = gnupg_module_name (GNUPG_MODULE_NAME_PROTECT_TOOL);
  else
    pgmname = opt.protect_tool_program;

  *retfp = NULL;

  /* To avoid an extra feeder process or doing selects and because
     gpg-protect-tool will anyway parse the entire pkcs#12 message in
     memory, we simply use tempfiles here and pass them to
     the gpg-protect-tool. */
  tmpfp = gnupg_tmpfile ();
  if (!tmpfp)
    {
      err = gpg_error_from_syserror ();
      log_error (_("error creating temporary file: %s\n"), strerror (errno));
      goto cleanup;
    }
  while (!(err = ksba_reader_read (reader, buffer, sizeof buffer, &nread)))
    {
      if (nread && fwrite (buffer, nread, 1, tmpfp) != 1)
        {
          err = gpg_error_from_syserror ();
          log_error (_("error writing to temporary file: %s\n"),
                     strerror (errno));
          goto cleanup;
        }
    }
  if (gpg_err_code (err) == GPG_ERR_EOF)
    err = 0;
  if (err)
    {
      log_error (_("error reading input: %s\n"), gpg_strerror (err));
      goto cleanup;
    }

  certfp = gnupg_tmpfile ();
  if (!certfp)
    {
      err = gpg_error_from_syserror ();
      log_error (_("error creating temporary file: %s\n"), strerror (errno));
      goto cleanup;
    }

  err = popen_protect_tool (ctrl, pgmname, tmpfp, certfp, &fp, &pid);
  if (err)
    {
      pid = -1;
      goto cleanup;
    }
  fclose (tmpfp);
  tmpfp = NULL;

  /* Read stderr of the protect tool. */
  pos = 0;
  cont_line = 0;
  while ((c=getc (fp)) != EOF)
    {
      /* fixme: We could here grep for status information of the
         protect tool to figure out better error codes for
         CHILD_ERR. */
      buffer[pos++] = c;
      if (pos >= sizeof buffer - 5 || c == '\n')
        {
          buffer[pos - (c == '\n')] = 0;
          if (cont_line)
            log_printf ("%s", buffer);
          else
            {
              if (!strncmp (buffer, "gpg-protect-tool: [PROTECT-TOOL:] ",34))
                {
                  char *p, *pend;

                  p = buffer + 34;
                  pend = strchr (p, ' ');
                  if (pend)
                    *pend = 0;
                  if ( !strcmp (p, "secretkey-stored"))
                    {
                      stats->count++;
                      stats->secret_read++;
                      stats->secret_imported++;
                    }
                  else if ( !strcmp (p, "secretkey-exists"))
                    {
                      stats->count++;
                      stats->secret_read++;
                      stats->secret_dups++;
                    }
                  else if ( !strcmp (p, "bad-passphrase"))
                    {

                    }
                }
              else 
                {
                  log_info ("%s", buffer);
                  if (!strncmp (buffer, "gpg-protect-tool: "
                                "possibly bad passphrase given",46))
                    bad_pass++;
                }
            }
          pos = 0;
          cont_line = (c != '\n');
        }
    }

  if (pos)
    {
      buffer[pos] = 0;
      if (cont_line)
        log_printf ("%s\n", buffer);
      else
        log_info ("%s\n", buffer);
    }


  /* If we found no error in the output of the child, setup a suitable
     error code, which will later be reset if the exit status of the
     child is 0. */
  if (!child_err)
    child_err = gpg_error (GPG_ERR_DECRYPT_FAILED);

 cleanup:
  if (tmpfp)
    fclose (tmpfp);
  if (fp)
    fclose (fp);
  if (pid != -1)
    {
      if (!gnupg_wait_process (pgmname, pid, NULL))
        child_err = 0;
    }
  if (!err)
    err = child_err;
  if (err)
    {
      if (certfp)
        fclose (certfp);
    }
  else
    *retfp = certfp;

  if (bad_pass)
    {
      /* We only write a plain error code and not direct
         BAD_PASSPHRASE because the pkcs12 parser might issue this
         message multiple times, BAD_PASSPHRASE in general requires a
         keyID and parts of the import might actually succeed so that
         IMPORT_PROBLEM is also not appropriate. */
      gpgsm_status_with_err_code (ctrl, STATUS_ERROR,
                                  "import.parsep12", GPG_ERR_BAD_PASSPHRASE);
    }
  
  return err;
}
Example #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, 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;
}
Example #9
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;
}
Example #10
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;
}