コード例 #1
0
ファイル: certdump.c プロジェクト: FMayzek/gnupg
/* Return a new string holding the format serial number and issuer
   ("#SN/issuer").  No filtering on invalid characters is done.
   Caller must release the string.  On memory failure NULL is
   returned.  */
char *
gpgsm_format_sn_issuer (ksba_sexp_t sn, const char *issuer)
{
  char *p, *p1;

  if (sn && issuer)
    {
      p1 = gpgsm_format_serial (sn);
      if (!p1)
        p = xtrystrdup ("[invalid SN]");
      else
        {
          p = xtrymalloc (strlen (p1) + strlen (issuer) + 2 + 1);
          if (p)
            {
              *p = '#';
              strcpy (stpcpy (stpcpy (p+1, p1),"/"), issuer);
            }
          xfree (p1);
        }
    }
  else
    p = xtrystrdup ("[invalid SN/issuer]");
  return p;
}
コード例 #2
0
/* Allocate and initialize the context for the base64 decoder.  If
   TITLE is NULL a plain base64 decoding is done.  If it is the empty
   string the decoder will skip everything until a "-----BEGIN " line
   has been seen, decoding ends at a "----END " line.  */
gpgrt_b64state_t
_gpgrt_b64dec_start (const char *title)
{
  gpgrt_b64state_t state;
  char *t = NULL;

  if (title)
    {
      t = xtrystrdup (title);
      if (!t)
        return NULL;
    }

  state = xtrycalloc (1, sizeof (struct _gpgrt_b64state));
  if (!state)
    {
      xfree (t);
      return NULL;
    }

  if (t)
    {
      state->title = t;
      state->idx = s_init;
    }
  else
    state->idx = s_b64_0;

  state->using_decoder = 1;

  return state;
}
コード例 #3
0
ファイル: trustlist.c プロジェクト: CryptoBITDigital/gnupg-1
/* To pretty print DNs in the Pinentry, we replace slashes by
   REPLSTRING.  The caller needs to free the returned string.  NULL is
   returned on error with ERRNO set.  */
static char *
reformat_name (const char *name, const char *replstring)
{
  const char *s;
  char *newname;
  char *d;
  size_t count;
  size_t replstringlen = strlen (replstring);

  /* If the name does not start with a slash it is not a preformatted
     DN and thus we don't bother to reformat it.  */
  if (*name != '/')
    return xtrystrdup (name);

  /* Count the names.  Note that a slash contained in a DN part is
     expected to be C style escaped and thus the slashes we see here
     are the actual part delimiters.  */
  for (s=name+1, count=0; *s; s++)
    if (*s == '/')
      count++;
  newname = xtrymalloc (strlen (name) + count*replstringlen + 1);
  if (!newname)
    return NULL;
  for (s=name+1, d=newname; *s; s++)
    if (*s == '/')
      d = stpcpy (d, replstring);
    else
      *d++ = *s;
  *d = 0;
  return newname;
}
コード例 #4
0
ファイル: mbox-util.c プロジェクト: gilbert-fernandes/gnupg
/* Return the mailbox (local-part@domain) form a standard user id.
   All plain ASCII characters in the result are converted to
   lowercase.  Caller must free the result.  Returns NULL if no valid
   mailbox was found (or we are out of memory). */
char *
mailbox_from_userid (const char *userid)
{
  const char *s, *s_end;
  size_t len;
  char *result = NULL;

  s = strchr (userid, '<');
  if (s)
    {
      /* Seems to be a standard user id.  */
      s++;
      s_end = strchr (s, '>');
      if (s_end && s_end > s)
        {
          len = s_end - s;
          result = xtrymalloc (len + 1);
          if (!result)
            return NULL; /* Ooops - out of core.  */
          strncpy (result, s, len);
          result[len] = 0;
          /* Apply some basic checks on the address.  We do not use
             is_valid_mailbox because those checks are too strict.  */
          if (string_count_chr (result, '@') != 1  /* Need exactly one '@.  */
              || *result == '@'           /* local-part missing.  */
              || result[len-1] == '@'     /* domain missing.  */
              || result[len-1] == '.'     /* ends with a dot.  */
              || string_has_ctrl_or_space (result)
              || has_dotdot_after_at (result))
            {
              xfree (result);
              result = NULL;
              errno = EINVAL;
            }
        }
      else
        errno = EINVAL;
    }
  else if (is_valid_mailbox (userid))
    {
      /* The entire user id is a mailbox.  Return that one.  Note that
         this fallback method has some restrictions on the valid
         syntax of the mailbox.  However, those who want weird
         addresses should know about it and use the regular <...>
         syntax.  */
      result = xtrystrdup (userid);
    }
  else
    errno = EINVAL;

  return result? ascii_strlwr (result): NULL;
}
コード例 #5
0
ファイル: misc.c プロジェクト: FMayzek/gnupg
/* Return the host name and the port (0 if none was given) from the
   URL.  Return NULL on error or if host is not included in the
   URL.  */
char *
host_and_port_from_url (const char *url, int *port)
{
  const char *s, *s2;
  char *buf, *p;
  int n;

  s = url;

  *port = 0;

  /* Find the scheme */
  if ( !(s2 = strchr (s, ':')) || s2 == s )
    return NULL;  /* No scheme given. */
  s = s2+1;

  /* Find the hostname */
  if (*s != '/')
    return NULL; /* Does not start with a slash. */

  s++;
  if (*s != '/')
    return NULL; /* No host name.  */
  s++;

  buf = xtrystrdup (s);
  if (!buf)
    {
      log_error (_("malloc failed: %s\n"), strerror (errno));
      return NULL;
    }
  if ((p = strchr (buf, '/')))
    *p++ = 0;
  strlwr (buf);
  if ((p = strchr (p, ':')))
    {
      *p++ = 0;
      *port = atoi (p);
    }

  /* Remove quotes and make sure that no Nul has been encoded. */
  if ((n = remove_percent_escapes (buf)) < 0
      || n != strlen (buf) )
    {
      log_error (_("bad URL encoding detected\n"));
      xfree (buf);
      return NULL;
    }

  return buf;
}
コード例 #6
0
ファイル: b64dec.c プロジェクト: GroovIM/transport
/* Initialize the context for the base64 decoder.  If TITLE is NULL a
   plain base64 decoding is done.  If it is the empty string the
   decoder will skip everything until a "-----BEGIN " line has been
   seen, decoding ends at a "----END " line.
   
   Not yet implemented: If TITLE is either "PGP" or begins with "PGP "
   the PGP armor lines are skipped as well.  */
gpg_error_t
b64dec_start (struct b64state *state, const char *title)
{
  memset (state, 0, sizeof *state);
  if (title)
    {
      if (!strncmp (title, "PGP", 3) && (!title[3] || title[3] == ' '))
        return gpg_error (GPG_ERR_NOT_IMPLEMENTED);

      state->title = xtrystrdup (title);
      if (!state->title)
        return gpg_error_from_syserror ();
      state->idx = s_init;
    }
  else
    state->idx = s_b64_0;
  return 0;
}
コード例 #7
0
ファイル: helpfile.c プロジェクト: GroovIM/transport
char *
gnupg_get_help_string (const char *key, int only_current_locale)
{
  static const char *locname;
  char *result;

  if (!locname)
    {
      char *buffer, *p;
      int count = 0;
      const char *s = gnupg_messages_locale_name ();
      buffer = xtrystrdup (s);
      if (!buffer)
        locname = "";
      else
        {
          for (p = buffer; *p; p++)
            if (*p == '.' || *p == '@' || *p == '/' /*(safeguard)*/)
              *p = 0;
            else if (*p == '_')
              {
                if (count++)
                  *p = 0;  /* Also cut at a underscore in the territory.  */
              }
          locname = buffer;
        }
    }

  if (!key || !*key)
    return NULL;

  result = findkey_locale (key, locname, only_current_locale, 
                           gnupg_sysconfdir ());
  if (!result)
    result = findkey_locale (key, locname, only_current_locale,
                             gnupg_datadir ());

  if (result)
    trim_trailing_spaces (result);

  return result;
}
コード例 #8
0
ファイル: be-encfs.c プロジェクト: 0ndorio/gnupg
/* Create the container described by the filename FNAME and the keyblob
   information in TUPLES. */
gpg_error_t
be_encfs_create_container (ctrl_t ctrl, const char *fname, tupledesc_t tuples,
                           unsigned int *r_id)
{
  gpg_error_t err;
  int dummy;
  char *containername = NULL;
  char *mountpoint = NULL;

  err = be_encfs_get_detached_name (fname, &containername, &dummy);
  if (err)
    goto leave;

  mountpoint = xtrystrdup ("/tmp/.#g13_XXXXXX");
  if (!mountpoint)
    {
      err = gpg_error_from_syserror ();
      goto leave;
    }
  if (!gnupg_mkdtemp (mountpoint))
    {
      err = gpg_error_from_syserror ();
      log_error (_("can't create directory '%s': %s\n"),
                 "/tmp/.#g13_XXXXXX", gpg_strerror (err));
      goto leave;
    }

  err = run_encfs_tool (ctrl, ENCFS_CMD_CREATE, containername, mountpoint,
                        tuples, r_id);

  /* In any case remove the temporary mount point.  */
  if (rmdir (mountpoint))
    log_error ("error removing temporary mount point '%s': %s\n",
               mountpoint, gpg_strerror (gpg_error_from_syserror ()));


 leave:
  xfree (containername);
  xfree (mountpoint);
  return err;
}
コード例 #9
0
ファイル: app.c プロジェクト: Juul/gnupg
/* Retrieve the serial number and the time of the last update of the
   card.  The serial number is returned as a malloced string (hex
   encoded) in SERIAL and the time of update is returned in STAMP.  If
   no update time is available the returned value is 0.  Caller must
   free SERIAL unless the function returns an error.  If STAMP is not
   of interest, NULL may be passed. */
gpg_error_t
app_get_serial_and_stamp (app_t app, char **serial, time_t *stamp)
{
  char *buf;

  if (!app || !serial)
    return gpg_error (GPG_ERR_INV_VALUE);

  *serial = NULL;
  if (stamp)
    *stamp = 0; /* not available */

  if (!app->serialnolen)
    buf = xtrystrdup ("FF7F00");
  else
    buf = bin2hex (app->serialno, app->serialnolen, NULL);
  if (!buf)
    return gpg_error_from_syserror ();

  *serial = buf;
  return 0;
}
コード例 #10
0
ファイル: findkey.c プロジェクト: Juul/gnupg
/* Unprotect the canconical encoded S-expression key in KEYBUF.  GRIP
   should be the hex encoded keygrip of that key to be used with the
   caching mechanism. DESC_TEXT may be set to override the default
   description used for the pinentry.  If LOOKUP_TTL is given this
   function is used to lookup the default ttl.  If R_PASSPHRASE is not
   NULL, the function succeeded and the key was protected the used
   passphrase (entered or from the cache) is stored there; if not NULL
   will be stored.  The caller needs to free the returned
   passphrase. */
