Ejemplo n.º 1
0
/* Create a key description for the CERT, this may be passed to the
   pinentry.  The caller must free the returned string.  NULL may be
   returned on error. */
char *
gpgsm_format_keydesc (ksba_cert_t cert)
{
  char *name, *subject, *buffer;
  ksba_isotime_t t;
  char created[20];
  char expires[20];
  char *sn;
  ksba_sexp_t sexp;
  char *orig_codeset;

  name = ksba_cert_get_subject (cert, 0);
  subject = name? gpgsm_format_name2 (name, 0) : NULL;
  ksba_free (name); name = NULL;

  sexp = ksba_cert_get_serial (cert);
  sn = sexp? gpgsm_format_serial (sexp) : NULL;
  ksba_free (sexp);

  ksba_cert_get_validity (cert, 0, t);
  if (*t)
    sprintf (created, "%.4s-%.2s-%.2s", t, t+4, t+6);
  else
    *created = 0;
  ksba_cert_get_validity (cert, 1, t);
  if (*t)
    sprintf (expires, "%.4s-%.2s-%.2s", t, t+4, t+6);
  else
    *expires = 0;

  orig_codeset = i18n_switchto_utf8 ();

  name = xtryasprintf (_("Please enter the passphrase to unlock the"
                         " secret key for the X.509 certificate:\n"
                         "\"%s\"\n"
                         "S/N %s, ID 0x%08lX,\n"
                         "created %s, expires %s.\n" ),
                       subject? subject:"?",
                       sn? sn: "?",
                       gpgsm_get_short_fingerprint (cert, NULL),
                       created, expires);

  i18n_switchback (orig_codeset);

  if (!name)
    {
      xfree (subject);
      xfree (sn);
      return NULL;
    }

  xfree (subject);
  xfree (sn);

  buffer = percent_plus_escape (name);
  xfree (name);
  return buffer;
}
Ejemplo n.º 2
0
/* Create a new runner context.  On success a new runner object is
   stored at R_RUNNER.  On failure NULL is stored at this address and
   an error code returned.  */
gpg_error_t
runner_new (runner_t *r_runner, const char *name)
{
  static unsigned int namecounter; /* Global name counter.  */
  char *namebuffer;
  runner_t runner, r;

  *r_runner = NULL;

  runner = xtrycalloc (1, sizeof *runner);
  if (!runner)
    return gpg_error_from_syserror ();

  /* Bump up the namecounter.  In case we ever had an overflow we
     check that this number is currently not in use.  The algorithm is
     a bit lame but should be sufficient because such an wrap is not
     very likely: Assuming that we do a mount 10 times a second, then
     we would overwrap on a 32 bit system after 13 years.  */
  do
    {
      namecounter++;
      for (r = running_threads; r; r = r->next_running)
        if (r->identifier == namecounter)
          break;
    }
  while (r);

  runner->identifier = namecounter;
  runner->name = namebuffer = xtryasprintf ("%s-%d", name, namecounter);
  if (!runner->name)
    {
      xfree (runner);
      return gpg_error_from_syserror ();
    }
  runner->refcount = 1;
  runner->pid = (pid_t)(-1);
  runner->in_fd = -1;
  runner->out_fd = -1;

  *r_runner = runner;
  return 0;
}
Ejemplo n.º 3
0
/* Issue an SCD SERIALNO openpgp command and if SERIALNO is not NULL
   ask the user to insert the requested card.  */
