示例#1
0
/* Pass COMMAND to dirmngr and print all output generated by Dirmngr
   to stdout.  A couple of inquiries are defined (see above).  ARGC
   arguments in ARGV are given to the Dirmngr.  Spaces, plus and
   percent characters within the argument strings are percent escaped
   so that blanks can act as delimiters. */
int
gpgsm_dirmngr_run_command (ctrl_t ctrl, const char *command,
                           int argc, char **argv)
{ 
  int rc;
  int i;
  const char *s;
  char *line, *p;
  size_t len;
  struct run_command_parm_s parm;

  rc = start_dirmngr (ctrl);
  if (rc)
    return rc;

  parm.ctx = dirmngr_ctx;

  len = strlen (command) + 1;
  for (i=0; i < argc; i++)
    len += 1 + 3*strlen (argv[i]); /* enough space for percent escaping */
  line = xtrymalloc (len);
  if (!line)
    {
      release_dirmngr (ctrl);
      return out_of_core ();
    }

  p = stpcpy (line, command);
  for (i=0; i < argc; i++)
    {
      *p++ = ' ';
      for (s=argv[i]; *s; s++)
        {
          if (!isascii (*s))
            *p++ = *s;
          else if (*s == ' ')
            *p++ = '+';
          else if (!isprint (*s) || *s == '+')
            {
              sprintf (p, "%%%02X", *(const unsigned char *)s);
              p += 3;
            }
          else
            *p++ = *s;
        }
    }
  *p = 0;

  rc = assuan_transact (dirmngr_ctx, line,
                        run_command_cb, NULL,
                        run_command_inq_cb, &parm,
                        run_command_status_cb, ctrl);
  xfree (line);
  log_info ("response of dirmngr: %s\n", rc? gpg_strerror (rc): "okay");
  release_dirmngr (ctrl);
  return rc;
}
示例#2
0
/* Try to connect to the dirmngr via socket or fork it off and work by
   pipes.  Handle the server's initial greeting */
static assuan_context_t
start_dirmngr (int only_daemon)
{
  int rc;
  char *infostr, *p;
  assuan_context_t ctx;
  int try_default = 0;

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

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

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

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

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

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

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

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

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

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

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

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

  return ctx;
}
示例#3
0
int
main (int argc, char **argv )
{
  ARGPARSE_ARGS pargs;
  assuan_context_t ctx;
  gpg_error_t err;
  unsigned char *certbuf;
  size_t certbuflen = 0;
  int cmd_ping = 0;
  int cmd_cache_cert = 0;
  int cmd_validate = 0;
  int cmd_lookup = 0;
  int cmd_loadcrl = 0;
  int cmd_squid_mode = 0;

  set_strusage (my_strusage);
  log_set_prefix ("dirmngr-client",
                  JNLIB_LOG_WITH_PREFIX);

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

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

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

  /* Setup I18N. */
  i18n_init();

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

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

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

  /* Build the helptable for radix64 to bin conversion. */
  if (opt.pem)
    {
      int i;
      unsigned char *s;

      for (i=0; i < 256; i++ )
        asctobin[i] = 255; /* Used to detect invalid characters. */
      for (s=bintoasc, i=0; *s; s++, i++)
        asctobin[*s] = i;
    }


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

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

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

  ctx = start_dirmngr (1);
  if (!ctx)
    exit (2);

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

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

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

  assuan_release (ctx);

  if (cmd_ping)
    {
      if (!opt.quiet)
        log_info (_("a dirmngr daemon is up and running\n"));
      return 0;
    }
  else if (cmd_lookup|| cmd_loadcrl || cmd_squid_mode)
    return err? 1:0;
  else if (cmd_cache_cert)
    {
      if (err && gpg_err_code (err) == GPG_ERR_DUP_VALUE )
        {
          if (!opt.quiet)
            log_info (_("certificate already cached\n"));
        }
      else if (err)
        {
          log_error (_("error caching certificate: %s\n"),
                     gpg_strerror (err));
          return 1;
        }
      return 0;
    }
  else if (cmd_validate && err)
    {
      log_error (_("validation of certificate failed: %s\n"),
                 gpg_strerror (err));
      return 1;
    }
  else if (!err)
    {
      if (!opt.quiet)
        log_info (_("certificate is valid\n"));
      return 0;
    }
  else if (gpg_err_code (err) == GPG_ERR_CERT_REVOKED )
    {
      if (!opt.quiet)
        log_info (_("certificate has been revoked\n"));
      return 1;
    }
  else
    {
      log_error (_("certificate check failed: %s\n"), gpg_strerror (err));
      return 2;
    }
}
示例#4
0
/* Run the Directory Manager's lookup command using the pattern
   compiled from the strings given in NAMES.  The caller must provide
   the callback CB which will be passed cert by cert.  Note that CTRL
   is optional.  With CACHE_ONLY the dirmngr will search only its own
   key cache. */
