Пример #1
0
/* Check whether a default secring.gpg from GnuPG < 2.1 exists and
   import it if not yet done.  */
void
migrate_secring (ctrl_t ctrl)
{
  dotlock_t lockhd = NULL;
  char *secring = NULL;
  char *flagfile = NULL;
  char *agent_version = NULL;

  secring = make_filename (opt.homedir, "secring" EXTSEP_S "gpg", NULL);
  if (access (secring, F_OK))
    goto leave; /* Does not exist or is not readable.  */
  flagfile = make_filename (opt.homedir, V21_MIGRATION_FNAME, NULL);
  if (!access (flagfile, F_OK))
    goto leave; /* Does exist - fine.  */

  log_info ("starting migration from earlier GnuPG versions\n");

  lockhd = dotlock_create (flagfile, 0);
  if (!lockhd)
    {
      log_error ("can't allocate lock for '%s': %s\n",
                 flagfile, gpg_strerror (gpg_error_from_syserror ()));
      goto leave;
    }
  if (dotlock_take (lockhd, -1))
    {
      log_error ("can't lock '%s': %s\n",
                 flagfile, gpg_strerror (gpg_error_from_syserror ()));
      dotlock_destroy (lockhd);
      lockhd = NULL;
      goto leave;
    }

  if (!agent_get_version (ctrl, &agent_version))
    {
      if (!gnupg_compare_version (agent_version, "2.1.0"))
        {
          log_error ("error: GnuPG agent version \"%s\" is too old. ",
                     agent_version);
          log_info ("Please make sure that a recent gpg-agent is running.\n");
          log_info ("(restarting the user session may achieve this.)\n");
          log_info ("migration aborted\n");
          xfree (agent_version);
          goto leave;
        }
      xfree (agent_version);
    }
  else
    {
      log_error ("error: GnuPG agent unusable. "
                 "Please check that a GnuPG agent can be started.\n");
      log_error ("migration aborted\n");
      goto leave;
    }

  log_info ("porting secret keys from '%s' to gpg-agent\n", secring);
  if (!import_old_secring (ctrl, secring))
    {
      FILE *fp = fopen (flagfile, "w");
      if (!fp || fclose (fp))
        log_error ("error creating flag file '%s': %s\n",
                   flagfile, gpg_strerror (gpg_error_from_syserror ()));
      else
        log_info ("migration succeeded\n");
    }

 leave:
  if (lockhd)
    {
      dotlock_release (lockhd);
      dotlock_destroy (lockhd);
    }
  xfree (flagfile);
  xfree (secring);
}
Пример #2
0
/* 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);
}
Пример #3
0
/* Debug function to print the entire hosttable.  */
gpg_error_t
ks_hkp_print_hosttable (ctrl_t ctrl)
{
  gpg_error_t err;
  int idx, idx2;
  hostinfo_t hi;
  membuf_t mb;
  time_t curtime;
  char *p, *died;
  const char *diedstr;

  err = ks_print_help (ctrl, "hosttable (idx, ipv6, ipv4, dead, name, time):");
  if (err)
    return err;

  /* FIXME: We need a lock for the hosttable.  */
  curtime = gnupg_get_time ();
  for (idx=0; idx < hosttable_size; idx++)
    if ((hi=hosttable[idx]))
      {
        if (hi->dead && hi->died_at)
          {
            died = elapsed_time_string (hi->died_at, curtime);
            diedstr = died? died : "error";
          }
        else
          diedstr = died = NULL;

        if (!hi->iporname_valid)
          {
            char *canon = NULL;

            xfree (hi->iporname);
            hi->iporname = NULL;

            /* Do a lookup just for the display purpose.  */
            if (hi->onion || hi->pool)
              ;
            else if (is_ip_address (hi->name))
              {
                dns_addrinfo_t aibuf, ai;

                /* Turn the numerical IP address string into an AI and
                 * then do a DNS PTR lookup.  */
                if (!resolve_dns_name (hi->name, 0, 0,
                                       SOCK_STREAM,
                                       &aibuf, &canon))
                  {
                    if (canon && is_ip_address (canon))
                      {
                        xfree (canon);
                        canon = NULL;
                      }
                    for (ai = aibuf; !canon && ai; ai = ai->next)
                      {
                        resolve_dns_addr (ai->addr, ai->addrlen,
                                          DNS_WITHBRACKET, &canon);
                        if (canon && is_ip_address (canon))
                          {
                            /* We already have the numeric IP - no need to
                             * display it a second time.  */
                            xfree (canon);
                            canon = NULL;
                          }
                      }
                  }
                free_dns_addrinfo (aibuf);
              }
            else
              {
                dns_addrinfo_t aibuf, ai;

                /* Get the IP address as a string from a name.  Note
                 * that resolve_dns_addr allocates CANON on success
                 * and thus terminates the loop. */
                if (!resolve_dns_name (hi->name, 0,
                                       hi->v6? AF_INET6 : AF_INET,
                                       SOCK_STREAM,
                                       &aibuf, NULL))
                  {
                    for (ai = aibuf; !canon && ai; ai = ai->next)
                      {
                        resolve_dns_addr (ai->addr, ai->addrlen,
                                          DNS_NUMERICHOST|DNS_WITHBRACKET,
                                          &canon);
                      }
                  }
                free_dns_addrinfo (aibuf);
              }

            hi->iporname = canon;
            hi->iporname_valid = 1;
          }

        err = ks_printf_help (ctrl, "%3d %s %s %s %s%s%s%s%s%s%s\n",
                              idx,
                              hi->onion? "O" : hi->v6? "6":" ",
                              hi->v4? "4":" ",
                              hi->dead? "d":" ",
                              hi->name,
                              hi->iporname? " (":"",
                              hi->iporname? hi->iporname : "",
                              hi->iporname? ")":"",
                              diedstr? "  (":"",
                              diedstr? diedstr:"",
                              diedstr? ")":""   );
        xfree (died);
        if (err)
          return err;

        if (hi->cname)
          err = ks_printf_help (ctrl, "  .       %s", hi->cname);
        if (err)
          return err;

        if (hi->pool)
          {
            init_membuf (&mb, 256);
            put_membuf_printf (&mb, "  .   -->");
            for (idx2 = 0; idx2 < hi->pool_len && hi->pool[idx2] != -1; idx2++)
              {
                put_membuf_printf (&mb, " %d", hi->pool[idx2]);
                if (hi->poolidx == hi->pool[idx2])
                  put_membuf_printf (&mb, "*");
              }
            put_membuf( &mb, "", 1);
            p = get_membuf (&mb, NULL);
            if (!p)
              return gpg_error_from_syserror ();
            err = ks_print_help (ctrl, p);
            xfree (p);
            if (err)
              return err;
          }
      }
  return 0;
}
Пример #4
0
/* 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.  If R_HTTP_STATUS is not NULL, the http
   status code will be stored there.  */
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, unsigned int *r_http_status)
{
  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;
  parsed_uri_t uri = NULL;
  int is_onion;

  *r_fp = NULL;

  err = http_parse_uri (&uri, request, 0);
  if (err)
    goto leave;
  is_onion = uri->onion;

  err = http_session_new (&session, httphost,
                          ((ctrl->http_no_crl? HTTP_FLAG_NO_CRL : 0)
                           | HTTP_FLAG_TRUST_DEF),
                          gnupg_http_tls_verify_cb, ctrl);
  if (err)
    goto leave;
  http_session_set_log_cb (session, cert_log_cb);
  http_session_set_timeout (session, ctrl->timeout);

 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)
                    |(dirmngr_use_tor ()? HTTP_FLAG_FORCE_TOR:0)
                    |(opt.disable_ipv4? HTTP_FLAG_IGNORE_IPv4 : 0)
                    |(opt.disable_ipv6? HTTP_FLAG_IGNORE_IPv6 : 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;
    }

  if (r_http_status)
    *r_http_status = http_get_status_code (http);

  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-- )
          {
            if (is_onion)
              {
                /* Make sure that an onion address only redirects to
                 * another onion address.  */
                http_release_parsed_uri (uri);
                uri = NULL;
                err = http_parse_uri (&uri, s, 0);
                if (err)
                  goto leave;

                if (! uri->onion)
                  {
                    err = gpg_error (GPG_ERR_FORBIDDEN);
                    goto leave;
                  }
              }

            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;

    case 501:
      err = gpg_error (GPG_ERR_NOT_IMPLEMENTED);
      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);
  http_release_parsed_uri (uri);
  return err;
}
Пример #5
0
/* Get the key described key the KEYSPEC string from the keyserver
   identified by URI.  On success R_FP has an open stream to read the
   data.  The data will be provided in a format GnuPG can import
   (either a binary OpenPGP message or an armored one).  */
gpg_error_t
ks_hkp_get (ctrl_t ctrl, parsed_uri_t uri, const char *keyspec, estream_t *r_fp)
{
  gpg_error_t err;
  KEYDB_SEARCH_DESC desc;
  char kidbuf[2+40+1];
  const char *exactname = NULL;
  char *searchkey = NULL;
  char *hostport = NULL;
  char *request = NULL;
  estream_t fp = NULL;
  int reselect;
  char *httphost = NULL;
  unsigned int httpflags;
  unsigned int tries = SEND_REQUEST_RETRIES;

  *r_fp = NULL;

  /* Remove search type indicator and adjust PATTERN accordingly.
     Note that HKP keyservers like the 0x to be present when searching
     by keyid.  We need to re-format the fingerprint and keyids so to
     remove the gpg specific force-use-of-this-key flag ("!").  */
  err = classify_user_id (keyspec, &desc, 1);
  if (err)
    return err;
  switch (desc.mode)
    {
    case KEYDB_SEARCH_MODE_SHORT_KID:
      snprintf (kidbuf, sizeof kidbuf, "0x%08lX", (ulong)desc.u.kid[1]);
      break;
    case KEYDB_SEARCH_MODE_LONG_KID:
      snprintf (kidbuf, sizeof kidbuf, "0x%08lX%08lX",
		(ulong)desc.u.kid[0], (ulong)desc.u.kid[1]);
      break;
    case KEYDB_SEARCH_MODE_FPR20:
    case KEYDB_SEARCH_MODE_FPR:
      /* This is a v4 fingerprint. */
      kidbuf[0] = '0';
      kidbuf[1] = 'x';
      bin2hex (desc.u.fpr, 20, kidbuf+2);
      break;

    case KEYDB_SEARCH_MODE_EXACT:
      exactname = desc.u.name;
      break;

    case KEYDB_SEARCH_MODE_FPR16:
      log_error ("HKP keyservers do not support v3 fingerprints\n");
      /* fall through */
    default:
      return gpg_error (GPG_ERR_INV_USER_ID);
    }

  searchkey = http_escape_string (exactname? exactname : kidbuf,
                                  EXTRA_ESCAPE_CHARS);
  if (!searchkey)
    {
      err = gpg_error_from_syserror ();
      goto leave;
    }

  reselect = 0;
 again:
  /* Build the request string.  */
  xfree (hostport); hostport = NULL;
  xfree (httphost); httphost = NULL;
  err = make_host_part (ctrl, uri->scheme, uri->host, uri->port,
                        reselect, uri->explicit_port,
                        &hostport, &httpflags, &httphost);
  if (err)
    goto leave;

  xfree (request);
  request = strconcat (hostport,
                       "/pks/lookup?op=get&options=mr&search=",
                       searchkey,
                       exactname? "&exact=on":"",
                       NULL);
  if (!request)
    {
      err = gpg_error_from_syserror ();
      goto leave;
    }

  /* Send the request.  */
  err = send_request (ctrl, request, hostport, httphost, httpflags,
                      NULL, NULL, &fp, NULL);
  if (handle_send_request_error (ctrl, err, request, &tries))
    {
      reselect = 1;
      goto again;
    }
  if (err)
    goto leave;

  err = dirmngr_status (ctrl, "SOURCE", hostport, NULL);
  if (err)
    goto leave;

  /* Return the read stream and close the HTTP context.  */
  *r_fp = fp;
  fp = NULL;

 leave:
  es_fclose (fp);
  xfree (request);
  xfree (hostport);
  xfree (httphost);
  xfree (searchkey);
  return err;
}
Пример #6
0
/*
 * Encrypt the file with the given userids (or ask if none is
 * supplied).  Either FILENAME or FILEFD must be given, but not both.
 * The caller may provide a checked list of public keys in
 * PROVIDED_PKS; if not the function builds a list of keys on its own.
 *
 * Note that FILEFD is currently only used by cmd_encrypt in the
 * not yet finished server.c.
 */
