From a1f76b3b54b75a150fe272b804d85ffd40a507a6 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Tue, 17 Apr 2018 08:33:44 +0200 Subject: [PATCH] core: Add extended versions of the encrypt functions. * src/gpgme.h.in (gpgme_op_encrypt_ext_start) New. (gpgme_op_encrypt_ext): New. (gpgme_op_encrypt_sign_ext_start): New. (gpgme_op_encrypt_sign_ext): New. * src/libgpgme.vers, tests/run-encrypt.c: Add them. * src/encrypt.c (encrypt_start): Add arg recpstring. (gpgme_op_encrypt): Factor code out to ... (gpgme_op_encrypt_ext): new function with new arg recpstring. (gpgme_op_encrypt_start): Factor code out to ... (gpgme_op_encrypt_ext_start): new function with new arg recpstring. * src/encrypt-sign.c (encrypt_sign_start): Add arg recpstring. (gpgme_op_encrypt_sign): Factor code out to ... (gpgme_op_encrypt_sign_ext): new function with new arg recpstring. (gpgme_op_encrypt_sign_start): Factor code out to ... (gpgme_op_encrypt_sign_ext_start): new function with new arg recpstring. * src/engine-backend.h (struct engine_ops): Change fields encrypt and encrypt_sign. * src/engine.c (_gpgme_engine_op_encrypt): Add arg recpstring and pass to engine. (_gpgme_engine_op_encrypt_sign): Ditto. * src/engine-gpg.c (append_args_from_recipients_string): New. (gpg_encrypt): Add arg recpstring and call new function as needed. (gpg_encrypt_sign): Ditto. * src/engine-gpgsm.c (set_recipients_from_string): New. (gpgsm_encrypt): Add arg recpstring and call new function as needed. * src/engine-uiserver.c (set_recipients_from_string): New. (uiserver_encrypt): Add arg recpstring and call new function as needed. * tests/run-encrypt.c (xstrdup): New. (main): Add option --keystring. * src/gpgme-json.c (get_keys): Simplify. (op_encrypt): Modify to make use of the extended encrypt function. -- This new feature can be used to avoid the need for a key lookup and thus several extra calls to the backend. Note that run-test uses a semicolon as delimiter because that make testing the feature on the command line much easier. Signed-off-by: Werner Koch --- NEWS | 11 ++-- doc/gpgme.texi | 77 +++++++++++++++++++++++++++ src/encrypt-sign.c | 121 ++++++++++++++++++++++++++++-------------- src/encrypt.c | 120 +++++++++++++++++++++++++++-------------- src/engine-backend.h | 2 + src/engine-gpg.c | 70 ++++++++++++++++++++---- src/engine-gpgsm.c | 58 ++++++++++++++++++-- src/engine-uiserver.c | 59 ++++++++++++++++++-- src/engine.c | 10 ++-- src/engine.h | 2 + src/gpgme-json.c | 74 +++++++++++++------------- src/gpgme.def | 5 ++ src/gpgme.h.in | 30 +++++++++-- src/libgpgme.vers | 11 ++-- tests/run-encrypt.c | 40 ++++++++++---- 15 files changed, 532 insertions(+), 158 deletions(-) diff --git a/NEWS b/NEWS index a8d73a50..51d79239 100644 --- a/NEWS +++ b/NEWS @@ -5,11 +5,16 @@ Noteworthy changes in version 1.10.1 (unreleased) * Interface changes relative to the 1.10.0 release: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - gpgme_import_result_t EXTENDED: New field 'skipped_v3_keys' - cpp: Key::locate NEW. - cpp: Data::toString NEW. + gpgme_op_encrypt_ext NEW. + gpgme_op_encrypt_ext_start NEW. + gpgme_op_encrypt_sign_ext NEW. + gpgme_op_encrypt_sign_ext_start NEW. + gpgme_import_result_t EXTENDED: New field 'skipped_v3_keys'. + cpp: Key::locate NEW. + cpp: Data::toString NEW. cpp: ImportResult::numV3KeysSkipped NEW. + Noteworthy changes in version 1.10.0 (2017-12-12) ------------------------------------------------- diff --git a/doc/gpgme.texi b/doc/gpgme.texi index cbb0e649..1df9c46e 100644 --- a/doc/gpgme.texi +++ b/doc/gpgme.texi @@ -6154,6 +6154,45 @@ pointer, and @code{GPG_ERR_UNUSABLE_PUBKEY} if @var{rset} does not contain any valid recipients. @end deftypefun +@deftypefun gpgme_error_t gpgme_op_encrypt_ext @ + (@w{gpgme_ctx_t @var{ctx}}, @ + @w{gpgme_key_t @var{recp}[]}, @ + @w{const char *@var{recpstring}}, @ + @w{gpgme_encrypt_flags_t @var{flags}}, @ + @w{gpgme_data_t @var{plain}}, @w{gpgme_data_t @var{cipher}}) + +@since{1.11.0} + +This is an extended version of @code{gpgme_op_encrypt} with +@var{recpstring} as additional parameter. If @var{recp} is NULL and +@var{recpstring} is not NULL, the latter is expected to be a linefeed +delimited string with the set of key specifications. In contrast to +@var{recp} the keys are given directly as strings and there is no need +to first create key objects. The keys are passed verbatim to the +backend engine. + +@end deftypefun + + +@deftypefun gpgme_error_t gpgme_op_encrypt_ext_start @ + (@w{gpgme_ctx_t @var{ctx}}, @ + @w{gpgme_key_t @var{recp}[]}, @ + @w{const char *@var{recpstring}}, @ + @w{gpgme_encrypt_flags_t @var{flags}}, @ + @w{gpgme_data_t @var{plain}}, @w{gpgme_data_t @var{cipher}}) + +@since{1.11.0} + +This is an extended version of @code{gpgme_op_encrypt_start} with +@var{recpstring} as additional parameter. If @var{recp} is NULL and +@var{recpstring} is not NULL, the latter is expected to be a linefeed +delimited string with the set of key specifications. In contrast to +@var{recp} the keys are given directly as strings and there is no need +to first create key objects. The keys are passed verbatim to the +backend engine. + +@end deftypefun + @deftp {Data type} {gpgme_encrypt_result_t} This is a pointer to a structure used to store the result of a @code{gpgme_op_encrypt} operation. After successfully encrypting @@ -6203,6 +6242,44 @@ if @var{ctx}, @var{rset}, @var{plain} or @var{cipher} is not a valid pointer. @end deftypefun +@deftypefun gpgme_error_t gpgme_op_encrypt_sign_ext @ + (@w{gpgme_ctx_t @var{ctx}}, @ + @w{gpgme_key_t @var{recp}[]}, @ + @w{const char *@var{recpstring}}, @ + @w{gpgme_encrypt_flags_t @var{flags}}, @ + @w{gpgme_data_t @var{plain}}, @w{gpgme_data_t @var{cipher}}) + +@since{1.11.0} + +This is an extended version of @code{gpgme_op_encrypt_sign} with +@var{recpstring} as additional parameter. If @var{recp} is NULL and +@var{recpstring} is not NULL, the latter is expected to be a linefeed +delimited string with the set of key specifications. In contrast to +@var{recp} the keys are given directly as strings and there is no need +to first create the key objects. The keys are passed verbatim to the +backend engine. + +@end deftypefun + +@deftypefun gpgme_error_t gpgme_op_encrypt_sign_ext_start @ + (@w{gpgme_ctx_t @var{ctx}}, @ + @w{gpgme_key_t @var{recp}[]}, @ + @w{const char *@var{recpstring}}, @ + @w{gpgme_encrypt_flags_t @var{flags}}, @ + @w{gpgme_data_t @var{plain}}, @w{gpgme_data_t @var{cipher}}) + +@since{1.11.0} + +This is an extended version of @code{gpgme_op_encrypt_sign_start} with +@var{recpstring} as additional parameter. If @var{recp} is NULL and +@var{recpstring} is not NULL, the latter is expected to be a linefeed +delimited string with the set of key specifications. In contrast to +@var{recp} the keys are given directly as strings and there is no need +to first create the key objects. The keys are passed verbatim to the +backend engine. + +@end deftypefun + @node Miscellaneous @section Miscellaneous operations diff --git a/src/encrypt-sign.c b/src/encrypt-sign.c index af6de63e..4db46e25 100644 --- a/src/encrypt-sign.c +++ b/src/encrypt-sign.c @@ -62,6 +62,7 @@ encrypt_sym_status_handler (void *priv, gpgme_status_code_t code, char *args) 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) { @@ -72,7 +73,7 @@ encrypt_sign_start (gpgme_ctx_t ctx, int synchronous, gpgme_key_t recp[], if (err) return err; - symmetric = !recp || (flags & GPGME_ENCRYPT_SYMMETRIC); + symmetric = (!recp && !recpstring) || (flags & GPGME_ENCRYPT_SYMMETRIC); if (!plain) return gpg_error (GPG_ERR_NO_DATA); @@ -103,53 +104,42 @@ encrypt_sign_start (gpgme_ctx_t ctx, int synchronous, gpgme_key_t recp[], : encrypt_sign_status_handler, ctx); - return _gpgme_engine_op_encrypt_sign (ctx->engine, recp, flags, plain, + return _gpgme_engine_op_encrypt_sign (ctx->engine, recp, recpstring, + flags, plain, cipher, ctx->use_armor, ctx /* FIXME */); } -/* 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. */ +/* 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) { - gpgme_error_t err; - - TRACE_BEG3 (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) - { - int i = 0; - - while (recp[i]) - { - TRACE_LOG3 ("recipient[%i] = %p (%s)", i, recp[i], - (recp[i]->subkeys && recp[i]->subkeys->fpr) ? - recp[i]->subkeys->fpr : "invalid"); - i++; - } - } - - err = encrypt_sign_start (ctx, 0, recp, flags, plain, cipher); - return err; + return gpgme_op_encrypt_sign_ext_start (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. */ +/* 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; @@ -159,21 +149,70 @@ gpgme_op_encrypt_sign (gpgme_ctx_t ctx, gpgme_key_t recp[], if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); - if (_gpgme_debug_trace () && recp) + if (_gpgme_debug_trace () && (recp || recpstring)) { - int i = 0; + if (recp) + { + int i = 0; - while (recp[i]) - { - TRACE_LOG3 ("recipient[%i] = %p (%s)", i, recp[i], - (recp[i]->subkeys && recp[i]->subkeys->fpr) ? - recp[i]->subkeys->fpr : "invalid"); - i++; - } + while (recp[i]) + { + TRACE_LOG3 ("recipient[%i] = %p (%s)", i, recp[i], + (recp[i]->subkeys && recp[i]->subkeys->fpr) ? + recp[i]->subkeys->fpr : "invalid"); + i++; + } + } + else + { + TRACE_LOG1 ("recipients = '%s'", recpstring); + } } - err = encrypt_sign_start (ctx, 1, recp, flags, plain, cipher); + err = encrypt_sign_start (ctx, 1, recp, recpstring, flags, plain, cipher); if (!err) err = _gpgme_wait_one (ctx); 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_BEG3 (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_LOG3 ("recipient[%i] = %p (%s)", i, recp[i], + (recp[i]->subkeys && recp[i]->subkeys->fpr) ? + recp[i]->subkeys->fpr : "invalid"); + i++; + } + } + else + { + TRACE_LOG1 ("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 40236544..2318497e 100644 --- a/src/encrypt.c +++ b/src/encrypt.c @@ -214,6 +214,7 @@ _gpgme_op_encrypt_init_result (gpgme_ctx_t ctx) 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) { @@ -228,13 +229,13 @@ encrypt_start (gpgme_ctx_t ctx, int synchronous, gpgme_key_t recp[], if (err) return err; - symmetric = !recp || (flags & GPGME_ENCRYPT_SYMMETRIC); + 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) + if (recp && !*recp) return gpg_error (GPG_ERR_INV_VALUE); if (symmetric && ctx->passphrase_cb) @@ -252,48 +253,41 @@ encrypt_start (gpgme_ctx_t ctx, int synchronous, gpgme_key_t recp[], : encrypt_status_handler, ctx); - return _gpgme_engine_op_encrypt (ctx->engine, recp, flags, plain, cipher, - ctx->use_armor); + 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) { - gpgme_error_t err; - - TRACE_BEG3 (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) - { - int i = 0; - - while (recp[i]) - { - TRACE_LOG3 ("recipient[%i] = %p (%s)", i, recp[i], - (recp[i]->subkeys && recp[i]->subkeys->fpr) ? - recp[i]->subkeys->fpr : "invalid"); - i++; - } - } - - err = encrypt_start (ctx, 0, recp, flags, plain, cipher); - return TRACE_ERR (err); + 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. */ + * 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 (gpgme_ctx_t ctx, gpgme_key_t recp[], - gpgme_encrypt_flags_t flags, - gpgme_data_t plain, gpgme_data_t cipher) +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; @@ -303,21 +297,67 @@ gpgme_op_encrypt (gpgme_ctx_t ctx, gpgme_key_t recp[], if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); - if (_gpgme_debug_trace () && recp) + if (_gpgme_debug_trace () && (recp || recpstring)) { - int i = 0; + if (recp) + { + int i = 0; - while (recp[i]) - { - TRACE_LOG3 ("recipient[%i] = %p (%s)", i, recp[i], + while (recp[i]) + { + TRACE_LOG3 ("recipient[%i] = %p (%s)", i, recp[i], (recp[i]->subkeys && recp[i]->subkeys->fpr) ? - recp[i]->subkeys->fpr : "invalid"); - i++; - } + recp[i]->subkeys->fpr : "invalid"); + i++; + } + } + else + { + TRACE_LOG1 ("recipients = '%s'", recpstring); + } } - err = encrypt_start (ctx, 1, recp, flags, plain, cipher); + err = encrypt_start (ctx, 1, recp, recpstring, flags, plain, cipher); if (!err) err = _gpgme_wait_one (ctx); 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_BEG3 (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_LOG3 ("recipient[%i] = %p (%s)", i, recp[i], + (recp[i]->subkeys && recp[i]->subkeys->fpr) ? + recp[i]->subkeys->fpr : "invalid"); + i++; + } + } + else + { + TRACE_LOG1 ("recipients = '%s'", recpstring); + } + } + + err = encrypt_start (ctx, 0, recp, recpstring, flags, plain, cipher); + return TRACE_ERR (err); +} diff --git a/src/engine-backend.h b/src/engine-backend.h index 97cf6a10..f6926662 100644 --- a/src/engine-backend.h +++ b/src/engine-backend.h @@ -72,10 +72,12 @@ struct engine_ops gpgme_error_t (*edit) (void *engine, int type, gpgme_key_t key, gpgme_data_t out, gpgme_ctx_t ctx /* FIXME */); gpgme_error_t (*encrypt) (void *engine, gpgme_key_t recp[], + const char *recpstring, gpgme_encrypt_flags_t flags, gpgme_data_t plain, gpgme_data_t ciph, int use_armor); gpgme_error_t (*encrypt_sign) (void *engine, gpgme_key_t recp[], + const char *recpstring, gpgme_encrypt_flags_t flags, gpgme_data_t plain, gpgme_data_t ciph, int use_armor, gpgme_ctx_t ctx /* FIXME */); diff --git a/src/engine-gpg.c b/src/engine-gpg.c index a37d1f7b..809806c4 100644 --- a/src/engine-gpg.c +++ b/src/engine-gpg.c @@ -1914,17 +1914,64 @@ append_args_from_recipients (engine_gpg_t gpg, gpgme_key_t recp[]) } +/* Take recipients from the LF delimited STRING and add -r args. */ +static gpg_error_t +append_args_from_recipients_string (engine_gpg_t gpg, const char *string) +{ + gpg_error_t err = 0; + int any = 0; + const char *s; + int n; + + do + { + /* Skip leading white space */ + while (*string == ' ' || *string == '\t') + string++; + if (!*string) + break; + + /* Look for the LF. */ + s = strchr (string, '\n'); + if (s) + n = s - string; + else + n = strlen (string); + while (n && (string[n-1] == ' ' || string[n-1] == '\t')) + n--; + + /* Add arg if it is not empty. */ + if (n) + { + err = add_arg (gpg, "-r"); + if (!err) + err = add_arg_len (gpg, NULL, string, n); + if (!err) + any = 1; + } + + string += n + !!s; + } + while (!err); + + if (!err && !any) + err = gpg_error (GPG_ERR_MISSING_KEY); + return err; +} + + static gpgme_error_t -gpg_encrypt (void *engine, gpgme_key_t recp[], gpgme_encrypt_flags_t flags, +gpg_encrypt (void *engine, gpgme_key_t recp[], const char *recpstring, + gpgme_encrypt_flags_t flags, gpgme_data_t plain, gpgme_data_t ciph, int use_armor) { engine_gpg_t gpg = engine; gpgme_error_t err = 0; - if (recp) + if (recp || recpstring) err = add_arg (gpg, "--encrypt"); - if (!err && ((flags & GPGME_ENCRYPT_SYMMETRIC) || !recp)) + if (!err && ((flags & GPGME_ENCRYPT_SYMMETRIC) || (!recp && !recpstring))) err = add_arg (gpg, "--symmetric"); if (!err && use_armor) @@ -1951,7 +1998,7 @@ gpg_encrypt (void *engine, gpgme_key_t recp[], gpgme_encrypt_flags_t flags, && have_gpg_version (gpg, "2.1.14")) err = add_arg (gpg, "--mimemode"); - if (recp) + if (recp || recpstring) { /* If we know that all recipients are valid (full or ultimate trust) we can suppress further checks. */ @@ -1961,7 +2008,9 @@ gpg_encrypt (void *engine, gpgme_key_t recp[], gpgme_encrypt_flags_t flags, if (!err && (flags & GPGME_ENCRYPT_NO_ENCRYPT_TO)) err = add_arg (gpg, "--no-encrypt-to"); - if (!err) + if (!err && !recp && recpstring) + err = append_args_from_recipients_string (gpg, recpstring); + else if (!err) err = append_args_from_recipients (gpg, recp); } @@ -1995,6 +2044,7 @@ gpg_encrypt (void *engine, gpgme_key_t recp[], gpgme_encrypt_flags_t flags, static gpgme_error_t gpg_encrypt_sign (void *engine, gpgme_key_t recp[], + const char *recpstring, gpgme_encrypt_flags_t flags, gpgme_data_t plain, gpgme_data_t ciph, int use_armor, gpgme_ctx_t ctx /* FIXME */) @@ -2002,10 +2052,10 @@ gpg_encrypt_sign (void *engine, gpgme_key_t recp[], engine_gpg_t gpg = engine; gpgme_error_t err = 0; - if (recp) + if (recp || recpstring) err = add_arg (gpg, "--encrypt"); - if (!err && ((flags & GPGME_ENCRYPT_SYMMETRIC) || !recp)) + if (!err && ((flags & GPGME_ENCRYPT_SYMMETRIC) || (!recp && !recpstring))) err = add_arg (gpg, "--symmetric"); if (!err) @@ -2023,7 +2073,7 @@ gpg_encrypt_sign (void *engine, gpgme_key_t recp[], && have_gpg_version (gpg, "2.1.14")) err = add_arg (gpg, "--mimemode"); - if (recp) + if (recp || recpstring) { /* If we know that all recipients are valid (full or ultimate trust) we can suppress further checks. */ @@ -2033,7 +2083,9 @@ gpg_encrypt_sign (void *engine, gpgme_key_t recp[], if (!err && (flags & GPGME_ENCRYPT_NO_ENCRYPT_TO)) err = add_arg (gpg, "--no-encrypt-to"); - if (!err) + if (!err && !recp && recpstring) + err = append_args_from_recipients_string (gpg, recpstring); + else if (!err) err = append_args_from_recipients (gpg, recp); } diff --git a/src/engine-gpgsm.c b/src/engine-gpgsm.c index b8e44e7c..da7e524f 100644 --- a/src/engine-gpgsm.c +++ b/src/engine-gpgsm.c @@ -1327,8 +1327,57 @@ set_recipients (engine_gpgsm_t gpgsm, gpgme_key_t recp[]) } +/* Take recipients from the LF delimited STRING and send RECIPIENT + * commands to gpgsm. */ static gpgme_error_t -gpgsm_encrypt (void *engine, gpgme_key_t recp[], gpgme_encrypt_flags_t flags, +set_recipients_from_string (engine_gpgsm_t gpgsm, const char *string) +{ + gpg_error_t err = 0; + char *line = NULL; + int no_pubkey = 0; + const char *s; + int n; + + for (;;) + { + while (*string == ' ' || *string == '\t') + string++; + if (!*string) + break; + + s = strchr (string, '\n'); + if (s) + n = s - string; + else + n = strlen (string); + while (n && (string[n-1] == ' ' || string[n-1] == '\t')) + n--; + + gpgrt_free (line); + if (gpgrt_asprintf (&line, "RECIPIENT %.*s", n, string) < 0) + { + err = gpg_error_from_syserror (); + break; + } + string += n + !!s; + + err = gpgsm_assuan_simple_command (gpgsm, line, gpgsm->status.fnc, + gpgsm->status.fnc_value); + + /* Fixme: Improve error reporting. */ + if (gpg_err_code (err) == GPG_ERR_NO_PUBKEY) + no_pubkey++; + else if (err) + break; + } + gpgrt_free (line); + return err? err : no_pubkey? gpg_error (GPG_ERR_NO_PUBKEY) : 0; +} + + +static gpgme_error_t +gpgsm_encrypt (void *engine, gpgme_key_t recp[], const char *recpstring, + gpgme_encrypt_flags_t flags, gpgme_data_t plain, gpgme_data_t ciph, int use_armor) { engine_gpgsm_t gpgsm = engine; @@ -1339,7 +1388,7 @@ gpgsm_encrypt (void *engine, gpgme_key_t recp[], gpgme_encrypt_flags_t flags, if (!recp) return gpg_error (GPG_ERR_NOT_IMPLEMENTED); - if (flags & GPGME_ENCRYPT_NO_ENCRYPT_TO) + if ((flags & GPGME_ENCRYPT_NO_ENCRYPT_TO)) { err = gpgsm_assuan_simple_command (gpgsm, "OPTION no-encrypt-to", NULL, NULL); @@ -1359,7 +1408,10 @@ gpgsm_encrypt (void *engine, gpgme_key_t recp[], gpgme_encrypt_flags_t flags, gpgsm_clear_fd (gpgsm, MESSAGE_FD); gpgsm->inline_data = NULL; - err = set_recipients (gpgsm, recp); + if (!recp && recpstring) + err = set_recipients_from_string (gpgsm, recpstring); + else + err = set_recipients (gpgsm, recp); if (!err) err = start (gpgsm, "ENCRYPT"); diff --git a/src/engine-uiserver.c b/src/engine-uiserver.c index fd5ac174..d8f4fce3 100644 --- a/src/engine-uiserver.c +++ b/src/engine-uiserver.c @@ -1075,8 +1075,58 @@ set_recipients (engine_uiserver_t uiserver, gpgme_key_t recp[]) } +/* Take recipients from the LF delimited STRING and send RECIPIENT + * commands to gpgsm. */ static gpgme_error_t -uiserver_encrypt (void *engine, gpgme_key_t recp[], gpgme_encrypt_flags_t flags, +set_recipients_from_string (engine_uiserver_t uiserver, const char *string) +{ + gpg_error_t err = 0; + char *line = NULL; + int no_pubkey = 0; + const char *s; + int n; + + for (;;) + { + while (*string == ' ' || *string == '\t') + string++; + if (!*string) + break; + + s = strchr (string, '\n'); + if (s) + n = s - string; + else + n = strlen (string); + while (n && (string[n-1] == ' ' || string[n-1] == '\t')) + n--; + + gpgrt_free (line); + if (gpgrt_asprintf (&line, "RECIPIENT %.*s", n, string) < 0) + { + err = gpg_error_from_syserror (); + break; + } + string += n + !!s; + + err = uiserver_assuan_simple_command (uiserver, line, + uiserver->status.fnc, + uiserver->status.fnc_value); + + /* Fixme: Improve error reporting. */ + if (gpg_err_code (err) == GPG_ERR_NO_PUBKEY) + no_pubkey++; + else if (err) + break; + } + gpgrt_free (line); + return err? err : no_pubkey? gpg_error (GPG_ERR_NO_PUBKEY) : 0; +} + + +static gpgme_error_t +uiserver_encrypt (void *engine, gpgme_key_t recp[], const char *recpstring, + gpgme_encrypt_flags_t flags, gpgme_data_t plain, gpgme_data_t ciph, int use_armor) { engine_uiserver_t uiserver = engine; @@ -1140,9 +1190,12 @@ uiserver_encrypt (void *engine, gpgme_key_t recp[], gpgme_encrypt_flags_t flags, uiserver->inline_data = NULL; - if (recp) + if (recp || recpstring) { - err = set_recipients (uiserver, recp); + if (recp) + err = set_recipients (uiserver, recp); + else + err = set_recipients_from_string (uiserver, recpstring); if (err) { gpgrt_free (cmd); diff --git a/src/engine.c b/src/engine.c index e51384f0..b716ca24 100644 --- a/src/engine.c +++ b/src/engine.c @@ -721,6 +721,7 @@ _gpgme_engine_op_edit (engine_t engine, int type, gpgme_key_t key, gpgme_error_t _gpgme_engine_op_encrypt (engine_t engine, gpgme_key_t recp[], + const char *recpstring, gpgme_encrypt_flags_t flags, gpgme_data_t plain, gpgme_data_t ciph, int use_armor) { @@ -730,13 +731,14 @@ _gpgme_engine_op_encrypt (engine_t engine, gpgme_key_t recp[], if (!engine->ops->encrypt) return gpg_error (GPG_ERR_NOT_IMPLEMENTED); - return (*engine->ops->encrypt) (engine->engine, recp, flags, plain, ciph, - use_armor); + return (*engine->ops->encrypt) (engine->engine, recp, recpstring, + flags, plain, ciph, use_armor); } gpgme_error_t _gpgme_engine_op_encrypt_sign (engine_t engine, gpgme_key_t recp[], + const char *recpstring, gpgme_encrypt_flags_t flags, gpgme_data_t plain, gpgme_data_t ciph, int use_armor, gpgme_ctx_t ctx /* FIXME */) @@ -747,8 +749,8 @@ _gpgme_engine_op_encrypt_sign (engine_t engine, gpgme_key_t recp[], if (!engine->ops->encrypt_sign) return gpg_error (GPG_ERR_NOT_IMPLEMENTED); - return (*engine->ops->encrypt_sign) (engine->engine, recp, flags, - plain, ciph, use_armor, ctx); + return (*engine->ops->encrypt_sign) (engine->engine, recp, recpstring, + flags, plain, ciph, use_armor, ctx); } diff --git a/src/engine.h b/src/engine.h index 34bf8e90..8b692f2e 100644 --- a/src/engine.h +++ b/src/engine.h @@ -98,11 +98,13 @@ gpgme_error_t _gpgme_engine_op_edit (engine_t engine, int type, gpgme_ctx_t ctx /* FIXME */); gpgme_error_t _gpgme_engine_op_encrypt (engine_t engine, gpgme_key_t recp[], + const char *recpstring, gpgme_encrypt_flags_t flags, gpgme_data_t plain, gpgme_data_t ciph, int use_armor); gpgme_error_t _gpgme_engine_op_encrypt_sign (engine_t engine, gpgme_key_t recp[], + const char *recpstring, gpgme_encrypt_flags_t flags, gpgme_data_t plain, gpgme_data_t ciph, diff --git a/src/gpgme-json.c b/src/gpgme-json.c index b54d9a8a..4b1cd5b8 100644 --- a/src/gpgme-json.c +++ b/src/gpgme-json.c @@ -354,18 +354,19 @@ get_protocol (cjson_t json, gpgme_protocol_t *r_protocol) } -/* Extract the keys from the KEYS array in the JSON object. CTX is a - * GPGME context object. On success an array with the keys is stored - * at R_KEYS. In failure an error code is returned. */ +/* Extract the keys from the "keys" array in the JSON object. On + * success a string with the keys identifiers is stored at R_KEYS. + * The keys in that string are LF delimited. On failure an error code + * is returned. */ static gpg_error_t -get_keys (gpgme_ctx_t ctx, cjson_t json, gpgme_key_t **r_keys) +get_keys (cjson_t json, char **r_keystring) { - gpg_error_t err; cjson_t j_keys, j_item; int i, nkeys; - gpgme_key_t *keys; + char *p; + size_t length; - *r_keys = NULL; + *r_keystring = NULL; j_keys = cJSON_GetObjectItem (json, "keys"); if (!j_keys) @@ -373,8 +374,15 @@ get_keys (gpgme_ctx_t ctx, cjson_t json, gpgme_key_t **r_keys) if (!cjson_is_array (j_keys) && !cjson_is_string (j_keys)) return gpg_error (GPG_ERR_INV_VALUE); + /* Fixme: We should better use a membuf like thing. */ + length = 1; /* For the EOS. */ if (cjson_is_string (j_keys)) - nkeys = 1; + { + nkeys = 1; + length += strlen (j_keys->valuestring); + if (strchr (j_keys->valuestring, '\n')) + return gpg_error (GPG_ERR_INV_USER_ID); + } else { nkeys = cJSON_GetArraySize (j_keys); @@ -385,43 +393,37 @@ get_keys (gpgme_ctx_t ctx, cjson_t json, gpgme_key_t **r_keys) j_item = cJSON_GetArrayItem (j_keys, i); if (!j_item || !cjson_is_string (j_item)) return gpg_error (GPG_ERR_INV_VALUE); + if (i) + length++; /* Space for delimiter. */ + length += strlen (j_item->valuestring); + if (strchr (j_item->valuestring, '\n')) + return gpg_error (GPG_ERR_INV_USER_ID); } } - /* Now allocate an array to store the gpgme key objects. */ - keys = xcalloc (nkeys + 1, sizeof *keys); + p = *r_keystring = xtrymalloc (length); + if (!p) + return gpg_error_from_syserror (); if (cjson_is_string (j_keys)) { - err = gpgme_get_key (ctx, j_keys->valuestring, &keys[0], 0); - if (err) - goto leave; + strcpy (p, j_keys->valuestring); } else { for (i=0; i < nkeys; i++) { j_item = cJSON_GetArrayItem (j_keys, i); - err = gpgme_get_key (ctx, j_item->valuestring, &keys[i], 0); - if (err) - goto leave; + if (i) + *p++ = '\n'; /* Add delimiter. */ + p = stpcpy (p, j_item->valuestring); } } - err = 0; - *r_keys = keys; - keys = NULL; - - leave: - if (keys) - { - for (i=0; keys[i]; i++) - gpgme_key_unref (keys[i]); - xfree (keys); - } - return err; + return 0; } + /* * GPGME support functions. @@ -582,11 +584,11 @@ op_encrypt (cjson_t request, cjson_t result) gpgme_ctx_t ctx = NULL; gpgme_protocol_t protocol; int opt_base64; - gpgme_key_t *keys = NULL; + char *keystring = NULL; cjson_t j_input; gpgme_data_t input = NULL; gpgme_data_t output = NULL; - int abool, i; + int abool; gpgme_encrypt_flags_t encrypt_flags = 0; if ((err = get_protocol (request, &protocol))) @@ -622,7 +624,7 @@ op_encrypt (cjson_t request, cjson_t result) /* Get the keys. */ - err = get_keys (ctx, request, &keys); + err = get_keys (request, &keystring); if (err) { /* Provide a custom error response. */ @@ -674,7 +676,8 @@ op_encrypt (cjson_t request, cjson_t result) } /* Encrypt. */ - err = gpgme_op_encrypt (ctx, keys, encrypt_flags, input, output); + err = gpgme_op_encrypt_ext (ctx, NULL, keystring, encrypt_flags, + input, output); /* encrypt_result = gpgme_op_encrypt_result (ctx); */ if (err) { @@ -713,12 +716,7 @@ op_encrypt (cjson_t request, cjson_t result) } leave: - if (keys) - { - for (i=0; keys[i]; i++) - gpgme_key_unref (keys[i]); - xfree (keys); - } + xfree (keystring); release_context (ctx); gpgme_data_release (input); return err; diff --git a/src/gpgme.def b/src/gpgme.def index cad30f6c..a01d89a2 100644 --- a/src/gpgme.def +++ b/src/gpgme.def @@ -267,5 +267,10 @@ EXPORTS gpgme_op_conf_dir @199 + gpgme_op_encrypt_ext @200 + gpgme_op_encrypt_ext_start @201 + gpgme_op_encrypt_sign_ext @202 + gpgme_op_encrypt_sign_ext_start @203 + ; END diff --git a/src/gpgme.h.in b/src/gpgme.h.in index e3198798..8aba5baa 100644 --- a/src/gpgme.h.in +++ b/src/gpgme.h.in @@ -1274,10 +1274,22 @@ gpgme_encrypt_flags_t; store the resulting ciphertext in CIPHER. */ 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); + gpgme_data_t plain, + gpgme_data_t cipher); 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); + gpgme_data_t plain, + gpgme_data_t cipher); +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 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); /* Encrypt plaintext PLAIN within CTX for the recipients RECP and store the resulting ciphertext in CIPHER. Also sign the ciphertext @@ -1289,7 +1301,19 @@ gpgme_error_t gpgme_op_encrypt_sign_start (gpgme_ctx_t ctx, gpgme_data_t cipher); 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); + gpgme_data_t plain, + gpgme_data_t cipher); +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 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); /* diff --git a/src/libgpgme.vers b/src/libgpgme.vers index a95befba..b49c86d9 100644 --- a/src/libgpgme.vers +++ b/src/libgpgme.vers @@ -215,10 +215,15 @@ GPGME_1.0 { gpgme_op_edit; gpgme_op_edit_start; gpgme_op_encrypt; - gpgme_op_encrypt_result; - gpgme_op_encrypt_sign; - gpgme_op_encrypt_sign_start; gpgme_op_encrypt_start; + gpgme_op_encrypt_ext; + gpgme_op_encrypt_ext_start; + gpgme_op_encrypt_sign; + gpgme_op_encrypt_sign_ext; + gpgme_op_encrypt_sign_start; + gpgme_op_encrypt_sign_ext_start; + gpgme_op_encrypt_result; + gpgme_op_export; gpgme_op_export_ext; gpgme_op_export_ext_start; diff --git a/tests/run-encrypt.c b/tests/run-encrypt.c index 51e2d60f..94084694 100644 --- a/tests/run-encrypt.c +++ b/tests/run-encrypt.c @@ -37,6 +37,19 @@ static int verbose; +static char * +xstrdup (const char *string) +{ + char *p = strdup (string); + if (!p) + { + fprintf (stderr, "strdup failed\n"); + exit (2); + } + return p; +} + + static gpg_error_t status_cb (void *opaque, const char *keyword, const char *value) { @@ -88,6 +101,7 @@ show_usage (int ex) " --uiserver use the UI server\n" " --loopback use a loopback pinentry\n" " --key NAME encrypt to key NAME\n" + " --keystring NAMES encrypt to ';' delimited NAMES\n" " --throw-keyids use this option\n" " --no-symkey-cache disable the use of that cache\n" " --wrap assume input is valid OpenPGP message\n" @@ -103,7 +117,6 @@ main (int argc, char **argv) int last_argc = -1; gpgme_error_t err; gpgme_ctx_t ctx; - const char *key_string = NULL; gpgme_protocol_t protocol = GPGME_PROTOCOL_OpenPGP; gpgme_data_t in, out; gpgme_encrypt_result_t result; @@ -113,6 +126,7 @@ main (int argc, char **argv) char *keyargs[10]; gpgme_key_t keys[10+1]; int keycount = 0; + char *keystring = NULL; int i; gpgme_encrypt_flags_t flags = GPGME_ENCRYPT_ALWAYS_TRUST; gpgme_off_t offset; @@ -174,6 +188,17 @@ main (int argc, char **argv) keyargs[keycount++] = *argv; argc--; argv++; } + else if (!strcmp (*argv, "--keystring")) + { + argc--; argv++; + if (!argc) + show_usage (1); + keystring = xstrdup (*argv); + for (i=0; keystring[i]; i++) + if (keystring[i] == ';') + keystring[i] = '\n'; + argc--; argv++; + } else if (!strcmp (*argv, "--throw-keyids")) { flags |= GPGME_ENCRYPT_THROW_KEYIDS; @@ -207,15 +232,6 @@ main (int argc, char **argv) if (argc != 1) show_usage (1); - if (key_string && protocol == GPGME_PROTOCOL_UISERVER) - { - fprintf (stderr, PGM ": ignoring --key in UI-server mode\n"); - key_string = NULL; - } - - if (!key_string) - key_string = "test"; - init_gpgme (protocol); err = gpgme_new (&ctx); @@ -298,7 +314,8 @@ main (int argc, char **argv) err = gpgme_data_new (&out); fail_if_err (err); - err = gpgme_op_encrypt (ctx, keycount ? keys : NULL, flags, in, out); + err = gpgme_op_encrypt_ext (ctx, keycount ? keys : NULL, keystring, + flags, in, out); result = gpgme_op_encrypt_result (ctx); if (result) print_result (result); @@ -318,5 +335,6 @@ main (int argc, char **argv) for (i=0; i < keycount; i++) gpgme_key_unref (keys[i]); gpgme_release (ctx); + free (keystring); return 0; }