static int
unprotect (ctrl_t ctrl, const char *cache_nonce, const char *desc_text,
           unsigned char **keybuf, const unsigned char *grip,
           cache_mode_t cache_mode, lookup_ttl_t lookup_ttl,
           char **r_passphrase)
{
  struct pin_entry_info_s *pi;
  struct try_unprotect_arg_s arg;
  int rc;
  unsigned char *result;
  size_t resultlen;
  char hexgrip[40+1];

  if (r_passphrase)
    *r_passphrase = NULL;

  bin2hex (grip, 20, hexgrip);

  /* Initially try to get it using a cache nonce.  */
  if (cache_nonce)
    {
      char *pw;

      pw = agent_get_cache (cache_nonce, CACHE_MODE_NONCE);
      if (pw)
        {
          rc = agent_unprotect (*keybuf, pw, NULL, &result, &resultlen);
          if (!rc)
            {
              if (r_passphrase)
                *r_passphrase = pw;
              else
                xfree (pw);
              xfree (*keybuf);
              *keybuf = result;
              return 0;
            }
          xfree (pw);
        }
    }

  /* First try to get it from the cache - if there is none or we can't
     unprotect it, we fall back to ask the user */
  if (cache_mode != CACHE_MODE_IGNORE)
    {
      char *pw;

    retry:
      pw = agent_get_cache (hexgrip, cache_mode);
      if (pw)
        {
          rc = agent_unprotect (*keybuf, pw, NULL, &result, &resultlen);
          if (!rc)
            {
              if (r_passphrase)
                *r_passphrase = pw;
              else
                xfree (pw);
              xfree (*keybuf);
              *keybuf = result;
              return 0;
            }
          xfree (pw);
          rc  = 0;
        }

      /* If the pinentry is currently in use, we wait up to 60 seconds
         for it to close and check the cache again.  This solves a common
         situation where several requests for unprotecting a key have
         been made but the user is still entering the passphrase for
         the first request.  Because all requests to agent_askpin are
         serialized they would then pop up one after the other to
         request the passphrase - despite that the user has already
         entered it and is then available in the cache.  This
         implementation is not race free but in the worst case the
         user has to enter the passphrase only once more. */
      if (pinentry_active_p (ctrl, 0))
        {
          /* Active - wait */
          if (!pinentry_active_p (ctrl, 60))
            {
              /* We need to give the other thread a chance to actually put
                 it into the cache. */
              npth_sleep (1);
              goto retry;
            }
          /* Timeout - better call pinentry now the plain way. */
        }
    }

  pi = gcry_calloc_secure (1, sizeof (*pi) + 100);
  if (!pi)
    return gpg_error_from_syserror ();
  pi->max_length = 100;
  pi->min_digits = 0;  /* we want a real passphrase */
  pi->max_digits = 16;
  pi->max_tries = 3;
  pi->check_cb = try_unprotect_cb;
  arg.ctrl = ctrl;
  arg.protected_key = *keybuf;
  arg.unprotected_key = NULL;
  arg.change_required = 0;
  pi->check_cb_arg = &arg;

  rc = agent_askpin (ctrl, desc_text, NULL, NULL, pi);
  if (!rc)
    {
      assert (arg.unprotected_key);
      if (arg.change_required)
        {
          size_t canlen, erroff;
          gcry_sexp_t s_skey;

          assert (arg.unprotected_key);
          canlen = gcry_sexp_canon_len (arg.unprotected_key, 0, NULL, NULL);
          rc = gcry_sexp_sscan (&s_skey, &erroff,
                                (char*)arg.unprotected_key, canlen);
          if (rc)
            {
              log_error ("failed to build S-Exp (off=%u): %s\n",
                         (unsigned int)erroff, gpg_strerror (rc));
              wipememory (arg.unprotected_key, canlen);
              xfree (arg.unprotected_key);
              xfree (pi);
              return rc;
            }
          rc = agent_protect_and_store (ctrl, s_skey, NULL);
          gcry_sexp_release (s_skey);
          if (rc)
            {
              log_error ("changing the passphrase failed: %s\n",
                         gpg_strerror (rc));
              wipememory (arg.unprotected_key, canlen);
              xfree (arg.unprotected_key);
              xfree (pi);
              return rc;
            }
        }
      else
        {
          agent_put_cache (hexgrip, cache_mode, pi->pin,
                           lookup_ttl? lookup_ttl (hexgrip) : 0);
          if (r_passphrase && *pi->pin)
            *r_passphrase = xtrystrdup (pi->pin);
        }
      xfree (*keybuf);
      *keybuf = arg.unprotected_key;
    }
  xfree (pi);
  return rc;
}
コード例 #11
0
ファイル: keyserver.c プロジェクト: Juul/gnupg
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;
}
コード例 #12
0
ファイル: asshelp.c プロジェクト: Domikk/gnupg
/* Send the assuan commands pertaining to the pinentry environment.  The
   OPT_* arguments are optional and may be used to override the
   defaults taken from the current locale. */
gpg_error_t
send_pinentry_environment (assuan_context_t ctx,
                           gpg_err_source_t errsource,
                           const char *opt_lc_ctype,
                           const char *opt_lc_messages,
                           session_env_t session_env)

{
  gpg_error_t err = 0;
#if defined(HAVE_SETLOCALE)
  char *old_lc = NULL;
#endif
  char *dft_lc = NULL;
  const char *dft_ttyname;
  int iterator;
  const char *name, *assname, *value;
  int is_default;

  iterator = 0;
  while ((name = session_env_list_stdenvnames (&iterator, &assname)))
    {
      value = session_env_getenv_or_default (session_env, name, NULL);
      if (!value)
        continue;

      if (assname)
        err = send_one_option (ctx, errsource, assname, value, 0);
      else
        {
          err = send_one_option (ctx, errsource, name, value, 1);
          if (gpg_err_code (err) == GPG_ERR_UNKNOWN_OPTION)
            err = 0;  /* Server too old; can't pass the new envvars.  */
        }
      if (err)
        return err;
    }


  dft_ttyname = session_env_getenv_or_default (session_env, "GPG_TTY",
                                               &is_default);
  if (dft_ttyname && !is_default)
    dft_ttyname = NULL;  /* We need the default value.  */

  /* Send the value for LC_CTYPE.  */
#if defined(HAVE_SETLOCALE) && defined(LC_CTYPE)
  old_lc = setlocale (LC_CTYPE, NULL);
  if (old_lc)
    {
      old_lc = xtrystrdup (old_lc);
      if (!old_lc)
        return gpg_error_from_syserror ();
    }
  dft_lc = setlocale (LC_CTYPE, "");
#endif
  if (opt_lc_ctype || (dft_ttyname && dft_lc))
    {
      err = send_one_option (ctx, errsource, "lc-ctype",
                             opt_lc_ctype ? opt_lc_ctype : dft_lc, 0);
    }
#if defined(HAVE_SETLOCALE) && defined(LC_CTYPE)
  if (old_lc)
    {
      setlocale (LC_CTYPE, old_lc);
      xfree (old_lc);
    }
#endif
  if (err)
    return err;

  /* Send the value for LC_MESSAGES.  */
#if defined(HAVE_SETLOCALE) && defined(LC_MESSAGES)
  old_lc = setlocale (LC_MESSAGES, NULL);
  if (old_lc)
    {
      old_lc = xtrystrdup (old_lc);
      if (!old_lc)
        return gpg_error_from_syserror ();
    }
  dft_lc = setlocale (LC_MESSAGES, "");
#endif
  if (opt_lc_messages || (dft_ttyname && dft_lc))
    {
      err = send_one_option (ctx, errsource, "lc-messages",
                             opt_lc_messages ? opt_lc_messages : dft_lc, 0);
    }
#if defined(HAVE_SETLOCALE) && defined(LC_MESSAGES)
  if (old_lc)
    {
      setlocale (LC_MESSAGES, old_lc);
      xfree (old_lc);
    }
#endif
  if (err)
    return err;

  return 0;
}
コード例 #13
0
ファイル: server.c プロジェクト: CryptoBITDigital/gnupg-1
static gpg_error_t
option_handler (assuan_context_t ctx, const char *key, const char *value)
{
  ctrl_t ctrl = assuan_get_pointer (ctx);
  gpg_error_t err = 0;

  if (!strcmp (key, "putenv"))
    {
      /* Change the session's environment to be used for the
         Pinentry.  Valid values are:
          <NAME>            Delete envvar NAME
          <KEY>=            Set envvar NAME to the empty string
          <KEY>=<VALUE>     Set envvar NAME to VALUE
      */
      err = session_env_putenv (opt.session_env, value);
    }
  else if (!strcmp (key, "display"))
    {
      err = session_env_setenv (opt.session_env, "DISPLAY", value);
    }
  else if (!strcmp (key, "ttyname"))
    {
      err = session_env_setenv (opt.session_env, "GPG_TTY", value);
    }
  else if (!strcmp (key, "ttytype"))
    {
      err = session_env_setenv (opt.session_env, "TERM", value);
    }
  else if (!strcmp (key, "lc-ctype"))
    {
      xfree (opt.lc_ctype);
      opt.lc_ctype = xtrystrdup (value);
      if (!opt.lc_ctype)
        err = gpg_error_from_syserror ();
    }
  else if (!strcmp (key, "lc-messages"))
    {
      xfree (opt.lc_messages);
      opt.lc_messages = xtrystrdup (value);
      if (!opt.lc_messages)
        err = gpg_error_from_syserror ();
    }
  else if (!strcmp (key, "xauthority"))
    {
      err = session_env_setenv (opt.session_env, "XAUTHORITY", value);
    }
  else if (!strcmp (key, "pinentry-user-data"))
    {
      err = session_env_setenv (opt.session_env, "PINENTRY_USER_DATA", value);
    }
  else if (!strcmp (key, "include-certs"))
    {
      int i = *value? atoi (value) : -1;
      if (ctrl->include_certs < -2)
        err = gpg_error (GPG_ERR_ASS_PARAMETER);
      else
        ctrl->include_certs = i;
    }
  else if (!strcmp (key, "list-mode"))
    {
      int i = *value? atoi (value) : 0;
      if (!i || i == 1) /* default and mode 1 */
        {
          ctrl->server_local->list_internal = 1;
          ctrl->server_local->list_external = 0;
        }
      else if (i == 2)
        {
          ctrl->server_local->list_internal = 0;
          ctrl->server_local->list_external = 1;
        }
      else if (i == 3)
        {
          ctrl->server_local->list_internal = 1;
          ctrl->server_local->list_external = 1;
        }
      else
        err = gpg_error (GPG_ERR_ASS_PARAMETER);
    }
  else if (!strcmp (key, "list-to-output"))
    {
      int i = *value? atoi (value) : 0;
      ctrl->server_local->list_to_output = i;
    }
  else if (!strcmp (key, "with-validation"))
    {
      int i = *value? atoi (value) : 0;
      ctrl->with_validation = i;
    }
  else if (!strcmp (key, "validation-model"))
    {
      int i = gpgsm_parse_validation_model (value);
      if ( i >= 0 && i <= 1 )
        ctrl->validation_model = i;
      else
        err = gpg_error (GPG_ERR_ASS_PARAMETER);
    }
  else if (!strcmp (key, "with-key-data"))
    {
      opt.with_key_data = 1;
    }
  else if (!strcmp (key, "enable-audit-log"))
    {
      int i = *value? atoi (value) : 0;
      ctrl->server_local->enable_audit_log = i;
    }
  else if (!strcmp (key, "allow-pinentry-notify"))
    {
      ctrl->server_local->allow_pinentry_notify = 1;
    }
  else if (!strcmp (key, "with-ephemeral-keys"))
    {
      int i = *value? atoi (value) : 0;
      ctrl->with_ephemeral_keys = i;
    }
  else if (!strcmp (key, "no-encrypt-to"))
    {
      ctrl->server_local->no_encrypt_to = 1;
    }
  else
    err = gpg_error (GPG_ERR_UNKNOWN_OPTION);

  return err;
}
コード例 #14
0
ファイル: ks-engine-hkp.c プロジェクト: Distrotech/gnupg
/* Send an HTTP request.  On success returns an estream object at
   R_FP.  HOSTPORTSTR is only used for diagnostics.  If HTTPHOST is
   not NULL it will be used as HTTP "Host" header.  If POST_CB is not
   NULL a post request is used and that callback is called to allow
   writing the post data.  */