int
encrypt_crypt (ctrl_t ctrl, int filefd, const char *filename,
               strlist_t remusr, int use_symkey, pk_list_t provided_keys,
               int outputfd)
{
  iobuf_t inp = NULL;
  iobuf_t out = NULL;
  PACKET pkt;
  PKT_plaintext *pt = NULL;
  DEK *symkey_dek = NULL;
  STRING2KEY *symkey_s2k = NULL;
  int rc = 0, rc2 = 0;
  u32 filesize;
  cipher_filter_context_t cfx;
  armor_filter_context_t *afx = NULL;
  compress_filter_context_t zfx;
  text_filter_context_t tfx;
  progress_filter_context_t *pfx;
  PK_LIST pk_list;
  int do_compress;
  int compliant;

  if (filefd != -1 && filename)
    return gpg_error (GPG_ERR_INV_ARG);  /* Both given.  */

  do_compress = !!opt.compress_algo;

  pfx = new_progress_context ();
  memset( &cfx, 0, sizeof cfx);
  memset( &zfx, 0, sizeof zfx);
  memset( &tfx, 0, sizeof tfx);
  init_packet(&pkt);

  if (use_symkey
      && (rc=setup_symkey(&symkey_s2k,&symkey_dek)))
    {
      release_progress_context (pfx);
      return rc;
    }

  if (provided_keys)
    pk_list = provided_keys;
  else
    {
      if ((rc = build_pk_list (ctrl, remusr, &pk_list)))
        {
          release_progress_context (pfx);
          return rc;
        }
    }

  /* Prepare iobufs. */
#ifdef HAVE_W32_SYSTEM
  if (filefd == -1)
    inp = iobuf_open (filename);
  else
    {
      inp = NULL;
      gpg_err_set_errno (ENOSYS);
    }
#else
  if (filefd == GNUPG_INVALID_FD)
    inp = iobuf_open (filename);
  else
    inp = iobuf_fdopen_nc (FD2INT(filefd), "rb");
#endif
  if (inp)
    iobuf_ioctl (inp, IOBUF_IOCTL_NO_CACHE, 1, NULL);
  if (inp && is_secured_file (iobuf_get_fd (inp)))
    {
      iobuf_close (inp);
      inp = NULL;
      gpg_err_set_errno (EPERM);
    }
  if (!inp)
    {
      char xname[64];

      rc = gpg_error_from_syserror ();
      if (filefd != -1)
        snprintf (xname, sizeof xname, "[fd %d]", filefd);
      else if (!filename)
        strcpy (xname, "[stdin]");
      else
        *xname = 0;
      log_error (_("can't open '%s': %s\n"),
                 *xname? xname : filename, gpg_strerror (rc) );
      goto leave;
    }

  if (opt.verbose)
    log_info (_("reading from '%s'\n"), iobuf_get_fname_nonnull (inp));

  handle_progress (pfx, inp, filename);

  if (opt.textmode)
    iobuf_push_filter (inp, text_filter, &tfx);

  rc = open_outfile (outputfd, filename, opt.armor? 1:0, 0, &out);
  if (rc)
    goto leave;

  if (opt.armor)
    {
      afx = new_armor_context ();
      push_armor_filter (afx, out);
    }

  /* Create a session key. */
  cfx.dek = xmalloc_secure_clear (sizeof *cfx.dek);
  if (!opt.def_cipher_algo)
    {
      /* Try to get it from the prefs.  */
      cfx.dek->algo = select_algo_from_prefs (pk_list, PREFTYPE_SYM, -1, NULL);
      /* The only way select_algo_from_prefs can fail here is when
         mixing v3 and v4 keys, as v4 keys have an implicit preference
         entry for 3DES, and the pk_list cannot be empty.  In this
         case, use 3DES anyway as it's the safest choice - perhaps the
         v3 key is being used in an OpenPGP implementation and we know
         that the implementation behind any v4 key can handle 3DES. */
      if (cfx.dek->algo == -1)
        {
          cfx.dek->algo = CIPHER_ALGO_3DES;
        }

      /* In case 3DES has been selected, print a warning if any key
         does not have a preference for AES.  This should help to
         indentify why encrypting to several recipients falls back to
         3DES. */
      if (opt.verbose && cfx.dek->algo == CIPHER_ALGO_3DES)
        warn_missing_aes_from_pklist (pk_list);
    }
  else
    {
      if (!opt.expert
          && (select_algo_from_prefs (pk_list, PREFTYPE_SYM,
                                      opt.def_cipher_algo, NULL)
              != opt.def_cipher_algo))
        {
          log_info(_("WARNING: forcing symmetric cipher %s (%d)"
                     " violates recipient preferences\n"),
                   openpgp_cipher_algo_name (opt.def_cipher_algo),
                   opt.def_cipher_algo);
        }

      cfx.dek->algo = opt.def_cipher_algo;
    }

  /* Check compliance.  */
  if (! gnupg_cipher_is_allowed (opt.compliance, 1, cfx.dek->algo,
                                 GCRY_CIPHER_MODE_CFB))
    {
      log_error (_("cipher algorithm '%s' may not be used in %s mode\n"),
		 openpgp_cipher_algo_name (cfx.dek->algo),
		 gnupg_compliance_option_string (opt.compliance));
      rc = gpg_error (GPG_ERR_CIPHER_ALGO);
      goto leave;
    }

  if (!gnupg_rng_is_compliant (opt.compliance))
    {
      rc = gpg_error (GPG_ERR_FORBIDDEN);
      log_error (_("%s is not compliant with %s mode\n"),
                 "RNG",
                 gnupg_compliance_option_string (opt.compliance));
      write_status_error ("random-compliance", rc);
      goto leave;
    }

  compliant = gnupg_cipher_is_compliant (CO_DE_VS, cfx.dek->algo,
                                         GCRY_CIPHER_MODE_CFB);

  {
    pk_list_t pkr;

    for (pkr = pk_list; pkr; pkr = pkr->next)
      {
        PKT_public_key *pk = pkr->pk;
        unsigned int nbits = nbits_from_pk (pk);

        if (!gnupg_pk_is_compliant (opt.compliance,
                                    pk->pubkey_algo, pk->pkey, nbits, NULL))
          log_info (_("WARNING: key %s is not suitable for encryption"
                      " in %s mode\n"),
                    keystr_from_pk (pk),
                    gnupg_compliance_option_string (opt.compliance));

        if (compliant
            && !gnupg_pk_is_compliant (CO_DE_VS, pk->pubkey_algo, pk->pkey,
                                       nbits, NULL))
          compliant = 0;
      }

  }

  if (compliant)
    write_status_strings (STATUS_ENCRYPTION_COMPLIANCE_MODE,
                          gnupg_status_compliance_flag (CO_DE_VS),
                          NULL);

  cfx.dek->use_aead = use_aead (pk_list, cfx.dek->algo);
  if (!cfx.dek->use_aead)
    cfx.dek->use_mdc = !!use_mdc (pk_list, cfx.dek->algo);

  /* Only do the is-file-already-compressed check if we are using a
   * MDC or AEAD.  This forces compressed files to be re-compressed if
   * we do not have a MDC to give some protection against chosen
   * ciphertext attacks. */
  if (do_compress
      && (cfx.dek->use_mdc || cfx.dek->use_aead)
      && is_file_compressed (filename, &rc2))
    {
      if (opt.verbose)
        log_info(_("'%s' already compressed\n"), filename);
      do_compress = 0;
    }
  if (rc2)
    {
      rc = rc2;
      goto leave;
    }

  make_session_key (cfx.dek);
  if (DBG_CRYPTO)
    log_printhex (cfx.dek->key, cfx.dek->keylen, "DEK is: ");

  rc = write_pubkey_enc_from_list (ctrl, pk_list, cfx.dek, out);
  if (rc)
    goto leave;

  /* We put the passphrase (if any) after any public keys as this
   * seems to be the most useful on the recipient side - there is no
   * point in prompting a user for a passphrase if they have the
   * secret key needed to decrypt.  */
  if (use_symkey && (rc = write_symkey_enc (symkey_s2k, cfx.dek->use_aead,
                                            symkey_dek, cfx.dek, out)))
    goto leave;

  if (!opt.no_literal)
    pt = setup_plaintext_name (filename, inp);

  /* Get the size of the file if possible, i.e., if it is a real file.  */
  if (filename && *filename
      && !iobuf_is_pipe_filename (filename) && !opt.textmode )
    {
      off_t tmpsize;
      int overflow;

      if ( !(tmpsize = iobuf_get_filelength(inp, &overflow))
           && !overflow && opt.verbose)
        log_info(_("WARNING: '%s' is an empty file\n"), filename );
      /* We can't encode the length of very large files because
         OpenPGP uses only 32 bit for file sizes.  So if the size
         of a file is larger than 2^32 minus some bytes for packet
         headers, we switch to partial length encoding. */
      if (tmpsize < (IOBUF_FILELENGTH_LIMIT - 65536) )
        filesize = tmpsize;
      else
        filesize = 0;
    }
  else
    filesize = opt.set_filesize ? opt.set_filesize : 0; /* stdin */

  if (!opt.no_literal)
    {
      pt->timestamp = make_timestamp();
      pt->mode = opt.mimemode? 'm' : opt.textmode ? 't' : 'b';
      pt->len = filesize;
      pt->new_ctb = !pt->len;
      pt->buf = inp;
      pkt.pkttype = PKT_PLAINTEXT;
      pkt.pkt.plaintext = pt;
      cfx.datalen = filesize && !do_compress? calc_packet_length( &pkt ) : 0;
    }
  else
    cfx.datalen = filesize && !do_compress ? filesize : 0;

  /* Register the cipher filter. */
  iobuf_push_filter (out,
                     cfx.dek->use_aead? cipher_filter_aead
                     /**/             : cipher_filter_cfb,
                     &cfx);

  /* Register the compress filter. */
  if (do_compress)
    {
      int compr_algo = opt.compress_algo;

      if (compr_algo == -1)
        {
          compr_algo = select_algo_from_prefs (pk_list, PREFTYPE_ZIP, -1, NULL);
          if (compr_algo == -1)
            compr_algo = DEFAULT_COMPRESS_ALGO;
          /* Theoretically impossible to get here since uncompressed
             is implicit.  */
        }
      else if (!opt.expert
               && select_algo_from_prefs(pk_list, PREFTYPE_ZIP,
                                         compr_algo, NULL) != compr_algo)
        {
          log_info (_("WARNING: forcing compression algorithm %s (%d)"
                      " violates recipient preferences\n"),
                    compress_algo_to_string(compr_algo), compr_algo);
        }

      /* Algo 0 means no compression. */
      if (compr_algo)
        {
          if (cfx.dek && (cfx.dek->use_mdc || cfx.dek->use_aead))
            zfx.new_ctb = 1;
          push_compress_filter (out,&zfx,compr_algo);
        }
    }

  /* Do the work. */
  if (!opt.no_literal)
    {
      if ((rc = build_packet( out, &pkt )))
        log_error ("build_packet failed: %s\n", gpg_strerror (rc));
    }
  else
    {
      /* User requested not to create a literal packet, so we copy the
         plain data. */
      byte copy_buffer[4096];
      int  bytes_copied;
      while ((bytes_copied = iobuf_read (inp, copy_buffer, 4096)) != -1)
        {
          rc = iobuf_write (out, copy_buffer, bytes_copied);
          if (rc)
            {
              log_error ("copying input to output failed: %s\n",
                         gpg_strerror (rc));
              break;
            }
        }
      wipememory (copy_buffer, 4096); /* Burn the buffer. */
    }

  /* Finish the stuff. */
 leave:
  iobuf_close (inp);
  if (rc)
    iobuf_cancel (out);
  else
    {
      iobuf_close (out); /* fixme: check returncode */
      write_status (STATUS_END_ENCRYPTION);
    }
  if (pt)
    pt->buf = NULL;
  free_packet (&pkt, NULL);
  xfree (cfx.dek);
  xfree (symkey_dek);
  xfree (symkey_s2k);
  if (!provided_keys)
    release_pk_list (pk_list);
  release_armor_context (afx);
  release_progress_context (pfx);
  return rc;
}
Пример #7
0
/* Try to find KEY in the file FNAME.  */
static char *
findkey_fname (const char *key, const char *fname)
{
  gpg_error_t err = 0;
  FILE *fp;
  int lnr = 0;
  int c;
  char *p, line[256];
  int in_item = 0;
  membuf_t mb = MEMBUF_ZERO;

  fp = fopen (fname, "r");
  if (!fp)
    {
      if (errno != ENOENT)
        {
          err = gpg_error_from_syserror ();
          log_error (_("can't open `%s': %s\n"), fname, gpg_strerror (err));
        }
      return NULL;
    }

  while (fgets (line, DIM(line)-1, fp))
    {
      lnr++;
      
      if (!*line || line[strlen(line)-1] != '\n')
        {
          /* Eat until end of line. */
          while ( (c=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));
        }
      else
        line[strlen(line)-1] = 0; /* Chop the LF. */
      
    again:
      if (!in_item)
        {
          /* Allow for empty lines and spaces while not in an item. */
          for (p=line; spacep (p); p++)
            ;
          if (!*p || *p == '#')
            continue;
          if (*line != '.' || spacep(line+1))
            {
              log_info (_("file `%s', line %d: %s\n"),
                        fname, lnr, _("ignoring garbage line"));
              continue;
            }
          trim_trailing_spaces (line);
          in_item = 1;
          if (!strcmp (line+1, key))
            {
              /* Found.  Start collecting.  */
              init_membuf (&mb, 1024);
            }
          continue;
        }

      /* If in an item only allow for comments in the first column
         and provide ". " as an escape sequence to allow for
         leading dots and hash marks in the actual text.  */
      if (*line == '#')
        continue;
      if (*line == '.')
        { 
          if (spacep(line+1))
            p = line + 2;
          else
            {
              trim_trailing_spaces (line);
              in_item = 0;
              if (is_membuf_ready (&mb))
                break;        /* Yep, found and collected the item.  */
              if (!line[1])
                continue;     /* Just an end of text dot. */
              goto again;     /* A new key line.  */
            }
        }
      else
        p = line;

      if (is_membuf_ready (&mb))
        {
          put_membuf_str (&mb, p);
          put_membuf (&mb, "\n", 1);
        }

    }
  if ( !err && ferror (fp) )
    {
      err = gpg_error_from_syserror ();
      log_error (_("error reading `%s', line %d: %s\n"),
                 fname, lnr, gpg_strerror (err));
    }
  
  fclose (fp);
  if (is_membuf_ready (&mb))
    {
      /* We have collected something.  */
      if (err)
        {
          xfree (get_membuf (&mb, NULL));
          return NULL;
        }
      else
        {
          put_membuf (&mb, "", 1);  /* Terminate string.  */
          return get_membuf (&mb, NULL);
        }
    }
  else
    return NULL;
}
Пример #8
0
/* Perform asynchronous operations in the global event loop (ie, any
   asynchronous operation except key listing and trustitem listing
   operations).  If CTX is not a null pointer, the function will
   return if the asynchronous operation in the context CTX finished.
   Otherwise the function will return if any asynchronous operation
   finished.  If HANG is zero, the function will not block for a long
   time.  Otherwise the function does not return until an operation
   matching CTX finished.

   If a matching context finished, it is returned, and *STATUS is set
   to the error value of the operation in that context.  Otherwise, if
   the timeout expires, NULL is returned and *STATUS is 0.  If an
   error occurs, NULL is returned and *STATUS is set to the error
   value.  */
