Beispiel #1
0
/* Callback used to ask for the PIN which shall be written into BUF.
   The buf has been allocated by the caller and is of size MAXBUF
   which includes the terminating null.  The function should return an
   UTF-8 string with the passphrase/PIN, the buffer may optionally be
   padded with arbitrary characters.

   INFO gets displayed as part of a generic string.  However if the
   first character of INFO is a vertical bar all up to the next
   verical bar are considered flags and only everything after the
   second vertical bar gets displayed as the full prompt.

   We don't need/implement the N/A flags.  When they occur, we signal
   an error.  */
int 
getpin_cb (void *opaque, const char *info, char *buf, size_t maxbuf)
{
  struct getpin_cb_data *cb_data = opaque;
  poldi_ctx_t ctx = cb_data->poldi_ctx;
  char *info_frobbed;
  int err;

  info_frobbed = NULL;
  err = 0;

#if 0
  /* FIXME: why "< 2"? -mo */
  if (buf && maxbuf < 2)
    return gpg_error (GPG_ERR_INV_VALUE);
#endif

  /* Older SCDaemons simply send "PIN" as prompt. We do not process
     this prompt here but use a special case later. */
  if (info && (strcmp (info, "PIN") != 0))
    {
      if (info[0] == '|')
	{
	  if (info[1] == '|')
	    /* Skip "||" at the beginning.  */
	    info += 2;
	  else
	    {
	      /* Weird that we received flags - they are neither expected nor
		 implemented here.  */
	      log_msg_error (ctx->loghandle,
			     _("getpin_cb called with flags set in info string `%s'\n"),
			     info);
	      err = gpg_error (GPG_ERR_INV_VALUE); /* FIXME? */
	      goto out;
	    }
	}
      err = frob_info_msg (info, &info_frobbed);
      if (err)
	{
	  log_msg_error (ctx->loghandle,
			 _("frob_info_msg failed for info msg of size %u\n"),
			 (unsigned int) strlen (info));
	  goto out;
	}
    }

  if (buf)
    {
      /* BUF being non-zero means we are not using a keypad.  */

      if (info_frobbed)
	err = query_user (ctx, info_frobbed, buf, maxbuf);
      else
	/* Use string which is more user friendly. */
	err = query_user (ctx, _("Please enter the PIN:"), buf, maxbuf);
    }
  else
    {
      /* Special handling for keypad mode hack. */

      /* If BUF has been passed as NULL, we are in keypad mode: the
	 callback notifies the user and immediately returns.  */
      if (maxbuf == 0)
	{
	  /* Close the "pinentry". */
	  err = keypad_mode_leave (ctx);
	}
      else if (maxbuf == 1)
	{
	  /* Open the "pinentry". */
	  if (info_frobbed)
	    err = keypad_mode_enter (ctx, info_frobbed);
	  else
	    err = keypad_mode_enter (ctx, _("Please enter the PIN:"));
	}
      else
        err = gpg_error (GPG_ERR_INV_VALUE); /* FIXME: must signal
						internal error(!)?
						-mo */
    }

 out:

  xfree (info_frobbed);

  return err;
}
Beispiel #2
0
static int
xxxx_do_check( PKT_secret_key *sk, const char *tryagain_text, int mode,
               int *canceled )
{
    gpg_error_t err;
    byte *buffer;
    u16 csum=0;
    int i, res;
    size_t nbytes;

    if( sk->is_protected ) { /* remove the protection */
	DEK *dek = NULL;
	u32 keyid[4]; /* 4! because we need two of them */
	gcry_cipher_hd_t cipher_hd=NULL;
	PKT_secret_key *save_sk;

	if( sk->protect.s2k.mode == 1001 ) {
	    log_info(_("secret key parts are not available\n"));
	    return G10ERR_UNU_SECKEY;
	}
	if( sk->protect.algo == CIPHER_ALGO_NONE )
	    BUG();
	if( openpgp_cipher_test_algo( sk->protect.algo ) ) {
	    log_info(_("protection algorithm %d%s is not supported\n"),
			sk->protect.algo,sk->protect.algo==1?" (IDEA)":"" );
	    return G10ERR_CIPHER_ALGO;
	}
	if(gcry_md_test_algo (sk->protect.s2k.hash_algo))
	  {
	    log_info(_("protection digest %d is not supported\n"),
		     sk->protect.s2k.hash_algo);
	    return G10ERR_DIGEST_ALGO;
	  }
	keyid_from_sk( sk, keyid );
	keyid[2] = keyid[3] = 0;
	if (!sk->flags.primary)
          {
            keyid[2] = sk->main_keyid[0];
            keyid[3] = sk->main_keyid[1];
          }
	dek = passphrase_to_dek( keyid, sk->pubkey_algo, sk->protect.algo,
				 &sk->protect.s2k, mode,
                                 tryagain_text, canceled );
        if (!dek && canceled && *canceled)
	    return GPG_ERR_CANCELED;


	err = openpgp_cipher_open (&cipher_hd, sk->protect.algo,
				   GCRY_CIPHER_MODE_CFB,
				   (GCRY_CIPHER_SECURE
				    | (sk->protect.algo >= 100 ?
				       0 : GCRY_CIPHER_ENABLE_SYNC)));
        if (err)
          log_fatal ("cipher open failed: %s\n", gpg_strerror (err) );

	err = gcry_cipher_setkey (cipher_hd, dek->key, dek->keylen);
        if (err)
          log_fatal ("set key failed: %s\n", gpg_strerror (err) );

	xfree(dek);
	save_sk = copy_secret_key( NULL, sk );

	gcry_cipher_setiv ( cipher_hd, sk->protect.iv, sk->protect.ivlen );

	csum = 0;
	if( sk->version >= 4 ) {
            int ndata;
	    unsigned int ndatabits;
	    byte *p, *data;
            u16 csumc = 0;

	    i = pubkey_get_npkey(sk->pubkey_algo);

            assert ( gcry_mpi_get_flag (sk->skey[i], GCRYMPI_FLAG_OPAQUE ));
            p = gcry_mpi_get_opaque ( sk->skey[i], &ndatabits );
            ndata = (ndatabits+7)/8;

            if ( ndata > 1 )
                csumc = p[ndata-2] << 8 | p[ndata-1];
	    data = xmalloc_secure ( ndata );
	    gcry_cipher_decrypt ( cipher_hd, data, ndata, p, ndata );
	    gcry_mpi_release (sk->skey[i]); sk->skey[i] = NULL ;

	    p = data;
            if (sk->protect.sha1chk) {
                /* This is the new SHA1 checksum method to detect
                   tampering with the key as used by the Klima/Rosa
                   attack */
                sk->csum = 0;
                csum = 1;
                if( ndata < 20 )
                    log_error("not enough bytes for SHA-1 checksum\n");
                else {
                    gcry_md_hd_t h;

                    if ( gcry_md_open (&h, DIGEST_ALGO_SHA1, 1))
                        BUG(); /* Algo not available. */
                    gcry_md_write (h, data, ndata - 20);
                    gcry_md_final (h);
                    if (!memcmp (gcry_md_read (h, DIGEST_ALGO_SHA1),
                                 data + ndata - 20, 20) )
                      {
                        /* Digest does match.  We have to keep the old
                           style checksum in sk->csum, so that the
                           test used for unprotected keys does work.
                           This test gets used when we are adding new
                           keys. */
                        sk->csum = csum = checksum (data, ndata-20);
                      }
                    gcry_md_close (h);
                }
            }
            else {
                if( ndata < 2 ) {
                    log_error("not enough bytes for checksum\n");
                    sk->csum = 0;
                    csum = 1;
                }
                else {
                    csum = checksum( data, ndata-2);
                    sk->csum = data[ndata-2] << 8 | data[ndata-1];
                    if ( sk->csum != csum ) {
                        /* This is a PGP 7.0.0 workaround */
                        sk->csum = csumc; /* take the encrypted one */
                    }
                }
            }

            /* Must check it here otherwise the mpi_read_xx would fail
               because the length may have an arbitrary value */
            if( sk->csum == csum ) {
                for( ; i < pubkey_get_nskey(sk->pubkey_algo); i++ ) {
                    if ( gcry_mpi_scan( &sk->skey[i], GCRYMPI_FMT_PGP,
                                        p, ndata, &nbytes))
                      {
                        /* Checksum was okay, but not correctly
                           decrypted.  */
                        sk->csum = 0;
                        csum = 1;
                        break;
                      }
                    ndata -= nbytes;
                    p += nbytes;
                }
                /* Note: at this point ndata should be 2 for a simple
                   checksum or 20 for the sha1 digest */
            }
	    xfree(data);
	}
	else {
	    for(i=pubkey_get_npkey(sk->pubkey_algo);
		    i < pubkey_get_nskey(sk->pubkey_algo); i++ ) {
                byte *p;
                size_t ndata;
                unsigned int ndatabits;

                assert (gcry_mpi_get_flag (sk->skey[i], GCRYMPI_FLAG_OPAQUE));
                p = gcry_mpi_get_opaque (sk->skey[i], &ndatabits);
                ndata = (ndatabits+7)/8;
                assert (ndata >= 2);
                assert (ndata == ((p[0] << 8 | p[1]) + 7)/8 + 2);
                buffer = xmalloc_secure (ndata);
		gcry_cipher_sync (cipher_hd);
                buffer[0] = p[0];
                buffer[1] = p[1];
                gcry_cipher_decrypt (cipher_hd, buffer+2, ndata-2,
                                     p+2, ndata-2);
                csum += checksum (buffer, ndata);
                gcry_mpi_release (sk->skey[i]);

		err = gcry_mpi_scan( &sk->skey[i], GCRYMPI_FMT_PGP,
				     buffer, ndata, &ndata );
		xfree (buffer);
                if (err)
                  {
                    /* Checksum was okay, but not correctly
                       decrypted.  */
                    sk->csum = 0;
                    csum = 1;
                    break;
                  }
/*  		csum += checksum_mpi (sk->skey[i]); */
	    }
	}
	gcry_cipher_close ( cipher_hd );

	/* Now let's see whether we have used the correct passphrase. */
	if( csum != sk->csum ) {
	    copy_secret_key( sk, save_sk );
            passphrase_clear_cache ( keyid, NULL, sk->pubkey_algo );
	    free_secret_key( save_sk );
	    return gpg_error (GPG_ERR_BAD_PASSPHRASE);
	}

	/* The checksum may fail, so we also check the key itself. */
	res = pk_check_secret_key ( sk->pubkey_algo, sk->skey );
	if( res ) {
	    copy_secret_key( sk, save_sk );
            passphrase_clear_cache ( keyid, NULL, sk->pubkey_algo );
	    free_secret_key( save_sk );
	    return gpg_error (GPG_ERR_BAD_PASSPHRASE);
	}
	free_secret_key( save_sk );
	sk->is_protected = 0;
    }
    else { /* not protected, assume it is okay if the checksum is okay */
	csum = 0;
	for(i=pubkey_get_npkey(sk->pubkey_algo);
		i < pubkey_get_nskey(sk->pubkey_algo); i++ ) {
	    csum += checksum_mpi( sk->skey[i] );
	}
	if( csum != sk->csum )
	    return G10ERR_CHECKSUM;
    }

    return 0;
}
Beispiel #3
0
gpg_error_t cmd_getattr (assuan_context_t ctx, char *line)
{
	pkcs11h_certificate_id_list_t user_certificates = NULL;
	char *serial = NULL;
	gpg_err_code_t error = GPG_ERR_GENERAL;

	if (!strcmp (line, "SERIALNO")) {
		if (
			(error = get_serial(ctx, &serial)) != GPG_ERR_NO_ERROR
		) {
			goto cleanup;
		}

		if (serial != NULL) {
			if (
				(error = assuan_write_status (
					ctx,
					"SERIALNO",
					serial
				)) != GPG_ERR_NO_ERROR
			) {
				goto cleanup;
			}
		}
	}
	else if (!strcmp (line, "KEY-FPR")) {
		if (
			(error = common_map_pkcs11_error (
				pkcs11h_certificate_enumCertificateIds (
					PKCS11H_ENUM_METHOD_CACHE_EXIST,
					ctx,
					PKCS11H_PROMPT_MASK_ALLOW_ALL,
					NULL,
					&user_certificates
				)
			)) != GPG_ERR_NO_ERROR ||
			(error = send_certificate_list (
				ctx,
				user_certificates,
				0
			)) != GPG_ERR_NO_ERROR
		) {
			goto cleanup;
		}
	}
	else if (!strcmp (line, "CHV-STATUS")) {
		if (
			(error = assuan_write_status(
				ctx,
				"CHV-STATUS",
				"1 1 1 1 1 1 1"
			)) != GPG_ERR_NO_ERROR
		) {
			goto cleanup;
		}
	}
	else if (!strcmp (line, "DISP-NAME")) {
		if (
			(error = assuan_write_status(
				ctx,
				"DISP-NAME",
				"PKCS#11"
			)) != GPG_ERR_NO_ERROR
		) {
			goto cleanup;
		}
	}
	else if (!strcmp (line, "KEY-ATTR")) {
		int i;
		for (i=0;i<3;i++) {
			char buffer[1024];

			/* I am not sure 2048 is right here... */
			snprintf(buffer, sizeof(buffer), "%d 1 %u %u %d", i+1, GCRY_PK_RSA, 2048, 0);

			if (
				(error = assuan_write_status(
					ctx,
					"KEY-ATTR",
					buffer
				)) != GPG_ERR_NO_ERROR
			) {
				goto cleanup;
			}
		}
	}
	else if (!strcmp (line, "EXTCAP")) {
		int i;
		for (i=0;i<3;i++) {
			char buffer[1024];

			/* I am not sure what these are... */
			snprintf(buffer, sizeof(buffer), "gc=%d ki=%d fc=%d pd=%d mcl3=%u aac=%d sm=%d",
				0, 0, 0, 0, 2048, 0, 0);

			if (
				(error = assuan_write_status(
					ctx,
					"EXTCAP",
					buffer
				)) != GPG_ERR_NO_ERROR
			) {
				goto cleanup;
			}
		}
	}
	else {
		error = GPG_ERR_INV_DATA;
		goto cleanup;
	}

	error = GPG_ERR_NO_ERROR;

cleanup:

	if (user_certificates != NULL) {
		pkcs11h_certificate_freeCertificateIdList (user_certificates);
		user_certificates = NULL;
	}

	if (serial != NULL) {
		free(serial);
		serial = NULL;
	}

	return gpg_error (error);
}
Beispiel #4
0
/* Parse a notation or policy URL subpacket.  If the packet type is
   not known, return no error but NULL in NOTATION.  */
