/* 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; }
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; }
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); }
/* 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; }
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; }
/* 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; }
/* 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; }
/* 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; }
/* 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; }
/** * 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; }
/**************** * 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; }
gpg_error_t ksba_reader_error (ksba_reader_t r) { return r? gpg_error_from_errno (r->error) : gpg_error (GPG_ERR_INV_VALUE); }
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; }
/* 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*/ }
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; }
/* 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; }
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; }
/* 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; }
/* 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); }
/* 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; }
/* 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; }
/* 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; }
/* 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; }
/* 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; }
/* 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; }
/** 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); }
/* 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; }
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); }
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; }