static gpg_error_t
send_request (ctrl_t ctrl, const char *request, const char *hostportstr,
              const char *httphost, unsigned int httpflags,
              gpg_error_t (*post_cb)(void *, http_t), void *post_cb_value,
              estream_t *r_fp)
{
  gpg_error_t err;
  http_session_t session = NULL;
  http_t http = NULL;
  int redirects_left = MAX_REDIRECTS;
  estream_t fp = NULL;
  char *request_buffer = NULL;

  *r_fp = NULL;

  err = http_session_new (&session, NULL);
  if (err)
    goto leave;
  http_session_set_log_cb (session, cert_log_cb);

 once_more:
  err = http_open (&http,
                   post_cb? HTTP_REQ_POST : HTTP_REQ_GET,
                   request,
                   httphost,
                   /* fixme: AUTH */ NULL,
                   (httpflags | (opt.honor_http_proxy? HTTP_FLAG_TRY_PROXY:0)),
                   ctrl->http_proxy,
                   session,
                   NULL,
                   /*FIXME curl->srvtag*/NULL);
  if (!err)
    {
      fp = http_get_write_ptr (http);
      /* Avoid caches to get the most recent copy of the key.  We set
         both the Pragma and Cache-Control versions of the header, so
         we're good with both HTTP 1.0 and 1.1.  */
      es_fputs ("Pragma: no-cache\r\n"
                "Cache-Control: no-cache\r\n", fp);
      if (post_cb)
        err = post_cb (post_cb_value, http);
      if (!err)
        {
          http_start_data (http);
          if (es_ferror (fp))
            err = gpg_error_from_syserror ();
        }
    }
  if (err)
    {
      /* Fixme: After a redirection we show the old host name.  */
      log_error (_("error connecting to '%s': %s\n"),
                 hostportstr, gpg_strerror (err));
      goto leave;
    }

  /* Wait for the response.  */
  dirmngr_tick (ctrl);
  err = http_wait_response (http);
  if (err)
    {
      log_error (_("error reading HTTP response for '%s': %s\n"),
                 hostportstr, gpg_strerror (err));
      goto leave;
    }

  if (http_get_tls_info (http, NULL))
    {
      /* Update the httpflags so that a redirect won't fallback to an
         unencrypted connection.  */
      httpflags |= HTTP_FLAG_FORCE_TLS;
    }

  switch (http_get_status_code (http))
    {
    case 200:
      err = 0;
      break; /* Success.  */

    case 301:
    case 302:
    case 307:
      {
        const char *s = http_get_header (http, "Location");

        log_info (_("URL '%s' redirected to '%s' (%u)\n"),
                  request, s?s:"[none]", http_get_status_code (http));
        if (s && *s && redirects_left-- )
          {
            xfree (request_buffer);
            request_buffer = xtrystrdup (s);
            if (request_buffer)
              {
                request = request_buffer;
                http_close (http, 0);
                http = NULL;
                goto once_more;
              }
            err = gpg_error_from_syserror ();
          }
        else
          err = gpg_error (GPG_ERR_NO_DATA);
        log_error (_("too many redirections\n"));
      }
      goto leave;

    default:
      log_error (_("error accessing '%s': http status %u\n"),
                 request, http_get_status_code (http));
      err = gpg_error (GPG_ERR_NO_DATA);
      goto leave;
    }

  /* FIXME: We should register a permanent redirection and whether a
     host has ever used TLS so that future calls will always use
     TLS. */

  fp = http_get_read_ptr (http);
  if (!fp)
    {
      err = gpg_error (GPG_ERR_BUG);
      goto leave;
    }

  /* Return the read stream and close the HTTP context.  */
  *r_fp = fp;
  http_close (http, 1);
  http = NULL;

 leave:
  http_close (http, 0);
  http_session_release (session);
  xfree (request_buffer);
  return err;
}
コード例 #15
0
ファイル: be-encfs.c プロジェクト: 0ndorio/gnupg
/* Run the encfs tool.  */
static gpg_error_t
run_encfs_tool (ctrl_t ctrl, enum encfs_cmds cmd,
                const char *rawdir, const char *mountpoint, tupledesc_t tuples,
                unsigned int *r_id)
{
  gpg_error_t err;
  encfs_parm_t parm;
  runner_t runner = NULL;
  int outbound[2] = { -1, -1 };
  int inbound[2]  = { -1, -1 };
  const char *pgmname;
  const char *argv[10];
  pid_t pid = (pid_t)(-1);
  int idx;

  (void)ctrl;

  parm = xtrycalloc (1, sizeof *parm);
  if (!parm)
    {
      err = gpg_error_from_syserror ();
      goto leave;
    }
  parm->cmd = cmd;
  parm->tuples = ref_tupledesc (tuples);
  parm->mountpoint = xtrystrdup (mountpoint);
  if (!parm->mountpoint)
    {
      err = gpg_error_from_syserror ();
      goto leave;
    }

  err = runner_new (&runner, "encfs");
  if (err)
    goto leave;

  err = gnupg_create_inbound_pipe (inbound);
  if (!err)
    err = gnupg_create_outbound_pipe (outbound);
  if (err)
    {
      log_error (_("error creating a pipe: %s\n"), gpg_strerror (err));
      goto leave;
    }

  pgmname = ENCFS;
  idx = 0;
  argv[idx++] = "-f";
  if (opt.verbose)
    argv[idx++] = "-v";
  argv[idx++] = "--stdinpass";
  argv[idx++] = "--annotate";
  argv[idx++] = rawdir;
  argv[idx++] = mountpoint;
  argv[idx++] = NULL;
  assert (idx <= DIM (argv));

  err = gnupg_spawn_process_fd (pgmname, argv,
                                outbound[0], -1, inbound[1], &pid);
  if (err)
    {
      log_error ("error spawning '%s': %s\n", pgmname, gpg_strerror (err));
      goto leave;
    }
  close (outbound[0]); outbound[0] = -1;
  close ( inbound[1]);  inbound[1] = -1;

  runner_set_fds (runner, inbound[0], outbound[1]);
  inbound[0] = -1;  /* Now owned by RUNNER.  */
  outbound[1] = -1; /* Now owned by RUNNER.  */

  runner_set_handler (runner, encfs_handler, encfs_handler_cleanup, parm);
  parm = NULL; /* Now owned by RUNNER.  */

  runner_set_pid (runner, pid);
  pid = (pid_t)(-1); /* The process is now owned by RUNNER.  */

  err = runner_spawn (runner);
  if (err)
    goto leave;

  *r_id = runner_get_rid (runner);
  log_info ("running '%s' in the background\n", pgmname);

 leave:
  if (inbound[0] != -1)
    close (inbound[0]);
  if (inbound[1] != -1)
    close (inbound[1]);
  if (outbound[0] != -1)
    close (outbound[0]);
  if (outbound[1] != -1)
    close (outbound[1]);
  if (pid != (pid_t)(-1))
    {
      gnupg_wait_process (pgmname, pid, 1, NULL);
      gnupg_release_process (pid);
    }
  runner_release (runner);
  encfs_handler_cleanup (parm);
  return err;
}
コード例 #16
0
ファイル: recsel.c プロジェクト: codebam/gnupg
/* Parse an expression.  The expression syntax is:
 *
 *   [<lc>] {{<flag>} PROPNAME <op> VALUE [<lc>]}
 *
 * A [] indicates an optional part, a {} a repetition.  PROPNAME and
 * VALUE may not be the empty string.  White space between the
 * elements is ignored.  Numerical values are computed as long int;
 * standard C notation applies.  <lc> is the logical connection
 * operator; either "&&" for a conjunction or "||" for a disjunction.
 * A conjunction is assumed at the begin of an expression and
 * conjunctions have higher precedence than disjunctions.  If VALUE
 * starts with one of the characters used in any <op> a space after
 * the <op> is required.  A VALUE is terminated by an <lc> unless the
 * "--" <flag> is used in which case the VALUE spans to the end of the
 * expression.  <op> may be any of
 *
 *   =~  Substring must match
 *   !~  Substring must not match
 *   =   The full string must match
 *   <>  The full string must not match
 *   ==  The numerical value must match
 *   !=  The numerical value must not match
 *   <=  The numerical value of the field must be LE than the value.
 *   <   The numerical value of the field must be LT than the value.
 *   >=  The numerical value of the field must be GT than the value.
 *   >=  The numerical value of the field must be GE than the value.
 *   -n  True if value is not empty (no VALUE parameter allowed).
 *   -z  True if value is empty (no VALUE parameter allowed).
 *   -t  Alias for "PROPNAME != 0" (no VALUE parameter allowed).
 *   -f  Alias for "PROPNAME == 0" (no VALUE parameter allowed).
 *
 * Values for <flag> must be space separated and any of:
 *
 *   --  VALUE spans to the end of the expression.
 *   -c  The string match in this part is done case-sensitive.
 *
 * For example four calls to recsel_parse_expr() with these values for
 * EXPR
 *
 *  "uid =~ Alfa"
 *  "&& uid !~ Test"
 *  "|| uid =~ Alpha"
 *  "uid !~ Test"
 *
 * or the equivalent expression
 *
 *  "uid =~ Alfa" && uid !~ Test" || uid =~ Alpha" && "uid !~ Test"
 *
 * are making a selector for records where the "uid" property contains
 * the strings "Alfa" or "Alpha" but not the String "test".
 *
 * The caller must pass the address of a selector variable to this
 * function and initialize the value of the function to NULL before
 * the first call.  recset_release needs to be called to free the
 * selector.
 */