gpgme_error_t
_gpgme_parse_notation(gpgme_sig_notation_t *notationp,
                      int type, int pkflags, int len, char *data)
{
    gpgme_error_t err;
    char *name = NULL;
    int name_len = 0;
    char *value = NULL;
    int value_len = 0;
    gpgme_sig_notation_flags_t flags = 0;
    char *decoded_data;
    unsigned char *bdata;

    /* Type 20: Notation data.  */
    /* Type 26: Policy URL.  */
    if(type != 20 && type != 26)
    {
        *notationp = NULL;
        return 0;
    }

    /* A few simple sanity checks.  */
    if(len > strlen(data))
        return gpg_error(GPG_ERR_INV_ENGINE);

    /* See below for the format of a notation subpacket.  It has at
       least four octets of flags and two times two octets of length
       information.  */
    if(type == 20 && len < 4 + 2 + 2)
        return gpg_error(GPG_ERR_INV_ENGINE);

    err = _gpgme_decode_percent_string(data, &decoded_data, 0, 1);
    if(err)
        return err;
    bdata = (unsigned char *) decoded_data;

    /* Flags common to notation data and policy URL.  */
    if(pkflags & GNUPG_SPK_CRITICAL)
        flags |= GPGME_SIG_NOTATION_CRITICAL;

    /* This information is relevant in parsing multi-octet numbers below:

       3.1. Scalar numbers

       Scalar numbers are unsigned, and are always stored in big-endian
       format.  Using n[k] to refer to the kth octet being interpreted,
       the value of a two-octet scalar is ((n[0] << 8) + n[1]).  The
       value of a four-octet scalar is ((n[0] << 24) + (n[1] << 16) +
       (n[2] << 8) + n[3]).

       From RFC2440: OpenPGP Message Format.  Copyright (C) The Internet
       Society (1998).  All Rights Reserved.  */
#define RFC2440_GET_WORD(chr) ((((int)((unsigned char *)(chr))[0]) << 8) \
			       + ((int)((unsigned char *)(chr))[1]))

    if(type == 20)
    {
        /* 5.2.3.15. Notation Data

        (4 octets of flags, 2 octets of name length (M),
         2 octets of value length (N), M octets of name data,
         N octets of value data)

         [...] The "flags" field holds four octets of flags.
         All undefined flags MUST be zero. Defined flags are:

         First octet: 0x80 = human-readable. [...]
         Other octets:  none.

         From RFC2440: OpenPGP Message Format.  Copyright (C) The
         Internet Society (1998).  All Rights Reserved.  */

        int chr;

        /* First octet of flags.  */
#define RFC2440_SPK20_FLAG1_HUMAN_READABLE 0x80

        chr = *bdata;
        bdata++;

        if(chr & RFC2440_SPK20_FLAG1_HUMAN_READABLE)
            flags |= GPGME_SIG_NOTATION_HUMAN_READABLE;

        /* The second, third and four octet of flags are unused.  */
        bdata++;
        bdata++;
        bdata++;

        name_len = RFC2440_GET_WORD(bdata);
        bdata += 2;

        value_len = RFC2440_GET_WORD(bdata);
        bdata += 2;

        /* Small sanity check.  */
        if(4 + 2 + 2 + name_len + value_len > len)
        {
            free(decoded_data);
            return gpg_error(GPG_ERR_INV_ENGINE);
        }

        name = (char *) bdata;
        bdata += name_len;

        value = (char *) bdata;
    }
    else
    {
        /* Type is 26.  */

        /* NAME is NULL, name_len is 0.  */

        value = (char *) bdata;
        value_len = strlen(value);
    }

    err = _gpgme_sig_notation_create(notationp, name, name_len,
                                     value, value_len, flags);

    free(decoded_data);
    return err;
}
Beispiel #5
0
gpgme_error_t
_gpgme_passphrase_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_op_data_lookup (ctx, OPDATA_PASSPHRASE, &hook,
			       sizeof (*opd), release_op_data);
  opd = hook;
  if (err)
    return err;

  switch (code)
    {
    case GPGME_STATUS_INQUIRE_MAXLEN:
      free (opd->maxlen);
      if (!(opd->maxlen = strdup (args)))
        return gpg_error_from_syserror ();
      break;
    case GPGME_STATUS_USERID_HINT:
      if (opd->uid_hint)
	free (opd->uid_hint);
      if (!(opd->uid_hint = strdup (args)))
        return gpg_error_from_syserror ();
      break;

    case GPGME_STATUS_BAD_PASSPHRASE:
      opd->bad_passphrase++;
      opd->no_passphrase = 0;
      break;

    case GPGME_STATUS_GOOD_PASSPHRASE:
      opd->bad_passphrase = 0;
      opd->no_passphrase = 0;
      break;

    case GPGME_STATUS_NEED_PASSPHRASE:
    case GPGME_STATUS_NEED_PASSPHRASE_SYM:
    case GPGME_STATUS_NEED_PASSPHRASE_PIN:
      if (opd->passphrase_info)
	free (opd->passphrase_info);
      opd->passphrase_info = strdup (args);
      if (!opd->passphrase_info)
	return gpg_error_from_syserror ();
      break;

    case GPGME_STATUS_MISSING_PASSPHRASE:
      opd->no_passphrase = 1;
      break;

    case GPGME_STATUS_EOF:
      if (opd->no_passphrase || opd->bad_passphrase)
	return gpg_error (GPG_ERR_BAD_PASSPHRASE);
      break;

    case GPGME_STATUS_ERROR:
      /* We abuse this status handler to forward ERROR status codes to
         the caller.  This should better be done in a generic handler,
         but for now this is sufficient.  */
      if (ctx->status_cb)
        {
          err = ctx->status_cb (ctx->status_cb_value, "ERROR", args);
          if (err)
            return err;
        }
      break;

    case GPGME_STATUS_FAILURE:
      /* We abuse this status handler to forward FAILURE status codes
         to the caller.  This should better be done in a generic
         handler, but for now this is sufficient.  */
      if (ctx->status_cb)
        {
          err = ctx->status_cb (ctx->status_cb_value, "FAILURE", args);
          if (err)
            return err;
        }
      break;


    default:
      /* Ignore all other codes.  */
      break;
    }
  return 0;
}
Beispiel #6
0
/* Return the information about the secret key specified by the binary
   keygrip GRIP.  If the key is a shadowed one the shadow information
   will be stored at the address R_SHADOW_INFO as an allocated
   S-expression.  */
gpg_error_t
agent_key_info_from_file (ctrl_t ctrl, const unsigned char *grip,
                          int *r_keytype, unsigned char **r_shadow_info)
{
  gpg_error_t err;
  unsigned char *buf;
  size_t len;
  int keytype;

  (void)ctrl;

  if (r_keytype)
    *r_keytype = PRIVATE_KEY_UNKNOWN;
  if (r_shadow_info)
    *r_shadow_info = NULL;

  {
    gcry_sexp_t sexp;

    err = read_key_file (grip, &sexp);
    if (err)
      {
        if (gpg_err_code (err) == GPG_ERR_ENOENT)
          return gpg_error (GPG_ERR_NOT_FOUND);
        else
          return err;
      }
    err = make_canon_sexp (sexp, &buf, &len);
    gcry_sexp_release (sexp);
    if (err)
      return err;
  }

  keytype = agent_private_key_type (buf);
  switch (keytype)
    {
    case PRIVATE_KEY_CLEAR:
      break;
    case PRIVATE_KEY_PROTECTED:
      /* If we ever require it we could retrieve the comment fields
         from such a key. */
      break;
    case PRIVATE_KEY_SHADOWED:
      if (r_shadow_info)
        {
          const unsigned char *s;
          size_t n;

          err = agent_get_shadow_info (buf, &s);
          if (!err)
            {
              n = gcry_sexp_canon_len (s, 0, NULL, NULL);
              assert (n);
              *r_shadow_info = xtrymalloc (n);
              if (!*r_shadow_info)
                err = gpg_error_from_syserror ();
              else
                memcpy (*r_shadow_info, s, n);
            }
        }
      break;
    default:
      err = gpg_error (GPG_ERR_BAD_SECKEY);
      break;
    }

  if (!err && r_keytype)
    *r_keytype = keytype;

  xfree (buf);
  return err;
}
Beispiel #7
0
/* Return the string name from the S-expression S_KEY as well as a
   string describing the names of the parameters.  ALGONAMESIZE and
   ELEMSSIZE give the allocated size of the provided buffers.  The
   buffers may be NULL if not required.  If R_LIST is not NULL the top
   level list will be stored tehre; the caller needs to release it in
   this case.  */
static gpg_error_t
key_parms_from_sexp (gcry_sexp_t s_key, gcry_sexp_t *r_list,
                     char *r_algoname, size_t algonamesize,
                     char *r_elems, size_t elemssize)
{
  gcry_sexp_t list, l2;
  const char *name, *algoname, *elems;
  size_t n;

  if (r_list)
    *r_list = NULL;

  list = gcry_sexp_find_token (s_key, "shadowed-private-key", 0 );
  if (!list)
    list = gcry_sexp_find_token (s_key, "protected-private-key", 0 );
  if (!list)
    list = gcry_sexp_find_token (s_key, "private-key", 0 );
  if (!list)
    {
      log_error ("invalid private key format\n");
      return gpg_error (GPG_ERR_BAD_SECKEY);
    }

  l2 = gcry_sexp_cadr (list);
  gcry_sexp_release (list);
  list = l2;
  name = gcry_sexp_nth_data (list, 0, &n);
  if (n==3 && !memcmp (name, "rsa", 3))
    {
      algoname = "rsa";
      elems = "ne";
    }
  else if (n==3 && !memcmp (name, "dsa", 3))
    {
      algoname = "dsa";
      elems = "pqgy";
    }
  else if (n==5 && !memcmp (name, "ecdsa", 5))
    {
      algoname = "ecdsa";
      elems = "pabgnq";
    }
  else if (n==4 && !memcmp (name, "ecdh", 4))
    {
      algoname = "ecdh";
      elems = "pabgnq";
    }
  else if (n==3 && !memcmp (name, "elg", 3))
    {
      algoname = "elg";
      elems = "pgy";
    }
  else
    {
      log_error ("unknown private key algorithm\n");
      gcry_sexp_release (list);
      return gpg_error (GPG_ERR_BAD_SECKEY);
    }

  if (r_algoname)
    {
      if (strlen (algoname) >= algonamesize)
        return gpg_error (GPG_ERR_BUFFER_TOO_SHORT);
      strcpy (r_algoname, algoname);
    }
  if (r_elems)
    {
      if (strlen (elems) >= elemssize)
        return gpg_error (GPG_ERR_BUFFER_TOO_SHORT);
      strcpy (r_elems, elems);
    }

  if (r_list)
    *r_list = list;
  else
    gcry_sexp_release (list);

  return 0;
}
Beispiel #8
0
/* Perform an encrypt operation.

   Encrypt the data received on DATA-FD and write it to OUT_FP.  The
   recipients are take from the certificate given in recplist; if this
   is NULL it will be encrypted for a default recipient */