gpgme_ctx_t
gpgme_wait_ext (gpgme_ctx_t ctx, gpgme_error_t *status,
		gpgme_error_t *op_err, int hang)
{
  do
    {
      unsigned int i = 0;
      struct ctx_list_item *li;
      struct fd_table fdt;
      int nr;

      /* Collect the active file descriptors.  */
      LOCK (ctx_list_lock);
      for (li = ctx_active_list; li; li = li->next)
	i += li->ctx->fdt.size;
      fdt.fds = malloc (i * sizeof (struct io_select_fd_s));
      if (!fdt.fds)
	{
          int saved_err = gpg_error_from_syserror ();
	  UNLOCK (ctx_list_lock);
	  if (status)
	    *status = saved_err;
	  if (op_err)
	    *op_err = 0;
	  return NULL;
	}
      fdt.size = i;
      i = 0;
      for (li = ctx_active_list; li; li = li->next)
	{
	  memcpy (&fdt.fds[i], li->ctx->fdt.fds,
		  li->ctx->fdt.size * sizeof (struct io_select_fd_s));
	  i += li->ctx->fdt.size;
	}
      UNLOCK (ctx_list_lock);

      nr = _gpgme_io_select (fdt.fds, fdt.size, 0);
      if (nr < 0)
	{
          int saved_err = gpg_error_from_syserror ();
	  free (fdt.fds);
	  if (status)
	    *status = saved_err;
	  if (op_err)
	    *op_err = 0;
	  return NULL;
	}

      for (i = 0; i < fdt.size && nr; i++)
	{
	  if (fdt.fds[i].fd != -1 && fdt.fds[i].signaled)
	    {
	      gpgme_ctx_t ictx;
	      gpgme_error_t err = 0;
	      gpgme_error_t local_op_err = 0;
	      struct wait_item_s *item;

	      assert (nr);
	      nr--;

	      item = (struct wait_item_s *) fdt.fds[i].opaque;
	      assert (item);
	      ictx = item->ctx;
	      assert (ictx);

	      LOCK (ctx->lock);
	      if (ctx->canceled)
		err = gpg_error (GPG_ERR_CANCELED);
	      UNLOCK (ctx->lock);

	      if (!err)
		err = _gpgme_run_io_cb (&fdt.fds[i], 0, &local_op_err);
	      if (err || local_op_err)
		{
		  /* An error occurred.  Close all fds in this context,
		     and signal it.  */
		  _gpgme_cancel_with_err (ictx, err, local_op_err);

		  /* Break out of the loop, and retry the select()
		     from scratch, because now all fds should be
		     gone.  */
		  break;
		}
	    }
	}
      free (fdt.fds);

      /* Now some contexts might have finished successfully.  */
      LOCK (ctx_list_lock);
    retry:
      for (li = ctx_active_list; li; li = li->next)
	{
	  gpgme_ctx_t actx = li->ctx;

	  for (i = 0; i < actx->fdt.size; i++)
	    if (actx->fdt.fds[i].fd != -1)
	      break;
	  if (i == actx->fdt.size)
	    {
	      struct gpgme_io_event_done_data data;
	      data.err = 0;
	      data.op_err = 0;

	      /* FIXME: This does not perform too well.  We have to
		 release the lock because the I/O event handler
		 acquires it to remove the context from the active
		 list.  Two alternative strategies are worth
		 considering: Either implement the DONE event handler
		 here in a lock-free manner, or save a list of all
		 contexts to be released and call the DONE events
		 afterwards.  */
	      UNLOCK (ctx_list_lock);
	      _gpgme_engine_io_event (actx->engine, GPGME_EVENT_DONE, &data);
	      LOCK (ctx_list_lock);
	      goto retry;
	    }
	}
      UNLOCK (ctx_list_lock);

      {
	gpgme_ctx_t dctx = ctx_wait (ctx, status, op_err);

	if (dctx)
	  {
	    ctx = dctx;
	    hang = 0;
	  }
	else if (!hang)
	  {
	    ctx = NULL;
	    if (status)
	      *status = 0;
	    if (op_err)
	      *op_err = 0;
	  }
      }
    }
  while (hang);

  return ctx;
}
Пример #9
0
gpgme_error_t
_gpgme_decrypt_status_handler (void *priv, gpgme_status_code_t code,
			       char *args)
{
  gpgme_ctx_t ctx = (gpgme_ctx_t) priv;
  gpgme_error_t err;
  void *hook;
  op_data_t opd;

  err = _gpgme_passphrase_status_handler (priv, code, args);
  if (err)
    return err;

  err = _gpgme_op_data_lookup (ctx, OPDATA_DECRYPT, &hook, -1, NULL);
  opd = hook;
  if (err)
    return err;

  switch (code)
    {
    case GPGME_STATUS_FAILURE:
      opd->failure_code = _gpgme_parse_failure (args);
      break;

    case GPGME_STATUS_EOF:
      /* FIXME: These error values should probably be attributed to
	 the underlying crypto engine (as error source).  */
      if (opd->failed)
	return gpg_error (GPG_ERR_DECRYPT_FAILED);
      else if (!opd->okay)
	return gpg_error (GPG_ERR_NO_DATA);
      else if (opd->failure_code)
        return opd->failure_code;
      break;

    case GPGME_STATUS_DECRYPTION_INFO:
      /* Fixme: Provide a way to return the used symmetric algorithm. */
      break;

    case GPGME_STATUS_DECRYPTION_OKAY:
      opd->okay = 1;
      break;

    case GPGME_STATUS_DECRYPTION_FAILED:
      opd->failed = 1;
      break;

    case GPGME_STATUS_ERROR:
      /* Note that this is an informational status code which should
         not lead to an error return unless it is something not
         related to the backend.  */
      {
	const char d_alg[] = "decrypt.algorithm";
	const char k_alg[] = "decrypt.keyusage";

	if (!strncmp (args, d_alg, sizeof (d_alg) - 1))
	  {
	    args += sizeof (d_alg) - 1;
	    while (*args == ' ')
	      args++;

	    if (gpg_err_code (atoi (args)) == GPG_ERR_UNSUPPORTED_ALGORITHM)
	      {
		char *end;

		while (*args && *args != ' ')
		  args++;
		while (*args == ' ')
		  args++;

		end = strchr (args, ' ');
		if (end)
		  *end = '\0';

		if (!(*args == '?' && *(args + 1) == '\0'))
		  {
		    opd->result.unsupported_algorithm = strdup (args);
		    if (!opd->result.unsupported_algorithm)
		      return gpg_error_from_syserror ();
		  }
	      }
	  }
	else if (!strncmp (args, k_alg, sizeof (k_alg) - 1))
	  {
	    args += sizeof (k_alg) - 1;
	    while (*args == ' ')
	      args++;

	    if (gpg_err_code (atoi (args)) == GPG_ERR_WRONG_KEY_USAGE)
	      opd->result.wrong_key_usage = 1;
	  }
      }
      break;

    case GPGME_STATUS_ENC_TO:
      err = parse_enc_to (args, opd->last_recipient_p, ctx->protocol);
      if (err)
	return err;

      opd->last_recipient_p = &(*opd->last_recipient_p)->next;
      break;

    case GPGME_STATUS_NO_SECKEY:
      {
	gpgme_recipient_t rec = opd->result.recipients;

	while (rec)
	  {
	    if (!strcmp (rec->keyid, args))
	      {
		rec->status = gpg_error (GPG_ERR_NO_SECKEY);
		break;
	      }
	    rec = rec->next;
	  }
	/* FIXME: Is this ok?  */
	if (!rec)
	  return trace_gpg_error (GPG_ERR_INV_ENGINE);
      }
      break;

    case GPGME_STATUS_PLAINTEXT:
      err = _gpgme_parse_plaintext (args, &opd->result.file_name);
      if (err)
	return err;
      break;

    case GPGME_STATUS_INQUIRE_MAXLEN:
      if (ctx->status_cb)
        {
          err = ctx->status_cb (ctx->status_cb_value, "INQUIRE_MAXLEN", args);
          if (err)
            return err;
        }
      break;

    default:
      break;
    }

  return 0;
}
Пример #10
0
/* Re-import certifciates.  IN_FD is a list of linefeed delimited
   fingerprints t re-import.  The actual re-import is done by clearing
   the ephemeral flag.  */