gpg_error_t
recsel_parse_expr (recsel_expr_t *selector, const char *expression)
{
  recsel_expr_t se_head = NULL;
  recsel_expr_t se, se2;
  char *expr_buffer;
  char *expr;
  char *s0, *s;
  int toend = 0;
  int xcase = 0;
  int disjun = 0;
  char *next_lc = NULL;

  while (*expression == ' ' || *expression == '\t')
    expression++;

  expr_buffer = xtrystrdup (expression);
  if (!expr_buffer)
    return my_error_from_syserror ();
  expr = expr_buffer;

  if (*expr == '|' && expr[1] == '|')
    {
      disjun = 1;
      expr += 2;
    }
  else if (*expr == '&' && expr[1] == '&')
    expr += 2;

 next_term:
  while (*expr == ' ' || *expr == '\t')
    expr++;

  while (*expr == '-')
    {
      switch (*++expr)
        {
        case '-': toend = 1; break;
        case 'c': xcase = 1; break;
        default:
          log_error ("invalid flag '-%c' in expression\n", *expr);
          recsel_release (se_head);
          xfree (expr_buffer);
          return my_error (GPG_ERR_INV_FLAG);
        }
      expr++;
      while (*expr == ' ' || *expr == '\t')
        expr++;
    }

  next_lc = toend? NULL : find_next_lc (expr);
  if (next_lc)
    *next_lc = 0;  /* Terminate this term.  */

  se = xtrymalloc (sizeof *se + strlen (expr));
  if (!se)
    return my_error_from_syserror ();
  strcpy (se->name, expr);
  se->next = NULL;
  se->not = 0;
  se->disjun = disjun;
  se->xcase = xcase;

  if (!se_head)
    se_head = se;
  else
    {
      for (se2 = se_head; se2->next; se2 = se2->next)
        ;
      se2->next = se;
    }


  s = strpbrk (expr, "=<>!~-");
  if (!s || s == expr )
    {
      log_error ("no field name given in expression\n");
      recsel_release (se_head);
      xfree (expr_buffer);
      return my_error (GPG_ERR_NO_NAME);
    }
  s0 = s;

  if (!strncmp (s, "=~", 2))
    {
      se->op = SELECT_SUB;
      s += 2;
    }
  else if (!strncmp (s, "!~", 2))
    {
      se->op = SELECT_SUB;
      se->not = 1;
      s += 2;
    }
  else if (!strncmp (s, "<>", 2))
    {
      se->op = SELECT_SAME;
      se->not = 1;
      s += 2;
    }
  else if (!strncmp (s, "==", 2))
    {
      se->op = SELECT_EQ;
      s += 2;
    }
  else if (!strncmp (s, "!=", 2))
    {
      se->op = SELECT_EQ;
      se->not = 1;
      s += 2;
    }
  else if (!strncmp (s, "<=", 2))
    {
      se->op = SELECT_LE;
      s += 2;
    }
  else if (!strncmp (s, ">=", 2))
    {
      se->op = SELECT_GE;
      s += 2;
    }
  else if (!strncmp (s, "<", 1))
    {
      se->op = SELECT_LT;
      s += 1;
    }
  else if (!strncmp (s, ">", 1))
    {
      se->op = SELECT_GT;
      s += 1;
    }
  else if (!strncmp (s, "=", 1))
    {
      se->op = SELECT_SAME;
      s += 1;
    }
  else if (!strncmp (s, "-z", 2))
    {
      se->op = SELECT_NONEMPTY;
      se->not = 1;
      s += 2;
    }
  else if (!strncmp (s, "-n", 2))
    {
      se->op = SELECT_NONEMPTY;
      s += 2;
    }
  else if (!strncmp (s, "-f", 2))
    {
      se->op = SELECT_ISTRUE;
      se->not = 1;
      s += 2;
    }
  else if (!strncmp (s, "-t", 2))
    {
      se->op = SELECT_ISTRUE;
      s += 2;
    }
  else
    {
      log_error ("invalid operator in expression\n");
      recsel_release (se_head);
      xfree (expr_buffer);
      return my_error (GPG_ERR_INV_OP);
    }

  /* We require that a space is used if the value starts with any of
     the operator characters.  */
  if (se->op == SELECT_NONEMPTY || se->op == SELECT_ISTRUE)
    ;
  else if (strchr ("=<>!~", *s))
    {
      log_error ("invalid operator in expression\n");
      recsel_release (se_head);
      xfree (expr_buffer);
      return my_error (GPG_ERR_INV_OP);
    }

  while (*s == ' ' || *s == '\t')
    s++;

  if (se->op == SELECT_NONEMPTY || se->op == SELECT_ISTRUE)
    {
      if (*s)
        {
          log_error ("value given for -n or -z\n");
          recsel_release (se_head);
          xfree (expr_buffer);
          return my_error (GPG_ERR_SYNTAX);
        }
    }
  else
    {
      if (!*s)
        {
          log_error ("no value given in expression\n");
          recsel_release (se_head);
          xfree (expr_buffer);
          return my_error (GPG_ERR_MISSING_VALUE);
        }
    }

  se->name[s0 - expr] = 0;
  trim_spaces (se->name);
  if (!se->name[0])
    {
      log_error ("no field name given in expression\n");
      recsel_release (se_head);
      xfree (expr_buffer);
      return my_error (GPG_ERR_NO_NAME);
    }

  trim_spaces (se->name + (s - expr));
  se->value = se->name + (s - expr);
  if (!se->value[0] && !(se->op == SELECT_NONEMPTY || se->op == SELECT_ISTRUE))
    {
      log_error ("no value given in expression\n");
      recsel_release (se_head);
      xfree (expr_buffer);
      return my_error (GPG_ERR_MISSING_VALUE);
    }

  se->numvalue = strtol (se->value, NULL, 0);

  if (next_lc)
    {
      disjun = next_lc[1] == '|';
      expr = next_lc + 2;
      goto next_term;
    }

  /* Read:y Append to passes last selector.  */
  if (!*selector)
    *selector = se_head;
  else
    {
      for (se2 = *selector; se2->next; se2 = se2->next)
        ;
      se2->next = se_head;
    }

  xfree (expr_buffer);
  return 0;
}
コード例 #17
0
ファイル: oid.c プロジェクト: idodeclare/MacGPG2
/**
 * ksba_oid_to_str:
 * @buffer: A BER encoded OID
 * @length: The length of this OID
 * 
 * Take a buffer with an object identifier in BER encoding and return
 * a string representing this OID.  We do not use the ASN.1 syntax
 * here but delimit the arcs with dots, so it is easier to parse in
 * most cases.  This dotted-decimal notation is also known as LDAPOID
 * string and described in RFC-2251.
 *
 * The function returns an empty string for an empty buffer and does
 * no interpretation of the OID.  The caller must free the returned
 * string using ksba_free() or the function he has registered as a
 * replacement.
 * 
 *
 * Return value: A allocated string or NULL in case of memory problem.
 **/
char *
ksba_oid_to_str (const char *buffer, size_t length)
{
  const unsigned char *buf = buffer;
  char *string, *p;
  int n = 0;
  unsigned long val, valmask;

  valmask = (unsigned long)0xfe << (8 * (sizeof (valmask) - 1));

  /* To calculate the length of the string we can safely assume an
     upper limit of 3 decimal characters per byte.  Two extra bytes
     account for the special first octect */
  string = p = xtrymalloc (length*(1+3)+2+1);
  if (!string)
    return NULL;
  if (!length)
    {
      *p = 0;
      return string;
    }

  if (buf[0] < 40)
    p += sprintf (p, "0.%d", buf[n]);
  else if (buf[0] < 80)
    p += sprintf (p, "1.%d", buf[n]-40);
  else {
    val = buf[n] & 0x7f;
    while ( (buf[n]&0x80) && ++n < length )
      {
        if ( (val & valmask) )
          goto badoid;  /* Overflow.  */
        val <<= 7;
        val |= buf[n] & 0x7f;
      }
    val -= 80;
    sprintf (p, "2.%lu", val);
    p += strlen (p);
  }
  for (n++; n < length; n++)
    {
      val = buf[n] & 0x7f;
      while ( (buf[n]&0x80) && ++n < length )
        {
          if ( (val & valmask) )
            goto badoid;  /* Overflow.  */
          val <<= 7;
          val |= buf[n] & 0x7f;
        }
      sprintf (p, ".%lu", val);
      p += strlen (p);
    }
    
  *p = 0;
  return string;

 badoid:
  /* Return a special OID (gnu.gnupg.badoid) to indicate the error
     case.  The OID is broken and thus we return one which can't do
     any harm.  Formally this does not need to be a bad OID but an OID
     with an arc that can't be represented in a 32 bit word is more
     than likely corrupt.  */
  xfree (string);
  return xtrystrdup ("1.3.6.1.4.1.11591.2.12242973"); 
}
コード例 #18
0
ファイル: cache.c プロジェクト: Distrotech/gnupg
/* Store the key for the last successful cache hit.  That value is
   used by agent_get_cache if the requested KEY is given as NULL.
   NULL may be used to remove that key. */