gpg_error_t
select_openpgp (const char *serialno)
{
    gpg_error_t err;

    /* Send the serialno command to initialize the connection.  Without
       a given S/N we don't care about the data returned.  If the card
       has already been initialized, this is a very fast command.  We
       request the openpgp card because that is what we expect.

       Note that an opt.limit_card_insert_tries of 1 means: No tries at
       all whereas 0 means do not limit the number of tries.  Due to the
       sue of a pinentry prompt with a cancel option we use it here in a
       boolean sense.  */
    if (!serialno || opt.limit_card_insert_tries == 1)
        err = assuan_transact (agent_ctx, "SCD SERIALNO openpgp",
                               NULL, NULL, NULL, NULL, NULL, NULL);
    else
    {
        char *this_sn = NULL;
        char *desc;
        int ask;
        char *want_sn;
        char *p;

        want_sn = xtrystrdup (serialno);
        if (!want_sn)
            return gpg_error_from_syserror ();
        p = strchr (want_sn, '/');
        if (p)
            *p = 0;

        do
        {
            ask = 0;
            err = assuan_transact (agent_ctx, "SCD SERIALNO openpgp",
                                   NULL, NULL, NULL, NULL,
                                   get_serialno_cb, &this_sn);
            if (gpg_err_code (err) == GPG_ERR_CARD_NOT_PRESENT)
                ask = 1;
            else if (gpg_err_code (err) == GPG_ERR_NOT_SUPPORTED)
                ask = 2;
            else if (err)
                ;
            else if (this_sn)
            {
                if (strcmp (want_sn, this_sn))
                    ask = 2;
            }

            xfree (this_sn);
            this_sn = NULL;

            if (ask)
            {
                char *formatted = NULL;
                char *ocodeset = i18n_switchto_utf8 ();

                if (!strncmp (want_sn, "D27600012401", 12)
                        && strlen (want_sn) == 32 )
                    formatted = xtryasprintf ("(%.4s) %.8s",
                                              want_sn + 16, want_sn + 20);

                err = 0;
                desc = xtryasprintf
                       ("%s:\n\n"
                        "  \"%s\"",
                        ask == 1
                        ? _("Please insert the card with serial number")
                        : _("Please remove the current card and "
                            "insert the one with serial number"),
                        formatted? formatted : want_sn);
                if (!desc)
                    err = gpg_error_from_syserror ();
                xfree (formatted);
                i18n_switchback (ocodeset);
                if (!err)
                    err = gpg_agent_get_confirmation (desc);
                xfree (desc);
            }
        }
        while (ask && !err);
        xfree (want_sn);
    }

    return err;
}
Ejemplo n.º 4
0
/* Return an allocated utf-8 string describing the key PK.  If ESCAPED
   is true spaces and control characters are percent or plus escaped.
   MODE describes the use of the key description; use one of the
   FORMAT_KEYDESC_ macros. */
char *
gpg_format_keydesc (PKT_public_key *pk, int mode, int escaped)
{
  char *uid;
  size_t uidlen;
  const char *algo_name;
  const char *timestr;
  char *orig_codeset;
  char *maink;
  char *desc;
  const char *prompt;
  const char *trailer = "";
  int is_subkey;

  is_subkey = (pk->main_keyid[0] && pk->main_keyid[1]
               && pk->keyid[0] != pk->main_keyid[0]
               && pk->keyid[1] != pk->main_keyid[1]);
  algo_name = openpgp_pk_algo_name (pk->pubkey_algo);
  timestr = strtimestamp (pk->timestamp);
  uid = get_user_id (is_subkey? pk->main_keyid:pk->keyid, &uidlen);

  orig_codeset = i18n_switchto_utf8 ();

  if (is_subkey)
    maink = xtryasprintf (_(" (main key ID %s)"), keystr (pk->main_keyid));
  else
    maink = NULL;

  switch (mode)
    {
    case FORMAT_KEYDESC_NORMAL:
      prompt = _("Please enter the passphrase to unlock the"
                 " OpenPGP secret key:");
      break;
    case FORMAT_KEYDESC_IMPORT:
      prompt = _("Please enter the passphrase to import the"
                 " OpenPGP secret key:");
      break;
    case FORMAT_KEYDESC_EXPORT:
      if (is_subkey)
        prompt = _("Please enter the passphrase to export the"
                   " OpenPGP secret subkey:");
      else
        prompt = _("Please enter the passphrase to export the"
                   " OpenPGP secret key:");
      break;
    case FORMAT_KEYDESC_DELKEY:
      if (is_subkey)
        prompt = _("Do you really want to permanently delete the"
                   " OpenPGP secret subkey key:");
      else
        prompt = _("Do you really want to permanently delete the"
                   " OpenPGP secret key:");
      trailer = "?";
      break;
    default:
      prompt = "?";
      break;
    }

  desc = xtryasprintf (_("%s\n"
                         "\"%.*s\"\n"
                         "%u-bit %s key, ID %s,\n"
                         "created %s%s.\n%s"),
                       prompt,
                       (int)uidlen, uid,
                       nbits_from_pk (pk), algo_name,
                       keystr (pk->keyid), timestr,
                       maink?maink:"", trailer);
  xfree (maink);
  xfree (uid);

  i18n_switchback (orig_codeset);

  if (escaped)
    {
      char *tmp = percent_plus_escape (desc);
      xfree (desc);
      desc = tmp;
    }

  return desc;
}
Ejemplo n.º 5
0
/* Callback function to try the unprotection from the passpharse query
   code. */