int
gpgsm_encrypt (ctrl_t ctrl, certlist_t recplist, int data_fd, estream_t out_fp)
{
  int rc = 0;
  Base64Context b64writer = NULL;
  gpg_error_t err;
  ksba_writer_t writer;
  ksba_reader_t reader = NULL;
  ksba_cms_t cms = NULL;
  ksba_stop_reason_t stopreason;
  KEYDB_HANDLE kh = NULL;
  struct encrypt_cb_parm_s encparm;
  DEK dek = NULL;
  int recpno;
  estream_t data_fp = NULL;
  certlist_t cl;
  int count;

  memset (&encparm, 0, sizeof encparm);

  audit_set_type (ctrl->audit, AUDIT_TYPE_ENCRYPT);

  /* Check that the certificate list is not empty and that at least
     one certificate is not flagged as encrypt_to; i.e. is a real
     recipient. */
  for (cl = recplist; cl; cl = cl->next)
    if (!cl->is_encrypt_to)
      break;
  if (!cl)
    {
      log_error(_("no valid recipients given\n"));
      gpgsm_status (ctrl, STATUS_NO_RECP, "0");
      audit_log_i (ctrl->audit, AUDIT_GOT_RECIPIENTS, 0);
      rc = gpg_error (GPG_ERR_NO_PUBKEY);
      goto leave;
    }

  for (count = 0, cl = recplist; cl; cl = cl->next)
    count++;
  audit_log_i (ctrl->audit, AUDIT_GOT_RECIPIENTS, count);

  kh = keydb_new (0);
  if (!kh)
    {
      log_error (_("failed to allocate keyDB handle\n"));
      rc = gpg_error (GPG_ERR_GENERAL);
      goto leave;
    }

  /* Fixme:  We should use the unlocked version of the es functions.  */
  data_fp = es_fdopen_nc (data_fd, "rb");
  if (!data_fp)
    {
      rc = gpg_error_from_syserror ();
      log_error ("fdopen() failed: %s\n", strerror (errno));
      goto leave;
    }

  err = ksba_reader_new (&reader);
  if (err)
      rc = err;
  if (!rc)
    rc = ksba_reader_set_cb (reader, encrypt_cb, &encparm);
  if (rc)
      goto leave;

  encparm.fp = data_fp;

  ctrl->pem_name = "ENCRYPTED MESSAGE";
  rc = gpgsm_create_writer (&b64writer, ctrl, out_fp, &writer);
  if (rc)
    {
      log_error ("can't create writer: %s\n", gpg_strerror (rc));
      goto leave;
    }

  err = ksba_cms_new (&cms);
  if (err)
    {
      rc = err;
      goto leave;
    }

  err = ksba_cms_set_reader_writer (cms, reader, writer);
  if (err)
    {
      log_debug ("ksba_cms_set_reader_writer failed: %s\n",
                 gpg_strerror (err));
      rc = err;
      goto leave;
    }

  audit_log (ctrl->audit, AUDIT_GOT_DATA);

  /* We are going to create enveloped data with uninterpreted data as
     inner content */
  err = ksba_cms_set_content_type (cms, 0, KSBA_CT_ENVELOPED_DATA);
  if (!err)
    err = ksba_cms_set_content_type (cms, 1, KSBA_CT_DATA);
  if (err)
    {
      log_debug ("ksba_cms_set_content_type failed: %s\n",
                 gpg_strerror (err));
      rc = err;
      goto leave;
    }

  /* Create a session key */
  dek = xtrycalloc_secure (1, sizeof *dek);
  if (!dek)
    rc = out_of_core ();
  else
  {
    dek->algoid = opt.def_cipher_algoid;
    rc = init_dek (dek);
  }
  if (rc)
    {
      log_error ("failed to create the session key: %s\n",
                 gpg_strerror (rc));
      goto leave;
    }

  err = ksba_cms_set_content_enc_algo (cms, dek->algoid, dek->iv, dek->ivlen);
  if (err)
    {
      log_error ("ksba_cms_set_content_enc_algo failed: %s\n",
                 gpg_strerror (err));
      rc = err;
      goto leave;
    }

  encparm.dek = dek;
  /* Use a ~8k (AES) or ~4k (3DES) buffer */
  encparm.bufsize = 500 * dek->ivlen;
  encparm.buffer = xtrymalloc (encparm.bufsize);
  if (!encparm.buffer)
    {
      rc = out_of_core ();
      goto leave;
    }

  audit_log_s (ctrl->audit, AUDIT_SESSION_KEY, dek->algoid);

  /* Gather certificates of recipients, encrypt the session key for
     each and store them in the CMS object */
  for (recpno = 0, cl = recplist; cl; recpno++, cl = cl->next)
    {
      unsigned char *encval;

      rc = encrypt_dek (dek, cl->cert, &encval);
      if (rc)
        {
          audit_log_cert (ctrl->audit, AUDIT_ENCRYPTED_TO, cl->cert, rc);
          log_error ("encryption failed for recipient no. %d: %s\n",
                     recpno, gpg_strerror (rc));
          goto leave;
        }

      err = ksba_cms_add_recipient (cms, cl->cert);
      if (err)
        {
          audit_log_cert (ctrl->audit, AUDIT_ENCRYPTED_TO, cl->cert, err);
          log_error ("ksba_cms_add_recipient failed: %s\n",
                     gpg_strerror (err));
          rc = err;
          xfree (encval);
          goto leave;
        }

      err = ksba_cms_set_enc_val (cms, recpno, encval);
      xfree (encval);
      audit_log_cert (ctrl->audit, AUDIT_ENCRYPTED_TO, cl->cert, err);
      if (err)
        {
          log_error ("ksba_cms_set_enc_val failed: %s\n",
                     gpg_strerror (err));
          rc = err;
          goto leave;
        }
    }

  /* Main control loop for encryption. */
  recpno = 0;
  do
    {
      err = ksba_cms_build (cms, &stopreason);
      if (err)
        {
          log_debug ("ksba_cms_build failed: %s\n", gpg_strerror (err));
          rc = err;
          goto leave;
        }
    }
  while (stopreason != KSBA_SR_READY);

  if (encparm.readerror)
    {
      log_error ("error reading input: %s\n", strerror (encparm.readerror));
      rc = gpg_error (gpg_err_code_from_errno (encparm.readerror));
      goto leave;
    }


  rc = gpgsm_finish_writer (b64writer);
  if (rc)
    {
      log_error ("write failed: %s\n", gpg_strerror (rc));
      goto leave;
    }
  audit_log (ctrl->audit, AUDIT_ENCRYPTION_DONE);
  log_info ("encrypted data created\n");

 leave:
  ksba_cms_release (cms);
  gpgsm_destroy_writer (b64writer);
  ksba_reader_release (reader);
  keydb_release (kh);
  xfree (dek);
  es_fclose (data_fp);
  xfree (encparm.buffer);
  return rc;
}
Beispiel #9
0
/* Initialize the data encryption key (session key). */
static int
init_dek (DEK dek)
{
  int rc=0, mode, i;

  dek->algo = gcry_cipher_map_name (dek->algoid);
  mode = gcry_cipher_mode_from_oid (dek->algoid);
  if (!dek->algo || !mode)
    {
      log_error ("unsupported algorithm '%s'\n", dek->algoid);
      return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM);
    }

  /* Extra check for algorithms we consider to be too weak for
     encryption, although we support them for decryption.  Note that
     there is another check below discriminating on the key length. */
  switch (dek->algo)
    {
    case GCRY_CIPHER_DES:
    case GCRY_CIPHER_RFC2268_40:
      log_error ("cipher algorithm '%s' not allowed: too weak\n",
                 gnupg_cipher_algo_name (dek->algo));
      return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM);
    default:
      break;
    }

  dek->keylen = gcry_cipher_get_algo_keylen (dek->algo);
  if (!dek->keylen || dek->keylen > sizeof (dek->key))
    return gpg_error (GPG_ERR_BUG);

  dek->ivlen = gcry_cipher_get_algo_blklen (dek->algo);
  if (!dek->ivlen || dek->ivlen > sizeof (dek->iv))
    return gpg_error (GPG_ERR_BUG);

  /* Make sure we don't use weak keys. */
  if (dek->keylen < 100/8)
    {
      log_error ("key length of '%s' too small\n", dek->algoid);
      return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM);
    }

  rc = gcry_cipher_open (&dek->chd, dek->algo, mode, GCRY_CIPHER_SECURE);
  if (rc)
    {
      log_error ("failed to create cipher context: %s\n", gpg_strerror (rc));
      return rc;
    }

  for (i=0; i < 8; i++)
    {
      gcry_randomize (dek->key, dek->keylen, GCRY_STRONG_RANDOM );
      rc = gcry_cipher_setkey (dek->chd, dek->key, dek->keylen);
      if (gpg_err_code (rc) != GPG_ERR_WEAK_KEY)
        break;
      log_info(_("weak key created - retrying\n") );
    }
  if (rc)
    {
      log_error ("failed to set the key: %s\n", gpg_strerror (rc));
      gcry_cipher_close (dek->chd);
      dek->chd = NULL;
      return rc;
    }

  gcry_create_nonce (dek->iv, dek->ivlen);
  rc = gcry_cipher_setiv (dek->chd, dek->iv, dek->ivlen);
  if (rc)
    {
      log_error ("failed to set the IV: %s\n", gpg_strerror (rc));
      gcry_cipher_close (dek->chd);
      dek->chd = NULL;
      return rc;
    }

  return 0;
}
Beispiel #10
0
/**
 * ksba_reader_read:
 * @r: Readder object
 * @buffer: A buffer for returning the data
 * @length: The length of this buffer
 * @nread:  Number of bytes actually read.
 * 
 * Read data from the current read position to the supplied @buffer,
 * max. @length bytes are read and the actual number of bytes read are
 * returned in @nread.  If there are no more bytes available %GPG_ERR_EOF is
 * returned and @nread is set to 0.
 *
 * If a @buffer of NULL is specified, the function does only return
 * the number of bytes available and does not move the read pointer.
 * This does only work for objects initialized from memory; if the
 * object is not capable of this it will return the error
 * GPG_ERR_NOT_IMPLEMENTED
 * 
 * Return value: 0 on success, GPG_ERR_EOF or another error code
 **/
gpg_error_t
ksba_reader_read (ksba_reader_t r, char *buffer, size_t length, size_t *nread)
{
  size_t nbytes;

  if (!r || !nread)
    return gpg_error (GPG_ERR_INV_VALUE);


  if (!buffer)
    {
      if (r->type != READER_TYPE_MEM)
        return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
      *nread = r->u.mem.size - r->u.mem.readpos;
      if (r->unread.buf)
        *nread += r->unread.length - r->unread.readpos;
      return *nread? 0 : gpg_error (GPG_ERR_EOF);
    }

  *nread = 0;

  if (r->unread.buf && r->unread.length)
    {
      nbytes = r->unread.length - r->unread.readpos;
      if (!nbytes)
        return gpg_error (GPG_ERR_BUG);
      
      if (nbytes > length)
        nbytes = length;
      memcpy (buffer, r->unread.buf + r->unread.readpos, nbytes);
      r->unread.readpos += nbytes;
      if (r->unread.readpos == r->unread.length)
        r->unread.readpos = r->unread.length = 0;
      *nread = nbytes;
      r->nread += nbytes;
      return 0;
    }


  if (!r->type)
    {
      r->eof = 1;
      return gpg_error (GPG_ERR_EOF);
    }
  else if (r->type == READER_TYPE_MEM)
    {
      nbytes = r->u.mem.size - r->u.mem.readpos;
      if (!nbytes)
        {
          r->eof = 1;
          return gpg_error (GPG_ERR_EOF);
        }
      
      if (nbytes > length)
        nbytes = length;
      memcpy (buffer, r->u.mem.buffer + r->u.mem.readpos, nbytes);
      *nread = nbytes;
      r->nread += nbytes;
      r->u.mem.readpos += nbytes;
    }
  else if (r->type == READER_TYPE_FILE)
    {
      int n;

      if (r->eof)
        return gpg_error (GPG_ERR_EOF);
      
      if (!length)
        {
          *nread = 0;
          return 0;
        }

      n = fread (buffer, 1, length, r->u.file);
      if (n > 0)
        {
          r->nread += n;
          *nread = n;
        }
      else
        *nread = 0;
      if (n < length)
        {
          if (ferror(r->u.file))
              r->error = errno;
          r->eof = 1;
          if (n <= 0)
            return gpg_error (GPG_ERR_EOF);
        }
    }
  else if (r->type == READER_TYPE_CB)
    {
      if (r->eof)
        return gpg_error (GPG_ERR_EOF);
      
      if (r->u.cb.fnc (r->u.cb.value, buffer, length, nread))
        {
          *nread = 0;
          r->eof = 1;
          return gpg_error (GPG_ERR_EOF);
        }
      r->nread += *nread;
    }
  else 
    return gpg_error (GPG_ERR_BUG);

  return 0;
} 
Beispiel #11
0
/****************
 * Decrypt the data, specified by ED with the key DEK.
 */