static int
reimport_one (ctrl_t ctrl, struct stats_s *stats, int in_fd)
{
  gpg_error_t err = 0;
  estream_t fp = NULL;
  char line[100];  /* Sufficient for a fingerprint.  */
  KEYDB_HANDLE kh;
  KEYDB_SEARCH_DESC desc;
  ksba_cert_t cert = NULL;
  unsigned int flags;

  kh = keydb_new (0);
  if (!kh)
    {
      err = gpg_error (GPG_ERR_ENOMEM);;
      log_error (_("failed to allocate keyDB handle\n"));
      goto leave;
    }
  keydb_set_ephemeral (kh, 1);
   
  fp = es_fdopen_nc (in_fd, "r");
  if (!fp)
    {
      err = gpg_error_from_syserror ();
      log_error ("es_fdopen(%d) failed: %s\n", in_fd, gpg_strerror (err));
      goto leave;
    }

  while (es_fgets (line, DIM(line)-1, fp) )
    {
      if (*line && line[strlen(line)-1] != '\n')
        {
          err = gpg_error (GPG_ERR_LINE_TOO_LONG);
          goto leave;
	}
      trim_spaces (line);
      if (!*line)
        continue;
    
      stats->count++;

      err = keydb_classify_name (line, &desc);
      if (err)
        {
          print_import_problem (ctrl, NULL, 0);
          stats->not_imported++;
          continue;
        }

      keydb_search_reset (kh);
      err = keydb_search (kh, &desc, 1);
      if (err)
        {
          print_import_problem (ctrl, NULL, 0);
          stats->not_imported++;
          continue;
        }

      ksba_cert_release (cert);
      cert = NULL;
      err = keydb_get_cert (kh, &cert);
      if (err)
        {
          log_error ("keydb_get_cert() failed: %s\n", gpg_strerror (err));
          print_import_problem (ctrl, NULL, 1);
          stats->not_imported++;
          continue;
        }

      err = keydb_get_flags (kh, KEYBOX_FLAG_BLOB, 0, &flags);
      if (err)
        {
          log_error (_("error getting stored flags: %s\n"), gpg_strerror (err));
          print_imported_status (ctrl, cert, 0);
          stats->not_imported++;
          continue;
        }
      if ( !(flags & KEYBOX_FLAG_BLOB_EPHEMERAL) )
        {
          print_imported_status (ctrl, cert, 0);
          stats->unchanged++;
          continue;
        }

      err = keydb_set_cert_flags (cert, 1, KEYBOX_FLAG_BLOB, 0,
                                  KEYBOX_FLAG_BLOB_EPHEMERAL, 0);
      if (err)
        {
          log_error ("clearing ephemeral flag failed: %s\n",
                     gpg_strerror (err)); 
          print_import_problem (ctrl, cert, 0);
          stats->not_imported++;
          continue;
        }

      print_imported_status (ctrl, cert, 1);
      stats->imported++;
    }
  err = 0;
  if (es_ferror (fp))
    {
      err = gpg_error_from_syserror ();
      log_error ("error reading fd %d: %s\n", in_fd, gpg_strerror (err));
      goto leave;
    }

 leave:
  ksba_cert_release (cert);
  keydb_release (kh);
  es_fclose (fp);
  return err;
}
Пример #11
0
/* Assume that the reader is at a pkcs#12 message and try to import
   certificates from that stupid format.  We will also store secret
   keys.  All of the pkcs#12 parsing and key storing is handled by the
   gpg-protect-tool, we merely have to take care of receiving the
   certificates. On success RETFP returns a temporary file with
   certificates. */
static gpg_error_t
parse_p12 (ctrl_t ctrl, ksba_reader_t reader,
           FILE **retfp, struct stats_s *stats)
{
  const char *pgmname;
  gpg_error_t err = 0, child_err = 0;
  int c, cont_line;
  unsigned int pos;
  FILE *tmpfp, *certfp = NULL, *fp = NULL;
  char buffer[1024];
  size_t nread;
  pid_t pid = -1;
  int bad_pass = 0;

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

  *retfp = NULL;

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

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

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

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

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

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

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


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

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

  if (bad_pass)
    {
      /* We only write a plain error code and not direct
         BAD_PASSPHRASE because the pkcs12 parser might issue this
         message multiple times, BAD_PASSPHRASE in general requires a
         keyID and parts of the import might actually succeed so that
         IMPORT_PROBLEM is also not appropriate. */
      gpgsm_status_with_err_code (ctrl, STATUS_ERROR,
                                  "import.parsep12", GPG_ERR_BAD_PASSPHRASE);
    }
  
  return err;
}
Пример #12
0
/* Call GPG to decrypt a block of data.


 */
gpg_error_t
gpg_decrypt_blob (ctrl_t ctrl, const void *ciph, size_t ciphlen,
                  void **r_plain, size_t *r_plainlen)
{
  gpg_error_t err;
  assuan_context_t ctx = NULL;
  int outbound_fds[2] = { -1, -1 };
  int inbound_fds[2]  = { -1, -1 };
  npth_t writer_thread = (npth_t)0;
  npth_t reader_thread = (npth_t)0;
  gpg_error_t writer_err, reader_err;
  membuf_t reader_mb;
  int ret;

  *r_plain = NULL;
  *r_plainlen = 0;

  /* Init the memory buffer to receive the encrypted stuff.  */
  init_membuf_secure (&reader_mb, 1024);

  /* Create two pipes.  */
  err = gnupg_create_outbound_pipe (outbound_fds);
  if (!err)
    err = gnupg_create_inbound_pipe (inbound_fds);
  if (err)
    {
      log_error (_("error creating a pipe: %s\n"), gpg_strerror (err));
      goto leave;
    }

  /* Start GPG and send the INPUT and OUTPUT commands.  */
  err = start_gpg (ctrl, outbound_fds[0], inbound_fds[1], &ctx);
  if (err)
    goto leave;
  close (outbound_fds[0]); outbound_fds[0] = -1;
  close (inbound_fds[1]); inbound_fds[1] = -1;

  /* Start a writer thread to feed the INPUT command of the server.  */
  err = start_writer (outbound_fds[1], ciph, ciphlen,
                      &writer_thread, &writer_err);
  if (err)
    return err;
  outbound_fds[1] = -1;  /* The thread owns the FD now.  */

  /* Start a reader thread to eat from the OUTPUT command of the
     server.  */
  err = start_reader (inbound_fds[0], &reader_mb,
                      &reader_thread, &reader_err);
  if (err)
    return err;
  outbound_fds[0] = -1;  /* The thread owns the FD now.  */

  /* Run the decryption.  */
  err = assuan_transact (ctx, "DECRYPT", NULL, NULL, NULL, NULL, NULL, NULL);
  if (err)
    {
      log_error ("the engine's DECRYPT command failed: %s <%s>\n",
                 gpg_strerror (err), gpg_strsource (err));
      goto leave;
    }

  /* Wait for reader and return the data.  */
  ret = npth_join (reader_thread, NULL);
  if (ret)
    {
      err = gpg_error_from_errno (ret);
      log_error ("waiting for reader thread failed: %s\n", gpg_strerror (err));
      goto leave;
    }
  memset (&reader_thread, '\0', sizeof (reader_thread));
  if (reader_err)
    {
      err = reader_err;
      log_error ("read error in reader thread: %s\n", gpg_strerror (err));
      goto leave;
    }

  /* Wait for the writer to catch a writer error.  */
  ret = npth_join (writer_thread, NULL);
  if (ret)
    {
      err = gpg_error_from_errno (ret);
      log_error ("waiting for writer thread failed: %s\n", gpg_strerror (err));
      goto leave;
    }
  memset (&writer_thread, '\0', sizeof (writer_thread));
  if (writer_err)
    {
      err = writer_err;
      log_error ("write error in writer thread: %s\n", gpg_strerror (err));
      goto leave;
    }

  /* Return the data.  */
  *r_plain = get_membuf (&reader_mb, r_plainlen);
  if (!*r_plain)
    {
      err = gpg_error_from_syserror ();
      log_error ("error while storing the data in the reader thread: %s\n",
                 gpg_strerror (err));
      goto leave;
    }

 leave:
  if (reader_thread)
    npth_detach (reader_thread);
  if (writer_thread)
    npth_detach (writer_thread);
  if (outbound_fds[0] != -1)
    close (outbound_fds[0]);
  if (outbound_fds[1] != -1)
    close (outbound_fds[1]);
  if (inbound_fds[0] != -1)
    close (inbound_fds[0]);
  if (inbound_fds[1] != -1)
    close (inbound_fds[1]);
  release_gpg (ctx);
  xfree (get_membuf (&reader_mb, NULL));
  return err;
}
Пример #13
0
/* Fire up a new GPG.  Handle the server's initial greeting.  Returns
   0 on success and stores the assuan context at R_CTX.  */
static gpg_error_t
start_gpg (ctrl_t ctrl, int input_fd, int output_fd, assuan_context_t *r_ctx)
{
  gpg_error_t err;
  assuan_context_t ctx = NULL;
  const char *pgmname;
  const char *argv[10];
  int no_close_list[5];
  int i;
  char line[ASSUAN_LINELENGTH];

  (void)ctrl;

  *r_ctx = NULL;

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

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

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

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

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

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

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

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

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

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

  *r_ctx = ctx;

  if (DBG_ASSUAN)
    log_debug ("connection to GPG established\n");
  return 0;
}
Пример #14
0
/* Call GPG to encrypt a block of data.


 */