int 
gpgsm_dirmngr_lookup (ctrl_t ctrl, strlist_t names, int cache_only,
                      void (*cb)(void*, ksba_cert_t), void *cb_value)
{ 
  int rc;
  char *pattern;
  char line[ASSUAN_LINELENGTH];
  struct lookup_parm_s parm;
  size_t len;
  assuan_context_t ctx;

  /* The lookup function can be invoked from the callback of a lookup
     function, for example to walk the chain.  */
  if (!dirmngr_ctx_locked)
    {
      rc = start_dirmngr (ctrl);
      if (rc)
	return rc;
      ctx = dirmngr_ctx;
    }
  else if (!dirmngr2_ctx_locked)
    {
      rc = start_dirmngr2 (ctrl);
      if (rc)
	return rc;
      ctx = dirmngr2_ctx;
    }
  else
    {
      log_fatal ("both dirmngr contexts are in use\n");
    }

  pattern = pattern_from_strlist (names);
  if (!pattern)
    {
      if (ctx == dirmngr_ctx)
	release_dirmngr (ctrl);
      else
	release_dirmngr2 (ctrl);

      return out_of_core ();
    }
  snprintf (line, DIM(line)-1, "LOOKUP%s %s", 
            cache_only? " --cache-only":"", pattern);
  line[DIM(line)-1] = 0;
  xfree (pattern);

  parm.ctrl = ctrl;
  parm.ctx = ctx;
  parm.cb = cb;
  parm.cb_value = cb_value;
  parm.error = 0;
  init_membuf (&parm.data, 4096);

  rc = assuan_transact (ctx, line, lookup_cb, &parm,
                        NULL, NULL, lookup_status_cb, &parm);
  xfree (get_membuf (&parm.data, &len));

  if (ctx == dirmngr_ctx)
    release_dirmngr (ctrl);
  else
    release_dirmngr2 (ctrl);

  if (rc)
      return rc;
  return parm.error;
}
示例#5
0
/* Call the directory manager to check whether the certificate is valid
   Returns 0 for valid or usually one of the errors:

  GPG_ERR_CERTIFICATE_REVOKED
  GPG_ERR_NO_CRL_KNOWN
  GPG_ERR_CRL_TOO_OLD

  Values for USE_OCSP:
     0 = Do CRL check.
     1 = Do an OCSP check.
     2 = Do an OCSP check using only the default responder.
 */