void
agent_store_cache_hit (const char *key)
{
  xfree (last_stored_cache_key);
  last_stored_cache_key = key? xtrystrdup (key) : NULL;
}
コード例 #19
0
ファイル: mount.c プロジェクト: ingochris/InfinityEncryption
/* Mount the container with name FILENAME at MOUNTPOINT.  */
gpg_error_t
g13_mount_container (ctrl_t ctrl, const char *filename, const char *mountpoint)
{
  gpg_error_t err;
  dotlock_t lock;
  void *enckeyblob = NULL;
  size_t enckeybloblen;
  void *keyblob = NULL;
  size_t keybloblen;
  tupledesc_t tuples = NULL;
  size_t n;
  const unsigned char *value;
  int conttype;
  unsigned int rid;
  char *mountpoint_buffer = NULL;

  /* A quick check to see whether the container exists.  */
  if (access (filename, R_OK))
    return gpg_error_from_syserror ();

  if (!mountpoint)
    {
      mountpoint_buffer = xtrystrdup ("/tmp/g13-XXXXXX");
      if (!mountpoint_buffer)
        return gpg_error_from_syserror ();
      if (!gnupg_mkdtemp (mountpoint_buffer))
        {
          err = gpg_error_from_syserror ();
          log_error (_("can't create directory '%s': %s\n"),
                     "/tmp/g13-XXXXXX", gpg_strerror (err));
          xfree (mountpoint_buffer);
          return err;
        }
      mountpoint = mountpoint_buffer;
    }

  /* Try to take a lock.  */
  lock = dotlock_create (filename, 0);
  if (!lock)
    {
      xfree (mountpoint_buffer);
      return gpg_error_from_syserror ();
    }

  if (dotlock_take (lock, 0))
    {
      err = gpg_error_from_syserror ();
      goto leave;
    }
  else
    err = 0;

  /* Check again that the file exists.  */
  {
    struct stat sb;

    if (stat (filename, &sb))
      {
        err = gpg_error_from_syserror ();
        goto leave;
      }
  }

  /* Read the encrypted keyblob.  */
  err = read_keyblob (filename, &enckeyblob, &enckeybloblen);
  if (err)
    goto leave;

  /* Decrypt that keyblob and store it in a tuple descriptor.  */
  err = decrypt_keyblob (ctrl, enckeyblob, enckeybloblen,
                         &keyblob, &keybloblen);
  if (err)
    goto leave;
  xfree (enckeyblob);
  enckeyblob = NULL;

  err = create_tupledesc (&tuples, keyblob, keybloblen);
  if (!err)
    keyblob = NULL;
  else
    {
      if (gpg_err_code (err) == GPG_ERR_NOT_SUPPORTED)
        log_error ("unknown keyblob version\n");
      goto leave;
    }
  if (opt.verbose)
    dump_keyblob (tuples);

  value = find_tuple (tuples, KEYBLOB_TAG_CONTTYPE, &n);
  if (!value || n != 2)
    conttype = 0;
  else
    conttype = (value[0] << 8 | value[1]);
  if (!be_is_supported_conttype (conttype))
    {
      log_error ("content type %d is not supported\n", conttype);
      err = gpg_error (GPG_ERR_NOT_SUPPORTED);
      goto leave;
    }
  err = be_mount_container (ctrl, conttype, filename, mountpoint, tuples, &rid);
  if (!err)
    {
      err = mountinfo_add_mount (filename, mountpoint, conttype, rid,
                                 !!mountpoint_buffer);
      /* Fixme: What shall we do if this fails?  Add a provisional
         mountinfo entry first and remove it on error? */
      if (!err)
        {
          char *tmp = percent_plus_escape (mountpoint);
          if (!tmp)
            err = gpg_error_from_syserror ();
          else
            {
              g13_status (ctrl, STATUS_MOUNTPOINT, tmp, NULL);
              xfree (tmp);
            }
        }
    }

 leave:
  destroy_tupledesc (tuples);
  xfree (keyblob);
  xfree (enckeyblob);
  dotlock_destroy (lock);
  xfree (mountpoint_buffer);
  return err;
}
コード例 #20
0
ファイル: certreq.c プロジェクト: GroovIM/transport
/*
 * r_sig  = (sig-val
 *	      (<algo>
 *		(<param_name1> <mpi>)
 *		...
 *		(<param_namen> <mpi>)
 *	      ))
 * The sexp must be in canonical form.
 * Fixme:  The code is mostly duplicated from cms.c
 * Note, that <algo> must be given as a stringified OID or the special
 * string "rsa" which is translated to sha1WithRSAEncryption
*/
gpg_error_t
ksba_certreq_set_sig_val (ksba_certreq_t cr, ksba_const_sexp_t sigval)
{
  const char *s, *endp;
  unsigned long n;

  if (!cr)
    return gpg_error (GPG_ERR_INV_VALUE);

  s = sigval;
  if (*s != '(')
    return gpg_error (GPG_ERR_INV_SEXP);
  s++;

  n = strtoul (s, (char**)&endp, 10);
  s = endp;
  if (!n || *s!=':')
    return gpg_error (GPG_ERR_INV_SEXP); /* we don't allow empty lengths */
  s++;
  if (n != 7 || memcmp (s, "sig-val", 7))
    return gpg_error (GPG_ERR_UNKNOWN_SEXP);
  s += 7;
  if (*s != '(')
    return gpg_error (digitp (s)? GPG_ERR_UNKNOWN_SEXP : GPG_ERR_INV_SEXP);
  s++;

  /* break out the algorithm ID */
  n = strtoul (s, (char**)&endp, 10);
  s = endp;
  if (!n || *s != ':')
    return gpg_error (GPG_ERR_INV_SEXP); /* we don't allow empty lengths */
  s++;
  xfree (cr->sig_val.algo);
  if (n==3 && s[0] == 'r' && s[1] == 's' && s[2] == 'a')
    { /* kludge to allow "rsa" to be passed as algorithm name */
      cr->sig_val.algo = xtrystrdup ("1.2.840.113549.1.1.5");
      if (!cr->sig_val.algo)
        return gpg_error (GPG_ERR_ENOMEM);
    }
  else
    {
      cr->sig_val.algo = xtrymalloc (n+1);
      if (!cr->sig_val.algo)
        return gpg_error (GPG_ERR_ENOMEM);
      memcpy (cr->sig_val.algo, s, n);
      cr->sig_val.algo[n] = 0;
    }
  s += n;

  /* And now the values - FIXME: For now we only support one */
  /* fixme: start loop */
  if (*s != '(')
    return gpg_error (digitp (s)? GPG_ERR_UNKNOWN_SEXP : GPG_ERR_INV_SEXP);
  s++;
  n = strtoul (s, (char**)&endp, 10);
  s = endp;
  if (!n || *s != ':')
    return gpg_error (GPG_ERR_INV_SEXP);
  s++;
  s += n; /* ignore the name of the parameter */

  if (!digitp(s))
    return gpg_error (GPG_ERR_UNKNOWN_SEXP); /* but may also be an invalid one */
  n = strtoul (s, (char**)&endp, 10);
  s = endp;
  if (!n || *s != ':')
    return gpg_error (GPG_ERR_INV_SEXP);
  s++;
  if (n > 1 && !*s)
    { /* We might have a leading zero due to the way we encode
         MPIs - this zero should not go into the BIT STRING.  */
      s++;
      n--;
    }
  xfree (cr->sig_val.value);
  cr->sig_val.value = xtrymalloc (n);
  if (!cr->sig_val.value)
    return gpg_error (GPG_ERR_ENOMEM);
  memcpy (cr->sig_val.value, s, n);
  cr->sig_val.valuelen = n;
  s += n;
  if ( *s != ')')
    return gpg_error (GPG_ERR_UNKNOWN_SEXP); /* but may also be an invalid one */
  s++;
  /* fixme: end loop over parameters */

  /* we need 2 closing parenthesis */
  if ( *s != ')' || s[1] != ')')
    return gpg_error (GPG_ERR_INV_SEXP);

  return 0;
}
コード例 #21
0
ファイル: wks-util.c プロジェクト: larryv/gnupg
/* Parse the policy flags by reading them from STREAM and storing them
 * into FLAGS.  If IGNORE_UNKNOWN is iset unknown keywords are
 * ignored.  */
gpg_error_t
wks_parse_policy (policy_flags_t flags, estream_t stream, int ignore_unknown)
{
  enum tokens {
    TOK_SUBMISSION_ADDRESS,
    TOK_MAILBOX_ONLY,
    TOK_DANE_ONLY,
    TOK_AUTH_SUBMIT,
    TOK_MAX_PENDING,
    TOK_PROTOCOL_VERSION
  };
  static struct {
    const char *name;
    enum tokens token;
  } keywords[] = {
    { "submission-address", TOK_SUBMISSION_ADDRESS },
    { "mailbox-only", TOK_MAILBOX_ONLY },
    { "dane-only",    TOK_DANE_ONLY    },
    { "auth-submit",  TOK_AUTH_SUBMIT  },
    { "max-pending",  TOK_MAX_PENDING  },
    { "protocol-version", TOK_PROTOCOL_VERSION }
  };
  gpg_error_t err = 0;
  int lnr = 0;
  char line[1024];
  char *p, *keyword, *value;
  int i, n;

  memset (flags, 0, sizeof *flags);

  while (es_fgets (line, DIM(line)-1, stream) )
    {
      lnr++;
      n = strlen (line);
      if (!n || line[n-1] != '\n')
        {
          err = gpg_error (*line? GPG_ERR_LINE_TOO_LONG
                           : GPG_ERR_INCOMPLETE_LINE);
          break;
        }
      trim_trailing_spaces (line);
      /* Skip empty and comment lines. */
      for (p=line; spacep (p); p++)
        ;
      if (!*p || *p == '#')
        continue;

      if (*p == ':')
        {
          err = gpg_error (GPG_ERR_SYNTAX);
          break;
        }

      keyword = p;
      value = NULL;
      if ((p = strchr (p, ':')))
        {
          /* Colon found: Keyword with value.  */
          *p++ = 0;
          for (; spacep (p); p++)
            ;
          if (!*p)
            {
              err = gpg_error (GPG_ERR_MISSING_VALUE);
              break;
            }
          value = p;
        }

      for (i=0; i < DIM (keywords); i++)
        if (!ascii_strcasecmp (keywords[i].name, keyword))
          break;
      if (!(i < DIM (keywords)))
        {
          if (ignore_unknown)
            continue;
          err = gpg_error (GPG_ERR_INV_NAME);
          break;
	}

      switch (keywords[i].token)
        {
        case TOK_SUBMISSION_ADDRESS:
          if (!value || !*value)
            {
              err = gpg_error (GPG_ERR_SYNTAX);
              goto leave;
            }
          xfree (flags->submission_address);
          flags->submission_address = xtrystrdup (value);
          if (!flags->submission_address)
            {
              err = gpg_error_from_syserror ();
              goto leave;
            }
          break;
        case TOK_MAILBOX_ONLY: flags->mailbox_only = 1; break;
        case TOK_DANE_ONLY:    flags->dane_only = 1;    break;
        case TOK_AUTH_SUBMIT:  flags->auth_submit = 1;  break;
        case TOK_MAX_PENDING:
          if (!value)
            {
              err = gpg_error (GPG_ERR_SYNTAX);
              goto leave;
            }
          /* FIXME: Define whether these are seconds, hours, or days
           * and decide whether to allow other units.  */
          flags->max_pending = atoi (value);
          break;
        case TOK_PROTOCOL_VERSION:
          if (!value)
            {
              err = gpg_error (GPG_ERR_SYNTAX);
              goto leave;
            }
          flags->protocol_version = atoi (value);
          break;
        }
    }

  if (!err && !es_feof (stream))
    err = gpg_error_from_syserror ();

 leave:
  if (err)
    log_error ("error reading '%s', line %d: %s\n",
               es_fname_get (stream), lnr, gpg_strerror (err));

  return err;
}
コード例 #22
0
ファイル: wks-util.c プロジェクト: larryv/gnupg
/* Run gpg on KEY and store the primary fingerprint at R_FPR and the
 * list of mailboxes at R_MBOXES.  Returns 0 on success; on error NULL
 * is stored at R_FPR and R_MBOXES and an error code is returned.
 * R_FPR may be NULL if the fingerprint is not needed.  */