static int
try_unprotect_cb (struct pin_entry_info_s *pi)
{
  struct try_unprotect_arg_s *arg = pi->check_cb_arg;
  size_t dummy;
  gpg_error_t err;
  gnupg_isotime_t now, protected_at, tmptime;
  char *desc = NULL;

  assert (!arg->unprotected_key);

  arg->change_required = 0;
  err = agent_unprotect (arg->protected_key, pi->pin, protected_at,
                         &arg->unprotected_key, &dummy);
  if (err)
    return err;
  if (!opt.max_passphrase_days || arg->ctrl->in_passwd)
    return 0;  /* No regular passphrase change required.  */

  if (!*protected_at)
    {
      /* No protection date known - must force passphrase change.  */
      desc = xtrystrdup (_("Note: This passphrase has never been changed.%0A"
                           "Please change it now."));
      if (!desc)
        return gpg_error_from_syserror ();
    }
  else
    {
      gnupg_get_isotime (now);
      gnupg_copy_time (tmptime, protected_at);
      err = add_days_to_isotime (tmptime, opt.max_passphrase_days);
      if (err)
        return err;
      if (strcmp (now, tmptime) > 0 )
        {
          /* Passphrase "expired".  */
          desc = xtryasprintf
            (_("This passphrase has not been changed%%0A"
               "since %.4s-%.2s-%.2s.  Please change it now."),
             protected_at, protected_at+4, protected_at+6);
          if (!desc)
            return gpg_error_from_syserror ();
        }
    }

  if (desc)
    {
      /* Change required.  */
      if (opt.enforce_passphrase_constraints)
        {
          err = agent_get_confirmation (arg->ctrl, desc,
                                        _("Change passphrase"), NULL, 0);
          if (!err)
            arg->change_required = 1;
        }
      else
        {
          err = agent_get_confirmation (arg->ctrl, desc,
                                        _("Change passphrase"),
                                        _("I'll change it later"), 0);
          if (!err)
            arg->change_required = 1;
          else if (gpg_err_code (err) == GPG_ERR_CANCELED)
            err = 0;
        }
      xfree (desc);
    }

  return 0;
}
Ejemplo n.º 6
0
/* Startup the server.  CTRL must have been allocated by the caller
   and set to the default values. */
int
gpg_server (ctrl_t ctrl)
{
  int rc;
#ifndef HAVE_W32_SYSTEM
  int filedes[2];
#endif
  assuan_context_t ctx = NULL;
  static const char hello[] = ("GNU Privacy Guard's OpenPGP server "
                               VERSION " ready");

  /* We use a pipe based server so that we can work from scripts.
     assuan_init_pipe_server will automagically detect when we are
     called with a socketpair and ignore FILEDES in this case.  */
#ifndef HAVE_W32_SYSTEM
  filedes[0] = assuan_fdopen (0);
  filedes[1] = assuan_fdopen (1);
#endif
  rc = assuan_new (&ctx);
  if (rc)
    {
      log_error ("failed to allocate the assuan context: %s\n",
		 gpg_strerror (rc));
      goto leave;
    }

#ifdef HAVE_W32_SYSTEM
  rc = gpg_error (GPG_ERR_NOT_IMPLEMENTED);
#else
  rc = assuan_init_pipe_server (ctx, filedes);
#endif
  if (rc)
    {
      log_error ("failed to initialize the server: %s\n", gpg_strerror (rc));
      goto leave;
    }

  rc = register_commands (ctx);
  if (rc)
    {
      log_error ("failed to the register commands with Assuan: %s\n",
                 gpg_strerror(rc));
      goto leave;
    }

  assuan_set_pointer (ctx, ctrl);
  if (opt.verbose || opt.debug)
    {
      char *tmp = NULL;

      tmp = xtryasprintf ("Home: %s\n"
                          "Config: %s\n"
                          "%s",
                          opt.homedir,
                          "fixme: need config filename",
                          hello);
      if (tmp)
        {
          assuan_set_hello_line (ctx, tmp);
          xfree (tmp);
        }
    }
  else
    assuan_set_hello_line (ctx, hello);
  assuan_register_reset_notify (ctx, reset_notify);
  assuan_register_input_notify (ctx, input_notify);
  assuan_register_output_notify (ctx, output_notify);
  assuan_register_option_handler (ctx, option_handler);

  ctrl->server_local = xtrycalloc (1, sizeof *ctrl->server_local);
  if (!ctrl->server_local)
    {
      rc = gpg_error_from_syserror ();
      goto leave;
    }
  ctrl->server_local->assuan_ctx = ctx;
  ctrl->server_local->message_fd = GNUPG_INVALID_FD;

  for (;;)
    {
      rc = assuan_accept (ctx);
      if (rc == -1)
        {
          rc = 0;
          break;
        }
      else if (rc)
        {
          log_info ("Assuan accept problem: %s\n", gpg_strerror (rc));
          break;
        }

      rc = assuan_process (ctx);
      if (rc)
        {
          log_info ("Assuan processing failed: %s\n", gpg_strerror (rc));
          continue;
        }
    }

 leave:
  if (ctrl->server_local)
    {
      release_pk_list (ctrl->server_local->recplist);

      xfree (ctrl->server_local);
      ctrl->server_local = NULL;
    }
  assuan_release (ctx);
  return rc;
}
Ejemplo n.º 7
0
static gpg_error_t
keyserver_get (ctrl_t ctrl, KEYDB_SEARCH_DESC *desc, int ndesc,
               struct keyserver_spec *keyserver)