gpg_error_t
gpg_encrypt_blob (ctrl_t ctrl, const void *plain, size_t plainlen,
                  strlist_t keys, void **r_ciph, size_t *r_ciphlen)
{
  gpg_error_t err;
  assuan_context_t ctx = NULL;
  int outbound_fds[2] = { -1, -1 };
  int inbound_fds[2]  = { -1, -1 };
  npth_t writer_thread = (npth_t)0;
  npth_t reader_thread = (npth_t)0;
  gpg_error_t writer_err, reader_err;
  membuf_t reader_mb;
  char line[ASSUAN_LINELENGTH];
  strlist_t sl;
  int ret;

  *r_ciph = NULL;
  *r_ciphlen = 0;

  /* Init the memory buffer to receive the encrypted stuff.  */
  init_membuf (&reader_mb, 4096);

  /* Create two pipes.  */
  err = gnupg_create_outbound_pipe (outbound_fds);
  if (!err)
    err = gnupg_create_inbound_pipe (inbound_fds);
  if (err)
    {
      log_error (_("error creating a pipe: %s\n"), gpg_strerror (err));
      goto leave;
    }

  /* Start GPG and send the INPUT and OUTPUT commands.  */
  err = start_gpg (ctrl, outbound_fds[0], inbound_fds[1], &ctx);
  if (err)
    goto leave;
  close (outbound_fds[0]); outbound_fds[0] = -1;
  close (inbound_fds[1]); inbound_fds[1] = -1;

  /* Start a writer thread to feed the INPUT command of the server.  */
  err = start_writer (outbound_fds[1], plain, plainlen,
                      &writer_thread, &writer_err);
  if (err)
    return err;
  outbound_fds[1] = -1;  /* The thread owns the FD now.  */

  /* Start a reader thread to eat from the OUTPUT command of the
     server.  */
  err = start_reader (inbound_fds[0], &reader_mb,
                      &reader_thread, &reader_err);
  if (err)
    return err;
  outbound_fds[0] = -1;  /* The thread owns the FD now.  */

  /* Run the encryption.  */
  for (sl = keys; sl; sl = sl->next)
    {
      snprintf (line, sizeof line, "RECIPIENT -- %s", sl->d);
      err = assuan_transact (ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
      if (err)
        {
          log_error ("the engine's RECIPIENT command failed: %s <%s>\n",
                 gpg_strerror (err), gpg_strsource (err));
          goto leave;
        }
    }

  err = assuan_transact (ctx, "ENCRYPT", NULL, NULL, NULL, NULL, NULL, NULL);
  if (err)
    {
      log_error ("the engine's ENCRYPT command failed: %s <%s>\n",
                 gpg_strerror (err), gpg_strsource (err));
      goto leave;
    }

  /* Wait for reader and return the data.  */
  ret = npth_join (reader_thread, NULL);
  if (ret)
    {
      err = gpg_error_from_errno (ret);
      log_error ("waiting for reader thread failed: %s\n", gpg_strerror (err));
      goto leave;
    }
  /* FIXME: Not really valid, as npth_t is an opaque type.  */
  memset (&reader_thread, '\0', sizeof (reader_thread));
  if (reader_err)
    {
      err = reader_err;
      log_error ("read error in reader thread: %s\n", gpg_strerror (err));
      goto leave;
    }

  /* Wait for the writer to catch  a writer error.  */
  ret = npth_join (writer_thread, NULL);
  if (ret)
    {
      err = gpg_error_from_errno (ret);
      log_error ("waiting for writer thread failed: %s\n", gpg_strerror (err));
      goto leave;
    }
  memset (&writer_thread, '\0', sizeof (writer_thread));
  if (writer_err)
    {
      err = writer_err;
      log_error ("write error in writer thread: %s\n", gpg_strerror (err));
      goto leave;
    }

  /* Return the data.  */
  *r_ciph = get_membuf (&reader_mb, r_ciphlen);
  if (!*r_ciph)
    {
      err = gpg_error_from_syserror ();
      log_error ("error while storing the data in the reader thread: %s\n",
                 gpg_strerror (err));
      goto leave;
    }

 leave:
  /* FIXME: Not valid, as npth_t is an opaque type.  */
  if (reader_thread)
    npth_detach (reader_thread);
  if (writer_thread)
    npth_detach (writer_thread);
  if (outbound_fds[0] != -1)
    close (outbound_fds[0]);
  if (outbound_fds[1] != -1)
    close (outbound_fds[1]);
  if (inbound_fds[0] != -1)
    close (inbound_fds[0]);
  if (inbound_fds[1] != -1)
    close (inbound_fds[1]);
  release_gpg (ctx);
  xfree (get_membuf (&reader_mb, NULL));
  return err;
}
Пример #15
0
int
exec_write(struct exec_info **info,const char *program,
           const char *args_in,const char *name,int writeonly,int binary)
{
  int ret=G10ERR_GENERAL;

  if(opt.exec_disable && !opt.no_perm_warn)
    {
      log_info(_("external program calls are disabled due to unsafe "
		 "options file permissions\n"));

      return ret;
    }

#if defined(HAVE_GETUID) && defined(HAVE_GETEUID)
  /* There should be no way to get to this spot while still carrying
     setuid privs.  Just in case, bomb out if we are. */
  if ( getuid () != geteuid ())
    BUG ();
#endif

  if(program==NULL && args_in==NULL)
    BUG();

  *info=xmalloc_clear(sizeof(struct exec_info));

  if(name)
    (*info)->name=xstrdup(name);
  (*info)->flags.binary=binary;
  (*info)->flags.writeonly=writeonly;

  /* Expand the args, if any */
  if(args_in && expand_args(*info,args_in))
    goto fail;

#ifdef EXEC_TEMPFILE_ONLY
  if(!(*info)->flags.use_temp_files)
    {
      log_error(_("this platform requires temporary files when calling"
		  " external programs\n"));
      goto fail;
    }

#else /* !EXEC_TEMPFILE_ONLY */

  /* If there are no args, or there are args, but no temp files, we
     can use fork/exec/pipe */
  if(args_in==NULL || (*info)->flags.use_temp_files==0)
    {
      int to[2],from[2];

      if(pipe(to)==-1)
	goto fail;

      if(pipe(from)==-1)
	{
	  close(to[0]);
	  close(to[1]);
	  goto fail;
	}

      if(((*info)->child=fork())==-1)
	{
	  close(to[0]);
	  close(to[1]);
	  close(from[0]);
	  close(from[1]);
	  goto fail;
	}

      if((*info)->child==0)
	{
	  char *shell=getenv("SHELL");

	  if(shell==NULL)
	    shell="/bin/sh";

	  /* I'm the child */

	  /* If the program isn't going to respond back, they get to
             keep their stdout/stderr */
	  if(!(*info)->flags.writeonly)
	    {
	      /* implied close of STDERR */
	      if(dup2(STDOUT_FILENO,STDERR_FILENO)==-1)
		_exit(1);

	      /* implied close of STDOUT */
	      close(from[0]);
	      if(dup2(from[1],STDOUT_FILENO)==-1)
		_exit(1);
	    }

	  /* implied close of STDIN */
	  close(to[1]);
	  if(dup2(to[0],STDIN_FILENO)==-1)
	    _exit(1);

	  if(args_in==NULL)
	    {
	      if(DBG_EXTPROG)
		log_debug("execlp: %s\n",program);

	      execlp(program,program,(void *)NULL);
	    }
	  else
	    {
	      if(DBG_EXTPROG)
		log_debug("execlp: %s -c %s\n",shell,(*info)->command);

	      execlp(shell,shell,"-c",(*info)->command,(void *)NULL);
	    }

	  /* If we get this far the exec failed.  Clean up and return. */

	  if(args_in==NULL)
	    log_error(_("unable to execute program `%s': %s\n"),
		      program,strerror(errno));
	  else
	    log_error(_("unable to execute shell `%s': %s\n"),
		      shell,strerror(errno));

	  /* This mimics the POSIX sh behavior - 127 means "not found"
             from the shell. */
	  if(errno==ENOENT)
	    _exit(127);

	  _exit(1);
	}

      /* I'm the parent */

      close(to[0]);

      (*info)->tochild=fdopen(to[1],binary?"wb":"w");
      if((*info)->tochild==NULL)
	{
          ret = gpg_error_from_syserror ();
	  close(to[1]);
	  goto fail;
	}

      close(from[1]);

      (*info)->fromchild=iobuf_fdopen(from[0],"r");
      if((*info)->fromchild==NULL)
	{
          ret = gpg_error_from_syserror ();
	  close(from[0]);
	  goto fail;
	}

      /* fd iobufs are cached?! */
      iobuf_ioctl((*info)->fromchild,3,1,NULL);

      return 0;
    }
#endif /* !EXEC_TEMPFILE_ONLY */

  if(DBG_EXTPROG)
    log_debug("using temp file `%s'\n",(*info)->tempfile_in);

  /* It's not fork/exec/pipe, so create a temp file */
  if( is_secured_filename ((*info)->tempfile_in) )
    {
      (*info)->tochild = NULL;
      errno = EPERM;
    }
  else
    (*info)->tochild=fopen((*info)->tempfile_in,binary?"wb":"w");
  if((*info)->tochild==NULL)
    {
      ret = gpg_error_from_syserror ();
      log_error(_("can't create `%s': %s\n"),
		(*info)->tempfile_in,strerror(errno));
      goto fail;
    }

  ret=0;

 fail:
  if (ret)
    {
      xfree (*info);
      *info = NULL;
    }
  return ret;
}
int
_keybox_dump_file (const char *filename, int stats_only, FILE *outfp)
{
  FILE *fp;
  KEYBOXBLOB blob;
  int rc;
  unsigned long count = 0;
  struct file_stats_s stats;

  memset (&stats, 0, sizeof stats);

  if (!(fp = open_file (&filename, outfp)))
    return gpg_error_from_syserror ();

  while ( !(rc = _keybox_read_blob (&blob, fp)) )
    {
      if (stats_only)
        {
          update_stats (blob, &stats);
        }
      else
        {
          fprintf (outfp, "BEGIN-RECORD: %lu\n", count );
          _keybox_dump_blob (blob, outfp);
          fprintf (outfp, "END-RECORD\n");
        }
      _keybox_release_blob (blob);
      count++;
    }
  if (rc == -1)
    rc = 0;
  if (rc)
    fprintf (outfp, "error reading `%s': %s\n", filename, gpg_strerror (rc));

  if (fp != stdin)
    fclose (fp);

  if (stats_only)
    {
      fprintf (outfp,
               "Total number of blobs: %8lu\n"
               "               header: %8lu\n"
               "                empty: %8lu\n"
               "              openpgp: %8lu\n"
               "                 x509: %8lu\n"
               "          non flagged: %8lu\n"
               "       secret flagged: %8lu\n"
               "    ephemeral flagged: %8lu\n",
               stats.total_blob_count,
               stats.header_blob_count,
               stats.empty_blob_count,
               stats.pgp_blob_count,
               stats.x509_blob_count,
               stats.non_flagged,
               stats.secret_flagged,
               stats.ephemeral_flagged);
        if (stats.unknown_blob_count)
          fprintf (outfp, "   unknown blob types: %8lu\n",
                   stats.unknown_blob_count);
        if (stats.too_short_blobs)
          fprintf (outfp, "      too short blobs: %8lu\n",
                   stats.too_short_blobs);
        if (stats.too_large_blobs)
          fprintf (outfp, "      too large blobs: %8lu\n",
                   stats.too_large_blobs);
    }

  return rc;
}
Пример #17
0
int
exec_read(struct exec_info *info)
{
  int ret=G10ERR_GENERAL;

  fclose(info->tochild);
  info->tochild=NULL;

  if(info->flags.use_temp_files)
    {
      if(DBG_EXTPROG)
	log_debug("system() command is %s\n",info->command);

#if defined (_WIN32)
      info->progreturn=w32_system(info->command);
#else
      info->progreturn=system(info->command);
#endif

      if(info->progreturn==-1)
	{
	  log_error(_("system error while calling external program: %s\n"),
		    strerror(errno));
	  info->progreturn=127;
	  goto fail;
	}

#if defined(WIFEXITED) && defined(WEXITSTATUS)
      if(WIFEXITED(info->progreturn))
	info->progreturn=WEXITSTATUS(info->progreturn);
      else
	{
	  log_error(_("unnatural exit of external program\n"));
	  info->progreturn=127;
	  goto fail;
	}
#else
      /* If we don't have the macros, do the best we can. */
      info->progreturn = (info->progreturn & 0xff00) >> 8;
#endif

      /* 127 is the magic value returned from system() to indicate
         that the shell could not be executed, or from /bin/sh to
         indicate that the program could not be executed. */

      if(info->progreturn==127)
	{
	  log_error(_("unable to execute external program\n"));
	  goto fail;
	}

      if(!info->flags.writeonly)
	{
	  info->fromchild=iobuf_open(info->tempfile_out);
          if (info->fromchild
              && is_secured_file (iobuf_get_fd (info->fromchild)))
            {
              iobuf_close (info->fromchild);
              info->fromchild = NULL;
              errno = EPERM;
            }
	  if(info->fromchild==NULL)
	    {
              ret = gpg_error_from_syserror ();
	      log_error(_("unable to read external program response: %s\n"),
			strerror(errno));
	      goto fail;
	    }

	  /* Do not cache this iobuf on close */
	  iobuf_ioctl(info->fromchild,3,1,NULL);
	}
    }

  ret=0;

 fail:
  return ret;
}
int
_keybox_dump_find_dups (const char *filename, int print_them, FILE *outfp)
{
  FILE *fp;
  KEYBOXBLOB blob;
  int rc;
  unsigned long recno = 0;
  unsigned char zerodigest[20];
  struct dupitem_s *dupitems;
  size_t dupitems_size, dupitems_count, lastn, n;
  char fprbuf[3*20+1];

  (void)print_them;

  memset (zerodigest, 0, sizeof zerodigest);

  if (!(fp = open_file (&filename, outfp)))
    return gpg_error_from_syserror ();

  dupitems_size = 1000;
  dupitems = malloc (dupitems_size * sizeof *dupitems);
  if (!dupitems)
    {
      gpg_error_t tmperr = gpg_error_from_syserror ();
      fprintf (outfp, "error allocating array for `%s': %s\n",
               filename, strerror(errno));
      return tmperr;
    }
  dupitems_count = 0;

  while ( !(rc = _keybox_read_blob (&blob, fp)) )
    {
      unsigned char digest[20];

      if (hash_blob_rawdata (blob, digest))
        fprintf (outfp, "error in blob %ld of `%s'\n", recno, filename);
      else if (memcmp (digest, zerodigest, 20))
        {
          if (dupitems_count >= dupitems_size)
            {
              struct dupitem_s *tmp;

              dupitems_size += 1000;
              tmp = realloc (dupitems, dupitems_size * sizeof *dupitems);
              if (!tmp)
                {
                  gpg_error_t tmperr = gpg_error_from_syserror ();
                  fprintf (outfp, "error reallocating array for `%s': %s\n",
                           filename, strerror(errno));
                  free (dupitems);
                  return tmperr;
                }
              dupitems = tmp;
            }
          dupitems[dupitems_count].recno = recno;
          memcpy (dupitems[dupitems_count].digest, digest, 20);
          dupitems_count++;
        }
      _keybox_release_blob (blob);
      recno++;
    }
  if (rc == -1)
    rc = 0;
  if (rc)
    fprintf (outfp, "error reading `%s': %s\n", filename, gpg_strerror (rc));
  if (fp != stdin)
    fclose (fp);

  qsort (dupitems, dupitems_count, sizeof *dupitems, cmp_dupitems);

  for (lastn=0, n=1; n < dupitems_count; lastn=n, n++)
    {
      if (!memcmp (dupitems[lastn].digest, dupitems[n].digest, 20))
        {
          bin2hexcolon (dupitems[lastn].digest, 20, fprbuf);
          fprintf (outfp, "fpr=%s recno=%lu", fprbuf, dupitems[lastn].recno);
          do
            fprintf (outfp, " %lu", dupitems[n].recno);
          while (++n < dupitems_count
                 && !memcmp (dupitems[lastn].digest, dupitems[n].digest, 20));
          putc ('\n', outfp);
          n--;
        }
    }

  free (dupitems);

  return rc;
}
Пример #19
0
/* Encrypt a session key using DEK and store a pointer to the result
 * at R_ENCKEY and its length at R_ENCKEYLEN.
 *
 * R_SESKEY points to the unencrypted session key (.KEY, .KEYLEN) and
 * the algorithm that will be used to encrypt the contents of the
 * SKESK packet (.ALGO).  If R_SESKEY points to NULL, then a random
 * session key that is appropriate for DEK->ALGO is generated and
 * stored at R_SESKEY.  If AEAD_ALGO is not 0 the given AEAD algorithm
 * is used for encryption.
 */
gpg_error_t
encrypt_seskey (DEK *dek, aead_algo_t aead_algo,
                DEK **r_seskey, void **r_enckey, size_t *r_enckeylen)
{
  gpg_error_t err;
  gcry_cipher_hd_t hd = NULL;
  byte *buf = NULL;
  DEK *seskey;

  *r_enckey = NULL;
  *r_enckeylen = 0;

  if (*r_seskey)
    seskey = *r_seskey;
  else
    {
      seskey = xtrycalloc (1, sizeof(DEK));
      if (!seskey)
        {
          err = gpg_error_from_syserror ();
          goto leave;
        }
      seskey->algo = dek->algo;
      make_session_key (seskey);
      /*log_hexdump( "thekey", c->key, c->keylen );*/
    }


  if (aead_algo)
    {
      unsigned int noncelen;
      enum gcry_cipher_modes ciphermode;
      byte ad[4];

      err = openpgp_aead_algo_info (aead_algo, &ciphermode, &noncelen);
      if (err)
        goto leave;

      /* Allocate space for the nonce, the key, and the authentication
       * tag (16).  */
      buf = xtrymalloc_secure (noncelen + seskey->keylen + 16);
      if (!buf)
        {
          err = gpg_error_from_syserror ();
          goto leave;
        }

      gcry_randomize (buf, noncelen, GCRY_STRONG_RANDOM);

      err = openpgp_cipher_open (&hd, dek->algo,
                                 ciphermode, GCRY_CIPHER_SECURE);
      if (!err)
        err = gcry_cipher_setkey (hd, dek->key, dek->keylen);
      if (!err)
        err = gcry_cipher_setiv (hd, buf, noncelen);
      if (err)
        goto leave;

      ad[0] = (0xc0 | PKT_SYMKEY_ENC);
      ad[1] = 5;
      ad[2] = dek->algo;
      ad[3] = aead_algo;
      err = gcry_cipher_authenticate (hd, ad, 4);
      if (err)
        goto leave;

      memcpy (buf + noncelen, seskey->key, seskey->keylen);
      gcry_cipher_final (hd);
      err = gcry_cipher_encrypt (hd, buf + noncelen, seskey->keylen, NULL,0);
      if (err)
        goto leave;
      err = gcry_cipher_gettag (hd, buf + noncelen + seskey->keylen, 16);
      if (err)
        goto leave;
      *r_enckeylen = noncelen + seskey->keylen + 16;
      *r_enckey = buf;
      buf = NULL;
    }
  else
    {
      /* In the old version 4 SKESK the encrypted session key is
       * prefixed with a one-octet algorithm id.  */
      buf = xtrymalloc_secure (1 + seskey->keylen);
      if (!buf)
        {
          err = gpg_error_from_syserror ();
          goto leave;
        }
      buf[0] = seskey->algo;
      memcpy (buf + 1, seskey->key, seskey->keylen);

      err = openpgp_cipher_open (&hd, dek->algo, GCRY_CIPHER_MODE_CFB, 1);
      if (!err)
        err = gcry_cipher_setkey (hd, dek->key, dek->keylen);
      if (!err)
        err = gcry_cipher_setiv (hd, NULL, 0);
      if (!err)
        err = gcry_cipher_encrypt (hd, buf, seskey->keylen + 1, NULL, 0);
      if (err)
        goto leave;
      *r_enckeylen = seskey->keylen + 1;
      *r_enckey = buf;
      buf = NULL;
    }

  /* Return the session key in case we allocated it.  */
  *r_seskey = seskey;
  seskey = NULL;

 leave:
  gcry_cipher_close (hd);
  if (seskey != *r_seskey)
    xfree (seskey);
  xfree (buf);
  return err;
}
Пример #20
0
/* 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;
}
Пример #21
0
/* Build the remote part of the URL from SCHEME, HOST and an optional
 * PORT.  If NO_SRV is set no SRV record lookup will be done.  Returns
 * an allocated string at R_HOSTPORT or NULL on failure.  If
 * R_HTTPHOST is not NULL it receives a malloced string with the
 * hostname; this may be different from HOST if HOST is selected from
 * a pool.  */
static gpg_error_t
make_host_part (ctrl_t ctrl,
                const char *scheme, const char *host, unsigned short port,
                int force_reselect, int no_srv,
                char **r_hostport, unsigned int *r_httpflags, char **r_httphost)
{
  gpg_error_t err;
  const char *srvtag;
  char portstr[10];
  char *hostname;
  enum ks_protocol protocol;

  *r_hostport = NULL;

  if (!strcmp (scheme, "hkps") || !strcmp (scheme,"https"))
    {
      scheme = "https";
      srvtag = no_srv? NULL : "pgpkey-https";
      protocol = KS_PROTOCOL_HKPS;
    }
  else /* HKP or HTTP.  */
    {
      scheme = "http";
      srvtag = no_srv? NULL : "pgpkey-http";
      protocol = KS_PROTOCOL_HKP;
    }

  portstr[0] = 0;
  err = map_host (ctrl, host, srvtag, force_reselect, protocol,
                  &hostname, portstr, r_httpflags, r_httphost);
  if (err)
    return err;

  /* If map_host did not return a port (from a SRV record) but a port
   * has been specified (implicitly or explicitly) then use that port.
   * In the case that a port was not specified (which is probably a
   * bug in https.c) we will set up defaults.  */
  if (*portstr)
    ;
  else if (!*portstr && port)
    snprintf (portstr, sizeof portstr, "%hu", port);
  else if (!strcmp (scheme,"https"))
    strcpy (portstr, "443");
  else
    strcpy (portstr, "11371");

  if (*hostname != '[' && is_ip_address (hostname) == 6)
    *r_hostport = strconcat (scheme, "://[", hostname, "]:", portstr, NULL);
  else
    *r_hostport = strconcat (scheme, "://", hostname, ":", portstr, NULL);
  xfree (hostname);
  if (!*r_hostport)
    {
      if (r_httphost)
        {
          xfree (*r_httphost);
          *r_httphost = NULL;
        }
      return gpg_error_from_syserror ();
    }
  return 0;
}
Пример #22
0
/* 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;
}
Пример #23
0
/* Search the keyserver identified by URI for keys matching PATTERN.
   On success R_FP has an open stream to read the data.  If
   R_HTTP_STATUS is not NULL, the http status code will be stored
   there.  */
gpg_error_t
ks_hkp_search (ctrl_t ctrl, parsed_uri_t uri, const char *pattern,
               estream_t *r_fp, unsigned int *r_http_status)
{
  gpg_error_t err;
  KEYDB_SEARCH_DESC desc;
  char fprbuf[2+40+1];
  char *hostport = NULL;
  char *request = NULL;
  estream_t fp = NULL;
  int reselect;
  unsigned int httpflags;
  char *httphost = NULL;
  unsigned int tries = SEND_REQUEST_RETRIES;

  *r_fp = NULL;

  /* Remove search type indicator and adjust PATTERN accordingly.
     Note that HKP keyservers like the 0x to be present when searching
     by keyid.  We need to re-format the fingerprint and keyids so to
     remove the gpg specific force-use-of-this-key flag ("!").  */
  err = classify_user_id (pattern, &desc, 1);
  if (err)
    return err;
  switch (desc.mode)
    {
    case KEYDB_SEARCH_MODE_EXACT:
    case KEYDB_SEARCH_MODE_SUBSTR:
    case KEYDB_SEARCH_MODE_MAIL:
    case KEYDB_SEARCH_MODE_MAILSUB:
      pattern = desc.u.name;
      break;
    case KEYDB_SEARCH_MODE_SHORT_KID:
      snprintf (fprbuf, sizeof fprbuf, "0x%08lX", (ulong)desc.u.kid[1]);
      pattern = fprbuf;
      break;
    case KEYDB_SEARCH_MODE_LONG_KID:
      snprintf (fprbuf, sizeof fprbuf, "0x%08lX%08lX",
                (ulong)desc.u.kid[0], (ulong)desc.u.kid[1]);
      pattern = fprbuf;
      break;
    case KEYDB_SEARCH_MODE_FPR16:
      fprbuf[0] = '0';
      fprbuf[1] = 'x';
      bin2hex (desc.u.fpr, 16, fprbuf+2);
      pattern = fprbuf;
      break;
    case KEYDB_SEARCH_MODE_FPR20:
    case KEYDB_SEARCH_MODE_FPR:
      fprbuf[0] = '0';
      fprbuf[1] = 'x';
      bin2hex (desc.u.fpr, 20, fprbuf+2);
      pattern = fprbuf;
      break;
    default:
      return gpg_error (GPG_ERR_INV_USER_ID);
    }

  /* Build the request string.  */
  reselect = 0;
 again:
  {
    char *searchkey;

    xfree (hostport); hostport = NULL;
    xfree (httphost); httphost = NULL;
    err = make_host_part (ctrl, uri->scheme, uri->host, uri->port,
                          reselect, uri->explicit_port,
                          &hostport, &httpflags, &httphost);
    if (err)
      goto leave;

    searchkey = http_escape_string (pattern, EXTRA_ESCAPE_CHARS);
    if (!searchkey)
      {
        err = gpg_error_from_syserror ();
        goto leave;
      }

    xfree (request);
    request = strconcat (hostport,
                         "/pks/lookup?op=index&options=mr&search=",
                         searchkey,
                         NULL);
    xfree (searchkey);
    if (!request)
      {
        err = gpg_error_from_syserror ();
        goto leave;
      }
  }

  /* Send the request.  */
  err = send_request (ctrl, request, hostport, httphost, httpflags,
                      NULL, NULL, &fp, r_http_status);
  if (handle_send_request_error (ctrl, err, request, &tries))
    {
      reselect = 1;
      goto again;
    }
  if (err)
    goto leave;

  err = dirmngr_status (ctrl, "SOURCE", hostport, NULL);
  if (err)
    goto leave;

  /* Peek at the response.  */
  {
    int c = es_getc (fp);
    if (c == -1)
      {
        err = es_ferror (fp)?gpg_error_from_syserror ():gpg_error (GPG_ERR_EOF);
        log_error ("error reading response: %s\n", gpg_strerror (err));
        goto leave;
      }
    if (c == '<')
      {
        /* The document begins with a '<': Assume a HTML response,
           which we don't support.  */
        err = gpg_error (GPG_ERR_UNSUPPORTED_ENCODING);
        goto leave;
      }
    es_ungetc (c, fp);
  }

  /* Return the read stream.  */
  *r_fp = fp;
  fp = NULL;

 leave:
  es_fclose (fp);
  xfree (request);
  xfree (hostport);
  xfree (httphost);
  return err;
}
Пример #24
0
/* Given PATTERN, which is a string as used by GnuPG to specify a
   certificate, return all matching certificates by calling the
   supplied function RETFNC.  */
gpg_error_t
get_certs_bypattern (const char *pattern,
                     gpg_error_t (*retfnc)(void*,ksba_cert_t),
                     void *retfnc_data)
{
  gpg_error_t err = GPG_ERR_BUG;
  enum pattern_class class;
  size_t offset, sn_offset;
  const char *hexserialno;
  ksba_sexp_t serialno = NULL;
  ksba_cert_t cert = NULL;
  unsigned int seq;

  if (!pattern || !retfnc)
    return gpg_error (GPG_ERR_INV_ARG);

  class = classify_pattern (pattern, &offset, &sn_offset);
  hexserialno = pattern + sn_offset;
  pattern += offset;
  switch (class)
    {
    case PATTERN_UNKNOWN:
      err = gpg_error (GPG_ERR_INV_NAME);
      break;

    case PATTERN_FINGERPRINT20:
      cert = get_cert_byhexfpr (pattern);
      err = cert? 0 : gpg_error (GPG_ERR_NOT_FOUND);
      break;

    case PATTERN_SERIALNO_ISSUER:
      serialno = hexsn_to_sexp (hexserialno);
      if (!serialno)
        err = gpg_error_from_syserror ();
      else
        {
          cert = get_cert_bysn (pattern, serialno);
          err = cert? 0 : gpg_error (GPG_ERR_NOT_FOUND);
        }
      break;

    case PATTERN_ISSUER:
      for (seq=0,err=0; !err && (cert = get_cert_byissuer (pattern, seq)); seq++)
        {
          err = retfnc (retfnc_data, cert);
          ksba_cert_release (cert);
          cert = NULL;
        }
      if (!err && !seq)
        err = gpg_error (GPG_ERR_NOT_FOUND);
      break;

    case PATTERN_SUBJECT:
      for (seq=0,err=0; !err && (cert = get_cert_bysubject (pattern, seq));seq++)
        {
          err = retfnc (retfnc_data, cert);
          ksba_cert_release (cert);
          cert = NULL;
        }
      if (!err && !seq)
        err = gpg_error (GPG_ERR_NOT_FOUND);
      break;

    case PATTERN_EMAIL:
    case PATTERN_EMAIL_SUBSTR:
    case PATTERN_FINGERPRINT16:
    case PATTERN_SHORT_KEYID:
    case PATTERN_LONG_KEYID:
    case PATTERN_SUBSTR:
    case PATTERN_SERIALNO:
      /* Not supported.  */
      err = gpg_error (GPG_ERR_INV_NAME);
    }


  if (!err && cert)
    err = retfnc (retfnc_data, cert);
  ksba_cert_release (cert);
  xfree (serialno);
  return err;
}
Пример #25
0
/* Send the key in {DATA,DATALEN} to the keyserver identified by URI.  */
gpg_error_t
ks_hkp_put (ctrl_t ctrl, parsed_uri_t uri, const void *data, size_t datalen)
{
  gpg_error_t err;
  char *hostport = NULL;
  char *request = NULL;
  estream_t fp = NULL;
  struct put_post_parm_s parm;
  char *armored = NULL;
  int reselect;
  char *httphost = NULL;
  unsigned int httpflags;
  unsigned int tries = SEND_REQUEST_RETRIES;

  parm.datastring = NULL;

  err = armor_data (&armored, data, datalen);
  if (err)
    goto leave;

  parm.datastring = http_escape_string (armored, EXTRA_ESCAPE_CHARS);
  if (!parm.datastring)
    {
      err = gpg_error_from_syserror ();
      goto leave;
    }
  xfree (armored);
  armored = NULL;

  /* Build the request string.  */
  reselect = 0;
 again:
  xfree (hostport); hostport = NULL;
  xfree (httphost); httphost = NULL;
  err = make_host_part (ctrl, uri->scheme, uri->host, uri->port,
                        reselect, uri->explicit_port,
                        &hostport, &httpflags, &httphost);
  if (err)
    goto leave;

  xfree (request);
  request = strconcat (hostport, "/pks/add", NULL);
  if (!request)
    {
      err = gpg_error_from_syserror ();
      goto leave;
    }

  /* Send the request.  */
  err = send_request (ctrl, request, hostport, httphost, 0,
                      put_post_cb, &parm, &fp, NULL);
  if (handle_send_request_error (ctrl, err, request, &tries))
    {
      reselect = 1;
      goto again;
    }
  if (err)
    goto leave;

 leave:
  es_fclose (fp);
  xfree (parm.datastring);
  xfree (armored);
  xfree (request);
  xfree (hostport);
  xfree (httphost);
  return err;
}
Пример #26
0
/*
 * Make an output filename for the inputfile INAME.
 * Returns an IOBUF and an errorcode
 * Mode 0 = use ".gpg"
 *	1 = use ".asc"
 *	2 = use ".sig"
 *      3 = use ".rev"
 *
 * If INP_FD is not -1 the function simply creates an IOBUF for that
 * file descriptor and ignore INAME and MODE.  Note that INP_FD won't
 * be closed if the returned IOBUF is closed.  With RESTRICTEDPERM a
 * file will be created with mode 700 if possible.
 */
int
open_outfile (int inp_fd, const char *iname, int mode, int restrictedperm,
              iobuf_t *a)
{
  int rc = 0;

  *a = NULL;
  if (inp_fd != -1)
    {
      char xname[64];

      *a = iobuf_fdopen_nc (inp_fd, "wb");
      if (!*a)
        {
          rc = gpg_error_from_syserror ();
          snprintf (xname, sizeof xname, "[fd %d]", inp_fd);
          log_error (_("can't open '%s': %s\n"), xname, gpg_strerror (rc));
        }
      else if (opt.verbose)
        {
          snprintf (xname, sizeof xname, "[fd %d]", inp_fd);
          log_info (_("writing to '%s'\n"), xname);
        }
    }
  else if (iobuf_is_pipe_filename (iname) && !opt.outfile)
    {
      *a = iobuf_create (NULL, 0);
      if ( !*a )
        {
          rc = gpg_error_from_syserror ();
          log_error (_("can't open '%s': %s\n"), "[stdout]", strerror(errno) );
        }
      else if ( opt.verbose )
        log_info (_("writing to stdout\n"));
    }
  else
    {
      char *buf = NULL;
      const char *name;

      if (opt.dry_run)
        name = NAME_OF_DEV_NULL;
      else if (opt.outfile)
        name = opt.outfile;
      else
        {
#ifdef USE_ONLY_8DOT3
          if (opt.mangle_dos_filenames)
            {
              /* It is quite common for DOS systems to have only one
                 dot in a filename.  If we have something like this,
                 we simple replace the suffix except in cases where
                 the suffix is larger than 3 characters and not the
                 same as the new one.  We don't map the filenames to
                 8.3 because this is a duty of the file system.  */
              char *dot;
              const char *newsfx;

              newsfx = (mode==1 ? ".asc" :
                        mode==2 ? ".sig" :
                        mode==3 ? ".rev" : ".gpg");

              buf = xmalloc (strlen(iname)+4+1);
              strcpy (buf, iname);
              dot = strchr (buf, '.' );
              if ( dot && dot > buf && dot[1] && strlen(dot) <= 4
                   && CMP_FILENAME (newsfx, dot) )
                strcpy (dot, newsfx);
              else if (dot && !dot[1]) /* Do not duplicate a dot.  */
                strcpy (dot, newsfx+1);
              else
                strcat (buf, newsfx);
            }
          if (!buf)
#endif /* USE_ONLY_8DOT3 */
            {
              buf = xstrconcat (iname,
                                (mode==1 ? EXTSEP_S "asc" :
                                 mode==2 ? EXTSEP_S "sig" :
                                 mode==3 ? EXTSEP_S "rev" :
                                 /*     */ EXTSEP_S GPGEXT_GPG),
                                NULL);
            }
          name = buf;
        }

      rc = 0;
      while ( !overwrite_filep (name) )
        {
          char *tmp = ask_outfile_name (NULL, 0);
          if ( !tmp || !*tmp )
            {
              xfree (tmp);
              rc = gpg_error (GPG_ERR_EEXIST);
              break;
            }
          xfree (buf);
          name = buf = tmp;
        }

      if ( !rc )
        {
          if (is_secured_filename (name) )
            {
              *a = NULL;
              gpg_err_set_errno (EPERM);
            }
          else
            *a = iobuf_create (name, restrictedperm);
          if (!*a)
            {
              rc = gpg_error_from_syserror ();
              log_error(_("can't create '%s': %s\n"), name, strerror(errno) );
            }
          else if( opt.verbose )
            log_info (_("writing to '%s'\n"), name );
        }
      xfree(buf);
    }

  if (*a)
    iobuf_ioctl (*a, IOBUF_IOCTL_NO_CACHE, 1, NULL);

  return rc;
}
Пример #27
0
/* Map the host name NAME to the actual to be used host name.  This
 * allows us to manage round robin DNS names.  We use our own strategy
 * to choose one of the hosts.  For example we skip those hosts which
 * failed for some time and we stick to one host for a time
 * independent of DNS retry times.  If FORCE_RESELECT is true a new
 * host is always selected.  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;
}
Пример #28
0
/* Expands %i and %o in the args to the full temp files within the
   temp directory. */
static int
expand_args(struct exec_info *info,const char *args_in)
{
  const char *ch = args_in;
  membuf_t command;

  info->flags.use_temp_files=0;
  info->flags.keep_temp_files=0;

  if(DBG_EXTPROG)
    log_debug("expanding string \"%s\"\n",args_in);

  init_membuf (&command, 100);

  while(*ch!='\0')
    {
      if(*ch=='%')
	{
	  char *append=NULL;

	  ch++;

	  switch(*ch)
	    {
	    case 'O':
	      info->flags.keep_temp_files=1;
	      /* fall through */

	    case 'o': /* out */
	      if(!info->flags.madedir)
		{
		  if(make_tempdir(info))
		    goto fail;
		}
	      append=info->tempfile_out;
	      info->flags.use_temp_files=1;
	      break;

	    case 'I':
	      info->flags.keep_temp_files=1;
	      /* fall through */

	    case 'i': /* in */
	      if(!info->flags.madedir)
		{
		  if(make_tempdir(info))
		    goto fail;
		}
	      append=info->tempfile_in;
	      info->flags.use_temp_files=1;
	      break;

	    case '%':
	      append="%";
	      break;
	    }

	  if(append)
            put_membuf_str (&command, append);
	}
      else
        put_membuf (&command, ch, 1);

      ch++;
    }

  put_membuf (&command, "", 1);  /* Terminate string.  */

  info->command = get_membuf (&command, NULL);
  if (!info->command)
    return gpg_error_from_syserror ();

  if(DBG_EXTPROG)
    log_debug("args expanded to \"%s\", use %u, keep %u\n",info->command,
	      info->flags.use_temp_files,info->flags.keep_temp_files);

  return 0;

 fail:
  xfree (get_membuf (&command, NULL));
  return G10ERR_GENERAL;
}
Пример #29
0
/* We don't want to use use_seskey yet because older gnupg versions
   can't handle it, and there isn't really any point unless we're
   making a message that can be decrypted by a public key or
   passphrase. */
static int
encrypt_simple (const char *filename, int mode, int use_seskey)
{
  iobuf_t inp, out;
  PACKET pkt;
  PKT_plaintext *pt = NULL;
  STRING2KEY *s2k = NULL;
  byte enckey[33];
  int rc = 0;
  int seskeylen = 0;
  u32 filesize;
  cipher_filter_context_t cfx;
  armor_filter_context_t  *afx = NULL;
  compress_filter_context_t zfx;
  text_filter_context_t tfx;
  progress_filter_context_t *pfx;
  int do_compress = !!default_compress_algo();

  pfx = new_progress_context ();
  memset( &cfx, 0, sizeof cfx);
  memset( &zfx, 0, sizeof zfx);
  memset( &tfx, 0, sizeof tfx);
  init_packet(&pkt);

  /* Prepare iobufs. */
  inp = iobuf_open(filename);
  if (inp)
    iobuf_ioctl (inp, IOBUF_IOCTL_NO_CACHE, 1, NULL);
  if (inp && is_secured_file (iobuf_get_fd (inp)))
    {
      iobuf_close (inp);
      inp = NULL;
      gpg_err_set_errno (EPERM);
    }
  if (!inp)
    {
      rc = gpg_error_from_syserror ();
      log_error(_("can't open '%s': %s\n"), filename? filename: "[stdin]",
                strerror(errno) );
      release_progress_context (pfx);
      return rc;
    }

  handle_progress (pfx, inp, filename);

  if (opt.textmode)
    iobuf_push_filter( inp, text_filter, &tfx );

  cfx.dek = NULL;
  if ( mode )
    {
      int canceled;

      s2k = xmalloc_clear( sizeof *s2k );
      s2k->mode = opt.s2k_mode;
      s2k->hash_algo = S2K_DIGEST_ALGO;
      cfx.dek = passphrase_to_dek (NULL, 0,
                                   default_cipher_algo(), s2k, 4,
                                   NULL, &canceled);
      if ( !cfx.dek || !cfx.dek->keylen )
        {
          rc = gpg_error (canceled? GPG_ERR_CANCELED:GPG_ERR_INV_PASSPHRASE);
          xfree (cfx.dek);
          xfree (s2k);
          iobuf_close (inp);
          log_error (_("error creating passphrase: %s\n"), gpg_strerror (rc));
          release_progress_context (pfx);
          return rc;
        }
      if (use_seskey && s2k->mode != 1 && s2k->mode != 3)
        {
          use_seskey = 0;
          log_info (_("can't use a symmetric ESK packet "
                      "due to the S2K mode\n"));
        }

      if ( use_seskey )
        {
          DEK *dek = NULL;

          seskeylen = openpgp_cipher_get_algo_keylen (default_cipher_algo ());
          encrypt_seskey( cfx.dek, &dek, enckey );
          xfree( cfx.dek ); cfx.dek = dek;
        }

      if (opt.verbose)
        log_info(_("using cipher %s\n"),
                 openpgp_cipher_algo_name (cfx.dek->algo));

      cfx.dek->use_mdc=use_mdc(NULL,cfx.dek->algo);
    }

  if (do_compress && cfx.dek && cfx.dek->use_mdc
      && is_file_compressed(filename, &rc))
    {
      if (opt.verbose)
        log_info(_("'%s' already compressed\n"), filename);
      do_compress = 0;
    }

  if ( rc || (rc = open_outfile (-1, filename, opt.armor? 1:0, 0, &out )))
    {
      iobuf_cancel (inp);
      xfree (cfx.dek);
      xfree (s2k);
      release_progress_context (pfx);
      return rc;
    }

  if ( opt.armor )
    {
      afx = new_armor_context ();
      push_armor_filter (afx, out);
    }

  if ( s2k )
    {
      PKT_symkey_enc *enc = xmalloc_clear( sizeof *enc + seskeylen + 1 );
      enc->version = 4;
      enc->cipher_algo = cfx.dek->algo;
      enc->s2k = *s2k;
      if ( use_seskey && seskeylen )
        {
          enc->seskeylen = seskeylen + 1; /* algo id */
          memcpy (enc->seskey, enckey, seskeylen + 1 );
        }
      pkt.pkttype = PKT_SYMKEY_ENC;
      pkt.pkt.symkey_enc = enc;
      if ((rc = build_packet( out, &pkt )))
        log_error("build symkey packet failed: %s\n", gpg_strerror (rc) );
      xfree (enc);
    }

  if (!opt.no_literal)
    pt = setup_plaintext_name (filename, inp);

  /* Note that PGP 5 has problems decrypting symmetrically encrypted
     data if the file length is in the inner packet. It works when
     only partial length headers are use.  In the past, we always used
     partial body length here, but since PGP 2, PGP 6, and PGP 7 need
     the file length, and nobody should be using PGP 5 nowadays
     anyway, this is now set to the file length.  Note also that this
     only applies to the RFC-1991 style symmetric messages, and not
     the RFC-2440 style.  PGP 6 and 7 work with either partial length
     or fixed length with the new style messages. */

  if ( !iobuf_is_pipe_filename (filename) && *filename && !opt.textmode )
    {
      off_t tmpsize;
      int overflow;

      if ( !(tmpsize = iobuf_get_filelength(inp, &overflow))
           && !overflow && opt.verbose)
        log_info(_("WARNING: '%s' is an empty file\n"), filename );
      /* We can't encode the length of very large files because
         OpenPGP uses only 32 bit for file sizes.  So if the the
         size of a file is larger than 2^32 minus some bytes for
         packet headers, we switch to partial length encoding. */
      if ( tmpsize < (IOBUF_FILELENGTH_LIMIT - 65536) )
        filesize = tmpsize;
      else
        filesize = 0;
    }
  else
    filesize = opt.set_filesize ? opt.set_filesize : 0; /* stdin */

  if (!opt.no_literal)
    {
      pt->timestamp = make_timestamp();
      pt->mode = opt.textmode? 't' : 'b';
      pt->len = filesize;
      pt->new_ctb = !pt->len;
      pt->buf = inp;
      pkt.pkttype = PKT_PLAINTEXT;
      pkt.pkt.plaintext = pt;
      cfx.datalen = filesize && !do_compress ? calc_packet_length( &pkt ) : 0;
    }
  else
    {
      cfx.datalen = filesize && !do_compress ? filesize : 0;
      pkt.pkttype = 0;
      pkt.pkt.generic = NULL;
    }

  /* Register the cipher filter. */
  if (mode)
    iobuf_push_filter ( out, cipher_filter, &cfx );

  /* Register the compress filter. */
  if ( do_compress )
    {
      if (cfx.dek && cfx.dek->use_mdc)
        zfx.new_ctb = 1;
      push_compress_filter (out, &zfx, default_compress_algo());
    }

  /* Do the work. */
  if (!opt.no_literal)
    {
      if ( (rc = build_packet( out, &pkt )) )
        log_error("build_packet failed: %s\n", gpg_strerror (rc) );
    }
  else
    {
      /* User requested not to create a literal packet, so we copy the
         plain data.  */
    byte copy_buffer[4096];
    int  bytes_copied;
    while ((bytes_copied = iobuf_read(inp, copy_buffer, 4096)) != -1)
      if ( (rc=iobuf_write(out, copy_buffer, bytes_copied)) ) {
        log_error ("copying input to output failed: %s\n",
                   gpg_strerror (rc) );
        break;
      }
    wipememory (copy_buffer, 4096); /* burn buffer */
    }

  /* Finish the stuff.  */
  iobuf_close (inp);
  if (rc)
    iobuf_cancel(out);
  else
    {
      iobuf_close (out); /* fixme: check returncode */
      if (mode)
        write_status ( STATUS_END_ENCRYPTION );
    }
  if (pt)
    pt->buf = NULL;
  free_packet (&pkt);
  xfree (cfx.dek);
  xfree (s2k);
  release_armor_context (afx);
  release_progress_context (pfx);
  return rc;
}
/****************
 * Make an output filename for the inputfile INAME.
 * Returns an IOBUF and an errorcode
 * Mode 0 = use ".gpg"
 *	1 = use ".asc"
 *	2 = use ".sig"
 */
int
open_outfile( const char *iname, int mode, IOBUF *a )
{
  int rc = 0;

  *a = NULL;
  if( iobuf_is_pipe_filename (iname) && !opt.outfile ) {
    *a = iobuf_create(NULL);
    if( !*a ) {
      rc = gpg_error_from_syserror ();
      log_error(_("can't open `%s': %s\n"), "[stdout]", strerror(errno) );
    }
    else if( opt.verbose )
      log_info(_("writing to stdout\n"));
  }
  else {
    char *buf = NULL;
    const char *name;

    if ( opt.dry_run )
      {
#ifdef HAVE_W32_SYSTEM
        name = "nul";
#else
        name = "/dev/null";
#endif
      }
    else if( opt.outfile )
      name = opt.outfile;
    else {
#ifdef USE_ONLY_8DOT3
      if (opt.mangle_dos_filenames)
        {
          /* It is quite common DOS system to have only one dot in a
           * a filename So if we have something like this, we simple
           * replace the suffix execpt in cases where the suffix is
           * larger than 3 characters and not the same as.
           * We should really map the filenames to 8.3 but this tends to
           * be more complicated and is probaly a duty of the filesystem
           */
          char *dot;
          const char *newsfx = mode==1 ? ".asc" :
                               mode==2 ? ".sig" : ".gpg";

          buf = xmalloc(strlen(iname)+4+1);
          strcpy(buf,iname);
          dot = strchr(buf, '.' );
          if ( dot && dot > buf && dot[1] && strlen(dot) <= 4
				  && CMP_FILENAME(newsfx, dot) )
            {
              strcpy(dot, newsfx );
            }
          else if ( dot && !dot[1] ) /* don't duplicate a dot */
            strcpy( dot, newsfx+1 );
          else
            strcat ( buf, newsfx );
        }
      if (!buf)
#endif /* USE_ONLY_8DOT3 */
        {
          buf = xmalloc(strlen(iname)+4+1);
          strcpy(stpcpy(buf,iname), mode==1 ? EXTSEP_S "asc" :
		                   mode==2 ? EXTSEP_S "sig" : EXTSEP_S "gpg");
        }
      name = buf;
    }

    rc = 0;
    while( !overwrite_filep (name) )
      {
        char *tmp = ask_outfile_name (NULL, 0);
        if ( !tmp || !*tmp )
          {
            xfree (tmp);
            rc = gpg_error (GPG_ERR_EEXIST);
            break;
          }
        xfree (buf);
        name = buf = tmp;
      }

    if( !rc )
      {
        if (is_secured_filename (name) )
          {
            *a = NULL;
            errno = EPERM;
          }
        else
          *a = iobuf_create( name );
        if( !*a )
          {
            rc = gpg_error_from_syserror ();
            log_error(_("can't create `%s': %s\n"), name, strerror(errno) );
          }
        else if( opt.verbose )
          log_info(_("writing to `%s'\n"), name );
      }
    xfree(buf);
  }

  if (*a)
    iobuf_ioctl (*a,3,1,NULL); /* disable fd caching */

  return rc;
}