gpg_error_t
wks_list_key (estream_t key, char **r_fpr, uidinfo_list_t *r_mboxes)
{
  gpg_error_t err;
  ccparray_t ccp;
  const char **argv;
  estream_t listing;
  char *line = NULL;
  size_t length_of_line = 0;
  size_t  maxlen;
  ssize_t len;
  char **fields = NULL;
  int nfields;
  int lnr;
  char *fpr = NULL;
  uidinfo_list_t mboxes = NULL;

  if (r_fpr)
    *r_fpr = NULL;
  *r_mboxes = NULL;

  /* Open a memory stream.  */
  listing = es_fopenmem (0, "w+b");
  if (!listing)
    {
      err = gpg_error_from_syserror ();
      log_error ("error allocating memory buffer: %s\n", gpg_strerror (err));
      return err;
    }

  ccparray_init (&ccp, 0);

  ccparray_put (&ccp, "--no-options");
  if (!opt.verbose)
    ccparray_put (&ccp, "--quiet");
  else if (opt.verbose > 1)
    ccparray_put (&ccp, "--verbose");
  ccparray_put (&ccp, "--batch");
  ccparray_put (&ccp, "--status-fd=2");
  ccparray_put (&ccp, "--always-trust");
  ccparray_put (&ccp, "--with-colons");
  ccparray_put (&ccp, "--dry-run");
  ccparray_put (&ccp, "--import-options=import-minimal,import-show");
  ccparray_put (&ccp, "--import");

  ccparray_put (&ccp, NULL);
  argv = ccparray_get (&ccp, NULL);
  if (!argv)
    {
      err = gpg_error_from_syserror ();
      goto leave;
    }
  err = gnupg_exec_tool_stream (opt.gpg_program, argv, key,
                                NULL, listing,
                                key_status_cb, NULL);
  if (err)
    {
      log_error ("import failed: %s\n", gpg_strerror (err));
      goto leave;
    }

  es_rewind (listing);
  lnr = 0;
  maxlen = 2048; /* Set limit.  */
  while ((len = es_read_line (listing, &line, &length_of_line, &maxlen)) > 0)
    {
      lnr++;
      if (!maxlen)
        {
          log_error ("received line too long\n");
          err = gpg_error (GPG_ERR_LINE_TOO_LONG);
          goto leave;
        }
      /* Strip newline and carriage return, if present.  */
      while (len > 0
	     && (line[len - 1] == '\n' || line[len - 1] == '\r'))
	line[--len] = '\0';
      /* log_debug ("line '%s'\n", line); */

      xfree (fields);
      fields = strtokenize (line, ":");
      if (!fields)
        {
          err = gpg_error_from_syserror ();
          log_error ("strtokenize failed: %s\n", gpg_strerror (err));
          goto leave;
        }
      for (nfields = 0; fields[nfields]; nfields++)
        ;
      if (!nfields)
        {
          err = gpg_error (GPG_ERR_INV_ENGINE);
          goto leave;
        }
      if (!strcmp (fields[0], "sec"))
        {
          /* gpg may return "sec" as the first record - but we do not
           * accept secret keys.  */
          err = gpg_error (GPG_ERR_NO_PUBKEY);
          goto leave;
        }
      if (lnr == 1 && strcmp (fields[0], "pub"))
        {
          /* First record is not a public key.  */
          err = gpg_error (GPG_ERR_INV_ENGINE);
          goto leave;
        }
      if (lnr > 1 && !strcmp (fields[0], "pub"))
        {
          /* More than one public key.  */
          err = gpg_error (GPG_ERR_TOO_MANY);
          goto leave;
        }
      if (!strcmp (fields[0], "sub") || !strcmp (fields[0], "ssb"))
        break; /* We can stop parsing here.  */

      if (!strcmp (fields[0], "fpr") && nfields > 9 && !fpr)
        {
          fpr = xtrystrdup (fields[9]);
          if (!fpr)
            {
              err = gpg_error_from_syserror ();
              goto leave;
            }
        }
      else if (!strcmp (fields[0], "uid") && nfields > 9)
        {
          /* Fixme: Unescape fields[9] */
          if (!append_to_uidinfo_list (&mboxes, fields[9],
                                       parse_timestamp (fields[5], NULL)))
            {
              err = gpg_error_from_syserror ();
              goto leave;
            }
        }
    }
  if (len < 0 || es_ferror (listing))
    {
      err = gpg_error_from_syserror ();
      log_error ("error reading memory stream\n");
      goto leave;
    }

  if (!fpr)
    {
      err = gpg_error (GPG_ERR_NO_PUBKEY);
      goto leave;
    }

  if (r_fpr)
    {
      *r_fpr = fpr;
      fpr = NULL;
    }
  *r_mboxes = mboxes;
  mboxes = NULL;

 leave:
  xfree (fpr);
  free_uidinfo_list (mboxes);
  xfree (fields);
  es_free (line);
  xfree (argv);
  es_fclose (listing);
  return err;
}
コード例 #23
0
ファイル: g13-syshelp.c プロジェクト: 0ndorio/gnupg
/* Parse the /etc/gnupg/g13tab for user USERNAME.  Return a table for
   the user on success.  Return NULL on error and print
   diagnostics. */
static tab_item_t
parse_g13tab (const char *username)
{
  gpg_error_t err;
  int c, n;
  char line[512];
  char *p;
  char *fname;
  estream_t fp;
  int lnr;
  char **words = NULL;
  tab_item_t table = NULL;
  tab_item_t *tabletail, ti;

  fname = make_filename (gnupg_sysconfdir (), G13_NAME"tab", NULL);
  fp = es_fopen (fname, "r");
  if (!fp)
    {
      err = gpg_error_from_syserror ();
      log_error (_("error opening '%s': %s\n"), fname, gpg_strerror (err));
      goto leave;
    }

  tabletail = &table;
  err = 0;
  lnr = 0;
  while (es_fgets (line, DIM(line)-1, fp))
    {
      lnr++;
      n = strlen (line);
      if (!n || line[n-1] != '\n')
        {
          /* Eat until end of line. */
          while ((c=es_getc (fp)) != EOF && c != '\n')
            ;
          err = gpg_error (*line? GPG_ERR_LINE_TOO_LONG
                           : GPG_ERR_INCOMPLETE_LINE);
          log_error (_("file '%s', line %d: %s\n"),
                     fname, lnr, gpg_strerror (err));
          continue;
        }
      line[--n] = 0; /* Chop the LF. */
      if (n && line[n-1] == '\r')
        line[--n] = 0; /* Chop an optional CR. */

      /* Allow for empty lines and spaces */
      for (p=line; spacep (p); p++)
        ;
      if (!*p || *p == '#')
        continue;

      /* Parse the line.  The format is
       * <username> <blockdev> [<label>|"-" [<mountpoint>]]
       */
      xfree (words);
      words = strtokenize (p, " \t");
      if (!words)
        {
          err = gpg_error_from_syserror ();
          break;
        }
      if (!words[0] || !words[1])
        {
          log_error (_("file '%s', line %d: %s\n"),
                     fname, lnr, gpg_strerror (GPG_ERR_SYNTAX));
          continue;
        }
      if (!(*words[1] == '/'
            || !strncmp (words[1], "PARTUUID=", 9)
            || !strncmp (words[1], "partuuid=", 9)))
        {
          log_error (_("file '%s', line %d: %s\n"),
                     fname, lnr, "Invalid block device syntax");
          continue;
        }
      if (words[2])
        {
          if (strlen (words[2]) > 16 || strchr (words[2], '/'))
            {
              log_error (_("file '%s', line %d: %s\n"),
                         fname, lnr, "Label too long or invalid syntax");
              continue;
            }

          if (words[3] && *words[3] != '/')
            {
              log_error (_("file '%s', line %d: %s\n"),
                         fname, lnr, "Invalid mountpoint syntax");
              continue;
            }
        }
      if (strcmp (words[0], username))
        continue; /* Skip entries for other usernames!  */

      ti = xtrymalloc (sizeof *ti + strlen (words[1]));
      if (!ti)
        {
          err = gpg_error_from_syserror ();
          break;
        }
      ti->next = NULL;
      ti->label = NULL;
      ti->mountpoint = NULL;
      strcpy (ti->blockdev, *words[1]=='/'? words[1] : words[1]+9);
      if (words[2])
        {
          if (strcmp (words[2], "-")
              && !(ti->label = xtrystrdup (words[2])))
            {
              err = gpg_error_from_syserror ();
              xfree (ti);
              break;
            }
          if (words[3] && !(ti->mountpoint = xtrystrdup (words[3])))
            {
              err = gpg_error_from_syserror ();
              xfree (ti->label);
              xfree (ti);
              break;
            }
        }
      *tabletail = ti;
      tabletail = &ti->next;
    }

  if (!err && !es_feof (fp))
    err = gpg_error_from_syserror ();
  if (err)
    log_error (_("error reading '%s', line %d: %s\n"),
               fname, lnr, gpg_strerror (err));

 leave:
  xfree (words);
  es_fclose (fp);
  xfree (fname);
  if (err)
    {
      release_tab_items (table);
      return NULL;
    }
  return table;
}
コード例 #24
0
ファイル: ocsp.c プロジェクト: FMayzek/gnupg
/* Construct an OCSP request, send it to the configured OCSP responder
   and parse the response. On success the OCSP context may be used to
   further process the reponse. */