int
decrypt_data (ctrl_t ctrl, void *procctx, PKT_encrypted *ed, DEK *dek)
{
  decode_filter_ctx_t dfx;
  byte *p;
  int rc=0, c, i;
  byte temp[32];
  unsigned blocksize;
  unsigned nprefix;

  dfx = xtrycalloc (1, sizeof *dfx);
  if (!dfx)
    return gpg_error_from_syserror ();
  dfx->refcount = 1;

  if ( opt.verbose && !dek->algo_info_printed )
    {
      if (!openpgp_cipher_test_algo (dek->algo))
        log_info (_("%s encrypted data\n"),
                  openpgp_cipher_algo_name (dek->algo));
      else
        log_info (_("encrypted with unknown algorithm %d\n"), dek->algo );
      dek->algo_info_printed = 1;
    }

  {
    char buf[20];

    snprintf (buf, sizeof buf, "%d %d", ed->mdc_method, dek->algo);
    write_status_text (STATUS_DECRYPTION_INFO, buf);
  }

  if (opt.show_session_key)
    {
      char numbuf[25];
      char *hexbuf;

      snprintf (numbuf, sizeof numbuf, "%d:", dek->algo);
      hexbuf = bin2hex (dek->key, dek->keylen, NULL);
      if (!hexbuf)
        {
          rc = gpg_error_from_syserror ();
          goto leave;
        }
      log_info ("session key: '%s%s'\n", numbuf, hexbuf);
      write_status_strings (STATUS_SESSION_KEY, numbuf, hexbuf, NULL);
      xfree (hexbuf);
    }

  rc = openpgp_cipher_test_algo (dek->algo);
  if (rc)
    goto leave;
  blocksize = openpgp_cipher_get_algo_blklen (dek->algo);
  if ( !blocksize || blocksize > 16 )
    log_fatal ("unsupported blocksize %u\n", blocksize );
  nprefix = blocksize;
  if ( ed->len && ed->len < (nprefix+2) )
    {
       /* An invalid message.  We can't check that during parsing
          because we may not know the used cipher then.  */
      rc = gpg_error (GPG_ERR_INV_PACKET);
      goto leave;
    }

  if ( ed->mdc_method )
    {
      if (gcry_md_open (&dfx->mdc_hash, ed->mdc_method, 0 ))
        BUG ();
      if ( DBG_HASHING )
        gcry_md_debug (dfx->mdc_hash, "checkmdc");
    }

  rc = openpgp_cipher_open (&dfx->cipher_hd, dek->algo,
			    GCRY_CIPHER_MODE_CFB,
			    (GCRY_CIPHER_SECURE
			     | ((ed->mdc_method || dek->algo >= 100)?
				0 : GCRY_CIPHER_ENABLE_SYNC)));
  if (rc)
    {
      /* We should never get an error here cause we already checked
       * that the algorithm is available.  */
      BUG();
    }


  /* log_hexdump( "thekey", dek->key, dek->keylen );*/
  rc = gcry_cipher_setkey (dfx->cipher_hd, dek->key, dek->keylen);
  if ( gpg_err_code (rc) == GPG_ERR_WEAK_KEY )
    {
      log_info(_("WARNING: message was encrypted with"
                 " a weak key in the symmetric cipher.\n"));
      rc=0;
    }
  else if( rc )
    {
      log_error("key setup failed: %s\n", gpg_strerror (rc) );
      goto leave;
    }

  if (!ed->buf)
    {
      log_error(_("problem handling encrypted packet\n"));
      goto leave;
    }

  gcry_cipher_setiv (dfx->cipher_hd, NULL, 0);

  if ( ed->len )
    {
      for (i=0; i < (nprefix+2) && ed->len; i++, ed->len-- )
        {
          if ( (c=iobuf_get(ed->buf)) == -1 )
            break;
          else
            temp[i] = c;
        }
    }
  else
    {
      for (i=0; i < (nprefix+2); i++ )
        if ( (c=iobuf_get(ed->buf)) == -1 )
          break;
        else
          temp[i] = c;
    }

  gcry_cipher_decrypt (dfx->cipher_hd, temp, nprefix+2, NULL, 0);
  gcry_cipher_sync (dfx->cipher_hd);
  p = temp;
  /* log_hexdump( "prefix", temp, nprefix+2 ); */
  if (dek->symmetric
      && (p[nprefix-2] != p[nprefix] || p[nprefix-1] != p[nprefix+1]) )
    {
      rc = gpg_error (GPG_ERR_BAD_KEY);
      goto leave;
    }

  if ( dfx->mdc_hash )
    gcry_md_write (dfx->mdc_hash, temp, nprefix+2);

  dfx->refcount++;
  dfx->partial = ed->is_partial;
  dfx->length = ed->len;
  if ( ed->mdc_method )
    iobuf_push_filter ( ed->buf, mdc_decode_filter, dfx );
  else
    iobuf_push_filter ( ed->buf, decode_filter, dfx );

  if (opt.unwrap_encryption)
    {
      char *filename;
      estream_t fp;
      rc = get_output_file ("", 0, ed->buf, &filename, &fp);
      if (! rc)
        {
          iobuf_t output = iobuf_esopen (fp, "w", 0);
          armor_filter_context_t *afx = NULL;

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

          iobuf_copy (output, ed->buf);
          if ((rc = iobuf_error (ed->buf)))
            log_error (_("error reading '%s': %s\n"),
                       filename, gpg_strerror (rc));
          else if ((rc = iobuf_error (output)))
            log_error (_("error writing '%s': %s\n"),
                       filename, gpg_strerror (rc));

          iobuf_close (output);
          if (afx)
            release_armor_context (afx);
        }
    }
  else
    proc_packets (ctrl, procctx, ed->buf );

  ed->buf = NULL;
  if (dfx->eof_seen > 1 )
    rc = gpg_error (GPG_ERR_INV_PACKET);
  else if ( ed->mdc_method )
    {
      /* We used to let parse-packet.c handle the MDC packet but this
         turned out to be a problem with compressed packets: With old
         style packets there is no length information available and
         the decompressor uses an implicit end.  However we can't know
         this implicit end beforehand (:-) and thus may feed the
         decompressor with more bytes than actually needed.  It would
         be possible to unread the extra bytes but due to our weird
         iobuf system any unread is non reliable due to filters
         already popped off.  The easy and sane solution is to care
         about the MDC packet only here and never pass it to the
         packet parser.  Fortunatley the OpenPGP spec requires a
         strict format for the MDC packet so that we know that 22
         bytes are appended.  */
      int datalen = gcry_md_get_algo_dlen (ed->mdc_method);

      log_assert (dfx->cipher_hd);
      log_assert (dfx->mdc_hash);
      gcry_cipher_decrypt (dfx->cipher_hd, dfx->defer, 22, NULL, 0);
      gcry_md_write (dfx->mdc_hash, dfx->defer, 2);
      gcry_md_final (dfx->mdc_hash);

      if (   dfx->defer[0] != '\xd3'
          || dfx->defer[1] != '\x14'
          || datalen != 20
          || memcmp (gcry_md_read (dfx->mdc_hash, 0), dfx->defer+2, datalen))
        rc = gpg_error (GPG_ERR_BAD_SIGNATURE);
      /* log_printhex("MDC message:", dfx->defer, 22); */
      /* log_printhex("MDC calc:", gcry_md_read (dfx->mdc_hash,0), datalen); */
    }


 leave:
  release_dfx_context (dfx);
  return rc;
}
Beispiel #12
0
gpg_error_t
ksba_reader_error (ksba_reader_t r)
{
  return r? gpg_error_from_errno (r->error) : gpg_error (GPG_ERR_INV_VALUE);
}
Beispiel #13
0
static gpgme_error_t
parse_import (char *args, gpgme_import_status_t *import_status, int problem)
{
  gpgme_import_status_t import;
  char *tail;
  long int nr;

  import = malloc (sizeof (*import));
  if (!import)
    return gpg_error_from_errno (errno);
  import->next = NULL;

  errno = 0;
  nr = strtol (args, &tail, 0);
  if (errno || args == tail || *tail != ' ')
    {
      /* The crypto backend does not behave.  */
      free (import);
      return gpg_error (GPG_ERR_INV_ENGINE);
    }
  args = tail;

  if (problem)
    {
      switch (nr)
	{
	case 0:
	case 4:
	default:
	  import->result = gpg_error (GPG_ERR_GENERAL);
	  break;

	case 1:
	  import->result = gpg_error (GPG_ERR_BAD_CERT);
	  break;

	case 2:
	  import->result = gpg_error (GPG_ERR_MISSING_CERT);
	  break;

	case 3:
	  import->result = gpg_error (GPG_ERR_BAD_CERT_CHAIN);
	  break;
	}
      import->status = 0;
    }
  else
    {
      import->result = gpg_error (GPG_ERR_NO_ERROR);
      import->status = nr;
    }

  while (*args == ' ')
    args++;
  tail = strchr (args, ' ');
  if (tail)
    *tail = '\0';

  import->fpr = strdup (args);
  if (!import->fpr)
    {
      int saved_errno = errno;
      free (import);
      return gpg_error_from_errno (saved_errno);
    }

  *import_status = import;
  return 0;
}
Beispiel #14
0
/* Lock a spawning process.  The caller needs to provide the address
   of a variable to store the lock information and the name or the
   process.  */
static gpg_error_t
lock_spawning (lock_spawn_t *lock, const char *homedir, const char *name,
               int verbose)
{
#ifdef HAVE_W32_SYSTEM
  int waitrc;
  int timeout = (!strcmp (name, "agent")
                 ? SECS_TO_WAIT_FOR_AGENT
                 : SECS_TO_WAIT_FOR_DIRMNGR);

  (void)homedir; /* Not required. */

  *lock = CreateMutexW
    (NULL, FALSE,
     !strcmp (name, "agent")?   L"GnuPG_spawn_agent_sentinel":
     !strcmp (name, "dirmngr")? L"GnuPG_spawn_dirmngr_sentinel":
     /*                    */   L"GnuPG_spawn_unknown_sentinel");
  if (!*lock)
    {
      log_error ("failed to create the spawn_%s mutex: %s\n",
                 name, w32_strerror (-1));
      return gpg_error (GPG_ERR_GENERAL);
    }

 retry:
  waitrc = WaitForSingleObject (*lock, 1000);
  if (waitrc == WAIT_OBJECT_0)
    return 0;

  if (waitrc == WAIT_TIMEOUT && timeout)
    {
      timeout--;
      if (verbose)
        log_info ("another process is trying to start the %s ... (%ds)\n",
                  name, timeout);
      goto retry;
    }
  if (waitrc == WAIT_TIMEOUT)
    log_info ("error waiting for the spawn_%s mutex: timeout\n", name);
  else
    log_info ("error waiting for the spawn_%s mutex: (code=%d) %s\n",
              name, waitrc, w32_strerror (-1));
  return gpg_error (GPG_ERR_GENERAL);
#else /*!HAVE_W32_SYSTEM*/
  char *fname;

  (void)verbose;

  *lock = NULL;

  fname = make_filename
    (homedir,
     !strcmp (name, "agent")?   "gnupg_spawn_agent_sentinel":
     !strcmp (name, "dirmngr")? "gnupg_spawn_dirmngr_sentinel":
     /*                    */   "gnupg_spawn_unknown_sentinel",
     NULL);
  if (!fname)
    return gpg_error_from_syserror ();

  *lock = dotlock_create (fname, 0);
  xfree (fname);
  if (!*lock)
    return gpg_error_from_syserror ();

  /* FIXME: We should use a timeout of 5000 here - however
     make_dotlock does not yet support values other than -1 and 0.  */
  if (dotlock_take (*lock, -1))
    return gpg_error_from_syserror ();

  return 0;
#endif /*!HAVE_W32_SYSTEM*/
}
Beispiel #15
0
static gpg_error_t
new_part (void *cookie, const char *mediatype, const char *mediasubtype)
{
  receive_ctx_t ctx = cookie;
  gpg_error_t err = 0;

  ctx->collect_key_data = 0;
  ctx->collect_wkd_data = 0;

  if (!strcmp (mediatype, "application")
      && !strcmp (mediasubtype, "pgp-keys"))
    {
      log_info ("new '%s/%s' message part\n", mediatype, mediasubtype);
      if (ctx->key_data)
        {
          log_error ("we already got a key - ignoring this part\n");
          err = gpg_error (GPG_ERR_FALSE);
        }
      else
        {
          ctx->key_data = es_fopenmem (0, "w+b");
          if (!ctx->key_data)
            {
              err = gpg_error_from_syserror ();
              log_error ("error allocating space for key: %s\n",
                         gpg_strerror (err));
            }
          else
            {
              ctx->collect_key_data = 1;
              err = gpg_error (GPG_ERR_TRUE); /* We want the part decoded.  */
            }
        }
    }
  else if (!strcmp (mediatype, "application")
           && !strcmp (mediasubtype, "vnd.gnupg.wks"))
    {
      log_info ("new '%s/%s' message part\n", mediatype, mediasubtype);
      if (ctx->wkd_data)
        {
          log_error ("we already got a wkd part - ignoring this part\n");
          err = gpg_error (GPG_ERR_FALSE);
        }
      else
        {
          ctx->wkd_data = es_fopenmem (0, "w+b");
          if (!ctx->wkd_data)
            {
              err = gpg_error_from_syserror ();
              log_error ("error allocating space for key: %s\n",
                         gpg_strerror (err));
            }
          else
            {
              ctx->collect_wkd_data = 1;
              err = gpg_error (GPG_ERR_TRUE); /* We want the part decoded.  */
            }
        }
    }
  else
    {
      log_error ("unexpected '%s/%s' message part\n", mediatype, mediasubtype);
      err = gpg_error (GPG_ERR_FALSE); /* We do not want the part.  */
    }

  return err;
}
/****************
 * read the record with number recnum
 * returns: -1 on error, 0 on success
 */
int
tdbio_read_record( ulong recnum, TRUSTREC *rec, int expected )
{
    byte readbuf[TRUST_RECORD_LEN];
    const byte *buf, *p;
    gpg_error_t err = 0;
    int n, i;

    if( db_fd == -1 )
	open_db();
    buf = get_record_from_cache( recnum );
    if( !buf ) {
	if( lseek( db_fd, recnum * TRUST_RECORD_LEN, SEEK_SET ) == -1 ) {
            err = gpg_error_from_syserror ();
	    log_error(_("trustdb: lseek failed: %s\n"), strerror(errno) );
	    return err;
	}
	n = read( db_fd, readbuf, TRUST_RECORD_LEN);
	if( !n ) {
	    return -1; /* eof */
	}
	else if( n != TRUST_RECORD_LEN ) {
            err = gpg_error_from_syserror ();
	    log_error(_("trustdb: read failed (n=%d): %s\n"), n,
							strerror(errno) );
	    return err;
	}
	buf = readbuf;
    }
    rec->recnum = recnum;
    rec->dirty = 0;
    p = buf;
    rec->rectype = *p++;
    if( expected && rec->rectype != expected ) {
	log_error("%lu: read expected rec type %d, got %d\n",
		    recnum, expected, rec->rectype );
	return gpg_error (GPG_ERR_TRUSTDB);
    }
    p++;    /* skip reserved byte */
    switch( rec->rectype ) {
      case 0:  /* unused (free) record */
	break;
      case RECTYPE_VER: /* version record */
	if( memcmp(buf+1, "gpg", 3 ) ) {
	    log_error( _("%s: not a trustdb file\n"), db_name );
	    err = gpg_error (GPG_ERR_TRUSTDB);
	}
	p += 2; /* skip "gpg" */
	rec->r.ver.version  = *p++;
	rec->r.ver.marginals = *p++;
	rec->r.ver.completes = *p++;
	rec->r.ver.cert_depth = *p++;
	rec->r.ver.trust_model = *p++;
	rec->r.ver.min_cert_level = *p++;
	p += 2;
	rec->r.ver.created  = buf32_to_ulong (p); p += 4;
	rec->r.ver.nextcheck = buf32_to_ulong (p); p += 4;
	p += 4;
	p += 4;
	rec->r.ver.firstfree =buf32_to_ulong (p); p += 4;
	p += 4;
	rec->r.ver.trusthashtbl =buf32_to_ulong (p); p += 4;
	if( recnum ) {
	    log_error( _("%s: version record with recnum %lu\n"), db_name,
							     (ulong)recnum );
	    err = gpg_error (GPG_ERR_TRUSTDB);
	}
	else if( rec->r.ver.version != 3 ) {
	    log_error( _("%s: invalid file version %d\n"), db_name,
							rec->r.ver.version );
	    err = gpg_error (GPG_ERR_TRUSTDB);
	}
	break;
      case RECTYPE_FREE:
	rec->r.free.next  = buf32_to_ulong (p); p += 4;
	break;
      case RECTYPE_HTBL:
	for(i=0; i < ITEMS_PER_HTBL_RECORD; i++ ) {
	    rec->r.htbl.item[i] = buf32_to_ulong (p); p += 4;
	}
	break;
      case RECTYPE_HLST:
	rec->r.hlst.next = buf32_to_ulong (p); p += 4;
	for(i=0; i < ITEMS_PER_HLST_RECORD; i++ ) {
	    rec->r.hlst.rnum[i] = buf32_to_ulong (p); p += 4;
	}
	break;
      case RECTYPE_TRUST:
	memcpy( rec->r.trust.fingerprint, p, 20); p+=20;
        rec->r.trust.ownertrust = *p++;
        rec->r.trust.depth = *p++;
        rec->r.trust.min_ownertrust = *p++;
        p++;
	rec->r.trust.validlist = buf32_to_ulong (p); p += 4;
	break;
      case RECTYPE_VALID:
	memcpy( rec->r.valid.namehash, p, 20); p+=20;
        rec->r.valid.validity = *p++;
	rec->r.valid.next = buf32_to_ulong (p); p += 4;
	rec->r.valid.full_count = *p++;
	rec->r.valid.marginal_count = *p++;
	break;
      default:
	log_error( "%s: invalid record type %d at recnum %lu\n",
				   db_name, rec->rectype, (ulong)recnum );
	err = gpg_error (GPG_ERR_TRUSTDB);
	break;
    }

    return err;
}
Beispiel #17
0
/* Receive a WKS mail from FP and process it accordingly.  On success
 * the RESULT_CB is called with the mediatype and a stream with the
 * decrypted data. */
