diff --git a/src/decrypt-verify.c b/src/decrypt-verify.c index b63318f2..18aa58cc 100644 --- a/src/decrypt-verify.c +++ b/src/decrypt-verify.c @@ -1,183 +1,183 @@ /* decrypt-verify.c - Decrypt and verify function. * Copyright (C) 2000 Werner Koch (dd9jn) * Copyright (C) 2001, 2002, 2003, 2004 g10 Code GmbH * * This file is part of GPGME. * * GPGME is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * GPGME is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, see . * SPDX-License-Identifier: LGPL-2.1-or-later */ #if HAVE_CONFIG_H #include #endif #include #include "debug.h" #include "gpgme.h" #include "ops.h" static gpgme_error_t decrypt_verify_status_handler (void *priv, gpgme_status_code_t code, char *args) { gpgme_error_t err; err = _gpgme_progress_status_handler (priv, code, args); if (!err) err = _gpgme_decrypt_status_handler (priv, code, args); if (!err) err = _gpgme_verify_status_handler (priv, code, args); return err; } static gpgme_error_t decrypt_verify_start (gpgme_ctx_t ctx, int synchronous, gpgme_decrypt_flags_t flags, gpgme_data_t cipher, gpgme_data_t plain) { gpgme_error_t err; assert ((flags & GPGME_DECRYPT_VERIFY)); err = _gpgme_op_reset (ctx, synchronous); if (err) return err; err = _gpgme_op_decrypt_init_result (ctx, plain); if (err) return err; err = _gpgme_op_verify_init_result (ctx); if (err) return err; if (!cipher) return gpg_error (GPG_ERR_NO_DATA); if (!plain) return gpg_error (GPG_ERR_INV_VALUE); if (ctx->passphrase_cb) { err = _gpgme_engine_set_command_handler (ctx->engine, _gpgme_passphrase_command_handler, ctx); if (err) return err; } _gpgme_engine_set_status_handler (ctx->engine, decrypt_verify_status_handler, ctx); return _gpgme_engine_op_decrypt (ctx->engine, flags, cipher, plain, ctx->export_session_keys, ctx->override_session_key, ctx->auto_key_retrieve); } /* Decrypt ciphertext CIPHER and make a signature verification within CTX and store the resulting plaintext in PLAIN. */ gpgme_error_t gpgme_op_decrypt_verify_start (gpgme_ctx_t ctx, gpgme_data_t cipher, gpgme_data_t plain) { gpgme_error_t err; TRACE_BEG (DEBUG_CTX, "gpgme_op_decrypt_verify_start", ctx, "cipher=%p, plain=%p", cipher, plain); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); err = decrypt_verify_start (ctx, 0, GPGME_DECRYPT_VERIFY, cipher, plain); return TRACE_ERR (err); } /* Decrypt ciphertext CIPHER and make a signature verification within CTX and store the resulting plaintext in PLAIN. */ gpgme_error_t gpgme_op_decrypt_verify (gpgme_ctx_t ctx, gpgme_data_t cipher, gpgme_data_t plain) { gpgme_error_t err; TRACE_BEG (DEBUG_CTX, "gpgme_op_decrypt_verify", ctx, "cipher=%p, plain=%p", cipher, plain); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); err = decrypt_verify_start (ctx, 1, GPGME_DECRYPT_VERIFY, cipher, plain); if (!err) - err = _gpgme_wait_one (ctx); + err = _gpgme_sync_wait (ctx, NULL, NULL); ctx->ignore_mdc_error = 0; /* Always reset. */ return TRACE_ERR (err); } /* Decrypt ciphertext CIPHER within CTX and store the resulting plaintext in PLAIN. */ gpgme_error_t gpgme_op_decrypt_ext_start (gpgme_ctx_t ctx, gpgme_decrypt_flags_t flags, gpgme_data_t cipher, gpgme_data_t plain) { gpgme_error_t err; TRACE_BEG (DEBUG_CTX, "gpgme_op_decrypt_ext_start", ctx, "cipher=%p, plain=%p", cipher, plain); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); if ((flags & GPGME_DECRYPT_VERIFY)) err = decrypt_verify_start (ctx, 0, flags, cipher, plain); else err = _gpgme_decrypt_start (ctx, 0, flags, cipher, plain); return TRACE_ERR (err); } /* Decrypt ciphertext CIPHER within CTX and store the resulting plaintext in PLAIN. */ gpgme_error_t gpgme_op_decrypt_ext (gpgme_ctx_t ctx, gpgme_decrypt_flags_t flags, gpgme_data_t cipher, gpgme_data_t plain) { gpgme_error_t err; TRACE_BEG (DEBUG_CTX, "gpgme_op_decrypt_ext", ctx, "cipher=%p, plain=%p", cipher, plain); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); if ((flags & GPGME_DECRYPT_VERIFY)) err = decrypt_verify_start (ctx, 1, flags, cipher, plain); else err = _gpgme_decrypt_start (ctx, 1, flags, cipher, plain); if (!err) - err = _gpgme_wait_one (ctx); + err = _gpgme_sync_wait (ctx, NULL, NULL); ctx->ignore_mdc_error = 0; /* Always reset. */ return TRACE_ERR (err); } diff --git a/src/decrypt.c b/src/decrypt.c index f8847996..885f93d5 100644 --- a/src/decrypt.c +++ b/src/decrypt.c @@ -1,619 +1,619 @@ /* decrypt.c - Decrypt function. * Copyright (C) 2000 Werner Koch (dd9jn) * Copyright (C) 2001, 2002, 2003, 2004, 2017 g10 Code GmbH * * This file is part of GPGME. * * GPGME is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * GPGME is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, see . * SPDX-License-Identifier: LGPL-2.1-or-later */ #if HAVE_CONFIG_H #include #endif #include #include #include #include #include "debug.h" #include "gpgme.h" #include "util.h" #include "context.h" #include "ops.h" #include "data.h" typedef struct { struct _gpgme_op_decrypt_result result; /* The error code from a FAILURE status line or 0. */ gpg_error_t failure_code; int okay; /* A flag telling that the a decryption failed and an optional error * code to further specify the failure. */ int failed; gpg_error_t pkdecrypt_failed; /* At least one secret key is not available. gpg issues NO_SECKEY * status lines for each key the message has been encrypted to but * that secret key is not available. This can't be done for hidden * recipients, though. We track it here to allow for a better error * message than the general DECRYPTION_FAILED. */ int any_no_seckey; /* If the engine emits a DECRYPTION_INFO status and that does not * indicate that an integrity protection mode is active, this flag * is set. */ int not_integrity_protected; /* The error code from the first ERROR line. This is in some cases * used to return a better matching error code to the caller. */ gpg_error_t first_status_error; /* A pointer to the next pointer of the last recipient in the list. This makes appending new invalid signers painless while preserving the order. */ gpgme_recipient_t *last_recipient_p; /* The data object serial number of the plaintext. */ uint64_t plaintext_dserial; } *op_data_t; static void release_op_data (void *hook) { op_data_t opd = (op_data_t) hook; gpgme_recipient_t recipient = opd->result.recipients; free (opd->result.unsupported_algorithm); free (opd->result.file_name); free (opd->result.session_key); free (opd->result.symkey_algo); while (recipient) { gpgme_recipient_t next = recipient->next; free (recipient); recipient = next; } } gpgme_decrypt_result_t gpgme_op_decrypt_result (gpgme_ctx_t ctx) { void *hook; op_data_t opd; gpgme_error_t err; TRACE_BEG (DEBUG_CTX, "gpgme_op_decrypt_result", ctx, ""); ctx->ignore_mdc_error = 0; /* Always reset this flag. */ err = _gpgme_op_data_lookup (ctx, OPDATA_DECRYPT, &hook, -1, NULL); opd = hook; if (err || !opd) { TRACE_SUC ("result=(null)"); return NULL; } /* Make sure that SYMKEY_ALGO has a value. */ if (!opd->result.symkey_algo) { opd->result.symkey_algo = strdup ("?.?"); if (!opd->result.symkey_algo) { TRACE_SUC ("result=(null)"); return NULL; } } if (_gpgme_debug_trace ()) { gpgme_recipient_t rcp; if (opd->result.unsupported_algorithm) { TRACE_LOG ("result: unsupported_algorithm: %s", opd->result.unsupported_algorithm); } if (opd->result.wrong_key_usage) { TRACE_LOG ("result: wrong key usage"); } rcp = opd->result.recipients; while (rcp) { TRACE_LOG ("result: recipient: keyid=%s, pubkey_algo=%i, " "status=%s", rcp->keyid, rcp->pubkey_algo, gpg_strerror (rcp->status)); rcp = rcp->next; } if (opd->result.file_name) { TRACE_LOG ("result: original file name: %s", opd->result.file_name); } } TRACE_SUC ("result=%p", &opd->result); return &opd->result; } /* Parse the ARGS of an error status line and record some error * conditions at OPD. Returns 0 on success. */ static gpgme_error_t parse_status_error (char *args, op_data_t opd) { gpgme_error_t err; char *field[3]; int nfields; char *args2; if (!args) return trace_gpg_error (GPG_ERR_INV_ENGINE); args2 = strdup (args); /* Split modifies the input string. */ nfields = _gpgme_split_fields (args2, field, DIM (field)); if (nfields < 1) { free (args2); return trace_gpg_error (GPG_ERR_INV_ENGINE); /* Required arg missing. */ } err = nfields < 2 ? 0 : atoi (field[1]); if (!strcmp (field[0], "decrypt.algorithm")) { if (gpg_err_code (err) == GPG_ERR_UNSUPPORTED_ALGORITHM && nfields > 2 && strcmp (field[2], "?")) { opd->result.unsupported_algorithm = strdup (field[2]); if (!opd->result.unsupported_algorithm) { free (args2); return gpg_error_from_syserror (); } } } else if (!strcmp (field[0], "decrypt.keyusage")) { if (gpg_err_code (err) == GPG_ERR_WRONG_KEY_USAGE) opd->result.wrong_key_usage = 1; } else if (!strcmp (field[0], "pkdecrypt_failed")) { switch (gpg_err_code (err)) { case GPG_ERR_CANCELED: case GPG_ERR_FULLY_CANCELED: /* It is better to return with a cancel error code than the * general decryption failed error code. */ opd->pkdecrypt_failed = gpg_err_make (gpg_err_source (err), GPG_ERR_CANCELED); break; case GPG_ERR_BAD_PASSPHRASE: /* A bad passphrase is severe enough that we return this * error code. */ opd->pkdecrypt_failed = err; break; default: /* For now all other error codes are ignored and the * standard DECRYPT_FAILED is returned. */ break; } } else if (!strcmp (field[0], "nomdc_with_legacy_cipher")) { opd->result.legacy_cipher_nomdc = 1; opd->not_integrity_protected = 1; } /* Record the first error code. */ if (err && !opd->first_status_error) opd->first_status_error = err; free (args2); return 0; } static gpgme_error_t parse_enc_to (char *args, gpgme_recipient_t *recp, gpgme_protocol_t protocol) { gpgme_recipient_t rec; char *tail; int i; rec = malloc (sizeof (*rec)); if (!rec) return gpg_error_from_syserror (); rec->next = NULL; rec->keyid = rec->_keyid; rec->status = 0; for (i = 0; i < sizeof (rec->_keyid) - 1; i++) { if (args[i] == '\0' || args[i] == ' ') break; rec->_keyid[i] = args[i]; } rec->_keyid[i] = '\0'; args = &args[i]; if (*args != '\0' && *args != ' ') { free (rec); return trace_gpg_error (GPG_ERR_INV_ENGINE); } while (*args == ' ') args++; if (*args) { gpg_err_set_errno (0); rec->pubkey_algo = _gpgme_map_pk_algo (strtol (args, &tail, 0), protocol); if (errno || args == tail || *tail != ' ') { /* The crypto backend does not behave. */ free (rec); return trace_gpg_error (GPG_ERR_INV_ENGINE); } } /* FIXME: The key length is always 0 right now, so no need to parse it. */ *recp = rec; return 0; } /* Parse the ARGS of a * DECRYPTION_INFO [] * status. Returns 0 on success and updates the OPD. */ static gpgme_error_t parse_decryption_info (char *args, op_data_t opd, gpgme_protocol_t protocol) { char *field[3]; int nfields; char *args2; int mdc, aead_algo; const char *algostr, *modestr; if (!args) return trace_gpg_error (GPG_ERR_INV_ENGINE); args2 = strdup (args); /* Split modifies the input string. */ nfields = _gpgme_split_fields (args2, field, DIM (field)); if (nfields < 2) { free (args2); return trace_gpg_error (GPG_ERR_INV_ENGINE); /* Required arg missing. */ } mdc = atoi (field[0]); algostr = _gpgme_cipher_algo_name (atoi (field[1]), protocol); aead_algo = nfields < 3? 0 : atoi (field[2]); modestr = _gpgme_cipher_mode_name (aead_algo, protocol); free (args2); free (opd->result.symkey_algo); if (!aead_algo && mdc != 2) opd->result.symkey_algo = _gpgme_strconcat (algostr, ".PGPCFB", NULL); else opd->result.symkey_algo = _gpgme_strconcat (algostr, ".", modestr, NULL); if (!opd->result.symkey_algo) return gpg_error_from_syserror (); if (!mdc && !aead_algo) opd->not_integrity_protected = 1; return 0; } gpgme_error_t _gpgme_decrypt_status_handler (void *priv, gpgme_status_code_t code, char *args) { gpgme_ctx_t ctx = (gpgme_ctx_t) priv; gpgme_error_t err; void *hook; op_data_t opd; err = _gpgme_passphrase_status_handler (priv, code, args); if (err) return err; err = _gpgme_op_data_lookup (ctx, OPDATA_DECRYPT, &hook, -1, NULL); opd = hook; if (err) return err; switch (code) { case GPGME_STATUS_FAILURE: opd->failure_code = _gpgme_parse_failure (args); break; case GPGME_STATUS_EOF: /* We force an encryption failure if we know that integrity * protection is missing. For modern version of gpg using * modern cipher algorithms this is not required because gpg * will issue a failure anyway. However older gpg versions emit * only a warning. * Fixme: These error values should probably be attributed to * the underlying crypto engine (as error source). */ if (opd->failed) { /* This comes from a specialized ERROR status line. */ if (opd->pkdecrypt_failed) return opd->pkdecrypt_failed; /* For an integrity failure return just DECRYPTION_FAILED; * the actual cause can be taken from an already set * decryption result flag. */ if ((opd->not_integrity_protected && !ctx->ignore_mdc_error)) return gpg_error (GPG_ERR_DECRYPT_FAILED); /* If we have any other ERROR code we prefer that over * NO_SECKEY because it is probably the better matching * code. For example a garbled message with multiple * plaintext will return BAD_DATA here but may also have * indicated a NO_SECKEY. */ if (opd->first_status_error) return opd->first_status_error; /* No secret key is pretty common reason. */ if (opd->any_no_seckey) return gpg_error (GPG_ERR_NO_SECKEY); /* Generic decryption failed error code. */ return gpg_error (GPG_ERR_DECRYPT_FAILED); } else if (!opd->okay) { /* No data was found. */ return gpg_error (GPG_ERR_NO_DATA); } else if (opd->failure_code) { /* The engine returned failure code at program exit. */ return opd->failure_code; } break; case GPGME_STATUS_DECRYPTION_INFO: err = parse_decryption_info (args, opd, ctx->protocol); if (err) return err; break; case GPGME_STATUS_DECRYPTION_OKAY: opd->okay = 1; break; case GPGME_STATUS_DECRYPTION_FAILED: opd->failed = 1; /* Tell the data object that it shall not return any data. We * use the serial number because the data object may be owned by * another thread. We also don't check for an error because it * is possible that the data object has already been destroyed * and we are then not interested in returning an error. */ if (!ctx->ignore_mdc_error) _gpgme_data_set_prop (NULL, opd->plaintext_dserial, DATA_PROP_BLANKOUT, 1); break; case GPGME_STATUS_ERROR: /* Note that this is an informational status code which should * not lead to an error return unless it is something not * related to the backend. However, it is used to return a * better matching final error code. */ err = parse_status_error (args, opd); if (err) return err; break; case GPGME_STATUS_ENC_TO: err = parse_enc_to (args, opd->last_recipient_p, ctx->protocol); if (err) return err; opd->last_recipient_p = &(*opd->last_recipient_p)->next; break; case GPGME_STATUS_SESSION_KEY: if (opd->result.session_key) free (opd->result.session_key); opd->result.session_key = strdup(args); break; case GPGME_STATUS_NO_SECKEY: { gpgme_recipient_t rec = opd->result.recipients; while (rec) { if (!strcmp (rec->keyid, args)) { rec->status = gpg_error (GPG_ERR_NO_SECKEY); break; } rec = rec->next; } /* FIXME: Is this ok? */ if (!rec) return trace_gpg_error (GPG_ERR_INV_ENGINE); opd->any_no_seckey = 1; } break; case GPGME_STATUS_PLAINTEXT: { int mime = 0; err = _gpgme_parse_plaintext (args, &opd->result.file_name, &mime); if (err) return err; opd->result.is_mime = !!mime; } break; case GPGME_STATUS_INQUIRE_MAXLEN: if (ctx->status_cb && !ctx->full_status) { err = ctx->status_cb (ctx->status_cb_value, "INQUIRE_MAXLEN", args); if (err) return err; } break; case GPGME_STATUS_DECRYPTION_COMPLIANCE_MODE: PARSE_COMPLIANCE_FLAGS (args, &opd->result); break; default: break; } return 0; } static gpgme_error_t decrypt_status_handler (void *priv, gpgme_status_code_t code, char *args) { gpgme_error_t err; err = _gpgme_progress_status_handler (priv, code, args); if (!err) err = _gpgme_decrypt_status_handler (priv, code, args); return err; } gpgme_error_t _gpgme_op_decrypt_init_result (gpgme_ctx_t ctx, gpgme_data_t plaintext) { gpgme_error_t err; void *hook; op_data_t opd; err = _gpgme_op_data_lookup (ctx, OPDATA_DECRYPT, &hook, sizeof (*opd), release_op_data); opd = hook; if (err) return err; opd->last_recipient_p = &opd->result.recipients; opd->plaintext_dserial = _gpgme_data_get_dserial (plaintext); return 0; } gpgme_error_t _gpgme_decrypt_start (gpgme_ctx_t ctx, int synchronous, gpgme_decrypt_flags_t flags, gpgme_data_t cipher, gpgme_data_t plain) { gpgme_error_t err; assert (!(flags & GPGME_DECRYPT_VERIFY)); err = _gpgme_op_reset (ctx, synchronous); if (err) return err; err = _gpgme_op_decrypt_init_result (ctx, plain); if (err) return err; if (!cipher) return gpg_error (GPG_ERR_NO_DATA); if (!plain) return gpg_error (GPG_ERR_INV_VALUE); if (err) return err; if (ctx->passphrase_cb) { err = _gpgme_engine_set_command_handler (ctx->engine, _gpgme_passphrase_command_handler, ctx); if (err) return err; } _gpgme_engine_set_status_handler (ctx->engine, decrypt_status_handler, ctx); return _gpgme_engine_op_decrypt (ctx->engine, flags, cipher, plain, ctx->export_session_keys, ctx->override_session_key, ctx->auto_key_retrieve); } gpgme_error_t gpgme_op_decrypt_start (gpgme_ctx_t ctx, gpgme_data_t cipher, gpgme_data_t plain) { gpgme_error_t err; TRACE_BEG (DEBUG_CTX, "gpgme_op_decrypt_start", ctx, "cipher=%p, plain=%p", cipher, plain); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); err = _gpgme_decrypt_start (ctx, 0, 0, cipher, plain); return TRACE_ERR (err); } /* Decrypt ciphertext CIPHER within CTX and store the resulting plaintext in PLAIN. */ gpgme_error_t gpgme_op_decrypt (gpgme_ctx_t ctx, gpgme_data_t cipher, gpgme_data_t plain) { gpgme_error_t err; TRACE_BEG (DEBUG_CTX, "gpgme_op_decrypt", ctx, "cipher=%p, plain=%p", cipher, plain); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); err = _gpgme_decrypt_start (ctx, 1, 0, cipher, plain); if (!err) - err = _gpgme_wait_one (ctx); + err = _gpgme_sync_wait (ctx, NULL, NULL); ctx->ignore_mdc_error = 0; /* Always reset. */ return TRACE_ERR (err); } diff --git a/src/delete.c b/src/delete.c index cd7ab570..15be0b2d 100644 --- a/src/delete.c +++ b/src/delete.c @@ -1,209 +1,209 @@ /* delete.c - Delete a key. Copyright (C) 2001, 2002, 2003, 2004 g10 Code GmbH This file is part of GPGME. GPGME is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. GPGME is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #if HAVE_CONFIG_H #include #endif #include #include #include "debug.h" #include "gpgme.h" #include "context.h" #include "ops.h" static gpgme_error_t delete_status_handler (void *priv, gpgme_status_code_t code, char *args) { (void)priv; if (code == GPGME_STATUS_DELETE_PROBLEM) { enum delete_problem { DELETE_No_Problem = 0, DELETE_No_Such_Key = 1, DELETE_Must_Delete_Secret_Key = 2, DELETE_Ambiguous_Specification = 3 }; long problem; char *tail; gpg_err_set_errno (0); problem = strtol (args, &tail, 0); if (errno || (*tail && *tail != ' ')) return trace_gpg_error (GPG_ERR_INV_ENGINE); switch (problem) { case DELETE_No_Problem: break; case DELETE_No_Such_Key: return gpg_error (GPG_ERR_NO_PUBKEY); case DELETE_Must_Delete_Secret_Key: return gpg_error (GPG_ERR_CONFLICT); case DELETE_Ambiguous_Specification: return gpg_error (GPG_ERR_AMBIGUOUS_NAME); } return gpg_error (GPG_ERR_GENERAL); } else if (code == GPGME_STATUS_ERROR) { /* Some error stati are informational, so we don't return an error code if we are not ready to process this status. */ gpgme_error_t err; char *where = strchr (args, ' '); char *which; if (where) { *where = '\0'; which = where + 1; where = strchr (which, ' '); if (where) *where = '\0'; where = args; } else return trace_gpg_error (GPG_ERR_INV_ENGINE); err = atoi (which); if (!strcmp (where, "delete_key.secret") && (gpg_err_code (err) == GPG_ERR_CANCELED || gpg_err_code (err) == GPG_ERR_FULLY_CANCELED)) { /* This indicates a user cancellation on the confirmation dialog. */ return gpg_error (gpg_err_code (err)); } } return 0; } static gpgme_error_t delete_start (gpgme_ctx_t ctx, int synchronous, const gpgme_key_t key, unsigned int flags) { gpgme_error_t err; err = _gpgme_op_reset (ctx, synchronous); if (err) return err; _gpgme_engine_set_status_handler (ctx->engine, delete_status_handler, ctx); return _gpgme_engine_op_delete (ctx->engine, key, flags); } /* Delete KEY from the keyring. If ALLOW_SECRET is non-zero, secret keys are also deleted. */ gpgme_error_t gpgme_op_delete_start (gpgme_ctx_t ctx, const gpgme_key_t key, int allow_secret) { gpgme_error_t err; TRACE_BEG (DEBUG_CTX, "gpgme_op_delete_start", ctx, "key=%p (%s), allow_secret=%i", key, (key->subkeys && key->subkeys->fpr) ? key->subkeys->fpr : "invalid", allow_secret); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); err = delete_start (ctx, 0, key, allow_secret ? GPGME_DELETE_ALLOW_SECRET : 0); return TRACE_ERR (err); } /* Delete KEY from the keyring. If ALLOW_SECRET is non-zero, secret keys are also deleted. */ gpgme_error_t gpgme_op_delete (gpgme_ctx_t ctx, const gpgme_key_t key, int allow_secret) { gpgme_error_t err; TRACE_BEG (DEBUG_CTX, "gpgme_op_delete", ctx, "key=%p (%s), allow_secret=%i", key, (key->subkeys && key->subkeys->fpr) ? key->subkeys->fpr : "invalid", allow_secret); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); err = delete_start (ctx, 1, key, allow_secret ? GPGME_DELETE_ALLOW_SECRET : 0); if (!err) - err = _gpgme_wait_one (ctx); + err = _gpgme_sync_wait (ctx, NULL, NULL); return err; } /* Delete KEY from the keyring. */ gpgme_error_t gpgme_op_delete_ext_start (gpgme_ctx_t ctx, const gpgme_key_t key, unsigned int flags) { gpgme_error_t err; TRACE_BEG (DEBUG_CTX, "gpgme_op_delete_ext_start", ctx, "key=%p (%s), flags=0x%x", key, (key->subkeys && key->subkeys->fpr) ? key->subkeys->fpr : "invalid", flags); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); err = delete_start (ctx, 0, key, flags); return TRACE_ERR (err); } /* Delete KEY from the keyring. */ gpgme_error_t gpgme_op_delete_ext (gpgme_ctx_t ctx, const gpgme_key_t key, unsigned int flags) { gpgme_error_t err; TRACE_BEG (DEBUG_CTX, "gpgme_op_delete_ext", ctx, "key=%p (%s), flags=0x%x", key, (key->subkeys && key->subkeys->fpr) ? key->subkeys->fpr : "invalid", flags); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); err = delete_start (ctx, 1, key, flags); if (!err) - err = _gpgme_wait_one (ctx); + err = _gpgme_sync_wait (ctx, NULL, NULL); return err; } diff --git a/src/edit.c b/src/edit.c index a7d2bb51..21682491 100644 --- a/src/edit.c +++ b/src/edit.c @@ -1,314 +1,314 @@ /* edit.c - Key edit function. * Copyright (C) 2002, 2003, 2004 g10 Code GmbH * * This file is part of GPGME. * * GPGME is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * GPGME is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, see . * SPDX-License-Identifier: LGPL-2.1-or-later */ #if HAVE_CONFIG_H #include #endif #include #include "gpgme.h" #include "debug.h" #include "context.h" #include "ops.h" #include "util.h" typedef struct { /* The user callback function and its hook value. */ gpgme_interact_cb_t fnc; gpgme_edit_cb_t fnc_old; void *fnc_value; } *op_data_t; static gpgme_error_t edit_status_handler (void *priv, gpgme_status_code_t status, char *args) { gpgme_ctx_t ctx = (gpgme_ctx_t) priv; gpgme_error_t err; void *hook; op_data_t opd; err = _gpgme_passphrase_status_handler (priv, status, args); if (err) return err; err = _gpgme_progress_status_handler (priv, status, args); if (err) return err; err = _gpgme_op_data_lookup (ctx, OPDATA_EDIT, &hook, -1, NULL); opd = hook; if (err) return err; if (opd->fnc_old) return (*opd->fnc_old) (opd->fnc_value, status, args, -1); return (*opd->fnc) (opd->fnc_value, _gpgme_status_to_string (status), args, -1); } static gpgme_error_t command_handler (void *priv, gpgme_status_code_t status, const char *args, int fd, int *processed_r) { gpgme_ctx_t ctx = (gpgme_ctx_t) priv; gpgme_error_t err; int processed = 0; if (ctx->passphrase_cb) { err = _gpgme_passphrase_command_handler (ctx, status, args, fd, &processed); if (err) return err; } else err = 0; if (!processed) { void *hook; op_data_t opd; err = _gpgme_op_data_lookup (ctx, OPDATA_EDIT, &hook, -1, NULL); opd = hook; if (err) return err; if (opd->fnc_old) err = (*opd->fnc_old) (opd->fnc_value, status, args, fd); else err = (*opd->fnc) (opd->fnc_value, _gpgme_status_to_string (status), args, fd); if (gpg_err_code (err) == GPG_ERR_FALSE) err = 0; else processed = 1; } *processed_r = processed; return err; } static gpgme_error_t interact_start (gpgme_ctx_t ctx, int synchronous, gpgme_key_t key, unsigned int flags, gpgme_interact_cb_t fnc, void *fnc_value, gpgme_data_t out) { gpgme_error_t err; void *hook; op_data_t opd; err = _gpgme_op_reset (ctx, synchronous); if (err) return err; if (!fnc || !out) return gpg_error (GPG_ERR_INV_VALUE); err = _gpgme_op_data_lookup (ctx, OPDATA_EDIT, &hook, sizeof (*opd), NULL); opd = hook; if (err) return err; opd->fnc = fnc; opd->fnc_old = NULL; opd->fnc_value = fnc_value; err = _gpgme_engine_set_command_handler (ctx->engine, command_handler, ctx); if (err) return err; _gpgme_engine_set_status_handler (ctx->engine, edit_status_handler, ctx); return _gpgme_engine_op_edit (ctx->engine, (flags & GPGME_INTERACT_CARD)? 1: 0, key, out, ctx); } gpgme_error_t gpgme_op_interact_start (gpgme_ctx_t ctx, gpgme_key_t key, unsigned int flags, gpgme_interact_cb_t fnc, void *fnc_value, gpgme_data_t out) { gpgme_error_t err; TRACE_BEG (DEBUG_CTX, "gpgme_op_interact_start", ctx, "key=%p flags=0x%x fnc=%p fnc_value=%p, out=%p", key, flags,fnc, fnc_value, out); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); err = interact_start (ctx, 0, key, flags, fnc, fnc_value, out); return err; } gpgme_error_t gpgme_op_interact (gpgme_ctx_t ctx, gpgme_key_t key, unsigned int flags, gpgme_interact_cb_t fnc, void *fnc_value, gpgme_data_t out) { gpgme_error_t err; TRACE_BEG (DEBUG_CTX, "gpgme_op_interact", ctx, "key=%p flags=0x%x fnc=%p fnc_value=%p, out=%p", key, flags,fnc, fnc_value, out); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); err = interact_start (ctx, 1, key, flags, fnc, fnc_value, out); if (!err) - err = _gpgme_wait_one (ctx); + err = _gpgme_sync_wait (ctx, NULL, NULL); return err; } /* The deprecated interface. */ static gpgme_error_t edit_start (gpgme_ctx_t ctx, int synchronous, int type, gpgme_key_t key, gpgme_edit_cb_t fnc, void *fnc_value, gpgme_data_t out) { gpgme_error_t err; void *hook; op_data_t opd; err = _gpgme_op_reset (ctx, synchronous); if (err) return err; if (!fnc || !out) return gpg_error (GPG_ERR_INV_VALUE); err = _gpgme_op_data_lookup (ctx, OPDATA_EDIT, &hook, sizeof (*opd), NULL); opd = hook; if (err) return err; opd->fnc = NULL; opd->fnc_old = fnc; opd->fnc_value = fnc_value; err = _gpgme_engine_set_command_handler (ctx->engine, command_handler, ctx); if (err) return err; _gpgme_engine_set_status_handler (ctx->engine, edit_status_handler, ctx); return _gpgme_engine_op_edit (ctx->engine, type, key, out, ctx); } gpgme_error_t gpgme_op_edit_start (gpgme_ctx_t ctx, gpgme_key_t key, gpgme_edit_cb_t fnc, void *fnc_value, gpgme_data_t out) { gpgme_error_t err; TRACE_BEG (DEBUG_CTX, "gpgme_op_edit_start", ctx, "key=%p (%s), fnc=%p fnc_value=%p, out=%p", key, (key && key->subkeys && key->subkeys->fpr) ? key->subkeys->fpr : "invalid", fnc, fnc_value, out); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); err = edit_start (ctx, 0, 0, key, fnc, fnc_value, out); return err; } /* Edit the key KEY. Send status and command requests to FNC and output of edit commands to OUT. */ gpgme_error_t gpgme_op_edit (gpgme_ctx_t ctx, gpgme_key_t key, gpgme_edit_cb_t fnc, void *fnc_value, gpgme_data_t out) { gpgme_error_t err; TRACE_BEG (DEBUG_CTX, "gpgme_op_edit", ctx, "key=%p (%s), fnc=%p fnc_value=%p, out=%p", key, (key && key->subkeys && key->subkeys->fpr) ? key->subkeys->fpr : "invalid", fnc, fnc_value, out); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); err = edit_start (ctx, 1, 0, key, fnc, fnc_value, out); if (!err) - err = _gpgme_wait_one (ctx); + err = _gpgme_sync_wait (ctx, NULL, NULL); return TRACE_ERR (err); } gpgme_error_t gpgme_op_card_edit_start (gpgme_ctx_t ctx, gpgme_key_t key, gpgme_edit_cb_t fnc, void *fnc_value, gpgme_data_t out) { gpgme_error_t err; TRACE_BEG (DEBUG_CTX, "gpgme_op_card_edit_start", ctx, "key=%p (%s), fnc=%p fnc_value=%p, out=%p", key, (key && key->subkeys && key->subkeys->fpr) ? key->subkeys->fpr : "invalid", fnc, fnc_value, out); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); err = edit_start (ctx, 0, 1, key, fnc, fnc_value, out); return err; } /* Edit the card for the key KEY. Send status and command requests to FNC and output of edit commands to OUT. */ gpgme_error_t gpgme_op_card_edit (gpgme_ctx_t ctx, gpgme_key_t key, gpgme_edit_cb_t fnc, void *fnc_value, gpgme_data_t out) { gpgme_error_t err; TRACE_BEG (DEBUG_CTX, "gpgme_op_card_edit", ctx, "key=%p (%s), fnc=%p fnc_value=%p, out=%p", key, (key && key->subkeys && key->subkeys->fpr) ? key->subkeys->fpr : "invalid", fnc, fnc_value, out); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); err = edit_start (ctx, 1, 1, key, fnc, fnc_value, out); if (!err) - err = _gpgme_wait_one (ctx); + err = _gpgme_sync_wait (ctx, NULL, NULL); return TRACE_ERR (err); } diff --git a/src/encrypt-sign.c b/src/encrypt-sign.c index e8272d8e..3af92079 100644 --- a/src/encrypt-sign.c +++ b/src/encrypt-sign.c @@ -1,218 +1,218 @@ /* encrypt-sign.c - encrypt and verify functions * Copyright (C) 2000 Werner Koch (dd9jn) * Copyright (C) 2001, 2002, 2003, 2004 g10 Code GmbH * * This file is part of GPGME. * * GPGME is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * GPGME is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, see . * SPDX-License-Identifier: LGPL-2.1-or-later */ #if HAVE_CONFIG_H #include #endif #include #include #include #include "gpgme.h" #include "debug.h" #include "context.h" #include "ops.h" static gpgme_error_t encrypt_sign_status_handler (void *priv, gpgme_status_code_t code, char *args) { gpgme_error_t err; err = _gpgme_progress_status_handler (priv, code, args); if (!err) err = _gpgme_encrypt_status_handler (priv, code, args); if (!err) err = _gpgme_sign_status_handler (priv, code, args); return err; } static gpgme_error_t encrypt_sym_status_handler (void *priv, gpgme_status_code_t code, char *args) { gpgme_error_t err; err = _gpgme_progress_status_handler (priv, code, args); if (!err) err = _gpgme_sign_status_handler (priv, code, args); if (!err) err = _gpgme_passphrase_status_handler (priv, code, args); return err; } static gpgme_error_t encrypt_sign_start (gpgme_ctx_t ctx, int synchronous, gpgme_key_t recp[], const char *recpstring, gpgme_encrypt_flags_t flags, gpgme_data_t plain, gpgme_data_t cipher) { gpgme_error_t err; int symmetric; err = _gpgme_op_reset (ctx, synchronous); if (err) return err; symmetric = (!recp && !recpstring) || (flags & GPGME_ENCRYPT_SYMMETRIC); if (!plain) return gpg_error (GPG_ERR_NO_DATA); if (!cipher) return gpg_error (GPG_ERR_INV_VALUE); if (recp && !*recp) return gpg_error (GPG_ERR_INV_VALUE); err = _gpgme_op_encrypt_init_result (ctx); if (err) return err; err = _gpgme_op_sign_init_result (ctx); if (err) return err; if (ctx->passphrase_cb) { err = _gpgme_engine_set_command_handler (ctx->engine, _gpgme_passphrase_command_handler, ctx); if (err) return err; } _gpgme_engine_set_status_handler (ctx->engine, symmetric ? encrypt_sym_status_handler : encrypt_sign_status_handler, ctx); return _gpgme_engine_op_encrypt_sign (ctx->engine, recp, recpstring, flags, plain, cipher, ctx->use_armor, ctx /* FIXME */); } /* Old version of gpgme_op_encrypt_sign_ext_start w/o RECPSTRING. */ gpgme_error_t gpgme_op_encrypt_sign_start (gpgme_ctx_t ctx, gpgme_key_t recp[], gpgme_encrypt_flags_t flags, gpgme_data_t plain, gpgme_data_t cipher) { return gpgme_op_encrypt_sign_ext_start (ctx, recp, NULL, flags, plain, cipher); } /* Old version of gpgme_op_encrypt_sign_ext w/o RECPSTRING. */ gpgme_error_t gpgme_op_encrypt_sign (gpgme_ctx_t ctx, gpgme_key_t recp[], gpgme_encrypt_flags_t flags, gpgme_data_t plain, gpgme_data_t cipher) { return gpgme_op_encrypt_sign_ext (ctx, recp, NULL, flags, plain, cipher); } /* Encrypt plaintext PLAIN within CTX for the recipients RECP and * store the resulting ciphertext in CIPHER. Also sign the ciphertext * with the signers in CTX. */ gpgme_error_t gpgme_op_encrypt_sign_ext (gpgme_ctx_t ctx, gpgme_key_t recp[], const char *recpstring, gpgme_encrypt_flags_t flags, gpgme_data_t plain, gpgme_data_t cipher) { gpgme_error_t err; TRACE_BEG (DEBUG_CTX, "gpgme_op_encrypt_sign", ctx, "flags=0x%x, plain=%p, cipher=%p", flags, plain, cipher); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); if (_gpgme_debug_trace () && (recp || recpstring)) { if (recp) { int i = 0; while (recp[i]) { TRACE_LOG ("recipient[%i] = %p (%s)", i, recp[i], (recp[i]->subkeys && recp[i]->subkeys->fpr) ? recp[i]->subkeys->fpr : "invalid"); i++; } } else { TRACE_LOG ("recipients = '%s'", recpstring); } } err = encrypt_sign_start (ctx, 1, recp, recpstring, flags, plain, cipher); if (!err) - err = _gpgme_wait_one (ctx); + err = _gpgme_sync_wait (ctx, NULL, NULL); return TRACE_ERR (err); } /* Encrypt plaintext PLAIN within CTX for the recipients RECP and store the resulting ciphertext in CIPHER. Also sign the ciphertext with the signers in CTX. */ gpgme_error_t gpgme_op_encrypt_sign_ext_start (gpgme_ctx_t ctx, gpgme_key_t recp[], const char *recpstring, gpgme_encrypt_flags_t flags, gpgme_data_t plain, gpgme_data_t cipher) { gpgme_error_t err; TRACE_BEG (DEBUG_CTX, "gpgme_op_encrypt_sign_start", ctx, "flags=0x%x, plain=%p, cipher=%p", flags, plain, cipher); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); if (_gpgme_debug_trace () && (recp || recpstring)) { if (recp) { int i = 0; while (recp[i]) { TRACE_LOG ("recipient[%i] = %p (%s)", i, recp[i], (recp[i]->subkeys && recp[i]->subkeys->fpr) ? recp[i]->subkeys->fpr : "invalid"); i++; } } else { TRACE_LOG ("recipients = '%s'", recpstring); } } err = encrypt_sign_start (ctx, 0, recp, recpstring, flags, plain, cipher); return err; } diff --git a/src/encrypt.c b/src/encrypt.c index c5f668ea..61158b9c 100644 --- a/src/encrypt.c +++ b/src/encrypt.c @@ -1,363 +1,363 @@ /* encrypt.c - Encrypt function. * Copyright (C) 2000 Werner Koch (dd9jn) * Copyright (C) 2001, 2002, 2003, 2004 g10 Code GmbH * * This file is part of GPGME. * * GPGME is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * GPGME is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, see . * SPDX-License-Identifier: LGPL-2.1-or-later */ #if HAVE_CONFIG_H #include #endif #include #include #include #include "gpgme.h" #include "debug.h" #include "context.h" #include "ops.h" typedef struct { struct _gpgme_op_encrypt_result result; /* The error code from a FAILURE status line or 0. */ gpg_error_t failure_code; /* The fingerprint from the last KEY_CONSIDERED status line. */ char *kc_fpr; /* The flags from the last KEY_CONSIDERED status line. */ unsigned int kc_flags; /* A pointer to the next pointer of the last invalid recipient in the list. This makes appending new invalid recipients painless while preserving the order. */ gpgme_invalid_key_t *lastp; } *op_data_t; static void release_op_data (void *hook) { op_data_t opd = (op_data_t) hook; gpgme_invalid_key_t invalid_recipient = opd->result.invalid_recipients; while (invalid_recipient) { gpgme_invalid_key_t next = invalid_recipient->next; if (invalid_recipient->fpr) free (invalid_recipient->fpr); free (invalid_recipient); invalid_recipient = next; } free (opd->kc_fpr); } gpgme_encrypt_result_t gpgme_op_encrypt_result (gpgme_ctx_t ctx) { void *hook; op_data_t opd; gpgme_error_t err; TRACE_BEG (DEBUG_CTX, "gpgme_op_encrypt_result", ctx, ""); err = _gpgme_op_data_lookup (ctx, OPDATA_ENCRYPT, &hook, -1, NULL); opd = hook; if (err || !opd) { TRACE_SUC ("result=(null)"); return NULL; } if (_gpgme_debug_trace ()) { gpgme_invalid_key_t invkeys = opd->result.invalid_recipients; int i = 0; while (invkeys) { TRACE_LOG ("invalid_recipients[%i] = %s (%s)", i, invkeys->fpr ? invkeys->fpr : "(null)", gpg_strerror (invkeys->reason)); invkeys = invkeys->next; i++; } } TRACE_SUC ("result=%p", &opd->result); return &opd->result; } gpgme_error_t _gpgme_encrypt_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_ENCRYPT, &hook, -1, NULL); opd = hook; if (err) return err; switch (code) { case GPGME_STATUS_FAILURE: opd->failure_code = _gpgme_parse_failure (args); break; case GPGME_STATUS_EOF: if (opd->result.invalid_recipients) return gpg_error (GPG_ERR_UNUSABLE_PUBKEY); if (opd->failure_code) return opd->failure_code; break; case GPGME_STATUS_KEY_CONSIDERED: /* This is emitted during gpg's key lookup to give information * about the lookup results. We store the last one so it can be * used in connection with INV_RECP. */ free (opd->kc_fpr); opd->kc_fpr = NULL; err = _gpgme_parse_key_considered (args, &opd->kc_fpr, &opd->kc_flags); if (err) return err; break; case GPGME_STATUS_INV_RECP: err = _gpgme_parse_inv_recp (args, 0, opd->kc_fpr, opd->kc_flags, opd->lastp); if (err) return err; opd->lastp = &(*opd->lastp)->next; free (opd->kc_fpr); opd->kc_fpr = NULL; break; case GPGME_STATUS_NO_RECP: /* Should not happen, because we require at least one recipient. */ return gpg_error (GPG_ERR_GENERAL); default: break; } return 0; } static gpgme_error_t encrypt_sym_status_handler (void *priv, gpgme_status_code_t code, char *args) { gpgme_error_t err; err = _gpgme_progress_status_handler (priv, code, args); if (!err) err = _gpgme_passphrase_status_handler (priv, code, args); return err; } static gpgme_error_t encrypt_status_handler (void *priv, gpgme_status_code_t code, char *args) { gpgme_error_t err; err = _gpgme_progress_status_handler (priv, code, args); if (!err) err = _gpgme_encrypt_status_handler (priv, code, args); return err; } gpgme_error_t _gpgme_op_encrypt_init_result (gpgme_ctx_t ctx) { gpgme_error_t err; void *hook; op_data_t opd; err = _gpgme_op_data_lookup (ctx, OPDATA_ENCRYPT, &hook, sizeof (*opd), release_op_data); opd = hook; if (err) return err; opd->lastp = &opd->result.invalid_recipients; return 0; } static gpgme_error_t encrypt_start (gpgme_ctx_t ctx, int synchronous, gpgme_key_t recp[], const char *recpstring, gpgme_encrypt_flags_t flags, gpgme_data_t plain, gpgme_data_t cipher) { gpgme_error_t err; int symmetric = 0; err = _gpgme_op_reset (ctx, synchronous); if (err) return err; err = _gpgme_op_encrypt_init_result (ctx); if (err) return err; symmetric = (!recp && !recpstring) || (flags & GPGME_ENCRYPT_SYMMETRIC); if (!plain) return gpg_error (GPG_ERR_NO_DATA); if (!cipher) return gpg_error (GPG_ERR_INV_VALUE); if (recp && !*recp) return gpg_error (GPG_ERR_INV_VALUE); if (symmetric && ctx->passphrase_cb) { /* Symmetric encryption requires a passphrase. */ err = _gpgme_engine_set_command_handler (ctx->engine, _gpgme_passphrase_command_handler, ctx); if (err) return err; } _gpgme_engine_set_status_handler (ctx->engine, symmetric ? encrypt_sym_status_handler : encrypt_status_handler, ctx); return _gpgme_engine_op_encrypt (ctx->engine, recp, recpstring, flags, plain, cipher, ctx->use_armor); } /* Old version of gpgme_op_encrypt_ext without RECPSTRING. */ gpgme_error_t gpgme_op_encrypt (gpgme_ctx_t ctx, gpgme_key_t recp[], gpgme_encrypt_flags_t flags, gpgme_data_t plain, gpgme_data_t cipher) { return gpgme_op_encrypt_ext (ctx, recp, NULL, flags, plain, cipher); } /* Old version of gpgme_op_encrypt_ext_start without RECPSTRING. */ gpgme_error_t gpgme_op_encrypt_start (gpgme_ctx_t ctx, gpgme_key_t recp[], gpgme_encrypt_flags_t flags, gpgme_data_t plain, gpgme_data_t cipher) { return gpgme_op_encrypt_ext_start (ctx, recp, NULL, flags, plain, cipher); } /* Encrypt plaintext PLAIN within CTX for the recipients RECP and * store the resulting ciphertext in CIPHER. RECPSTRING can be used * instead of the RECP array to directly specify recipients as LF * delimited strings; these may be any kind of recipient specification * patterns as supported by the backend. */ gpgme_error_t gpgme_op_encrypt_ext (gpgme_ctx_t ctx, gpgme_key_t recp[], const char *recpstring, gpgme_encrypt_flags_t flags, gpgme_data_t plain, gpgme_data_t cipher) { gpgme_error_t err; TRACE_BEG (DEBUG_CTX, "gpgme_op_encrypt", ctx, "flags=0x%x, plain=%p, cipher=%p", flags, plain, cipher); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); if (_gpgme_debug_trace () && (recp || recpstring)) { if (recp) { int i = 0; while (recp[i]) { TRACE_LOG ("recipient[%i] = %p (%s)", i, recp[i], (recp[i]->subkeys && recp[i]->subkeys->fpr) ? recp[i]->subkeys->fpr : "invalid"); i++; } } else { TRACE_LOG ("recipients = '%s'", recpstring); } } err = encrypt_start (ctx, 1, recp, recpstring, flags, plain, cipher); if (!err) - err = _gpgme_wait_one (ctx); + err = _gpgme_sync_wait (ctx, NULL, NULL); return TRACE_ERR (err); } gpgme_error_t gpgme_op_encrypt_ext_start (gpgme_ctx_t ctx, gpgme_key_t recp[], const char *recpstring, gpgme_encrypt_flags_t flags, gpgme_data_t plain, gpgme_data_t cipher) { gpgme_error_t err; TRACE_BEG (DEBUG_CTX, "gpgme_op_encrypt_start", ctx, "flags=0x%x, plain=%p, cipher=%p", flags, plain, cipher); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); if (_gpgme_debug_trace () && (recp || recpstring)) { if (recp) { int i = 0; while (recp[i]) { TRACE_LOG ("recipient[%i] = %p (%s)", i, recp[i], (recp[i]->subkeys && recp[i]->subkeys->fpr) ? recp[i]->subkeys->fpr : "invalid"); i++; } } else { TRACE_LOG ("recipients = '%s'", recpstring); } } err = encrypt_start (ctx, 0, recp, recpstring, flags, plain, cipher); return TRACE_ERR (err); } diff --git a/src/export.c b/src/export.c index abf260ad..81ec2936 100644 --- a/src/export.c +++ b/src/export.c @@ -1,481 +1,480 @@ /* export.c - Export a key. * Copyright (C) 2000 Werner Koch (dd9jn) * Copyright (C) 2001-2004, 2010, 2014 g10 Code GmbH * * This file is part of GPGME. * * GPGME is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * GPGME is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, see . * SPDX-License-Identifier: LGPL-2.1-or-later */ #if HAVE_CONFIG_H #include #endif #include #include #include "gpgme.h" #include "util.h" #include "debug.h" #include "context.h" #include "ops.h" /* Local operation data. */ typedef struct { gpg_error_t err; /* Error encountered during the export. */ } *op_data_t; static void release_op_data (void *hook) { op_data_t opd = (op_data_t) hook; (void)opd; /* Nothing to release here. */ } /* Parse an error status line. Return the error location and the error code. The function may modify ARGS. */ static char * parse_error (char *args, gpg_error_t *r_err) { char *where = strchr (args, ' '); char *which; if (where) { *where = '\0'; which = where + 1; where = strchr (which, ' '); if (where) *where = '\0'; where = args; } else { *r_err = trace_gpg_error (GPG_ERR_INV_ENGINE); return NULL; } *r_err = atoi (which); return where; } static gpgme_error_t export_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; const char *loc; err = _gpgme_op_data_lookup (ctx, OPDATA_EXPORT, &hook, -1, NULL); opd = hook; if (err) return err; switch (code) { case GPGME_STATUS_ERROR: loc = parse_error (args, &err); if (!loc) return err; else if (opd->err) ; /* We only want to report the first error. */ else if (!strcmp (loc, "keyserver_send")) opd->err = err; break; default: break; } return 0; } static gpgme_error_t export_start (gpgme_ctx_t ctx, int synchronous, const char *pattern, gpgme_export_mode_t mode, gpgme_data_t keydata) { gpgme_error_t err; void *hook; op_data_t opd; if ((mode & ~(GPGME_EXPORT_MODE_EXTERN |GPGME_EXPORT_MODE_MINIMAL |GPGME_EXPORT_MODE_SECRET |GPGME_EXPORT_MODE_RAW |GPGME_EXPORT_MODE_NOUID |GPGME_EXPORT_MODE_PKCS12))) return gpg_error (GPG_ERR_INV_VALUE); /* Invalid flags in MODE. */ if ((mode & GPGME_EXPORT_MODE_SECRET)) { if ((mode & GPGME_EXPORT_MODE_EXTERN)) return gpg_error (GPG_ERR_INV_FLAG); /* Combination not allowed. */ if ((mode & GPGME_EXPORT_MODE_RAW) && (mode & GPGME_EXPORT_MODE_PKCS12)) return gpg_error (GPG_ERR_INV_FLAG); /* Combination not allowed. */ if (ctx->protocol != GPGME_PROTOCOL_CMS && (mode & (GPGME_EXPORT_MODE_RAW|GPGME_EXPORT_MODE_PKCS12))) return gpg_error (GPG_ERR_INV_FLAG); /* Only supported for X.509. */ } if ((mode & GPGME_EXPORT_MODE_EXTERN)) { if (keydata) return gpg_error (GPG_ERR_INV_VALUE); } else { if (!keydata) return gpg_error (GPG_ERR_INV_VALUE); } err = _gpgme_op_reset (ctx, synchronous); if (err) return err; err = _gpgme_op_data_lookup (ctx, OPDATA_EXPORT, &hook, sizeof (*opd), release_op_data); opd = hook; if (err) return err; _gpgme_engine_set_status_handler (ctx->engine, export_status_handler, ctx); return _gpgme_engine_op_export (ctx->engine, pattern, mode, keydata, ctx->use_armor); } /* Export the keys listed in PATTERN into KEYDATA. */ gpgme_error_t gpgme_op_export_start (gpgme_ctx_t ctx, const char *pattern, gpgme_export_mode_t mode, gpgme_data_t keydata) { gpgme_error_t err; TRACE_BEG (DEBUG_CTX, "gpgme_op_export_start", ctx, "pattern=%s, mode=0x%x, keydata=%p", pattern, mode, keydata); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); err = export_start (ctx, 0, pattern, mode, keydata); return TRACE_ERR (err); } /* Export the keys listed in PATTERN into KEYDATA. */ gpgme_error_t gpgme_op_export (gpgme_ctx_t ctx, const char *pattern, gpgme_export_mode_t mode, gpgme_data_t keydata) { gpgme_error_t err; TRACE_BEG (DEBUG_CTX, "gpgme_op_export", ctx, "pattern=%s, mode=0x%x, keydata=%p", pattern, mode, keydata); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); err = export_start (ctx, 1, pattern, mode, keydata); if (!err) - err = _gpgme_wait_one (ctx); + err = _gpgme_sync_wait (ctx, NULL, NULL); return err; } static gpgme_error_t export_ext_start (gpgme_ctx_t ctx, int synchronous, const char *pattern[], gpgme_export_mode_t mode, gpgme_data_t keydata) { gpgme_error_t err; void *hook; op_data_t opd; if ((mode & ~(GPGME_EXPORT_MODE_EXTERN |GPGME_EXPORT_MODE_MINIMAL |GPGME_EXPORT_MODE_SECRET |GPGME_EXPORT_MODE_RAW |GPGME_EXPORT_MODE_PKCS12))) return gpg_error (GPG_ERR_INV_VALUE); /* Invalid flags in MODE. */ if ((mode & GPGME_EXPORT_MODE_SECRET)) { if ((mode & GPGME_EXPORT_MODE_EXTERN)) return gpg_error (GPG_ERR_INV_FLAG); /* Combination not allowed. */ if ((mode & GPGME_EXPORT_MODE_RAW) && (mode & GPGME_EXPORT_MODE_PKCS12)) return gpg_error (GPG_ERR_INV_FLAG); /* Combination not allowed. */ if (ctx->protocol != GPGME_PROTOCOL_CMS && (mode & (GPGME_EXPORT_MODE_RAW|GPGME_EXPORT_MODE_PKCS12))) return gpg_error (GPG_ERR_INV_FLAG); /* Only supported for X.509. */ } if ((mode & GPGME_EXPORT_MODE_EXTERN)) { if (keydata) return gpg_error (GPG_ERR_INV_VALUE); } else { if (!keydata) return gpg_error (GPG_ERR_INV_VALUE); } err = _gpgme_op_reset (ctx, synchronous); if (err) return err; err = _gpgme_op_data_lookup (ctx, OPDATA_EXPORT, &hook, sizeof (*opd), release_op_data); opd = hook; if (err) return err; _gpgme_engine_set_status_handler (ctx->engine, export_status_handler, ctx); return _gpgme_engine_op_export_ext (ctx->engine, pattern, mode, keydata, ctx->use_armor); } /* Export the keys listed in PATTERN into KEYDATA. */ gpgme_error_t gpgme_op_export_ext_start (gpgme_ctx_t ctx, const char *pattern[], gpgme_export_mode_t mode, gpgme_data_t keydata) { gpgme_error_t err; TRACE_BEG (DEBUG_CTX, "gpgme_op_export_ext_start", ctx, "mode=0x%x, keydata=%p", mode, keydata); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); if (_gpgme_debug_trace () && pattern) { int i = 0; while (pattern[i]) { TRACE_LOG ("pattern[%i] = %s", i, pattern[i]); i++; } } err = export_ext_start (ctx, 0, pattern, mode, keydata); return TRACE_ERR (err); } /* Export the keys listed in PATTERN into KEYDATA. */ gpgme_error_t gpgme_op_export_ext (gpgme_ctx_t ctx, const char *pattern[], gpgme_export_mode_t mode, gpgme_data_t keydata) { gpgme_error_t err; TRACE_BEG (DEBUG_CTX, "gpgme_op_export_ext_start", ctx, "mode=0x%x, keydata=%p", mode, keydata); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); if (_gpgme_debug_trace () && pattern) { int i = 0; while (pattern[i]) { TRACE_LOG ("pattern[%i] = %s", i, pattern[i]); i++; } } err = export_ext_start (ctx, 1, pattern, mode, keydata); if (!err) { - err = _gpgme_wait_one (ctx); + err = _gpgme_sync_wait (ctx, NULL, NULL); if (!err) { /* For this synchronous operation we check for operational errors and return them. For asynchronous operations there is currently no way to do this - we need to add a gpgme_op_export_result function to fix that. */ void *hook; op_data_t opd; err = _gpgme_op_data_lookup (ctx, OPDATA_EXPORT, &hook, -1, NULL); opd = hook; if (!err) err = opd->err; } } return TRACE_ERR (err); } static gpgme_error_t export_keys_start (gpgme_ctx_t ctx, int synchronous, gpgme_key_t keys[], gpgme_export_mode_t mode, gpgme_data_t keydata) { gpgme_error_t err; int nkeys, idx; char **pattern; if (!keys) return gpg_error (GPG_ERR_INV_VALUE); /* Create a list of pattern from the keys. */ for (idx=nkeys=0; keys[idx]; idx++) if (keys[idx]->protocol == ctx->protocol) nkeys++; if (!nkeys) return gpg_error (GPG_ERR_NO_DATA); pattern = calloc (nkeys+1, sizeof *pattern); if (!pattern) return gpg_error_from_syserror (); for (idx=nkeys=0; keys[idx]; idx++) if (keys[idx]->protocol == ctx->protocol && keys[idx]->subkeys && keys[idx]->subkeys->fpr && *keys[idx]->subkeys->fpr) { pattern[nkeys] = strdup (keys[idx]->subkeys->fpr); if (!pattern[nkeys]) { err = gpg_error_from_syserror (); goto leave; } nkeys++; } /* Pass on to the regular function. */ err = export_ext_start (ctx, synchronous, (const char**)pattern, mode, keydata); leave: for (idx=0; pattern[idx]; idx++) free (pattern[idx]); free (pattern); return err; } /* Export the keys from the array KEYS into KEYDATA. Only keys of the current protocol are exported and only those which have a fingerprint set; that is keys received with some external search methods are silently skipped. */ gpgme_error_t gpgme_op_export_keys_start (gpgme_ctx_t ctx, gpgme_key_t keys[], gpgme_export_mode_t mode, gpgme_data_t keydata) { gpg_error_t err; TRACE_BEG (DEBUG_CTX, "gpgme_op_export_keys_start", ctx, "mode=0x%x, keydata=%p", mode, keydata); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); if (_gpgme_debug_trace () && keys) { int i = 0; while (keys[i]) { TRACE_LOG ("keys[%i] = %p (%s)", i, keys[i], (keys[i]->subkeys && keys[i]->subkeys->fpr) ? keys[i]->subkeys->fpr : "invalid"); i++; } } err = export_keys_start (ctx, 0, keys, mode, keydata); return TRACE_ERR (err); } gpgme_error_t gpgme_op_export_keys (gpgme_ctx_t ctx, gpgme_key_t keys[], gpgme_export_mode_t mode, gpgme_data_t keydata) { gpgme_error_t err; TRACE_BEG (DEBUG_CTX, "gpgme_op_export_keys", ctx, "mode=0x%x, keydata=%p", mode, keydata); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); if (_gpgme_debug_trace () && keys) { int i = 0; while (keys[i]) { TRACE_LOG ("keys[%i] = %p (%s)", i, keys[i], (keys[i]->subkeys && keys[i]->subkeys->fpr) ? keys[i]->subkeys->fpr : "invalid"); i++; } } err = export_keys_start (ctx, 1, keys, mode, keydata); if (!err) { - err = _gpgme_wait_one (ctx); + err = _gpgme_sync_wait (ctx, NULL, NULL); if (!err) { /* For this synchronous operation we check for operational errors and return them. For asynchronous operations there is currently no way to do this - we need to add a gpgme_op_export_result function to fix that. */ void *hook; op_data_t opd; err = _gpgme_op_data_lookup (ctx, OPDATA_EXPORT, &hook, -1, NULL); opd = hook; if (!err) err = opd->err; } } return TRACE_ERR (err); } - diff --git a/src/fdtable.c b/src/fdtable.c index de240fc3..13d6ef94 100644 --- a/src/fdtable.c +++ b/src/fdtable.c @@ -1,680 +1,680 @@ /* fdtable.c - Keep track of file descriptors. * Copyright (C) 2019 g10 Code GmbH * * This file is part of GPGME. * * GPGME is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * GPGME is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, see . * SPDX-License-Identifier: LGPL-2.1-or-later */ #if HAVE_CONFIG_H #include #endif #include #include #include "gpgme.h" #include "debug.h" #include "context.h" #include "fdtable.h" /* The table to hold information about file descriptors. Currently we * use a linear search and extend the table as needed. Eventually we * may swicth to a hash table and allocate items on the fly. */ struct fdtable_item_s { int fd; /* -1 indicates an unused entry. */ uint64_t owner; /* The S/N of the context owning this FD. */ /* ACTIVE is set if this fd is in the global event loop, has an * active callback (.io_cb), and has seen the start event. */ unsigned int active:1; /* DONE is set if this fd was previously active but is not active * any longer, either because is finished successfully or its I/O * callback returned an error. Note that ACTIVE and DONE should * never both be set. */ unsigned int done:1; /* Infos for io_select. */ unsigned int for_read:1; unsigned int for_write:1; unsigned int signaled:1; /* We are in a closing handler. Note that while this flag is active * the remove code holds an index into the table. Thus we better * make sure that the index won't change. Or change the removal * code to re-find the fd. */ unsigned int closing:1; /* We are currently running the IO callback. */ unsigned int io_cb_running:1; /* The I/O callback handler with its value context. */ struct { gpgme_io_cb_t cb; void *value; } io_cb; /* The error code and the operational error for the done status. */ gpg_error_t done_status; gpg_error_t done_op_err; /* The callback to be called before the descriptor is actually closed. */ struct { fdtable_handler_t handler; void *value; } close_notify; }; typedef struct fdtable_item_s *fdtable_item_t; /* The actual table, its size and the lock to guard access. */ static fdtable_item_t fdtable; static unsigned int fdtablesize; DEFINE_STATIC_LOCK (fdtable_lock); /* Insert FD into our file descriptor table. This function checks * that FD is not yet in the table. On success 0 is returned; if FD * is already in the table GPG_ERR_DUP_KEY is returned. Other error * codes may also be returned. */ gpg_error_t _gpgme_fdtable_insert (int fd) { gpg_error_t err; int firstunused, idx; TRACE_BEG (DEBUG_SYSIO, __func__, NULL, "fd=%d", fd); if (fd < 0 ) return TRACE_ERR (gpg_error (GPG_ERR_INV_ARG)); LOCK (fdtable_lock); firstunused = -1; for (idx=0; idx < fdtablesize; idx++) if (fdtable[idx].fd == -1) { if (firstunused == -1) firstunused = idx; } else if (fdtable[idx].fd == fd) { err = gpg_error (GPG_ERR_DUP_KEY); goto leave; } if (firstunused == -1) { /* We need to increase the size of the table. The approach we * take is straightforward to minimize the risk of bugs. */ fdtable_item_t newtbl; size_t newsize = fdtablesize + 64; newtbl = calloc (newsize, sizeof *newtbl); if (!newtbl) { err = gpg_error_from_syserror (); goto leave; } for (idx=0; idx < fdtablesize; idx++) newtbl[idx] = fdtable[idx]; for (; idx < newsize; idx++) newtbl[idx].fd = -1; free (fdtable); fdtable = newtbl; idx = fdtablesize; fdtablesize = newsize; } else idx = firstunused; fdtable[idx].fd = fd; fdtable[idx].owner = 0; fdtable[idx].active = 0; fdtable[idx].done = 0; fdtable[idx].for_read = 0; fdtable[idx].for_write = 0; fdtable[idx].signaled = 0; fdtable[idx].closing = 0; fdtable[idx].io_cb_running = 0; fdtable[idx].io_cb.cb = NULL; fdtable[idx].io_cb.value = NULL; fdtable[idx].close_notify.handler = NULL; fdtable[idx].close_notify.value = NULL; err = 0; leave: UNLOCK (fdtable_lock); return TRACE_ERR (err); } /* Add the close notification HANDLER to the table under the key FD. * FD must exist. VALUE is a pointer passed to the handler along with * the FD. */ gpg_error_t _gpgme_fdtable_add_close_notify (int fd, fdtable_handler_t handler, void *value) { gpg_error_t err; int idx; TRACE_BEG (DEBUG_SYSIO, __func__, NULL, "fd=%d", fd); if (fd < 0 ) return TRACE_ERR (gpg_error (GPG_ERR_INV_ARG)); LOCK (fdtable_lock); for (idx=0; idx < fdtablesize; idx++) if (fdtable[idx].fd == fd) break; if (idx == fdtablesize) { err = gpg_error (GPG_ERR_NO_KEY); goto leave; } if (fdtable[idx].close_notify.handler) { err = gpg_error (GPG_ERR_DUP_VALUE); goto leave; } fdtable[idx].close_notify.handler = handler; fdtable[idx].close_notify.value = value; err = 0; leave: UNLOCK (fdtable_lock); return TRACE_ERR (err); } /* Set the I/O callback for the FD. FD must already exist otherwise * GPG_ERR_NO_KEY is returned. OWNER is the serial of the owning * context. If DIRECTION is 1 the callback wants to read from it; if * it is 0 the callback want to write to it. CB is the actual * callback and CB_VALUE the values passed to that callback. If a * callback as already been set GPG_ERR_DUP_VALUE is returned. To * remove the handler, FD and OWNER must be passed as usual but CB be * passed as NULL. */ gpg_error_t _gpgme_fdtable_set_io_cb (int fd, uint64_t owner, int direction, gpgme_io_cb_t cb, void *cb_value) { gpg_error_t err; int idx; TRACE_BEG (DEBUG_SYSIO, __func__, NULL, "fd=%d ctx=%lu dir=%d", fd, (unsigned long)owner, direction); if (fd < 0 || !owner) return TRACE_ERR (gpg_error (GPG_ERR_INV_ARG)); LOCK (fdtable_lock); if (cb) { for (idx=0; idx < fdtablesize; idx++) if (fdtable[idx].fd == fd) break; if (idx == fdtablesize) { err = gpg_error (GPG_ERR_NO_KEY); TRACE_LOG ("with_cb: fd=%d owner=%lu", fd, (unsigned long)owner); goto leave; } if (fdtable[idx].io_cb.cb) { err = gpg_error (GPG_ERR_DUP_VALUE); goto leave; } fdtable[idx].owner = owner; fdtable[idx].for_read = (direction == 1); fdtable[idx].for_write = (direction == 0); fdtable[idx].signaled = 0; fdtable[idx].io_cb.cb = cb; fdtable[idx].io_cb.value = cb_value; } else /* Remove. */ { /* We compare also the owner as a cross-check. */ for (idx=0; idx < fdtablesize; idx++) if (fdtable[idx].fd == fd && fdtable[idx].owner == owner) break; if (idx == fdtablesize) { err = gpg_error (GPG_ERR_NO_KEY); TRACE_LOG ("remove: fd=%d owner=%lu", fd, (unsigned long)owner); for (idx=0; idx < fdtablesize; idx++) TRACE_LOG (" TBL: fd=%d owner=%lu", fdtable[idx].fd, (unsigned long)fdtable[idx].owner); goto leave; } fdtable[idx].for_read = 0; fdtable[idx].for_write = 0; fdtable[idx].signaled = 0; fdtable[idx].io_cb.cb = NULL; fdtable[idx].io_cb.value = NULL; fdtable[idx].owner = 0; } err = 0; leave: UNLOCK (fdtable_lock); return TRACE_ERR (err); } /* Set all FDs of OWNER into the active state. */ gpg_error_t _gpgme_fdtable_set_active (uint64_t owner) { int idx; TRACE_BEG (DEBUG_SYSIO, __func__, NULL, "ctx=%lu", (unsigned long)owner); if (!owner ) return TRACE_ERR (gpg_error (GPG_ERR_INV_ARG)); LOCK (fdtable_lock); for (idx=0; idx < fdtablesize; idx++) if (fdtable[idx].fd != -1 && fdtable[idx].owner == owner && fdtable[idx].io_cb.cb) { fdtable[idx].active = 1; fdtable[idx].done = 0; } UNLOCK (fdtable_lock); return TRACE_ERR (0); } /* Set all FDs of OWNER into the done state. STATUS and OP_ERR are * recorded. */ gpg_error_t _gpgme_fdtable_set_done (uint64_t owner, gpg_error_t status, gpg_error_t op_err) { int idx; TRACE_BEG (DEBUG_SYSIO, __func__, NULL, "ctx=%lu", (unsigned long)owner); if (!owner ) return TRACE_ERR (gpg_error (GPG_ERR_INV_ARG)); LOCK (fdtable_lock); for (idx=0; idx < fdtablesize; idx++) if (fdtable[idx].fd != -1 && fdtable[idx].owner == owner && fdtable[idx].active) { fdtable[idx].active = 0; fdtable[idx].done = 1; fdtable[idx].done_status = status; fdtable[idx].done_op_err = op_err; } UNLOCK (fdtable_lock); return TRACE_ERR (0); } /* Walk over all fds in FDS and copy the signaled flag if set. It * does not clear any signal flag in the global table. */ void _gpgme_fdtable_set_signaled (io_select_t fds, unsigned int nfds) { int idx; unsigned int n, count; if (!nfds) return; /* FIXME: Highly inefficient code in case of large select lists. */ count = 0; LOCK (fdtable_lock); for (idx=0; idx < fdtablesize; idx++) { if (fdtable[idx].fd == -1) continue; for (n = 0; n < nfds; n++) if (fdtable[idx].fd == fds[n].fd) { if (fds[n].signaled && !fdtable[idx].signaled) { fdtable[idx].signaled = 1; count++; /* Only for tracing. */ } break; } } UNLOCK (fdtable_lock); TRACE (DEBUG_SYSIO, __func__, NULL, "fds newly signaled=%u", count); } /* Remove FD from the table after calling the close handler. Note * that at the time the close handler is called the FD has been * removed form the table. Thus the close handler may not access the * fdtable anymore and assume that FD is still there. Callers may * want to handle the error code GPG_ERR_NO_KEY which indicates that * FD is not anymore or not yet in the table. */ gpg_error_t _gpgme_fdtable_remove (int fd) { gpg_error_t err; int idx; fdtable_handler_t handler; void *handlervalue; TRACE_BEG (DEBUG_SYSIO, __func__, NULL, "fd=%d", fd); if (fd < 0 ) return TRACE_ERR (gpg_error (GPG_ERR_INV_ARG)); LOCK (fdtable_lock); for (idx=0; idx < fdtablesize; idx++) if (fdtable[idx].fd == fd) break; if (idx == fdtablesize) { UNLOCK (fdtable_lock); return TRACE_ERR (gpg_error (GPG_ERR_NO_KEY)); } TRACE_LOG ("removal of fd=%d owner=%lu (closing=%d)", fdtable[idx].fd, (unsigned long)fdtable[idx].owner, fdtable[idx].closing); handler = fdtable[idx].close_notify.handler; fdtable[idx].close_notify.handler = NULL; handlervalue = fdtable[idx].close_notify.value; fdtable[idx].close_notify.value = NULL; /* The handler might call into the fdtable again, so of we have a * handler we can't immediately close it but instead record the fact * and remove the entry from the table only after the handler has * been run. */ if (handler) fdtable[idx].closing = 1; else if (!fdtable[idx].closing) fdtable[idx].fd = -1; UNLOCK (fdtable_lock); if (handler) { err = handler (fd, handlervalue); LOCK (fdtable_lock); TRACE_LOG ("final removal of fd=%d owner=%lu (closing=%d)", fdtable[idx].fd, (unsigned long)fdtable[idx].owner, fdtable[idx].closing); fdtable[idx].fd = -1; UNLOCK (fdtable_lock); } else err = 0; return TRACE_ERR (err); } /* Return the number of active I/O callbacks for OWNER or for all if * OWNER is 0. */ unsigned int _gpgme_fdtable_io_cb_count (uint64_t owner) { int idx; unsigned int count = 0; LOCK (fdtable_lock); for (idx=0; idx < fdtablesize; idx++) if (fdtable[idx].fd != -1 && (!owner || fdtable[idx].owner == owner)) count++; UNLOCK (fdtable_lock); TRACE (DEBUG_SYSIO, __func__, NULL, "ctx=%lu count=%u", (unsigned long)owner, count); return count; } /* Run all signaled IO callbacks of OWNER or all signaled callbacks if * OWNER is 0. Returns an error code on the first real error * encountered. If R_OP_ERR is not NULL an optional operational error * can be stored tehre. For EOF the respective flags are set. */ gpg_error_t _gpgme_fdtable_run_io_cbs (uint64_t owner, gpg_error_t *r_op_err) { gpg_error_t err; int idx; int fd; gpgme_io_cb_t iocb; struct io_cb_data iocb_data; uint64_t serial; unsigned int cb_count; gpgme_ctx_t actx; + TRACE_BEG (DEBUG_SYSIO, __func__, NULL, "ctx=%lu", owner); + if (r_op_err) *r_op_err = 0; - TRACE_BEG (DEBUG_SYSIO, __func__, NULL, "ctx=%lu", owner); - for (;;) { fd = -1; LOCK (fdtable_lock); for (idx=0; idx < fdtablesize; idx++) if (fdtable[idx].fd != -1 && (!owner || fdtable[idx].owner == owner) && fdtable[idx].signaled) { fd = fdtable[idx].fd; serial = fdtable[idx].owner; iocb = fdtable[idx].io_cb.cb; iocb_data.handler_value = fdtable[idx].io_cb.value; iocb_data.op_err = 0; fdtable[idx].signaled = 0; if (iocb) { fdtable[idx].io_cb_running = 1; break; } } UNLOCK (fdtable_lock); if (fd == -1) break; /* No more callbacks found. */ /* If the context object is still valid and has not been * canceled, we run the I/O callback. */ err = _gpgme_get_ctx (serial, &actx); if (!err) { err = iocb (&iocb_data, fd); if (err) TRACE_LOG ("iocb(fd=%d) err=%s", fd, gpg_strerror (err)); } /* Clear the running flag and while we are at it also count the * remaining callbacks. */ cb_count = 0; LOCK (fdtable_lock); for (idx=0; idx < fdtablesize; idx++) { if (fdtable[idx].fd == -1) continue; if (fdtable[idx].fd == fd) fdtable[idx].io_cb_running = 0; if (fdtable[idx].owner == serial) cb_count++; } UNLOCK (fdtable_lock); /* Handle errors or success from the IO callback. In the error * case we close all fds belonging to the same context. In the * success case we check whether any callback is left and only * if that is not the case, tell the engine that we are done. * The latter indirectly sets the fd into the done state. */ if (err) { _gpgme_cancel_with_err (serial, err, 0); return TRACE_ERR (err); } else if (iocb_data.op_err) { /* An operational error occurred. Cancel the current * operation but not the session, and signal it. */ _gpgme_cancel_with_err (serial, 0, iocb_data.op_err); /* NOTE: This relies on the operational error being * generated after the operation really has completed, for * example after no further status line output is generated. * Otherwise the following I/O will spill over into the next * operation. */ if (r_op_err) *r_op_err = iocb_data.op_err; return TRACE_ERR (0); } else if (!cb_count && actx) { struct gpgme_io_event_done_data data = { 0, 0 }; _gpgme_engine_io_event (actx->engine, GPGME_EVENT_DONE, &data); } } return TRACE_ERR (0); } /* Retrieve a list of file descriptors owned by OWNER, or with OWNER * being 0 of all fds, and store that list as a new array at R_FDS. * Return the number of FDS in that list or 0 if none were selected. * FLAGS give further selection flags: * FDTABLE_FLAG_ACTIVE - Only those with the active flag set. * FDTABLE_FLAG_DONE - Only those with the done flag set. * FDTABLE_FLAG_FOR_READ - Only those with the readable FDs. * FDTABLE_FLAG_FOR_WRITE - Only those with the writable FDs. * FDTABLE_FLAG_SIGNALED - Only those with the signaled flag set. * FDTABLE_FLAG_NOT_SIGNALED - Only those with the signaled flag cleared. * FDTABLE_FLAG_CLEAR - Clear the signaled flag.. */ unsigned int _gpgme_fdtable_get_fds (io_select_t *r_fds, uint64_t owner, unsigned int flags) { int idx; unsigned int count = 0; io_select_t fds; *r_fds = NULL; gpg_err_set_errno (0); /* We take an easy approach and allocate the array at the size of * the entire fdtable. */ fds = calloc (fdtablesize, sizeof *fds); if (!fds) return 0; LOCK (fdtable_lock); for (idx=0; idx < fdtablesize; idx++) if (fdtable[idx].fd != -1 && (!owner || fdtable[idx].owner == owner)) { if ((flags & FDTABLE_FLAG_ACTIVE) && !fdtable[idx].active) continue; if ((flags & FDTABLE_FLAG_DONE) && !fdtable[idx].done) continue; if ((flags & FDTABLE_FLAG_FOR_READ) && !fdtable[idx].for_read) continue; if ((flags & FDTABLE_FLAG_FOR_WRITE) && !fdtable[idx].for_write) continue; if ((flags & FDTABLE_FLAG_SIGNALED) && !fdtable[idx].signaled) continue; if ((flags & FDTABLE_FLAG_NOT_SIGNALED) && fdtable[idx].signaled) continue; if (fdtable[idx].io_cb_running || fdtable[idx].closing) continue; /* The callback has not yet finished or we are * already closing. Does not make sense to allow * selecting on it. */ fds[count].fd = fdtable[idx].fd; fds[count].for_read = fdtable[idx].for_read; fds[count].for_write = fdtable[idx].for_write; fds[count].signaled = (flags & FDTABLE_FLAG_SIGNALED)? 0 : fdtable[idx].signaled; count++; } UNLOCK (fdtable_lock); *r_fds = fds; TRACE (DEBUG_SYSIO, __func__, NULL, "ctx=%lu count=%u", (unsigned long)owner, count); return count; } /* If OWNER is 0 return the status info of the first fd with the done * flag set. If OWNER is not 0 search for a matching owner with the * done flag set and return its status info. Returns the serial * number of the context found. */ uint64_t _gpgme_fdtable_get_done (uint64_t owner, gpg_error_t *r_status, gpg_error_t *r_op_err) { uint64_t serial = 0; int idx; TRACE_BEG (DEBUG_SYSIO, __func__, NULL, "ctx=%lu", (unsigned long)owner); LOCK (fdtable_lock); for (idx=0; idx < fdtablesize; idx++) if (fdtable[idx].fd != -1 && (!owner || fdtable[idx].owner == owner) && fdtable[idx].done) { /* Found. If an owner has been given also clear the done * flags from all other fds of this owner. Note that they * have the same status info anyway. */ *r_status = fdtable[idx].done_status; *r_op_err = fdtable[idx].done_op_err; fdtable[idx].done = 0; serial = fdtable[idx].owner; if (owner) { for (; idx < fdtablesize; idx++) if (fdtable[idx].fd != -1 && fdtable[idx].owner == owner) fdtable[idx].done = 0; } break; } UNLOCK (fdtable_lock); TRACE_SUC ("ctx=%lu", (unsigned long)serial); return serial; } diff --git a/src/genkey.c b/src/genkey.c index 77576b18..e23ef2d6 100644 --- a/src/genkey.c +++ b/src/genkey.c @@ -1,665 +1,665 @@ /* genkey.c - Key generation. * Copyright (C) 2000 Werner Koch (dd9jn) * Copyright (C) 2001, 2002, 2003, 2004, 2016 g10 Code GmbH * * This file is part of GPGME. * * GPGME is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * GPGME is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, see . * SPDX-License-Identifier: LGPL-2.1-or-later */ #if HAVE_CONFIG_H #include #endif #include #include #include #include "gpgme.h" #include "debug.h" #include "context.h" #include "ops.h" #include "util.h" typedef struct { struct _gpgme_op_genkey_result result; /* The error code from a FAILURE status line or 0. */ gpg_error_t failure_code; /* The error code from certain ERROR status lines or 0. */ gpg_error_t error_code; /* Flag to indicate that a UID is to be added. */ gpg_error_t uidmode; /* The key parameters passed to the crypto engine. */ gpgme_data_t key_parameter; } *op_data_t; static void release_op_data (void *hook) { op_data_t opd = (op_data_t) hook; if (opd->result.fpr) free (opd->result.fpr); if (opd->key_parameter) gpgme_data_release (opd->key_parameter); } gpgme_genkey_result_t gpgme_op_genkey_result (gpgme_ctx_t ctx) { void *hook; op_data_t opd; gpgme_error_t err; TRACE_BEG (DEBUG_CTX, "gpgme_op_genkey_result", ctx, ""); err = _gpgme_op_data_lookup (ctx, OPDATA_GENKEY, &hook, -1, NULL); opd = hook; if (err || !opd) { TRACE_SUC ("result=(null)"); return NULL; } TRACE_LOG ("fpr = %s, %s, %s", opd->result.fpr, opd->result.primary ? "primary" : "no primary", opd->result.sub ? "sub" : "no sub"); TRACE_SUC ("result=%p", &opd->result); return &opd->result; } /* Parse an error status line. Return the error location and the error code. The function may modify ARGS. */ static char * parse_error (char *args, gpg_error_t *r_err) { char *where = strchr (args, ' '); char *which; if (where) { *where = '\0'; which = where + 1; where = strchr (which, ' '); if (where) *where = '\0'; where = args; } else { *r_err = trace_gpg_error (GPG_ERR_INV_ENGINE); return NULL; } *r_err = atoi (which); return where; } static gpgme_error_t genkey_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; char *loc; /* Pipe the status code through the progress status handler. */ err = _gpgme_progress_status_handler (ctx, code, args); if (err) return err; err = _gpgme_op_data_lookup (ctx, OPDATA_GENKEY, &hook, -1, NULL); opd = hook; if (err) return err; switch (code) { case GPGME_STATUS_KEY_CREATED: if (args && *args) { if (*args == 'B' || *args == 'P') { opd->result.primary = 1; opd->result.uid = 1; } if (*args == 'B' || *args == 'S') opd->result.sub = 1; if (args[1] == ' ') { if (opd->result.fpr) free (opd->result.fpr); opd->result.fpr = strdup (&args[2]); if (!opd->result.fpr) return gpg_error_from_syserror (); } } break; case GPGME_STATUS_ERROR: loc = parse_error (args, &err); if (!loc) return err; if (!opd->error_code) opd->error_code = err; break; case GPGME_STATUS_FAILURE: opd->failure_code = _gpgme_parse_failure (args); break; case GPGME_STATUS_EOF: if (opd->error_code) return opd->error_code; else if (!opd->uidmode && !opd->result.primary && !opd->result.sub) return gpg_error (GPG_ERR_GENERAL); else if (opd->failure_code) return opd->failure_code; else if (opd->uidmode == 1) opd->result.uid = 1; /* We have no status line, thus this hack. */ break; case GPGME_STATUS_INQUIRE_MAXLEN: if (ctx->status_cb && !ctx->full_status) { err = ctx->status_cb (ctx->status_cb_value, "INQUIRE_MAXLEN", args); if (err) return err; } break; default: break; } return 0; } static gpgme_error_t get_key_parameter (const char *parms, gpgme_data_t *key_parameter) { const char *content; const char *attrib; const char *endtag; /* Extract the key parameter from the XML structure. */ parms = strstr (parms, "'); if (!content) return gpg_error (GPG_ERR_INV_VALUE); content++; attrib = strstr (parms, "format=\"internal\""); if (!attrib || attrib >= content) return gpg_error (GPG_ERR_INV_VALUE); endtag = strstr (content, ""); if (!endtag) endtag = content + strlen (content); /* FIXME: Check that there are no control statements inside. */ while (content < endtag && (content[0] == '\n' || (content[0] == '\r' && content[1] == '\n'))) content++; return gpgme_data_new_from_mem (key_parameter, content, endtag - content, 1); } static gpgme_error_t genkey_start (gpgme_ctx_t ctx, int synchronous, const char *parms, gpgme_data_t pubkey, gpgme_data_t seckey) { gpgme_error_t err; void *hook; op_data_t opd; err = _gpgme_op_reset (ctx, synchronous); if (err) return err; err = _gpgme_op_data_lookup (ctx, OPDATA_GENKEY, &hook, sizeof (*opd), release_op_data); opd = hook; if (err) return err; err = get_key_parameter (parms, &opd->key_parameter); if (err) return err; _gpgme_engine_set_status_handler (ctx->engine, genkey_status_handler, ctx); if (ctx->passphrase_cb) { err = _gpgme_engine_set_command_handler (ctx->engine, _gpgme_passphrase_command_handler, ctx); if (err) return err; } return _gpgme_engine_op_genkey (ctx->engine, NULL, NULL, 0, 0, NULL, 0, opd->key_parameter, ctx->use_armor? GENKEY_EXTRAFLAG_ARMOR:0, pubkey, seckey); } /* Generate a new keypair and add it to the keyring. PUBKEY and SECKEY should be null for now. PARMS specifies what keys should be generated. */ gpgme_error_t gpgme_op_genkey_start (gpgme_ctx_t ctx, const char *parms, gpgme_data_t pubkey, gpgme_data_t seckey) { gpgme_error_t err; TRACE_BEG (DEBUG_CTX, "gpgme_op_genkey_start", ctx, "pubkey=%p, seckey=%p", pubkey, seckey); TRACE_LOGBUF (parms, parms? strlen (parms):0); if (!ctx || !parms) return TRACE_ERR (gpg_error (GPG_ERR_INV_ARG)); err = genkey_start (ctx, 0, parms, pubkey, seckey); return TRACE_ERR (err); } /* Generate a new keypair and add it to the keyring. PUBKEY and SECKEY should be null for now. PARMS specifies what keys should be generated. */ gpgme_error_t gpgme_op_genkey (gpgme_ctx_t ctx, const char *parms, gpgme_data_t pubkey, gpgme_data_t seckey) { gpgme_error_t err; TRACE_BEG (DEBUG_CTX, "gpgme_op_genkey", ctx, "pubkey=%p, seckey=%p", pubkey, seckey); TRACE_LOGBUF (parms, parms? strlen (parms):0); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_ARG)); err = genkey_start (ctx, 1, parms, pubkey, seckey); if (!err) - err = _gpgme_wait_one (ctx); + err = _gpgme_sync_wait (ctx, NULL, NULL); return TRACE_ERR (err); } static gpgme_error_t createkey_start (gpgme_ctx_t ctx, int synchronous, const char *userid, const char *algo, unsigned long reserved, unsigned long expires, gpgme_key_t anchorkey, unsigned int flags) { gpgme_error_t err; void *hook; op_data_t opd; err = _gpgme_op_reset (ctx, synchronous); if (err) return err; if (reserved || anchorkey || !userid) return gpg_error (GPG_ERR_INV_ARG); err = _gpgme_op_data_lookup (ctx, OPDATA_GENKEY, &hook, sizeof (*opd), release_op_data); opd = hook; if (err) return err; _gpgme_engine_set_status_handler (ctx->engine, genkey_status_handler, ctx); if (ctx->passphrase_cb) { err = _gpgme_engine_set_command_handler (ctx->engine, _gpgme_passphrase_command_handler, ctx); if (err) return err; } return _gpgme_engine_op_genkey (ctx->engine, userid, algo, reserved, expires, anchorkey, flags, NULL, ctx->use_armor? GENKEY_EXTRAFLAG_ARMOR:0, NULL, NULL); } gpgme_error_t gpgme_op_createkey_start (gpgme_ctx_t ctx, const char *userid, const char *algo, unsigned long reserved, unsigned long expires, gpgme_key_t anchorkey, unsigned int flags) { gpgme_error_t err; TRACE_BEG (DEBUG_CTX, "gpgme_op_createkey_start", ctx, "userid='%s', algo='%s' flags=0x%x", userid, algo, flags); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_ARG)); err = createkey_start (ctx, 0, userid, algo, reserved, expires, anchorkey, flags); return TRACE_ERR (err); } gpgme_error_t gpgme_op_createkey (gpgme_ctx_t ctx, const char *userid, const char *algo, unsigned long reserved, unsigned long expires, gpgme_key_t anchorkey, unsigned int flags) { gpgme_error_t err; TRACE_BEG (DEBUG_CTX, "gpgme_op_createkey", ctx, "userid='%s', algo='%s' flags=0x%x", userid, algo, flags); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_ARG)); err = createkey_start (ctx, 1, userid, algo, reserved, expires, anchorkey, flags); if (!err) - err = _gpgme_wait_one (ctx); + err = _gpgme_sync_wait (ctx, NULL, NULL); return TRACE_ERR (err); } static gpgme_error_t createsubkey_start (gpgme_ctx_t ctx, int synchronous, gpgme_key_t key, const char *algo, unsigned long reserved, unsigned long expires, unsigned int flags) { gpgme_error_t err; void *hook; op_data_t opd; if (ctx->protocol != GPGME_PROTOCOL_OPENPGP) return gpgme_error (GPG_ERR_UNSUPPORTED_PROTOCOL); err = _gpgme_op_reset (ctx, synchronous); if (err) return err; if (reserved || !key) return gpg_error (GPG_ERR_INV_ARG); err = _gpgme_op_data_lookup (ctx, OPDATA_GENKEY, &hook, sizeof (*opd), release_op_data); opd = hook; if (err) return err; _gpgme_engine_set_status_handler (ctx->engine, genkey_status_handler, ctx); if (ctx->passphrase_cb) { err = _gpgme_engine_set_command_handler (ctx->engine, _gpgme_passphrase_command_handler, ctx); if (err) return err; } return _gpgme_engine_op_genkey (ctx->engine, NULL, algo, reserved, expires, key, flags, NULL, ctx->use_armor? GENKEY_EXTRAFLAG_ARMOR:0, NULL, NULL); } /* Add a subkey to an existing KEY. */ gpgme_error_t gpgme_op_createsubkey_start (gpgme_ctx_t ctx, gpgme_key_t key, const char *algo, unsigned long reserved, unsigned long expires, unsigned int flags) { gpgme_error_t err; TRACE_BEG (DEBUG_CTX, "gpgme_op_createsubkey_start", ctx, "key=%p, algo='%s' flags=0x%x", key, algo, flags); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_ARG)); err = createsubkey_start (ctx, 0, key, algo, reserved, expires, flags); return TRACE_ERR (err); } gpgme_error_t gpgme_op_createsubkey (gpgme_ctx_t ctx, gpgme_key_t key, const char *algo, unsigned long reserved, unsigned long expires, unsigned int flags) { gpgme_error_t err; TRACE_BEG (DEBUG_CTX, "gpgme_op_createsubkey", ctx, "key=%p, algo='%s' flags=0x%x", key, algo, flags); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_ARG)); err = createsubkey_start (ctx, 1, key, algo, reserved, expires, flags); if (!err) - err = _gpgme_wait_one (ctx); + err = _gpgme_sync_wait (ctx, NULL, NULL); return TRACE_ERR (err); } static gpgme_error_t addrevuid_start (gpgme_ctx_t ctx, int synchronous, int extraflags, gpgme_key_t key, const char *userid, unsigned int flags) { gpgme_error_t err; void *hook; op_data_t opd; if (ctx->protocol != GPGME_PROTOCOL_OPENPGP) return gpgme_error (GPG_ERR_UNSUPPORTED_PROTOCOL); if (!key || !userid) return gpg_error (GPG_ERR_INV_ARG); err = _gpgme_op_reset (ctx, synchronous); if (err) return err; err = _gpgme_op_data_lookup (ctx, OPDATA_GENKEY, &hook, sizeof (*opd), release_op_data); opd = hook; if (err) return err; opd->uidmode = extraflags? 2 : 1; _gpgme_engine_set_status_handler (ctx->engine, genkey_status_handler, ctx); if (ctx->passphrase_cb) { err = _gpgme_engine_set_command_handler (ctx->engine, _gpgme_passphrase_command_handler, ctx); if (err) return err; } return _gpgme_engine_op_genkey (ctx->engine, userid, NULL, 0, 0, key, flags, NULL, extraflags, NULL, NULL); } /* Add USERID to an existing KEY. */ gpgme_error_t gpgme_op_adduid_start (gpgme_ctx_t ctx, gpgme_key_t key, const char *userid, unsigned int flags) { gpgme_error_t err; TRACE_BEG (DEBUG_CTX, "gpgme_op_adduid_start", ctx, "uid='%s' flags=0x%x", userid, flags); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_ARG)); err = addrevuid_start (ctx, 0, 0, key, userid, flags); return TRACE_ERR (err); } gpgme_error_t gpgme_op_adduid (gpgme_ctx_t ctx, gpgme_key_t key, const char *userid, unsigned int flags) { gpgme_error_t err; TRACE_BEG (DEBUG_CTX, "gpgme_op_adduid", ctx, "uid='%s' flags=0x%x", userid, flags); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_ARG)); err = addrevuid_start (ctx, 1, 0, key, userid, flags); if (!err) - err = _gpgme_wait_one (ctx); + err = _gpgme_sync_wait (ctx, NULL, NULL); return TRACE_ERR (err); } /* Revoke USERID from KEY. */ gpgme_error_t gpgme_op_revuid_start (gpgme_ctx_t ctx, gpgme_key_t key, const char *userid, unsigned int flags) { gpgme_error_t err; TRACE_BEG (DEBUG_CTX, "gpgme_op_revuid_start", ctx, "uid='%s' flags=0x%x", userid, flags); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_ARG)); err = addrevuid_start (ctx, 0, GENKEY_EXTRAFLAG_REVOKE, key, userid, flags); return TRACE_ERR (err); } gpgme_error_t gpgme_op_revuid (gpgme_ctx_t ctx, gpgme_key_t key, const char *userid, unsigned int flags) { gpgme_error_t err; TRACE_BEG (DEBUG_CTX, "gpgme_op_revuid", ctx, "uid='%s' flags=0x%x", userid, flags); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_ARG)); err = addrevuid_start (ctx, 1, GENKEY_EXTRAFLAG_REVOKE, key, userid, flags); if (!err) - err = _gpgme_wait_one (ctx); + err = _gpgme_sync_wait (ctx, NULL, NULL); return TRACE_ERR (err); } /* Set a flag on the USERID of KEY. The only supported flag right now * is "primary" to mark the primary key. */ static gpg_error_t set_uid_flag (gpgme_ctx_t ctx, int synchronous, gpgme_key_t key, const char *userid, const char *name, const char *value) { gpgme_error_t err; TRACE_BEG (DEBUG_CTX, "gpgme_op_set_uid_flag", ctx, "%d uid='%s' '%s'='%s'", synchronous, userid, name, value); if (!ctx || !name || !key || !userid) return TRACE_ERR (gpg_error (GPG_ERR_INV_ARG)); if (!strcmp (name, "primary")) { if (value) err = gpg_error (GPG_ERR_INV_ARG); else err = addrevuid_start (ctx, synchronous, GENKEY_EXTRAFLAG_SETPRIMARY, key, userid, 0); } else return err = gpg_error (GPG_ERR_UNKNOWN_NAME); if (synchronous && !err) - err = _gpgme_wait_one (ctx); + err = _gpgme_sync_wait (ctx, NULL, NULL); return TRACE_ERR (err); } /* See set_uid_flag. */ gpgme_error_t gpgme_op_set_uid_flag_start (gpgme_ctx_t ctx, gpgme_key_t key, const char *userid, const char *name, const char *value) { return set_uid_flag (ctx, 0, key, userid, name, value); } /* See set_uid_flag. This is the synchronous variant. */ gpgme_error_t gpgme_op_set_uid_flag (gpgme_ctx_t ctx, gpgme_key_t key, const char *userid, const char *name, const char *value) { return set_uid_flag (ctx, 1, key, userid, name, value); } diff --git a/src/getauditlog.c b/src/getauditlog.c index 13473873..750dae37 100644 --- a/src/getauditlog.c +++ b/src/getauditlog.c @@ -1,105 +1,104 @@ /* getauditlog.c - Retrieve the audit log. * Copyright (C) 2007 g10 Code GmbH * * This file is part of GPGME. * * GPGME is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * GPGME is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, see . * SPDX-License-Identifier: LGPL-2.1-or-later */ #if HAVE_CONFIG_H #include #endif #include "gpgme.h" #include "debug.h" #include "context.h" #include "ops.h" static gpgme_error_t getauditlog_status_handler (void *priv, gpgme_status_code_t code, char *args) { (void)priv; (void)code; (void)args; return 0; } static gpgme_error_t getauditlog_start (gpgme_ctx_t ctx, int synchronous, gpgme_data_t output, unsigned int flags) { gpgme_error_t err; if (!output) return gpg_error (GPG_ERR_INV_VALUE); if (!(flags & GPGME_AUDITLOG_DIAG)) { err = _gpgme_op_reset (ctx, ((synchronous&255) | 256) ); if (err) return err; } _gpgme_engine_set_status_handler (ctx->engine, getauditlog_status_handler, ctx); return _gpgme_engine_op_getauditlog (ctx->engine, output, flags); } /* Return the auditlog for the current session. This may be called after a successful or failed operation. If no audit log is available GPG_ERR_NO_DATA is returned. This is the asynchronous variant. */ gpgme_error_t gpgme_op_getauditlog_start (gpgme_ctx_t ctx, gpgme_data_t output, unsigned int flags) { gpg_error_t err; TRACE_BEG (DEBUG_CTX, "gpgme_op_getauditlog_start", ctx, "output=%p, flags=0x%x", output, flags); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); err = getauditlog_start (ctx, 0, output, flags); return TRACE_ERR (err); } /* Return the auditlog for the current session. This may be called after a successful or failed operation. If no audit log is available GPG_ERR_NO_DATA is returned. This is the synchronous variant. */ gpgme_error_t gpgme_op_getauditlog (gpgme_ctx_t ctx, gpgme_data_t output, unsigned int flags) { gpgme_error_t err; TRACE_BEG (DEBUG_CTX, "gpgme_op_getauditlog", ctx, "output=%p, flags=0x%x", output, flags); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); err = getauditlog_start (ctx, 1, output, flags); if (!err) - err = _gpgme_wait_one (ctx); + err = _gpgme_sync_wait (ctx, NULL, NULL); return TRACE_ERR (err); } - diff --git a/src/import.c b/src/import.c index 2834aec2..679966ce 100644 --- a/src/import.c +++ b/src/import.c @@ -1,452 +1,452 @@ /* import.c - Import a key. * Copyright (C) 2000 Werner Koch (dd9jn) * Copyright (C) 2001, 2002, 2003, 2004 g10 Code GmbH * * This file is part of GPGME. * * GPGME is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * GPGME is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, see . * SPDX-License-Identifier: LGPL-2.1-or-later */ #if HAVE_CONFIG_H #include #endif #include #include #include #include "gpgme.h" #include "debug.h" #include "context.h" #include "ops.h" #include "util.h" typedef struct { struct _gpgme_op_import_result result; /* A pointer to the next pointer of the last import status in the list. This makes appending new imports painless while preserving the order. */ gpgme_import_status_t *lastp; } *op_data_t; static void release_op_data (void *hook) { op_data_t opd = (op_data_t) hook; gpgme_import_status_t import = opd->result.imports; while (import) { gpgme_import_status_t next = import->next; free (import->fpr); free (import); import = next; } } gpgme_import_result_t gpgme_op_import_result (gpgme_ctx_t ctx) { void *hook; op_data_t opd; gpgme_error_t err; TRACE_BEG (DEBUG_CTX, "gpgme_op_import_result", ctx, ""); err = _gpgme_op_data_lookup (ctx, OPDATA_IMPORT, &hook, -1, NULL); opd = hook; if (err || !opd) { TRACE_SUC ("result=(null)"); return NULL; } if (_gpgme_debug_trace ()) { gpgme_import_status_t impstat; int i; TRACE_LOG ("%i considered, %i no UID, %i imported, %i imported RSA, " "%i unchanged", opd->result.considered, opd->result.no_user_id, opd->result.imported, opd->result.imported_rsa, opd->result.unchanged); TRACE_LOG ("%i new UIDs, %i new sub keys, %i new signatures, " "%i new revocations", opd->result.new_user_ids, opd->result.new_sub_keys, opd->result.new_signatures, opd->result.new_revocations); TRACE_LOG ("%i secret keys, %i imported, %i unchanged", opd->result.secret_read, opd->result.secret_imported, opd->result.secret_unchanged); TRACE_LOG ("%i skipped new keys, %i not imported, %i v3 skipped", opd->result.skipped_new_keys, opd->result.not_imported, opd->result.skipped_v3_keys); impstat = opd->result.imports; i = 0; while (impstat) { TRACE_LOG ("import[%i] for %s = 0x%x (%s)", i, impstat->fpr, impstat->status, gpgme_strerror (impstat->result)); impstat = impstat->next; i++; } } TRACE_SUC ("result=%p", &opd->result); return &opd->result; } 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_syserror (); import->next = NULL; gpg_err_set_errno (0); nr = strtol (args, &tail, 0); if (errno || args == tail || *tail != ' ') { /* The crypto backend does not behave. */ free (import); return trace_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_ISSUER_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) { free (import); return gpg_error_from_syserror (); } *import_status = import; return 0; } gpgme_error_t parse_import_res (char *args, gpgme_import_result_t result) { char *tail; gpg_err_set_errno (0); #define PARSE_NEXT(x) \ (x) = strtol (args, &tail, 0); \ if (errno || args == tail || !(*tail == ' ' || !*tail)) \ /* The crypto backend does not behave. */ \ return trace_gpg_error (GPG_ERR_INV_ENGINE); \ args = tail; PARSE_NEXT (result->considered); PARSE_NEXT (result->no_user_id); PARSE_NEXT (result->imported); PARSE_NEXT (result->imported_rsa); PARSE_NEXT (result->unchanged); PARSE_NEXT (result->new_user_ids); PARSE_NEXT (result->new_sub_keys); PARSE_NEXT (result->new_signatures); PARSE_NEXT (result->new_revocations); PARSE_NEXT (result->secret_read); PARSE_NEXT (result->secret_imported); PARSE_NEXT (result->secret_unchanged); PARSE_NEXT (result->skipped_new_keys); PARSE_NEXT (result->not_imported); if (args && *args) { PARSE_NEXT (result->skipped_v3_keys); } return 0; } static gpgme_error_t import_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_IMPORT, &hook, -1, NULL); opd = hook; if (err) return err; switch (code) { case GPGME_STATUS_IMPORT_OK: case GPGME_STATUS_IMPORT_PROBLEM: err = parse_import (args, opd->lastp, code == GPGME_STATUS_IMPORT_OK ? 0 : 1); if (err) return err; opd->lastp = &(*opd->lastp)->next; break; case GPGME_STATUS_IMPORT_RES: err = parse_import_res (args, &opd->result); break; default: break; } return err; } static gpgme_error_t _gpgme_op_import_start (gpgme_ctx_t ctx, int synchronous, gpgme_data_t keydata) { gpgme_error_t err; void *hook; op_data_t opd; err = _gpgme_op_reset (ctx, synchronous); if (err) return err; err = _gpgme_op_data_lookup (ctx, OPDATA_IMPORT, &hook, sizeof (*opd), release_op_data); opd = hook; if (err) return err; opd->lastp = &opd->result.imports; if (!keydata) return gpg_error (GPG_ERR_NO_DATA); _gpgme_engine_set_status_handler (ctx->engine, import_status_handler, ctx); return _gpgme_engine_op_import (ctx->engine, keydata, NULL); } gpgme_error_t gpgme_op_import_start (gpgme_ctx_t ctx, gpgme_data_t keydata) { gpg_error_t err; TRACE_BEG (DEBUG_CTX, "gpgme_op_import_start", ctx, "keydata=%p", keydata); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); err = _gpgme_op_import_start (ctx, 0, keydata); return TRACE_ERR (err); } /* Import the key in KEYDATA into the keyring. */ gpgme_error_t gpgme_op_import (gpgme_ctx_t ctx, gpgme_data_t keydata) { gpgme_error_t err; TRACE_BEG (DEBUG_CTX, "gpgme_op_import", ctx, "keydata=%p", keydata); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); err = _gpgme_op_import_start (ctx, 1, keydata); if (!err) - err = _gpgme_wait_one (ctx); + err = _gpgme_sync_wait (ctx, NULL, NULL); return TRACE_ERR (err); } static gpgme_error_t _gpgme_op_import_keys_start (gpgme_ctx_t ctx, int synchronous, gpgme_key_t *keys) { gpgme_error_t err; void *hook; op_data_t opd; int idx, firstidx, nkeys; err = _gpgme_op_reset (ctx, synchronous); if (err) return err; err = _gpgme_op_data_lookup (ctx, OPDATA_IMPORT, &hook, sizeof (*opd), release_op_data); opd = hook; if (err) return err; opd->lastp = &opd->result.imports; if (!keys) return gpg_error (GPG_ERR_NO_DATA); for (idx=nkeys=0, firstidx=-1; keys[idx]; idx++) { /* We only consider keys of the current protocol. */ if (keys[idx]->protocol != ctx->protocol) continue; if (firstidx == -1) firstidx = idx; /* If a key has been found using a different key listing mode, we bail out. This makes the processing easier. Fixme: To allow a mix of keys we would need to sort them by key listing mode and start two import operations one after the other. */ if (keys[idx]->keylist_mode != keys[firstidx]->keylist_mode) return gpg_error (GPG_ERR_CONFLICT); nkeys++; } if (!nkeys) return gpg_error (GPG_ERR_NO_DATA); _gpgme_engine_set_status_handler (ctx->engine, import_status_handler, ctx); return _gpgme_engine_op_import (ctx->engine, NULL, keys); } /* Asynchronous version of gpgme_op_import_key. */ gpgme_error_t gpgme_op_import_keys_start (gpgme_ctx_t ctx, gpgme_key_t *keys) { gpg_error_t err; TRACE_BEG (DEBUG_CTX, "gpgme_op_import_keys_start", ctx, ""); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); if (_gpgme_debug_trace () && keys) { int i = 0; while (keys[i]) { TRACE_LOG ("keys[%i] = %p (%s)", i, keys[i], (keys[i]->subkeys && keys[i]->subkeys->fpr) ? keys[i]->subkeys->fpr : "invalid"); i++; } } err = _gpgme_op_import_keys_start (ctx, 0, keys); return TRACE_ERR (err); } /* Import the keys from the array KEYS into the keyring. In particular it is used to actually import keys retrieved from an external source (i.e. using GPGME_KEYLIST_MODE_EXTERN). It replaces the old workaround of exporting and then importing a key as used to make an X.509 key permanent. This function automagically does the right thing. KEYS is a NULL terminated array of gpgme key objects. The result is the usual import result structure. Only keys matching the current protocol are imported; other keys are ignored. */ gpgme_error_t gpgme_op_import_keys (gpgme_ctx_t ctx, gpgme_key_t *keys) { gpgme_error_t err; TRACE_BEG (DEBUG_CTX, "gpgme_op_import_keys", ctx, ""); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); if (_gpgme_debug_trace () && keys) { int i = 0; while (keys[i]) { TRACE_LOG ("keys[%i] = %p (%s)", i, keys[i], (keys[i]->subkeys && keys[i]->subkeys->fpr) ? keys[i]->subkeys->fpr : "invalid"); i++; } } err = _gpgme_op_import_keys_start (ctx, 1, keys); if (!err) - err = _gpgme_wait_one (ctx); + err = _gpgme_sync_wait (ctx, NULL, NULL); return TRACE_ERR (err); } /* Deprecated interface. */ gpgme_error_t gpgme_op_import_ext (gpgme_ctx_t ctx, gpgme_data_t keydata, int *nr) { gpgme_error_t err = gpgme_op_import (ctx, keydata); if (!err && nr) { gpgme_import_result_t result = gpgme_op_import_result (ctx); *nr = result->considered; } return err; } diff --git a/src/keylist.c b/src/keylist.c index cdb115fd..44b70f8e 100644 --- a/src/keylist.c +++ b/src/keylist.c @@ -1,1344 +1,1344 @@ /* keylist.c - Listing keys. * Copyright (C) 2000 Werner Koch (dd9jn) * Copyright (C) 2001, 2002, 2003, 2004, 2006, 2007, * 2008, 2009 g10 Code GmbH * * This file is part of GPGME. * * GPGME is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * GPGME is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, see . * SPDX-License-Identifier: LGPL-2.1-or-later */ #if HAVE_CONFIG_H #include #endif #include #include #include #ifdef HAVE_SYS_TYPES_H /* Solaris 8 needs sys/types.h before time.h. */ # include #endif #include #include #include #include #include /* Suppress warning for accessing deprecated member "class". */ #define _GPGME_IN_GPGME #include "gpgme.h" #include "util.h" #include "context.h" #include "ops.h" #include "debug.h" struct key_queue_item_s { struct key_queue_item_s *next; gpgme_key_t key; }; typedef struct { struct _gpgme_op_keylist_result result; /* The error code from ERROR keydb_search. */ gpgme_error_t keydb_search_err; gpgme_key_t tmp_key; /* This points to the last uid in tmp_key. */ gpgme_user_id_t tmp_uid; /* This points to the last sig in tmp_uid. */ gpgme_key_sig_t tmp_keysig; /* Something new is available. */ int key_cond; struct key_queue_item_s *key_queue; } *op_data_t; static void release_op_data (void *hook) { op_data_t opd = (op_data_t) hook; struct key_queue_item_s *key = opd->key_queue; if (opd->tmp_key) gpgme_key_unref (opd->tmp_key); /* opd->tmp_uid and opd->tmp_keysig are actually part of opd->tmp_key, so we do not need to release them here. */ while (key) { struct key_queue_item_s *next = key->next; gpgme_key_unref (key->key); key = next; } } gpgme_keylist_result_t gpgme_op_keylist_result (gpgme_ctx_t ctx) { void *hook; op_data_t opd; gpgme_error_t err; TRACE_BEG (DEBUG_CTX, "gpgme_op_keylist_result", ctx, ""); err = _gpgme_op_data_lookup (ctx, OPDATA_KEYLIST, &hook, -1, NULL); opd = hook; if (err || !opd) { TRACE_SUC ("result=(null)"); return NULL; } TRACE_LOG ("truncated = %i", opd->result.truncated); TRACE_SUC ("result=%p", &opd->result); return &opd->result; } static gpgme_error_t keylist_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; (void)args; err = _gpgme_op_data_lookup (ctx, OPDATA_KEYLIST, &hook, -1, NULL); opd = hook; if (err) return err; switch (code) { case GPGME_STATUS_TRUNCATED: opd->result.truncated = 1; break; case GPGME_STATUS_ERROR: err = _gpgme_parse_failure (args); if (!opd->keydb_search_err && !strcmp (args, "keydb_search")) opd->keydb_search_err = err; err = 0; break; default: break; } return err; } static void set_subkey_trust_info (gpgme_subkey_t subkey, const char *src) { while (*src && !isdigit (*src)) { switch (*src) { case 'e': subkey->expired = 1; break; case 'r': subkey->revoked = 1; break; case 'd': /* Note that gpg 1.3 won't print that anymore but only uses the capabilities field. */ subkey->disabled = 1; break; case 'i': subkey->invalid = 1; break; } src++; } } static void set_mainkey_trust_info (gpgme_key_t key, const char *src) { /* First set the trust info of the main key (the first subkey). */ set_subkey_trust_info (key->subkeys, src); /* Now set the summarized trust info. */ while (*src && !isdigit (*src)) { switch (*src) { case 'e': key->expired = 1; break; case 'r': key->revoked = 1; break; case 'd': /* Note that gpg 1.3 won't print that anymore but only uses the capabilities field. However, it is still used for external key listings. */ key->disabled = 1; break; case 'i': key->invalid = 1; break; } src++; } } static void set_userid_flags (gpgme_key_t key, const char *src) { gpgme_user_id_t uid = key->_last_uid; assert (uid); /* Look at letters and stop at the first digit. */ while (*src && !isdigit (*src)) { switch (*src) { case 'r': uid->revoked = 1; break; case 'i': uid->invalid = 1; break; case 'n': uid->validity = GPGME_VALIDITY_NEVER; break; case 'm': uid->validity = GPGME_VALIDITY_MARGINAL; break; case 'f': uid->validity = GPGME_VALIDITY_FULL; break; case 'u': uid->validity = GPGME_VALIDITY_ULTIMATE; break; } src++; } } static void set_subkey_capability (gpgme_subkey_t subkey, const char *src) { while (*src) { switch (*src) { case 'e': subkey->can_encrypt = 1; break; case 's': subkey->can_sign = 1; break; case 'c': subkey->can_certify = 1; break; case 'a': subkey->can_authenticate = 1; break; case 'q': subkey->is_qualified = 1; break; case 'd': subkey->disabled = 1; break; } src++; } } static void set_mainkey_capability (gpgme_key_t key, const char *src) { /* First set the capabilities of the main key (the first subkey). */ set_subkey_capability (key->subkeys, src); while (*src) { switch (*src) { case 'd': case 'D': /* Note, that this flag is also set using the key validity field for backward compatibility with gpg 1.2. We use d and D, so that a future gpg version will be able to disable certain subkeys. Currently it is expected that gpg sets this for the primary key. */ key->disabled = 1; break; case 'e': case 'E': key->can_encrypt = 1; break; case 's': case 'S': key->can_sign = 1; break; case 'c': case 'C': key->can_certify = 1; break; case 'a': case 'A': key->can_authenticate = 1; break; case 'q': case 'Q': key->is_qualified = 1; break; } src++; } } static void set_ownertrust (gpgme_key_t key, const char *src) { /* Look at letters and stop at the first digit. */ while (*src && !isdigit (*src)) { switch (*src) { case 'n': key->owner_trust = GPGME_VALIDITY_NEVER; break; case 'm': key->owner_trust = GPGME_VALIDITY_MARGINAL; break; case 'f': key->owner_trust = GPGME_VALIDITY_FULL; break; case 'u': key->owner_trust = GPGME_VALIDITY_ULTIMATE; break; default: key->owner_trust = GPGME_VALIDITY_UNKNOWN; break; } src++; } } static gpgme_keyorg_t parse_keyorg (const char *string) { switch (atoi (string)) { case 0: return GPGME_KEYORG_UNKNOWN; case 1: case 2: return GPGME_KEYORG_KS; case 3: return GPGME_KEYORG_DANE; case 4: return GPGME_KEYORG_WKD; case 5: return GPGME_KEYORG_URL; case 6: return GPGME_KEYORG_FILE; case 7: return GPGME_KEYORG_SELF; default: return GPGME_KEYORG_OTHER; } } /* Parse field 15 of a secret key or subkey. This fields holds a reference to smartcards. FIELD is the content of the field and we are allowed to modify it. */ static gpg_error_t parse_sec_field15 (gpgme_key_t key, gpgme_subkey_t subkey, char *field) { if (!*field) ; /* Empty. */ else if (*field == '#') { /* This is a stub for an offline key. We reset the SECRET flag of the subkey here. Note that the secret flag of the entire key will be true even then. We even explicitly set key->secret to make it works for GPGME_KEYLIST_MODE_WITH_SECRET. */ subkey->secret = 0; key->secret = 1; } else if (strchr ("01234567890ABCDEFabcdef", *field)) { /* Fields starts with a hex digit; thus it is a serial number. */ key->secret = 1; subkey->is_cardkey = 1; subkey->card_number = strdup (field); if (!subkey->card_number) return gpg_error_from_syserror (); } else if (*field == '+') { key->secret = 1; subkey->secret = 1; } else { /* RFU. */ } return 0; } /* Parse a tfs record. */ static gpg_error_t parse_tfs_record (gpgme_user_id_t uid, char **field, int nfield) { gpg_error_t err; gpgme_tofu_info_t ti; unsigned long uval; /* We add only the first TOFU record in case future versions emit * several. */ if (uid->tofu) return 0; /* Check that we have enough fields and that the version is supported. */ if (nfield < 8 || atoi(field[1]) != 1) return trace_gpg_error (GPG_ERR_INV_ENGINE); ti = calloc (1, sizeof *ti); if (!ti) return gpg_error_from_syserror (); /* Note that we allow a value of up to 7 which is what we can store * in the ti->validity. */ err = _gpgme_strtoul_field (field[2], &uval); if (err || uval > 7) goto inv_engine; ti->validity = uval; /* Parse the sign-count. */ err = _gpgme_strtoul_field (field[3], &uval); if (err) goto inv_engine; if (uval > USHRT_MAX) uval = USHRT_MAX; ti->signcount = uval; /* Parse the encr-count. */ err = _gpgme_strtoul_field (field[4], &uval); if (err) goto inv_engine; if (uval > USHRT_MAX) uval = USHRT_MAX; ti->encrcount = uval; /* Parse the policy. */ if (!strcmp (field[5], "none")) ti->policy = GPGME_TOFU_POLICY_NONE; else if (!strcmp (field[5], "auto")) ti->policy = GPGME_TOFU_POLICY_AUTO; else if (!strcmp (field[5], "good")) ti->policy = GPGME_TOFU_POLICY_GOOD; else if (!strcmp (field[5], "bad")) ti->policy = GPGME_TOFU_POLICY_BAD; else if (!strcmp (field[5], "ask")) ti->policy = GPGME_TOFU_POLICY_ASK; else /* "unknown" and invalid policy strings. */ ti->policy = GPGME_TOFU_POLICY_UNKNOWN; /* Parse first and last seen timestamps. */ err = _gpgme_strtoul_field (field[6], &uval); if (err) goto inv_engine; ti->signfirst = uval; err = _gpgme_strtoul_field (field[7], &uval); if (err) goto inv_engine; ti->signlast = uval; if (nfield > 9) { /* This condition is only to allow for gpg 2.1.15 - can * eventually be removed. */ err = _gpgme_strtoul_field (field[8], &uval); if (err) goto inv_engine; ti->encrfirst = uval; err = _gpgme_strtoul_field (field[9], &uval); if (err) goto inv_engine; ti->encrlast = uval; } /* Ready. */ uid->tofu = ti; return 0; inv_engine: free (ti); return trace_gpg_error (GPG_ERR_INV_ENGINE); } /* We have read an entire key into tmp_key and should now finish it. It is assumed that this releases tmp_key. */ static void finish_key (gpgme_ctx_t ctx, op_data_t opd) { gpgme_key_t key = opd->tmp_key; opd->tmp_key = NULL; opd->tmp_uid = NULL; opd->tmp_keysig = NULL; if (key) _gpgme_engine_io_event (ctx->engine, GPGME_EVENT_NEXT_KEY, key); } /* Note: We are allowed to modify LINE. */ static gpgme_error_t keylist_colon_handler (void *priv, char *line) { gpgme_ctx_t ctx = (gpgme_ctx_t) priv; enum { RT_NONE, RT_SIG, RT_UID, RT_TFS, RT_SUB, RT_PUB, RT_FPR, RT_GRP, RT_SSB, RT_SEC, RT_CRT, RT_CRS, RT_REV, RT_SPK } rectype = RT_NONE; #define NR_FIELDS 20 char *field[NR_FIELDS]; int fields = 0; void *hook; op_data_t opd; gpgme_error_t err; gpgme_key_t key; gpgme_subkey_t subkey = NULL; gpgme_key_sig_t keysig = NULL; err = _gpgme_op_data_lookup (ctx, OPDATA_KEYLIST, &hook, -1, NULL); opd = hook; if (err) return err; key = opd->tmp_key; TRACE (DEBUG_CTX, "gpgme:keylist_colon_handler", ctx, "key = %p, line = %s", key, line ? line : "(null)"); if (!line) { /* End Of File. */ finish_key (ctx, opd); return 0; } while (line && fields < NR_FIELDS) { field[fields++] = line; line = strchr (line, ':'); if (line) *(line++) = '\0'; } if (!strcmp (field[0], "sig")) rectype = RT_SIG; else if (!strcmp (field[0], "rev")) rectype = RT_REV; else if (!strcmp (field[0], "pub")) rectype = RT_PUB; else if (!strcmp (field[0], "sec")) rectype = RT_SEC; else if (!strcmp (field[0], "crt")) rectype = RT_CRT; else if (!strcmp (field[0], "crs")) rectype = RT_CRS; else if (!strcmp (field[0], "fpr") && key) rectype = RT_FPR; else if (!strcmp (field[0], "grp") && key) rectype = RT_GRP; else if (!strcmp (field[0], "uid") && key) rectype = RT_UID; else if (!strcmp (field[0], "tfs") && key) rectype = RT_TFS; else if (!strcmp (field[0], "sub") && key) rectype = RT_SUB; else if (!strcmp (field[0], "ssb") && key) rectype = RT_SSB; else if (!strcmp (field[0], "spk") && key) rectype = RT_SPK; else rectype = RT_NONE; /* Only look at signature and trust info records immediately following a user ID. For this, clear the user ID pointer when encountering anything but a signature or trust record. */ if (rectype != RT_SIG && rectype != RT_REV && rectype != RT_TFS) opd->tmp_uid = NULL; /* Only look at subpackets immediately following a signature. For this, clear the signature pointer when encountering anything but a subpacket. */ if (rectype != RT_SPK) opd->tmp_keysig = NULL; switch (rectype) { case RT_PUB: case RT_SEC: case RT_CRT: case RT_CRS: /* Start a new keyblock. */ err = _gpgme_key_new (&key); if (err) return err; key->keylist_mode = ctx->keylist_mode; err = _gpgme_key_add_subkey (key, &subkey); if (err) { gpgme_key_unref (key); return err; } if (rectype == RT_SEC || rectype == RT_CRS) key->secret = subkey->secret = 1; if (rectype == RT_CRT || rectype == RT_CRS) key->protocol = GPGME_PROTOCOL_CMS; finish_key (ctx, opd); opd->tmp_key = key; /* Field 2 has the trust info. */ if (fields >= 2) set_mainkey_trust_info (key, field[1]); /* Field 3 has the key length. */ if (fields >= 3) { int i = atoi (field[2]); /* Ignore invalid values. */ if (i > 1) subkey->length = i; } /* Field 4 has the public key algorithm. */ if (fields >= 4) { int i = atoi (field[3]); if (i >= 1 && i < 128) subkey->pubkey_algo = _gpgme_map_pk_algo (i, ctx->protocol); } /* Field 5 has the long keyid. Allow short key IDs for the output of an external keyserver listing. */ if (fields >= 5 && strlen (field[4]) <= DIM(subkey->_keyid) - 1) strcpy (subkey->_keyid, field[4]); /* Field 6 has the timestamp (seconds). */ if (fields >= 6) subkey->timestamp = _gpgme_parse_timestamp (field[5], NULL); /* Field 7 has the expiration time (seconds). */ if (fields >= 7) subkey->expires = _gpgme_parse_timestamp (field[6], NULL); /* Field 8 has the X.509 serial number. */ if (fields >= 8 && (rectype == RT_CRT || rectype == RT_CRS)) { key->issuer_serial = strdup (field[7]); if (!key->issuer_serial) return gpg_error_from_syserror (); } /* Field 9 has the ownertrust. */ if (fields >= 9) set_ownertrust (key, field[8]); /* Field 10 is not used for gpg due to --fixed-list-mode option but GPGSM stores the issuer name. */ if (fields >= 10 && (rectype == RT_CRT || rectype == RT_CRS)) if (_gpgme_decode_c_string (field[9], &key->issuer_name, 0)) return gpg_error (GPG_ERR_ENOMEM); /* FIXME */ /* Field 11 has the signature class. */ /* Field 12 has the capabilities. */ if (fields >= 12) set_mainkey_capability (key, field[11]); /* Field 15 carries special flags of a secret key. */ if (fields >= 15 && (key->secret || (ctx->keylist_mode & GPGME_KEYLIST_MODE_WITH_SECRET))) { err = parse_sec_field15 (key, subkey, field[14]); if (err) return err; } /* Field 17 has the curve name for ECC. */ if (fields >= 17 && *field[16]) { subkey->curve = strdup (field[16]); if (!subkey->curve) return gpg_error_from_syserror (); } /* Field 18 has the compliance flags. */ if (fields >= 17 && *field[17]) PARSE_COMPLIANCE_FLAGS (field[17], subkey); if (fields >= 20) { key->last_update = _gpgme_parse_timestamp_ul (field[18]); key->origin = parse_keyorg (field[19]); } break; case RT_SUB: case RT_SSB: /* Start a new subkey. */ err = _gpgme_key_add_subkey (key, &subkey); if (err) return err; if (rectype == RT_SSB) subkey->secret = 1; /* Field 2 has the trust info. */ if (fields >= 2) set_subkey_trust_info (subkey, field[1]); /* Field 3 has the key length. */ if (fields >= 3) { int i = atoi (field[2]); /* Ignore invalid values. */ if (i > 1) subkey->length = i; } /* Field 4 has the public key algorithm. */ if (fields >= 4) { int i = atoi (field[3]); if (i >= 1 && i < 128) subkey->pubkey_algo = _gpgme_map_pk_algo (i, ctx->protocol); } /* Field 5 has the long keyid. */ if (fields >= 5 && strlen (field[4]) == DIM(subkey->_keyid) - 1) strcpy (subkey->_keyid, field[4]); /* Field 6 has the timestamp (seconds). */ if (fields >= 6) subkey->timestamp = _gpgme_parse_timestamp (field[5], NULL); /* Field 7 has the expiration time (seconds). */ if (fields >= 7) subkey->expires = _gpgme_parse_timestamp (field[6], NULL); /* Field 8 is reserved (LID). */ /* Field 9 has the ownertrust. */ /* Field 10, the user ID, is n/a for a subkey. */ /* Field 11 has the signature class. */ /* Field 12 has the capabilities. */ if (fields >= 12) set_subkey_capability (subkey, field[11]); /* Field 15 carries special flags of a secret key. */ if (fields >= 15 && (key->secret || (ctx->keylist_mode & GPGME_KEYLIST_MODE_WITH_SECRET))) { err = parse_sec_field15 (key, subkey, field[14]); if (err) return err; } /* Field 17 has the curve name for ECC. */ if (fields >= 17 && *field[16]) { subkey->curve = strdup (field[16]); if (!subkey->curve) return gpg_error_from_syserror (); } /* Field 18 has the compliance flags. */ if (fields >= 17 && *field[17]) PARSE_COMPLIANCE_FLAGS (field[17], subkey); break; case RT_UID: /* Field 2 has the trust info, and field 10 has the user ID. */ if (fields >= 10) { if (_gpgme_key_append_name (key, field[9], 1)) return gpg_error (GPG_ERR_ENOMEM); /* FIXME */ if (field[1]) set_userid_flags (key, field[1]); opd->tmp_uid = key->_last_uid; if (fields >= 20) { opd->tmp_uid->last_update = _gpgme_parse_timestamp_ul (field[18]); opd->tmp_uid->origin = parse_keyorg (field[19]); } } break; case RT_TFS: if (opd->tmp_uid) { err = parse_tfs_record (opd->tmp_uid, field, fields); if (err) return err; } break; case RT_FPR: /* Field 10 has the fingerprint (take only the first one). */ if (fields >= 10 && field[9] && *field[9]) { /* Need to apply it to the last subkey because all subkeys do have fingerprints. */ subkey = key->_last_subkey; if (!subkey->fpr) { subkey->fpr = strdup (field[9]); if (!subkey->fpr) return gpg_error_from_syserror (); } /* If this is the first subkey, store the fingerprint also in the KEY object. */ if (subkey == key->subkeys) { if (key->fpr && strcmp (key->fpr, subkey->fpr)) { /* FPR already set but mismatch: Should never happen. */ return trace_gpg_error (GPG_ERR_INTERNAL); } if (!key->fpr) { key->fpr = strdup (subkey->fpr); if (!key->fpr) return gpg_error_from_syserror (); } } } /* Field 13 has the gpgsm chain ID (take only the first one). */ if (fields >= 13 && !key->chain_id && *field[12]) { key->chain_id = strdup (field[12]); if (!key->chain_id) return gpg_error_from_syserror (); } break; case RT_GRP: /* Field 10 has the keygrip. */ if (fields >= 10 && field[9] && *field[9]) { /* Need to apply it to the last subkey because all subkeys have a keygrip. */ subkey = key->_last_subkey; if (!subkey->keygrip) { subkey->keygrip = strdup (field[9]); if (!subkey->keygrip) return gpg_error_from_syserror (); } } break; case RT_SIG: case RT_REV: if (!opd->tmp_uid) return 0; /* Start a new (revoked) signature. */ assert (opd->tmp_uid == key->_last_uid); keysig = _gpgme_key_add_sig (key, (fields >= 10) ? field[9] : NULL); if (!keysig) return gpg_error (GPG_ERR_ENOMEM); /* FIXME */ /* Field 2 has the calculated trust ('!', '-', '?', '%'). */ if (fields >= 2) switch (field[1][0]) { case '!': keysig->status = gpg_error (GPG_ERR_NO_ERROR); break; case '-': keysig->status = gpg_error (GPG_ERR_BAD_SIGNATURE); break; case '?': keysig->status = gpg_error (GPG_ERR_NO_PUBKEY); break; case '%': keysig->status = gpg_error (GPG_ERR_GENERAL); break; default: keysig->status = gpg_error (GPG_ERR_NO_ERROR); break; } /* Field 4 has the public key algorithm. */ if (fields >= 4) { int i = atoi (field[3]); if (i >= 1 && i < 128) keysig->pubkey_algo = _gpgme_map_pk_algo (i, ctx->protocol); } /* Field 5 has the long keyid. */ if (fields >= 5 && strlen (field[4]) == DIM(keysig->_keyid) - 1) strcpy (keysig->_keyid, field[4]); /* Field 6 has the timestamp (seconds). */ if (fields >= 6) keysig->timestamp = _gpgme_parse_timestamp (field[5], NULL); /* Field 7 has the expiration time (seconds). */ if (fields >= 7) keysig->expires = _gpgme_parse_timestamp (field[6], NULL); /* Field 11 has the signature class (eg, 0x30 means revoked). */ if (fields >= 11) if (field[10][0] && field[10][1]) { int sig_class = _gpgme_hextobyte (field[10]); if (sig_class >= 0) { keysig->sig_class = sig_class; keysig->class = keysig->sig_class; if (sig_class == 0x30) keysig->revoked = 1; } if (field[10][2] == 'x') keysig->exportable = 1; } opd->tmp_keysig = keysig; break; case RT_SPK: if (!opd->tmp_keysig) return 0; assert (opd->tmp_keysig == key->_last_uid->_last_keysig); if (fields >= 4) { /* Field 2 has the subpacket type. */ int type = atoi (field[1]); /* Field 3 has the flags. */ int flags = atoi (field[2]); /* Field 4 has the length. */ int len = atoi (field[3]); /* Field 5 has the data. */ char *data = field[4]; /* Type 20: Notation data. */ /* Type 26: Policy URL. */ if (type == 20 || type == 26) { gpgme_sig_notation_t notation; keysig = opd->tmp_keysig; /* At this time, any error is serious. */ err = _gpgme_parse_notation (¬ation, type, flags, len, data); if (err) return err; /* Add a new notation. FIXME: Could be factored out. */ if (!keysig->notations) keysig->notations = notation; if (keysig->_last_notation) keysig->_last_notation->next = notation; keysig->_last_notation = notation; } } case RT_NONE: /* Unknown record. */ break; } return 0; } void _gpgme_op_keylist_event_cb (void *data, gpgme_event_io_t type, void *type_data) { gpgme_error_t err; gpgme_ctx_t ctx = (gpgme_ctx_t) data; gpgme_key_t key = (gpgme_key_t) type_data; void *hook; op_data_t opd; struct key_queue_item_s *q, *q2; assert (type == GPGME_EVENT_NEXT_KEY); err = _gpgme_op_data_lookup (ctx, OPDATA_KEYLIST, &hook, -1, NULL); opd = hook; if (err) return; q = malloc (sizeof *q); if (!q) { gpgme_key_unref (key); /* FIXME return GPGME_Out_Of_Core; */ return; } q->key = key; q->next = NULL; /* FIXME: Use a tail pointer? */ if (!(q2 = opd->key_queue)) opd->key_queue = q; else { for (; q2->next; q2 = q2->next) ; q2->next = q; } opd->key_cond = 1; } /* Start a keylist operation within CTX, searching for keys which match PATTERN. If SECRET_ONLY is true, only secret keys are returned. */ gpgme_error_t gpgme_op_keylist_start (gpgme_ctx_t ctx, const char *pattern, int secret_only) { gpgme_error_t err; void *hook; op_data_t opd; int flags = 0; TRACE_BEG (DEBUG_CTX, "gpgme_op_keylist_start", ctx, "pattern=%s, secret_only=%i", pattern, secret_only); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); err = _gpgme_op_reset (ctx, 2); if (err) return TRACE_ERR (err); err = _gpgme_op_data_lookup (ctx, OPDATA_KEYLIST, &hook, sizeof (*opd), release_op_data); opd = hook; if (err) return TRACE_ERR (err); _gpgme_engine_set_status_handler (ctx->engine, keylist_status_handler, ctx); err = _gpgme_engine_set_colon_line_handler (ctx->engine, keylist_colon_handler, ctx); if (err) return TRACE_ERR (err); if (ctx->offline) flags |= GPGME_ENGINE_FLAG_OFFLINE; err = _gpgme_engine_op_keylist (ctx->engine, pattern, secret_only, ctx->keylist_mode, flags); return TRACE_ERR (err); } /* Start a keylist operation within CTX, searching for keys which match PATTERN. If SECRET_ONLY is true, only secret keys are returned. */ gpgme_error_t gpgme_op_keylist_ext_start (gpgme_ctx_t ctx, const char *pattern[], int secret_only, int reserved) { gpgme_error_t err; void *hook; op_data_t opd; int flags = 0; TRACE_BEG (DEBUG_CTX, "gpgme_op_keylist_ext_start", ctx, "secret_only=%i, reserved=0x%x", secret_only, reserved); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); err = _gpgme_op_reset (ctx, 2); if (err) return TRACE_ERR (err); err = _gpgme_op_data_lookup (ctx, OPDATA_KEYLIST, &hook, sizeof (*opd), release_op_data); opd = hook; if (err) return TRACE_ERR (err); _gpgme_engine_set_status_handler (ctx->engine, keylist_status_handler, ctx); err = _gpgme_engine_set_colon_line_handler (ctx->engine, keylist_colon_handler, ctx); if (err) return TRACE_ERR (err); if (ctx->offline) flags |= GPGME_ENGINE_FLAG_OFFLINE; err = _gpgme_engine_op_keylist_ext (ctx->engine, pattern, secret_only, reserved, ctx->keylist_mode, flags); return TRACE_ERR (err); } /* Start a keylist operation within CTX to show keys contained * in DATA. */ gpgme_error_t gpgme_op_keylist_from_data_start (gpgme_ctx_t ctx, gpgme_data_t data, int reserved) { gpgme_error_t err; void *hook; op_data_t opd; TRACE_BEG (DEBUG_CTX, "gpgme_op_keylist_from_data_start", ctx, ""); if (!ctx || !data || reserved) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); err = _gpgme_op_reset (ctx, 2); if (err) return TRACE_ERR (err); err = _gpgme_op_data_lookup (ctx, OPDATA_KEYLIST, &hook, sizeof (*opd), release_op_data); opd = hook; if (err) return TRACE_ERR (err); _gpgme_engine_set_status_handler (ctx->engine, keylist_status_handler, ctx); err = _gpgme_engine_set_colon_line_handler (ctx->engine, keylist_colon_handler, ctx); if (err) return TRACE_ERR (err); err = _gpgme_engine_op_keylist_data (ctx->engine, data); return TRACE_ERR (err); } /* Return the next key from the keylist in R_KEY. */ gpgme_error_t gpgme_op_keylist_next (gpgme_ctx_t ctx, gpgme_key_t *r_key) { gpgme_error_t err; struct key_queue_item_s *queue_item; void *hook; op_data_t opd; TRACE_BEG (DEBUG_CTX, "gpgme_op_keylist_next", ctx, ""); if (!ctx || !r_key) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); *r_key = NULL; if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); err = _gpgme_op_data_lookup (ctx, OPDATA_KEYLIST, &hook, -1, NULL); opd = hook; if (err) return TRACE_ERR (err); if (opd == NULL) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); if (!opd->key_queue) { - err = _gpgme_wait_on_condition (ctx, &opd->key_cond, NULL); + err = _gpgme_sync_wait (ctx, &opd->key_cond, NULL); if (err) return TRACE_ERR (err); if (!opd->key_cond) return TRACE_ERR (opd->keydb_search_err? opd->keydb_search_err /**/ : gpg_error (GPG_ERR_EOF)); opd->key_cond = 0; assert (opd->key_queue); } queue_item = opd->key_queue; opd->key_queue = queue_item->next; if (!opd->key_queue) opd->key_cond = 0; *r_key = queue_item->key; free (queue_item); TRACE_SUC ("key=%p (%s)", *r_key, ((*r_key)->subkeys && (*r_key)->subkeys->fpr) ? (*r_key)->subkeys->fpr : "invalid"); return 0; } /* Terminate a pending keylist operation within CTX. */ gpgme_error_t gpgme_op_keylist_end (gpgme_ctx_t ctx) { TRACE (DEBUG_CTX, "gpgme_op_keylist_end", ctx, ""); if (!ctx) return gpg_error (GPG_ERR_INV_VALUE); return 0; } /* Get the key with the fingerprint FPR from the crypto backend. If SECRET is true, get the secret key. */ gpgme_error_t gpgme_get_key (gpgme_ctx_t ctx, const char *fpr, gpgme_key_t *r_key, int secret) { gpgme_ctx_t listctx; gpgme_error_t err; gpgme_key_t result, key; TRACE_BEG (DEBUG_CTX, "gpgme_get_key", ctx, "fpr=%s, secret=%i", fpr, secret); if (r_key) *r_key = NULL; if (!ctx || !r_key || !fpr) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); if (strlen (fpr) < 8) /* We have at least a key ID. */ return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); /* FIXME: We use our own context because we have to avoid the user's I/O callback handlers. */ err = gpgme_new (&listctx); if (err) return TRACE_ERR (err); { gpgme_protocol_t proto; gpgme_engine_info_t info; /* Clone the relevant state. */ proto = gpgme_get_protocol (ctx); gpgme_set_protocol (listctx, proto); gpgme_set_keylist_mode (listctx, gpgme_get_keylist_mode (ctx)); info = gpgme_ctx_get_engine_info (ctx); while (info && info->protocol != proto) info = info->next; if (info) gpgme_ctx_set_engine_info (listctx, proto, info->file_name, info->home_dir); } err = gpgme_op_keylist_start (listctx, fpr, secret); if (!err) err = gpgme_op_keylist_next (listctx, &result); if (!err) { try_next_key: err = gpgme_op_keylist_next (listctx, &key); if (gpgme_err_code (err) == GPG_ERR_EOF) err = 0; else { if (!err && result && result->subkeys && result->subkeys->fpr && key && key->subkeys && key->subkeys->fpr && !strcmp (result->subkeys->fpr, key->subkeys->fpr)) { /* The fingerprint is identical. We assume that this is the same key and don't mark it as an ambiguous. This problem may occur with corrupted keyrings and has been noticed often with gpgsm. In fact gpgsm uses a similar hack to sort out such duplicates but it can't do that while listing keys. */ gpgme_key_unref (key); goto try_next_key; } if (!err) { gpgme_key_unref (key); err = gpg_error (GPG_ERR_AMBIGUOUS_NAME); } gpgme_key_unref (result); result = NULL; } } gpgme_release (listctx); if (! err) { *r_key = result; TRACE_LOG ("key=%p (%s)", *r_key, ((*r_key)->subkeys && (*r_key)->subkeys->fpr) ? (*r_key)->subkeys->fpr : "invalid"); } return TRACE_ERR (err); } diff --git a/src/keysign.c b/src/keysign.c index 44ab5ff2..c50df26a 100644 --- a/src/keysign.c +++ b/src/keysign.c @@ -1,219 +1,219 @@ /* keysign.c - OpenPGP key signing * Copyright (C) 2016 g10 Code GmbH * * This file is part of GPGME. * * GPGME is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * GPGME is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, see . * SPDX-License-Identifier: LGPL-2.1-or-later */ #if HAVE_CONFIG_H #include #endif #include #include #include #include "gpgme.h" #include "debug.h" #include "context.h" #include "ops.h" #include "util.h" typedef struct { /* The error code from a FAILURE status line or 0. */ gpg_error_t failure_code; /* The error code from certain ERROR status lines or 0. */ gpg_error_t error_code; } *op_data_t; static void release_op_data (void *hook) { op_data_t opd = (op_data_t) hook; (void)opd; } /* Parse an error status line. Return the error location and the error code. The function may modify ARGS. */ static char * parse_error (char *args, gpg_error_t *r_err) { char *where = strchr (args, ' '); char *which; if (where) { *where = '\0'; which = where + 1; where = strchr (which, ' '); if (where) *where = '\0'; where = args; } else { *r_err = trace_gpg_error (GPG_ERR_INV_ENGINE); return NULL; } *r_err = atoi (which); return where; } static gpgme_error_t keysign_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; char *loc; /* Pipe the status code through the progress status handler. */ err = _gpgme_progress_status_handler (ctx, code, args); if (err) return err; err = _gpgme_op_data_lookup (ctx, OPDATA_KEYSIGN, &hook, -1, NULL); opd = hook; if (err) return err; switch (code) { case GPGME_STATUS_ERROR: loc = parse_error (args, &err); if (!loc) return err; if (!opd->error_code) opd->error_code = err; break; case GPGME_STATUS_FAILURE: opd->failure_code = _gpgme_parse_failure (args); break; case GPGME_STATUS_EOF: if (opd->error_code) return opd->error_code; else if (opd->failure_code) return opd->failure_code; break; case GPGME_STATUS_INQUIRE_MAXLEN: if (ctx->status_cb && !ctx->full_status) { err = ctx->status_cb (ctx->status_cb_value, "INQUIRE_MAXLEN", args); if (err) return err; } break; default: break; } return 0; } /* Sign the USERID of KEY using the current set of signers. If USERID * is NULL, sign all user ids. To put several user ids into USERID, * separate them by LF and set the flag GPGME_KEYSIGN_LFSEP. */ static gpgme_error_t keysign_start (gpgme_ctx_t ctx, int synchronous, gpgme_key_t key, const char *userid, unsigned long expires, unsigned int flags) { gpgme_error_t err; void *hook; op_data_t opd; if (ctx->protocol != GPGME_PROTOCOL_OPENPGP) return gpgme_error (GPG_ERR_UNSUPPORTED_PROTOCOL); err = _gpgme_op_reset (ctx, synchronous); if (err) return err; if (!key) return gpg_error (GPG_ERR_INV_ARG); err = _gpgme_op_data_lookup (ctx, OPDATA_KEYSIGN, &hook, sizeof (*opd), release_op_data); opd = hook; if (err) return err; _gpgme_engine_set_status_handler (ctx->engine, keysign_status_handler, ctx); if (ctx->passphrase_cb) { err = _gpgme_engine_set_command_handler (ctx->engine, _gpgme_passphrase_command_handler, ctx); if (err) return err; } return _gpgme_engine_op_keysign (ctx->engine, key, userid, expires, flags, ctx); } /* Sign the USERID of KEY using the current set of signers. */ gpgme_error_t gpgme_op_keysign_start (gpgme_ctx_t ctx, gpgme_key_t key, const char *userid, unsigned long expires, unsigned int flags) { gpgme_error_t err; TRACE_BEG (DEBUG_CTX, "gpgme_op_keysign_start", ctx, "key=%p, uid='%s' flags=0x%x", key, userid, flags); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_ARG)); err = keysign_start (ctx, 0, key, userid, expires, flags); return TRACE_ERR (err); } gpgme_error_t gpgme_op_keysign (gpgme_ctx_t ctx, gpgme_key_t key, const char *userid, unsigned long expires, unsigned int flags) { gpgme_error_t err; TRACE_BEG (DEBUG_CTX, "gpgme_op_keysign", ctx, "key=%p, uid='%s' flags=0x%x", key, userid, flags); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_ARG)); err = keysign_start (ctx, 1, key, userid, expires, flags); if (!err) - err = _gpgme_wait_one (ctx); + err = _gpgme_sync_wait (ctx, NULL, NULL); return TRACE_ERR (err); } diff --git a/src/opassuan.c b/src/opassuan.c index bfb06869..497eded3 100644 --- a/src/opassuan.c +++ b/src/opassuan.c @@ -1,231 +1,231 @@ /* opassuan.c - Low-level Assuan operations. * Copyright (C) 2009 g10 Code GmbH * * This file is part of GPGME. * * GPGME is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * GPGME is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, see . * SPDX-License-Identifier: LGPL-2.1-or-later */ #if HAVE_CONFIG_H #include #endif /* Suppress warning for accessing deprecated member "err". */ #define _GPGME_IN_GPGME 1 #include "gpgme.h" #include "context.h" #include "ops.h" #include "util.h" #include "debug.h" /* LEGACY: Remove this when removing the deprecated result structure. */ typedef struct { struct _gpgme_op_assuan_result result; } *op_data_t; static gpgme_error_t opassuan_start (gpgme_ctx_t ctx, int synchronous, const char *command, gpgme_assuan_data_cb_t data_cb, void *data_cb_value, gpgme_assuan_inquire_cb_t inq_cb, void *inq_cb_value, gpgme_assuan_status_cb_t status_cb, void *status_cb_value) { gpgme_error_t err; if (!command || !*command) return gpg_error (GPG_ERR_INV_VALUE); /* The flag value 256 is used to suppress an engine reset. This is required to keep the connection running. */ err = _gpgme_op_reset (ctx, ((synchronous&255) | 256)); if (err) return err; { /* LEGACY: Remove this when removing the deprecated result structure. */ void *hook; op_data_t opd; err = _gpgme_op_data_lookup (ctx, OPDATA_ASSUAN, &hook, sizeof (*opd), NULL); if (err) return err; } return _gpgme_engine_op_assuan_transact (ctx->engine, command, data_cb, data_cb_value, inq_cb, inq_cb_value, status_cb, status_cb_value); } /* XXXX. This is the asynchronous variant. */ gpgme_error_t gpgme_op_assuan_transact_start (gpgme_ctx_t ctx, const char *command, gpgme_assuan_data_cb_t data_cb, void *data_cb_value, gpgme_assuan_inquire_cb_t inq_cb, void *inq_cb_value, gpgme_assuan_status_cb_t status_cb, void *status_cb_value) { gpg_error_t err; TRACE_BEG (DEBUG_CTX, "gpgme_op_assuan_transact_start", ctx, "command=%s, data_cb=%p/%p, inq_cb=%p/%p, status_cb=%p/%p", command, data_cb, data_cb_value, inq_cb, inq_cb_value, status_cb, status_cb_value); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); err = opassuan_start (ctx, 0, command, data_cb, data_cb_value, inq_cb, inq_cb_value, status_cb, status_cb_value); return TRACE_ERR (err); } /* XXXX. This is the synchronous variant. */ gpgme_error_t gpgme_op_assuan_transact_ext (gpgme_ctx_t ctx, const char *command, gpgme_assuan_data_cb_t data_cb, void *data_cb_value, gpgme_assuan_inquire_cb_t inq_cb, void *inq_cb_value, gpgme_assuan_status_cb_t status_cb, void *status_cb_value, gpgme_error_t *op_err_p) { gpgme_error_t err; gpgme_error_t op_err; TRACE_BEG (DEBUG_CTX, "gpgme_op_assuan_transact", ctx, "command=%s, data_cb=%p/%p, inq_cb=%p/%p, status_cb=%p/%p, " "op_err=%p", command, data_cb, data_cb_value, inq_cb, inq_cb_value, status_cb, status_cb_value, op_err_p); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); err = opassuan_start (ctx, 1, command, data_cb, data_cb_value, inq_cb, inq_cb_value, status_cb, status_cb_value); if (err) goto out; - err = _gpgme_wait_one_ext (ctx, &op_err); + err = _gpgme_sync_wait (ctx, NULL, &op_err); if (op_err) { TRACE_LOG ("op_err = %s <%s>", gpgme_strerror (op_err), gpgme_strsource (op_err)); if (! op_err_p) { TRACE_LOG ("warning: operational error ignored by user"); } } if (op_err_p) *op_err_p = op_err; out: return TRACE_ERR (err); } /* Compatibility code for old interface. */ /* Evil hack breaking abstractions for the purpose of localizing our other hack. This is copied from engine.c. */ struct engine { struct engine_ops *ops; void *engine; }; gpg_error_t _gpgme_engine_assuan_last_op_err (void *engine); gpgme_assuan_result_t gpgme_op_assuan_result (gpgme_ctx_t ctx) { gpgme_error_t err; void *hook; op_data_t opd; TRACE_BEG (DEBUG_CTX, "gpgme_op_assuan_result", ctx, ""); err = _gpgme_op_data_lookup (ctx, OPDATA_ASSUAN, &hook, -1, NULL); opd = hook; /* Check in case this function is used without having run a command before. */ if (err || !opd) { TRACE_SUC ("result=(null)"); return NULL; } /* All of this is a hack for the old style interface. The new style interface returns op errors directly. */ opd->result.err = _gpgme_engine_assuan_last_op_err (ctx->engine->engine); if (opd->result.err) { TRACE_LOG ("err = %s", gpg_strerror (0)); } else { TRACE_LOG ("err = %s <%s>", gpg_strerror (opd->result.err), gpg_strsource (opd->result.err)); } TRACE_SUC ("result=%p", &opd->result); return &opd->result; } gpgme_error_t gpgme_op_assuan_transact (gpgme_ctx_t ctx, const char *command, gpgme_assuan_data_cb_t data_cb, void *data_cb_value, gpgme_assuan_inquire_cb_t inq_cb, void *inq_cb_value, gpgme_assuan_status_cb_t status_cb, void *status_cb_value) { gpgme_error_t err; TRACE (DEBUG_CTX, "gpgme_op_assuan_transact", ctx, ""); if (!ctx) return gpg_error (GPG_ERR_INV_VALUE); /* Users of the old-style session based interfaces need to look at the result structure. */ err = gpgme_op_assuan_transact_ext (ctx, command, data_cb, data_cb_value, inq_cb, inq_cb_value, status_cb, status_cb_value, NULL); return err; } diff --git a/src/ops.h b/src/ops.h index 013b2cd2..c8b7b3ed 100644 --- a/src/ops.h +++ b/src/ops.h @@ -1,192 +1,191 @@ /* ops.h - Internal operation support. Copyright (C) 2000 Werner Koch (dd9jn) Copyright (C) 2001, 2002, 2003, 2004, 2005 g10 Code GmbH This file is part of GPGME. GPGME is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. GPGME is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef OPS_H #define OPS_H #include "gpgme.h" #include "context.h" /* Clear all notation data from the context. */ void _gpgme_sig_notation_clear (gpgme_ctx_t ctx); void _gpgme_release_result (gpgme_ctx_t ctx); /* From wait.c. */ -gpgme_error_t _gpgme_wait_one (gpgme_ctx_t ctx); -gpgme_error_t _gpgme_wait_one_ext (gpgme_ctx_t ctx, gpgme_error_t *op_err); -gpgme_error_t _gpgme_wait_on_condition (gpgme_ctx_t ctx, volatile int *cond, - gpgme_error_t *op_err); +gpgme_error_t _gpgme_sync_wait (gpgme_ctx_t ctx, + volatile int *cond, + gpg_error_t *op_err); /* From data.c. */ gpgme_error_t _gpgme_data_inbound_handler (void *opaque, int fd); gpgme_error_t _gpgme_data_outbound_handler (void *opaque, int fd); /* From op-support.c. */ /* Find or create the op data object of type TYPE. */ gpgme_error_t _gpgme_op_data_lookup (gpgme_ctx_t ctx, ctx_op_data_id_t type, void **hook, int size, void (*cleanup) (void *)); /* Prepare a new operation on CTX. */ gpgme_error_t _gpgme_op_reset (gpgme_ctx_t ctx, int synchronous); /* Parse the KEY_CONSIDERED status line. */ gpgme_error_t _gpgme_parse_key_considered (const char *args, char **r_fpr, unsigned int *r_flags); /* Parse the INV_RECP status line in ARGS and return the result in KEY. */ gpgme_error_t _gpgme_parse_inv_recp (char *args, int for_signing, const char *kc_fpr, unsigned int kc_flags, gpgme_invalid_key_t *key); /* Parse the PLAINTEXT status line in ARGS and return the result in FILENAMEP and R_MIME. */ gpgme_error_t _gpgme_parse_plaintext (char *args, char **filenamep,int *r_mime); /* Parse a FAILURE status line and return the error code. ARGS is modified to contain the location part. */ gpgme_error_t _gpgme_parse_failure (char *args); /* From verify.c. */ gpgme_error_t _gpgme_op_verify_init_result (gpgme_ctx_t ctx); gpgme_error_t _gpgme_verify_status_handler (void *priv, gpgme_status_code_t code, char *args); /* From decrypt.c. */ gpgme_error_t _gpgme_op_decrypt_init_result (gpgme_ctx_t ctx, gpgme_data_t plaintext); gpgme_error_t _gpgme_decrypt_status_handler (void *priv, gpgme_status_code_t code, char *args); gpgme_error_t _gpgme_decrypt_start (gpgme_ctx_t ctx, int synchronous, gpgme_decrypt_flags_t flags, gpgme_data_t cipher, gpgme_data_t plain); /* From signers.c. */ void _gpgme_signers_clear (gpgme_ctx_t ctx); /* From sign.c. */ /* Create an initial op data object for signing. Needs to be called once before calling _gpgme_sign_status_handler. */ gpgme_error_t _gpgme_op_sign_init_result (gpgme_ctx_t ctx); /* Process a status line for signing operations. */ gpgme_error_t _gpgme_sign_status_handler (void *priv, gpgme_status_code_t code, char *args); /* From encrypt.c. */ /* Create an initial op data object for encrypt. Needs to be called once before calling _gpgme_encrypt_status_handler. */ gpgme_error_t _gpgme_op_encrypt_init_result (gpgme_ctx_t ctx); /* Process a status line for encryption operations. */ gpgme_error_t _gpgme_encrypt_status_handler (void *priv, gpgme_status_code_t code, char *args); /* From passphrase.c. */ gpgme_error_t _gpgme_passphrase_status_handler (void *priv, gpgme_status_code_t code, char *args); gpgme_error_t _gpgme_passphrase_command_handler (void *opaque, gpgme_status_code_t code, const char *key, int fd, int *processed); /* From progress.c. */ gpgme_error_t _gpgme_progress_status_handler (void *priv, gpgme_status_code_t code, char *args); /* From key.c. */ gpgme_error_t _gpgme_key_new (gpgme_key_t *r_key); gpgme_error_t _gpgme_key_add_subkey (gpgme_key_t key, gpgme_subkey_t *r_subkey); gpgme_error_t _gpgme_key_append_name (gpgme_key_t key, const char *src, int convert); gpgme_key_sig_t _gpgme_key_add_sig (gpgme_key_t key, char *src); /* From keylist.c. */ void _gpgme_op_keylist_event_cb (void *data, gpgme_event_io_t type, void *type_data); /* From trust-item.c. */ /* Create a new trust item. */ gpgme_error_t _gpgme_trust_item_new (gpgme_trust_item_t *r_item); /* From trustlist.c. */ void _gpgme_op_trustlist_event_cb (void *data, gpgme_event_io_t type, void *type_data); /* From version.c. */ /* Return true if MY_VERSION is at least REQ_VERSION, and false otherwise. */ int _gpgme_compare_versions (const char *my_version, const char *req_version); char *_gpgme_get_program_version (const char *const path); /* From sig-notation.c. */ /* 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); /* Free the signature notation object and all associated resources. The object must already be removed from any linked list as the next pointer is ignored. */ void _gpgme_sig_notation_free (gpgme_sig_notation_t notation); /* 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); #endif /* OPS_H */ diff --git a/src/passwd.c b/src/passwd.c index 0ba8c08d..e02ffb56 100644 --- a/src/passwd.c +++ b/src/passwd.c @@ -1,205 +1,204 @@ /* passwd.c - Passphrase changing function * Copyright (C) 2010 g10 Code GmbH * * This file is part of GPGME. * * GPGME is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * GPGME is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, see . * SPDX-License-Identifier: LGPL-2.1-or-later */ #if HAVE_CONFIG_H #include #endif #include #include "gpgme.h" #include "debug.h" #include "context.h" #include "ops.h" typedef struct { /* The error code from a FAILURE status line or 0. */ gpg_error_t failure_code; int success_seen; int error_seen; } *op_data_t; /* Parse an error status line and return the error code. */ static gpgme_error_t parse_error (char *args) { gpgme_error_t err; char *where = strchr (args, ' '); char *which; if (where) { *where = '\0'; which = where + 1; where = strchr (which, ' '); if (where) *where = '\0'; where = args; } else return trace_gpg_error (GPG_ERR_INV_ENGINE); err = atoi (which); if (!strcmp (where, "keyedit.passwd")) return err; return 0; } static gpgme_error_t passwd_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_PASSWD, &hook, -1, NULL); opd = hook; if (err) return err; switch (code) { case GPGME_STATUS_ERROR: err = parse_error (args); if (err) opd->error_seen = 1; break; case GPGME_STATUS_SUCCESS: opd->success_seen = 1; break; case GPGME_STATUS_FAILURE: opd->failure_code = _gpgme_parse_failure (args); break; case GPGME_STATUS_EOF: /* In case the OpenPGP engine does not properly implement the passwd command we won't get a success status back and thus we conclude that this operation is not supported. This is for example the case for GnuPG < 2.0.16. Note that this test is obsolete for assuan based engines because they will properly return an error for an unknown command. */ if (ctx->protocol == GPGME_PROTOCOL_OpenPGP && !opd->error_seen && !opd->success_seen) err = gpg_error (GPG_ERR_NOT_SUPPORTED); else if (opd->failure_code) err = opd->failure_code; break; default: break; } return err; } static gpgme_error_t passwd_start (gpgme_ctx_t ctx, int synchronous, gpgme_key_t key, unsigned int flags) { gpgme_error_t err; void *hook; op_data_t opd; if (!key) return gpg_error (GPG_ERR_INV_VALUE); if (flags) return gpg_error (GPG_ERR_INV_FLAG); err = _gpgme_op_reset (ctx, synchronous); if (err) return err; err = _gpgme_op_data_lookup (ctx, OPDATA_PASSWD, &hook, sizeof (*opd), NULL); opd = hook; if (err) return err; opd->success_seen = 0; opd->error_seen = 0; _gpgme_engine_set_status_handler (ctx->engine, passwd_status_handler, ctx); if (ctx->passphrase_cb) { err = _gpgme_engine_set_command_handler (ctx->engine, _gpgme_passphrase_command_handler, ctx); if (err) return err; } return _gpgme_engine_op_passwd (ctx->engine, key, flags); } /* Change the passphrase for KEY. FLAGS is reserved for future use and must be passed as 0. The engine is expected to present a user interface to enter the old and the new passphrase. This is the asynchronous variant. Note that if ever the need arises to supply a passphrase we can do this with a flag value and the passphrase callback feature. */ gpgme_error_t gpgme_op_passwd_start (gpgme_ctx_t ctx, gpgme_key_t key, unsigned int flags) { gpg_error_t err; TRACE_BEG (DEBUG_CTX, "gpgme_op_passwd_start", ctx, "key=%p, flags=0x%x", key, flags); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); err = passwd_start (ctx, 0, key, flags); return TRACE_ERR (err); } /* Change the passphrase for KEY. FLAGS is reserved for future use and must be passed as 0. This is the synchronous variant. */ gpgme_error_t gpgme_op_passwd (gpgme_ctx_t ctx, gpgme_key_t key, unsigned int flags) { gpgme_error_t err; TRACE_BEG (DEBUG_CTX, "gpgme_op_passwd", ctx, "key=%p, flags=0x%x", key, flags); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); err = passwd_start (ctx, 1, key, flags); if (!err) - err = _gpgme_wait_one (ctx); + err = _gpgme_sync_wait (ctx, NULL, NULL); return TRACE_ERR (err); } - diff --git a/src/sign.c b/src/sign.c index 31081aea..e54aa246 100644 --- a/src/sign.c +++ b/src/sign.c @@ -1,500 +1,500 @@ /* sign.c - Signing function. * Copyright (C) 2000 Werner Koch (dd9jn) * Copyright (C) 2001, 2002, 2003, 2004, 2007 g10 Code GmbH * * This file is part of GPGME. * * GPGME is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * GPGME is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, see . * SPDX-License-Identifier: LGPL-2.1-or-later */ #if HAVE_CONFIG_H #include #endif #include #include #include /* Suppress warning for accessing deprecated member "class". */ #define _GPGME_IN_GPGME 1 #include "gpgme.h" #include "context.h" #include "ops.h" #include "util.h" #include "debug.h" typedef struct { struct _gpgme_op_sign_result result; /* The error code from a FAILURE status line or 0. */ gpg_error_t failure_code; /* The fingerprint from the last KEY_CONSIDERED status line. */ char *kc_fpr; /* The flags from the last KEY_CONSIDERED status line. */ unsigned int kc_flags; /* A pointer to the next pointer of the last invalid signer in the list. This makes appending new invalid signers painless while preserving the order. */ gpgme_invalid_key_t *last_signer_p; /* Likewise for signature information. */ gpgme_new_signature_t *last_sig_p; /* Flags used while processing the status lines. */ unsigned int ignore_inv_recp:1; unsigned int inv_sgnr_seen:1; unsigned int sig_created_seen:1; } *op_data_t; static void release_signatures (gpgme_new_signature_t sig) { while (sig) { gpgme_new_signature_t next = sig->next; free (sig->fpr); free (sig); sig = next; } } static void release_op_data (void *hook) { op_data_t opd = (op_data_t) hook; gpgme_invalid_key_t invalid_signer = opd->result.invalid_signers; while (invalid_signer) { gpgme_invalid_key_t next = invalid_signer->next; if (invalid_signer->fpr) free (invalid_signer->fpr); free (invalid_signer); invalid_signer = next; } release_signatures (opd->result.signatures); free (opd->kc_fpr); } gpgme_sign_result_t gpgme_op_sign_result (gpgme_ctx_t ctx) { void *hook; op_data_t opd; gpgme_error_t err; gpgme_invalid_key_t inv_key, key; gpgme_new_signature_t sig; unsigned int inv_signers = 0; unsigned int signatures = 0; TRACE_BEG (DEBUG_CTX, "gpgme_op_sign_result", ctx, ""); err = _gpgme_op_data_lookup (ctx, OPDATA_SIGN, &hook, -1, NULL); opd = hook; if (err || !opd) { TRACE_SUC ("result=(null)"); return NULL; } for (inv_key = opd->result.invalid_signers; inv_key; inv_key = inv_key->next) inv_signers++; for (sig = opd->result.signatures; sig; sig = sig->next) signatures++; if (gpgme_signers_count (ctx) && signatures + inv_signers != gpgme_signers_count (ctx)) { /* In this case at least one signatures was not created perhaps due to a bad passphrase etc. Thus the entire message is broken and should not be used. We add the already created signatures to the invalid signers list and thus this case can be detected. */ TRACE_LOG ("result: invalid signers: %u, signatures: %u, count: %u", inv_signers, signatures, gpgme_signers_count (ctx)); for (sig = opd->result.signatures; sig; sig = sig->next) { key = calloc (1, sizeof *key); if (!key) { TRACE_SUC ("out of core; result=(null)"); return NULL; } if (sig->fpr) { key->fpr = strdup (sig->fpr); if (!key->fpr) { free (key); TRACE_SUC ("out of core; result=(null)"); return NULL; } } key->reason = GPG_ERR_GENERAL; inv_key = opd->result.invalid_signers; if (inv_key) { for (; inv_key->next; inv_key = inv_key->next) ; inv_key->next = key; } else opd->result.invalid_signers = key; } release_signatures (opd->result.signatures); opd->result.signatures = NULL; } if (_gpgme_debug_trace()) { TRACE_LOG ("result: invalid signers: %i, signatures: %i", inv_signers, signatures); for (inv_key=opd->result.invalid_signers; inv_key; inv_key=inv_key->next) { TRACE_LOG ("result: invalid signer: fpr=%s, reason=%s <%s>", inv_key->fpr, gpgme_strerror (inv_key->reason), gpgme_strsource (inv_key->reason)); } for (sig = opd->result.signatures; sig; sig = sig->next) { TRACE_LOG ("result: signature: type=%i, pubkey_algo=%i, " "hash_algo=%i, timestamp=%li, fpr=%s, sig_class=%i", sig->type, sig->pubkey_algo, sig->hash_algo, sig->timestamp, sig->fpr, sig->sig_class); } } TRACE_SUC ("result=%p", &opd->result); return &opd->result; } static gpgme_error_t parse_sig_created (char *args, gpgme_new_signature_t *sigp, gpgme_protocol_t protocol) { gpgme_new_signature_t sig; char *tail; sig = malloc (sizeof (*sig)); if (!sig) return gpg_error_from_syserror (); sig->next = NULL; switch (*args) { case 'S': sig->type = GPGME_SIG_MODE_NORMAL; break; case 'D': sig->type = GPGME_SIG_MODE_DETACH; break; case 'C': sig->type = GPGME_SIG_MODE_CLEAR; break; default: /* The backend engine is not behaving. */ free (sig); return trace_gpg_error (GPG_ERR_INV_ENGINE); } args++; if (*args != ' ') { free (sig); return trace_gpg_error (GPG_ERR_INV_ENGINE); } gpg_err_set_errno (0); sig->pubkey_algo = _gpgme_map_pk_algo (strtol (args, &tail, 0), protocol); if (errno || args == tail || *tail != ' ') { /* The crypto backend does not behave. */ free (sig); return trace_gpg_error (GPG_ERR_INV_ENGINE); } args = tail; sig->hash_algo = strtol (args, &tail, 0); if (errno || args == tail || *tail != ' ') { /* The crypto backend does not behave. */ free (sig); return trace_gpg_error (GPG_ERR_INV_ENGINE); } args = tail; sig->sig_class = strtol (args, &tail, 0); sig->class = sig->sig_class; sig->_obsolete_class = sig->sig_class; if (errno || args == tail || *tail != ' ') { /* The crypto backend does not behave. */ free (sig); return trace_gpg_error (GPG_ERR_INV_ENGINE); } args = tail; sig->timestamp = _gpgme_parse_timestamp (args, &tail); if (sig->timestamp == -1 || args == tail || *tail != ' ') { /* The crypto backend does not behave. */ free (sig); return trace_gpg_error (GPG_ERR_INV_ENGINE); } args = tail; while (*args == ' ') args++; if (!*args) { /* The crypto backend does not behave. */ free (sig); return trace_gpg_error (GPG_ERR_INV_ENGINE); } tail = strchr (args, ' '); if (tail) *tail = '\0'; sig->fpr = strdup (args); if (!sig->fpr) { free (sig); return gpg_error_from_syserror (); } *sigp = sig; return 0; } gpgme_error_t _gpgme_sign_status_handler (void *priv, gpgme_status_code_t code, char *args) { gpgme_ctx_t ctx = (gpgme_ctx_t) priv; gpgme_error_t err; void *hook; op_data_t opd; err = _gpgme_passphrase_status_handler (priv, code, args); if (err) return err; err = _gpgme_op_data_lookup (ctx, OPDATA_SIGN, &hook, -1, NULL); opd = hook; if (err) return err; switch (code) { case GPGME_STATUS_SIG_CREATED: opd->sig_created_seen = 1; err = parse_sig_created (args, opd->last_sig_p, ctx->protocol); if (err) return err; opd->last_sig_p = &(*opd->last_sig_p)->next; break; case GPGME_STATUS_KEY_CONSIDERED: /* This is emitted during gpg's key lookup to give information * about the lookup results. We store the last one so it can be * used in connection with INV_RECP. */ free (opd->kc_fpr); opd->kc_fpr = NULL; err = _gpgme_parse_key_considered (args, &opd->kc_fpr, &opd->kc_flags); if (err) return err; break; case GPGME_STATUS_INV_RECP: if (opd->inv_sgnr_seen && opd->ignore_inv_recp) break; /* FALLTHROUGH */ case GPGME_STATUS_INV_SGNR: if (code == GPGME_STATUS_INV_SGNR) opd->inv_sgnr_seen = 1; free (opd->kc_fpr); opd->kc_fpr = NULL; err = _gpgme_parse_inv_recp (args, 1, opd->kc_fpr, opd->kc_flags, opd->last_signer_p); if (err) return err; opd->last_signer_p = &(*opd->last_signer_p)->next; free (opd->kc_fpr); opd->kc_fpr = NULL; break; case GPGME_STATUS_FAILURE: opd->failure_code = _gpgme_parse_failure (args); break; case GPGME_STATUS_EOF: /* The UI server does not send information about the created signature. This is irrelevant for this protocol and thus we should not check for that. */ if (opd->result.invalid_signers) err = gpg_error (GPG_ERR_UNUSABLE_SECKEY); else if (!opd->sig_created_seen && ctx->protocol != GPGME_PROTOCOL_UISERVER) err = opd->failure_code? opd->failure_code:gpg_error (GPG_ERR_GENERAL); break; case GPGME_STATUS_INQUIRE_MAXLEN: if (ctx->status_cb && !ctx->full_status) err = ctx->status_cb (ctx->status_cb_value, "INQUIRE_MAXLEN", args); break; default: break; } return err; } static gpgme_error_t sign_status_handler (void *priv, gpgme_status_code_t code, char *args) { gpgme_error_t err; err = _gpgme_progress_status_handler (priv, code, args); if (!err) err = _gpgme_sign_status_handler (priv, code, args); return err; } static gpgme_error_t sign_init_result (gpgme_ctx_t ctx, int ignore_inv_recp) { gpgme_error_t err; void *hook; op_data_t opd; err = _gpgme_op_data_lookup (ctx, OPDATA_SIGN, &hook, sizeof (*opd), release_op_data); opd = hook; if (err) return err; opd->failure_code = 0; opd->last_signer_p = &opd->result.invalid_signers; opd->last_sig_p = &opd->result.signatures; opd->ignore_inv_recp = !!ignore_inv_recp; opd->inv_sgnr_seen = 0; opd->sig_created_seen = 0; return 0; } gpgme_error_t _gpgme_op_sign_init_result (gpgme_ctx_t ctx) { return sign_init_result (ctx, 0); } static gpgme_error_t sign_start (gpgme_ctx_t ctx, int synchronous, gpgme_data_t plain, gpgme_data_t sig, gpgme_sig_mode_t mode) { gpgme_error_t err; err = _gpgme_op_reset (ctx, synchronous); if (err) return err; /* If we are using the CMS protocol, we ignore the INV_RECP status code if a newer GPGSM is in use. GPGMS does not support combined sign+encrypt and thus this can't harm. */ err = sign_init_result (ctx, (ctx->protocol == GPGME_PROTOCOL_CMS)); if (err) return err; if (mode != GPGME_SIG_MODE_NORMAL && mode != GPGME_SIG_MODE_DETACH && mode != GPGME_SIG_MODE_CLEAR) return gpg_error (GPG_ERR_INV_VALUE); if (!plain) return gpg_error (GPG_ERR_NO_DATA); if (!sig) return gpg_error (GPG_ERR_INV_VALUE); if (ctx->passphrase_cb) { err = _gpgme_engine_set_command_handler (ctx->engine, _gpgme_passphrase_command_handler, ctx); if (err) return err; } _gpgme_engine_set_status_handler (ctx->engine, sign_status_handler, ctx); return _gpgme_engine_op_sign (ctx->engine, plain, sig, mode, ctx->use_armor, ctx->use_textmode, ctx->include_certs, ctx /* FIXME */); } /* Sign the plaintext PLAIN and store the signature in SIG. */ gpgme_error_t gpgme_op_sign_start (gpgme_ctx_t ctx, gpgme_data_t plain, gpgme_data_t sig, gpgme_sig_mode_t mode) { gpg_error_t err; TRACE_BEG (DEBUG_CTX, "gpgme_op_sign_start", ctx, "plain=%p, sig=%p, mode=%i", plain, sig, mode); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); err = sign_start (ctx, 0, plain, sig, mode); return TRACE_ERR (err); } /* Sign the plaintext PLAIN and store the signature in SIG. */ gpgme_error_t gpgme_op_sign (gpgme_ctx_t ctx, gpgme_data_t plain, gpgme_data_t sig, gpgme_sig_mode_t mode) { gpgme_error_t err; TRACE_BEG (DEBUG_CTX, "gpgme_op_sign", ctx, "plain=%p, sig=%p, mode=%i", plain, sig, mode); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); err = sign_start (ctx, 1, plain, sig, mode); if (!err) - err = _gpgme_wait_one (ctx); + err = _gpgme_sync_wait (ctx, NULL, NULL); return TRACE_ERR (err); } diff --git a/src/spawn.c b/src/spawn.c index c5431b14..7f71d66f 100644 --- a/src/spawn.c +++ b/src/spawn.c @@ -1,106 +1,106 @@ /* spawn.c - Run an arbitrary command with callbacks. * Copyright (C) 2014 Code GmbH * * This file is part of GPGME. * * GPGME is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * GPGME is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, see . * SPDX-License-Identifier: LGPL-2.1-or-later */ #if HAVE_CONFIG_H #include #endif #include #include "gpgme.h" #include "debug.h" #include "context.h" #include "util.h" #include "ops.h" static gpgme_error_t spawn_start (gpgme_ctx_t ctx, int synchronous, const char *file, const char *argv[], gpgme_data_t datain, gpgme_data_t dataout, gpgme_data_t dataerr, unsigned int flags) { gpgme_error_t err; const char *tmp_argv[2]; if (ctx->protocol != GPGME_PROTOCOL_SPAWN) return gpg_error (GPG_ERR_UNSUPPORTED_PROTOCOL); err = _gpgme_op_reset (ctx, synchronous); if (err) return err; if (!argv) { tmp_argv[0] = _gpgme_get_basename (file); tmp_argv[1] = NULL; argv = tmp_argv; } return _gpgme_engine_op_spawn (ctx->engine, file, argv, datain, dataout, dataerr, flags); } /* Run the command FILE with the arguments in ARGV. Connect stdin to DATAIN, stdout to DATAOUT, and STDERR to DATAERR. If one the data streams is NULL, connect to /dev/null instead. */ gpgme_error_t gpgme_op_spawn_start (gpgme_ctx_t ctx, const char *file, const char *argv[], gpgme_data_t datain, gpgme_data_t dataout, gpgme_data_t dataerr, unsigned int flags) { gpgme_error_t err; TRACE_BEG (DEBUG_CTX, "gpgme_op_spawn_start", ctx, "file=(%s) flaggs=%x", file, flags); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); err = spawn_start (ctx, 0, file, argv, datain, dataout, dataerr, flags); return err; } /* Run the command FILE with the arguments in ARGV. Connect stdin to DATAIN, stdout to DATAOUT, and STDERR to DATAERR. If one the data streams is NULL, connect to /dev/null instead. Synchronous variant. */ gpgme_error_t gpgme_op_spawn (gpgme_ctx_t ctx, const char *file, const char *argv[], gpgme_data_t datain, gpgme_data_t dataout, gpgme_data_t dataerr, unsigned int flags) { gpgme_error_t err; TRACE_BEG (DEBUG_CTX, "gpgme_op_spawn", ctx, "file=(%s) flags=%x", file, flags); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); err = spawn_start (ctx, 1, file, argv, datain, dataout, dataerr, flags); if (!err) - err = _gpgme_wait_one (ctx); + err = _gpgme_sync_wait (ctx, NULL, NULL); return TRACE_ERR (err); } diff --git a/src/tofupolicy.c b/src/tofupolicy.c index 79593186..c2936a51 100644 --- a/src/tofupolicy.c +++ b/src/tofupolicy.c @@ -1,185 +1,185 @@ /* tofupolicy.c - Tofu policy helpers. * Copyright (C) 2016 g10 Code GmbH * * This file is part of GPGME. * * GPGME is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * GPGME is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, see . * SPDX-License-Identifier: LGPL-2.1-or-later */ #if HAVE_CONFIG_H #include #endif #include #include "gpgme.h" #include "debug.h" #include "context.h" #include "ops.h" typedef struct { /* The error code from a FAILURE status line or 0. */ gpg_error_t failure_code; /* The error code from an ERROR status line or 0. */ gpg_error_t error_code; } *op_data_t; /* Parse an error status line. Return the error location and the error code. The function may modify ARGS. */ static char * parse_error (char *args, gpg_error_t *r_err) { char *where = strchr (args, ' '); char *which; if (where) { *where = '\0'; which = where + 1; where = strchr (which, ' '); if (where) *where = '\0'; where = args; } else { *r_err = trace_gpg_error (GPG_ERR_INV_ENGINE); return NULL; } *r_err = atoi (which); return where; } static gpgme_error_t tofu_policy_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; char *loc; err = _gpgme_op_data_lookup (ctx, OPDATA_TOFU_POLICY, &hook, -1, NULL); opd = hook; if (err) return err; switch (code) { case GPGME_STATUS_ERROR: loc = parse_error (args, &err); if (!loc) return err; if (!opd->error_code) opd->error_code = err; break; case GPGME_STATUS_FAILURE: opd->failure_code = _gpgme_parse_failure (args); break; case GPGME_STATUS_EOF: if (opd->error_code) err = opd->error_code; else if (opd->failure_code) err = opd->failure_code; break; default: break; } return err; } /* Set the TOFU policy for KEY to POLICY. */ static gpgme_error_t tofu_policy_start (gpgme_ctx_t ctx, int synchronous, gpgme_key_t key, gpgme_tofu_policy_t policy) { gpgme_error_t err; void *hook; op_data_t opd; if (ctx->protocol != GPGME_PROTOCOL_OPENPGP) return gpgme_error (GPG_ERR_UNSUPPORTED_PROTOCOL); if (!key) return gpg_error (GPG_ERR_INV_VALUE); err = _gpgme_op_reset (ctx, synchronous); if (err) return err; err = _gpgme_op_data_lookup (ctx, OPDATA_TOFU_POLICY, &hook, sizeof (*opd), NULL); opd = hook; if (err) return err; _gpgme_engine_set_status_handler (ctx->engine, tofu_policy_status_handler, ctx); return _gpgme_engine_op_tofu_policy (ctx->engine, key, policy); } /* Set the TOFU policy of KEY to POLCIY. This is the asynchronous * variant. */ gpgme_error_t gpgme_op_tofu_policy_start (gpgme_ctx_t ctx, gpgme_key_t key, gpgme_tofu_policy_t policy) { gpg_error_t err; TRACE_BEG (DEBUG_CTX, "gpgme_op_tofu_policy_start", ctx, "key=%p, policy=%u", key, (unsigned int)policy); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); err = tofu_policy_start (ctx, 0, key, policy); return TRACE_ERR (err); } /* This is the synchronous variant of gpgme_op_tofu_policy_start. */ gpgme_error_t gpgme_op_tofu_policy (gpgme_ctx_t ctx, gpgme_key_t key, gpgme_tofu_policy_t policy) { gpgme_error_t err; TRACE_BEG (DEBUG_CTX, "gpgme_op_tofu_policy", ctx, "key=%p, policy=%u", key, (unsigned int)policy); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); err = tofu_policy_start (ctx, 1, key, policy); if (!err) - err = _gpgme_wait_one (ctx); + err = _gpgme_sync_wait (ctx, NULL, NULL); return TRACE_ERR (err); } diff --git a/src/trustlist.c b/src/trustlist.c index a0e82385..8a966002 100644 --- a/src/trustlist.c +++ b/src/trustlist.c @@ -1,279 +1,279 @@ /* trustlist.c - Trust item listing. * Copyright (C) 2000 Werner Koch (dd9jn) * Copyright (C) 2001, 2002, 2003, 2004 g10 Code GmbH * * This file is part of GPGME. * * GPGME is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * GPGME is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, see . * SPDX-License-Identifier: LGPL-2.1-or-later */ #if HAVE_CONFIG_H #include #endif #include #include #include #include #include "gpgme.h" #include "debug.h" #include "util.h" #include "context.h" #include "ops.h" struct trust_queue_item_s { struct trust_queue_item_s *next; gpgme_trust_item_t item; }; typedef struct { /* Something new is available. */ int trust_cond; struct trust_queue_item_s *trust_queue; } *op_data_t; static gpgme_error_t trustlist_status_handler (void *priv, gpgme_status_code_t code, char *args) { (void)priv; (void)code; (void)args; return 0; } /* This handler is used to parse the output of --list-trust-path: Format: level:keyid:type:recno:ot:val:mc:cc:name: With TYPE = U for a user ID K for a key The RECNO is either the one of the dir record or the one of the uid record. OT is the the usual trust letter and only available on K lines. VAL is the calculated validity MC is the marginal trust counter and only available on U lines CC is the same for the complete count NAME ist the username and only printed on U lines. */ static gpgme_error_t trustlist_colon_handler (void *priv, char *line) { gpgme_ctx_t ctx = (gpgme_ctx_t) priv; gpgme_error_t err; char *p, *pend; int field = 0; gpgme_trust_item_t item = NULL; if (!line) return 0; /* EOF */ for (p = line; p; p = pend) { field++; pend = strchr (p, ':'); if (pend) *pend++ = 0; switch (field) { case 1: /* level */ err = _gpgme_trust_item_new (&item); if (err) return err; item->level = atoi (p); break; case 2: /* long keyid */ if (strlen (p) == DIM(item->keyid) - 1) strcpy (item->keyid, p); break; case 3: /* type */ item->type = *p == 'K'? 1 : *p == 'U'? 2 : 0; break; case 5: /* owner trust */ item->_owner_trust[0] = *p; break; case 6: /* validity */ item->_validity[0] = *p; break; case 9: /* user ID */ item->name = strdup (p); if (!item->name) { int saved_err = gpg_error_from_syserror (); gpgme_trust_item_unref (item); return saved_err; } break; } } if (item) _gpgme_engine_io_event (ctx->engine, GPGME_EVENT_NEXT_TRUSTITEM, item); return 0; } void _gpgme_op_trustlist_event_cb (void *data, gpgme_event_io_t type, void *type_data) { gpgme_ctx_t ctx = (gpgme_ctx_t) data; gpgme_error_t err; void *hook; op_data_t opd; gpgme_trust_item_t item = (gpgme_trust_item_t) type_data; struct trust_queue_item_s *q, *q2; assert (type == GPGME_EVENT_NEXT_TRUSTITEM); err = _gpgme_op_data_lookup (ctx, OPDATA_TRUSTLIST, &hook, -1, NULL); opd = hook; if (err) return; q = malloc (sizeof *q); if (!q) { gpgme_trust_item_unref (item); /* FIXME: GPGME_Out_Of_Core; */ return; } q->item = item; q->next = NULL; /* FIXME: Use a tail pointer */ q2 = opd->trust_queue; if (!q2) opd->trust_queue = q; else { while (q2->next) q2 = q2->next; q2->next = q; } /* FIXME: unlock queue */ opd->trust_cond = 1; } gpgme_error_t gpgme_op_trustlist_start (gpgme_ctx_t ctx, const char *pattern, int max_level) { gpgme_error_t err = 0; void *hook; op_data_t opd; TRACE_BEG (DEBUG_CTX, "gpgme_op_trustlist_start", ctx, "pattern=%s, max_level=%i", pattern, max_level); if (!ctx || !pattern || !*pattern) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); err = _gpgme_op_reset (ctx, 2); if (err) return TRACE_ERR (err); err = _gpgme_op_data_lookup (ctx, OPDATA_TRUSTLIST, &hook, sizeof (*opd), NULL); opd = hook; if (err) return TRACE_ERR (err); _gpgme_engine_set_status_handler (ctx->engine, trustlist_status_handler, ctx); err = _gpgme_engine_set_colon_line_handler (ctx->engine, trustlist_colon_handler, ctx); if (err) return TRACE_ERR (err); err = _gpgme_engine_op_trustlist (ctx->engine, pattern); return TRACE_ERR (err); } gpgme_error_t gpgme_op_trustlist_next (gpgme_ctx_t ctx, gpgme_trust_item_t *r_item) { gpgme_error_t err; void *hook; op_data_t opd; struct trust_queue_item_s *q; TRACE_BEG (DEBUG_CTX, "gpgme_op_trustlist_next", ctx, ""); if (!ctx || !r_item) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); *r_item = NULL; if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); err = _gpgme_op_data_lookup (ctx, OPDATA_TRUSTLIST, &hook, -1, NULL); opd = hook; if (err) return TRACE_ERR (err); if (opd == NULL) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); if (!opd->trust_queue) { - err = _gpgme_wait_on_condition (ctx, &opd->trust_cond, NULL); + err = _gpgme_sync_wait (ctx, &opd->trust_cond, NULL); if (err) return TRACE_ERR (err); if (!opd->trust_cond) return TRACE_ERR (gpg_error (GPG_ERR_EOF)); opd->trust_cond = 0; assert (opd->trust_queue); } q = opd->trust_queue; opd->trust_queue = q->next; *r_item = q->item; free (q); if ((*r_item)->type == 1) { TRACE_SUC ("trust_item=%p: %s: owner trust %s with level %i " "and validity %s", *r_item, (*r_item)->keyid, (*r_item)->owner_trust, (*r_item)->level, (*r_item)->validity); } else if ((*r_item)->type == 2) { TRACE_SUC ("trust_item=%p: %s: UID %s with level %i " "and validity %s", *r_item, (*r_item)->keyid, (*r_item)->name, (*r_item)->level, (*r_item)->validity); } else { TRACE_SUC ("trust_item=%p: %s: unknown type %i with level %i " "and validity %s", *r_item, (*r_item)->keyid, (*r_item)->type, (*r_item)->level, (*r_item)->validity); } return 0; } /* Terminate a pending trustlist operation within CTX. */ gpgme_error_t gpgme_op_trustlist_end (gpgme_ctx_t ctx) { TRACE (DEBUG_CTX, "gpgme_op_trustlist_end", ctx, ""); if (!ctx) return gpg_error (GPG_ERR_INV_VALUE); return 0; } diff --git a/src/verify.c b/src/verify.c index f6bc23c4..eab933b4 100644 --- a/src/verify.c +++ b/src/verify.c @@ -1,1394 +1,1394 @@ /* verify.c - Signature verification. * Copyright (C) 2000 Werner Koch (dd9jn) * Copyright (C) 2001, 2002, 2003, 2004, 2005 g10 Code GmbH * * This file is part of GPGME. * * GPGME is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * GPGME is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, see . * SPDX-License-Identifier: LGPL-2.1-or-later */ #if HAVE_CONFIG_H #include #endif #include #include #include #include #include #include "gpgme.h" #include "debug.h" #include "util.h" #include "context.h" #include "ops.h" typedef struct { struct _gpgme_op_verify_result result; /* The error code from a FAILURE status line or 0. */ gpg_error_t failure_code; gpgme_signature_t current_sig; int did_prepare_new_sig; int only_newsig_seen; int plaintext_seen; int conflict_user_seen; } *op_data_t; static void release_op_data (void *hook) { op_data_t opd = (op_data_t) hook; gpgme_signature_t sig = opd->result.signatures; while (sig) { gpgme_signature_t next = sig->next; gpgme_sig_notation_t notation = sig->notations; while (notation) { gpgme_sig_notation_t next_nota = notation->next; _gpgme_sig_notation_free (notation); notation = next_nota; } if (sig->fpr) free (sig->fpr); if (sig->pka_address) free (sig->pka_address); if (sig->key) gpgme_key_unref (sig->key); free (sig); sig = next; } if (opd->result.file_name) free (opd->result.file_name); } gpgme_verify_result_t gpgme_op_verify_result (gpgme_ctx_t ctx) { void *hook; op_data_t opd; gpgme_error_t err; gpgme_signature_t sig; TRACE_BEG (DEBUG_CTX, "gpgme_op_verify_result", ctx, ""); err = _gpgme_op_data_lookup (ctx, OPDATA_VERIFY, &hook, -1, NULL); opd = hook; if (err || !opd) { TRACE_SUC ("result=(null)"); return NULL; } /* It is possible that we saw a new signature only followed by an ERROR line for that. In particular a missing X.509 key triggers this. In this case it is surprising that the summary field has not been updated. We fix it here by explicitly looking for this case. The real fix would be to have GPGME emit ERRSIG. */ for (sig = opd->result.signatures; sig; sig = sig->next) { if (!sig->summary) { switch (gpg_err_code (sig->status)) { case GPG_ERR_KEY_EXPIRED: sig->summary |= GPGME_SIGSUM_KEY_EXPIRED; break; case GPG_ERR_NO_PUBKEY: sig->summary |= GPGME_SIGSUM_KEY_MISSING; break; default: break; } } } /* Now for some tracing stuff. */ if (_gpgme_debug_trace ()) { int i; for (sig = opd->result.signatures, i = 0; sig; sig = sig->next, i++) { TRACE_LOG ("sig[%i] = fpr %s, summary 0x%x, status %s", i, sig->fpr, sig->summary, gpg_strerror (sig->status)); TRACE_LOG ("sig[%i] = timestamps 0x%lx/0x%lx flags:%s%s%s", i, sig->timestamp, sig->exp_timestamp, sig->wrong_key_usage ? "wrong key usage" : "", sig->pka_trust == 1 ? "pka bad" : (sig->pka_trust == 2 ? "pka_okay" : "pka RFU"), sig->chain_model ? "chain model" : ""); TRACE_LOG ("sig[%i] = validity 0x%x (%s), algos %s/%s", i, sig->validity, gpg_strerror (sig->validity_reason), gpgme_pubkey_algo_name (sig->pubkey_algo), gpgme_hash_algo_name (sig->hash_algo)); if (sig->pka_address) { TRACE_LOG ("sig[%i] = PKA address %s", i, sig->pka_address); } if (sig->notations) { TRACE_LOG ("sig[%i] = has notations (not shown)", i); } } } TRACE_SUC ("result=%p", &opd->result); return &opd->result; } /* Build a summary vector from RESULT. */ static void calc_sig_summary (gpgme_signature_t sig) { unsigned long sum = 0; /* Calculate the red/green flag. */ if (sig->validity == GPGME_VALIDITY_FULL || sig->validity == GPGME_VALIDITY_ULTIMATE) { if (gpg_err_code (sig->status) == GPG_ERR_NO_ERROR || gpg_err_code (sig->status) == GPG_ERR_SIG_EXPIRED || gpg_err_code (sig->status) == GPG_ERR_KEY_EXPIRED) sum |= GPGME_SIGSUM_GREEN; } else if (sig->validity == GPGME_VALIDITY_NEVER) { if (gpg_err_code (sig->status) == GPG_ERR_NO_ERROR || gpg_err_code (sig->status) == GPG_ERR_SIG_EXPIRED || gpg_err_code (sig->status) == GPG_ERR_KEY_EXPIRED) sum |= GPGME_SIGSUM_RED; } else if (gpg_err_code (sig->status) == GPG_ERR_BAD_SIGNATURE) sum |= GPGME_SIGSUM_RED; /* FIXME: handle the case when key and message are expired. */ switch (gpg_err_code (sig->status)) { case GPG_ERR_SIG_EXPIRED: sum |= GPGME_SIGSUM_SIG_EXPIRED; break; case GPG_ERR_KEY_EXPIRED: sum |= GPGME_SIGSUM_KEY_EXPIRED; break; case GPG_ERR_NO_PUBKEY: sum |= GPGME_SIGSUM_KEY_MISSING; break; case GPG_ERR_CERT_REVOKED: sum |= GPGME_SIGSUM_KEY_REVOKED; break; case GPG_ERR_BAD_SIGNATURE: case GPG_ERR_NO_ERROR: break; default: sum |= GPGME_SIGSUM_SYS_ERROR; break; } /* Now look at the certain reason codes. */ switch (gpg_err_code (sig->validity_reason)) { case GPG_ERR_CRL_TOO_OLD: if (sig->validity == GPGME_VALIDITY_UNKNOWN) sum |= GPGME_SIGSUM_CRL_TOO_OLD; break; case GPG_ERR_CERT_REVOKED: /* Note that this is a second way to set this flag. It may also have been set due to a sig->status of STATUS_REVKEYSIG from parse_new_sig. */ sum |= GPGME_SIGSUM_KEY_REVOKED; break; default: break; } /* Check other flags. */ if (sig->wrong_key_usage) sum |= GPGME_SIGSUM_BAD_POLICY; /* Set the valid flag when the signature is unquestionable valid. (The test is identical to if(sum == GPGME_SIGSUM_GREEN)). */ if ((sum & GPGME_SIGSUM_GREEN) && !(sum & ~GPGME_SIGSUM_GREEN)) sum |= GPGME_SIGSUM_VALID; sig->summary = sum; } static gpgme_error_t prepare_new_sig (op_data_t opd) { gpgme_signature_t sig; if (opd->only_newsig_seen && opd->current_sig) { /* We have only seen the NEWSIG status and nothing else - we better skip this signature therefore and reuse it for the next possible signature. */ sig = opd->current_sig; memset (sig, 0, sizeof *sig); assert (opd->result.signatures == sig); } else { sig = calloc (1, sizeof (*sig)); if (!sig) return gpg_error_from_syserror (); if (!opd->result.signatures) opd->result.signatures = sig; if (opd->current_sig) opd->current_sig->next = sig; opd->current_sig = sig; } opd->did_prepare_new_sig = 1; opd->only_newsig_seen = 0; return 0; } static gpgme_error_t parse_new_sig (op_data_t opd, gpgme_status_code_t code, char *args, gpgme_protocol_t protocol) { gpgme_signature_t sig; char *end = strchr (args, ' '); char *tail; int got_fpr = 0; if (end) { *end = '\0'; end++; } if (!opd->did_prepare_new_sig) { gpg_error_t err; err = prepare_new_sig (opd); if (err) return err; } assert (opd->did_prepare_new_sig); opd->did_prepare_new_sig = 0; assert (opd->current_sig); sig = opd->current_sig; /* FIXME: We should set the source of the state. */ switch (code) { case GPGME_STATUS_GOODSIG: sig->status = gpg_error (GPG_ERR_NO_ERROR); break; case GPGME_STATUS_EXPSIG: sig->status = gpg_error (GPG_ERR_SIG_EXPIRED); break; case GPGME_STATUS_EXPKEYSIG: sig->status = gpg_error (GPG_ERR_KEY_EXPIRED); break; case GPGME_STATUS_BADSIG: sig->status = gpg_error (GPG_ERR_BAD_SIGNATURE); break; case GPGME_STATUS_REVKEYSIG: sig->status = gpg_error (GPG_ERR_CERT_REVOKED); break; case GPGME_STATUS_ERRSIG: /* Parse the pubkey algo. */ if (!end) goto parse_err_sig_fail; gpg_err_set_errno (0); sig->pubkey_algo = _gpgme_map_pk_algo (strtol (end, &tail, 0), protocol); if (errno || end == tail || *tail != ' ') goto parse_err_sig_fail; end = tail; while (*end == ' ') end++; /* Parse the hash algo. */ if (!*end) goto parse_err_sig_fail; gpg_err_set_errno (0); sig->hash_algo = strtol (end, &tail, 0); if (errno || end == tail || *tail != ' ') goto parse_err_sig_fail; end = tail; while (*end == ' ') end++; /* Skip the sig class. */ end = strchr (end, ' '); if (!end) goto parse_err_sig_fail; while (*end == ' ') end++; /* Parse the timestamp. */ sig->timestamp = _gpgme_parse_timestamp (end, &tail); if (sig->timestamp == -1 || end == tail || (*tail && *tail != ' ')) return trace_gpg_error (GPG_ERR_INV_ENGINE); end = tail; while (*end == ' ') end++; /* Parse the return code. */ if (!*end) goto parse_err_sig_fail; gpg_err_set_errno (0); sig->status = strtoul (end, &tail, 10); if (errno || end == tail || (*tail && *tail != ' ')) goto parse_err_sig_fail; if (!*tail) goto parse_err_sig_ok; end = tail; while (*end == ' ') end++; /* Parse the new fingerprint (from the ISSUER_FPR subpacket). */ if (!*end || (*end == '-' && (end[1] == ' ' || !end[1]))) goto parse_err_sig_ok; /* Okay (just trailing spaces). */ sig->fpr = strdup (end); if (!sig->fpr) return gpg_error_from_syserror (); got_fpr = 1; goto parse_err_sig_ok; parse_err_sig_fail: sig->status = gpg_error (GPG_ERR_GENERAL); parse_err_sig_ok: break; default: return gpg_error (GPG_ERR_GENERAL); } if (*args && !got_fpr) { sig->fpr = strdup (args); if (!sig->fpr) return gpg_error_from_syserror (); } return 0; } static gpgme_error_t parse_valid_sig (gpgme_signature_t sig, char *args, gpgme_protocol_t protocol) { char *end = strchr (args, ' '); if (end) { *end = '\0'; end++; } if (!*args) /* We require at least the fingerprint. */ return gpg_error (GPG_ERR_GENERAL); if (sig->fpr) free (sig->fpr); sig->fpr = strdup (args); if (!sig->fpr) return gpg_error_from_syserror (); /* Skip the creation date. */ end = strchr (end, ' '); if (end) { char *tail; sig->timestamp = _gpgme_parse_timestamp (end, &tail); if (sig->timestamp == -1 || end == tail || (*tail && *tail != ' ')) return trace_gpg_error (GPG_ERR_INV_ENGINE); end = tail; sig->exp_timestamp = _gpgme_parse_timestamp (end, &tail); if (sig->exp_timestamp == -1 || end == tail || (*tail && *tail != ' ')) return trace_gpg_error (GPG_ERR_INV_ENGINE); end = tail; while (*end == ' ') end++; /* Skip the signature version. */ end = strchr (end, ' '); if (end) { while (*end == ' ') end++; /* Skip the reserved field. */ end = strchr (end, ' '); if (end) { /* Parse the pubkey algo. */ gpg_err_set_errno (0); sig->pubkey_algo = _gpgme_map_pk_algo (strtol (end, &tail, 0), protocol); if (errno || end == tail || *tail != ' ') return trace_gpg_error (GPG_ERR_INV_ENGINE); end = tail; while (*end == ' ') end++; if (*end) { /* Parse the hash algo. */ gpg_err_set_errno (0); sig->hash_algo = strtol (end, &tail, 0); if (errno || end == tail || *tail != ' ') return trace_gpg_error (GPG_ERR_INV_ENGINE); end = tail; } } } } return 0; } static gpgme_error_t parse_notation (gpgme_signature_t sig, gpgme_status_code_t code, char *args) { gpgme_error_t err; gpgme_sig_notation_t *lastp = &sig->notations; gpgme_sig_notation_t notation = sig->notations; char *p; if (code == GPGME_STATUS_NOTATION_NAME || code == GPGME_STATUS_POLICY_URL) { p = strchr (args, ' '); if (p) *p = '\0'; /* FIXME: We could keep a pointer to the last notation in the list. */ while (notation && notation->value) { lastp = ¬ation->next; notation = notation->next; } if (notation) /* There is another notation name without data for the previous one. The crypto backend misbehaves. */ return trace_gpg_error (GPG_ERR_INV_ENGINE); err = _gpgme_sig_notation_create (¬ation, NULL, 0, NULL, 0, 0); if (err) return err; if (code == GPGME_STATUS_NOTATION_NAME) { err = _gpgme_decode_percent_string (args, ¬ation->name, 0, 0); if (err) { _gpgme_sig_notation_free (notation); return err; } notation->name_len = strlen (notation->name); /* Set default flags for use with older gpg versions which * do not emit a NOTATIONS_FLAG line. */ notation->flags = GPGME_SIG_NOTATION_HUMAN_READABLE; notation->human_readable = 1; } else { /* This is a policy URL. */ err = _gpgme_decode_percent_string (args, ¬ation->value, 0, 0); if (err) { _gpgme_sig_notation_free (notation); return err; } notation->value_len = strlen (notation->value); } *lastp = notation; } else if (code == GPGME_STATUS_NOTATION_FLAGS) { char *field[2]; while (notation && notation->next) { lastp = ¬ation->next; notation = notation->next; } if (!notation || !notation->name) { /* There are notation flags without a previous notation name. * The crypto backend misbehaves. */ return trace_gpg_error (GPG_ERR_INV_ENGINE); } if (_gpgme_split_fields (args, field, DIM (field)) < 2) { /* Required args missing. */ return trace_gpg_error (GPG_ERR_INV_ENGINE); } notation->flags = 0; if (atoi (field[0])) { notation->flags |= GPGME_SIG_NOTATION_CRITICAL; notation->critical = 1; } if (atoi (field[1])) { notation->flags |= GPGME_SIG_NOTATION_HUMAN_READABLE; notation->human_readable = 1; } } else if (code == GPGME_STATUS_NOTATION_DATA) { int len = strlen (args) + 1; char *dest; /* FIXME: We could keep a pointer to the last notation in the list. */ while (notation && notation->next) { lastp = ¬ation->next; notation = notation->next; } if (!notation || !notation->name) /* There is notation data without a previous notation name. The crypto backend misbehaves. */ return trace_gpg_error (GPG_ERR_INV_ENGINE); if (!notation->value) { dest = notation->value = malloc (len); if (!dest) return gpg_error_from_syserror (); } else { int cur_len = strlen (notation->value); dest = realloc (notation->value, len + strlen (notation->value)); if (!dest) return gpg_error_from_syserror (); notation->value = dest; dest += cur_len; } err = _gpgme_decode_percent_string (args, &dest, len, 0); if (err) return err; notation->value_len += strlen (dest); } else return trace_gpg_error (GPG_ERR_INV_ENGINE); return 0; } static gpgme_error_t parse_trust (gpgme_signature_t sig, gpgme_status_code_t code, char *args) { char *end = strchr (args, ' '); if (end) *end = '\0'; switch (code) { case GPGME_STATUS_TRUST_UNDEFINED: default: sig->validity = GPGME_VALIDITY_UNKNOWN; break; case GPGME_STATUS_TRUST_NEVER: sig->validity = GPGME_VALIDITY_NEVER; break; case GPGME_STATUS_TRUST_MARGINAL: sig->validity = GPGME_VALIDITY_MARGINAL; break; case GPGME_STATUS_TRUST_FULLY: case GPGME_STATUS_TRUST_ULTIMATE: sig->validity = GPGME_VALIDITY_FULL; break; } sig->validity_reason = 0; sig->chain_model = 0; if (*args) { sig->validity_reason = atoi (args); while (*args && *args != ' ') args++; if (*args) { while (*args == ' ') args++; if (!strncmp (args, "chain", 2) && (args[2] == ' ' || !args[2])) sig->chain_model = 1; } } return 0; } /* Parse a TOFU_USER line and put the info into SIG. */ static gpgme_error_t parse_tofu_user (gpgme_signature_t sig, char *args, gpgme_protocol_t protocol) { gpg_error_t err; char *tail; gpgme_user_id_t uid; gpgme_tofu_info_t ti; char *fpr = NULL; char *address = NULL; tail = strchr (args, ' '); if (!tail || tail == args) { err = trace_gpg_error (GPG_ERR_INV_ENGINE); /* No fingerprint. */ goto leave; } *tail++ = 0; fpr = strdup (args); if (!fpr) { err = gpg_error_from_syserror (); goto leave; } if (sig->key && sig->key->fpr && strcmp (sig->key->fpr, fpr)) { /* GnuPG since 2.1.17 emits multiple TOFU_USER lines with different fingerprints in case of conflicts for a signature. */ err = gpg_error (GPG_ERR_DUP_VALUE); goto leave; } args = tail; tail = strchr (args, ' '); if (tail == args) { err = trace_gpg_error (GPG_ERR_INV_ENGINE); /* No addr-spec. */ goto leave; } if (tail) *tail = 0; err = _gpgme_decode_percent_string (args, &address, 0, 0); if (err) goto leave; if (!sig->key) { err = _gpgme_key_new (&sig->key); if (err) goto leave; sig->key->fpr = fpr; sig->key->protocol = protocol; fpr = NULL; } else if (!sig->key->fpr) { err = trace_gpg_error (GPG_ERR_INTERNAL); goto leave; } err = _gpgme_key_append_name (sig->key, address, 0); if (err) goto leave; uid = sig->key->_last_uid; assert (uid); ti = calloc (1, sizeof *ti); if (!ti) { err = gpg_error_from_syserror (); goto leave; } uid->tofu = ti; leave: free (fpr); free (address); return err; } /* Parse a TOFU_STATS line and store it in the last tofu info of SIG. * * TOFU_STATS \ * [ [ ]] */ static gpgme_error_t parse_tofu_stats (gpgme_signature_t sig, char *args) { gpgme_error_t err; gpgme_tofu_info_t ti; char *field[8]; int nfields; unsigned long uval; if (!sig->key || !sig->key->_last_uid || !(ti = sig->key->_last_uid->tofu)) return trace_gpg_error (GPG_ERR_INV_ENGINE); /* No TOFU_USER seen. */ if (ti->signfirst || ti->signcount || ti->validity || ti->policy) return trace_gpg_error (GPG_ERR_INV_ENGINE); /* Already set. */ nfields = _gpgme_split_fields (args, field, DIM (field)); if (nfields < 3) return trace_gpg_error (GPG_ERR_INV_ENGINE); /* Required args missing. */ /* Note that we allow a value of up to 7 which is what we can store * in the ti->validity. */ err = _gpgme_strtoul_field (field[0], &uval); if (err || uval > 7) return trace_gpg_error (GPG_ERR_INV_ENGINE); ti->validity = uval; /* Parse the sign-count. */ err = _gpgme_strtoul_field (field[1], &uval); if (err) return trace_gpg_error (GPG_ERR_INV_ENGINE); if (uval > USHRT_MAX) uval = USHRT_MAX; ti->signcount = uval; /* Parse the encr-count. */ err = _gpgme_strtoul_field (field[2], &uval); if (err) return trace_gpg_error (GPG_ERR_INV_ENGINE); if (uval > USHRT_MAX) uval = USHRT_MAX; ti->encrcount = uval; if (nfields == 3) return 0; /* All mandatory fields parsed. */ /* Parse the policy. */ if (!strcmp (field[3], "none")) ti->policy = GPGME_TOFU_POLICY_NONE; else if (!strcmp (field[3], "auto")) ti->policy = GPGME_TOFU_POLICY_AUTO; else if (!strcmp (field[3], "good")) ti->policy = GPGME_TOFU_POLICY_GOOD; else if (!strcmp (field[3], "bad")) ti->policy = GPGME_TOFU_POLICY_BAD; else if (!strcmp (field[3], "ask")) ti->policy = GPGME_TOFU_POLICY_ASK; else /* "unknown" and invalid policy strings. */ ti->policy = GPGME_TOFU_POLICY_UNKNOWN; if (nfields == 4) return 0; /* No more optional fields. */ /* Parse first and last seen timestamps (none or both are required). */ if (nfields < 6) return trace_gpg_error (GPG_ERR_INV_ENGINE); /* "tm2" missing. */ err = _gpgme_strtoul_field (field[4], &uval); if (err) return trace_gpg_error (GPG_ERR_INV_ENGINE); ti->signfirst = uval; err = _gpgme_strtoul_field (field[5], &uval); if (err) return trace_gpg_error (GPG_ERR_INV_ENGINE); ti->signlast = uval; if (nfields > 7) { /* This condition is only to allow for gpg 2.1.15 - can * eventually be removed. */ err = _gpgme_strtoul_field (field[6], &uval); if (err) return trace_gpg_error (GPG_ERR_INV_ENGINE); ti->encrfirst = uval; err = _gpgme_strtoul_field (field[7], &uval); if (err) return trace_gpg_error (GPG_ERR_INV_ENGINE); ti->encrlast = uval; } return 0; } /* Parse a TOFU_STATS_LONG line and store it in the last tofu info of SIG. */ static gpgme_error_t parse_tofu_stats_long (gpgme_signature_t sig, char *args, int raw) { gpgme_error_t err; gpgme_tofu_info_t ti; char *p; if (!sig->key || !sig->key->_last_uid || !(ti = sig->key->_last_uid->tofu)) return trace_gpg_error (GPG_ERR_INV_ENGINE); /* No TOFU_USER seen. */ if (ti->description) return trace_gpg_error (GPG_ERR_INV_ENGINE); /* Already set. */ err = _gpgme_decode_percent_string (args, &ti->description, 0, 0); if (err) return err; /* Remove the non-breaking spaces. */ if (!raw) { for (p = ti->description; *p; p++) if (*p == '~') *p = ' '; } return 0; } /* Parse an error status line and if SET_STATUS is true update the result status as appropriate. With SET_STATUS being false, only check for an error. */ static gpgme_error_t parse_error (gpgme_signature_t sig, char *args, int set_status) { gpgme_error_t err; char *where = strchr (args, ' '); char *which; if (where) { *where = '\0'; which = where + 1; where = strchr (which, ' '); if (where) *where = '\0'; where = args; } else return trace_gpg_error (GPG_ERR_INV_ENGINE); err = atoi (which); if (!strcmp (where, "proc_pkt.plaintext") && gpg_err_code (err) == GPG_ERR_BAD_DATA) { /* This indicates a double plaintext. The only solid way to handle this is by failing the operation. */ return gpg_error (GPG_ERR_BAD_DATA); } else if (!set_status) ; else if (!strcmp (where, "verify.findkey")) sig->status = err; else if (!strcmp (where, "verify.keyusage") && gpg_err_code (err) == GPG_ERR_WRONG_KEY_USAGE) sig->wrong_key_usage = 1; return 0; } gpgme_error_t _gpgme_verify_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; gpgme_signature_t sig; char *end; err = _gpgme_op_data_lookup (ctx, OPDATA_VERIFY, &hook, -1, NULL); opd = hook; if (err) return err; sig = opd->current_sig; switch (code) { case GPGME_STATUS_NEWSIG: if (sig) calc_sig_summary (sig); err = prepare_new_sig (opd); opd->only_newsig_seen = 1; opd->conflict_user_seen = 0; return err; case GPGME_STATUS_GOODSIG: case GPGME_STATUS_EXPSIG: case GPGME_STATUS_EXPKEYSIG: case GPGME_STATUS_BADSIG: case GPGME_STATUS_ERRSIG: case GPGME_STATUS_REVKEYSIG: if (sig && !opd->did_prepare_new_sig) calc_sig_summary (sig); opd->only_newsig_seen = 0; return parse_new_sig (opd, code, args, ctx->protocol); case GPGME_STATUS_VALIDSIG: opd->only_newsig_seen = 0; return sig ? parse_valid_sig (sig, args, ctx->protocol) : trace_gpg_error (GPG_ERR_INV_ENGINE); case GPGME_STATUS_NODATA: opd->only_newsig_seen = 0; if (!sig) return gpg_error (GPG_ERR_NO_DATA); sig->status = gpg_error (GPG_ERR_NO_DATA); break; case GPGME_STATUS_UNEXPECTED: opd->only_newsig_seen = 0; if (!sig) return gpg_error (GPG_ERR_GENERAL); sig->status = gpg_error (GPG_ERR_NO_DATA); break; case GPGME_STATUS_NOTATION_NAME: case GPGME_STATUS_NOTATION_FLAGS: case GPGME_STATUS_NOTATION_DATA: case GPGME_STATUS_POLICY_URL: opd->only_newsig_seen = 0; return sig ? parse_notation (sig, code, args) : trace_gpg_error (GPG_ERR_INV_ENGINE); case GPGME_STATUS_TRUST_UNDEFINED: case GPGME_STATUS_TRUST_NEVER: case GPGME_STATUS_TRUST_MARGINAL: case GPGME_STATUS_TRUST_FULLY: case GPGME_STATUS_TRUST_ULTIMATE: opd->only_newsig_seen = 0; return sig ? parse_trust (sig, code, args) : trace_gpg_error (GPG_ERR_INV_ENGINE); case GPGME_STATUS_PKA_TRUST_BAD: case GPGME_STATUS_PKA_TRUST_GOOD: opd->only_newsig_seen = 0; /* Check that we only get one of these status codes per signature; if not the crypto backend misbehaves. */ if (!sig || sig->pka_trust || sig->pka_address) return trace_gpg_error (GPG_ERR_INV_ENGINE); sig->pka_trust = code == GPGME_STATUS_PKA_TRUST_GOOD? 2 : 1; end = strchr (args, ' '); if (end) *end = 0; sig->pka_address = strdup (args); break; case GPGME_STATUS_TOFU_USER: opd->only_newsig_seen = 0; if (!sig) return trace_gpg_error (GPG_ERR_INV_ENGINE); err = parse_tofu_user (sig, args, ctx->protocol); /* gpg emits TOFU User lines for each conflicting key. * GPGME does not expose this to have a clean API and * a GPGME user can do a keylisting with the address * normalisation. * So when a duplicated TOFU_USER line is encountered * we ignore the conflicting tofu stats emitted afterwards. */ if (gpg_err_code (err) == GPG_ERR_DUP_VALUE) { opd->conflict_user_seen = 1; break; } opd->conflict_user_seen = 0; return trace_gpg_error (err); case GPGME_STATUS_TOFU_STATS: opd->only_newsig_seen = 0; if (opd->conflict_user_seen) break; return sig ? parse_tofu_stats (sig, args) /* */ : trace_gpg_error (GPG_ERR_INV_ENGINE); case GPGME_STATUS_TOFU_STATS_LONG: opd->only_newsig_seen = 0; if (opd->conflict_user_seen) break; return sig ? parse_tofu_stats_long (sig, args, ctx->raw_description) /* */ : trace_gpg_error (GPG_ERR_INV_ENGINE); case GPGME_STATUS_ERROR: opd->only_newsig_seen = 0; /* Some error stati are informational, so we don't return an error code if we are not ready to process this status. */ return parse_error (sig, args, !!sig ); case GPGME_STATUS_FAILURE: opd->failure_code = _gpgme_parse_failure (args); break; case GPGME_STATUS_EOF: if (sig && !opd->did_prepare_new_sig) calc_sig_summary (sig); if (opd->only_newsig_seen && sig) { gpgme_signature_t sig2; /* The last signature has no valid information - remove it from the list. */ assert (!sig->next); if (sig == opd->result.signatures) opd->result.signatures = NULL; else { for (sig2 = opd->result.signatures; sig2; sig2 = sig2->next) if (sig2->next == sig) { sig2->next = NULL; break; } } /* Note that there is no need to release the members of SIG because we won't be here if they have been set. */ free (sig); opd->current_sig = NULL; } opd->only_newsig_seen = 0; if (opd->failure_code) return opd->failure_code; break; case GPGME_STATUS_PLAINTEXT: if (++opd->plaintext_seen > 1) return gpg_error (GPG_ERR_BAD_DATA); { int mime = 0; err = _gpgme_parse_plaintext (args, &opd->result.file_name, &mime); if (err) return err; opd->result.is_mime = !!mime; } break; case GPGME_STATUS_VERIFICATION_COMPLIANCE_MODE: PARSE_COMPLIANCE_FLAGS (args, opd->current_sig); break; default: break; } return 0; } static gpgme_error_t verify_status_handler (void *priv, gpgme_status_code_t code, char *args) { gpgme_error_t err; err = _gpgme_progress_status_handler (priv, code, args); if (!err) err = _gpgme_verify_status_handler (priv, code, args); return err; } gpgme_error_t _gpgme_op_verify_init_result (gpgme_ctx_t ctx) { void *hook; op_data_t opd; return _gpgme_op_data_lookup (ctx, OPDATA_VERIFY, &hook, sizeof (*opd), release_op_data); } static gpgme_error_t verify_start (gpgme_ctx_t ctx, int synchronous, gpgme_data_t sig, gpgme_data_t signed_text, gpgme_data_t plaintext) { gpgme_error_t err; err = _gpgme_op_reset (ctx, synchronous); if (err) return err; err = _gpgme_op_verify_init_result (ctx); if (err) return err; _gpgme_engine_set_status_handler (ctx->engine, verify_status_handler, ctx); if (!sig) return gpg_error (GPG_ERR_NO_DATA); return _gpgme_engine_op_verify (ctx->engine, sig, signed_text, plaintext, ctx); } /* Decrypt ciphertext CIPHER and make a signature verification within CTX and store the resulting plaintext in PLAIN. */ gpgme_error_t gpgme_op_verify_start (gpgme_ctx_t ctx, gpgme_data_t sig, gpgme_data_t signed_text, gpgme_data_t plaintext) { gpg_error_t err; TRACE_BEG (DEBUG_CTX, "gpgme_op_verify_start", ctx, "sig=%p, signed_text=%p, plaintext=%p", sig, signed_text, plaintext); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); err = verify_start (ctx, 0, sig, signed_text, plaintext); return TRACE_ERR (err); } /* Decrypt ciphertext CIPHER and make a signature verification within CTX and store the resulting plaintext in PLAIN. */ gpgme_error_t gpgme_op_verify (gpgme_ctx_t ctx, gpgme_data_t sig, gpgme_data_t signed_text, gpgme_data_t plaintext) { gpgme_error_t err; TRACE_BEG (DEBUG_CTX, "gpgme_op_verify", ctx, "sig=%p, signed_text=%p, plaintext=%p", sig, signed_text, plaintext); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); err = verify_start (ctx, 1, sig, signed_text, plaintext); if (!err) - err = _gpgme_wait_one (ctx); + err = _gpgme_sync_wait (ctx, NULL, NULL); return TRACE_ERR (err); } /* Compatibility interfaces. */ /* Get the key used to create signature IDX in CTX and return it in R_KEY. */ gpgme_error_t gpgme_get_sig_key (gpgme_ctx_t ctx, int idx, gpgme_key_t *r_key) { gpgme_verify_result_t result; gpgme_signature_t sig; if (!ctx) return gpg_error (GPG_ERR_INV_VALUE); result = gpgme_op_verify_result (ctx); sig = result->signatures; while (sig && idx) { sig = sig->next; idx--; } if (!sig || idx) return gpg_error (GPG_ERR_EOF); return gpgme_get_key (ctx, sig->fpr, r_key, 0); } /* Retrieve the signature status of signature IDX in CTX after a successful verify operation in R_STAT (if non-null). The creation time stamp of the signature is returned in R_CREATED (if non-null). The function returns a string containing the fingerprint. */ const char * gpgme_get_sig_status (gpgme_ctx_t ctx, int idx, _gpgme_sig_stat_t *r_stat, time_t *r_created) { gpgme_verify_result_t result; gpgme_signature_t sig; result = gpgme_op_verify_result (ctx); sig = result->signatures; while (sig && idx) { sig = sig->next; idx--; } if (!sig || idx) return NULL; if (r_stat) { switch (gpg_err_code (sig->status)) { case GPG_ERR_NO_ERROR: *r_stat = GPGME_SIG_STAT_GOOD; break; case GPG_ERR_BAD_SIGNATURE: *r_stat = GPGME_SIG_STAT_BAD; break; case GPG_ERR_NO_PUBKEY: *r_stat = GPGME_SIG_STAT_NOKEY; break; case GPG_ERR_NO_DATA: *r_stat = GPGME_SIG_STAT_NOSIG; break; case GPG_ERR_SIG_EXPIRED: *r_stat = GPGME_SIG_STAT_GOOD_EXP; break; case GPG_ERR_KEY_EXPIRED: *r_stat = GPGME_SIG_STAT_GOOD_EXPKEY; break; default: *r_stat = GPGME_SIG_STAT_ERROR; break; } } if (r_created) *r_created = sig->timestamp; return sig->fpr; } /* Retrieve certain attributes of a signature. IDX is the index number of the signature after a successful verify operation. WHAT is an attribute where GPGME_ATTR_EXPIRE is probably the most useful one. WHATIDX is to be passed as 0 for most attributes . */ unsigned long gpgme_get_sig_ulong_attr (gpgme_ctx_t ctx, int idx, _gpgme_attr_t what, int whatidx) { gpgme_verify_result_t result; gpgme_signature_t sig; (void)whatidx; result = gpgme_op_verify_result (ctx); sig = result->signatures; while (sig && idx) { sig = sig->next; idx--; } if (!sig || idx) return 0; switch (what) { case GPGME_ATTR_CREATED: return sig->timestamp; case GPGME_ATTR_EXPIRE: return sig->exp_timestamp; case GPGME_ATTR_VALIDITY: return (unsigned long) sig->validity; case GPGME_ATTR_SIG_STATUS: switch (gpg_err_code (sig->status)) { case GPG_ERR_NO_ERROR: return GPGME_SIG_STAT_GOOD; case GPG_ERR_BAD_SIGNATURE: return GPGME_SIG_STAT_BAD; case GPG_ERR_NO_PUBKEY: return GPGME_SIG_STAT_NOKEY; case GPG_ERR_NO_DATA: return GPGME_SIG_STAT_NOSIG; case GPG_ERR_SIG_EXPIRED: return GPGME_SIG_STAT_GOOD_EXP; case GPG_ERR_KEY_EXPIRED: return GPGME_SIG_STAT_GOOD_EXPKEY; default: return GPGME_SIG_STAT_ERROR; } case GPGME_ATTR_SIG_SUMMARY: return sig->summary; default: break; } return 0; } const char * gpgme_get_sig_string_attr (gpgme_ctx_t ctx, int idx, _gpgme_attr_t what, int whatidx) { gpgme_verify_result_t result; gpgme_signature_t sig; result = gpgme_op_verify_result (ctx); sig = result->signatures; while (sig && idx) { sig = sig->next; idx--; } if (!sig || idx) return NULL; switch (what) { case GPGME_ATTR_FPR: return sig->fpr; case GPGME_ATTR_ERRTOK: if (whatidx == 1) return sig->wrong_key_usage ? "Wrong_Key_Usage" : ""; else return ""; default: break; } return NULL; } diff --git a/src/vfs-create.c b/src/vfs-create.c index 51b8307c..7e071ba3 100644 --- a/src/vfs-create.c +++ b/src/vfs-create.c @@ -1,205 +1,204 @@ /* vfs-create.c - vfs create support in GPGME * Copyright (C) 2009 g10 Code GmbH * * This file is part of GPGME. * * GPGME is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * GPGME is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, see . * SPDX-License-Identifier: LGPL-2.1-or-later */ #if HAVE_CONFIG_H #include #endif #include #include #include "gpgme.h" #include "debug.h" #include "context.h" #include "ops.h" #include "util.h" static gpgme_error_t vfs_start (gpgme_ctx_t ctx, int synchronous, const char *command, gpgme_assuan_data_cb_t data_cb, void *data_cb_value, gpgme_assuan_inquire_cb_t inq_cb, void *inq_cb_value, gpgme_assuan_status_cb_t status_cb, void *status_cb_value) { gpgme_error_t err; if (!command || !*command) return gpg_error (GPG_ERR_INV_VALUE); /* The flag value 256 is used to suppress an engine reset. This is required to keep the connection running. */ err = _gpgme_op_reset (ctx, ((synchronous & 255) | 256)); if (err) return err; return _gpgme_engine_op_assuan_transact (ctx->engine, command, data_cb, data_cb_value, inq_cb, inq_cb_value, status_cb, status_cb_value); } #if 0 /* XXXX. This is the asynchronous variant. */ static gpgme_error_t gpgme_op_vfs_transact_start (gpgme_ctx_t ctx, const char *command, gpgme_assuan_data_cb_t data_cb, void *data_cb_value, gpgme_assuan_inquire_cb_t inq_cb, void *inq_cb_value, gpgme_assuan_status_cb_t status_cb, void *status_cb_value) { return vfs_start (ctx, 0, command, data_cb, data_cb_value, inq_cb, inq_cb_value, status_cb, status_cb_value); } #endif /* XXXX. This is the synchronous variant. */ static gpgme_error_t gpgme_op_vfs_transact (gpgme_ctx_t ctx, const char *command, gpgme_assuan_data_cb_t data_cb, void *data_cb_value, gpgme_assuan_inquire_cb_t inq_cb, void *inq_cb_value, gpgme_assuan_status_cb_t status_cb, void *status_cb_value, gpgme_error_t *op_err) { gpgme_error_t err; if (!ctx) return gpg_error (GPG_ERR_INV_VALUE); err = vfs_start (ctx, 1, command, data_cb, data_cb_value, inq_cb, inq_cb_value, status_cb, status_cb_value); if (!err) - err = _gpgme_wait_one_ext (ctx, op_err); + err = _gpgme_sync_wait (ctx, NULL, op_err); return err; } /* The actual exported interface follows. */ /* The container is automatically uncreateed when the context is reset or destroyed. This is a synchronous convenience interface, which automatically returns an operation error if there is no transmission error. */ static gpgme_error_t _gpgme_op_vfs_create (gpgme_ctx_t ctx, gpgme_key_t recp[], const char *container_file, unsigned int flags, gpgme_error_t *op_err) { gpg_error_t err; char *cmd; char *container_file_esc = NULL; int i; (void)flags; /* We want to encourage people to check error values, so not getting them is discouraged here. Also makes our code easier. */ if (! op_err) return gpg_error (GPG_ERR_INV_VALUE); err = _gpgme_encode_percent_string (container_file, &container_file_esc, 0); if (err) return err; i = 0; while (!err && recp[i]) { if (!recp[i]->subkeys || !recp[i]->subkeys->fpr) { free (container_file_esc); return gpg_error (GPG_ERR_UNUSABLE_PUBKEY); } if (gpgrt_asprintf (&cmd, "RECIPIENT %s", recp[i]->subkeys->fpr) < 0) { err = gpg_error_from_syserror (); free (container_file_esc); return err; } err = gpgme_op_vfs_transact (ctx, cmd, NULL, NULL, NULL, NULL, NULL, NULL, op_err); gpgrt_free (cmd); if (err || *op_err) { free (container_file_esc); return err; } recp++; } if (gpgrt_asprintf (&cmd, "CREATE -- %s", container_file_esc) < 0) { err = gpg_error_from_syserror (); free (container_file_esc); return err; } free (container_file_esc); err = gpgme_op_vfs_transact (ctx, cmd, NULL, NULL, NULL, NULL, NULL, NULL, op_err); gpgrt_free (cmd); return err; } gpgme_error_t gpgme_op_vfs_create (gpgme_ctx_t ctx, gpgme_key_t recp[], const char *container_file, unsigned int flags, gpgme_error_t *op_err) { gpg_error_t err; TRACE_BEG (DEBUG_CTX, "gpgme_op_vfs_create", ctx, "container_file=%s, flags=0x%x, op_err=%p", container_file, flags, op_err); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); if (_gpgme_debug_trace () && recp) { int i = 0; while (recp[i]) { TRACE_LOG ("recipient[%i] = %p (%s)", i, recp[i], (recp[i]->subkeys && recp[i]->subkeys->fpr) ? recp[i]->subkeys->fpr : "invalid"); i++; } } err = _gpgme_op_vfs_create (ctx, recp, container_file, flags, op_err); return TRACE_ERR (err); } - diff --git a/src/vfs-mount.c b/src/vfs-mount.c index c6ee7c97..4ef12277 100644 --- a/src/vfs-mount.c +++ b/src/vfs-mount.c @@ -1,247 +1,246 @@ /* vfs-mount.c - vfs mount support in GPGME * Copyright (C) 2009 g10 Code GmbH * * This file is part of GPGME. * * GPGME is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * GPGME is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, see . * SPDX-License-Identifier: LGPL-2.1-or-later */ #if HAVE_CONFIG_H #include #endif #include #include "gpgme.h" #include "debug.h" #include "context.h" #include "ops.h" #include "util.h" typedef struct { struct _gpgme_op_vfs_mount_result result; } *op_data_t; gpgme_vfs_mount_result_t gpgme_op_vfs_mount_result (gpgme_ctx_t ctx) { gpgme_error_t err; void *hook; op_data_t opd; err = _gpgme_op_data_lookup (ctx, OPDATA_VFS_MOUNT, &hook, -1, NULL); opd = hook; /* Check in case this function is used without having run a command before. */ if (err || !opd) return NULL; return &opd->result; } static gpgme_error_t _gpgme_vfs_mount_status_handler (void *priv, const char *code, const 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_VFS_MOUNT, &hook, -1, NULL); opd = hook; if (err) return err; if (! strcasecmp ("MOUNTPOINT", code)) { if (opd->result.mount_dir) free (opd->result.mount_dir); opd->result.mount_dir = strdup (args); } return 0; } static gpgme_error_t vfs_start (gpgme_ctx_t ctx, int synchronous, const char *command, gpgme_assuan_data_cb_t data_cb, void *data_cb_value, gpgme_assuan_inquire_cb_t inq_cb, void *inq_cb_value, gpgme_assuan_status_cb_t status_cb, void *status_cb_value) { gpgme_error_t err; void *hook; op_data_t opd; if (!command || !*command) return gpg_error (GPG_ERR_INV_VALUE); /* The flag value 256 is used to suppress an engine reset. This is required to keep the connection running. */ err = _gpgme_op_reset (ctx, ((synchronous & 255) | 256)); if (err) return err; err = _gpgme_op_data_lookup (ctx, OPDATA_VFS_MOUNT, &hook, sizeof (*opd), NULL); opd = hook; if (err) return err; return _gpgme_engine_op_assuan_transact (ctx->engine, command, data_cb, data_cb_value, inq_cb, inq_cb_value, status_cb, status_cb_value); } #if 0 /* XXXX. This is the asynchronous variant. */ static gpgme_error_t gpgme_op_vfs_transact_start (gpgme_ctx_t ctx, const char *command, gpgme_assuan_data_cb_t data_cb, void *data_cb_value, gpgme_assuan_inquire_cb_t inq_cb, void *inq_cb_value, gpgme_assuan_status_cb_t status_cb, void *status_cb_value) { return vfs_start (ctx, 0, command, data_cb, data_cb_value, inq_cb, inq_cb_value, status_cb, status_cb_value); } #endif /* XXXX. This is the synchronous variant. */ static gpgme_error_t gpgme_op_vfs_transact (gpgme_ctx_t ctx, const char *command, gpgme_assuan_data_cb_t data_cb, void *data_cb_value, gpgme_assuan_inquire_cb_t inq_cb, void *inq_cb_value, gpgme_assuan_status_cb_t status_cb, void *status_cb_value, gpgme_error_t *op_err) { gpgme_error_t err; if (!ctx) return gpg_error (GPG_ERR_INV_VALUE); err = vfs_start (ctx, 1, command, data_cb, data_cb_value, inq_cb, inq_cb_value, status_cb, status_cb_value); if (!err) - err = _gpgme_wait_one_ext (ctx, op_err); + err = _gpgme_sync_wait (ctx, NULL, op_err); return err; } /* The actual exported interface follows. */ /* The container is automatically unmounted when the context is reset or destroyed. This is a synchronous convenience interface, which automatically returns an operation error if there is no transmission error. */ static gpgme_error_t _gpgme_op_vfs_mount (gpgme_ctx_t ctx, const char *container_file, const char *mount_dir, int flags, gpgme_error_t *op_err) { gpg_error_t err; char *cmd; char *container_file_esc = NULL; (void)flags; /* We want to encourage people to check error values, so not getting them is discouraged here. Also makes our code easier. */ if (! op_err) return gpg_error (GPG_ERR_INV_VALUE); err = _gpgme_encode_percent_string (container_file, &container_file_esc, 0); if (err) return err; if (gpgrt_asprintf (&cmd, "OPEN -- %s", container_file_esc) < 0) { err = gpg_error_from_syserror (); free (container_file_esc); return err; } free (container_file_esc); err = gpgme_op_vfs_transact (ctx, cmd, NULL, NULL, NULL, NULL, NULL, NULL, op_err); gpgrt_free (cmd); if (err || *op_err) return err; if (mount_dir) { char *mount_dir_esc = NULL; err = _gpgme_encode_percent_string (mount_dir, &mount_dir_esc, 0); if (err) return err; if (gpgrt_asprintf (&cmd, "MOUNT -- %s", mount_dir_esc) < 0) { err = gpg_error_from_syserror (); free (mount_dir_esc); return err; } free (mount_dir_esc); } else { if (gpgrt_asprintf (&cmd, "MOUNT") < 0) return gpg_error_from_syserror (); } err = gpgme_op_vfs_transact (ctx, cmd, NULL, NULL, NULL, NULL, _gpgme_vfs_mount_status_handler, ctx, op_err); gpgrt_free (cmd); return err; } gpgme_error_t gpgme_op_vfs_mount (gpgme_ctx_t ctx, const char *container_file, const char *mount_dir, unsigned int flags, gpgme_error_t *op_err) { gpg_error_t err; TRACE_BEG (DEBUG_CTX, "gpgme_op_vfs_mount", ctx, "container=%s, mount_dir=%s, flags=0x%x, op_err=%p", container_file, mount_dir, flags, op_err); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); err = _gpgme_op_vfs_mount (ctx, container_file, mount_dir, flags, op_err); return TRACE_ERR (err); } - diff --git a/src/wait.c b/src/wait.c index 7418f00d..b88b7813 100644 --- a/src/wait.c +++ b/src/wait.c @@ -1,477 +1,458 @@ /* wait.c * Copyright (C) 2000 Werner Koch (dd9jn) * Copyright (C) 2001, 2002, 2003, 2004, 2005, 2007 g10 Code GmbH * * This file is part of GPGME. * * GPGME is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * GPGME is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, see . * SPDX-License-Identifier: LGPL-2.1-or-later */ #if HAVE_CONFIG_H #include #endif #include #include #include #include #ifdef HAVE_SYS_TYPES_H # include #endif #include "util.h" #include "context.h" #include "ops.h" #include "wait.h" #include "sema.h" #include "priv-io.h" #include "engine.h" #include "debug.h" #include "fdtable.h" /* Wrapper for the user wait handler to match the exported prototype. * This is used by _gpgme_add_io_cb_user. */ static gpg_error_t user_io_cb_handler (void *data, int fd) { struct io_cb_tag_s *tag = data; gpg_error_t err; uint64_t serial; gpgme_ctx_t ctx; gpg_error_t op_err; (void)fd; assert (data); serial = tag->serial; assert (serial); err = _gpgme_fdtable_run_io_cbs (serial, &op_err); if (err || op_err) ; else if (!_gpgme_fdtable_io_cb_count (serial)) { /* No more active callbacks - emit a DONE. */ struct gpgme_io_event_done_data done_data = { 0, 0 }; _gpgme_get_ctx (serial, &ctx); if (ctx) _gpgme_engine_io_event (ctx->engine, GPGME_EVENT_DONE, &done_data); } return 0; } /* Register the file descriptor FD with the handler FNC (which gets FNC_DATA as its first argument) for the direction DIR. DATA should be the context for which the fd is added. R_TAG will hold the tag that can be used to remove the fd. This function is used for the global and the private wait loops. */ gpgme_error_t _gpgme_add_io_cb (void *data, int fd, int dir, gpgme_io_cb_t fnc, void *fnc_data, void **r_tag) { gpgme_error_t err; gpgme_ctx_t ctx = (gpgme_ctx_t) data; struct io_cb_tag_s *tag; TRACE_BEG (DEBUG_SYSIO, __func__, NULL, "ctx=%lu fd=%d, dir %d", CTXSERIAL (ctx), fd, dir); if (!fnc) return gpg_error (GPG_ERR_INV_ARG); assert (fnc); assert (ctx); tag = calloc (1, sizeof *tag); if (!tag) return gpg_error_from_syserror (); tag->serial = ctx->serial; tag->fd = fd; err = _gpgme_fdtable_set_io_cb (fd, ctx->serial, dir, fnc, fnc_data); if (err) { free (tag); return TRACE_ERR (err); } *r_tag = tag; TRACE_SUC ("tag=%p", tag); return 0; } /* Register the file descriptor FD with the handler FNC (which gets FNC_DATA as its first argument) for the direction DIR. DATA should be the context for which the fd is added. R_TAG will hold the tag that can be used to remove the fd. This function is used for the user wait loops. */ gpg_error_t _gpgme_add_io_cb_user (void *data, int fd, int dir, gpgme_io_cb_t fnc, void *fnc_data, void **r_tag) { gpgme_ctx_t ctx = (gpgme_ctx_t) data; struct io_cb_tag_s *tag; gpgme_error_t err; TRACE_BEG (DEBUG_SYSIO, __func__, NULL, "ctx=%lu fd=%d, dir %d", CTXSERIAL (ctx), fd, dir); assert (ctx); err = _gpgme_add_io_cb (data, fd, dir, fnc, fnc_data, r_tag); if (err) return TRACE_ERR (err); tag = *r_tag; assert (tag); err = ctx->user_io_cbs.add (ctx->user_io_cbs.add_priv, fd, dir, user_io_cb_handler, *r_tag, &tag->user_tag); if (err) _gpgme_remove_io_cb (*r_tag); return TRACE_ERR (err); } /* This function is used for the global and the private wait loops. */ void _gpgme_remove_io_cb (void *data) { struct io_cb_tag_s *tag = data; gpg_error_t err; assert (tag); err = _gpgme_fdtable_set_io_cb (tag->fd, tag->serial, 0, NULL, NULL); if (err) { TRACE (DEBUG_CTX, __func__, NULL, "tag=%p (ctx=%lu fd=%d) failed: %s", tag, tag->serial, tag->fd, gpg_strerror (err)); } else { TRACE (DEBUG_CTX, __func__, NULL, "tag=%p (ctx=%lu fd=%d) done", tag, tag->serial, tag->fd); } free (tag); } /* This function is used for the user wait loops. */ void _gpgme_remove_io_cb_user (void *data) { struct io_cb_tag_s *tag = data; gpgme_ctx_t ctx; assert (tag); _gpgme_get_ctx (tag->serial, &ctx); if (ctx) ctx->user_io_cbs.remove (tag->user_tag); _gpgme_remove_io_cb (data); } /* The internal I/O callback function used for the global event loop. That loop is used for all asynchronous operations (except key listing) for which no user I/O callbacks are specified. A context sets up its initial I/O callbacks and then sends the GPGME_EVENT_START event. After that, it is added to the global list of active contexts. The gpgme_wait function contains a select() loop over all file descriptors in all active contexts. If an error occurs, it closes all fds in that context and moves the context to the global done list. Likewise, if a context has removed all I/O callbacks, it is moved to the global done list. All contexts in the global done list are eligible for being returned by gpgme_wait if requested by the caller. */ void _gpgme_wait_global_event_cb (void *data, gpgme_event_io_t type, void *type_data) { gpgme_ctx_t ctx = (gpgme_ctx_t) data; gpg_error_t err; assert (ctx); switch (type) { case GPGME_EVENT_START: { err = _gpgme_fdtable_set_active (ctx->serial); if (err) /* An error occurred. Close all fds in this context, and send the error in a done event. */ _gpgme_cancel_with_err (ctx->serial, err, 0); } break; case GPGME_EVENT_DONE: { gpgme_io_event_done_data_t done_data = (gpgme_io_event_done_data_t) type_data; _gpgme_fdtable_set_done (ctx->serial, done_data->err, done_data->op_err); } break; case GPGME_EVENT_NEXT_KEY: assert (!"Unexpected event GPGME_EVENT_NEXT_KEY"); break; case GPGME_EVENT_NEXT_TRUSTITEM: assert (!"Unexpected event GPGME_EVENT_NEXT_TRUSTITEM"); break; default: assert (!"Unexpected event"); break; } } /* The internal I/O callback function used for private event loops. * The private event loops are used for all blocking operations, and * for the key and trust item listing operations. They are completely * separated from each other. */ void _gpgme_wait_private_event_cb (void *data, gpgme_event_io_t type, void *type_data) { switch (type) { case GPGME_EVENT_START: /* Nothing to do here, as the wait routine is called after the initialization is finished. */ break; case GPGME_EVENT_DONE: break; case GPGME_EVENT_NEXT_KEY: _gpgme_op_keylist_event_cb (data, type, type_data); break; case GPGME_EVENT_NEXT_TRUSTITEM: _gpgme_op_trustlist_event_cb (data, type, type_data); break; } } /* The internal I/O callback function used for user event loops. User * event loops are used for all asynchronous operations for which a * user callback is defined. */ void _gpgme_wait_user_event_cb (void *data, gpgme_event_io_t type, void *type_data) { gpgme_ctx_t ctx = data; if (ctx->user_io_cbs.event) ctx->user_io_cbs.event (ctx->user_io_cbs.event_priv, type, type_data); } /* Perform asynchronous operations in the global event loop (ie, any asynchronous operation except key listing and trustitem listing operations). If CTX is not a null pointer, the function will return if the asynchronous operation in the context CTX finished. Otherwise the function will return if any asynchronous operation finished. If HANG is zero, the function will not block for a long time. Otherwise the function does not return until an operation matching CTX finished. If a matching context finished, it is returned, and *STATUS is set to the error value of the operation in that context. Otherwise, if the timeout expires, NULL is returned and *STATUS is 0. If an error occurs, NULL is returned and *STATUS is set to the error value. */ gpgme_ctx_t gpgme_wait_ext (gpgme_ctx_t ctx, gpgme_error_t *status, gpgme_error_t *op_err, int hang) { gpg_error_t err; io_select_t fds = NULL; unsigned int nfds; int nr; uint64_t serial; do { /* Get all fds of CTX (or all if CTX is NULL) we want to wait * for and which are in the active state. */ free (fds); nfds = _gpgme_fdtable_get_fds (&fds, ctx? ctx->serial : 0, ( FDTABLE_FLAG_ACTIVE | FDTABLE_FLAG_CLEAR)); if (!nfds) { err = gpg_error_from_syserror (); if (gpg_err_code (err) != GPG_ERR_MISSING_ERRNO) { if (status) *status = err; if (op_err) *op_err = 0; free (fds); return NULL; } /* Nothing to select. Run the select anyway, so that we use * its timeout. */ } nr = _gpgme_io_select (fds, nfds, 0); if (nr < 0) { if (status) *status = gpg_error_from_syserror (); if (op_err) *op_err = 0; free (fds); return NULL; } _gpgme_fdtable_set_signaled (fds, nfds); _gpgme_fdtable_run_io_cbs (ctx? ctx->serial : 0, NULL); serial = _gpgme_fdtable_get_done (ctx? ctx->serial : 0, status, op_err); if (serial) { _gpgme_get_ctx (serial, &ctx); hang = 0; } else if (!hang) { ctx = NULL; if (status) *status = 0; if (op_err) *op_err = 0; } } while (hang); free (fds); return ctx; } gpgme_ctx_t gpgme_wait (gpgme_ctx_t ctx, gpgme_error_t *status, int hang) { return gpgme_wait_ext (ctx, status, NULL, hang); } -/* If COND is a null pointer, wait until the blocking operation in CTX - finished and return its error value. Otherwise, wait until COND is - satisfied or the operation finished. */ +/* Wait until the blocking operation in context CTX has finished and + * return the error value. If COND is not NULL return early if COND + * is satisfied. A session based error will be returned at R_OP_ERR + * if it is not NULL. */ gpgme_error_t -_gpgme_wait_on_condition (gpgme_ctx_t ctx, volatile int *cond, - gpgme_error_t *r_op_err) +_gpgme_sync_wait (gpgme_ctx_t ctx, volatile int *cond, gpg_error_t *r_op_err) { gpgme_error_t err = 0; int hang = 1; io_select_t fds = NULL; unsigned int nfds; - int op_err; int nr; + TRACE_BEG (DEBUG_SYSIO, __func__, NULL, "ctx=%lu", CTXSERIAL (ctx)); + if (r_op_err) *r_op_err = 0; if (!ctx) - return gpg_error (GPG_ERR_INV_VALUE); + { + err = gpg_error (GPG_ERR_INV_VALUE); + goto leave; + } do { /* Get all fds of CTX we want to wait for. */ free (fds); nfds = _gpgme_fdtable_get_fds (&fds, ctx->serial, FDTABLE_FLAG_CLEAR); if (!nfds) { err = gpg_error_from_syserror (); if (gpg_err_code (err) != GPG_ERR_MISSING_ERRNO) + goto leave; + } + else + { + nr = _gpgme_io_select (fds, nfds, 0); + if (nr < 0) { - free (fds); - return err; + /* An error occurred. Close all fds in this context, and + signal it. */ + err = gpg_error_from_syserror (); + _gpgme_cancel_with_err (ctx->serial, err, 0); + goto leave; } - /* Nothing to select. Run the select anyway, so that we use - * its timeout. */ - } + _gpgme_fdtable_set_signaled (fds, nfds); - nr = _gpgme_io_select (fds, nfds, 0); - if (nr < 0) - { - /* An error occurred. Close all fds in this context, and - signal it. */ - err = gpg_error_from_syserror (); - _gpgme_cancel_with_err (ctx->serial, err, 0); - free (fds); - return err; - } - _gpgme_fdtable_set_signaled (fds, nfds); - - err = _gpgme_fdtable_run_io_cbs (ctx->serial, r_op_err); - if (err || (r_op_err && *r_op_err)) - { - free (fds); - return err; + err = _gpgme_fdtable_run_io_cbs (ctx->serial, r_op_err); + if (err || (r_op_err && *r_op_err)) + goto leave; } if (!_gpgme_fdtable_io_cb_count (ctx->serial)) { + /* No more matching fds with IO callbacks. */ struct gpgme_io_event_done_data data = {0, 0}; _gpgme_engine_io_event (ctx->engine, GPGME_EVENT_DONE, &data); hang = 0; } if (cond && *cond) hang = 0; } while (hang); + err = 0; + leave: free (fds); - return 0; -} - - -/* Wait until the blocking operation in context CTX has finished and - return the error value. This variant can not be used for - session-based protocols. */ -gpgme_error_t -_gpgme_wait_one (gpgme_ctx_t ctx) -{ - return _gpgme_wait_on_condition (ctx, NULL, NULL); -} - -/* Wait until the blocking operation in context CTX has finished and - return the error value. This is the right variant to use for - sesion-based protocols. */ -gpgme_error_t -_gpgme_wait_one_ext (gpgme_ctx_t ctx, gpgme_error_t *op_err) -{ - return _gpgme_wait_on_condition (ctx, NULL, op_err); + return TRACE_ERR (err); }