static gpg_error_t
do_ocsp_request (ctrl_t ctrl, ksba_ocsp_t ocsp, gcry_md_hd_t md,
                 const char *url, ksba_cert_t cert, ksba_cert_t issuer_cert)
{
  gpg_error_t err;
  unsigned char *request, *response;
  size_t requestlen, responselen;
  http_t http;
  ksba_ocsp_response_status_t response_status;
  const char *t;
  int redirects_left = 2;
  char *free_this = NULL;

  (void)ctrl;

  if (opt.disable_http)
    {
      log_error (_("OCSP request not possible due to disabled HTTP\n"));
      return gpg_error (GPG_ERR_NOT_SUPPORTED);
    }

  err = ksba_ocsp_add_target (ocsp, cert, issuer_cert);
  if (err)
    {
      log_error (_("error setting OCSP target: %s\n"), gpg_strerror (err));
      return err;
    }

  {
    size_t n;
    unsigned char nonce[32];

    n = ksba_ocsp_set_nonce (ocsp, NULL, 0);
    if (n > sizeof nonce)
      n = sizeof nonce;
    gcry_create_nonce (nonce, n);
    ksba_ocsp_set_nonce (ocsp, nonce, n);
  }

  err = ksba_ocsp_build_request (ocsp, &request, &requestlen);
  if (err)
    {
      log_error (_("error building OCSP request: %s\n"), gpg_strerror (err));
      return err;
    }

 once_more:
  err = http_open (&http, HTTP_REQ_POST, url, NULL,
                   (opt.honor_http_proxy? HTTP_FLAG_TRY_PROXY:0),
                   opt.http_proxy, NULL, NULL, NULL);
  if (err)
    {
      log_error (_("error connecting to '%s': %s\n"), url, gpg_strerror (err));
      xfree (free_this);
      return err;
    }

  es_fprintf (http_get_write_ptr (http),
	      "Content-Type: application/ocsp-request\r\n"
	      "Content-Length: %lu\r\n",
	      (unsigned long)requestlen );
  http_start_data (http);
  if (es_fwrite (request, requestlen, 1, http_get_write_ptr (http)) != 1)
    {
      err = gpg_error_from_errno (errno);
      log_error ("error sending request to '%s': %s\n", url, strerror (errno));
      http_close (http, 0);
      xfree (request);
      xfree (free_this);
      return err;
    }
  xfree (request);
  request = NULL;

  err = http_wait_response (http);
  if (err || http_get_status_code (http) != 200)
    {
      if (err)
        log_error (_("error reading HTTP response for '%s': %s\n"),
                   url, gpg_strerror (err));
      else
        {
          switch (http_get_status_code (http))
            {
            case 301:
            case 302:
              {
                const char *s = http_get_header (http, "Location");

                log_info (_("URL '%s' redirected to '%s' (%u)\n"),
                          url, s?s:"[none]", http_get_status_code (http));
                if (s && *s && redirects_left-- )
                  {
                    xfree (free_this); url = NULL;
                    free_this = xtrystrdup (s);
                    if (!free_this)
                      err = gpg_error_from_errno (errno);
                    else
                      {
                        url = free_this;
                        http_close (http, 0);
                        goto once_more;
                      }
                  }
                else
                  err = gpg_error (GPG_ERR_NO_DATA);
                log_error (_("too many redirections\n"));
              }
              break;

            default:
              log_error (_("error accessing '%s': http status %u\n"),
                         url, http_get_status_code (http));
              err = gpg_error (GPG_ERR_NO_DATA);
              break;
            }
        }
      http_close (http, 0);
      xfree (free_this);
      return err;
    }

  err = read_response (http_get_read_ptr (http), &response, &responselen);
  http_close (http, 0);
  if (err)
    {
      log_error (_("error reading HTTP response for '%s': %s\n"),
                 url, gpg_strerror (err));
      xfree (free_this);
      return err;
    }

  err = ksba_ocsp_parse_response (ocsp, response, responselen,
                                  &response_status);
  if (err)
    {
      log_error (_("error parsing OCSP response for '%s': %s\n"),
                 url, gpg_strerror (err));
      xfree (response);
      xfree (free_this);
      return err;
    }

  switch (response_status)
    {
    case KSBA_OCSP_RSPSTATUS_SUCCESS:      t = "success"; break;
    case KSBA_OCSP_RSPSTATUS_MALFORMED:    t = "malformed"; break;
    case KSBA_OCSP_RSPSTATUS_INTERNAL:     t = "internal error"; break;
    case KSBA_OCSP_RSPSTATUS_TRYLATER:     t = "try later"; break;
    case KSBA_OCSP_RSPSTATUS_SIGREQUIRED:  t = "must sign request"; break;
    case KSBA_OCSP_RSPSTATUS_UNAUTHORIZED: t = "unauthorized"; break;
    case KSBA_OCSP_RSPSTATUS_REPLAYED:     t = "replay detected"; break;
    case KSBA_OCSP_RSPSTATUS_OTHER:        t = "other (unknown)"; break;
    case KSBA_OCSP_RSPSTATUS_NONE:         t = "no status"; break;
    default:                               t = "[unknown status]"; break;
    }
  if (response_status == KSBA_OCSP_RSPSTATUS_SUCCESS)
    {
      if (opt.verbose)
        log_info (_("OCSP responder at '%s' status: %s\n"), url, t);

      err = ksba_ocsp_hash_response (ocsp, response, responselen,
                                     HASH_FNC, md);
      if (err)
        log_error (_("hashing the OCSP response for '%s' failed: %s\n"),
                   url, gpg_strerror (err));
    }
  else
    {
      log_error (_("OCSP responder at '%s' status: %s\n"), url, t);
      err = gpg_error (GPG_ERR_GENERAL);
    }

  xfree (response);
  xfree (free_this);
  return err;
}
コード例 #25
0
ファイル: ks-engine-http.c プロジェクト: rmoriz/gnupg-mirror
/* Get the key from URL which is expected to specify a http style
   scheme.  On success R_FP has an open stream to read the data.  */
gpg_error_t
ks_http_fetch (ctrl_t ctrl, const char *url, estream_t *r_fp)
{
  gpg_error_t err;
  http_session_t session = NULL;
  http_t http = NULL;
  int redirects_left = MAX_REDIRECTS;
  estream_t fp = NULL;
  char *request_buffer = NULL;

  err = http_session_new (&session, NULL);
  if (err)
    goto leave;
  http_session_set_log_cb (session, cert_log_cb);

  *r_fp = NULL;
 once_more:
  err = http_open (&http,
                   HTTP_REQ_GET,
                   url,
                   /* httphost */ NULL,
                   /* fixme: AUTH */ NULL,
                   0,
                   /* fixme: proxy*/ NULL,
                   session,
                   NULL,
                   /*FIXME curl->srvtag*/NULL);
  if (!err)
    {
      fp = http_get_write_ptr (http);
      /* Avoid caches to get the most recent copy of the key.  We set
         both the Pragma and Cache-Control versions of the header, so
         we're good with both HTTP 1.0 and 1.1.  */
      es_fputs ("Pragma: no-cache\r\n"
                "Cache-Control: no-cache\r\n", fp);
      http_start_data (http);
      if (es_ferror (fp))
        err = gpg_error_from_syserror ();
    }
  if (err)
    {
      /* Fixme: After a redirection we show the old host name.  */
      log_error (_("error connecting to '%s': %s\n"),
                 url, gpg_strerror (err));
      goto leave;
    }

  /* Wait for the response.  */
  dirmngr_tick (ctrl);
  err = http_wait_response (http);
  if (err)
    {
      log_error (_("error reading HTTP response for '%s': %s\n"),
                 url, gpg_strerror (err));
      goto leave;
    }

  switch (http_get_status_code (http))
    {
    case 200:
      err = 0;
      break; /* Success.  */

    case 301:
    case 302:
    case 307:
      {
        const char *s = http_get_header (http, "Location");

        log_info (_("URL '%s' redirected to '%s' (%u)\n"),
                  url, s?s:"[none]", http_get_status_code (http));
        if (s && *s && redirects_left-- )
          {
            xfree (request_buffer);
            request_buffer = xtrystrdup (s);
            if (request_buffer)
              {
                url = request_buffer;
                http_close (http, 0);
                http = NULL;
                goto once_more;
              }
            err = gpg_error_from_syserror ();
          }
        else
          err = gpg_error (GPG_ERR_NO_DATA);
        log_error (_("too many redirections\n"));
      }
      goto leave;

    default:
      log_error (_("error accessing '%s': http status %u\n"),
                 url, http_get_status_code (http));
      err = gpg_error (GPG_ERR_NO_DATA);
      goto leave;
    }

  fp = http_get_read_ptr (http);
  if (!fp)
    {
      err = gpg_error (GPG_ERR_BUG);
      goto leave;
    }

  /* Return the read stream and close the HTTP context.  */
  *r_fp = fp;
  http_close (http, 1);
  http = NULL;

 leave:
  http_close (http, 0);
  http_session_release (session);
  xfree (request_buffer);
  return err;
}
コード例 #26
0
ファイル: ks-engine-hkp.c プロジェクト: larryv/gnupg
/* Add the host AI under the NAME into the HOSTTABLE.  If PORT is not
   zero, it specifies which port to use to talk to the host for
   PROTOCOL.  If NAME specifies a pool (as indicated by IS_POOL),
   update the given reference table accordingly.  */
static void
add_host (const char *name, int is_pool,
          const dns_addrinfo_t ai,
          enum ks_protocol protocol, unsigned short port)
{
  gpg_error_t tmperr;
  char *tmphost;
  int idx, tmpidx;
  hostinfo_t host;
  int i;

  idx = find_hostinfo (name);
  host = hosttable[idx];

  if (is_pool)
    {
      /* For a pool immediately convert the address to a string.  */
      tmperr = resolve_dns_addr (ai->addr, ai->addrlen,
                                 (DNS_NUMERICHOST | DNS_WITHBRACKET), &tmphost);
    }
  else if (!is_ip_address (name))
    {
      /* This is a hostname.  Use the name as given without going
       * through resolve_dns_addr.  */
      tmphost = xtrystrdup (name);
      if (!tmphost)
        tmperr = gpg_error_from_syserror ();
      else
        tmperr = 0;
    }
  else
    {
      /* Do a PTR lookup on AI.  If a name was not found the function
       * returns the numeric address (with brackets).  */
      tmperr = resolve_dns_addr (ai->addr, ai->addrlen,
                                 DNS_WITHBRACKET, &tmphost);
    }