gpg_error_t
wks_receive (estream_t fp,
             gpg_error_t (*result_cb)(void *opaque,
                                      const char *mediatype,
                                      estream_t data),
             void *cb_data)
{
  gpg_error_t err;
  receive_ctx_t ctx;
  mime_parser_t parser;
  estream_t plaintext = NULL;
  int c;

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

  err = mime_parser_new (&parser, ctx);
  if (err)
    goto leave;
  if (opt.verbose > 1 || opt.debug)
    mime_parser_set_verbose (parser, opt.debug? 10: 1);
  mime_parser_set_new_part (parser, new_part);
  mime_parser_set_part_data (parser, part_data);
  mime_parser_set_collect_encrypted (parser, collect_encrypted);
  mime_parser_set_collect_signeddata (parser, collect_signeddata);
  mime_parser_set_collect_signature (parser, collect_signature);

  err = mime_parser_parse (parser, fp);
  if (err)
    goto leave;

  if (ctx->key_data)
    log_info ("key data found\n");
  if (ctx->wkd_data)
    log_info ("wkd data found\n");

  if (ctx->plaintext)
    {
      if (opt.verbose)
        log_info ("parsing decrypted message\n");
      plaintext = ctx->plaintext;
      ctx->plaintext = NULL;
      if (ctx->encrypted)
        es_rewind (ctx->encrypted);
      if (ctx->signeddata)
        es_rewind (ctx->signeddata);
      if (ctx->signature)
        es_rewind (ctx->signature);
      err = mime_parser_parse (parser, plaintext);
      if (err)
        return err;
    }

  if (!ctx->key_data && !ctx->wkd_data)
    {
      log_error ("no suitable data found in the message\n");
      err = gpg_error (GPG_ERR_NO_DATA);
      goto leave;
    }

  if (ctx->key_data)
    {
      if (opt.debug)
        {
          es_rewind (ctx->key_data);
          log_debug ("Key: '");
          log_printf ("\n");
          while ((c = es_getc (ctx->key_data)) != EOF)
            log_printf ("%c", c);
          log_printf ("'\n");
        }
      if (result_cb)
        {
          es_rewind (ctx->key_data);
          err = result_cb (cb_data, "application/pgp-keys", ctx->key_data);
          if (err)
            goto leave;
        }
    }
  if (ctx->wkd_data)
    {
      if (opt.debug)
        {
          es_rewind (ctx->wkd_data);
          log_debug ("WKD: '");
          log_printf ("\n");
          while ((c = es_getc (ctx->wkd_data)) != EOF)
            log_printf ("%c", c);
          log_printf ("'\n");
        }
      if (result_cb)
        {
          es_rewind (ctx->wkd_data);
          err = result_cb (cb_data, "application/vnd.gnupg.wks", ctx->wkd_data);
          if (err)
            goto leave;
        }
    }


 leave:
  es_fclose (plaintext);
  mime_parser_release (parser);
  es_fclose (ctx->encrypted);
  es_fclose (ctx->plaintext);
  es_fclose (ctx->signeddata);
  es_fclose (ctx->signature);
  es_fclose (ctx->key_data);
  es_fclose (ctx->wkd_data);
  xfree (ctx);
  return err;
}
Beispiel #18
0
static gpgme_error_t
status_handler (void *opaque, int fd)
{
  struct io_cb_data *data = (struct io_cb_data *) opaque;
  engine_g13_t g13 = (engine_g13_t) data->handler_value;
  gpgme_error_t err = 0;
  char *line;
  size_t linelen;

  do
    {
      err = assuan_read_line (g13->assuan_ctx, &line, &linelen);
      if (err)
	{
	  /* Try our best to terminate the connection friendly.  */
	  /*	  assuan_write_line (g13->assuan_ctx, "BYE"); */
          TRACE2 (DEBUG_CTX, "gpgme:status_handler", g13,
		  "fd 0x%x: error reading assuan line: %s",
                  fd, gpg_strerror (err));
	}
      else if (linelen >= 3
	       && line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
	       && (line[3] == '\0' || line[3] == ' '))
	{
	  if (line[3] == ' ')
	    err = atoi (&line[4]);
	  if (! err)
	    err = gpg_error (GPG_ERR_GENERAL);
          TRACE2 (DEBUG_CTX, "gpgme:status_handler", g13,
		  "fd 0x%x: ERR line: %s",
                  fd, err ? gpg_strerror (err) : "ok");

	  /* Command execution errors are not fatal, as we use
	     a session based protocol.  */
	  data->op_err = err;

	  /* The caller will do the rest (namely, call cancel_op,
	     which closes status_fd).  */
	  return 0;
	}
      else if (linelen >= 2
	       && line[0] == 'O' && line[1] == 'K'
	       && (line[2] == '\0' || line[2] == ' '))
	{
          TRACE1 (DEBUG_CTX, "gpgme:status_handler", g13,
		  "fd 0x%x: OK line", fd);

	  _gpgme_io_close (g13->status_cb.fd);
	  return 0;
	}
      else if (linelen > 2
	       && line[0] == 'D' && line[1] == ' ')
        {
	  /* We are using the colon handler even for plain inline data
             - strange name for that function but for historic reasons
             we keep it.  */
          /* FIXME We can't use this for binary data because we
             assume this is a string.  For the current usage of colon
             output it is correct.  */
          char *src = line + 2;
	  char *end = line + linelen;
	  char *dst = src;

          linelen = 0;
          while (src < end)
            {
              if (*src == '%' && src + 2 < end)
                {
                  /* Handle escaped characters.  */
                  ++src;
                  *dst++ = _gpgme_hextobyte (src);
                  src += 2;
                }
              else
                *dst++ = *src++;

              linelen++;
            }

          src = line + 2;
          if (linelen && g13->user.data_cb)
            err = g13->user.data_cb (g13->user.data_cb_value,
                                       src, linelen);
          else
            err = 0;

          TRACE2 (DEBUG_CTX, "gpgme:g13_status_handler", g13,
		  "fd 0x%x: D inlinedata; status from cb: %s",
                  fd, (g13->user.data_cb ?
                       (err? gpg_strerror (err):"ok"):"no callback"));

        }
      else if (linelen > 2
	       && line[0] == 'S' && line[1] == ' ')
	{
	  char *src;
	  char *args;

	  src = line + 2;
          while (*src == ' ')
            src++;

	  args = strchr (line + 2, ' ');
	  if (!args)
	    args = line + linelen; /* set to an empty string */
	  else
	    *(args++) = 0;

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

          if (g13->user.status_cb)
            err = g13->user.status_cb (g13->user.status_cb_value,
				       src, args);
          else
            err = 0;

          TRACE3 (DEBUG_CTX, "gpgme:g13_status_handler", g13,
		  "fd 0x%x: S line (%s) - status from cb: %s",
                  fd, line+2, (g13->user.status_cb ?
                               (err? gpg_strerror (err):"ok"):"no callback"));
	}
      else if (linelen >= 7
               && line[0] == 'I' && line[1] == 'N' && line[2] == 'Q'
               && line[3] == 'U' && line[4] == 'I' && line[5] == 'R'
               && line[6] == 'E'
               && (line[7] == '\0' || line[7] == ' '))
        {
          char *src;
	  char *args;

          for (src=line+7; *src == ' '; src++)
            ;

	  args = strchr (src, ' ');
	  if (!args)
	    args = line + linelen; /* Let it point to an empty string.  */
	  else
	    *(args++) = 0;

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

          err = default_inq_cb (g13, src, args);
          if (!err)
            {
              /* Flush and send END.  */
              err = assuan_send_data (g13->assuan_ctx, NULL, 0);
            }
          else if (gpg_err_code (err) == GPG_ERR_ASS_CANCELED)
            {
              /* Flush and send CANcel.  */
              err = assuan_send_data (g13->assuan_ctx, NULL, 1);
            }
          assuan_write_line (g13->assuan_ctx, "END");
        }
    }
  while (!err && assuan_pending_line (g13->assuan_ctx));

  return err;
}
Beispiel #19
0
/* Return the secret key as an S-Exp in RESULT after locating it using
   the GRIP.  Stores NULL at RESULT if the operation shall be diverted
   to a token; in this case an allocated S-expression with the
   shadow_info part from the file is stored at SHADOW_INFO.
   CACHE_MODE defines now the cache shall be used.  DESC_TEXT may be
   set to present a custom description for the pinentry.  LOOKUP_TTL
   is an optional function to convey a TTL to the cache manager; we do
   not simply pass the TTL value because the value is only needed if
   an unprotect action was needed and looking up the TTL may have some
   overhead (e.g. scanning the sshcontrol file).  If a CACHE_NONCE is
   given that cache item is first tried to get a passphrase.  If
   R_PASSPHRASE is not NULL, the function succeeded and the key was
   protected the used passphrase (entered or from the cache) is stored
   there; if not NULL will be stored.  The caller needs to free the
   returned passphrase.   */
gpg_error_t
agent_key_from_file (ctrl_t ctrl, const char *cache_nonce,
                     const char *desc_text,
                     const unsigned char *grip, unsigned char **shadow_info,
                     cache_mode_t cache_mode, lookup_ttl_t lookup_ttl,
                     gcry_sexp_t *result, char **r_passphrase)
{
  int rc;
  unsigned char *buf;
  size_t len, buflen, erroff;
  gcry_sexp_t s_skey;
  int got_shadow_info = 0;

  *result = NULL;
  if (shadow_info)
    *shadow_info = NULL;
  if (r_passphrase)
    *r_passphrase = NULL;

  rc = read_key_file (grip, &s_skey);
  if (rc)
    return rc;

  /* For use with the protection functions we also need the key as an
     canonical encoded S-expression in a buffer.  Create this buffer
     now.  */
  rc = make_canon_sexp (s_skey, &buf, &len);
  if (rc)
    return rc;

  switch (agent_private_key_type (buf))
    {
    case PRIVATE_KEY_CLEAR:
      break; /* no unprotection needed */
    case PRIVATE_KEY_PROTECTED:
      {
	char *desc_text_final;
	char *comment = NULL;

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

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

        desc_text_final = NULL;
	if (desc_text)
          rc = modify_description (desc_text, comment? comment:"", s_skey,
                                   &desc_text_final);
        gcry_free (comment);

	if (!rc)
	  {
	    rc = unprotect (ctrl, cache_nonce, desc_text_final, &buf, grip,
                            cache_mode, lookup_ttl, r_passphrase);
	    if (rc)
	      log_error ("failed to unprotect the secret key: %s\n",
			 gpg_strerror (rc));
	  }

	xfree (desc_text_final);
      }
      break;
    case PRIVATE_KEY_SHADOWED:
      if (shadow_info)
        {
          const unsigned char *s;
          size_t n;

          rc = agent_get_shadow_info (buf, &s);
          if (!rc)
            {
              n = gcry_sexp_canon_len (s, 0, NULL,NULL);
              assert (n);
              *shadow_info = xtrymalloc (n);
              if (!*shadow_info)
                rc = out_of_core ();
              else
                {
                  memcpy (*shadow_info, s, n);
                  rc = 0;
                  got_shadow_info = 1;
                }
            }
          if (rc)
            log_error ("get_shadow_info failed: %s\n", gpg_strerror (rc));
        }
      else
        rc = gpg_error (GPG_ERR_UNUSABLE_SECKEY);
      break;
    default:
      log_error ("invalid private key format\n");
      rc = gpg_error (GPG_ERR_BAD_SECKEY);
      break;
    }
  gcry_sexp_release (s_skey);
  s_skey = NULL;
  if (rc || got_shadow_info)
    {
      xfree (buf);
      if (r_passphrase)
        {
          xfree (*r_passphrase);
          *r_passphrase = NULL;
        }
      return rc;
    }

  buflen = gcry_sexp_canon_len (buf, 0, NULL, NULL);
  rc = gcry_sexp_sscan (&s_skey, &erroff, (char*)buf, buflen);
  wipememory (buf, buflen);
  xfree (buf);
  if (rc)
    {
      log_error ("failed to build S-Exp (off=%u): %s\n",
                 (unsigned int)erroff, gpg_strerror (rc));
      if (r_passphrase)
        {
          xfree (*r_passphrase);
          *r_passphrase = NULL;
        }
      return rc;
    }

  *result = s_skey;
  return 0;
}
Beispiel #20
0
/* Return 0 if the cert is usable for encryption.  A MODE of 0 checks
   for signing, a MODE of 1 checks for encryption, a MODE of 2 checks
   for verification and a MODE of 3 for decryption (just for
   debugging).  MODE 4 is for certificate signing, MODE 5 for OCSP
   response signing, MODE 6 is for CRL signing. */