{
  gpg_error_t err = 0;
  char **pattern;
  int idx, npat;
  estream_t datastream;

  /* Create an array filled with a search pattern for each key.  The
     array is delimited by a NULL entry.  */
  pattern = xtrycalloc (ndesc+1, sizeof *pattern);
  if (!pattern)
    return gpg_error_from_syserror ();
  for (npat=idx=0; idx < ndesc; idx++)
    {
      int quiet = 0;

      if (desc[idx].mode == KEYDB_SEARCH_MODE_FPR20
          || desc[idx].mode == KEYDB_SEARCH_MODE_FPR16)
        {
          pattern[npat] = xtrymalloc (2+2*20+1);
          if (!pattern[npat])
            err = gpg_error_from_syserror ();
          else
            {
              strcpy (pattern[npat], "0x");
              bin2hex (desc[idx].u.fpr,
                       desc[idx].mode == KEYDB_SEARCH_MODE_FPR20? 20 : 16,
                       pattern[npat]+2);
              npat++;
            }
        }
      else if(desc[idx].mode == KEYDB_SEARCH_MODE_LONG_KID)
        {
          pattern[npat] = xtryasprintf ("0x%08lX%08lX",
                                        (ulong)desc[idx].u.kid[0],
                                        (ulong)desc[idx].u.kid[1]);
          if (!pattern[npat])
            err = gpg_error_from_syserror ();
          else
            npat++;
        }
      else if(desc[idx].mode == KEYDB_SEARCH_MODE_SHORT_KID)
        {
          pattern[npat] = xtryasprintf ("0x%08lX", (ulong)desc[idx].u.kid[1]);
          if (!pattern[npat])
            err = gpg_error_from_syserror ();
          else
            npat++;
        }
      else if(desc[idx].mode == KEYDB_SEARCH_MODE_EXACT)
        {
          /* FIXME: We don't need this.  It is used as a dummy by
             keyserver_fetch which passes an entire URL.  Better use a
             separate function here. */
          pattern[npat] = xtrystrdup ("0x0000000000000000");
          if (!pattern[npat])
            err = gpg_error_from_syserror ();
          else
            {
              npat++;
              quiet = 1;
            }
        }
      else if (desc[idx].mode == KEYDB_SEARCH_MODE_NONE)
        continue;
      else
        BUG();

      if (err)
        {
          for (idx=0; idx < npat; idx++)
            xfree (pattern[idx]);
          xfree (pattern);
          return err;
        }

      if (!quiet && keyserver)
        {
          if (keyserver->host)
            log_info (_("requesting key %s from %s server %s\n"),
                      keystr_from_desc (&desc[idx]),
                      keyserver->scheme, keyserver->host);
          else
            log_info (_("requesting key %s from %s\n"),
                      keystr_from_desc (&desc[idx]), keyserver->uri);
        }
    }


  err = gpg_dirmngr_ks_get (ctrl, pattern, &datastream);
  for (idx=0; idx < npat; idx++)
    xfree (pattern[idx]);
  xfree (pattern);
  if (!err)
    {
      void *stats_handle;

      stats_handle = import_new_stats_handle();

      /* FIXME: Check whether this comment should be moved to dirmngr.

         Slurp up all the key data.  In the future, it might be nice
         to look for KEY foo OUTOFBAND and FAILED indicators.  It's
         harmless to ignore them, but ignoring them does make gpg
         complain about "no valid OpenPGP data found".  One way to do
         this could be to continue parsing this line-by-line and make
         a temp iobuf for each key. */

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

      import_print_stats (stats_handle);
      import_release_stats_handle (stats_handle);
    }
  es_fclose (datastream);