int
gpgsm_dirmngr_isvalid (ctrl_t ctrl,
                       ksba_cert_t cert, ksba_cert_t issuer_cert, int use_ocsp)
{
  static int did_options;
  int rc;
  char *certid;
  char line[ASSUAN_LINELENGTH];
  struct inq_certificate_parm_s parm;
  struct isvalid_status_parm_s stparm;

  rc = start_dirmngr (ctrl);
  if (rc)
    return rc;

  if (use_ocsp)
    {
      certid = gpgsm_get_fingerprint_hexstring (cert, GCRY_MD_SHA1);
    }
  else
    {
      certid = gpgsm_get_certid (cert);
      if (!certid)
        {
          log_error ("error getting the certificate ID\n");
	  release_dirmngr (ctrl);
          return gpg_error (GPG_ERR_GENERAL);
        }
    }

  if (opt.verbose > 1)
    {
      char *fpr = gpgsm_get_fingerprint_string (cert, GCRY_MD_SHA1);
      log_info ("asking dirmngr about %s%s\n", fpr,
                use_ocsp? " (using OCSP)":"");
      xfree (fpr);
    }

  parm.ctx = dirmngr_ctx;
  parm.ctrl = ctrl;
  parm.cert = cert;
  parm.issuer_cert = issuer_cert;

  stparm.ctrl = ctrl;
  stparm.seen = 0;
  memset (stparm.fpr, 0, 20);

  /* FIXME: If --disable-crl-checks has been set, we should pass an
     option to dirmngr, so that no fallback CRL check is done after an
     ocsp check.  It is not a problem right now as dirmngr does not
     fallback to CRL checking.  */

  /* It is sufficient to send the options only once because we have
     one connection per process only. */
  if (!did_options)
    {
      if (opt.force_crl_refresh)
        assuan_transact (dirmngr_ctx, "OPTION force-crl-refresh=1",
                         NULL, NULL, NULL, NULL, NULL, NULL);
      did_options = 1;
    }
  snprintf (line, DIM(line)-1, "ISVALID%s %s", 
            use_ocsp == 2? " --only-ocsp --force-default-responder":"",
            certid);
  line[DIM(line)-1] = 0;
  xfree (certid);

  rc = assuan_transact (dirmngr_ctx, line, NULL, NULL,
                        inq_certificate, &parm,
                        isvalid_status_cb, &stparm);
  if (opt.verbose > 1)
    log_info ("response of dirmngr: %s\n", rc? gpg_strerror (rc): "okay");
  rc = rc;

  if (!rc && stparm.seen)
    {
      /* Need to also check the certificate validity. */
      if (stparm.seen != 1)
        {
          log_error ("communication problem with dirmngr detected\n");
          rc = gpg_error (GPG_ERR_INV_CRL);
        }
      else
        {
          KEYDB_HANDLE kh;
          ksba_cert_t rspcert = NULL;

          /* Fixme: First try to get the certificate from the
             dirmngr's cache - it should be there. */
          kh = keydb_new (0);
          if (!kh)
            rc = gpg_error (GPG_ERR_ENOMEM);
          if (!rc)
            rc = keydb_search_fpr (kh, stparm.fpr);
          if (!rc)
            rc = keydb_get_cert (kh, &rspcert);
          if (rc)
            {
              log_error ("unable to find the certificate used "
                         "by the dirmngr: %s\n", gpg_strerror (rc));
              rc = gpg_error (GPG_ERR_INV_CRL);
            }
          keydb_release (kh);

          if (!rc)
            {
              rc = gpgsm_cert_use_ocsp_p (rspcert);
              if (rc)
                rc = gpg_error (GPG_ERR_INV_CRL);
              else
                {
                  /* Note the no_dirmngr flag: This avoids checking
                     this certificate over and over again. */
                  rc = gpgsm_validate_chain (ctrl, rspcert, "", NULL, 0, NULL, 
                                             VALIDATE_FLAG_NO_DIRMNGR, NULL);
                  if (rc)
                    {
                      log_error ("invalid certificate used for CRL/OCSP: %s\n",
                                 gpg_strerror (rc));
                      rc = gpg_error (GPG_ERR_INV_CRL);
                    }
                }
            }
          ksba_cert_release (rspcert);
        }
    }
  release_dirmngr (ctrl);
  return rc;
}