static int
cert_usage_p (ksba_cert_t cert, int mode)
{
  gpg_error_t err;
  unsigned int use;
  char *extkeyusages;
  int have_ocsp_signing = 0;

  err = ksba_cert_get_ext_key_usages (cert, &extkeyusages);
  if (gpg_err_code (err) == GPG_ERR_NO_DATA)
    err = 0; /* No policy given. */
  if (!err)
    {
      unsigned int extusemask = ~0; /* Allow all. */

      if (extkeyusages)
        {
          char *p, *pend;
          int any_critical = 0;

          extusemask = 0;

          p = extkeyusages;
          while (p && (pend=strchr (p, ':')))
            {
              *pend++ = 0;
              /* Only care about critical flagged usages. */
              if ( *pend == 'C' )
                {
                  any_critical = 1;
                  if ( !strcmp (p, oid_kp_serverAuth))
                    extusemask |= (KSBA_KEYUSAGE_DIGITAL_SIGNATURE
                                   | KSBA_KEYUSAGE_KEY_ENCIPHERMENT
                                   | KSBA_KEYUSAGE_KEY_AGREEMENT);
                  else if ( !strcmp (p, oid_kp_clientAuth))
                    extusemask |= (KSBA_KEYUSAGE_DIGITAL_SIGNATURE
                                   | KSBA_KEYUSAGE_KEY_AGREEMENT);
                  else if ( !strcmp (p, oid_kp_codeSigning))
                    extusemask |= (KSBA_KEYUSAGE_DIGITAL_SIGNATURE);
                  else if ( !strcmp (p, oid_kp_emailProtection))
                    extusemask |= (KSBA_KEYUSAGE_DIGITAL_SIGNATURE
                                   | KSBA_KEYUSAGE_NON_REPUDIATION
                                   | KSBA_KEYUSAGE_KEY_ENCIPHERMENT
                                   | KSBA_KEYUSAGE_KEY_AGREEMENT);
                  else if ( !strcmp (p, oid_kp_timeStamping))
                    extusemask |= (KSBA_KEYUSAGE_DIGITAL_SIGNATURE
                                   | KSBA_KEYUSAGE_NON_REPUDIATION);
                }

              /* This is a hack to cope with OCSP.  Note that we do
                 not yet fully comply with the requirements and that
                 the entire CRL/OCSP checking thing should undergo a
                 thorough review and probably redesign. */
              if ( !strcmp (p, oid_kp_ocspSigning))
                have_ocsp_signing = 1;

              if ((p = strchr (pend, '\n')))
                p++;
            }
          ksba_free (extkeyusages);
          extkeyusages = NULL;

          if (!any_critical)
            extusemask = ~0; /* Reset to the don't care mask. */
        }


      err = ksba_cert_get_key_usage (cert, &use);
      if (gpg_err_code (err) == GPG_ERR_NO_DATA)
        {
          err = 0;
          if (opt.verbose && mode < 2)
            log_info (_("no key usage specified - assuming all usages\n"));
          use = ~0;
        }

      /* Apply extKeyUsage. */
      use &= extusemask;

    }
  if (err)
    {
      log_error (_("error getting key usage information: %s\n"),
                 gpg_strerror (err));
      ksba_free (extkeyusages);
      return err;
    }

  if (mode == 4)
    {
      if ((use & (KSBA_KEYUSAGE_KEY_CERT_SIGN)))
        return 0;
      log_info (_("certificate should not have "
                  "been used for certification\n"));
      return gpg_error (GPG_ERR_WRONG_KEY_USAGE);
    }

  if (mode == 5)
    {
      if (use != ~0
          && (have_ocsp_signing
              || (use & (KSBA_KEYUSAGE_KEY_CERT_SIGN
                         |KSBA_KEYUSAGE_CRL_SIGN))))
        return 0;
      log_info (_("certificate should not have "
                  "been used for OCSP response signing\n"));
      return gpg_error (GPG_ERR_WRONG_KEY_USAGE);
    }

  if (mode == 6)
    {
      if ((use & (KSBA_KEYUSAGE_CRL_SIGN)))
        return 0;
      log_info (_("certificate should not have "
                  "been used for CRL signing\n"));
      return gpg_error (GPG_ERR_WRONG_KEY_USAGE);
    }

  if ((use & ((mode&1)?
              (KSBA_KEYUSAGE_KEY_ENCIPHERMENT|KSBA_KEYUSAGE_DATA_ENCIPHERMENT):
              (KSBA_KEYUSAGE_DIGITAL_SIGNATURE|KSBA_KEYUSAGE_NON_REPUDIATION)))
      )
    return 0;

  log_info (mode==3? _("certificate should not have been used "
                       "for encryption\n"):
            mode==2? _("certificate should not have been used for signing\n"):
            mode==1? _("certificate is not usable for encryption\n"):
                     _("certificate is not usable for signing\n"));
  return gpg_error (GPG_ERR_WRONG_KEY_USAGE);
}
Beispiel #21
0
/* Return the public key for the keygrip GRIP.  The result is stored
   at RESULT.  This function extracts the public key from the private
   key database.  On failure an error code is returned and NULL stored
   at RESULT. */
gpg_error_t
agent_public_key_from_file (ctrl_t ctrl,
                            const unsigned char *grip,
                            gcry_sexp_t *result)
{
  gpg_error_t err;
  int i, idx;
  gcry_sexp_t s_skey;
  char algoname[6];
  char elems[6];
  gcry_sexp_t uri_sexp, comment_sexp;
  const char *uri, *comment;
  size_t uri_length, comment_length;
  char *format, *p;
  void *args[4+2+2+1]; /* Size is max. # of elements + 2 for uri + 2
                           for comment + end-of-list.  */
  int argidx;
  gcry_sexp_t list, l2;
  const char *s;
  gcry_mpi_t *array;

  (void)ctrl;

  *result = NULL;

  err = read_key_file (grip, &s_skey);
  if (err)
    return err;

  err = key_parms_from_sexp (s_skey, &list,
                            algoname, sizeof algoname,
                            elems, sizeof elems);
  if (err)
    {
      gcry_sexp_release (s_skey);
      return err;
    }

  /* Allocate an array for the parameters and copy them out of the
     secret key.   FIXME: We should have a generic copy function. */
  array = xtrycalloc (strlen(elems) + 1, sizeof *array);
  if (!array)
    {
      err = gpg_error_from_syserror ();
      gcry_sexp_release (list);
      gcry_sexp_release (s_skey);
      return err;
    }

  for (idx=0, s=elems; *s; s++, idx++ )
    {
      l2 = gcry_sexp_find_token (list, s, 1);
      if (!l2)
        {
          /* Required parameter not found.  */
          for (i=0; i<idx; i++)
            gcry_mpi_release (array[i]);
          xfree (array);
          gcry_sexp_release (list);
          gcry_sexp_release (s_skey);
          return gpg_error (GPG_ERR_BAD_SECKEY);
	}
      array[idx] = gcry_sexp_nth_mpi (l2, 1, GCRYMPI_FMT_USG);
      gcry_sexp_release (l2);
      if (!array[idx])
        {
          /* Required parameter is invalid. */
          for (i=0; i<idx; i++)
            gcry_mpi_release (array[i]);
          xfree (array);
          gcry_sexp_release (list);
          gcry_sexp_release (s_skey);
          return gpg_error (GPG_ERR_BAD_SECKEY);
	}
    }
  gcry_sexp_release (list);
  list = NULL;

  uri = NULL;
  uri_length = 0;
  uri_sexp = gcry_sexp_find_token (s_skey, "uri", 0);
  if (uri_sexp)
    uri = gcry_sexp_nth_data (uri_sexp, 1, &uri_length);

  comment = NULL;
  comment_length = 0;
  comment_sexp = gcry_sexp_find_token (s_skey, "comment", 0);
  if (comment_sexp)
    comment = gcry_sexp_nth_data (comment_sexp, 1, &comment_length);

  gcry_sexp_release (s_skey);
  s_skey = NULL;


  /* FIXME: The following thing is pretty ugly code; we should
     investigate how to make it cleaner.  Probably code to handle
     canonical S-expressions in a memory buffer is better suited for
     such a task.  After all that is what we do in protect.c.  Neeed
     to find common patterns and write a straightformward API to use
     them.  */
  assert (sizeof (size_t) <= sizeof (void*));

  format = xtrymalloc (15+7*strlen (elems)+10+15+1+1);
  if (!format)
    {
      err = gpg_error_from_syserror ();
      for (i=0; array[i]; i++)
        gcry_mpi_release (array[i]);
      xfree (array);
      gcry_sexp_release (uri_sexp);
      gcry_sexp_release (comment_sexp);
      return err;
    }

  argidx = 0;
  p = stpcpy (stpcpy (format, "(public-key("), algoname);
  for (idx=0, s=elems; *s; s++, idx++ )
    {
      *p++ = '(';
      *p++ = *s;
      p = stpcpy (p, " %m)");
      assert (argidx < DIM (args));
      args[argidx++] = &array[idx];
    }
  *p++ = ')';
  if (uri)
    {
      p = stpcpy (p, "(uri %b)");
      assert (argidx+1 < DIM (args));
      args[argidx++] = (void *)&uri_length;
      args[argidx++] = (void *)&uri;
    }
  if (comment)
    {
      p = stpcpy (p, "(comment %b)");
      assert (argidx+1 < DIM (args));
      args[argidx++] = (void *)&comment_length;
      args[argidx++] = (void*)&comment;
    }
  *p++ = ')';
  *p = 0;
  assert (argidx < DIM (args));
  args[argidx] = NULL;

  err = gcry_sexp_build_array (&list, NULL, format, args);
  xfree (format);
  for (i=0; array[i]; i++)
    gcry_mpi_release (array[i]);
  xfree (array);
  gcry_sexp_release (uri_sexp);
  gcry_sexp_release (comment_sexp);

  if (!err)
    *result = list;
  return err;
}
Beispiel #22
0
/* Helper for validate_cert_chain.  */
static gpg_error_t
check_revocations (ctrl_t ctrl, chain_item_t chain)
{
  gpg_error_t err = 0;
  int any_revoked = 0;
  int any_no_crl = 0;
  int any_crl_too_old = 0;
  chain_item_t ci;

  assert (ctrl->check_revocations_nest_level >= 0);
  assert (chain);

  if (ctrl->check_revocations_nest_level > 10)
    {
      log_error (_("CRL checking too deeply nested\n"));
      return gpg_error(GPG_ERR_BAD_CERT_CHAIN);
    }
  ctrl->check_revocations_nest_level++;


  for (ci=chain; ci; ci = ci->next)
    {
      assert (ci->cert);
      if (ci == chain)
        {
          /* It does not make sense to check the root certificate for
             revocations.  In almost all cases this will lead to a
             catch-22 as the root certificate is the final trust
             anchor for the certificates and the CRLs.  We expect the
             user to remove root certificates from the list of trusted
             certificates in case they have been revoked. */
          if (opt.verbose)
            cert_log_name (_("not checking CRL for"), ci->cert);
          continue;
        }

      if (opt.verbose)
        cert_log_name (_("checking CRL for"), ci->cert);
      err = crl_cache_cert_isvalid (ctrl, ci->cert, 0);
      if (gpg_err_code (err) == GPG_ERR_NO_CRL_KNOWN)
        {
          err = crl_cache_reload_crl (ctrl, ci->cert);
          if (!err)
            err = crl_cache_cert_isvalid (ctrl, ci->cert, 0);
        }
      switch (gpg_err_code (err))
        {
        case 0: err = 0; break;
        case GPG_ERR_CERT_REVOKED: any_revoked = 1; err = 0; break;
        case GPG_ERR_NO_CRL_KNOWN: any_no_crl = 1; err = 0; break;
        case GPG_ERR_CRL_TOO_OLD: any_crl_too_old = 1; err = 0; break;
        default: break;
        }
    }
  ctrl->check_revocations_nest_level--;


  if (err)
    ;
  else if (any_revoked)
    err = gpg_error (GPG_ERR_CERT_REVOKED);
  else if (any_no_crl)
    err = gpg_error (GPG_ERR_NO_CRL_KNOWN);
  else if (any_crl_too_old)
    err = gpg_error (GPG_ERR_CRL_TOO_OLD);
  else
    err = 0;
  return err;
}
Beispiel #23
0
/* Create a new, empty signature notation data object.  */
gpgme_error_t
_gpgme_sig_notation_create(gpgme_sig_notation_t *notationp,
                           const char *name, int name_len,
                           const char *value, int value_len,
                           gpgme_sig_notation_flags_t flags)
{
    gpgme_error_t err = 0;
    gpgme_sig_notation_t notation;

    /* Currently, we require all notations to be human-readable.  */
    if(name && !(flags & GPGME_SIG_NOTATION_HUMAN_READABLE))
        return gpg_error(GPG_ERR_INV_VALUE);

    notation = calloc(1, sizeof(*notation));
    if(!notation)
        return gpg_error_from_errno(errno);

    /* This is critical.  We want to reliably identify policy URLs by
       using a NULL pointer for NAME.  So all notations must have a NAME
       string, even if it is empty.  */
    if(name)
    {
        /* We add a trailing '\0' for stringification in the good
        case.  */
        notation->name = malloc(name_len + 1);
        if(!notation->name)
        {
            err = gpg_error_from_errno(errno);
            goto err;
        }

        memcpy(notation->name, name, name_len);
        notation->name[name_len] = '\0';
        notation->name_len = name_len;
    }

    if(value)
    {
        /* We add a trailing '\0' for stringification in the good
        case.  */
        notation->value = malloc(value_len + 1);
        if(!notation->value)
        {
            err = gpg_error_from_errno(errno);
            goto err;
        }

        memcpy(notation->value, value, value_len);
        notation->value[value_len] = '\0';
        notation->value_len = value_len;
    }

    sig_notation_set_flags(notation, flags);

    *notationp = notation;
    return 0;

err:
    _gpgme_sig_notation_free(notation);
    return err;
}
Beispiel #24
0
/* Validate the certificate CHAIN up to the trust anchor. Optionally
   return the closest expiration time in R_EXPTIME (this is useful for
   caching issues).  MODE is one of the VALIDATE_MODE_* constants.

   If R_TRUST_ANCHOR is not NULL and the validation would fail only
   because the root certificate is not trusted, the hexified
   fingerprint of that root certificate is stored at R_TRUST_ANCHOR
   and success is returned.  The caller needs to free the value at
   R_TRUST_ANCHOR; in all other cases NULL is stored there.  */