  if (tmperr)
    {
      log_info ("resolve_dns_addr failed while checking '%s': %s\n",
                name, gpg_strerror (tmperr));
    }
  else if (host->pool_len + 1 >= MAX_POOL_SIZE)
    {
      log_error ("resolve_dns_addr for '%s': '%s'"
                 " [index table full - ignored]\n", name, tmphost);
    }
  else
    {
      if (!is_pool && is_ip_address (name))
        /* Update the original entry.  */
        tmpidx = idx;
      else
        tmpidx = find_hostinfo (tmphost);
      log_info ("resolve_dns_addr for '%s': '%s'%s\n",
                name, tmphost,
                tmpidx == -1? "" : " [already known]");

      if (tmpidx == -1) /* Create a new entry.  */
        tmpidx = create_new_hostinfo (tmphost);

      if (tmpidx == -1)
        {
          log_error ("map_host for '%s' problem: %s - '%s' [ignored]\n",
                     name, strerror (errno), tmphost);
        }
      else  /* Set or update the entry. */
        {
          if (port)
            hosttable[tmpidx]->port[protocol] = port;

          if (ai->family == AF_INET6)
            {
              hosttable[tmpidx]->v6 = 1;
            }
          else if (ai->family == AF_INET)
            {
              hosttable[tmpidx]->v4 = 1;
            }
          else
            BUG ();

          /* If we updated the main entry, we're done.  */
          if (idx == tmpidx)
            goto leave;

          /* If we updated an existing entry, we're done.  */
          for (i = 0; i < host->pool_len; i++)
            if (host->pool[i] == tmpidx)
              goto leave;

          /* Otherwise, we need to add it to the pool.  Check if there
             is space.  */
          if (host->pool_len + 1 > host->pool_size)
            {
              int *new_pool;
              size_t new_size;

              if (host->pool_size == 0)
                new_size = 4;
              else
                new_size = host->pool_size * 2;

              new_pool = xtryrealloc (host->pool,
                                      new_size * sizeof *new_pool);

              if (new_pool == NULL)
                goto leave;

              host->pool = new_pool;
              host->pool_size = new_size;
            }

          /* Finally, add it.  */
          log_assert (host->pool_len < host->pool_size);
          host->pool[host->pool_len++] = tmpidx;
        }
    }
 leave:
  xfree (tmphost);
}
コード例 #27
0
ファイル: findkey.c プロジェクト: HoraceWeebler/gnupg
/* 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;
}
コード例 #28
0
ファイル: ks-engine-hkp.c プロジェクト: larryv/gnupg
/* 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.  If SRVTAG is NULL no service record
 * lookup will be done, if it is set that service name is used.  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 from
 * a service record, 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_HTTPHOST
 * is not NULL a malloced name of the host is stored there; this might
 * be different from R_HOST in case it has been selected from a
 * pool.  */
static gpg_error_t
map_host (ctrl_t ctrl, const char *name, const char *srvtag, int force_reselect,
          enum ks_protocol protocol, char **r_host, char *r_portstr,
          unsigned int *r_httpflags, char **r_httphost)
{
  gpg_error_t err = 0;
  hostinfo_t hi;
  int idx;
  dns_addrinfo_t aibuf, ai;
  int is_pool;
  int new_hosts = 0;
  char *cname;

  *r_host = NULL;
  if (r_httpflags)
    *r_httpflags = 0;
  if (r_httphost)
    *r_httphost = 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)
    {
      idx = create_new_hostinfo (name);
      if (idx == -1)
        return gpg_error_from_syserror ();
      hi = hosttable[idx];
      hi->onion = is_onion_address (name);
    }
  else
    hi = hosttable[idx];

  is_pool = hi->pool != NULL;

  if (srvtag && !is_ip_address (name)
      && ! hi->onion
      && ! (hi->did_srv_lookup & 1 << protocol))
    {
      struct srventry *srvs;
      unsigned int srvscount;

      /* Check for SRV records.  */
      err = get_dns_srv (name, srvtag, NULL, &srvs, &srvscount);
      if (err)
        {
          if (gpg_err_code (err) == GPG_ERR_ECONNREFUSED)
            tor_not_running_p (ctrl);
          return err;
        }

      if (srvscount > 0)
        {
          int i;
          if (! is_pool)
            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, protocol, srvs[i].port);
              new_hosts = 1;
            }

          xfree (srvs);
        }

      hi->did_srv_lookup |= 1 << protocol;
    }

  if (! hi->did_a_lookup
      && ! hi->onion)
    {
      /* 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;
              if (opt.disable_ipv4 && ai->family == AF_INET)
                continue;
              if (opt.disable_ipv6 && ai->family == AF_INET6)
                continue;
              dirmngr_tick (ctrl);

              add_host (name, is_pool, ai, 0, 0);
              new_hosts = 1;
            }

          hi->did_a_lookup = 1;
        }
      xfree (cname);
      free_dns_addrinfo (aibuf);
    }
  if (new_hosts)
    hostinfo_sort_pool (hi);

  if (hi->pool)
    {
      /* Deal with the pool name before selecting a host. */
      if (r_httphost)
        {
          *r_httphost = xtrystrdup (hi->cname? hi->cname : hi->name);
          if (!*r_httphost)
            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);
          if (hi->poolidx == -1)
            {
              log_error ("no alive host found in pool '%s'\n", name);
              if (r_httphost)
                {
                  xfree (*r_httphost);
                  *r_httphost = NULL;
                }
              return gpg_error (GPG_ERR_NO_KEYSERVER);
            }
        }

      assert (hi->poolidx >= 0 && hi->poolidx < hosttable_size);
      hi = hosttable[hi->poolidx];
      assert (hi);
    }
  else if (r_httphost && is_ip_address (hi->name))
    {
      /* This is a numerical IP address and not a pool.  We want to
       * find the canonical name so that it can be used in the HTTP
       * Host header.  Fixme: We should store that name in the
       * hosttable. */
      char *host;

      err = resolve_dns_name (hi->name, 0, 0, SOCK_STREAM, &aibuf, NULL);
      if (!err)
        {
          for (ai = aibuf; ai; ai = ai->next)
            {
              if ((!opt.disable_ipv6 && ai->family == AF_INET6)
                  || (!opt.disable_ipv4 && ai->family == AF_INET))
                {
                  err = resolve_dns_addr (ai->addr, ai->addrlen, 0, &host);
                  if (!err)
                    {
                      /* Okay, we return the first found name.  */
                      *r_httphost = host;
                      break;
                    }
                }
            }
        }
      free_dns_addrinfo (aibuf);
    }

  if (hi->dead)
    {
      log_error ("host '%s' marked as dead\n", hi->name);
      if (r_httphost)
        {
          xfree (*r_httphost);
          *r_httphost = 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_httphost)
        {
          xfree (*r_httphost);
          *r_httphost = NULL;
        }
      return err;
    }
  if (hi->port[protocol])
    snprintf (r_portstr, 6 /* five digits and the sentinel */,
              "%hu", hi->port[protocol]);
  return 0;
}
コード例 #29
0
ファイル: call-agent.c プロジェクト: chrisboyle/gnupg
/* 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;
}
コード例 #30
0
ファイル: ks-engine-hkp.c プロジェクト: Distrotech/gnupg
/* 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 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, 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)
    {
      /* We never saw this host.  Allocate a new entry.  */
      struct addrinfo hints, *aibuf, *ai;
      int *reftbl;
      size_t reftblsize;
      int refidx;
      int is_pool = 0;

      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];

      /* Find all A records for this entry and put them into the pool
         list - if any.  */
      memset (&hints, 0, sizeof (hints));
      hints.ai_family = AF_UNSPEC;
      hints.ai_socktype = SOCK_STREAM;
      hints.ai_flags = AI_CANONNAME;
      /* We can't use the the AI_IDN flag because that does the
         conversion using the current locale.  However, GnuPG always
         used UTF-8.  To support IDN we would need to make use of the
         libidn API.  */
      if (!getaddrinfo (name, NULL, &hints, &aibuf))
        {
          int n_v6, n_v4;

          /* First figure out whether this is a pool.  For a pool we
             use a different strategy than for a plains erver: 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. */
          n_v6 = n_v4 = 0;
          for (ai = aibuf; ai; ai = ai->ai_next)
            {
              if (ai->ai_family != AF_INET6)
                n_v6++;
              else if (ai->ai_family != AF_INET)
                n_v4++;
            }
          if (n_v6 > 1 || n_v4 > 1)
            is_pool = 1;
          if (is_pool && aibuf->ai_canonname)
            hi->cname = xtrystrdup (aibuf->ai_canonname);

          for (ai = aibuf; ai; ai = ai->ai_next)
            {
              char tmphost[NI_MAXHOST + 2];
              int tmpidx;
              int is_numeric;
              int ec;
              int i;

              if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6)
                continue;

              dirmngr_tick (ctrl);

              if (!is_pool && !is_ip_address (name))
                {
                  /* This is a hostname but not a pool.  Use the name
                     as given without going through getnameinfo.  */
                  if (strlen (name)+1 > sizeof tmphost)
                    {
                      ec = EAI_SYSTEM;
                      gpg_err_set_errno (EINVAL);
                    }
                  else
                    {
                      ec = 0;
                      strcpy (tmphost, name);
                    }
                  is_numeric = 0;
                }
              else
                ec = my_getnameinfo (ai, tmphost, sizeof tmphost,
                                     0, &is_numeric);

              if (ec)
                {
                  log_info ("getnameinfo failed while checking '%s': %s\n",
                            name, gai_strerror (ec));
                }
              else if (refidx+1 >= reftblsize)
                {
                  log_error ("getnameinfo returned for '%s': '%s'"
                            " [index table full - ignored]\n", name, tmphost);
                }
              else
                {
                  tmpidx = find_hostinfo (tmphost);
                  log_info ("getnameinfo returned for '%s': '%s'%s\n",
                            name, tmphost,
                            tmpidx == -1? "" : " [already known]");

                  if (tmpidx == -1) /* Create a new entry.  */
                    tmpidx = create_new_hostinfo (tmphost);

                  if (tmpidx == -1)
                    {
                      log_error ("map_host for '%s' problem: %s - '%s'"
                                 " [ignored]\n",
                                 name, strerror (errno), tmphost);
                    }
                  else  /* Set or update the entry. */
                    {
                      char *ipaddr = NULL;

                      if (!is_numeric)
                        {
                          ec = my_getnameinfo (ai, tmphost, sizeof tmphost,
                                               1, &is_numeric);
                          if (!ec && !(ipaddr = xtrystrdup (tmphost)))
                            ec = EAI_SYSTEM;
                          if (ec)
                            log_info ("getnameinfo failed: %s\n",
                                      gai_strerror (ec));
                        }

                      if (ai->ai_family == AF_INET6)
                        {
                          hosttable[tmpidx]->v6 = 1;
                          xfree (hosttable[tmpidx]->v6addr);
                          hosttable[tmpidx]->v6addr = ipaddr;
                        }
                      else if (ai->ai_family == AF_INET)
                        {
                          hosttable[tmpidx]->v4 = 1;
                          xfree (hosttable[tmpidx]->v4addr);
                          hosttable[tmpidx]->v4addr = ipaddr;
                        }
                      else
                        BUG ();

                      for (i=0; i < refidx; i++)
                        if (reftbl[i] == tmpidx)
                          break;
                      if (!(i < refidx) && tmpidx != idx)
                        reftbl[refidx++] = tmpidx;
                    }
                }
            }
          freeaddrinfo (aibuf);
        }
      reftbl[refidx] = -1;
      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 (reftbl, 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 && hi->cname)
        {
          *r_poolname = xtrystrdup (hi->cname);
          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;
    }

  *r_host = xtrystrdup (hi->name);
  if (!*r_host)
    {
      err = gpg_error_from_syserror ();
      if (r_poolname)
        {
          xfree (*r_poolname);
          *r_poolname = NULL;
        }
      return err;
    }
  return 0;
}