  return err;
}
Ejemplo n.º 8
0
/* Insert the given fpr into our trustdb.  We expect FPR to be an all
   uppercase hexstring of 40 characters. FLAG is either 'P' or 'C'.
   This function does first check whether that key has already been put
   into the trustdb and returns success in this case.  Before a FPR
   actually gets inserted, the user is asked by means of the Pinentry
   whether this is actual want he wants to do.  */
gpg_error_t
agent_marktrusted (ctrl_t ctrl, const char *name, const char *fpr, int flag)
{
  gpg_error_t err = 0;
  char *desc;
  char *fname;
  estream_t fp;
  char *fprformatted;
  char *nameformatted;
  int is_disabled;
  int yes_i_trust;

  /* Check whether we are at all allowed to modify the trustlist.
     This is useful so that the trustlist may be a symlink to a global
     trustlist with only admin priviliges to modify it.  Of course
     this is not a secure way of denying access, but it avoids the
     usual clicking on an Okay button most users are used to. */
  fname = make_filename (opt.homedir, "trustlist.txt", NULL);
  if ( access (fname, W_OK) && errno != ENOENT)
    {
      xfree (fname);
      return gpg_error (GPG_ERR_EPERM);
    }
  xfree (fname);

  if (!agent_istrusted (ctrl, fpr, &is_disabled))
    {
      return 0; /* We already got this fingerprint.  Silently return
                   success. */
    }

  /* This feature must explicitly been enabled. */
  if (!opt.allow_mark_trusted)
    return gpg_error (GPG_ERR_NOT_SUPPORTED);

  if (is_disabled)
    {
      /* There is an disabled entry in the trustlist.  Return an error
         so that the user won't be asked again for that one.  Changing
         this flag with the integrated marktrusted feature is and will
         not be made possible.  */
      return gpg_error (GPG_ERR_NOT_TRUSTED);
    }


  /* Insert a new one. */
  nameformatted = reformat_name (name, "%0A   ");
  if (!nameformatted)
    return gpg_error_from_syserror ();

  /* First a general question whether this is trusted.  */
  desc = xtryasprintf (
                /* TRANSLATORS: This prompt is shown by the Pinentry
                   and has one special property: A "%%0A" is used by
                   Pinentry to insert a line break.  The double
                   percent sign is actually needed because it is also
                   a printf format string.  If you need to insert a
                   plain % sign, you need to encode it as "%%25".  The
                   "%s" gets replaced by the name as stored in the
                   certificate. */
                _("Do you ultimately trust%%0A"
                  "  \"%s\"%%0A"
                  "to correctly certify user certificates?"),
                nameformatted);
  if (!desc)
    {
      xfree (nameformatted);
      return out_of_core ();
    }
  err = agent_get_confirmation (ctrl, desc, _("Yes"), _("No"), 1);
  xfree (desc);
  if (!err)
    yes_i_trust = 1;
  else if (gpg_err_code (err) == GPG_ERR_NOT_CONFIRMED)
    yes_i_trust = 0;
  else
    {
      xfree (nameformatted);
      return err;
    }


  fprformatted = insert_colons (fpr);
  if (!fprformatted)
    {
      xfree (nameformatted);
      return out_of_core ();
    }

  /* If the user trusts this certificate he has to verify the
     fingerprint of course.  */
  if (yes_i_trust)
    {
      desc = xtryasprintf
        (
         /* TRANSLATORS: This prompt is shown by the Pinentry and has
            one special property: A "%%0A" is used by Pinentry to
            insert a line break.  The double percent sign is actually
            needed because it is also a printf format string.  If you
            need to insert a plain % sign, you need to encode it as
            "%%25".  The second "%s" gets replaced by a hexdecimal
            fingerprint string whereas the first one receives the name
            as stored in the certificate. */
         _("Please verify that the certificate identified as:%%0A"
           "  \"%s\"%%0A"
           "has the fingerprint:%%0A"
           "  %s"), nameformatted, fprformatted);
      if (!desc)
        {
          xfree (fprformatted);
          xfree (nameformatted);
          return out_of_core ();
        }

      /* TRANSLATORS: "Correct" is the label of a button and intended
         to be hit if the fingerprint matches the one of the CA.  The
         other button is "the default "Cancel" of the Pinentry. */
      err = agent_get_confirmation (ctrl, desc, _("Correct"), _("Wrong"), 1);
      xfree (desc);
      if (gpg_err_code (err) == GPG_ERR_NOT_CONFIRMED)
        yes_i_trust = 0;
      else if (err)
        {
          xfree (fprformatted);
          xfree (nameformatted);
          return err;
        }
    }


  /* Now check again to avoid duplicates.  We take the lock to make
     sure that nobody else plays with our file and force a reread.  */
  lock_trusttable ();
  agent_reload_trustlist ();
  if (!agent_istrusted (ctrl, fpr, &is_disabled) || is_disabled)
    {
      unlock_trusttable ();
      xfree (fprformatted);
      xfree (nameformatted);
      return is_disabled? gpg_error (GPG_ERR_NOT_TRUSTED) : 0;
    }

  fname = make_filename (opt.homedir, "trustlist.txt", NULL);
  if ( access (fname, F_OK) && errno == ENOENT)
    {
      fp = es_fopen (fname, "wx,mode=-rw-r");
      if (!fp)
        {
          err = gpg_error_from_syserror ();
          log_error ("can't create `%s': %s\n", fname, gpg_strerror (err));
          xfree (fname);
          unlock_trusttable ();
          xfree (fprformatted);
          xfree (nameformatted);
          return err;
        }
      es_fputs (headerblurb, fp);
      es_fclose (fp);
    }
  fp = es_fopen (fname, "a+,mode=-rw-r");
  if (!fp)
    {
      err = gpg_error_from_syserror ();
      log_error ("can't open `%s': %s\n", fname, gpg_strerror (err));
      xfree (fname);
      unlock_trusttable ();
      xfree (fprformatted);
      xfree (nameformatted);
      return err;
    }

  /* Append the key. */
  es_fputs ("\n# ", fp);
  xfree (nameformatted);
  nameformatted = reformat_name (name, "\n# ");
  if (!nameformatted || strchr (name, '\n'))
    {
      /* Note that there should never be a LF in NAME but we better
         play safe and print a sanitized version in this case.  */
      es_write_sanitized (fp, name, strlen (name), NULL, NULL);
    }
  else
    es_fputs (nameformatted, fp);
  es_fprintf (fp, "\n%s%s %c\n", yes_i_trust?"":"!", fprformatted, flag);
  if (es_ferror (fp))
    err = gpg_error_from_syserror ();

  if (es_fclose (fp))
    err = gpg_error_from_syserror ();

  agent_reload_trustlist ();
  xfree (fname);
  unlock_trusttable ();
  xfree (fprformatted);
  xfree (nameformatted);
  return err;
}
Ejemplo n.º 9
0
/* Delete the key with GRIP from the disk after having asked for
   confirmation using DESC_TEXT.  If FORCE is set the function won't
   require a confirmation via Pinentry or warns if the key is also
   used by ssh.

   Common error codes are:
     GPG_ERR_NO_SECKEY
     GPG_ERR_KEY_ON_CARD
     GPG_ERR_NOT_CONFIRMED
*/
gpg_error_t
agent_delete_key (ctrl_t ctrl, const char *desc_text,
                  const unsigned char *grip, int force)
{
  gpg_error_t err;
  gcry_sexp_t s_skey = NULL;
  unsigned char *buf = NULL;
  size_t len;
  char *desc_text_final = NULL;
  char *comment = NULL;
  ssh_control_file_t cf = NULL;
  char hexgrip[40+4+1];
  char *default_desc = NULL;

  err = read_key_file (grip, &s_skey);
  if (gpg_err_code (err) == GPG_ERR_ENOENT)
    err = gpg_error (GPG_ERR_NO_SECKEY);
  if (err)
    goto leave;

  err = make_canon_sexp (s_skey, &buf, &len);
  if (err)
    goto leave;

  switch (agent_private_key_type (buf))
    {
    case PRIVATE_KEY_CLEAR:
    case PRIVATE_KEY_OPENPGP_NONE:
    case PRIVATE_KEY_PROTECTED:
      bin2hex (grip, 20, hexgrip);
      if (!force)
        {
          if (!desc_text)
            {
              default_desc = xtryasprintf
          (L_("Do you really want to delete the key identified by keygrip%%0A"
              "  %s%%0A  %%C%%0A?"), hexgrip);
              desc_text = default_desc;
            }

          /* Note, that we will take the comment as a C string for
             display purposes; i.e. all stuff beyond a Nul character is
             ignored.  */
          {
            gcry_sexp_t comment_sexp;

            comment_sexp = gcry_sexp_find_token (s_skey, "comment", 0);
            if (comment_sexp)
              comment = gcry_sexp_nth_string (comment_sexp, 1);
            gcry_sexp_release (comment_sexp);
          }

          if (desc_text)
            err = modify_description (desc_text, comment? comment:"", s_skey,
                                      &desc_text_final);
          if (err)
            goto leave;

          err = agent_get_confirmation (ctrl, desc_text_final,
                                        L_("Delete key"), L_("No"), 0);
          if (err)
            goto leave;

          cf = ssh_open_control_file ();
          if (cf)
            {
              if (!ssh_search_control_file (cf, hexgrip, NULL, NULL, NULL))
                {
                  err = agent_get_confirmation
                    (ctrl,
                     L_("Warning: This key is also listed for use with SSH!\n"
                        "Deleting the key might remove your ability to "
                        "access remote machines."),
                     L_("Delete key"), L_("No"), 0);
                  if (err)
                    goto leave;
                }
            }
        }
      err = remove_key_file (grip);
      break;

    case PRIVATE_KEY_SHADOWED:
      err = remove_key_file (grip);
      break;

    default:
      log_error ("invalid private key format\n");
      err = gpg_error (GPG_ERR_BAD_SECKEY);
      break;
    }

 leave:
  ssh_close_control_file (cf);
  gcry_free (comment);
  xfree (desc_text_final);
  xfree (default_desc);
  xfree (buf);
  gcry_sexp_release (s_skey);
  return err;
}
Ejemplo n.º 10
0
/* Map the host name NAME to the actual to be used host name.  This
   allows us to manage round robin DNS names.  We use our own strategy
   to choose one of the hosts.  For example we skip those hosts which
   failed for some time and we stick to one host for a time
   independent of DNS retry times.  If FORCE_RESELECT is true a new
   host is always selected.  The selected host is stored as a malloced
   string at R_HOST; on error NULL is stored.  If we know the port
   used by the selected host, a string representation is written to
   R_PORTSTR, otherwise it is left untouched.  If R_HTTPFLAGS is not
   NULL it will receive flags which are to be passed to http_open.  If
   R_POOLNAME is not NULL a malloced name of the pool is stored or
   NULL if it is not a pool. */