gpg_error_t
validate_cert_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t r_exptime,
                     int mode, char **r_trust_anchor)
{
  gpg_error_t err = 0;
  int depth, maxdepth;
  char *issuer = NULL;
  char *subject = NULL;
  ksba_cert_t subject_cert = NULL, issuer_cert = NULL;
  ksba_isotime_t current_time;
  ksba_isotime_t exptime;
  int any_expired = 0;
  int any_no_policy_match = 0;
  chain_item_t chain;


  if (r_exptime)
    *r_exptime = 0;
  *exptime = 0;

  if (r_trust_anchor)
    *r_trust_anchor = NULL;

  if (!opt.system_daemon)
    {
      /* For backward compatibility we only do this in daemon mode.  */
      log_info (_("running in compatibility mode - "
                  "certificate chain not checked!\n"));
      return 0; /* Okay. */
    }

  if (DBG_X509)
    dump_cert ("subject", cert);

  /* May the target certificate be used for this purpose?  */
  switch (mode)
    {
    case VALIDATE_MODE_OCSP:
      err = cert_use_ocsp_p (cert);
      break;
    case VALIDATE_MODE_CRL:
    case VALIDATE_MODE_CRL_RECURSIVE:
      err = cert_use_crl_p (cert);
      break;
    default:
      err = 0;
      break;
    }
  if (err)
    return err;

  /* If we already validated the certificate not too long ago, we can
     avoid the excessive computations and lookups unless the caller
     asked for the expiration time.  */
  if (!r_exptime)
    {
      size_t buflen;
      time_t validated_at;

      err = ksba_cert_get_user_data (cert, "validated_at",
                                     &validated_at, sizeof (validated_at),
                                     &buflen);
      if (err || buflen != sizeof (validated_at) || !validated_at)
        err = 0; /* Not available or other error. */
      else
        {
          /* If the validation is not older than 30 minutes we are ready. */
          if (validated_at < gnupg_get_time () + (30*60))
            {
              if (opt.verbose)
                log_info ("certificate is good (cached)\n");
              /* Note, that we can't jump to leave here as this would
                 falsely updated the validation timestamp.  */
              return 0;
            }
        }
    }

  /* Get the current time. */
  gnupg_get_isotime (current_time);

  /* We walk up the chain until we find a trust anchor. */
  subject_cert = cert;
  maxdepth = 10;
  chain = NULL;
  depth = 0;
  for (;;)
    {
      /* Get the subject and issuer name from the current
         certificate.  */
      ksba_free (issuer);
      ksba_free (subject);
      issuer = ksba_cert_get_issuer (subject_cert, 0);
      subject = ksba_cert_get_subject (subject_cert, 0);

      if (!issuer)
        {
          log_error (_("no issuer found in certificate\n"));
          err = gpg_error (GPG_ERR_BAD_CERT);
          goto leave;
        }

      /* Handle the notBefore and notAfter timestamps.  */
      {
        ksba_isotime_t not_before, not_after;

        err = ksba_cert_get_validity (subject_cert, 0, not_before);
        if (!err)
          err = ksba_cert_get_validity (subject_cert, 1, not_after);
        if (err)
          {
            log_error (_("certificate with invalid validity: %s"),
                       gpg_strerror (err));
            err = gpg_error (GPG_ERR_BAD_CERT);
            goto leave;
          }

        /* Keep track of the nearest expiration time in EXPTIME.  */
        if (*not_after)
          {
            if (!*exptime)
              gnupg_copy_time (exptime, not_after);
            else if (strcmp (not_after, exptime) < 0 )
              gnupg_copy_time (exptime, not_after);
          }

        /* Check whether the certificate is already valid.  */
        if (*not_before && strcmp (current_time, not_before) < 0 )
          {
            log_error (_("certificate not yet valid"));
            log_info ("(valid from ");
            dump_isotime (not_before);
            log_printf (")\n");
            err = gpg_error (GPG_ERR_CERT_TOO_YOUNG);
            goto leave;
          }

        /* Now check whether the certificate has expired.  */
        if (*not_after && strcmp (current_time, not_after) > 0 )
          {
            log_error (_("certificate has expired"));
            log_info ("(expired at ");
            dump_isotime (not_after);
            log_printf (")\n");
            any_expired = 1;
          }
      }

      /* Do we have any critical extensions in the certificate we
         can't handle? */
      err = unknown_criticals (subject_cert);
      if (err)
        goto leave; /* yes. */

      /* Check that given policies are allowed.  */
      err = check_cert_policy (subject_cert);
      if (gpg_err_code (err) == GPG_ERR_NO_POLICY_MATCH)
        {
          any_no_policy_match = 1;
          err = 0;
        }
      else if (err)
        goto leave;

      /* Is this a self-signed certificate? */
      if (is_root_cert ( subject_cert, issuer, subject))
        {
          /* Yes, this is our trust anchor.  */
          if (check_cert_sig (subject_cert, subject_cert) )
            {
              log_error (_("selfsigned certificate has a BAD signature"));
              err = gpg_error (depth? GPG_ERR_BAD_CERT_CHAIN
                                    : GPG_ERR_BAD_CERT);
              goto leave;
            }

          /* Is this certificate allowed to act as a CA.  */
          err = allowed_ca (subject_cert, NULL);
          if (err)
            goto leave;  /* No. */

          err = is_trusted_cert (subject_cert);
          if (!err)
            ; /* Yes we trust this cert.  */
          else if (gpg_err_code (err) == GPG_ERR_NOT_TRUSTED)
            {
              char *fpr;

              log_error (_("root certificate is not marked trusted"));
              fpr = get_fingerprint_hexstring (subject_cert);
              log_info (_("fingerprint=%s\n"), fpr? fpr : "?");
              dump_cert ("issuer", subject_cert);
              if (r_trust_anchor)
                {
                  /* Caller wants to do another trustiness check.  */
                  *r_trust_anchor = fpr;
                  err = 0;
                }
              else
                xfree (fpr);
            }
          else
            {
              log_error (_("checking trustworthiness of "
                           "root certificate failed: %s\n"),
                         gpg_strerror (err));
            }
          if (err)
            goto leave;

          /* Prepend the certificate to our list.  */
          {
            chain_item_t ci;

            ci = xtrycalloc (1, sizeof *ci);
            if (!ci)
              {
                err = gpg_error_from_errno (errno);
                goto leave;
              }
            ksba_cert_ref (subject_cert);
            ci->cert = subject_cert;
            cert_compute_fpr (subject_cert, ci->fpr);
            ci->next = chain;
            chain = ci;
          }

          if (opt.verbose)
            {
              if (r_trust_anchor && *r_trust_anchor)
                log_info ("root certificate is good but not trusted\n");
              else
                log_info ("root certificate is good and trusted\n");
            }

          break;  /* Okay: a self-signed certicate is an end-point. */
        }

      /* To avoid loops, we use an arbitrary limit on the length of
         the chain. */
      depth++;
      if (depth > maxdepth)
        {
          log_error (_("certificate chain too long\n"));
          err = gpg_error (GPG_ERR_BAD_CERT_CHAIN);
          goto leave;
        }

      /* Find the next cert up the tree. */
      ksba_cert_release (issuer_cert); issuer_cert = NULL;
      err = find_issuing_cert (ctrl, subject_cert, &issuer_cert);
      if (err)
        {
          if (gpg_err_code (err) == GPG_ERR_NOT_FOUND)
            {
              log_error (_("issuer certificate not found"));
              log_info ("issuer certificate: #/");
              dump_string (issuer);
              log_printf ("\n");
            }
          else
            log_error (_("issuer certificate not found: %s\n"),
                         gpg_strerror (err));
          /* Use a better understandable error code.  */
          err = gpg_error (GPG_ERR_MISSING_ISSUER_CERT);
          goto leave;
        }

/*     try_another_cert: */
      if (DBG_X509)
        {
          log_debug ("got issuer's certificate:\n");
          dump_cert ("issuer", issuer_cert);
        }

      /* Now check the signature of the certificate.  Well, we
         should delay this until later so that faked certificates
         can't be turned into a DoS easily.  */
      err = check_cert_sig (issuer_cert, subject_cert);
      if (err)
        {
          log_error (_("certificate has a BAD signature"));
#if 0
          if (gpg_err_code (err) == GPG_ERR_BAD_SIGNATURE)
            {
              /* We now try to find other issuer certificates which
                 might have been used.  This is required because some
                 CAs are reusing the issuer and subject DN for new
                 root certificates without using a  authorityKeyIdentifier. */
              rc = find_up (kh, subject_cert, issuer, 1);
              if (!rc)
                {
                  ksba_cert_t tmp_cert;

                  rc = keydb_get_cert (kh, &tmp_cert);
                  if (rc || !compare_certs (issuer_cert, tmp_cert))
                    {
                      /* The find next did not work or returned an
                         identical certificate.  We better stop here
                         to avoid infinite checks. */
                      rc = gpg_error (GPG_ERR_BAD_SIGNATURE);
                      ksba_cert_release (tmp_cert);
                    }
                  else
                    {
                      do_list (0, lm, fp, _("found another possible matching "
                                            "CA certificate - trying again"));
                      ksba_cert_release (issuer_cert);
                      issuer_cert = tmp_cert;
                      goto try_another_cert;
                    }
                }
            }
#endif
          /* We give a more descriptive error code than the one
             returned from the signature checking. */
          err = gpg_error (GPG_ERR_BAD_CERT_CHAIN);
          goto leave;
        }

      /* Check that the length of the chain is not longer than allowed
         by the CA.  */
      {
        int chainlen;

        err = allowed_ca (issuer_cert, &chainlen);
        if (err)
          goto leave;
        if (chainlen >= 0 && (depth - 1) > chainlen)
          {
            log_error (_("certificate chain longer than allowed by CA (%d)"),
                       chainlen);
            err = gpg_error (GPG_ERR_BAD_CERT_CHAIN);
            goto leave;
          }
      }

      /* May that certificate be used for certification? */
      err = cert_use_cert_p (issuer_cert);
      if (err)
        goto leave;  /* No.  */

      /* Prepend the certificate to our list.  */
      {
        chain_item_t ci;

        ci = xtrycalloc (1, sizeof *ci);
        if (!ci)
          {
            err = gpg_error_from_errno (errno);
            goto leave;
          }
        ksba_cert_ref (subject_cert);
        ci->cert = subject_cert;
        cert_compute_fpr (subject_cert, ci->fpr);
        ci->next = chain;
        chain = ci;
      }

      if (opt.verbose)
        log_info (_("certificate is good\n"));

      /* Now to the next level up.  */
      subject_cert = issuer_cert;
      issuer_cert = NULL;
    }

  if (!err)
    { /* If we encountered an error somewhere during the checks, set
         the error code to the most critical one */
      if (any_expired)
        err = gpg_error (GPG_ERR_CERT_EXPIRED);
      else if (any_no_policy_match)
        err = gpg_error (GPG_ERR_NO_POLICY_MATCH);
    }

  if (!err && opt.verbose)
    {
      chain_item_t citem;

      log_info (_("certificate chain is good\n"));
      for (citem = chain; citem; citem = citem->next)
        cert_log_name ("  certificate", citem->cert);
    }

  if (!err && mode != VALIDATE_MODE_CRL)
    { /* Now that everything is fine, walk the chain and check each
         certificate for revocations.

         1. item in the chain  - The root certificate.
         2. item               - the CA below the root
         last item             - the target certificate.

         Now for each certificate in the chain check whether it has
         been included in a CRL and thus be revoked.  We don't do OCSP
         here because this does not seem to make much sense.  This
         might become a recursive process and we should better cache
         our validity results to avoid double work.  Far worse a
         catch-22 may happen for an improper setup hierarchy and we
         need a way to break up such a deadlock. */
      err = check_revocations (ctrl, chain);
    }

  if (!err && opt.verbose)
    {
      if (r_trust_anchor && *r_trust_anchor)
        log_info ("target certificate may be valid\n");
      else
        log_info ("target certificate is valid\n");
    }
  else if (err && opt.verbose)
    log_info ("target certificate is NOT valid\n");


 leave:
  if (!err && !(r_trust_anchor && *r_trust_anchor))
    {
      /* With no error we can update the validation cache.  We do this
         for all certificates in the chain.  Note that we can't use
         the cache if the caller requested to check the trustiness of
         the root certificate himself.  Adding such a feature would
         require us to also store the fingerprint of root
         certificate.  */
      chain_item_t citem;
      time_t validated_at = gnupg_get_time ();

      for (citem = chain; citem; citem = citem->next)
        {
          err = ksba_cert_set_user_data (citem->cert, "validated_at",
                                         &validated_at, sizeof (validated_at));
          if (err)
            {
              log_error ("set_user_data(validated_at) failed: %s\n",
                         gpg_strerror (err));
              err = 0;
            }
        }
    }

  if (r_exptime)
    gnupg_copy_time (r_exptime, exptime);
  ksba_free (issuer);
  ksba_free (subject);
  ksba_cert_release (issuer_cert);
  if (subject_cert != cert)
    ksba_cert_release (subject_cert);
  while (chain)
    {
      chain_item_t ci_next = chain->next;
      if (chain->cert)
        ksba_cert_release (chain->cert);
      xfree (chain);
      chain = ci_next;
    }
  if (err && r_trust_anchor && *r_trust_anchor)
    {
      xfree (*r_trust_anchor);
      *r_trust_anchor = NULL;
    }
  return err;
}
Beispiel #25
0
/* Fire up a new GPG.  Handle the server's initial greeting.  Returns
   0 on success and stores the assuan context at R_CTX.  */
static gpg_error_t
start_gpg (ctrl_t ctrl, const char *gpg_program, strlist_t gpg_arguments,
           int input_fd, int output_fd, assuan_context_t *r_ctx)
{
  gpg_error_t err;
  assuan_context_t ctx = NULL;
  const char *pgmname;
  const char **argv;
  assuan_fd_t no_close_list[5];
  int i;
  char line[ASSUAN_LINELENGTH];

  (void)ctrl;

  *r_ctx = NULL;

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

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

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

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

  argv = xtrycalloc (strlist_length (gpg_arguments) + 3, sizeof *argv);
  if (argv == NULL)
    {
      err = my_error_from_syserror ();
      return err;
    }
  i = 0;
  argv[i++] = pgmname;
  argv[i++] = "--server";
  for (; gpg_arguments; gpg_arguments = gpg_arguments->next)
    argv[i++] = gpg_arguments->d;
  argv[i++] = NULL;

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

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

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

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

  *r_ctx = ctx;
  return 0;
}
Beispiel #26
0
/* Check the signature on CERT using the ISSUER_CERT.  This function
   does only test the cryptographic signature and nothing else.  It is
   assumed that the ISSUER_CERT is valid. */