static gpg_error_t
map_host (ctrl_t ctrl, const char *name, int force_reselect,
          char **r_host, char *r_portstr,
          unsigned int *r_httpflags, char **r_poolname)
{
  gpg_error_t err = 0;
  hostinfo_t hi;
  int idx;

  *r_host = NULL;
  if (r_httpflags)
    *r_httpflags = 0;
  if (r_poolname)
    *r_poolname = NULL;

  /* No hostname means localhost.  */
  if (!name || !*name)
    {
      *r_host = xtrystrdup ("localhost");
      return *r_host? 0 : gpg_error_from_syserror ();
    }

  /* See whether the host is in our table.  */
  idx = find_hostinfo (name);
  if (idx == -1 && is_onion_address (name))
    {
      idx = create_new_hostinfo (name);
      if (idx == -1)
        return gpg_error_from_syserror ();
      hi = hosttable[idx];
      hi->onion = 1;
    }
  else if (idx == -1)
    {
      /* We never saw this host.  Allocate a new entry.  */
      dns_addrinfo_t aibuf, ai;
      int *reftbl;
      size_t reftblsize;
      int refidx;
      int is_pool = 0;
      char *cname;
#ifdef	USE_DNS_SRV
      char *srvrecord;
      struct srventry *srvs;
      int srvscount;
#endif	/* USE_DNS_SRV */

      reftblsize = 100;
      reftbl = xtrymalloc (reftblsize * sizeof *reftbl);
      if (!reftbl)
        return gpg_error_from_syserror ();
      refidx = 0;

      idx = create_new_hostinfo (name);
      if (idx == -1)
        {
          err = gpg_error_from_syserror ();
          xfree (reftbl);
          return err;
        }
      hi = hosttable[idx];

#ifdef	USE_DNS_SRV
      /* Check for SRV records.  */
      srvrecord = xtryasprintf ("_hkp._tcp.%s", name);
      if (srvrecord == NULL)
        {
          err = gpg_error_from_syserror ();
          xfree (reftbl);
          return err;
        }

      srvscount = getsrv (srvrecord, &srvs);
      xfree (srvrecord);
      if (srvscount < 0)
        {
          err = gpg_error_from_syserror ();
          xfree (reftbl);
          return err;
        }

      if (srvscount > 0)
        {
          int i;
          is_pool = srvscount > 1;

          for (i = 0; i < srvscount; i++)
            {
              err = resolve_dns_name (srvs[i].target, 0,
                                      AF_UNSPEC, SOCK_STREAM,
                                      &ai, &cname);
              if (err)
                continue;
              dirmngr_tick (ctrl);
              add_host (name, is_pool, ai, srvs[i].port,
                        reftbl, reftblsize, &refidx);
            }

          xfree (srvs);
        }
#endif	/* USE_DNS_SRV */

      /* Find all A records for this entry and put them into the pool
         list - if any.  */
      err = resolve_dns_name (name, 0, 0, SOCK_STREAM, &aibuf, &cname);
      if (err)
        {
          log_error ("resolving '%s' failed: %s\n", name, gpg_strerror (err));
          err = 0;
        }
      else
        {
          /* First figure out whether this is a pool.  For a pool we
             use a different strategy than for a plain server: We use
             the canonical name of the pool as the virtual host along
             with the IP addresses.  If it is not a pool, we use the
             specified name. */
          if (! is_pool)
            is_pool = arecords_is_pool (aibuf);
          if (is_pool && cname)
            {
              hi->cname = cname;
              cname = NULL;
            }

          for (ai = aibuf; ai; ai = ai->next)
            {
              if (ai->family != AF_INET && ai->family != AF_INET6)
                continue;
              dirmngr_tick (ctrl);

              add_host (name, is_pool, ai, 0, reftbl, reftblsize, &refidx);
            }
        }
      reftbl[refidx] = -1;
      xfree (cname);
      free_dns_addrinfo (aibuf);

      if (refidx && is_pool)
        {
          assert (!hi->pool);
          hi->pool = xtryrealloc (reftbl, (refidx+1) * sizeof *reftbl);
          if (!hi->pool)
            {
              err = gpg_error_from_syserror ();
              log_error ("shrinking index table in map_host failed: %s\n",
                         gpg_strerror (err));
              xfree (reftbl);
              return err;
            }
          qsort (hi->pool, refidx, sizeof *reftbl, sort_hostpool);
        }
      else
        xfree (reftbl);
    }

  hi = hosttable[idx];
  if (hi->pool)
    {
      /* Deal with the pool name before selecting a host. */
      if (r_poolname)
        {
          *r_poolname = xtrystrdup (hi->cname? hi->cname : hi->name);
          if (!*r_poolname)
            return gpg_error_from_syserror ();
        }

      /* If the currently selected host is now marked dead, force a
         re-selection .  */
      if (force_reselect)
        hi->poolidx = -1;
      else if (hi->poolidx >= 0 && hi->poolidx < hosttable_size
               && hosttable[hi->poolidx] && hosttable[hi->poolidx]->dead)
        hi->poolidx = -1;

      /* Select a host if needed.  */
      if (hi->poolidx == -1)
        {
          hi->poolidx = select_random_host (hi->pool);
          if (hi->poolidx == -1)
            {
              log_error ("no alive host found in pool '%s'\n", name);
              if (r_poolname)
                {
                  xfree (*r_poolname);
                  *r_poolname = NULL;
                }
              return gpg_error (GPG_ERR_NO_KEYSERVER);
            }
        }

      assert (hi->poolidx >= 0 && hi->poolidx < hosttable_size);
      hi = hosttable[hi->poolidx];
      assert (hi);
    }

  if (hi->dead)
    {
      log_error ("host '%s' marked as dead\n", hi->name);
      if (r_poolname)
        {
          xfree (*r_poolname);
          *r_poolname = NULL;
        }
      return gpg_error (GPG_ERR_NO_KEYSERVER);
    }

  if (r_httpflags)
    {
      /* If the hosttable does not indicate that a certain host
         supports IPv<N>, we explicit set the corresponding http
         flags.  The reason for this is that a host might be listed in
         a pool as not v6 only but actually support v6 when later
         the name is resolved by our http layer.  */
      if (!hi->v4)
        *r_httpflags |= HTTP_FLAG_IGNORE_IPv4;
      if (!hi->v6)
        *r_httpflags |= HTTP_FLAG_IGNORE_IPv6;

      /* Note that we do not set the HTTP_FLAG_FORCE_TOR for onion
         addresses because the http module detects this itself.  This
         also allows us to use an onion address without Tor mode being
         enabled.  */
    }

  *r_host = xtrystrdup (hi->name);
  if (!*r_host)
    {
      err = gpg_error_from_syserror ();
      if (r_poolname)
        {
          xfree (*r_poolname);
          *r_poolname = NULL;
        }
      return err;
    }
  if (hi->port)
    snprintf (r_portstr, 6 /* five digits and the sentinel */,
              "%hu", hi->port);
  return 0;
}