static gpg_error_t
check_cert_sig (ksba_cert_t issuer_cert, ksba_cert_t cert)
{
  gpg_error_t err;
  const char *algoid;
  gcry_md_hd_t md;
  int i, algo;
  ksba_sexp_t p;
  size_t n;
  gcry_sexp_t s_sig, s_hash, s_pkey;
  const char *s;
  char algo_name[16+1]; /* hash algorithm name converted to lower case. */
  int digestlen;
  unsigned char *digest;

  /* Hash the target certificate using the algorithm from that certificate.  */
  algoid = ksba_cert_get_digest_algo (cert);
  algo = gcry_md_map_name (algoid);
  if (!algo)
    {
      log_error (_("unknown hash algorithm '%s'\n"), algoid? algoid:"?");
      return gpg_error (GPG_ERR_GENERAL);
    }
  s = gcry_md_algo_name (algo);
  for (i=0; *s && i < sizeof algo_name - 1; s++, i++)
    algo_name[i] = tolower (*s);
  algo_name[i] = 0;

  err = gcry_md_open (&md, algo, 0);
  if (err)
    {
      log_error ("md_open failed: %s\n", gpg_strerror (err));
      return err;
    }
  if (DBG_HASHING)
    gcry_md_debug (md, "hash.cert");

  err = ksba_cert_hash (cert, 1, HASH_FNC, md);
  if (err)
    {
      log_error ("ksba_cert_hash failed: %s\n", gpg_strerror (err));
      gcry_md_close (md);
      return err;
    }
  gcry_md_final (md);

  /* Get the signature value out of the target certificate.  */
  p = ksba_cert_get_sig_val (cert);
  n = gcry_sexp_canon_len (p, 0, NULL, NULL);
  if (!n)
    {
      log_error ("libksba did not return a proper S-Exp\n");
      gcry_md_close (md);
      ksba_free (p);
      return gpg_error (GPG_ERR_BUG);
    }
  if (DBG_CRYPTO)
    {
      int j;
      log_debug ("signature value:");
      for (j=0; j < n; j++)
        log_printf (" %02X", p[j]);
      log_printf ("\n");
    }

  err = gcry_sexp_sscan ( &s_sig, NULL, p, n);
  ksba_free (p);
  if (err)
    {
      log_error ("gcry_sexp_scan failed: %s\n", gpg_strerror (err));
      gcry_md_close (md);
      return err;
    }

  /* Get the public key from the issuer certificate.  */
  p = ksba_cert_get_public_key (issuer_cert);
  n = gcry_sexp_canon_len (p, 0, NULL, NULL);
  if (!n)
    {
      log_error ("libksba did not return a proper S-Exp\n");
      gcry_md_close (md);
      ksba_free (p);
      gcry_sexp_release (s_sig);
      return gpg_error (GPG_ERR_BUG);
    }
  err = gcry_sexp_sscan ( &s_pkey, NULL, p, n);
  ksba_free (p);
  if (err)
    {
      log_error ("gcry_sexp_scan failed: %s\n", gpg_strerror (err));
      gcry_md_close (md);
      gcry_sexp_release (s_sig);
      return err;
    }


  /* Prepare the values for signature verification. At this point we
     have these values:

     S_PKEY    - S-expression with the issuer's public key.
     S_SIG     - Signature value as given in the certrificate.
     MD        - Finalized hash context with hash of the certificate.
     ALGO_NAME - Lowercase hash algorithm name
   */
  digestlen = gcry_md_get_algo_dlen (algo);
  digest = gcry_md_read (md, algo);
  if (pk_algo_from_sexp (s_pkey) == GCRY_PK_DSA)
    {
      if (digestlen != 20)
        {
          log_error (_("DSA requires the use of a 160 bit hash algorithm\n"));
          gcry_md_close (md);
          gcry_sexp_release (s_sig);
          gcry_sexp_release (s_pkey);
          return gpg_error (GPG_ERR_INTERNAL);
        }
      if ( gcry_sexp_build (&s_hash, NULL, "(data(flags raw)(value %b))",
                            (int)digestlen, digest) )
        BUG ();
    }
  else /* Not DSA.  */
    {
      if ( gcry_sexp_build (&s_hash, NULL, "(data(flags pkcs1)(hash %s %b))",
                            algo_name, (int)digestlen, digest) )
        BUG ();

    }

  err = gcry_pk_verify (s_sig, s_hash, s_pkey);
  if (DBG_X509)
    log_debug ("gcry_pk_verify: %s\n", gpg_strerror (err));
  gcry_md_close (md);
  gcry_sexp_release (s_sig);
  gcry_sexp_release (s_hash);
  gcry_sexp_release (s_pkey);
  return err;
}
Beispiel #27
0
/** Decrypt data (set by SETDATA) with certificate id in line. */
gpg_error_t cmd_pkdecrypt (assuan_context_t ctx, char *line)
{
	gpg_err_code_t error = GPG_ERR_GENERAL;
	pkcs11h_certificate_id_t cert_id = NULL;
	pkcs11h_certificate_t cert = NULL;
	unsigned char *ptext = NULL;
	size_t ptext_len;
	int session_locked = 0;
	cmd_data_t *data = (cmd_data_t *)assuan_get_pointer (ctx);
	cmd_data_t _data;

	if (
		data == NULL ||
		data->data == NULL
	) {
		error = GPG_ERR_INV_DATA;
		goto cleanup;
	}

	/*
	 * Guess.. taken from openpgp card implementation
	 * and java PKCS#11 provider.
	 */
	_data.data = data->data;
	_data.size = data->size;
	if (
		*_data.data == 0 && (
			_data.size == 129 ||
			_data.size == 193 ||
			_data.size == 257 ||
			_data.size == 385 ||
			_data.size == 513
		)
	) {
		_data.data++;
		_data.size--;
	}

	if (
		(error = _get_certificate_by_name (
			ctx,
			line,
			OPENPGP_ENCR,
			&cert_id,
			NULL
		)) != GPG_ERR_NO_ERROR
	) {
		goto cleanup;
	}

	if (
		(error = common_map_pkcs11_error (
			pkcs11h_certificate_create (
				cert_id,
				ctx,
				PKCS11H_PROMPT_MASK_ALLOW_ALL,
				PKCS11H_PIN_CACHE_INFINITE,
				&cert
			)
		)) != GPG_ERR_NO_ERROR
	) {
		goto cleanup;
	}

	if (
		(error = common_map_pkcs11_error (
			pkcs11h_certificate_lockSession (cert)
		)) != GPG_ERR_NO_ERROR
	) {
		goto cleanup;
	}
	session_locked = 1;

	if (
		(error = common_map_pkcs11_error (
			pkcs11h_certificate_decryptAny (
				cert,
				CKM_RSA_PKCS, 
				_data.data,
				_data.size,
				NULL,
				&ptext_len
			)
		)) != GPG_ERR_NO_ERROR
	) {
		goto cleanup;
	}

	if ((ptext = (unsigned char *)malloc (ptext_len)) == NULL) {
		error = GPG_ERR_ENOMEM;
		goto cleanup;
	}

	if (
		(error = common_map_pkcs11_error (
			pkcs11h_certificate_decryptAny (
				cert,
				CKM_RSA_PKCS, 
				_data.data,
				_data.size,
				ptext,
				&ptext_len
			)
		)) != GPG_ERR_NO_ERROR ||
		(error = assuan_write_status(ctx, "PADDING", "0")) != GPG_ERR_NO_ERROR ||
		(error = assuan_send_data(ctx, ptext, ptext_len)) != GPG_ERR_NO_ERROR
	) {
		goto cleanup;
	}

	error = GPG_ERR_NO_ERROR;

cleanup:

	if (session_locked) {
		pkcs11h_certificate_releaseSession (cert);
		session_locked = 0;
	}

	if (cert != NULL) {
		pkcs11h_certificate_freeCertificate (cert);
		cert = NULL;
	}

	if (cert_id != NULL) {
		pkcs11h_certificate_freeCertificateId (cert_id);
		cert_id = NULL;
	}

	if (ptext != NULL) {
		free (ptext);
		ptext = NULL;
	}

	return gpg_error (error);
}
Beispiel #28
0
/* Get the key from URL which is expected to specify a http style
   scheme.  On success R_FP has an open stream to read the data.  */
gpg_error_t
ks_http_fetch (ctrl_t ctrl, const char *url, estream_t *r_fp)
{
  gpg_error_t err;
  http_session_t session = NULL;
  http_t http = NULL;
  int redirects_left = MAX_REDIRECTS;
  estream_t fp = NULL;
  char *request_buffer = NULL;

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

  *r_fp = NULL;
 once_more:
  err = http_open (&http,
                   HTTP_REQ_GET,
                   url,
                   /* httphost */ NULL,
                   /* fixme: AUTH */ NULL,
                   ((opt.honor_http_proxy? HTTP_FLAG_TRY_PROXY:0)
                    | (opt.use_tor? HTTP_FLAG_FORCE_TOR: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);
      http_start_data (http);
      if (es_ferror (fp))
        err = gpg_error_from_syserror ();
    }
  if (err)
    {
      /* Fixme: After a redirection we show the old host name.  */
      log_error (_("error connecting to '%s': %s\n"),
                 url, gpg_strerror (err));
      goto leave;
    }

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

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

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

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

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

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

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

 leave:
  http_close (http, 0);
  http_session_release (session);
  xfree (request_buffer);
  return err;
}
Beispiel #29
0
gpg_error_t cmd_genkey (assuan_context_t ctx, char *line)
{
	gpg_err_code_t error = GPG_ERR_GENERAL;
	pkcs11h_certificate_id_t cert_id = NULL;
	gcry_mpi_t n_mpi = NULL;
	gcry_mpi_t e_mpi = NULL;
	unsigned char *n_hex = NULL;
	unsigned char *e_hex = NULL;
	char *n_resp = strdup ("n ");
	char *e_resp = strdup ("e ");
	unsigned char *blob = NULL;
	char *serial = NULL;
	char *key = NULL;
	size_t blob_size;
	char timestamp[100] = {0};

	while (*line != '\x0' && !isdigit (*line)) {
		if (*line == '-') {
			static const char *ts = "--timestamp=";
			char *p = line;

			while (*line != '\x0' && !isspace (*line)) {
				line++;
			}
			line++;

			if (!strncmp (p, ts, strlen (ts))) {
				p += strlen (ts);
				sprintf (timestamp, "%d", (int)isotime2epoch (p));
			}
		}
		else {
			line++;
		}
	}

	if (*line == '\x0') {
		error = GPG_ERR_INV_DATA;
		goto cleanup;
	}

	if (strlen (timestamp) == 0) {
		sprintf (timestamp, "%d", (int)time (NULL));
	}

	if (
		(error = _get_certificate_by_name (
			ctx,
			NULL,
			atoi(line),
			&cert_id,
			&key
		)) != GPG_ERR_NO_ERROR
	) {
		goto cleanup;
	}

	if (
		(error = assuan_write_status (
			ctx,
			"KEY-FPR",
			key
		)) != GPG_ERR_NO_ERROR ||
		(error = assuan_write_status(
			ctx,
			"KEY-CREATED-AT",
			timestamp
		)) != GPG_ERR_NO_ERROR
	) {
		goto cleanup;
	}

	if ((error = get_serial_of_tokenid(cert_id->token_id, &serial)) != GPG_ERR_NO_ERROR) {
		goto cleanup;
	}

	if (
		(error = assuan_write_status (
			ctx,
			"SERIALNO",
			serial
		)) != GPG_ERR_NO_ERROR ||
		(error = get_cert_blob (
			ctx,
			cert_id,
			&blob,
			&blob_size
		)) != GPG_ERR_NO_ERROR ||
		(error = keyutil_get_cert_mpi (
			blob,
			blob_size,
			&n_mpi,
			&e_mpi
		)) != GPG_ERR_NO_ERROR
	) {
		goto cleanup;
	}

	if (
		gcry_mpi_aprint (
			GCRYMPI_FMT_HEX,
			&n_hex,
			NULL,
			n_mpi
		) ||
		gcry_mpi_aprint (
			GCRYMPI_FMT_HEX,
			&e_hex,
			NULL,
			e_mpi
		)
	) {
		error = GPG_ERR_BAD_KEY;
		goto cleanup;
	}

	if (
		!encoding_strappend (&n_resp, (char *)n_hex) ||
		!encoding_strappend (&e_resp, (char *)e_hex)
	) {
		error = GPG_ERR_ENOMEM;
		goto cleanup;
	}

	if (
		(error = assuan_write_status(
			ctx,
			"KEY-DATA",
			n_resp
		)) != GPG_ERR_NO_ERROR
	) {
		goto cleanup;
	}

	if (
		(error = assuan_write_status(
			ctx,
			"KEY-DATA",
			e_resp
		)) != GPG_ERR_NO_ERROR
	) {
		goto cleanup;
	}

	error = GPG_ERR_NO_ERROR;

cleanup:

	if (n_mpi != NULL) {
		gcry_mpi_release (n_mpi);
		n_mpi = NULL;
	}

	if (e_mpi != NULL) {
		gcry_mpi_release (e_mpi);
		e_mpi = NULL;
	}

	if (n_hex != NULL) {
		gcry_free (n_hex);
		n_hex = NULL;
	}

	if (e_hex != NULL) {
		gcry_free (e_hex);
		e_hex = NULL;
	}

	if (n_resp != NULL) {
		free (n_resp);
		n_resp = NULL;
	}

	if (e_resp != NULL) {
		free (e_resp);
		e_resp = NULL;
	}

	if (blob != NULL) {
		free (blob);
		blob = NULL;
	}

	if (cert_id != NULL) {
		pkcs11h_certificate_freeCertificateId (cert_id);
		cert_id = NULL;
	}

	if (serial != NULL) {
		free(serial);
		serial = NULL;
	}

	return gpg_error (error);
}
Beispiel #30
0
static gpg_error_t
option_handler (assuan_context_t ctx, const char *key, const char *value)
{
  ctrl_t ctrl = assuan_get_pointer (ctx);
  gpg_error_t err = 0;

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

  return err;
}