From 984ff7e3a2a9a261a0732cbdaf3b2f22d325b433 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ingo=20Kl=C3=B6cker?= Date: Mon, 16 Jan 2023 12:28:13 +0100 Subject: [PATCH 01/24] core: Do not add obsolete --no-sk-comments to command line * src/engine-gpg.c (build_argv): Don't add "--no-sk-comments" to argv. -- This option is a no-op since 1.4.3 and 1.4.23 is the latest 1.4 release. GnuPG-bug-id: 6342 --- src/engine-gpg.c | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/src/engine-gpg.c b/src/engine-gpg.c index 9d20f2ba..a0f24a8a 100644 --- a/src/engine-gpg.c +++ b/src/engine-gpg.c @@ -944,8 +944,6 @@ build_argv (engine_gpg_t gpg, const char *pgmname) if (!gpg->cmd.used) argc++; /* --batch */ - argc++; /* --no-sk-comments */ - argv = calloc (argc + 1, sizeof *argv); allocated_argc = argc; @@ -1125,15 +1123,6 @@ build_argv (engine_gpg_t gpg, const char *pgmname) } argc++; } - argv[argc] = strdup ("--no-sk-comments"); - if (!argv[argc]) - { - int saved_err = gpg_error_from_syserror (); - free (fd_data_map); - free_argv (argv); - return saved_err; - } - argc++; for (a = gpg->arglist; a; a = a->next) { if (a->arg_locp) From fbce7deb3b68af900f692591d5d05fa5c1a83f5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ingo=20Kl=C3=B6cker?= Date: Tue, 17 Jan 2023 09:29:05 +0100 Subject: [PATCH 02/24] core: Use "goto leave" pattern to clean up resources * src/engine-gpg.c (build_argv): Initialize fd_data_map and argv. Goto leave on error. Free fd_data_map and argv on error. -- This will make it easier to add an additional "--with-gpg-args" option where needed. GnuPG-bug-id: 6342 --- src/engine-gpg.c | 130 ++++++++++++++++++++++------------------------- 1 file changed, 62 insertions(+), 68 deletions(-) diff --git a/src/engine-gpg.c b/src/engine-gpg.c index a0f24a8a..2f61ed4b 100644 --- a/src/engine-gpg.c +++ b/src/engine-gpg.c @@ -871,9 +871,9 @@ build_argv (engine_gpg_t gpg, const char *pgmname) { gpgme_error_t err; struct arg_and_data_s *a; - struct fd_data_map_s *fd_data_map; + struct fd_data_map_s *fd_data_map = NULL; size_t datac=0, argc=0, allocated_argc=0; - char **argv; + char **argv = NULL; int need_special = 0; int use_agent = 0; char *p; @@ -952,19 +952,18 @@ build_argv (engine_gpg_t gpg, const char *pgmname) fd_data_map = calloc (datac + 1, sizeof *fd_data_map); if (!fd_data_map) { - int saved_err = gpg_error_from_syserror (); - free_argv (argv); - return saved_err; + err = gpg_error_from_syserror (); + if (err) + goto leave; } argc = datac = 0; argv[argc] = strdup (_gpgme_get_basename (pgmname)); /* argv[0] */ if (!argv[argc]) { - int saved_err = gpg_error_from_syserror (); - free (fd_data_map); - free_argv (argv); - return saved_err; + err = gpg_error_from_syserror (); + if (err) + goto leave; } argc++; if (need_special) @@ -972,10 +971,9 @@ build_argv (engine_gpg_t gpg, const char *pgmname) argv[argc] = strdup ("--enable-special-filenames"); if (!argv[argc]) { - int saved_err = gpg_error_from_syserror (); - free (fd_data_map); - free_argv (argv); - return saved_err; + err = gpg_error_from_syserror (); + if (err) + goto leave; } argc++; } @@ -984,10 +982,9 @@ build_argv (engine_gpg_t gpg, const char *pgmname) argv[argc] = strdup ("--use-agent"); if (!argv[argc]) { - int saved_err = gpg_error_from_syserror (); - free (fd_data_map); - free_argv (argv); - return saved_err; + err = gpg_error_from_syserror (); + if (err) + goto leave; } argc++; } @@ -1000,10 +997,9 @@ build_argv (engine_gpg_t gpg, const char *pgmname) gpg->request_origin, NULL); if (!argv[argc]) { - int saved_err = gpg_error_from_syserror (); - free (fd_data_map); - free_argv (argv); - return saved_err; + err = gpg_error_from_syserror (); + if (err) + goto leave; } argc++; } @@ -1013,10 +1009,9 @@ build_argv (engine_gpg_t gpg, const char *pgmname) argv[argc] = strdup (gpg->auto_key_locate); if (!argv[argc]) { - int saved_err = gpg_error_from_syserror (); - free (fd_data_map); - free_argv (argv); - return saved_err; + err = gpg_error_from_syserror (); + if (err) + goto leave; } argc++; } @@ -1026,10 +1021,9 @@ build_argv (engine_gpg_t gpg, const char *pgmname) argv[argc] = strdup (gpg->trust_model); if (!argv[argc]) { - int saved_err = gpg_error_from_syserror (); - free (fd_data_map); - free_argv (argv); - return saved_err; + err = gpg_error_from_syserror (); + if (err) + goto leave; } argc++; } @@ -1039,10 +1033,9 @@ build_argv (engine_gpg_t gpg, const char *pgmname) argv[argc] = strdup ("--no-symkey-cache"); if (!argv[argc]) { - int saved_err = gpg_error_from_syserror (); - free (fd_data_map); - free_argv (argv); - return saved_err; + err = gpg_error_from_syserror (); + if (err) + goto leave; } argc++; } @@ -1052,10 +1045,9 @@ build_argv (engine_gpg_t gpg, const char *pgmname) argv[argc] = strdup ("--ignore-mdc-error"); if (!argv[argc]) { - int saved_err = gpg_error_from_syserror (); - free (fd_data_map); - free_argv (argv); - return saved_err; + err = gpg_error_from_syserror (); + if (err) + goto leave; } argc++; } @@ -1065,10 +1057,9 @@ build_argv (engine_gpg_t gpg, const char *pgmname) argv[argc] = strdup ("--disable-dirmngr"); if (!argv[argc]) { - int saved_err = gpg_error_from_syserror (); - free (fd_data_map); - free_argv (argv); - return saved_err; + err = gpg_error_from_syserror (); + if (err) + goto leave; } argc++; } @@ -1078,10 +1069,9 @@ build_argv (engine_gpg_t gpg, const char *pgmname) argv[argc] = strdup ("--no-auto-check-trustdb"); if (!argv[argc]) { - int saved_err = gpg_error_from_syserror (); - free (fd_data_map); - free_argv (argv); - return saved_err; + err = gpg_error_from_syserror (); + if (err) + goto leave; } argc++; } @@ -1102,10 +1092,9 @@ build_argv (engine_gpg_t gpg, const char *pgmname) argv[argc] = strdup (s); if (!argv[argc]) { - int saved_err = gpg_error_from_syserror (); - free (fd_data_map); - free_argv (argv); - return saved_err; + err = gpg_error_from_syserror (); + if (err) + goto leave; } argc++; } @@ -1116,10 +1105,9 @@ build_argv (engine_gpg_t gpg, const char *pgmname) argv[argc] = strdup ("--batch"); if (!argv[argc]) { - int saved_err = gpg_error_from_syserror (); - free (fd_data_map); - free_argv (argv); - return saved_err; + err = gpg_error_from_syserror (); + if (err) + goto leave; } argc++; } @@ -1140,10 +1128,9 @@ build_argv (engine_gpg_t gpg, const char *pgmname) if (_gpgme_io_pipe (fds, fd_data_map[datac].inbound ? 1 : 0) == -1) { - int saved_err = gpg_error_from_syserror (); - free (fd_data_map); - free_argv (argv); - return saved_err; + err = gpg_error_from_syserror (); + if (err) + goto leave; } if (_gpgme_io_set_close_notify (fds[0], close_notify_handler, gpg) @@ -1191,10 +1178,9 @@ build_argv (engine_gpg_t gpg, const char *pgmname) argv[argc] = malloc (buflen); if (!argv[argc]) { - int saved_err = gpg_error_from_syserror (); - free (fd_data_map); - free_argv (argv); - return saved_err; + err = gpg_error_from_syserror (); + if (err) + goto leave; } ptr = argv[argc]; @@ -1216,10 +1202,9 @@ build_argv (engine_gpg_t gpg, const char *pgmname) argv[argc] = strdup (a->arg); if (!argv[argc]) { - int saved_err = gpg_error_from_syserror (); - free (fd_data_map); - free_argv (argv); - return saved_err; + err = gpg_error_from_syserror (); + if (err) + goto leave; } argc++; } @@ -1230,9 +1215,18 @@ build_argv (engine_gpg_t gpg, const char *pgmname) allocated array like ccparray in gnupg. */ assert (argc <= allocated_argc); - gpg->argv = argv; - gpg->fd_data_map = fd_data_map; - return 0; +leave: + if (err) + { + free (fd_data_map); + free_argv (argv); + } + else + { + gpg->argv = argv; + gpg->fd_data_map = fd_data_map; + } + return err; } From 12e490d97f2e7064bc9b5bea0d197790d10db106 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ingo=20Kl=C3=B6cker?= Date: Wed, 18 Jan 2023 10:30:17 +0100 Subject: [PATCH 03/24] core: Extend gpgme_get_dirinfo to return the gpgtar name * src/dirinfo.c (WANT_GPGTAR_NAME): New. (dirinfo): Add field gpgtar_name. (get_gpgconf_item): Build gpgtar_name on demand and return it. (_gpgme_get_default_gpgtar_name) : New. (gpgme_get_dirinfo): New value "gpgtar-name" for WHAT. * src/util.h (_gpgme_get_default_gpgtar_name): New. * tests/t-engine-info.c (main): Add gpgtar-name to the output. GnuPG-bug-id: 6342 --- doc/gpgme.texi | 3 +++ src/dirinfo.c | 19 +++++++++++++++++++ src/util.h | 1 + tests/t-engine-info.c | 1 + 4 files changed, 24 insertions(+) diff --git a/doc/gpgme.texi b/doc/gpgme.texi index f19b8325..eeb865aa 100644 --- a/doc/gpgme.texi +++ b/doc/gpgme.texi @@ -1024,6 +1024,9 @@ Return the name of the pinentry program. @item gpg-wks-client-name Return the name of the Web Key Service tool. +@item gpgtar-name +Return the name of the gpgtar program. + @end table @end deftypefun diff --git a/src/dirinfo.c b/src/dirinfo.c index 8ea15d81..60e536f5 100644 --- a/src/dirinfo.c +++ b/src/dirinfo.c @@ -59,6 +59,7 @@ enum WANT_DIRMNGR_NAME, WANT_PINENTRY_NAME, WANT_GPG_WKS_CLIENT_NAME, + WANT_GPGTAR_NAME, WANT_GPG_ONE_MODE }; @@ -88,6 +89,7 @@ static struct { char *dirmngr_name; char *pinentry_name; char *gpg_wks_client_name; + char *gpgtar_name; int gpg_one_mode; /* System is in gpg1 mode. */ } dirinfo; @@ -407,6 +409,14 @@ get_gpgconf_item (int what) NULL); result = dirinfo.gpg_wks_client_name; break; + case WANT_GPGTAR_NAME: + if (!dirinfo.gpgtar_name && dirinfo.bindir) + dirinfo.gpgtar_name = _gpgme_strconcat (dirinfo.bindir, + "/", + "gpgtar", + NULL); + result = dirinfo.gpgtar_name; + break; } UNLOCK (dirinfo_lock); return result; @@ -455,6 +465,13 @@ _gpgme_get_default_gpgconf_name (void) return get_gpgconf_item (WANT_GPGCONF_NAME); } +/* Return the default gpgtar file name. Returns NULL if not known. */ +const char * +_gpgme_get_default_gpgtar_name (void) +{ + return get_gpgconf_item (WANT_GPGTAR_NAME); +} + /* Return the default UI-server socket name. Returns NULL if not known. */ const char * @@ -524,6 +541,8 @@ gpgme_get_dirinfo (const char *what) return get_gpgconf_item (WANT_PINENTRY_NAME); else if (!strcmp (what, "gpg-wks-client-name")) return get_gpgconf_item (WANT_GPG_WKS_CLIENT_NAME); + else if (!strcmp (what, "gpgtar-name")) + return get_gpgconf_item (WANT_GPGTAR_NAME); else if (!strcmp (what, "agent-ssh-socket")) return get_gpgconf_item (WANT_AGENT_SSH_SOCKET); else if (!strcmp (what, "dirmngr-socket")) diff --git a/src/util.h b/src/util.h index 89075848..90213b10 100644 --- a/src/util.h +++ b/src/util.h @@ -56,6 +56,7 @@ const char *_gpgme_get_default_gpg_name (void); const char *_gpgme_get_default_gpgsm_name (void); const char *_gpgme_get_default_g13_name (void); const char *_gpgme_get_default_gpgconf_name (void); +const char *_gpgme_get_default_gpgtar_name (void); const char *_gpgme_get_default_uisrv_socket (void); int _gpgme_in_gpg_one_mode (void); diff --git a/tests/t-engine-info.c b/tests/t-engine-info.c index 3f8d0037..1a351391 100644 --- a/tests/t-engine-info.c +++ b/tests/t-engine-info.c @@ -133,6 +133,7 @@ main (int argc, char **argv ) "dirmngr-name", "pinentry-name", "gpg-wks-client-name", + "gpgtar-name", NULL }; const char *s; int i; From 1a9dfdfccba1a10ce87809beba71d8e035df3aa5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ingo=20Kl=C3=B6cker?= Date: Wed, 18 Jan 2023 10:33:54 +0100 Subject: [PATCH 04/24] cpp,doc: Update list of allowed values for dirInfo -- GnuPG-bug-id: 6342 --- lang/cpp/src/global.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lang/cpp/src/global.h b/lang/cpp/src/global.h index 1336142a..84c8d462 100644 --- a/lang/cpp/src/global.h +++ b/lang/cpp/src/global.h @@ -103,7 +103,7 @@ homedir, sysconfdir, bindir, libexecdir, libdir, datadir, localedir, agent-socket, agent-ssh-socket, dirmngr-socket, uiserver-socket, gpgconf-name, gpg-name, gpgsm-name, g13-name, keyboxd-name, agent-name, scdaemon-name, -dirmngr-name, pinentry-name, socketdir. +dirmngr-name, pinentry-name, socketdir, gpg-wks-client-name, gpgtar-name. This may be extended in the future. */ From 7a68a1ca645296fa90ff9e08a99879cf8d440630 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ingo=20Kl=C3=B6cker?= Date: Wed, 18 Jan 2023 10:37:39 +0100 Subject: [PATCH 05/24] doc: Document socketdir value of gpgme_get_dirinfo -- --- doc/gpgme.texi | 3 +++ 1 file changed, 3 insertions(+) diff --git a/doc/gpgme.texi b/doc/gpgme.texi index eeb865aa..2532f800 100644 --- a/doc/gpgme.texi +++ b/doc/gpgme.texi @@ -981,6 +981,9 @@ Return the name of the directory with GnuPG shared data. @item localedir Return the name of the directory with GnuPG locale data. +@item socketdir +Return the name of the directory with the following sockets. + @item agent-socket Return the name of the socket to connect to the gpg-agent. From d56b3bc1cfa128d0246cfac59411adaad2579bc4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ingo=20Kl=C3=B6cker?= Date: Wed, 18 Jan 2023 10:54:39 +0100 Subject: [PATCH 06/24] core: Defer adding --status-fd and --logger-fd to argument list * src/engine-gpg.c (_append_to_arglist, _prepend_to_arglist): New. (_add_arg): Use _append_to_arglist and _prepend_to_arglist. (add_data_ext): New. Extends add_data. (add_data): Uses add_data_ext. (gpg_new): Do not add --status-fd and --logger-fd to argument list. (start): Prepend --logger-fd and --status-fd to the argument list. -- This change makes it possible to handle those two arguments differently if gpgtar is used instead of gpg. GnuPG-bug-id: 6342 --- src/engine-gpg.c | 101 ++++++++++++++++++++++++++++------------------- 1 file changed, 60 insertions(+), 41 deletions(-) diff --git a/src/engine-gpg.c b/src/engine-gpg.c index 2f61ed4b..0ef4f309 100644 --- a/src/engine-gpg.c +++ b/src/engine-gpg.c @@ -222,6 +222,27 @@ close_notify_handler (int fd, void *opaque) } } +static void +_append_to_arglist (engine_gpg_t gpg, struct arg_and_data_s *a) +{ + a->next = NULL; + *gpg->argtail = a; + gpg->argtail = &a->next; +} + +static void +_prepend_to_arglist (engine_gpg_t gpg, struct arg_and_data_s *a) +{ + a->next = gpg->arglist; + if (!gpg->arglist) + { + /* If this is the first argument, we need to update the tail + pointer. */ + gpg->argtail = &a->next; + } + gpg->arglist = a; +} + /* If FRONT is true, push at the front of the list. Use this for options added late in the process. */ static gpgme_error_t @@ -247,22 +268,9 @@ _add_arg (engine_gpg_t gpg, const char *prefix, const char *arg, size_t arglen, memcpy (a->arg + prefixlen, arg, arglen); a->arg[prefixlen + arglen] = 0; if (front) - { - a->next = gpg->arglist; - if (!gpg->arglist) - { - /* If this is the first argument, we need to update the tail - pointer. */ - gpg->argtail = &a->next; - } - gpg->arglist = a; - } + _prepend_to_arglist (gpg, a); else - { - a->next = NULL; - *gpg->argtail = a; - gpg->argtail = &a->next; - } + _append_to_arglist (gpg, a); return 0; } @@ -301,7 +309,7 @@ add_arg_len (engine_gpg_t gpg, const char *prefix, static gpgme_error_t -add_data (engine_gpg_t gpg, gpgme_data_t data, int dup_to, int inbound) +add_data_ext (engine_gpg_t gpg, gpgme_data_t data, int dup_to, int inbound, int front) { struct arg_and_data_s *a; @@ -311,7 +319,6 @@ add_data (engine_gpg_t gpg, gpgme_data_t data, int dup_to, int inbound) a = malloc (offsetof (struct arg_and_data_s, arg)); if (!a) return gpg_error_from_syserror (); - a->next = NULL; a->data = data; a->inbound = inbound; a->arg_locp = NULL; @@ -326,12 +333,22 @@ add_data (engine_gpg_t gpg, gpgme_data_t data, int dup_to, int inbound) a->print_fd = 0; a->dup_to = dup_to; } - *gpg->argtail = a; - gpg->argtail = &a->next; + + if (front) + _prepend_to_arglist (gpg, a); + else + _append_to_arglist (gpg, a); + return 0; } +static gpgme_error_t +add_data (engine_gpg_t gpg, gpgme_data_t data, int dup_to, int inbound) +{ + return add_data_ext (gpg, data, dup_to, inbound, 0); +} + /* Return true if the engine's version is at least VERSION. */ static int have_gpg_version (engine_gpg_t gpg, const char *version) @@ -547,18 +564,6 @@ gpg_new (void **engine, const char *file_name, const char *home_dir, goto leave; } - rc = add_arg (gpg, "--status-fd"); - if (rc) - goto leave; - - { - char buf[25]; - _gpgme_io_fd2str (buf, sizeof (buf), gpg->status.fd[1]); - rc = add_arg_with_locp (gpg, buf, &gpg->status.arg_loc); - if (rc) - goto leave; - } - rc = add_arg (gpg, "--no-tty"); if (!rc) rc = add_arg (gpg, "--charset"); @@ -632,16 +637,6 @@ gpg_new (void **engine, const char *file_name, const char *home_dir, } } - rc = gpgme_data_new (&gpg->diagnostics); - if (rc) - goto leave; - - rc = add_arg (gpg, "--logger-fd"); - if (rc) - goto leave; - - rc = add_data (gpg, gpg->diagnostics, -2, 1); - leave: if (rc) gpg_release (gpg); @@ -1547,6 +1542,30 @@ start (engine_gpg_t gpg) if (!gpg->file_name && !_gpgme_get_default_gpg_name ()) return trace_gpg_error (GPG_ERR_INV_ENGINE); + rc = gpgme_data_new (&gpg->diagnostics); + if (rc) + return rc; + + rc = add_data_ext (gpg, gpg->diagnostics, -2, 1, 1); + if (rc) + return rc; + + rc = add_arg_ext (gpg, "--logger-fd", 1); + if (rc) + return rc; + + { + char buf[25]; + _gpgme_io_fd2str (buf, sizeof (buf), gpg->status.fd[1]); + rc = add_arg_with_locp (gpg, buf, &gpg->status.arg_loc, 1); + if (rc) + return rc; + } + + rc = add_arg_ext (gpg, "--status-fd", 1); + if (rc) + return rc; + if (gpg->lc_ctype) { rc = add_arg_ext (gpg, gpg->lc_ctype, 1); From 5d8316da1d3dd0ab73743898e76c51932d0f13e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ingo=20Kl=C3=B6cker?= Date: Wed, 18 Jan 2023 11:48:02 +0100 Subject: [PATCH 07/24] core: Support usage of gpgtar for encryption * src/gpgme.h.in (GPGME_ENCRYPT_ARCHIVE): New encryption flag. * src/engine-gpg.c (arg_and_data_s): New field gpg_arg. (engine_gpg): New flag use_gpgtar. (_add_arg): Add argument gpg_arg and set it. (add_arg_ext, add_arg_with_locp, add_arg, add_arg_pfx, add_arg_len): Adjust call of _add_arg. (add_arg_with_locp): Add argument front. (add_gpg_arg, add_gpg_arg_with_value): New. (gpg_new): Use add_gpg_arg_with_value for gpg-only options with a value and add_gpg_arg for gpg-only options without. (build_argv): Consider usage of gpgtar when counting arguments to pass to gpg/gpgtar. Prepend "--gpg-args" to all gpg-only arguments if gpgtar is used. (start): Set program to use. Read diagnostics output from stderr when using gpgtar. Do not pass --status-fd to gpgtar for gpg < 2.4.1. Use add_gpg_arg_with_value for --lc-ctype and --lc-messages. (gpg_encrypt): Set use_gpgtar engine flag if GPGME_ENCRYPT_ARCHIVE flag is set. Check for new enough gpg and incompatible flags. Use add_gpg_arg_with_value for gpg-only options with a value and add_gpg_arg for gpg-only options without a value. Set extra options for gpgtar and pass input data to stdin when using gpgtar. * src/engine-gpgsm.c (gpgsm_encrypt): Return error if new flag is set. * src/engine-uiserver.c (uiserver_encrypt): Ditto. * tests/run-encrypt.c (show_usage): New options --archive and --diagnostics. (main): Parse new options. Encrypt with gpgtar if --archive is given. Print stderr of gpg/gpgtar if --diagnostics is given. -- With this change the gpgme_op_encrypt* functions get support for encrypting a list of files and/or directories passed as NUL-separated list in "plain" data with gpgtar. GnuPG-bug-id: 6342 --- src/engine-gpg.c | 294 ++++++++++++++++++++++++++++++------------ src/engine-gpgsm.c | 3 + src/engine-uiserver.c | 3 + src/gpgme.h.in | 3 +- tests/run-encrypt.c | 117 +++++++++++------ 5 files changed, 303 insertions(+), 117 deletions(-) diff --git a/src/engine-gpg.c b/src/engine-gpg.c index 0ef4f309..a681a220 100644 --- a/src/engine-gpg.c +++ b/src/engine-gpg.c @@ -57,6 +57,7 @@ struct arg_and_data_s int inbound; /* True if this is used for reading from gpg. */ int dup_to; int print_fd; /* Print the fd number and not the special form of it. */ + int gpg_arg; /* True if this argument is not known by gpgtar. */ int *arg_locp; /* Write back the argv idx of this argument when building command line to this location. */ char arg[FLEXIBLE_ARRAY_MEMBER]; /* Used if data above is not used. */ @@ -145,6 +146,7 @@ struct engine_gpg char *trust_model; struct { + unsigned int use_gpgtar : 1; unsigned int no_symkey_cache : 1; unsigned int offline : 1; unsigned int ignore_mdc_error : 1; @@ -247,7 +249,7 @@ _prepend_to_arglist (engine_gpg_t gpg, struct arg_and_data_s *a) options added late in the process. */ static gpgme_error_t _add_arg (engine_gpg_t gpg, const char *prefix, const char *arg, size_t arglen, - int front, int *arg_locp) + int front, int *arg_locp, int gpg_arg) { struct arg_and_data_s *a; size_t prefixlen = prefix? strlen (prefix) : 0; @@ -262,6 +264,7 @@ _add_arg (engine_gpg_t gpg, const char *prefix, const char *arg, size_t arglen, a->data = NULL; a->dup_to = -1; a->arg_locp = arg_locp; + a->gpg_arg = gpg_arg; if (prefixlen) memcpy (a->arg, prefix, prefixlen); @@ -279,32 +282,44 @@ _add_arg (engine_gpg_t gpg, const char *prefix, const char *arg, size_t arglen, static gpgme_error_t add_arg_ext (engine_gpg_t gpg, const char *arg, int front) { - return _add_arg (gpg, NULL, arg, strlen (arg), front, NULL); + return _add_arg (gpg, NULL, arg, strlen (arg), front, NULL, 0); } static gpgme_error_t -add_arg_with_locp (engine_gpg_t gpg, const char *arg, int *locp) +add_arg_with_locp (engine_gpg_t gpg, const char *arg, int *locp, int front) { - return _add_arg (gpg, NULL, arg, strlen (arg), 0, locp); + return _add_arg (gpg, NULL, arg, strlen (arg), front, locp, 0); } static gpgme_error_t add_arg (engine_gpg_t gpg, const char *arg) { - return _add_arg (gpg, NULL, arg, strlen (arg), 0, NULL); + return _add_arg (gpg, NULL, arg, strlen (arg), 0, NULL, 0); } static gpgme_error_t add_arg_pfx (engine_gpg_t gpg, const char *prefix, const char *arg) { - return _add_arg (gpg, prefix, arg, strlen (arg), 0, NULL); + return _add_arg (gpg, prefix, arg, strlen (arg), 0, NULL, 0); +} + +static gpgme_error_t +add_gpg_arg (engine_gpg_t gpg, const char *arg) +{ + return _add_arg (gpg, NULL, arg, strlen (arg), 0, NULL, 1); +} + +static gpgme_error_t +add_gpg_arg_with_value (engine_gpg_t gpg, const char *arg, const char *value, int front) +{ + return _add_arg (gpg, arg, value, strlen (value), front, NULL, 1); } static gpgme_error_t add_arg_len (engine_gpg_t gpg, const char *prefix, const char *arg, size_t arglen) { - return _add_arg (gpg, prefix, arg, arglen, 0, NULL); + return _add_arg (gpg, prefix, arg, arglen, 0, NULL, 0); } @@ -557,22 +572,18 @@ gpg_new (void **engine, const char *file_name, const char *home_dir, if (home_dir) { - rc = add_arg (gpg, "--homedir"); - if (!rc) - rc = add_arg (gpg, home_dir); + rc = add_gpg_arg_with_value (gpg, "--homedir=", home_dir, 0); if (rc) goto leave; } - rc = add_arg (gpg, "--no-tty"); + rc = add_gpg_arg (gpg, "--no-tty"); if (!rc) - rc = add_arg (gpg, "--charset"); + rc = add_gpg_arg (gpg, "--charset=utf8"); if (!rc) - rc = add_arg (gpg, "utf8"); - if (!rc) - rc = add_arg (gpg, "--enable-progress-filter"); + rc = add_gpg_arg (gpg, "--enable-progress-filter"); if (!rc && have_gpg_version (gpg, "2.1.11")) - rc = add_arg (gpg, "--exit-on-status-write-error"); + rc = add_gpg_arg (gpg, "--exit-on-status-write-error"); if (rc) goto leave; @@ -581,9 +592,7 @@ gpg_new (void **engine, const char *file_name, const char *home_dir, goto leave; if (dft_display) { - rc = add_arg (gpg, "--display"); - if (!rc) - rc = add_arg (gpg, dft_display); + rc = add_gpg_arg_with_value (gpg, "--display=", dft_display, 0); free (dft_display); if (rc) @@ -610,11 +619,7 @@ gpg_new (void **engine, const char *file_name, const char *home_dir, if (!err) { if (*dft_ttyname) - { - rc = add_arg (gpg, "--ttyname"); - if (!rc) - rc = add_arg (gpg, dft_ttyname); - } + rc = add_gpg_arg_with_value (gpg, "--ttyname=", dft_ttyname, 0); else rc = 0; if (!rc) @@ -624,11 +629,7 @@ gpg_new (void **engine, const char *file_name, const char *home_dir, goto leave; if (dft_ttytype) - { - rc = add_arg (gpg, "--ttytype"); - if (!rc) - rc = add_arg (gpg, dft_ttytype); - } + rc = add_gpg_arg_with_value (gpg, "--ttytype=", dft_ttytype, 0); free (dft_ttytype); } @@ -902,7 +903,7 @@ build_argv (engine_gpg_t gpg, const char *pgmname) argc++; /* For argv[0]. */ for (a = gpg->arglist; a; a = a->next) { - argc++; + argc += 1 + (gpg->flags.use_gpgtar && a->gpg_arg); if (a->data) { /*fprintf (stderr, "build_argv: data\n" );*/ @@ -921,21 +922,21 @@ build_argv (engine_gpg_t gpg, const char *pgmname) if (use_agent) argc++; if (*gpg->request_origin) - argc++; + argc += 1 + !!gpg->flags.use_gpgtar; if (gpg->auto_key_locate) - argc++; + argc += 1 + !!gpg->flags.use_gpgtar; if (gpg->trust_model) - argc++; + argc += 1 + !!gpg->flags.use_gpgtar; if (gpg->flags.no_symkey_cache) - argc++; + argc += 1 + !!gpg->flags.use_gpgtar; if (gpg->flags.ignore_mdc_error) - argc++; + argc += 1 + !!gpg->flags.use_gpgtar; if (gpg->flags.offline) - argc++; + argc += 1 + !!gpg->flags.use_gpgtar; if (gpg->flags.no_auto_check_trustdb) - argc++; + argc += 1 + !!gpg->flags.use_gpgtar; if (gpg->pinentry_mode) - argc++; + argc += 1 + !!gpg->flags.use_gpgtar; if (!gpg->cmd.used) argc++; /* --batch */ @@ -988,6 +989,17 @@ build_argv (engine_gpg_t gpg, const char *pgmname) if (*gpg->request_origin) { + if (gpg->flags.use_gpgtar) + { + argv[argc] = strdup ("--gpg-args"); + if (!argv[argc]) + { + err = gpg_error_from_syserror (); + if (err) + goto leave; + } + argc++; + } argv[argc] = _gpgme_strconcat ("--request-origin=", gpg->request_origin, NULL); if (!argv[argc]) @@ -1001,6 +1013,17 @@ build_argv (engine_gpg_t gpg, const char *pgmname) if (gpg->auto_key_locate) { + if (gpg->flags.use_gpgtar) + { + argv[argc] = strdup ("--gpg-args"); + if (!argv[argc]) + { + err = gpg_error_from_syserror (); + if (err) + goto leave; + } + argc++; + } argv[argc] = strdup (gpg->auto_key_locate); if (!argv[argc]) { @@ -1013,6 +1036,17 @@ build_argv (engine_gpg_t gpg, const char *pgmname) if (gpg->trust_model) { + if (gpg->flags.use_gpgtar) + { + argv[argc] = strdup ("--gpg-args"); + if (!argv[argc]) + { + err = gpg_error_from_syserror (); + if (err) + goto leave; + } + argc++; + } argv[argc] = strdup (gpg->trust_model); if (!argv[argc]) { @@ -1025,6 +1059,17 @@ build_argv (engine_gpg_t gpg, const char *pgmname) if (gpg->flags.no_symkey_cache) { + if (gpg->flags.use_gpgtar) + { + argv[argc] = strdup ("--gpg-args"); + if (!argv[argc]) + { + err = gpg_error_from_syserror (); + if (err) + goto leave; + } + argc++; + } argv[argc] = strdup ("--no-symkey-cache"); if (!argv[argc]) { @@ -1037,6 +1082,17 @@ build_argv (engine_gpg_t gpg, const char *pgmname) if (gpg->flags.ignore_mdc_error) { + if (gpg->flags.use_gpgtar) + { + argv[argc] = strdup ("--gpg-args"); + if (!argv[argc]) + { + err = gpg_error_from_syserror (); + if (err) + goto leave; + } + argc++; + } argv[argc] = strdup ("--ignore-mdc-error"); if (!argv[argc]) { @@ -1049,6 +1105,17 @@ build_argv (engine_gpg_t gpg, const char *pgmname) if (gpg->flags.offline) { + if (gpg->flags.use_gpgtar) + { + argv[argc] = strdup ("--gpg-args"); + if (!argv[argc]) + { + err = gpg_error_from_syserror (); + if (err) + goto leave; + } + argc++; + } argv[argc] = strdup ("--disable-dirmngr"); if (!argv[argc]) { @@ -1061,6 +1128,17 @@ build_argv (engine_gpg_t gpg, const char *pgmname) if (gpg->flags.no_auto_check_trustdb) { + if (gpg->flags.use_gpgtar) + { + argv[argc] = strdup ("--gpg-args"); + if (!argv[argc]) + { + err = gpg_error_from_syserror (); + if (err) + goto leave; + } + argc++; + } argv[argc] = strdup ("--no-auto-check-trustdb"); if (!argv[argc]) { @@ -1084,6 +1162,17 @@ build_argv (engine_gpg_t gpg, const char *pgmname) } if (s) { + if (gpg->flags.use_gpgtar) + { + argv[argc] = strdup ("--gpg-args"); + if (!argv[argc]) + { + err = gpg_error_from_syserror (); + if (err) + goto leave; + } + argc++; + } argv[argc] = strdup (s); if (!argv[argc]) { @@ -1194,6 +1283,17 @@ build_argv (engine_gpg_t gpg, const char *pgmname) } else { + if (gpg->flags.use_gpgtar && a->gpg_arg) + { + argv[argc] = strdup ("--gpg-args"); + if (!argv[argc]) + { + err = gpg_error_from_syserror (); + if (err) + goto leave; + } + argc++; + } argv[argc] = strdup (a->arg); if (!argv[argc]) { @@ -1539,52 +1639,65 @@ start (engine_gpg_t gpg) if (!gpg) return gpg_error (GPG_ERR_INV_VALUE); - if (!gpg->file_name && !_gpgme_get_default_gpg_name ()) + if (!gpg->flags.use_gpgtar) + pgmname = gpg->file_name ? gpg->file_name : _gpgme_get_default_gpg_name (); + else + pgmname = _gpgme_get_default_gpgtar_name (); + if (!pgmname) return trace_gpg_error (GPG_ERR_INV_ENGINE); rc = gpgme_data_new (&gpg->diagnostics); if (rc) return rc; - rc = add_data_ext (gpg, gpg->diagnostics, -2, 1, 1); - if (rc) - return rc; + if (gpg->flags.use_gpgtar) + { + /* Read the diagnostics output from gpgtar's stderr. */ + rc = add_data (gpg, gpg->diagnostics, 2, 1); + if (rc) + return rc; + } + else + { + rc = add_data_ext (gpg, gpg->diagnostics, -2, 1, 1); + if (rc) + return rc; - rc = add_arg_ext (gpg, "--logger-fd", 1); - if (rc) - return rc; + rc = add_arg_ext (gpg, "--logger-fd", 1); + if (rc) + return rc; + } - { - char buf[25]; - _gpgme_io_fd2str (buf, sizeof (buf), gpg->status.fd[1]); - rc = add_arg_with_locp (gpg, buf, &gpg->status.arg_loc, 1); - if (rc) - return rc; - } + if (!gpg->flags.use_gpgtar || have_gpg_version (gpg, "2.4.1")) + { + /* Do not pass --status-fd to gpgtar for gpg < 2.4.1. */ + { + char buf[25]; + _gpgme_io_fd2str (buf, sizeof (buf), gpg->status.fd[1]); + rc = add_arg_with_locp (gpg, buf, &gpg->status.arg_loc, 1); + if (rc) + return rc; + } - rc = add_arg_ext (gpg, "--status-fd", 1); - if (rc) - return rc; + rc = add_arg_ext (gpg, "--status-fd", 1); + if (rc) + return rc; + } if (gpg->lc_ctype) { - rc = add_arg_ext (gpg, gpg->lc_ctype, 1); - if (!rc) - rc = add_arg_ext (gpg, "--lc-ctype", 1); + rc = add_gpg_arg_with_value (gpg, "--lc-ctype=", gpg->lc_ctype, 1); if (rc) return rc; } if (gpg->lc_messages) { - rc = add_arg_ext (gpg, gpg->lc_messages, 1); - if (!rc) - rc = add_arg_ext (gpg, "--lc-messages", 1); + rc = add_gpg_arg_with_value (gpg, "--lc-messages=", gpg->lc_messages, 1); if (rc) return rc; } - pgmname = gpg->file_name ? gpg->file_name : _gpgme_get_default_gpg_name (); rc = build_argv (gpg, pgmname); if (rc) return rc; @@ -2201,6 +2314,14 @@ gpg_encrypt (void *engine, gpgme_key_t recp[], const char *recpstring, engine_gpg_t gpg = engine; gpgme_error_t err = 0; + gpg->flags.use_gpgtar = !!(flags & GPGME_ENCRYPT_ARCHIVE); + + if (gpg->flags.use_gpgtar && !have_gpg_version (gpg, "2.3.5")) + return gpg_error (GPG_ERR_NOT_SUPPORTED); + + if (gpg->flags.use_gpgtar && (flags & GPGME_ENCRYPT_WRAP)) + return gpg_error (GPG_ERR_INV_VALUE); + if (recp || recpstring) err = add_arg (gpg, "--encrypt"); @@ -2208,7 +2329,7 @@ gpg_encrypt (void *engine, gpgme_key_t recp[], const char *recpstring, err = add_arg (gpg, "--symmetric"); if (!err && use_armor) - err = add_arg (gpg, "--armor"); + err = add_gpg_arg (gpg, "--armor"); if (!err && (flags & GPGME_ENCRYPT_WRAP)) { @@ -2218,31 +2339,31 @@ gpg_encrypt (void *engine, gpgme_key_t recp[], const char *recpstring, * the encryption would add an additional compression layer. * We better suppress that. */ flags |= GPGME_ENCRYPT_NO_COMPRESS; - err = add_arg (gpg, "--no-literal"); + err = add_gpg_arg (gpg, "--no-literal"); } if (!err && (flags & GPGME_ENCRYPT_NO_COMPRESS)) - err = add_arg (gpg, "--compress-algo=none"); + err = add_gpg_arg (gpg, "--compress-algo=none"); if (!err && (flags & GPGME_ENCRYPT_THROW_KEYIDS)) - err = add_arg (gpg, "--throw-keyids"); + err = add_gpg_arg (gpg, "--throw-keyids"); if (gpgme_data_get_encoding (plain) == GPGME_DATA_ENCODING_MIME && have_gpg_version (gpg, "2.1.14")) - err = add_arg (gpg, "--mimemode"); + err = add_gpg_arg (gpg, "--mimemode"); if (!err && gpg->flags.include_key_block) - err = add_arg (gpg, "--include-key-block"); + err = add_gpg_arg (gpg, "--include-key-block"); if (recp || recpstring) { /* If we know that all recipients are valid (full or ultimate trust) we can suppress further checks. */ if (!err && (flags & GPGME_ENCRYPT_ALWAYS_TRUST)) - err = add_arg (gpg, "--always-trust"); + err = add_gpg_arg (gpg, "--always-trust"); if (!err && (flags & GPGME_ENCRYPT_NO_ENCRYPT_TO)) - err = add_arg (gpg, "--no-encrypt-to"); + err = add_gpg_arg (gpg, "--no-encrypt-to"); if (!err && !recp && recpstring) err = append_args_from_recipients_string (gpg, flags, recpstring); @@ -2260,16 +2381,31 @@ gpg_encrypt (void *engine, gpgme_key_t recp[], const char *recpstring, if (gpgme_data_get_file_name (plain)) { if (!err) - err = add_arg (gpg, "--set-filename"); - if (!err) - err = add_arg (gpg, gpgme_data_get_file_name (plain)); + err = add_gpg_arg_with_value (gpg, "--set-filename", gpgme_data_get_file_name (plain), 0); + } + if (gpg->flags.use_gpgtar) + { + if (!err) + err = add_arg (gpg, "--files-from"); + if (!err) + err = add_arg (gpg, "-"); + if (!err) + err = add_arg (gpg, "--null"); + if (!err) + err = add_arg (gpg, "--utf8-strings"); + /* Pass the filenames to gpgtar's stdin. */ + if (!err) + err = add_data (gpg, plain, 0, 0); + } + else + { + if (!err) + err = add_input_size_hint (gpg, plain); + if (!err) + err = add_arg (gpg, "--"); + if (!err) + err = add_data (gpg, plain, -1, 0); } - if (!err) - err = add_input_size_hint (gpg, plain); - if (!err) - err = add_arg (gpg, "--"); - if (!err) - err = add_data (gpg, plain, -1, 0); if (!err) err = start (gpg); diff --git a/src/engine-gpgsm.c b/src/engine-gpgsm.c index 9ab05551..4632ef6c 100644 --- a/src/engine-gpgsm.c +++ b/src/engine-gpgsm.c @@ -1469,6 +1469,9 @@ gpgsm_encrypt (void *engine, gpgme_key_t recp[], const char *recpstring, if (!recp && !recpstring) /* Symmetric only */ return gpg_error (GPG_ERR_NOT_IMPLEMENTED); + if (flags & GPGME_ENCRYPT_ARCHIVE) + return gpg_error (GPG_ERR_NOT_IMPLEMENTED); + if ((flags & GPGME_ENCRYPT_NO_ENCRYPT_TO)) { err = gpgsm_assuan_simple_command (gpgsm, diff --git a/src/engine-uiserver.c b/src/engine-uiserver.c index 9fce1de4..232eb4b4 100644 --- a/src/engine-uiserver.c +++ b/src/engine-uiserver.c @@ -1145,6 +1145,9 @@ uiserver_encrypt (void *engine, gpgme_key_t recp[], const char *recpstring, else return gpgme_error (GPG_ERR_UNSUPPORTED_PROTOCOL); + if (flags & GPGME_ENCRYPT_ARCHIVE) + return gpg_error (GPG_ERR_NOT_IMPLEMENTED); + if (flags & GPGME_ENCRYPT_PREPARE) { if (!recp || plain || ciph) diff --git a/src/gpgme.h.in b/src/gpgme.h.in index 502d68cd..db1f8f13 100644 --- a/src/gpgme.h.in +++ b/src/gpgme.h.in @@ -1299,7 +1299,8 @@ typedef enum GPGME_ENCRYPT_SYMMETRIC = 32, GPGME_ENCRYPT_THROW_KEYIDS = 64, GPGME_ENCRYPT_WRAP = 128, - GPGME_ENCRYPT_WANT_ADDRESS = 256 + GPGME_ENCRYPT_WANT_ADDRESS = 256, + GPGME_ENCRYPT_ARCHIVE = 512 } gpgme_encrypt_flags_t; diff --git a/tests/run-encrypt.c b/tests/run-encrypt.c index 7b0e29a7..a38dabcb 100644 --- a/tests/run-encrypt.c +++ b/tests/run-encrypt.c @@ -107,6 +107,8 @@ show_usage (int ex) " --no-symkey-cache disable the use of that cache\n" " --wrap assume input is valid OpenPGP message\n" " --symmetric encrypt symmetric (OpenPGP only)\n" + " --archive encrypt given file or directory into an archive\n" + " --diagnostics print diagnostics\n" , stderr); exit (ex); } @@ -132,6 +134,7 @@ main (int argc, char **argv) gpgme_encrypt_flags_t flags = GPGME_ENCRYPT_ALWAYS_TRUST; gpgme_off_t offset; int no_symkey_cache = 0; + int diagnostics = 0; if (argc) { argc--; argv++; } @@ -225,6 +228,16 @@ main (int argc, char **argv) no_symkey_cache = 1; argc--; argv++; } + else if (!strcmp (*argv, "--archive")) + { + flags |= GPGME_ENCRYPT_ARCHIVE; + argc--; argv++; + } + else if (!strcmp (*argv, "--diagnostics")) + { + diagnostics = 1; + argc--; argv++; + } else if (!strncmp (*argv, "--", 2)) show_usage (1); @@ -269,48 +282,56 @@ main (int argc, char **argv) } keys[i] = NULL; - err = gpgme_data_new_from_file (&in, *argv, 1); - if (err) + if (flags & GPGME_ENCRYPT_ARCHIVE) { - fprintf (stderr, PGM ": error reading `%s': %s\n", - *argv, gpg_strerror (err)); - exit (1); + const char *path = *argv; + err = gpgme_data_new_from_mem (&in, path, strlen (path), 0); } - offset = gpgme_data_seek (in, 0, SEEK_END); - if (offset == (gpgme_off_t)(-1)) + else { - err = gpg_error_from_syserror (); - fprintf (stderr, PGM ": error seeking `%s': %s\n", - *argv, gpg_strerror (err)); - exit (1); - } - if (gpgme_data_seek (in, 0, SEEK_SET) == (gpgme_off_t)(-1)) - { - err = gpg_error_from_syserror (); - fprintf (stderr, PGM ": error seeking `%s': %s\n", - *argv, gpg_strerror (err)); - exit (1); - } - { - char numbuf[50]; - char *p; + err = gpgme_data_new_from_file (&in, *argv, 1); + if (err) + { + fprintf (stderr, PGM ": error reading `%s': %s\n", + *argv, gpg_strerror (err)); + exit (1); + } + offset = gpgme_data_seek (in, 0, SEEK_END); + if (offset == (gpgme_off_t)(-1)) + { + err = gpg_error_from_syserror (); + fprintf (stderr, PGM ": error seeking `%s': %s\n", + *argv, gpg_strerror (err)); + exit (1); + } + if (gpgme_data_seek (in, 0, SEEK_SET) == (gpgme_off_t)(-1)) + { + err = gpg_error_from_syserror (); + fprintf (stderr, PGM ": error seeking `%s': %s\n", + *argv, gpg_strerror (err)); + exit (1); + } + { + char numbuf[50]; + char *p; - p = numbuf + sizeof numbuf; - *--p = 0; - do - { - *--p = '0' + (offset % 10); - offset /= 10; + p = numbuf + sizeof numbuf; + *--p = 0; + do + { + *--p = '0' + (offset % 10); + offset /= 10; + } + while (offset); + err = gpgme_data_set_flag (in, "size-hint", p); + if (err) + { + fprintf (stderr, PGM ": error setting size-hint for `%s': %s\n", + *argv, gpg_strerror (err)); + exit (1); + } } - while (offset); - err = gpgme_data_set_flag (in, "size-hint", p); - if (err) - { - fprintf (stderr, PGM ": error setting size-hint for `%s': %s\n", - *argv, gpg_strerror (err)); - exit (1); - } - } + } err = gpgme_data_new (&out); fail_if_err (err); @@ -318,6 +339,28 @@ main (int argc, char **argv) err = gpgme_op_encrypt_ext (ctx, keycount ? keys : NULL, keystring, flags, in, out); result = gpgme_op_encrypt_result (ctx); + + if (diagnostics) + { + gpgme_data_t diag; + gpgme_error_t diag_err; + + gpgme_data_new (&diag); + diag_err = gpgme_op_getauditlog (ctx, diag, GPGME_AUDITLOG_DIAG); + if (diag_err) + { + fprintf (stderr, PGM ": getting diagnostics failed: %s\n", + gpgme_strerror (diag_err)); + } + else + { + fputs ("Begin Diagnostics:\n", stdout); + print_data (diag); + fputs ("End Diagnostics.\n", stdout); + } + gpgme_data_release (diag); + } + if (result) print_result (result); if (err) From aa201b0bb63d703a3794826fac2e2013fb0c34e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ingo=20Kl=C3=B6cker?= Date: Thu, 19 Jan 2023 10:29:56 +0100 Subject: [PATCH 08/24] core: Fix --set-filename argument * src/engine-gpg.c (gpg_encrypt): Append equal sign to argument name. -- GnuPG-bug-id: 6342 --- src/engine-gpg.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/engine-gpg.c b/src/engine-gpg.c index a681a220..a15975d7 100644 --- a/src/engine-gpg.c +++ b/src/engine-gpg.c @@ -2381,7 +2381,7 @@ gpg_encrypt (void *engine, gpgme_key_t recp[], const char *recpstring, if (gpgme_data_get_file_name (plain)) { if (!err) - err = add_gpg_arg_with_value (gpg, "--set-filename", gpgme_data_get_file_name (plain), 0); + err = add_gpg_arg_with_value (gpg, "--set-filename=", gpgme_data_get_file_name (plain), 0); } if (gpg->flags.use_gpgtar) { From 0ca45e48b45af1978027e6fe26c7e406fd5ec74b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ingo=20Kl=C3=B6cker?= Date: Thu, 19 Jan 2023 10:45:04 +0100 Subject: [PATCH 09/24] core: Support usage of gpgtar for creating a signed archive * src/gpgme.h.in (GPGME_SIG_MODE_ARCHIVE): New signature mode. * src/engine-gpg.c (append_args_from_sender, append_args_from_sig_notations): Use add_gpg_arg_with_value for gpg-only options with a value. (gpg_sign): Set use_gpgtar engine flag if GPGME_SIG_MODE_ARCHIVE mode is set. Check for new enough gpg. Use add_gpg_arg_with_value for gpg-only options with a value and add_gpg_arg for gpg-only options without a value. Set extra options for gpgtar and pass input data to stdin when using gpgtar. * src/sign.c (sign_start): Add GPGME_SIG_MODE_ARCHIVE as valid mode. * tests/run-sign.c (show_usage): New options --archive and --diagnostics. (main): Parse new options. Sign with gpgtar if --archive is given. Print stderr of gpg/gpgtar if --diagnostics is given. -- With this change the gpgme_op_sign* functions get support for creating a signed archive from files and/or directories passed as NUL-separated list in the "in" data with gpgtar. GnuPG-bug-id: 6342 --- src/engine-gpg.c | 70 ++++++++++++++++++++++++++++-------------------- src/gpgme.h.in | 3 ++- src/sign.c | 2 +- tests/run-sign.c | 53 ++++++++++++++++++++++++++++++++---- 4 files changed, 92 insertions(+), 36 deletions(-) diff --git a/src/engine-gpg.c b/src/engine-gpg.c index a15975d7..67cc6570 100644 --- a/src/engine-gpg.c +++ b/src/engine-gpg.c @@ -1982,16 +1982,11 @@ append_args_from_signers (engine_gpg_t gpg, gpgme_ctx_t ctx /* FIXME */) static gpgme_error_t append_args_from_sender (engine_gpg_t gpg, gpgme_ctx_t ctx) { - gpgme_error_t err; + gpgme_error_t err = 0; if (ctx->sender && have_gpg_version (gpg, "2.1.15")) - { - err = add_arg (gpg, "--sender"); - if (!err) - err = add_arg (gpg, ctx->sender); - } - else - err = 0; + err = add_gpg_arg_with_value (gpg, "--sender=", ctx->sender, 0); + return err; } @@ -2045,14 +2040,12 @@ append_args_from_sig_notations (engine_gpg_t gpg, gpgme_ctx_t ctx /* FIXME */, if (!err) { if ((flags & NOTATION_FLAG_SET)) - err = add_arg (gpg, "--set-notation"); + err = add_gpg_arg_with_value (gpg, "--set-notation=", arg, 0); else if ((flags & NOTATION_FLAG_CERT)) - err = add_arg (gpg, "--cert-notation"); + err = add_gpg_arg_with_value (gpg, "--cert-notation=", arg, 0); else - err = add_arg (gpg, "--sig-notation"); + err = add_gpg_arg_with_value (gpg, "--sig-notation=", arg, 0); } - if (!err) - err = add_arg (gpg, arg); if (arg) free (arg); @@ -2079,9 +2072,7 @@ append_args_from_sig_notations (engine_gpg_t gpg, gpgme_ctx_t ctx /* FIXME */, value = notation->value; if (!err) - err = add_arg (gpg, "--sig-policy-url"); - if (!err) - err = add_arg (gpg, value); + err = add_gpg_arg_with_value (gpg, "--sig-policy-url=", value, 0); if (value != notation->value) free (value); @@ -3540,6 +3531,11 @@ gpg_sign (void *engine, gpgme_data_t in, gpgme_data_t out, (void)include_certs; + gpg->flags.use_gpgtar = mode == GPGME_SIG_MODE_ARCHIVE; + + if (gpg->flags.use_gpgtar && !have_gpg_version (gpg, "2.3.5")) + return gpg_error (GPG_ERR_NOT_SUPPORTED); + if (mode == GPGME_SIG_MODE_CLEAR) err = add_arg (gpg, "--clearsign"); else @@ -3548,19 +3544,19 @@ gpg_sign (void *engine, gpgme_data_t in, gpgme_data_t out, if (!err && mode == GPGME_SIG_MODE_DETACH) err = add_arg (gpg, "--detach"); if (!err && use_armor) - err = add_arg (gpg, "--armor"); + err = add_gpg_arg (gpg, "--armor"); if (!err) { if (gpgme_data_get_encoding (in) == GPGME_DATA_ENCODING_MIME && have_gpg_version (gpg, "2.1.14")) - err = add_arg (gpg, "--mimemode"); + err = add_gpg_arg (gpg, "--mimemode"); else if (use_textmode) - err = add_arg (gpg, "--textmode"); + err = add_gpg_arg (gpg, "--textmode"); } } if (!err && gpg->flags.include_key_block) - err = add_arg (gpg, "--include-key-block"); + err = add_gpg_arg (gpg, "--include-key-block"); if (!err) err = append_args_from_signers (gpg, ctx); if (!err) @@ -3571,18 +3567,34 @@ gpg_sign (void *engine, gpgme_data_t in, gpgme_data_t out, if (gpgme_data_get_file_name (in)) { if (!err) - err = add_arg (gpg, "--set-filename"); - if (!err) - err = add_arg (gpg, gpgme_data_get_file_name (in)); + err = add_gpg_arg_with_value (gpg, "--set-filename=", gpgme_data_get_file_name (in), 0); } /* Tell the gpg object about the data. */ - if (!err) - err = add_input_size_hint (gpg, in); - if (!err) - err = add_arg (gpg, "--"); - if (!err) - err = add_data (gpg, in, -1, 0); + if (gpg->flags.use_gpgtar) + { + if (!err) + err = add_arg (gpg, "--files-from"); + if (!err) + err = add_arg (gpg, "-"); + if (!err) + err = add_arg (gpg, "--null"); + if (!err) + err = add_arg (gpg, "--utf8-strings"); + /* Pass the filenames to gpgtar's stdin. */ + if (!err) + err = add_data (gpg, in, 0, 0); + } + else + { + if (!err) + err = add_input_size_hint (gpg, in); + if (!err) + err = add_arg (gpg, "--"); + if (!err) + err = add_data (gpg, in, -1, 0); + } + if (!err) err = add_data (gpg, out, 1, 1); diff --git a/src/gpgme.h.in b/src/gpgme.h.in index db1f8f13..fabba257 100644 --- a/src/gpgme.h.in +++ b/src/gpgme.h.in @@ -308,7 +308,8 @@ typedef enum { GPGME_SIG_MODE_NORMAL = 0, GPGME_SIG_MODE_DETACH = 1, - GPGME_SIG_MODE_CLEAR = 2 + GPGME_SIG_MODE_CLEAR = 2, + GPGME_SIG_MODE_ARCHIVE = 3 } gpgme_sig_mode_t; diff --git a/src/sign.c b/src/sign.c index 31db9bde..7617d27f 100644 --- a/src/sign.c +++ b/src/sign.c @@ -447,7 +447,7 @@ sign_start (gpgme_ctx_t ctx, int synchronous, gpgme_data_t plain, return err; if (mode != GPGME_SIG_MODE_NORMAL && mode != GPGME_SIG_MODE_DETACH - && mode != GPGME_SIG_MODE_CLEAR) + && mode != GPGME_SIG_MODE_CLEAR && mode != GPGME_SIG_MODE_ARCHIVE) return gpg_error (GPG_ERR_INV_VALUE); if (!plain) diff --git a/tests/run-sign.c b/tests/run-sign.c index 37211d72..73a970d0 100644 --- a/tests/run-sign.c +++ b/tests/run-sign.c @@ -87,6 +87,8 @@ show_usage (int ex) " --sender MBOX use MBOX as sender address\n" " --include-key-block use this option with gpg\n" " --clear create a clear text signature\n" + " --archive create a signed archive with the given file or directory\n" + " --diagnostics print diagnostics\n" , stderr); exit (ex); } @@ -106,6 +108,7 @@ main (int argc, char **argv) int print_status = 0; int use_loopback = 0; int include_key_block = 0; + int diagnostics = 0; const char *sender = NULL; const char *s; @@ -178,6 +181,16 @@ main (int argc, char **argv) sigmode = GPGME_SIG_MODE_CLEAR; argc--; argv++; } + else if (!strcmp (*argv, "--archive")) + { + sigmode = GPGME_SIG_MODE_ARCHIVE; + argc--; argv++; + } + else if (!strcmp (*argv, "--diagnostics")) + { + diagnostics = 1; + argc--; argv++; + } else if (!strncmp (*argv, "--", 2)) show_usage (1); @@ -236,12 +249,20 @@ main (int argc, char **argv) } } - err = gpgme_data_new_from_file (&in, *argv, 1); - if (err) + if (sigmode == GPGME_SIG_MODE_ARCHIVE) { - fprintf (stderr, PGM ": error reading `%s': %s\n", - *argv, gpg_strerror (err)); - exit (1); + const char *path = *argv; + err = gpgme_data_new_from_mem (&in, path, strlen (path), 0); + } + else + { + err = gpgme_data_new_from_file (&in, *argv, 1); + if (err) + { + fprintf (stderr, PGM ": error reading `%s': %s\n", + *argv, gpg_strerror (err)); + exit (1); + } } err = gpgme_data_new (&out); @@ -249,6 +270,28 @@ main (int argc, char **argv) err = gpgme_op_sign (ctx, in, out, sigmode); result = gpgme_op_sign_result (ctx); + + if (diagnostics) + { + gpgme_data_t diag; + gpgme_error_t diag_err; + + gpgme_data_new (&diag); + diag_err = gpgme_op_getauditlog (ctx, diag, GPGME_AUDITLOG_DIAG); + if (diag_err) + { + fprintf (stderr, PGM ": getting diagnostics failed: %s\n", + gpgme_strerror (diag_err)); + } + else + { + fputs ("Begin Diagnostics:\n", stdout); + print_data (diag); + fputs ("End Diagnostics.\n", stdout); + } + gpgme_data_release (diag); + } + if (result) print_result (result, sigmode); if (err) From 29cfcd316d1bfc98ca663369a9503cc169dd3447 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ingo=20Kl=C3=B6cker?= Date: Thu, 19 Jan 2023 11:08:42 +0100 Subject: [PATCH 10/24] core: Support usage of gpgtar for creating an encrypted signed archive * src/engine-gpg.c (gpg_encrypt_sign): Set use_gpgtar engine flag if GPGME_ENCRYPT_ARCHIVE mode is set. Check for new enough gpg. Use add_gpg_arg_with_value for gpg-only options with a value and add_gpg_arg for gpg-only options without a value. Set extra options for gpgtar and pass input data to stdin when using gpgtar. * tests/run-encrypt.c (print_result): Rename to print_encrypt_result. Print header. (print_sign_result): New. (show_usage): New option --sign. (main): Parse new option. Sign and encrypt --sign is given. Print results of signing additionally to results of encryption. -- With this change the gpgme_op_encrypt_sign* functions get support for creating an encrypted and signed archive from files and/or directories passed as NUL-separated list in the "plain" data with gpgtar. GnuPG-bug-id: 6342 --- src/engine-gpg.c | 52 +++++++++++++++++++++++++++++-------------- tests/run-encrypt.c | 54 +++++++++++++++++++++++++++++++++++++++------ 2 files changed, 83 insertions(+), 23 deletions(-) diff --git a/src/engine-gpg.c b/src/engine-gpg.c index 67cc6570..16dfcc48 100644 --- a/src/engine-gpg.c +++ b/src/engine-gpg.c @@ -2415,6 +2415,11 @@ gpg_encrypt_sign (void *engine, gpgme_key_t recp[], engine_gpg_t gpg = engine; gpgme_error_t err = 0; + gpg->flags.use_gpgtar = !!(flags & GPGME_ENCRYPT_ARCHIVE); + + if (gpg->flags.use_gpgtar && !have_gpg_version (gpg, "2.3.5")) + return gpg_error (GPG_ERR_NOT_SUPPORTED); + if (recp || recpstring) err = add_arg (gpg, "--encrypt"); @@ -2424,30 +2429,30 @@ gpg_encrypt_sign (void *engine, gpgme_key_t recp[], if (!err) err = add_arg (gpg, "--sign"); if (!err && use_armor) - err = add_arg (gpg, "--armor"); + err = add_gpg_arg (gpg, "--armor"); if (!err && (flags & GPGME_ENCRYPT_NO_COMPRESS)) - err = add_arg (gpg, "--compress-algo=none"); + err = add_gpg_arg (gpg, "--compress-algo=none"); if (!err && (flags & GPGME_ENCRYPT_THROW_KEYIDS)) - err = add_arg (gpg, "--throw-keyids"); + err = add_gpg_arg (gpg, "--throw-keyids"); if (gpgme_data_get_encoding (plain) == GPGME_DATA_ENCODING_MIME && have_gpg_version (gpg, "2.1.14")) - err = add_arg (gpg, "--mimemode"); + err = add_gpg_arg (gpg, "--mimemode"); if (!err && gpg->flags.include_key_block) - err = add_arg (gpg, "--include-key-block"); + err = add_gpg_arg (gpg, "--include-key-block"); if (recp || recpstring) { /* If we know that all recipients are valid (full or ultimate trust) we can suppress further checks. */ if (!err && (flags & GPGME_ENCRYPT_ALWAYS_TRUST)) - err = add_arg (gpg, "--always-trust"); + err = add_gpg_arg (gpg, "--always-trust"); if (!err && (flags & GPGME_ENCRYPT_NO_ENCRYPT_TO)) - err = add_arg (gpg, "--no-encrypt-to"); + err = add_gpg_arg (gpg, "--no-encrypt-to"); if (!err && !recp && recpstring) err = append_args_from_recipients_string (gpg, flags, recpstring); @@ -2474,16 +2479,31 @@ gpg_encrypt_sign (void *engine, gpgme_key_t recp[], if (gpgme_data_get_file_name (plain)) { if (!err) - err = add_arg (gpg, "--set-filename"); - if (!err) - err = add_arg (gpg, gpgme_data_get_file_name (plain)); + err = add_gpg_arg_with_value (gpg, "--set-filename=", gpgme_data_get_file_name (plain), 0); + } + if (gpg->flags.use_gpgtar) + { + if (!err) + err = add_arg (gpg, "--files-from"); + if (!err) + err = add_arg (gpg, "-"); + if (!err) + err = add_arg (gpg, "--null"); + if (!err) + err = add_arg (gpg, "--utf8-strings"); + /* Pass the filenames to gpgtar's stdin. */ + if (!err) + err = add_data (gpg, plain, 0, 0); + } + else + { + if (!err) + err = add_input_size_hint (gpg, plain); + if (!err) + err = add_arg (gpg, "--"); + if (!err) + err = add_data (gpg, plain, -1, 0); } - if (!err) - err = add_input_size_hint (gpg, plain); - if (!err) - err = add_arg (gpg, "--"); - if (!err) - err = add_data (gpg, plain, -1, 0); if (!err) err = start (gpg); diff --git a/tests/run-encrypt.c b/tests/run-encrypt.c index a38dabcb..82a2cead 100644 --- a/tests/run-encrypt.c +++ b/tests/run-encrypt.c @@ -77,10 +77,11 @@ progress_cb (void *opaque, const char *what, int type, int current, int total) static void -print_result (gpgme_encrypt_result_t result) +print_encrypt_result (gpgme_encrypt_result_t result) { gpgme_invalid_key_t invkey; + printf ("\nEncryption results\n"); for (invkey = result->invalid_recipients; invkey; invkey = invkey->next) printf ("Encryption key `%s' not used: %s <%s>\n", nonnull (invkey->fpr), @@ -88,6 +89,30 @@ print_result (gpgme_encrypt_result_t result) } +static void +print_sign_result (gpgme_sign_result_t result) +{ + gpgme_invalid_key_t invkey; + gpgme_new_signature_t sig; + + printf ("\nSigning results\n"); + for (invkey = result->invalid_signers; invkey; invkey = invkey->next) + printf ("Signing key `%s' not used: %s <%s>\n", + nonnull (invkey->fpr), + gpg_strerror (invkey->reason), gpg_strsource (invkey->reason)); + + for (sig = result->signatures; sig; sig = sig->next) + { + printf ("Key fingerprint: %s\n", nonnull (sig->fpr)); + printf ("Signature type : %d\n", sig->type); + printf ("Public key algo: %d\n", sig->pubkey_algo); + printf ("Hash algo .....: %d\n", sig->hash_algo); + printf ("Creation time .: %ld\n", sig->timestamp); + printf ("Sig class .....: 0x%u\n", sig->sig_class); + } +} + + static int show_usage (int ex) @@ -95,6 +120,7 @@ show_usage (int ex) fputs ("usage: " PGM " [options] FILE\n\n" "Options:\n" " --verbose run in verbose mode\n" + " --sign sign data before encryption\n" " --status print status lines from the backend\n" " --progress print progress info\n" " --openpgp use the OpenPGP protocol (default)\n" @@ -122,7 +148,8 @@ main (int argc, char **argv) gpgme_ctx_t ctx; gpgme_protocol_t protocol = GPGME_PROTOCOL_OpenPGP; gpgme_data_t in, out; - gpgme_encrypt_result_t result; + gpgme_encrypt_result_t encrypt_result; + gpgme_sign_result_t sign_result; int print_status = 0; int print_progress = 0; int use_loopback = 0; @@ -135,6 +162,7 @@ main (int argc, char **argv) gpgme_off_t offset; int no_symkey_cache = 0; int diagnostics = 0; + int sign = 0; if (argc) { argc--; argv++; } @@ -157,6 +185,11 @@ main (int argc, char **argv) verbose = 1; argc--; argv++; } + else if (!strcmp (*argv, "--sign")) + { + sign = 1; + argc--; argv++; + } else if (!strcmp (*argv, "--status")) { print_status = 1; @@ -336,9 +369,12 @@ main (int argc, char **argv) err = gpgme_data_new (&out); fail_if_err (err); - err = gpgme_op_encrypt_ext (ctx, keycount ? keys : NULL, keystring, - flags, in, out); - result = gpgme_op_encrypt_result (ctx); + if (sign) + err = gpgme_op_encrypt_sign_ext (ctx, keycount ? keys : NULL, keystring, + flags, in, out); + else + err = gpgme_op_encrypt_ext (ctx, keycount ? keys : NULL, keystring, + flags, in, out); if (diagnostics) { @@ -361,8 +397,12 @@ main (int argc, char **argv) gpgme_data_release (diag); } - if (result) - print_result (result); + sign_result = gpgme_op_sign_result (ctx); + if (sign_result) + print_sign_result (sign_result); + encrypt_result = gpgme_op_encrypt_result (ctx); + if (encrypt_result) + print_encrypt_result (encrypt_result); if (err) { fprintf (stderr, PGM ": encrypting failed: %s\n", gpg_strerror (err)); From 4c872b674189d84d936d6c5cbe18b27c90c162e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ingo=20Kl=C3=B6cker?= Date: Thu, 19 Jan 2023 12:08:35 +0100 Subject: [PATCH 11/24] doc: Update NEWS and API documentation -- GnuPG-bug-id: 6342 --- NEWS | 8 ++++++++ doc/gpgme.texi | 27 +++++++++++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/NEWS b/NEWS index b5fb0a73..3ccb371f 100644 --- a/NEWS +++ b/NEWS @@ -5,6 +5,12 @@ Noteworthy changes in version 1.18.1 (unreleased) * Optionally, build QGpgME for Qt 6 + * Support component "gpgtar-name" in gpgme_get_dirinfo. [T6342] + + * Extended gpgme_op_encrypt*, gpgme_op_encrypt_sign*, and + gpgme_op_sign* to allow creating an encrypted and/or signed + archive. [T6342] + * cpp: Handle error when trying to sign expired keys. [T6155] * cpp, qt: Fix building with C++11. [T6141] @@ -24,6 +30,8 @@ Noteworthy changes in version 1.18.1 (unreleased) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ gpgme_get_ctx_flag EXTENDED: New flag 'no-auto-check-trustdb'. gpgme_set_ctx_flag EXTENDED: New flag 'no-auto-check-trustdb'. + GPGME_ENCRYPT_ARCHIVE NEW. + GPGME_SIG_MODE_ARCHIVE NEW. cpp: GpgGenCardKeyInteractor::Curve NEW. cpp: GpgGenCardKeyInteractor::setCurve NEW. qt: ListAllKeysJob::Option NEW. diff --git a/doc/gpgme.texi b/doc/gpgme.texi index 2532f800..8b6106af 100644 --- a/doc/gpgme.texi +++ b/doc/gpgme.texi @@ -6238,6 +6238,13 @@ A detached signature is made. @item GPGME_SIG_MODE_CLEAR A clear text signature is made. The @acronym{ASCII} armor and text mode settings of the context are ignored. + +@item GPGME_SIG_MODE_ARCHIVE +@since{1.19.0} + +A signed archive is created from the given files and directories. This +feature is currently only supported for the OpenPGP crypto engine. + @end table @end deftp @@ -6249,6 +6256,12 @@ the data object @var{plain} and returns it in the data object specified for @var{sig}), the text mode attributes set for the context @var{ctx} and the requested signature mode @var{mode}. +If signature mode @code(GPGME_SIG_MODE_ARCHIVE) is requested, then a +signed archive is created from the files and directories given as +NUL-separated list in the data object @var{plain} and returned in the +data object @var{sig}. The paths of the files and directories have to +be given as paths relative to the current working directory. + After the operation completed successfully, the result can be retrieved with @code{gpgme_op_sign_result}. @@ -6420,6 +6433,12 @@ ciphertext created is determined by the @acronym{ASCII} armor (or, if that is not set, by the encoding specified for @var{cipher}) and the text mode attributes set for the context @var{ctx}. +If the flag @code(GPGME_ENCRYPT_ARCHIVE) is set, then an encrypted +archive is created from the files and directories given as NUL-separated +list in the data object @var{plain} and returned in the data object +@var{cipher}. The paths of the files and directories have to +be given as paths relative to the current working directory. + @var{recp} must be a @code{NULL}-terminated array of keys. The user must keep references for all keys during the whole duration of the call (but see @code{gpgme_op_encrypt_start} for the requirements with @@ -6495,6 +6514,14 @@ of now the key must be specified using the @var{recpstring} argument of the extended encrypt functions. This feature is currently only supported for the OpenPGP crypto engine. +@item GPGME_ENCRYPT_ARCHIVE +@since{1.19.0} + +The @code{GPGME_ENCRYPT_ARCHIVE} symbol specifies that the input is a +NUL-separated list of file paths and directory paths that shall be +encrypted into an archive. This feature is currently only supported +for the OpenPGP crypto engine. + @end table If @code{GPG_ERR_UNUSABLE_PUBKEY} is returned, some recipients in From 419adf41afa40e5b34f646f2cb16d8aebda20048 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ingo=20Kl=C3=B6cker?= Date: Mon, 23 Jan 2023 12:17:22 +0100 Subject: [PATCH 12/24] core: Allow setting the base directory when creating an archive * src/engine-gpg.c (gpg_encrypt, gpg_encrypt_sign, gpg_sign): Pass file name set in data with --directory option to gpgtar. * tests/run-encrypt.c (show_usage): New option --directory. (main): Parse new option. Set file name of input data to option value. * tests/run-sign.c (show_usage): New option --directory. (main): Parse new option. Set file name of input data to option value. -- GnuPG-bug-id: 6342 --- doc/gpgme.texi | 11 +++++++++-- src/engine-gpg.c | 46 +++++++++++++++++++++++++++++---------------- tests/run-encrypt.c | 16 ++++++++++++++++ tests/run-sign.c | 16 ++++++++++++++++ 4 files changed, 71 insertions(+), 18 deletions(-) diff --git a/doc/gpgme.texi b/doc/gpgme.texi index 8b6106af..b8b90bb1 100644 --- a/doc/gpgme.texi +++ b/doc/gpgme.texi @@ -2199,6 +2199,11 @@ associated with the data object. The file name will be stored in the output when encrypting or signing the data and will be returned to the user when decrypting or verifying the output data. +If a signed or encrypted archive is created, then the file name will be +interpreted as the base directory for the relative paths of the files and +directories to put into the archive. This corresponds to the --directory +option of gpgtar. + The function returns the error code @code{GPG_ERR_INV_VALUE} if @var{dh} is not a valid pointer and @code{GPG_ERR_ENOMEM} if not enough memory is available. @@ -6260,7 +6265,8 @@ If signature mode @code(GPGME_SIG_MODE_ARCHIVE) is requested, then a signed archive is created from the files and directories given as NUL-separated list in the data object @var{plain} and returned in the data object @var{sig}. The paths of the files and directories have to -be given as paths relative to the current working directory. +be given as paths relative to the current working directory or relative +to the base directory set with @code{gpgme_data_set_file_name}. After the operation completed successfully, the result can be retrieved with @code{gpgme_op_sign_result}. @@ -6437,7 +6443,8 @@ If the flag @code(GPGME_ENCRYPT_ARCHIVE) is set, then an encrypted archive is created from the files and directories given as NUL-separated list in the data object @var{plain} and returned in the data object @var{cipher}. The paths of the files and directories have to -be given as paths relative to the current working directory. +be given as paths relative to the current working directory or relative +to the base directory set with @code{gpgme_data_set_file_name}. @var{recp} must be a @code{NULL}-terminated array of keys. The user must keep references for all keys during the whole duration of the diff --git a/src/engine-gpg.c b/src/engine-gpg.c index 16dfcc48..06eb3e18 100644 --- a/src/engine-gpg.c +++ b/src/engine-gpg.c @@ -2369,13 +2369,15 @@ gpg_encrypt (void *engine, gpgme_key_t recp[], const char *recpstring, err = add_arg (gpg, "-"); if (!err) err = add_data (gpg, ciph, 1, 1); - if (gpgme_data_get_file_name (plain)) - { - if (!err) - err = add_gpg_arg_with_value (gpg, "--set-filename=", gpgme_data_get_file_name (plain), 0); - } if (gpg->flags.use_gpgtar) { + const char *file_name = gpgme_data_get_file_name (plain); + if (!err && file_name) + { + err = add_arg (gpg, "--directory"); + if (!err) + err = add_arg (gpg, file_name); + } if (!err) err = add_arg (gpg, "--files-from"); if (!err) @@ -2390,6 +2392,9 @@ gpg_encrypt (void *engine, gpgme_key_t recp[], const char *recpstring, } else { + const char *file_name = gpgme_data_get_file_name (plain); + if (!err && file_name) + err = add_gpg_arg_with_value (gpg, "--set-filename=", file_name, 0); if (!err) err = add_input_size_hint (gpg, plain); if (!err) @@ -2476,13 +2481,15 @@ gpg_encrypt_sign (void *engine, gpgme_key_t recp[], err = add_arg (gpg, "-"); if (!err) err = add_data (gpg, ciph, 1, 1); - if (gpgme_data_get_file_name (plain)) - { - if (!err) - err = add_gpg_arg_with_value (gpg, "--set-filename=", gpgme_data_get_file_name (plain), 0); - } if (gpg->flags.use_gpgtar) { + const char *file_name = gpgme_data_get_file_name (plain); + if (!err && file_name) + { + err = add_arg (gpg, "--directory"); + if (!err) + err = add_arg (gpg, file_name); + } if (!err) err = add_arg (gpg, "--files-from"); if (!err) @@ -2497,6 +2504,9 @@ gpg_encrypt_sign (void *engine, gpgme_key_t recp[], } else { + const char *file_name = gpgme_data_get_file_name (plain); + if (!err && file_name) + err = add_gpg_arg_with_value (gpg, "--set-filename=", file_name, 0); if (!err) err = add_input_size_hint (gpg, plain); if (!err) @@ -3584,15 +3594,16 @@ gpg_sign (void *engine, gpgme_data_t in, gpgme_data_t out, if (!err) err = append_args_from_sig_notations (gpg, ctx, NOTATION_FLAG_SIG); - if (gpgme_data_get_file_name (in)) - { - if (!err) - err = add_gpg_arg_with_value (gpg, "--set-filename=", gpgme_data_get_file_name (in), 0); - } - /* Tell the gpg object about the data. */ if (gpg->flags.use_gpgtar) { + const char *file_name = gpgme_data_get_file_name (in); + if (!err && file_name) + { + err = add_arg (gpg, "--directory"); + if (!err) + err = add_arg (gpg, file_name); + } if (!err) err = add_arg (gpg, "--files-from"); if (!err) @@ -3607,6 +3618,9 @@ gpg_sign (void *engine, gpgme_data_t in, gpgme_data_t out, } else { + const char *file_name = gpgme_data_get_file_name (in); + if (!err && file_name) + err = add_gpg_arg_with_value (gpg, "--set-filename=", file_name, 0); if (!err) err = add_input_size_hint (gpg, in); if (!err) diff --git a/tests/run-encrypt.c b/tests/run-encrypt.c index 82a2cead..2d1c6e9d 100644 --- a/tests/run-encrypt.c +++ b/tests/run-encrypt.c @@ -134,6 +134,7 @@ show_usage (int ex) " --wrap assume input is valid OpenPGP message\n" " --symmetric encrypt symmetric (OpenPGP only)\n" " --archive encrypt given file or directory into an archive\n" + " --directory DIR switch to directory DIR before encrypting into an archive\n" " --diagnostics print diagnostics\n" , stderr); exit (ex); @@ -157,6 +158,7 @@ main (int argc, char **argv) gpgme_key_t keys[10+1]; int keycount = 0; char *keystring = NULL; + const char *directory = NULL; int i; gpgme_encrypt_flags_t flags = GPGME_ENCRYPT_ALWAYS_TRUST; gpgme_off_t offset; @@ -266,6 +268,14 @@ main (int argc, char **argv) flags |= GPGME_ENCRYPT_ARCHIVE; argc--; argv++; } + else if (!strcmp (*argv, "--directory")) + { + argc--; argv++; + if (!argc) + show_usage (1); + directory = *argv; + argc--; argv++; + } else if (!strcmp (*argv, "--diagnostics")) { diagnostics = 1; @@ -319,6 +329,12 @@ main (int argc, char **argv) { const char *path = *argv; err = gpgme_data_new_from_mem (&in, path, strlen (path), 0); + fail_if_err (err); + if (directory) + { + err = gpgme_data_set_file_name (in, directory); + fail_if_err (err); + } } else { diff --git a/tests/run-sign.c b/tests/run-sign.c index 73a970d0..84d7c070 100644 --- a/tests/run-sign.c +++ b/tests/run-sign.c @@ -88,6 +88,7 @@ show_usage (int ex) " --include-key-block use this option with gpg\n" " --clear create a clear text signature\n" " --archive create a signed archive with the given file or directory\n" + " --directory DIR switch to directory DIR before creating the archive\n" " --diagnostics print diagnostics\n" , stderr); exit (ex); @@ -101,6 +102,7 @@ main (int argc, char **argv) gpgme_error_t err; gpgme_ctx_t ctx; const char *key_string = NULL; + const char *directory = NULL; gpgme_protocol_t protocol = GPGME_PROTOCOL_OpenPGP; gpgme_sig_mode_t sigmode = GPGME_SIG_MODE_NORMAL; gpgme_data_t in, out; @@ -186,6 +188,14 @@ main (int argc, char **argv) sigmode = GPGME_SIG_MODE_ARCHIVE; argc--; argv++; } + else if (!strcmp (*argv, "--directory")) + { + argc--; argv++; + if (!argc) + show_usage (1); + directory = *argv; + argc--; argv++; + } else if (!strcmp (*argv, "--diagnostics")) { diagnostics = 1; @@ -253,6 +263,12 @@ main (int argc, char **argv) { const char *path = *argv; err = gpgme_data_new_from_mem (&in, path, strlen (path), 0); + fail_if_err (err); + if (directory) + { + err = gpgme_data_set_file_name (in, directory); + fail_if_err (err); + } } else { From 95ea3bf831aeac201108876e73750692aa1ba3f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ingo=20Kl=C3=B6cker?= Date: Tue, 24 Jan 2023 11:05:52 +0100 Subject: [PATCH 13/24] core: Support usage of gpgtar for decrypting an encrypted archive * src/gpgme.h.in (GPGME_DECRYPT_ARCHIVE): New decryption flag. * src/engine-gpg.c (gpg_decrypt): Set use_gpgtar engine flag if GPGME_DECRYPT_ARCHIVE flag is set. Check for new enough gpg and incompatible flags. Use add_gpg_arg_with_value for gpg-only options with a value and add_gpg_arg for gpg-only options without a value. Set extra options for gpgtar and pass input data to stdin when using gpgtar. * tests/run-decrypt.c (show_usage): New options --archive and --directory. (main): Parse new options. Decrypt with gpgtar if --archive is given. Set file name of output data to value of --directory option. -- GnuPG-bug-id: 6342 --- NEWS | 4 +++ doc/gpgme.texi | 17 ++++++++++-- src/engine-gpg.c | 67 +++++++++++++++++++++++++++++++-------------- src/gpgme.h.in | 1 + tests/run-decrypt.c | 26 ++++++++++++++++++ 5 files changed, 93 insertions(+), 22 deletions(-) diff --git a/NEWS b/NEWS index 3ccb371f..824faeb7 100644 --- a/NEWS +++ b/NEWS @@ -11,6 +11,9 @@ Noteworthy changes in version 1.18.1 (unreleased) gpgme_op_sign* to allow creating an encrypted and/or signed archive. [T6342] + * Extended gpgme_op_decrypt* and gpgme_op_decrypt_verify* to allow + extracting an encrypted and/or signed archive. [T6342] + * cpp: Handle error when trying to sign expired keys. [T6155] * cpp, qt: Fix building with C++11. [T6141] @@ -30,6 +33,7 @@ Noteworthy changes in version 1.18.1 (unreleased) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ gpgme_get_ctx_flag EXTENDED: New flag 'no-auto-check-trustdb'. gpgme_set_ctx_flag EXTENDED: New flag 'no-auto-check-trustdb'. + GPGME_DECRYPT_ARCHIVE NEW. GPGME_ENCRYPT_ARCHIVE NEW. GPGME_SIG_MODE_ARCHIVE NEW. cpp: GpgGenCardKeyInteractor::Curve NEW. diff --git a/doc/gpgme.texi b/doc/gpgme.texi index b8b90bb1..184b1afe 100644 --- a/doc/gpgme.texi +++ b/doc/gpgme.texi @@ -3172,8 +3172,8 @@ The string given in @var{value} is passed to the GnuPG engine to override the session key for decryption. The format of that session key is specific to GnuPG and can be retrieved during a decrypt operation when the context flag "export-session-key" is enabled. Please be aware that -using this feature with GnuPG < 2.1.16 will leak the session key on -many platforms via ps(1). +using this feature with GnuPG < 2.1.16 or when decrypting an archive +will leak the session key on many platforms via ps(1). @item "auto-key-retrieve" Setting the @var{value} to "1" asks the backend to automatically @@ -5589,6 +5589,12 @@ The function @code{gpgme_op_decrypt} decrypts the ciphertext in the data object @var{cipher} and stores it into the data object @var{plain}. +If the flag @code(GPGME_DECRYPT_ARCHIVE) is set, then an encrypted +archive in the data object @var{cipher} is decrypted and extracted. +The content of the archive is extracted into a directory named +@code{GPGARCH_n_} (where @code{n} is a number) or into the directory +set with @code{gpgme_data_set_file_name} for the data object @var{plain}. + The function returns the error code @code{GPG_ERR_NO_ERROR} if the ciphertext could be decrypted successfully, @code{GPG_ERR_INV_VALUE} if @var{ctx}, @var{cipher} or @var{plain} is not a valid pointer, @@ -5632,6 +5638,13 @@ multiple of the following bit values: The @code{GPGME_DECRYPT_VERIFY} symbol specifies that this function shall exactly act as @code{gpgme_op_decrypt_verify}. +@item GPGME_DECRYPT_ARCHIVE +@since{1.19.0} + +The @code{GPGME_DECRYPT_ARCHIVE} symbol specifies that the input is an +encrypted archive that shall be decrypted and extracted. This feature +is currently only supported for the OpenPGP crypto engine. + @item GPGME_DECRYPT_UNWRAP @since{1.8.0} diff --git a/src/engine-gpg.c b/src/engine-gpg.c index 06eb3e18..86422112 100644 --- a/src/engine-gpg.c +++ b/src/engine-gpg.c @@ -310,7 +310,8 @@ add_gpg_arg (engine_gpg_t gpg, const char *arg) } static gpgme_error_t -add_gpg_arg_with_value (engine_gpg_t gpg, const char *arg, const char *value, int front) +add_gpg_arg_with_value (engine_gpg_t gpg, const char *arg, const char *value, + int front) { return _add_arg (gpg, arg, value, strlen (value), front, NULL, 1); } @@ -1829,6 +1830,14 @@ gpg_decrypt (void *engine, engine_gpg_t gpg = engine; gpgme_error_t err; + gpg->flags.use_gpgtar = !!(flags & GPGME_DECRYPT_ARCHIVE); + + if (gpg->flags.use_gpgtar && !have_gpg_version (gpg, "2.3.5")) + return gpg_error (GPG_ERR_NOT_SUPPORTED); + + if (gpg->flags.use_gpgtar && (flags & GPGME_DECRYPT_UNWRAP)) + return gpg_error (GPG_ERR_INV_VALUE); + err = add_arg (gpg, "--decrypt"); if (!err && (flags & GPGME_DECRYPT_UNWRAP)) @@ -1840,17 +1849,17 @@ gpg_decrypt (void *engine, } if (!err && export_session_key) - err = add_arg (gpg, "--show-session-key"); + err = add_gpg_arg (gpg, "--show-session-key"); if (!err && auto_key_retrieve) - err = add_arg (gpg, "--auto-key-retrieve"); + err = add_gpg_arg (gpg, "--auto-key-retrieve"); if (!err && gpg->flags.auto_key_import) - err = add_arg (gpg, "--auto-key-import"); + err = add_gpg_arg (gpg, "--auto-key-import"); if (!err && override_session_key && *override_session_key) { - if (have_gpg_version (gpg, "2.1.16")) + if (have_gpg_version (gpg, "2.1.16") && !gpg->flags.use_gpgtar) { gpgme_data_release (gpg->override_session_key); TRACE (DEBUG_ENGINE, "override", gpg, "seskey='%s' len=%zu\n", @@ -1880,25 +1889,43 @@ gpg_decrypt (void *engine, else { /* Using that option may leak the session key via ps(1). */ - err = add_arg (gpg, "--override-session-key"); - if (!err) - err = add_arg (gpg, override_session_key); + err = add_gpg_arg_with_value (gpg, "--override-session-key=", + override_session_key, 0); } } /* Tell the gpg object about the data. */ - if (!err) - err = add_arg (gpg, "--output"); - if (!err) - err = add_arg (gpg, "-"); - if (!err) - err = add_data (gpg, plain, 1, 1); - if (!err) - err = add_input_size_hint (gpg, ciph); - if (!err) - err = add_arg (gpg, "--"); - if (!err) - err = add_data (gpg, ciph, -1, 0); + if (gpg->flags.use_gpgtar) + { + const char *file_name = gpgme_data_get_file_name (plain); + if (!err && file_name) + { + err = add_arg (gpg, "--directory"); + if (!err) + err = add_arg (gpg, file_name); + } + if (!err) + err = add_input_size_hint (gpg, ciph); + if (!err) + err = add_arg (gpg, "--"); + if (!err) + err = add_data (gpg, ciph, 0, 0); + } + else + { + if (!err) + err = add_arg (gpg, "--output"); + if (!err) + err = add_arg (gpg, "-"); + if (!err) + err = add_data (gpg, plain, 1, 1); + if (!err) + err = add_input_size_hint (gpg, ciph); + if (!err) + err = add_arg (gpg, "--"); + if (!err) + err = add_data (gpg, ciph, -1, 0); + } if (!err) err = start (gpg); diff --git a/src/gpgme.h.in b/src/gpgme.h.in index fabba257..b498cd3b 100644 --- a/src/gpgme.h.in +++ b/src/gpgme.h.in @@ -1426,6 +1426,7 @@ gpgme_decrypt_result_t gpgme_op_decrypt_result (gpgme_ctx_t ctx); typedef enum { GPGME_DECRYPT_VERIFY = 1, + GPGME_DECRYPT_ARCHIVE = 2, GPGME_DECRYPT_UNWRAP = 128 } gpgme_decrypt_flags_t; diff --git a/tests/run-decrypt.c b/tests/run-decrypt.c index cf719925..ee49ead6 100644 --- a/tests/run-decrypt.c +++ b/tests/run-decrypt.c @@ -91,6 +91,8 @@ show_usage (int ex) " --unwrap remove only the encryption layer\n" " --large-buffers use large I/O buffer\n" " --sensitive mark data objects as sensitive\n" + " --archive extract files from an encrypted archive\n" + " --directory DIR extract the files into the directory DIR\n" " --diagnostics print diagnostics\n" , stderr); exit (ex); @@ -113,6 +115,7 @@ main (int argc, char **argv) int export_session_key = 0; const char *override_session_key = NULL; const char *request_origin = NULL; + const char *directory = NULL; int no_symkey_cache = 0; int ignore_mdc_error = 0; int raw_output = 0; @@ -205,6 +208,19 @@ main (int argc, char **argv) raw_output = 1; argc--; argv++; } + else if (!strcmp (*argv, "--archive")) + { + flags |= GPGME_DECRYPT_ARCHIVE; + argc--; argv++; + } + else if (!strcmp (*argv, "--directory")) + { + argc--; argv++; + if (!argc) + show_usage (1); + directory = *argv; + argc--; argv++; + } else if (!strncmp (*argv, "--", 2)) show_usage (1); @@ -302,6 +318,16 @@ main (int argc, char **argv) gpgme_strerror (err)); exit (1); } + if (directory && (flags & GPGME_DECRYPT_ARCHIVE)) + { + err = gpgme_data_set_file_name (out, directory); + if (err) + { + fprintf (stderr, PGM ": error setting file name (out): %s\n", + gpgme_strerror (err)); + exit (1); + } + } if (large_buffers) { err = gpgme_data_set_flag (out, "io-buffer-size", "1000000"); From 5b79b323971cb0794c45791851d85f8a66f0a441 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ingo=20Kl=C3=B6cker?= Date: Wed, 25 Jan 2023 11:21:39 +0100 Subject: [PATCH 14/24] core: Support usage of gpgtar for verifying a signed archive * src/gpgme.h.in (gpgme_verify_flags_t): New enum. (GPGME_VERIFY_ARCHIVE): New const. (gpgme_op_verify_ext_start): New func. (gpgme_op_verify_ext): New func. * src/gpgme.def, src/libgpgme.vers: Add new functions. * src/verify.c (gpgme_op_verify_ext_start): New. (gpgme_op_verify_ext): New. (verify_start): Add arg FLAGS. Pass the flags to _gpgme_engine_op_verify. (gpgme_op_verify_start): Call gpgme_op_verify_ext_start with 0 for FLAGS. (gpgme_op_verify): Call gpgme_op_verify_ext with 0 for FLAGS. * src/engine.c, src/engine.h (_gpgme_engine_op_verify): Add arg FLAGS. * src/engine-backend.h (struct engine_ops): Add FLAGS to 'verify'. * src/engine-gpg.c (gpg_verify): Add arg FLAGS. Set use_gpgtar engine flag if GPGME_VERIFY_ARCHIVE flag is set. Check for new enough gpg. Use add_gpg_arg for gpg-only options without a value. Set extra options for gpgtar and pass input data to stdin when using gpgtar. * src/engine-gpgsm.c (gpgsm_verify): Add arg FLAGS. Return error if GPGME_VERIFY_ARCHIVE flag is set. * src/engine-uiserver.c (uiserver_verify): Ditto. * tests/run-verify.c (show_usage): New options --archive, --directory, and --diagnostics. (main): Parse new options. Verify and extract with gpgtar if --archive is given. Set file name of output data to value of --directory option. Print stderr of gpg/gpgtar if --diagnostics is given. -- GnuPG-bug-id: 6342 --- src/engine-backend.h | 6 ++-- src/engine-gpg.c | 31 ++++++++++++++++--- src/engine-gpgsm.c | 8 +++-- src/engine-uiserver.c | 8 +++-- src/engine.c | 10 +++---- src/engine.h | 4 ++- src/gpgme.def | 2 ++ src/gpgme.h.in | 17 +++++++++++ src/libgpgme.vers | 3 ++ src/verify.c | 49 +++++++++++++++++++++--------- tests/run-verify.c | 70 +++++++++++++++++++++++++++++++++++++++++-- 11 files changed, 175 insertions(+), 33 deletions(-) diff --git a/src/engine-backend.h b/src/engine-backend.h index 75ed49cd..07ea8f14 100644 --- a/src/engine-backend.h +++ b/src/engine-backend.h @@ -122,9 +122,9 @@ struct engine_ops gpgme_sig_mode_t mode, int use_armor, int use_textmode, int include_certs, gpgme_ctx_t ctx /* FIXME */); - gpgme_error_t (*verify) (void *engine, gpgme_data_t sig, - gpgme_data_t signed_text, gpgme_data_t plaintext, - gpgme_ctx_t ctx); + gpgme_error_t (*verify) (void *engine, gpgme_verify_flags_t flags, + gpgme_data_t sig, gpgme_data_t signed_text, + gpgme_data_t plaintext, gpgme_ctx_t ctx); gpgme_error_t (*getauditlog) (void *engine, gpgme_data_t output, unsigned int flags); gpgme_error_t (*setexpire) (void *engine, gpgme_key_t key, diff --git a/src/engine-gpg.c b/src/engine-gpg.c index 86422112..2f63d6ff 100644 --- a/src/engine-gpg.c +++ b/src/engine-gpg.c @@ -3666,20 +3666,43 @@ gpg_sign (void *engine, gpgme_data_t in, gpgme_data_t out, } static gpgme_error_t -gpg_verify (void *engine, gpgme_data_t sig, gpgme_data_t signed_text, - gpgme_data_t plaintext, gpgme_ctx_t ctx) +gpg_verify (void *engine, gpgme_verify_flags_t flags, gpgme_data_t sig, + gpgme_data_t signed_text, gpgme_data_t plaintext, gpgme_ctx_t ctx) { engine_gpg_t gpg = engine; gpgme_error_t err; + gpg->flags.use_gpgtar = !!(flags & GPGME_VERIFY_ARCHIVE); + + if (gpg->flags.use_gpgtar && !have_gpg_version (gpg, "2.3.5")) + return gpg_error (GPG_ERR_NOT_SUPPORTED); + err = append_args_from_sender (gpg, ctx); if (!err && gpg->flags.auto_key_import) - err = add_arg (gpg, "--auto-key-import"); + err = add_gpg_arg (gpg, "--auto-key-import"); if (!err && ctx->auto_key_retrieve) - err = add_arg (gpg, "--auto-key-retrieve"); + err = add_gpg_arg (gpg, "--auto-key-retrieve"); if (err) ; + else if (gpg->flags.use_gpgtar) + { + const char *file_name = gpgme_data_get_file_name (plaintext); + if (!err && file_name) + { + err = add_arg (gpg, "--directory"); + if (!err) + err = add_arg (gpg, file_name); + } + /* gpgtar uses --decrypt also for signed-only archives */ + err = add_arg (gpg, "--decrypt"); + if (!err) + err = add_input_size_hint (gpg, sig); + if (!err) + err = add_arg (gpg, "--"); + if (!err) + err = add_data (gpg, sig, 0, 0); + } else if (plaintext) { /* Normal or cleartext signature. */ diff --git a/src/engine-gpgsm.c b/src/engine-gpgsm.c index 4632ef6c..fdc85dd6 100644 --- a/src/engine-gpgsm.c +++ b/src/engine-gpgsm.c @@ -2112,8 +2112,9 @@ gpgsm_sign (void *engine, gpgme_data_t in, gpgme_data_t out, static gpgme_error_t -gpgsm_verify (void *engine, gpgme_data_t sig, gpgme_data_t signed_text, - gpgme_data_t plaintext, gpgme_ctx_t ctx) +gpgsm_verify (void *engine, gpgme_verify_flags_t flags, gpgme_data_t sig, + gpgme_data_t signed_text, gpgme_data_t plaintext, + gpgme_ctx_t ctx) { engine_gpgsm_t gpgsm = engine; gpgme_error_t err; @@ -2123,6 +2124,9 @@ gpgsm_verify (void *engine, gpgme_data_t sig, gpgme_data_t signed_text, if (!gpgsm) return gpg_error (GPG_ERR_INV_VALUE); + if (flags & GPGME_VERIFY_ARCHIVE) + return gpg_error (GPG_ERR_NOT_IMPLEMENTED); + gpgsm->input_cb.data = sig; err = gpgsm_set_fd (gpgsm, INPUT_FD, map_data_enc (gpgsm->input_cb.data)); if (err) diff --git a/src/engine-uiserver.c b/src/engine-uiserver.c index 232eb4b4..7aa9cfe3 100644 --- a/src/engine-uiserver.c +++ b/src/engine-uiserver.c @@ -1294,8 +1294,9 @@ uiserver_sign (void *engine, gpgme_data_t in, gpgme_data_t out, /* FIXME: Missing a way to specify --silent. */ static gpgme_error_t -uiserver_verify (void *engine, gpgme_data_t sig, gpgme_data_t signed_text, - gpgme_data_t plaintext, gpgme_ctx_t ctx) +uiserver_verify (void *engine, gpgme_verify_flags_t flags, gpgme_data_t sig, + gpgme_data_t signed_text, gpgme_data_t plaintext, + gpgme_ctx_t ctx) { engine_uiserver_t uiserver = engine; gpgme_error_t err; @@ -1316,6 +1317,9 @@ uiserver_verify (void *engine, gpgme_data_t sig, gpgme_data_t signed_text, else return gpgme_error (GPG_ERR_UNSUPPORTED_PROTOCOL); + if (flags & GPGME_VERIFY_ARCHIVE) + return gpg_error (GPG_ERR_NOT_IMPLEMENTED); + if (gpgrt_asprintf (&cmd, "VERIFY%s", protocol) < 0) return gpg_error_from_syserror (); diff --git a/src/engine.c b/src/engine.c index 895b7e1a..2397f88d 100644 --- a/src/engine.c +++ b/src/engine.c @@ -940,9 +940,9 @@ _gpgme_engine_op_trustlist (engine_t engine, const char *pattern) gpgme_error_t -_gpgme_engine_op_verify (engine_t engine, gpgme_data_t sig, - gpgme_data_t signed_text, gpgme_data_t plaintext, - gpgme_ctx_t ctx) +_gpgme_engine_op_verify (engine_t engine, gpgme_verify_flags_t flags, + gpgme_data_t sig, gpgme_data_t signed_text, + gpgme_data_t plaintext, gpgme_ctx_t ctx) { if (!engine) return gpg_error (GPG_ERR_INV_VALUE); @@ -950,8 +950,8 @@ _gpgme_engine_op_verify (engine_t engine, gpgme_data_t sig, if (!engine->ops->verify) return gpg_error (GPG_ERR_NOT_IMPLEMENTED); - return (*engine->ops->verify) (engine->engine, sig, signed_text, plaintext, - ctx); + return (*engine->ops->verify) (engine->engine, flags, sig, signed_text, + plaintext, ctx); } diff --git a/src/engine.h b/src/engine.h index d580d997..e5654047 100644 --- a/src/engine.h +++ b/src/engine.h @@ -166,7 +166,9 @@ gpgme_error_t _gpgme_engine_op_sign (engine_t engine, gpgme_data_t in, gpgme_ctx_t ctx /* FIXME */); gpgme_error_t _gpgme_engine_op_trustlist (engine_t engine, const char *pattern); -gpgme_error_t _gpgme_engine_op_verify (engine_t engine, gpgme_data_t sig, +gpgme_error_t _gpgme_engine_op_verify (engine_t engine, + gpgme_verify_flags_t flags, + gpgme_data_t sig, gpgme_data_t signed_text, gpgme_data_t plaintext, gpgme_ctx_t ctx); diff --git a/src/gpgme.def b/src/gpgme.def index d8ccd4ca..67c189c7 100644 --- a/src/gpgme.def +++ b/src/gpgme.def @@ -283,5 +283,7 @@ EXPORTS gpgme_op_receive_keys @209 gpgme_op_receive_keys_start @210 + gpgme_op_verify_ext @211 + gpgme_op_verify_ext_start @212 ; END diff --git a/src/gpgme.h.in b/src/gpgme.h.in index b498cd3b..a337a124 100644 --- a/src/gpgme.h.in +++ b/src/gpgme.h.in @@ -1634,6 +1634,13 @@ typedef struct _gpgme_op_verify_result *gpgme_verify_result_t; /* Retrieve a pointer to the result of the verify operation. */ gpgme_verify_result_t gpgme_op_verify_result (gpgme_ctx_t ctx); +/* The valid verify flags. */ +typedef enum + { + GPGME_VERIFY_ARCHIVE = 1 + } +gpgme_verify_flags_t; + /* Verify within CTX that SIG is a valid signature for TEXT. */ gpgme_error_t gpgme_op_verify_start (gpgme_ctx_t ctx, gpgme_data_t sig, gpgme_data_t signed_text, @@ -1641,6 +1648,16 @@ gpgme_error_t gpgme_op_verify_start (gpgme_ctx_t ctx, gpgme_data_t sig, 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 gpgme_op_verify_ext_start (gpgme_ctx_t ctx, + gpgme_verify_flags_t flags, + gpgme_data_t sig, + gpgme_data_t signed_text, + gpgme_data_t plaintext); +gpgme_error_t gpgme_op_verify_ext (gpgme_ctx_t ctx, + gpgme_verify_flags_t flags, + gpgme_data_t sig, + gpgme_data_t signed_text, + gpgme_data_t plaintext); /* diff --git a/src/libgpgme.vers b/src/libgpgme.vers index 86d2a5df..20ae9fea 100644 --- a/src/libgpgme.vers +++ b/src/libgpgme.vers @@ -282,6 +282,9 @@ GPGME_1.0 { gpgme_op_receive_keys; gpgme_op_receive_keys_start; + gpgme_op_verify_ext; + gpgme_op_verify_ext_start; + local: *; diff --git a/src/verify.c b/src/verify.c index 81b2ff92..a8467156 100644 --- a/src/verify.c +++ b/src/verify.c @@ -1135,8 +1135,9 @@ _gpgme_op_verify_init_result (gpgme_ctx_t ctx) 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) +verify_start (gpgme_ctx_t ctx, int synchronous, gpgme_verify_flags_t flags, + gpgme_data_t sig, gpgme_data_t signed_text, + gpgme_data_t plaintext) { gpgme_error_t err; @@ -1153,26 +1154,45 @@ verify_start (gpgme_ctx_t ctx, int synchronous, gpgme_data_t sig, if (!sig) return gpg_error (GPG_ERR_NO_DATA); - return _gpgme_engine_op_verify (ctx->engine, sig, signed_text, plaintext, - ctx); + return _gpgme_engine_op_verify (ctx->engine, flags, sig, signed_text, + plaintext, ctx); } -/* Decrypt ciphertext CIPHER and make a signature verification within - CTX and store the resulting plaintext in PLAIN. */ +/* Old version of gpgme_op_verify_ext_start without FLAGS. */ gpgme_error_t gpgme_op_verify_start (gpgme_ctx_t ctx, gpgme_data_t sig, gpgme_data_t signed_text, gpgme_data_t plaintext) +{ + return gpgme_op_verify_ext_start (ctx, 0, sig, signed_text, plaintext); +} + + +/* Old version of gpgme_op_verify_ext without FLAGS. */ +gpgme_error_t +gpgme_op_verify (gpgme_ctx_t ctx, gpgme_data_t sig, gpgme_data_t signed_text, + gpgme_data_t plaintext) +{ + return gpgme_op_verify_ext (ctx, 0, sig, signed_text, plaintext); +} + + +/* Decrypt ciphertext CIPHER and make a signature verification within + CTX and store the resulting plaintext in PLAIN. */ +gpgme_error_t +gpgme_op_verify_ext_start (gpgme_ctx_t ctx, gpgme_verify_flags_t flags, + 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); + "flags=0x%x, sig=%p, signed_text=%p, plaintext=%p", + flags, sig, signed_text, plaintext); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); - err = verify_start (ctx, 0, sig, signed_text, plaintext); + err = verify_start (ctx, 0, flags, sig, signed_text, plaintext); return TRACE_ERR (err); } @@ -1180,19 +1200,20 @@ gpgme_op_verify_start (gpgme_ctx_t ctx, gpgme_data_t sig, /* 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_op_verify_ext (gpgme_ctx_t ctx, gpgme_verify_flags_t flags, + 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); + "flags=0x%x, sig=%p, signed_text=%p, plaintext=%p", + flags, sig, signed_text, plaintext); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); - err = verify_start (ctx, 1, sig, signed_text, plaintext); + err = verify_start (ctx, 1, flags, sig, signed_text, plaintext); if (!err) err = _gpgme_wait_one (ctx); return TRACE_ERR (err); diff --git a/tests/run-verify.c b/tests/run-verify.c index f131f491..831c4614 100644 --- a/tests/run-verify.c +++ b/tests/run-verify.c @@ -235,6 +235,9 @@ show_usage (int ex) " --repeat N repeat the operation N times\n" " --auto-key-retrieve\n" " --auto-key-import\n" + " --archive extract files from a signed archive FILE\n" + " --directory DIR extract the files into the directory DIR\n" + " --diagnostics print diagnostics\n" , stderr); exit (ex); } @@ -246,10 +249,13 @@ main (int argc, char **argv) int last_argc = -1; const char *s; gpgme_protocol_t protocol = GPGME_PROTOCOL_OpenPGP; + gpgme_verify_flags_t flags = 0; int print_status = 0; const char *sender = NULL; + const char *directory = NULL; int auto_key_retrieve = 0; int auto_key_import = 0; + int diagnostics = 0; int repeats = 1; int i; @@ -312,12 +318,30 @@ main (int argc, char **argv) auto_key_import = 1; argc--; argv++; } + else if (!strcmp (*argv, "--archive")) + { + flags |= GPGME_VERIFY_ARCHIVE; + argc--; argv++; + } + else if (!strcmp (*argv, "--directory")) + { + argc--; argv++; + if (!argc) + show_usage (1); + directory = *argv; + argc--; argv++; + } + else if (!strcmp (*argv, "--diagnostics")) + { + diagnostics = 1; + argc--; argv++; + } else if (!strncmp (*argv, "--", 2)) show_usage (1); } - if (argc < 1 || argc > 2) + if (argc < 1 || argc > 2 || (argc > 1 && (flags & GPGME_VERIFY_ARCHIVE))) show_usage (1); init_gpgme (protocol); @@ -330,6 +354,7 @@ main (int argc, char **argv) gpgme_data_t sig = NULL; FILE *fp_msg = NULL; gpgme_data_t msg = NULL; + gpgme_data_t out = NULL; gpgme_verify_result_t result; if (repeats > 1) @@ -415,8 +440,48 @@ main (int argc, char **argv) } } - err = gpgme_op_verify (ctx, sig, msg, NULL); + if (directory && (flags & GPGME_VERIFY_ARCHIVE)) + { + err = gpgme_data_new (&out); + if (err) + { + fprintf (stderr, PGM ": error allocating data object: %s\n", + gpgme_strerror (err)); + exit (1); + } + err = gpgme_data_set_file_name (out, directory); + if (err) + { + fprintf (stderr, PGM ": error setting file name (out): %s\n", + gpgme_strerror (err)); + exit (1); + } + } + + err = gpgme_op_verify_ext (ctx, flags, sig, msg, out); result = gpgme_op_verify_result (ctx); + + if (diagnostics) + { + gpgme_data_t diag; + gpgme_error_t diag_err; + + gpgme_data_new (&diag); + diag_err = gpgme_op_getauditlog (ctx, diag, GPGME_AUDITLOG_DIAG); + if (diag_err) + { + fprintf (stderr, PGM ": getting diagnostics failed: %s\n", + gpgme_strerror (diag_err)); + } + else + { + fputs ("Begin Diagnostics:\n", stdout); + print_data (diag); + fputs ("End Diagnostics.\n", stdout); + } + gpgme_data_release (diag); + } + if (result) print_result (result); if (err) @@ -425,6 +490,7 @@ main (int argc, char **argv) exit (1); } + gpgme_data_release (out); gpgme_data_release (msg); gpgme_data_release (sig); From ab7146aa61f009f51a9bcf5b8855b128aadec55d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ingo=20Kl=C3=B6cker?= Date: Wed, 25 Jan 2023 12:17:05 +0100 Subject: [PATCH 15/24] doc: Update NEWS and API documentation -- GnuPG-bug-id: 6342 --- NEWS | 8 ++++-- doc/gpgme.texi | 66 +++++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 66 insertions(+), 8 deletions(-) diff --git a/NEWS b/NEWS index 824faeb7..365d4f95 100644 --- a/NEWS +++ b/NEWS @@ -11,8 +11,8 @@ Noteworthy changes in version 1.18.1 (unreleased) gpgme_op_sign* to allow creating an encrypted and/or signed archive. [T6342] - * Extended gpgme_op_decrypt* and gpgme_op_decrypt_verify* to allow - extracting an encrypted and/or signed archive. [T6342] + * Extended gpgme_op_decrypt*, gpgme_op_decrypt_verify*, and gpgme_op_verify* + to allow extracting an encrypted and/or signed archive. [T6342] * cpp: Handle error when trying to sign expired keys. [T6155] @@ -36,6 +36,10 @@ Noteworthy changes in version 1.18.1 (unreleased) GPGME_DECRYPT_ARCHIVE NEW. GPGME_ENCRYPT_ARCHIVE NEW. GPGME_SIG_MODE_ARCHIVE NEW. + GPGME_VERIFY_ARCHIVE NEW. + gpgme_verify_flags_t NEW. + gpgme_op_verify_ext_start NEW. + gpgme_op_verify_ext NEW. cpp: GpgGenCardKeyInteractor::Curve NEW. cpp: GpgGenCardKeyInteractor::setCurve NEW. qt: ListAllKeysJob::Option NEW. diff --git a/doc/gpgme.texi b/doc/gpgme.texi index 184b1afe..d7c38f82 100644 --- a/doc/gpgme.texi +++ b/doc/gpgme.texi @@ -5589,12 +5589,6 @@ The function @code{gpgme_op_decrypt} decrypts the ciphertext in the data object @var{cipher} and stores it into the data object @var{plain}. -If the flag @code(GPGME_DECRYPT_ARCHIVE) is set, then an encrypted -archive in the data object @var{cipher} is decrypted and extracted. -The content of the archive is extracted into a directory named -@code{GPGARCH_n_} (where @code{n} is a number) or into the directory -set with @code{gpgme_data_set_file_name} for the data object @var{plain}. - The function returns the error code @code{GPG_ERR_NO_ERROR} if the ciphertext could be decrypted successfully, @code{GPG_ERR_INV_VALUE} if @var{ctx}, @var{cipher} or @var{plain} is not a valid pointer, @@ -5628,6 +5622,12 @@ The function @code{gpgme_op_decrypt_ext} is the same as @code{gpgme_op_decrypt} but has an additional argument @var{flags}. If @var{flags} is 0 both function behave identically. +If the flag @code(GPGME_DECRYPT_ARCHIVE) is set, then an encrypted +archive in the data object @var{cipher} is decrypted and extracted. +The content of the archive is extracted into a directory named +@code{GPGARCH_n_} (where @code{n} is a number) or into the directory +set with @code{gpgme_data_set_file_name} for the data object @var{plain}. + The value in @var{flags} is a bitwise-or combination of one or multiple of the following bit values: @@ -5824,6 +5824,60 @@ operation could be started successfully, @code{GPG_ERR_INV_VALUE} if any data to verify. @end deftypefun + +@deftypefun gpgme_error_t gpgme_op_verify_ext ( @ + @w{gpgme_ctx_t @var{ctx}}, @ + @w{gpgme_verify_flags_t @var{flags}}, @ + @w{gpgme_data_t @var{sig}}, @ + @w{gpgme_data_t @var{signed_text}}, @ + @w{gpgme_data_t @var{plain}}) + +The function @code{gpgme_op_verify_ext} is the same as +@code{gpgme_op_verify} but has an additional argument +@var{flags}. If @var{flags} is 0 both function behave identically. + +If the flag @code(GPGME_VERIFY_ARCHIVE) is set, then a signed archive +in the data object @var{sig} is verified and extracted. The content of +the archive is extracted into a directory named @code{GPGARCH_n_} +(where @code{n} is a number) or into the directory set with +@code{gpgme_data_set_file_name} for the data object @var{plain}. + +The value in @var{flags} is a bitwise-or combination of one or +multiple of the following bit values: + +@table @code +@item GPGME_VERIFY_ARCHIVE +@since{1.19.0} + +The @code{GPGME_VERIFY_ARCHIVE} symbol specifies that the input is a +signed archive that shall be verified and extracted. This feature +is currently only supported for the OpenPGP crypto engine. + +@end table + +The function returns the error codes as descriped for +@code{gpgme_op_decrypt} respective @code{gpgme_op_encrypt}. +@end deftypefun + +@deftypefun gpgme_error_t gpgme_op_verify_ext_start ( @ + @w{gpgme_ctx_t @var{ctx}}, @ + @w{gpgme_verify_flags_t @var{flags}}, @ + @w{gpgme_data_t @var{sig}}, @ + @w{gpgme_data_t @var{signed_text}}, @ + @w{gpgme_data_t @var{plain}}) + +The function @code{gpgme_op_verify_ext_start} initiates a +@code{gpgme_op_verify_ext} operation. It can be completed by calling +@code{gpgme_wait} on the context. @xref{Waiting For Completion}. + +The function returns the error code @code{GPG_ERR_NO_ERROR} if the +operation could be started successfully, @code{GPG_ERR_INV_VALUE} if +@var{ctx}, @var{sig} or @var{plain} is not a valid pointer, and +@code{GPG_ERR_NO_DATA} if @var{sig} or @var{plain} does not contain +any data to verify. +@end deftypefun + + @deftp {Data type} {gpgme_sig_notation_t} This is a pointer to a structure used to store a part of the result of a @code{gpgme_op_verify} operation. The structure contains the From 3580bb139b2e41f6c2397e506ee66f4892ceec62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ingo=20Kl=C3=B6cker?= Date: Thu, 26 Jan 2023 09:52:11 +0100 Subject: [PATCH 16/24] core: Update required GnuPG version for new archive features * src/engine-gpg.c (gpg_decrypt, gpg_encrypt, gpg_encrypt_sign, gpg_sign, gpg_verify): Require gpg 2.4.1. -- To work properly the archive feature needs a fix added in GnuPG 2.4.1. GnuPG-bug-id: 6342 --- doc/gpgme.texi | 11 +++++++---- src/engine-gpg.c | 10 +++++----- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/doc/gpgme.texi b/doc/gpgme.texi index d7c38f82..22377088 100644 --- a/doc/gpgme.texi +++ b/doc/gpgme.texi @@ -5643,7 +5643,8 @@ shall exactly act as @code{gpgme_op_decrypt_verify}. The @code{GPGME_DECRYPT_ARCHIVE} symbol specifies that the input is an encrypted archive that shall be decrypted and extracted. This feature -is currently only supported for the OpenPGP crypto engine. +is currently only supported for the OpenPGP crypto engine and requires +GnuPG 2.4.1. @item GPGME_DECRYPT_UNWRAP @since{1.8.0} @@ -5851,7 +5852,8 @@ multiple of the following bit values: The @code{GPGME_VERIFY_ARCHIVE} symbol specifies that the input is a signed archive that shall be verified and extracted. This feature -is currently only supported for the OpenPGP crypto engine. +is currently only supported for the OpenPGP crypto engine and requires +GnuPG 2.4.1. @end table @@ -6315,7 +6317,8 @@ mode settings of the context are ignored. @since{1.19.0} A signed archive is created from the given files and directories. This -feature is currently only supported for the OpenPGP crypto engine. +feature is currently only supported for the OpenPGP crypto engine and requires +GnuPG 2.4.1. @end table @end deftp @@ -6594,7 +6597,7 @@ supported for the OpenPGP crypto engine. The @code{GPGME_ENCRYPT_ARCHIVE} symbol specifies that the input is a NUL-separated list of file paths and directory paths that shall be encrypted into an archive. This feature is currently only supported -for the OpenPGP crypto engine. +for the OpenPGP crypto engine and requires GnuPG 2.4.1. @end table diff --git a/src/engine-gpg.c b/src/engine-gpg.c index 2f63d6ff..e921df0a 100644 --- a/src/engine-gpg.c +++ b/src/engine-gpg.c @@ -1832,7 +1832,7 @@ gpg_decrypt (void *engine, gpg->flags.use_gpgtar = !!(flags & GPGME_DECRYPT_ARCHIVE); - if (gpg->flags.use_gpgtar && !have_gpg_version (gpg, "2.3.5")) + if (gpg->flags.use_gpgtar && !have_gpg_version (gpg, "2.4.1")) return gpg_error (GPG_ERR_NOT_SUPPORTED); if (gpg->flags.use_gpgtar && (flags & GPGME_DECRYPT_UNWRAP)) @@ -2334,7 +2334,7 @@ gpg_encrypt (void *engine, gpgme_key_t recp[], const char *recpstring, gpg->flags.use_gpgtar = !!(flags & GPGME_ENCRYPT_ARCHIVE); - if (gpg->flags.use_gpgtar && !have_gpg_version (gpg, "2.3.5")) + if (gpg->flags.use_gpgtar && !have_gpg_version (gpg, "2.4.1")) return gpg_error (GPG_ERR_NOT_SUPPORTED); if (gpg->flags.use_gpgtar && (flags & GPGME_ENCRYPT_WRAP)) @@ -2449,7 +2449,7 @@ gpg_encrypt_sign (void *engine, gpgme_key_t recp[], gpg->flags.use_gpgtar = !!(flags & GPGME_ENCRYPT_ARCHIVE); - if (gpg->flags.use_gpgtar && !have_gpg_version (gpg, "2.3.5")) + if (gpg->flags.use_gpgtar && !have_gpg_version (gpg, "2.4.1")) return gpg_error (GPG_ERR_NOT_SUPPORTED); if (recp || recpstring) @@ -3590,7 +3590,7 @@ gpg_sign (void *engine, gpgme_data_t in, gpgme_data_t out, gpg->flags.use_gpgtar = mode == GPGME_SIG_MODE_ARCHIVE; - if (gpg->flags.use_gpgtar && !have_gpg_version (gpg, "2.3.5")) + if (gpg->flags.use_gpgtar && !have_gpg_version (gpg, "2.4.1")) return gpg_error (GPG_ERR_NOT_SUPPORTED); if (mode == GPGME_SIG_MODE_CLEAR) @@ -3674,7 +3674,7 @@ gpg_verify (void *engine, gpgme_verify_flags_t flags, gpgme_data_t sig, gpg->flags.use_gpgtar = !!(flags & GPGME_VERIFY_ARCHIVE); - if (gpg->flags.use_gpgtar && !have_gpg_version (gpg, "2.3.5")) + if (gpg->flags.use_gpgtar && !have_gpg_version (gpg, "2.4.1")) return gpg_error (GPG_ERR_NOT_SUPPORTED); err = append_args_from_sender (gpg, ctx); From 1328a575864376dcedb21dde9f67779a6d55c18d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ingo=20Kl=C3=B6cker?= Date: Fri, 27 Jan 2023 09:29:10 +0100 Subject: [PATCH 17/24] doc: Fix syntax errors -- GnuPG-bug-id: 6342 --- doc/gpgme.texi | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/doc/gpgme.texi b/doc/gpgme.texi index 22377088..8bde11bc 100644 --- a/doc/gpgme.texi +++ b/doc/gpgme.texi @@ -5622,7 +5622,7 @@ The function @code{gpgme_op_decrypt_ext} is the same as @code{gpgme_op_decrypt} but has an additional argument @var{flags}. If @var{flags} is 0 both function behave identically. -If the flag @code(GPGME_DECRYPT_ARCHIVE) is set, then an encrypted +If the flag @code{GPGME_DECRYPT_ARCHIVE} is set, then an encrypted archive in the data object @var{cipher} is decrypted and extracted. The content of the archive is extracted into a directory named @code{GPGARCH_n_} (where @code{n} is a number) or into the directory @@ -5837,7 +5837,7 @@ The function @code{gpgme_op_verify_ext} is the same as @code{gpgme_op_verify} but has an additional argument @var{flags}. If @var{flags} is 0 both function behave identically. -If the flag @code(GPGME_VERIFY_ARCHIVE) is set, then a signed archive +If the flag @code{GPGME_VERIFY_ARCHIVE} is set, then a signed archive in the data object @var{sig} is verified and extracted. The content of the archive is extracted into a directory named @code{GPGARCH_n_} (where @code{n} is a number) or into the directory set with @@ -6331,7 +6331,7 @@ the data object @var{plain} and returns it in the data object specified for @var{sig}), the text mode attributes set for the context @var{ctx} and the requested signature mode @var{mode}. -If signature mode @code(GPGME_SIG_MODE_ARCHIVE) is requested, then a +If signature mode @code{GPGME_SIG_MODE_ARCHIVE} is requested, then a signed archive is created from the files and directories given as NUL-separated list in the data object @var{plain} and returned in the data object @var{sig}. The paths of the files and directories have to @@ -6509,7 +6509,7 @@ ciphertext created is determined by the @acronym{ASCII} armor (or, if that is not set, by the encoding specified for @var{cipher}) and the text mode attributes set for the context @var{ctx}. -If the flag @code(GPGME_ENCRYPT_ARCHIVE) is set, then an encrypted +If the flag @code{GPGME_ENCRYPT_ARCHIVE} is set, then an encrypted archive is created from the files and directories given as NUL-separated list in the data object @var{plain} and returned in the data object @var{cipher}. The paths of the files and directories have to From 261245a2e0e76568607c79a0c80372cb6f46444e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ingo=20Kl=C3=B6cker?= Date: Fri, 27 Jan 2023 09:37:05 +0100 Subject: [PATCH 18/24] cpp: Add convenience overload to set file name * lang/cpp/src/data.h, lang/cpp/src/data.cpp (setFileName): Add overload. -- GnuPG-bug-id: 6342 --- NEWS | 1 + lang/cpp/src/data.cpp | 5 +++++ lang/cpp/src/data.h | 1 + 3 files changed, 7 insertions(+) diff --git a/NEWS b/NEWS index 365d4f95..4830dd51 100644 --- a/NEWS +++ b/NEWS @@ -42,6 +42,7 @@ Noteworthy changes in version 1.18.1 (unreleased) gpgme_op_verify_ext NEW. cpp: GpgGenCardKeyInteractor::Curve NEW. cpp: GpgGenCardKeyInteractor::setCurve NEW. + cpp: Data::setFileName EXTENDED: New overload qt: ListAllKeysJob::Option NEW. qt: ListAllKeysJob::Options NEW. qt: ListAllKeysJob::setOptions NEW. diff --git a/lang/cpp/src/data.cpp b/lang/cpp/src/data.cpp index 7a93cbc2..dd8b1740 100644 --- a/lang/cpp/src/data.cpp +++ b/lang/cpp/src/data.cpp @@ -217,6 +217,11 @@ GpgME::Error GpgME::Data::setFileName(const char *name) return Error(gpgme_data_set_file_name(d->data, name)); } +GpgME::Error GpgME::Data::setFileName(const std::string &name) +{ + return Error(gpgme_data_set_file_name(d->data, name.c_str())); +} + ssize_t GpgME::Data::read(void *buffer, size_t length) { return gpgme_data_read(d->data, buffer, length); diff --git a/lang/cpp/src/data.h b/lang/cpp/src/data.h index 649e4104..9a5d5b0c 100644 --- a/lang/cpp/src/data.h +++ b/lang/cpp/src/data.h @@ -106,6 +106,7 @@ public: char *fileName() const; Error setFileName(const char *name); + Error setFileName(const std::string &name); ssize_t read(void *buffer, size_t length); ssize_t write(const void *buffer, size_t length); From d28ea8c6b3db008150e2bae99a33e30b55c4bc10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ingo=20Kl=C3=B6cker?= Date: Fri, 27 Jan 2023 09:40:05 +0100 Subject: [PATCH 19/24] cpp: Support new archive encryption flag * lang/cpp/src/context.h (EncryptArchive): New flag. * lang/cpp/src/context.cpp (encryptflags2encryptflags): Convert EncryptArchive to corresponding gpgme encrypt flags. (operator<<): Add new flag to debug stream. -- GnuPG-bug-id: 6342 --- lang/cpp/src/context.cpp | 4 ++++ lang/cpp/src/context.h | 3 ++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/lang/cpp/src/context.cpp b/lang/cpp/src/context.cpp index dba958cf..f7f6f54d 100644 --- a/lang/cpp/src/context.cpp +++ b/lang/cpp/src/context.cpp @@ -1335,6 +1335,9 @@ static gpgme_encrypt_flags_t encryptflags2encryptflags(Context::EncryptionFlags if (flags & Context::Symmetric) { result |= GPGME_ENCRYPT_SYMMETRIC; } + if (flags & Context::EncryptArchive) { + result |= GPGME_ENCRYPT_ARCHIVE; + } return static_cast(result); } @@ -1909,6 +1912,7 @@ std::ostream &operator<<(std::ostream &os, Context::EncryptionFlags flags) CHECK(ExpectSign); CHECK(NoCompress); CHECK(Symmetric); + CHECK(EncryptArchive); #undef CHECK return os << ')'; } diff --git a/lang/cpp/src/context.h b/lang/cpp/src/context.h index 7bd1b03d..b38e8cdf 100644 --- a/lang/cpp/src/context.h +++ b/lang/cpp/src/context.h @@ -447,7 +447,8 @@ public: NoCompress = 16, Symmetric = 32, ThrowKeyIds = 64, - EncryptWrap = 128 + EncryptWrap = 128, + EncryptArchive = 512, }; EncryptionResult encrypt(const std::vector &recipients, const Data &plainText, Data &cipherText, EncryptionFlags flags); GpgME::Error encryptSymmetrically(const Data &plainText, Data &cipherText); From 275a3a2c16d27e5bc87f7d80af2ee703a92b6758 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ingo=20Kl=C3=B6cker?= Date: Fri, 27 Jan 2023 11:44:55 +0100 Subject: [PATCH 20/24] qt: Add data provider for list of file names * lang/qt/src/Makefile.am: Add new files and corresponding camel-case header. * lang/qt/src/dataprovider.h: Include interface from gpgme++ when not building. * lang/qt/src/filelistdataprovider.cpp, lang/qt/src/filelistdataprovider.h: New. -- The new data provider simplifies providing a nul-separated list of UTF-8-encoded filenames, e.g. for creating signed or encrypted archives. It is a simple read-only proxy for QByteArrayDataProvider. GnuPG-bug-id: 6342 --- NEWS | 1 + lang/qt/src/Makefile.am | 3 + lang/qt/src/dataprovider.h | 6 ++ lang/qt/src/filelistdataprovider.cpp | 98 ++++++++++++++++++++++++++++ lang/qt/src/filelistdataprovider.h | 79 ++++++++++++++++++++++ 5 files changed, 187 insertions(+) create mode 100644 lang/qt/src/filelistdataprovider.cpp create mode 100644 lang/qt/src/filelistdataprovider.h diff --git a/NEWS b/NEWS index 4830dd51..a96973cd 100644 --- a/NEWS +++ b/NEWS @@ -49,6 +49,7 @@ Noteworthy changes in version 1.18.1 (unreleased) qt: ListAllKeysJob::options NEW. qt: Job::startNow NEW. qt: ImportJob::startLater NEW. + qt: FileListDataProvider NEW. Noteworthy changes in version 1.18.0 (2022-08-10) diff --git a/lang/qt/src/Makefile.am b/lang/qt/src/Makefile.am index 510802a8..40a73861 100644 --- a/lang/qt/src/Makefile.am +++ b/lang/qt/src/Makefile.am @@ -34,6 +34,7 @@ EXTRA_DIST = QGpgmeConfig.cmake.in.in QGpgmeConfigVersion.cmake.in \ qgpgme_sources = \ dataprovider.cpp \ debug.cpp \ + filelistdataprovider.cpp \ job.cpp multideletejob.cpp qgpgmeadduseridjob.cpp \ qgpgmeaddexistingsubkeyjob.cpp \ qgpgmebackend.cpp qgpgmechangeexpiryjob.cpp qgpgmechangeownertrustjob.cpp \ @@ -72,6 +73,7 @@ qgpgme_headers= \ downloadjob.h \ encryptjob.h \ exportjob.h \ + filelistdataprovider.h \ hierarchicalkeylistjob.h \ job.h \ keyformailboxjob.h \ @@ -120,6 +122,7 @@ camelcase_headers= \ DownloadJob \ EncryptJob \ ExportJob \ + FileListDataProvider \ HierarchicalKeyKistJob \ Job \ MultiDeleteJob \ diff --git a/lang/qt/src/dataprovider.h b/lang/qt/src/dataprovider.h index 636d6fb6..b14e7d54 100644 --- a/lang/qt/src/dataprovider.h +++ b/lang/qt/src/dataprovider.h @@ -25,7 +25,13 @@ #define __QGPGME_DATAPROVIDER_H__ #include "qgpgme_export.h" + +#ifdef BUILDING_QGPGME #include +#else +#include +#endif + #include #include diff --git a/lang/qt/src/filelistdataprovider.cpp b/lang/qt/src/filelistdataprovider.cpp new file mode 100644 index 00000000..004854ef --- /dev/null +++ b/lang/qt/src/filelistdataprovider.cpp @@ -0,0 +1,98 @@ +/* + filelistdataprovider.cpp + + This file is part of qgpgme, the Qt API binding for gpgme + Copyright (c) 2023 g10 Code GmbH + Software engineering by Ingo Klöcker + + QGpgME is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + QGpgME 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + In addition, as a special exception, the copyright holders give + permission to link the code of this program with any edition of + the Qt library by Trolltech AS, Norway (or with modified versions + of Qt that use the same license as Qt), and distribute linked + combinations including the two. You must obey the GNU General + Public License in all respects for all of the code used other than + Qt. If you modify this file, you may extend this exception to + your version of the file, but you are not obligated to do so. If + you do not wish to do so, delete this exception statement from + your version. +*/ + +#ifdef HAVE_CONFIG_H + #include "config.h" +#endif + +#include "filelistdataprovider.h" + +#include "dataprovider.h" + +#include + +#include + +#include + +using namespace QGpgME; +using namespace GpgME; + +static QByteArray encodeFilenames(const std::vector &filenames) +{ + QByteArray ret; + if (filenames.empty()) { + return ret; + } + // calculate and reserve the needed minimum size of the result + const auto addSize = [](unsigned int n, const QString &s) { return n + s.size(); }; + const unsigned int minSize = filenames.size() + + std::accumulate(filenames.cbegin(), filenames.cend(), 0u, addSize); + ret.reserve(minSize); + // pack the filenames into the byte array + for (const auto &f : filenames) { + if (!f.isEmpty()) { + ret += f.toUtf8() + '\0'; + } + } + ret.chop(1); // remove the trailing nul + return ret; +} + +FileListDataProvider::FileListDataProvider(const std::vector &filenames) + : mProvider{new QByteArrayDataProvider{encodeFilenames(filenames)}} +{ +} + +FileListDataProvider::~FileListDataProvider() = default; + +ssize_t FileListDataProvider::read(void* buffer, size_t bufSize) +{ + return mProvider->read(buffer, bufSize); +} + +ssize_t FileListDataProvider::write(const void *, size_t) +{ + Error::setSystemError(GPG_ERR_EBADF); + return -1; +} + +off_t FileListDataProvider::seek(off_t offset, int whence) +{ + return mProvider->seek(offset, whence); +} + +void FileListDataProvider::release() +{ + mProvider->release(); +} diff --git a/lang/qt/src/filelistdataprovider.h b/lang/qt/src/filelistdataprovider.h new file mode 100644 index 00000000..25e52e41 --- /dev/null +++ b/lang/qt/src/filelistdataprovider.h @@ -0,0 +1,79 @@ +/* + filelistdataprovider.h + + This file is part of qgpgme, the Qt API binding for gpgme + Copyright (c) 2023 g10 Code GmbH + Software engineering by Ingo Klöcker + + QGpgME is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + QGpgME 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + In addition, as a special exception, the copyright holders give + permission to link the code of this program with any edition of + the Qt library by Trolltech AS, Norway (or with modified versions + of Qt that use the same license as Qt), and distribute linked + combinations including the two. You must obey the GNU General + Public License in all respects for all of the code used other than + Qt. If you modify this file, you may extend this exception to + your version of the file, but you are not obligated to do so. If + you do not wish to do so, delete this exception statement from + your version. +*/ + +#ifndef __QGPGME_FILELISTDATAPROVIDER_H__ +#define __QGPGME_FILELISTDATAPROVIDER_H__ + +#include "qgpgme_export.h" + +#ifdef BUILDING_QGPGME +#include +#else +#include +#endif + +#include +#include + +class QString; + +namespace QGpgME +{ + +/** + * This read-only data provider simplifies providing a nul-separated list of + * UTF-8-encoded filenames, e.g. for creating signed or encrypted archives. + */ +class QGPGME_EXPORT FileListDataProvider : public GpgME::DataProvider +{ +public: + explicit FileListDataProvider(const std::vector &filenames); + ~FileListDataProvider() override; + +private: + bool isSupported(Operation op) const override + { + return op != Operation::Write; + } + ssize_t read(void *buffer, size_t bufSize) override; + ssize_t write(const void *buffer, size_t bufSize) override; + off_t seek(off_t offset, int whence) override; + void release() override; + +private: + std::unique_ptr mProvider; +}; + +} + +#endif // __QGPGME_FILELISTDATAPROVIDER_H__ From 7afd135ccec73585e06272f98b4a0895e3ee6579 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ingo=20Kl=C3=B6cker?= Date: Fri, 27 Jan 2023 11:53:45 +0100 Subject: [PATCH 21/24] qt: Add job for creating encrypted archives * lang/qt/src/encryptarchivejob.cpp, lang/qt/src/encryptarchivejob.h, lang/qt/src/encryptarchivejob_p.h, lang/qt/src/qgpgmeencryptarchivejob.cpp, lang/qt/src/qgpgmeencryptarchivejob.h: New. * lang/qt/src/protocol.h (class Protocol): Add pure virtual member function encryptArchiveJob * lang/qt/src/protocol_p.h (Protocol::encryptArchiveJob): ... and implement it. * lang/qt/src/Makefile.am: Update accordingly. * lang/qt/tests/run-encryptarchivejob.cpp: New. * lang/qt/tests/Makefile.am: Add new test runner. -- GnuPG-bug-id: 6342 --- NEWS | 4 + lang/qt/src/Makefile.am | 8 ++ lang/qt/src/encryptarchivejob.cpp | 62 ++++++++++ lang/qt/src/encryptarchivejob.h | 101 +++++++++++++++ lang/qt/src/encryptarchivejob_p.h | 49 ++++++++ lang/qt/src/protocol.h | 3 + lang/qt/src/protocol_p.h | 13 ++ lang/qt/src/qgpgmeencryptarchivejob.cpp | 152 +++++++++++++++++++++++ lang/qt/src/qgpgmeencryptarchivejob.h | 82 ++++++++++++ lang/qt/tests/Makefile.am | 2 + lang/qt/tests/run-encryptarchivejob.cpp | 158 ++++++++++++++++++++++++ 11 files changed, 634 insertions(+) create mode 100644 lang/qt/src/encryptarchivejob.cpp create mode 100644 lang/qt/src/encryptarchivejob.h create mode 100644 lang/qt/src/encryptarchivejob_p.h create mode 100644 lang/qt/src/qgpgmeencryptarchivejob.cpp create mode 100644 lang/qt/src/qgpgmeencryptarchivejob.h create mode 100644 lang/qt/tests/run-encryptarchivejob.cpp diff --git a/NEWS b/NEWS index a96973cd..f36be620 100644 --- a/NEWS +++ b/NEWS @@ -29,6 +29,8 @@ Noteworthy changes in version 1.18.1 (unreleased) * qt: Allow deferred start of import jobs. [T6323] + * qt: Support creating encrypted archives. [T6342] + * Interface changes relative to the 1.18.0 release: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ gpgme_get_ctx_flag EXTENDED: New flag 'no-auto-check-trustdb'. @@ -50,6 +52,8 @@ Noteworthy changes in version 1.18.1 (unreleased) qt: Job::startNow NEW. qt: ImportJob::startLater NEW. qt: FileListDataProvider NEW. + qt: EncryptArchiveJob NEW. + qt: Protocol::encryptArchiveJob NEW. Noteworthy changes in version 1.18.0 (2022-08-10) diff --git a/lang/qt/src/Makefile.am b/lang/qt/src/Makefile.am index 40a73861..240e5716 100644 --- a/lang/qt/src/Makefile.am +++ b/lang/qt/src/Makefile.am @@ -34,12 +34,14 @@ EXTRA_DIST = QGpgmeConfig.cmake.in.in QGpgmeConfigVersion.cmake.in \ qgpgme_sources = \ dataprovider.cpp \ debug.cpp \ + encryptarchivejob.cpp \ filelistdataprovider.cpp \ job.cpp multideletejob.cpp qgpgmeadduseridjob.cpp \ qgpgmeaddexistingsubkeyjob.cpp \ qgpgmebackend.cpp qgpgmechangeexpiryjob.cpp qgpgmechangeownertrustjob.cpp \ qgpgmechangepasswdjob.cpp qgpgmedecryptjob.cpp \ qgpgmedecryptverifyjob.cpp qgpgmedeletejob.cpp qgpgmedownloadjob.cpp \ + qgpgmeencryptarchivejob.cpp \ qgpgmeencryptjob.cpp qgpgmeexportjob.cpp qgpgmeimportfromkeyserverjob.cpp \ qgpgmeimportjob.cpp qgpgmekeygenerationjob.cpp qgpgmekeylistjob.cpp \ listallkeysjob.cpp qgpgmelistallkeysjob.cpp qgpgmenewcryptoconfig.cpp \ @@ -71,6 +73,7 @@ qgpgme_headers= \ decryptjob.h \ decryptverifyjob.h \ downloadjob.h \ + encryptarchivejob.h \ encryptjob.h \ exportjob.h \ filelistdataprovider.h \ @@ -120,6 +123,7 @@ camelcase_headers= \ DecryptVerifyJob \ DN \ DownloadJob \ + EncryptArchiveJob \ EncryptJob \ ExportJob \ FileListDataProvider \ @@ -156,6 +160,7 @@ camelcase_headers= \ private_qgpgme_headers = \ changeexpiryjob_p.h \ + encryptarchivejob_p.h \ encryptjob_p.h \ importjob_p.h \ listallkeysjob_p.h \ @@ -172,6 +177,7 @@ private_qgpgme_headers = \ qgpgmedecryptverifyjob.h \ qgpgmedeletejob.h \ qgpgmedownloadjob.h \ + qgpgmeencryptarchivejob.h \ qgpgmeencryptjob.h \ qgpgmeexportjob.h \ qgpgmeimportfromkeyserverjob.h \ @@ -209,6 +215,7 @@ qgpgme_moc_sources = \ decryptverifyjob.moc \ deletejob.moc \ downloadjob.moc \ + encryptarchivejob.moc \ encryptjob.moc \ exportjob.moc \ hierarchicalkeylistjob.moc \ @@ -228,6 +235,7 @@ qgpgme_moc_sources = \ qgpgmedecryptverifyjob.moc \ qgpgmedeletejob.moc \ qgpgmedownloadjob.moc \ + qgpgmeencryptarchivejob.moc \ qgpgmeencryptjob.moc \ qgpgmeexportjob.moc \ qgpgmeimportfromkeyserverjob.moc \ diff --git a/lang/qt/src/encryptarchivejob.cpp b/lang/qt/src/encryptarchivejob.cpp new file mode 100644 index 00000000..5c5533be --- /dev/null +++ b/lang/qt/src/encryptarchivejob.cpp @@ -0,0 +1,62 @@ +/* + encryptarchivejob.cpp + + This file is part of qgpgme, the Qt API binding for gpgme + Copyright (c) 2023 g10 Code GmbH + Software engineering by Ingo Klöcker + + QGpgME is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + QGpgME 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + In addition, as a special exception, the copyright holders give + permission to link the code of this program with any edition of + the Qt library by Trolltech AS, Norway (or with modified versions + of Qt that use the same license as Qt), and distribute linked + combinations including the two. You must obey the GNU General + Public License in all respects for all of the code used other than + Qt. If you modify this file, you may extend this exception to + your version of the file, but you are not obligated to do so. If + you do not wish to do so, delete this exception statement from + your version. +*/ + +#ifdef HAVE_CONFIG_H + #include "config.h" +#endif + +#include "encryptarchivejob.h" +#include "encryptarchivejob_p.h" + +using namespace QGpgME; + +EncryptArchiveJob::EncryptArchiveJob(QObject *parent) + : Job{parent} +{ +} + +EncryptArchiveJob::~EncryptArchiveJob() = default; + +void EncryptArchiveJob::setBaseDirectory(const QString &baseDirectory) +{ + auto d = jobPrivate(this); + d->m_baseDirectory = baseDirectory; +} + +QString EncryptArchiveJob::baseDirectory() const +{ + auto d = jobPrivate(this); + return d->m_baseDirectory; +} + +#include "encryptarchivejob.moc" diff --git a/lang/qt/src/encryptarchivejob.h b/lang/qt/src/encryptarchivejob.h new file mode 100644 index 00000000..1221994f --- /dev/null +++ b/lang/qt/src/encryptarchivejob.h @@ -0,0 +1,101 @@ +/* + encryptarchivejob.h + + This file is part of qgpgme, the Qt API binding for gpgme + Copyright (c) 2023 g10 Code GmbH + Software engineering by Ingo Klöcker + + QGpgME is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + QGpgME 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + In addition, as a special exception, the copyright holders give + permission to link the code of this program with any edition of + the Qt library by Trolltech AS, Norway (or with modified versions + of Qt that use the same license as Qt), and distribute linked + combinations including the two. You must obey the GNU General + Public License in all respects for all of the code used other than + Qt. If you modify this file, you may extend this exception to + your version of the file, but you are not obligated to do so. If + you do not wish to do so, delete this exception statement from + your version. +*/ + +#ifndef __QGPGME_ENCRYPTARCHIVEJOB_H__ +#define __QGPGME_ENCRYPTARCHIVEJOB_H__ + +#include "job.h" + +#ifdef BUILDING_QGPGME +# include "context.h" +#else +# include +#endif + +namespace GpgME +{ +class Key; +} + +namespace QGpgME +{ + +/** + * Abstract base class for job for creating encrypted archives + */ +class QGPGME_EXPORT EncryptArchiveJob : public Job +{ + Q_OBJECT +protected: + explicit EncryptArchiveJob(QObject *parent); +public: + ~EncryptArchiveJob() override; + + void setBaseDirectory(const QString &baseDirectory); + QString baseDirectory() const; + + /** + * Starts the creation of an encrypted archive. + * + * Encrypts the files and directories in \a paths into an archive for the + * keys in \a recipients. If \a recipients is empty, then symmetric + * encryption is performed. The encrypted archive is written to \a cipherText. + * + * Emits result() when the job has finished. + */ + virtual GpgME::Error start(const std::vector &recipients, + const std::vector &paths, + const std::shared_ptr &cipherText, + const GpgME::Context::EncryptionFlags flags) = 0; + + /** + * Creates an encrypted archive. + * + * Encrypts the files and directories in \a paths into an archive for the + * keys in \a recipients. If \a recipients is empty, then symmetric + * encryption is performed. The encrypted archive is written to \a cipherText. + */ + // virtual GpgME::EncryptionResult exec(const std::vector &recipients, + // const std::vector &paths, + // const std::shared_ptr &cipherText, + // const GpgME::Context::EncryptionFlags flags) = 0; + +Q_SIGNALS: + void result(const GpgME::EncryptionResult &result, + const QString &auditLogAsHtml = {}, + const GpgME::Error &auditLogError = {}); +}; + +} + +#endif // __QGPGME_ENCRYPTARCHIVEJOB_H__ diff --git a/lang/qt/src/encryptarchivejob_p.h b/lang/qt/src/encryptarchivejob_p.h new file mode 100644 index 00000000..798096db --- /dev/null +++ b/lang/qt/src/encryptarchivejob_p.h @@ -0,0 +1,49 @@ +/* + encryptarchivejob_p.h + + This file is part of qgpgme, the Qt API binding for gpgme + Copyright (c) 2023 g10 Code GmbH + Software engineering by Ingo Klöcker + + QGpgME is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + QGpgME 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + In addition, as a special exception, the copyright holders give + permission to link the code of this program with any edition of + the Qt library by Trolltech AS, Norway (or with modified versions + of Qt that use the same license as Qt), and distribute linked + combinations including the two. You must obey the GNU General + Public License in all respects for all of the code used other than + Qt. If you modify this file, you may extend this exception to + your version of the file, but you are not obligated to do so. If + you do not wish to do so, delete this exception statement from + your version. +*/ + +#ifndef __QGPGME_ENCRYPTARCHIVEJOB_P_H__ +#define __QGPGME_ENCRYPTARCHIVEJOB_P_H__ + +#include "job_p.h" + +namespace QGpgME +{ + +struct EncryptArchiveJobPrivate : public JobPrivate +{ + QString m_baseDirectory; +}; + +} + +#endif // __QGPGME_ENCRYPTARCHIVEJOB_P_H__ diff --git a/lang/qt/src/protocol.h b/lang/qt/src/protocol.h index 019633a8..bb9f060b 100644 --- a/lang/qt/src/protocol.h +++ b/lang/qt/src/protocol.h @@ -50,6 +50,7 @@ class ImportFromKeyserverJob; class ExportJob; class DownloadJob; class DeleteJob; +class EncryptArchiveJob; class EncryptJob; class DecryptJob; class SignJob; @@ -189,6 +190,8 @@ public: * OpenPGP key. */ virtual SetPrimaryUserIDJob *setPrimaryUserIDJob() const = 0; + + virtual EncryptArchiveJob *encryptArchiveJob(bool armor = false) const = 0; }; /** Obtain a reference to the OpenPGP Protocol. diff --git a/lang/qt/src/protocol_p.h b/lang/qt/src/protocol_p.h index 915fef99..e6b2b8a8 100644 --- a/lang/qt/src/protocol_p.h +++ b/lang/qt/src/protocol_p.h @@ -46,6 +46,7 @@ #include "qgpgmedeletejob.h" #include "qgpgmedownloadjob.h" #include "qgpgmesignencryptjob.h" +#include "qgpgmeencryptarchivejob.h" #include "qgpgmeencryptjob.h" #include "qgpgmesignjob.h" #include "qgpgmesignkeyjob.h" @@ -506,6 +507,18 @@ public: } return new QGpgME::QGpgMESetPrimaryUserIDJob{context}; } + + QGpgME::EncryptArchiveJob *encryptArchiveJob(bool armor) const override + { + if (mProtocol != GpgME::OpenPGP) { + return nullptr; + } + if (auto context = GpgME::Context::createForProtocol(mProtocol)) { + context->setArmor(armor); + return new QGpgME::QGpgMEEncryptArchiveJob{context}; + } + return nullptr; + } }; } diff --git a/lang/qt/src/qgpgmeencryptarchivejob.cpp b/lang/qt/src/qgpgmeencryptarchivejob.cpp new file mode 100644 index 00000000..63c55364 --- /dev/null +++ b/lang/qt/src/qgpgmeencryptarchivejob.cpp @@ -0,0 +1,152 @@ +/* + qgpgmeencryptarchivejob.cpp + + This file is part of qgpgme, the Qt API binding for gpgme + Copyright (c) 2004,2007,2008 Klarälvdalens Datakonsult AB + Copyright (c) 2016 by Bundesamt für Sicherheit in der Informationstechnik + Software engineering by Intevation GmbH + Copyright (c) 2022,2023 g10 Code GmbH + Software engineering by Ingo Klöcker + + QGpgME is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + QGpgME 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + In addition, as a special exception, the copyright holders give + permission to link the code of this program with any edition of + the Qt library by Trolltech AS, Norway (or with modified versions + of Qt that use the same license as Qt), and distribute linked + combinations including the two. You must obey the GNU General + Public License in all respects for all of the code used other than + Qt. If you modify this file, you may extend this exception to + your version of the file, but you are not obligated to do so. If + you do not wish to do so, delete this exception statement from + your version. +*/ + +#ifdef HAVE_CONFIG_H + #include "config.h" +#endif + +#include "qgpgmeencryptarchivejob.h" + +#include "dataprovider.h" +#include "encryptarchivejob_p.h" +#include "filelistdataprovider.h" + +// #include +#include +// #include +// +// #include +// #include +// +// #include + +using namespace QGpgME; +using namespace GpgME; + +namespace +{ + +class QGpgMEEncryptArchiveJobPrivate : public EncryptArchiveJobPrivate +{ + QGpgMEEncryptArchiveJob *q = nullptr; + +public: + QGpgMEEncryptArchiveJobPrivate(QGpgMEEncryptArchiveJob *qq) + : q{qq} + { + } + + ~QGpgMEEncryptArchiveJobPrivate() override = default; + +private: + void start() override + { + q->run(); + } +}; + +} + +QGpgMEEncryptArchiveJob::QGpgMEEncryptArchiveJob(Context *context) + : mixin_type{context} +{ + setJobPrivate(this, std::unique_ptr{new QGpgMEEncryptArchiveJobPrivate{this}}); + lateInitialization(); +} + +static QGpgMEEncryptArchiveJob::result_type encrypt(Context *ctx, + QThread *thread, + const std::vector &recipients, + const std::vector &paths, + const std::weak_ptr &cipherText_, + Context::EncryptionFlags flags, + const QString &baseDirectory) +{ + const std::shared_ptr cipherText = cipherText_.lock(); + const _detail::ToThreadMover ctMover(cipherText, thread); + + QGpgME::FileListDataProvider in{paths}; + Data indata(&in); + if (!baseDirectory.isEmpty()) { + indata.setFileName(baseDirectory.toStdString()); + } + + QGpgME::QIODeviceDataProvider out{cipherText}; + Data outdata(&out); + + flags = static_cast(flags | Context::EncryptArchive); + const EncryptionResult res = ctx->encrypt(recipients, indata, outdata, flags); + Error ae; + const QString log = _detail::audit_log_as_html(ctx, ae); + return std::make_tuple(res, log, ae); +} + +GpgME::Error QGpgMEEncryptArchiveJob::start(const std::vector &recipients, + const std::vector &paths, + const std::shared_ptr &cipherText, + const GpgME::Context::EncryptionFlags flags) +{ + if (!cipherText) { + return Error::fromCode(GPG_ERR_INV_VALUE); + } + + run(std::bind(&encrypt, + std::placeholders::_1, + std::placeholders::_2, + recipients, + paths, + std::placeholders::_3, + flags, + baseDirectory()), + cipherText); + return {}; +} + +// EncryptionResult QGpgMEEncryptArchiveJob::exec(const std::vector &recipients, const QByteArray &plainText, +// const Context::EncryptionFlags eflags, QByteArray &cipherText) +// { +// const result_type r = encrypt_qba(context(), recipients, plainText, eflags, mOutputIsBase64Encoded, fileName()); +// cipherText = std::get<1>(r); +// resultHook(r); +// return mResult; +// } + +void QGpgMEEncryptArchiveJob::resultHook(const result_type &tuple) +{ + mResult = std::get<0>(tuple); +} + +#include "qgpgmeencryptarchivejob.moc" diff --git a/lang/qt/src/qgpgmeencryptarchivejob.h b/lang/qt/src/qgpgmeencryptarchivejob.h new file mode 100644 index 00000000..81398d07 --- /dev/null +++ b/lang/qt/src/qgpgmeencryptarchivejob.h @@ -0,0 +1,82 @@ +/* + qgpgmeencryptarchivejob.h + + This file is part of qgpgme, the Qt API binding for gpgme + Copyright (c) 2023 g10 Code GmbH + Software engineering by Ingo Klöcker + + QGpgME is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + QGpgME 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + In addition, as a special exception, the copyright holders give + permission to link the code of this program with any edition of + the Qt library by Trolltech AS, Norway (or with modified versions + of Qt that use the same license as Qt), and distribute linked + combinations including the two. You must obey the GNU General + Public License in all respects for all of the code used other than + Qt. If you modify this file, you may extend this exception to + your version of the file, but you are not obligated to do so. If + you do not wish to do so, delete this exception statement from + your version. +*/ + +#ifndef __QGPGME_QGPGMEENCRYPTARCHIVEJOB_H__ +#define __QGPGME_QGPGMEENCRYPTARCHIVEJOB_H__ + +#include "encryptarchivejob.h" + +#include "threadedjobmixin.h" + +#include +#include + +namespace QGpgME +{ + +class QGpgMEEncryptArchiveJob +#ifdef Q_MOC_RUN + : public EncryptArchiveJob +#else + : public _detail::ThreadedJobMixin> +#endif +{ + Q_OBJECT +#ifdef Q_MOC_RUN +public Q_SLOTS: + void slotFinished(); +#endif +public: + explicit QGpgMEEncryptArchiveJob(GpgME::Context *context); + ~QGpgMEEncryptArchiveJob() = default; + + GpgME::Error start(const std::vector &recipients, + const std::vector &paths, + const std::shared_ptr &cipherText, + const GpgME::Context::EncryptionFlags flags) override; + + // GpgME::EncryptionResult exec(const std::vector &recipients, + // const std::vector &paths, + // const std::shared_ptr &cipherText, + // const GpgME::Context::EncryptionFlags flags) override; + + /* from ThreadedJobMixin */ + void resultHook(const result_type &r) override; + +private: + GpgME::EncryptionResult mResult; +}; + +} + +#endif // __QGPGME_QGPGMEENCRYPTARCHIVEJOB_H__ diff --git a/lang/qt/tests/Makefile.am b/lang/qt/tests/Makefile.am index 564a9ff7..97e2b417 100644 --- a/lang/qt/tests/Makefile.am +++ b/lang/qt/tests/Makefile.am @@ -86,6 +86,7 @@ t_wkdlookup_SOURCES = t-wkdlookup.cpp $(support_src) t_import_SOURCES = t-import.cpp $(support_src) t_revokekey_SOURCES = t-revokekey.cpp $(support_src) t_setprimaryuserid_SOURCES = t-setprimaryuserid.cpp $(support_src) +run_encryptarchivejob_SOURCES = run-encryptarchivejob.cpp run_exportjob_SOURCES = run-exportjob.cpp run_importjob_SOURCES = run-importjob.cpp run_keyformailboxjob_SOURCES = run-keyformailboxjob.cpp @@ -102,6 +103,7 @@ noinst_PROGRAMS = \ run-keyformailboxjob t-wkspublish t-verify t-various t-config t-remarks \ t-trustsignatures t-changeexpiryjob t-wkdlookup t-import t-revokekey \ t-setprimaryuserid \ + run-encryptarchivejob \ run-importjob run-exportjob run-receivekeysjob run-refreshkeysjob CLEANFILES = secring.gpg pubring.gpg pubring.kbx trustdb.gpg dirmngr.conf \ diff --git a/lang/qt/tests/run-encryptarchivejob.cpp b/lang/qt/tests/run-encryptarchivejob.cpp new file mode 100644 index 00000000..e48af9f8 --- /dev/null +++ b/lang/qt/tests/run-encryptarchivejob.cpp @@ -0,0 +1,158 @@ +/* + run-encryptarchivejob.cpp + + This file is part of QGpgME's test suite. + Copyright (c) 2023 by g10 Code GmbH + Software engineering by Ingo Klöcker + + QGpgME is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License, + version 2, as published by the Free Software Foundation. + + QGpgME 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + In addition, as a special exception, the copyright holders give + permission to link the code of this program with any edition of + the Qt library by Trolltech AS, Norway (or with modified versions + of Qt that use the same license as Qt), and distribute linked + combinations including the two. You must obey the GNU General + Public License in all respects for all of the code used other than + Qt. If you modify this file, you may extend this exception to + your version of the file, but you are not obligated to do so. If + you do not wish to do so, delete this exception statement from + your version. +*/ + +#ifdef HAVE_CONFIG_H + #include "config.h" +#endif + +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include +#include + +using namespace GpgME; + +std::ostream &operator<<(std::ostream &os, const QString &s) +{ + return os << s.toLocal8Bit().constData(); +} + +const char *displayName(Protocol protocol) +{ + switch (protocol) { + case GpgME::OpenPGP: + return "OpenPGP"; + case GpgME::CMS: + return "S/MIME"; + default: + return "Unknown protocol"; + } +} + +struct CommandLineOptions { + bool armor; + QString archiveName; + QString baseDirectory; + std::vector filesAndDirectories; +}; + +CommandLineOptions parseCommandLine(const QStringList &arguments) +{ + CommandLineOptions options; + + QCommandLineParser parser; + parser.setApplicationDescription("Test program for EncryptArchiveJob"); + parser.addHelpOption(); + parser.addOptions({ + {{"o", "output"}, "Write output to FILE.", "FILE"}, + {{"a", "armor"}, "Create ASCII armored output."}, + {{"C", "directory"}, "Change to DIRECTORY before creating the archive.", "DIRECTORY"}, + }); + parser.addPositionalArgument("files", "Files and directories to add to the archive", "[files] [directories]"); + + parser.process(arguments); + + const auto args = parser.positionalArguments(); + if (args.empty()) { + parser.showHelp(1); + } + + options.armor = parser.isSet("armor"); + options.archiveName = parser.value("output"); + options.baseDirectory = parser.value("directory"); + std::copy(args.begin(), args.end(), std::back_inserter(options.filesAndDirectories)); + + return options; +} + +std::shared_ptr createOutput(const QString &fileName) +{ + std::shared_ptr output; + + if (fileName.isEmpty()) { + output.reset(new QFile); + output->open(stdout, QIODevice::WriteOnly); + } else { + if (QFile::exists(fileName)) { + qCritical() << "File" << fileName << "exists. Bailing out."; + } else { + output.reset(new QFile{fileName}); + output->open(QIODevice::WriteOnly); + } + } + + return output; +} + +int main(int argc, char **argv) +{ + GpgME::initializeLibrary(); + + QCoreApplication app{argc, argv}; + app.setApplicationName("run-encryptarchivejob"); + + const auto options = parseCommandLine(app.arguments()); + + auto output = createOutput(options.archiveName); + if (!output) { + return 1; + } + + auto job = QGpgME::openpgp()->encryptArchiveJob(options.armor); + if (!job) { + std::cerr << "Error: Could not create job" << std::endl; + return 1; + } + job->setBaseDirectory(options.baseDirectory); + QObject::connect(job, &QGpgME::EncryptArchiveJob::result, &app, [](const GpgME::EncryptionResult &result, const QString &auditLog, const GpgME::Error &) { + std::cerr << "Diagnostics: " << auditLog << std::endl; + std::cerr << "Result: " << result << std::endl; + qApp->quit(); + }); + + const auto err = job->start({}, options.filesAndDirectories, output, GpgME::Context::None); + if (err) { + std::cerr << "Error: Starting the job failed: " << err.asString() << std::endl; + return 1; + } + + return app.exec(); +} From 48b11f576260286bddef3833e25d0059638fe0bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ingo=20Kl=C3=B6cker?= Date: Mon, 30 Jan 2023 11:58:01 +0100 Subject: [PATCH 22/24] core: Use signature modes as flags * src/engine-backend.h (engine_ops.sign): Rename argument mode to flags. * src/engine-gpg.c (gpg_sign): Rename argument mode to flags. Check for invalid combination of flags. * src/engine-gpgsm.c (gpgsm_sign): Rename argument mode to flags. Check for unsupported flags. * src/engine-uiserver.c (gpgsm_sign): Rename argument mode to flags. Check for unsupported flags. * src/engine.c, src/engine.h (_gpgme_engine_op_sign): Rename argument mode to flags. * src/gpgme.h.in (GPGME_SIG_MODE_ARCHIVE): Change value to 4. (gpgme_op_sign_start, gpgme_op_sign): Rename argument mode to flags. * src/sign.c (sign_start): Rename argument mode to flags. Adjust check for invalid flags. (gpgme_op_sign_start, gpgme_op_sign): Rename argument mode to flags. -- Using the signature mode constants as flags is more natural, even if currently all flags are mutually exclusive, because archives are signed with a normal signature. GnuPG-bug-id: 6342 --- src/engine-backend.h | 2 +- src/engine-gpg.c | 12 ++++++++---- src/engine-gpgsm.c | 7 +++++-- src/engine-uiserver.c | 7 +++++-- src/engine.c | 4 ++-- src/engine.h | 2 +- src/gpgme.h.in | 8 ++++---- src/sign.c | 21 +++++++++++---------- 8 files changed, 37 insertions(+), 26 deletions(-) diff --git a/src/engine-backend.h b/src/engine-backend.h index 07ea8f14..d430620b 100644 --- a/src/engine-backend.h +++ b/src/engine-backend.h @@ -119,7 +119,7 @@ struct engine_ops gpgme_key_t key, gpgme_tofu_policy_t policy); gpgme_error_t (*sign) (void *engine, gpgme_data_t in, gpgme_data_t out, - gpgme_sig_mode_t mode, int use_armor, + gpgme_sig_mode_t flags, int use_armor, int use_textmode, int include_certs, gpgme_ctx_t ctx /* FIXME */); gpgme_error_t (*verify) (void *engine, gpgme_verify_flags_t flags, diff --git a/src/engine-gpg.c b/src/engine-gpg.c index e921df0a..41f24d1e 100644 --- a/src/engine-gpg.c +++ b/src/engine-gpg.c @@ -3580,7 +3580,7 @@ gpg_tofu_policy (void *engine, gpgme_key_t key, gpgme_tofu_policy_t policy) static gpgme_error_t gpg_sign (void *engine, gpgme_data_t in, gpgme_data_t out, - gpgme_sig_mode_t mode, int use_armor, int use_textmode, + gpgme_sig_mode_t flags, int use_armor, int use_textmode, int include_certs, gpgme_ctx_t ctx /* FIXME */) { engine_gpg_t gpg = engine; @@ -3588,17 +3588,21 @@ gpg_sign (void *engine, gpgme_data_t in, gpgme_data_t out, (void)include_certs; - gpg->flags.use_gpgtar = mode == GPGME_SIG_MODE_ARCHIVE; + if ((flags != GPGME_SIG_MODE_NORMAL) && (flags != GPGME_SIG_MODE_DETACH) + && (flags != GPGME_SIG_MODE_CLEAR) && (flags != GPGME_SIG_MODE_ARCHIVE)) + return gpg_error (GPG_ERR_INV_VALUE); + + gpg->flags.use_gpgtar = !!(flags & GPGME_SIG_MODE_ARCHIVE); if (gpg->flags.use_gpgtar && !have_gpg_version (gpg, "2.4.1")) return gpg_error (GPG_ERR_NOT_SUPPORTED); - if (mode == GPGME_SIG_MODE_CLEAR) + if (flags & GPGME_SIG_MODE_CLEAR) err = add_arg (gpg, "--clearsign"); else { err = add_arg (gpg, "--sign"); - if (!err && mode == GPGME_SIG_MODE_DETACH) + if (!err && (flags & GPGME_SIG_MODE_DETACH)) err = add_arg (gpg, "--detach"); if (!err && use_armor) err = add_gpg_arg (gpg, "--armor"); diff --git a/src/engine-gpgsm.c b/src/engine-gpgsm.c index fdc85dd6..7ac4f2db 100644 --- a/src/engine-gpgsm.c +++ b/src/engine-gpgsm.c @@ -2043,7 +2043,7 @@ gpgsm_keylist_ext (void *engine, const char *pattern[], int secret_only, static gpgme_error_t gpgsm_sign (void *engine, gpgme_data_t in, gpgme_data_t out, - gpgme_sig_mode_t mode, int use_armor, int use_textmode, + gpgme_sig_mode_t flags, int use_armor, int use_textmode, int include_certs, gpgme_ctx_t ctx /* FIXME */) { engine_gpgsm_t gpgsm = engine; @@ -2057,6 +2057,9 @@ gpgsm_sign (void *engine, gpgme_data_t in, gpgme_data_t out, if (!gpgsm) return gpg_error (GPG_ERR_INV_VALUE); + if (flags & (GPGME_SIG_MODE_CLEAR | GPGME_SIG_MODE_ARCHIVE)) + return gpg_error (GPG_ERR_INV_VALUE); + /* FIXME: This does not work as RESET does not reset it so we can't revert back to default. */ if (include_certs != GPGME_INCLUDE_CERTS_DEFAULT) @@ -2105,7 +2108,7 @@ gpgsm_sign (void *engine, gpgme_data_t in, gpgme_data_t out, gpgsm_clear_fd (gpgsm, MESSAGE_FD); gpgsm->inline_data = NULL; - err = start (gpgsm, mode == GPGME_SIG_MODE_DETACH + err = start (gpgsm, (flags & GPGME_SIG_MODE_DETACH) ? "SIGN --detached" : "SIGN"); return err; } diff --git a/src/engine-uiserver.c b/src/engine-uiserver.c index 7aa9cfe3..a298bec6 100644 --- a/src/engine-uiserver.c +++ b/src/engine-uiserver.c @@ -1214,7 +1214,7 @@ uiserver_encrypt (void *engine, gpgme_key_t recp[], const char *recpstring, static gpgme_error_t uiserver_sign (void *engine, gpgme_data_t in, gpgme_data_t out, - gpgme_sig_mode_t mode, int use_armor, int use_textmode, + gpgme_sig_mode_t flags, int use_armor, int use_textmode, int include_certs, gpgme_ctx_t ctx /* FIXME */) { engine_uiserver_t uiserver = engine; @@ -1237,8 +1237,11 @@ uiserver_sign (void *engine, gpgme_data_t in, gpgme_data_t out, else return gpgme_error (GPG_ERR_UNSUPPORTED_PROTOCOL); + if (flags & (GPGME_SIG_MODE_CLEAR | GPGME_SIG_MODE_ARCHIVE)) + return gpg_error (GPG_ERR_INV_VALUE); + if (gpgrt_asprintf (&cmd, "SIGN%s%s", protocol, - (mode == GPGME_SIG_MODE_DETACH) ? " --detached" : "") < 0) + (flags & GPGME_SIG_MODE_DETACH) ? " --detached" : "") < 0) return gpg_error_from_syserror (); key = gpgme_signers_enum (ctx, 0); diff --git a/src/engine.c b/src/engine.c index 2397f88d..ab399e73 100644 --- a/src/engine.c +++ b/src/engine.c @@ -912,7 +912,7 @@ _gpgme_engine_op_keylist_data (engine_t engine, gpgme_keylist_mode_t mode, gpgme_error_t _gpgme_engine_op_sign (engine_t engine, gpgme_data_t in, gpgme_data_t out, - gpgme_sig_mode_t mode, int use_armor, + gpgme_sig_mode_t flags, int use_armor, int use_textmode, int include_certs, gpgme_ctx_t ctx /* FIXME */) { @@ -922,7 +922,7 @@ _gpgme_engine_op_sign (engine_t engine, gpgme_data_t in, gpgme_data_t out, if (!engine->ops->sign) return gpg_error (GPG_ERR_NOT_IMPLEMENTED); - return (*engine->ops->sign) (engine->engine, in, out, mode, use_armor, + return (*engine->ops->sign) (engine->engine, in, out, flags, use_armor, use_textmode, include_certs, ctx); } diff --git a/src/engine.h b/src/engine.h index e5654047..59d159a4 100644 --- a/src/engine.h +++ b/src/engine.h @@ -160,7 +160,7 @@ gpgme_error_t _gpgme_engine_op_keylist_data (engine_t engine, gpgme_keylist_mode_t mode, gpgme_data_t data); gpgme_error_t _gpgme_engine_op_sign (engine_t engine, gpgme_data_t in, - gpgme_data_t out, gpgme_sig_mode_t mode, + gpgme_data_t out, gpgme_sig_mode_t flags, int use_armor, int use_textmode, int include_certs, gpgme_ctx_t ctx /* FIXME */); diff --git a/src/gpgme.h.in b/src/gpgme.h.in index a337a124..3ea07a81 100644 --- a/src/gpgme.h.in +++ b/src/gpgme.h.in @@ -303,13 +303,13 @@ typedef enum gpgme_hash_algo_t; -/* The available signature modes. */ +/* The available signature mode flags. */ typedef enum { GPGME_SIG_MODE_NORMAL = 0, GPGME_SIG_MODE_DETACH = 1, GPGME_SIG_MODE_CLEAR = 2, - GPGME_SIG_MODE_ARCHIVE = 3 + GPGME_SIG_MODE_ARCHIVE = 4 } gpgme_sig_mode_t; @@ -1522,10 +1522,10 @@ gpgme_sign_result_t gpgme_op_sign_result (gpgme_ctx_t ctx); /* 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); + gpgme_sig_mode_t flags); gpgme_error_t gpgme_op_sign (gpgme_ctx_t ctx, gpgme_data_t plain, gpgme_data_t sig, - gpgme_sig_mode_t mode); + gpgme_sig_mode_t flags); /* diff --git a/src/sign.c b/src/sign.c index 7617d27f..7198d934 100644 --- a/src/sign.c +++ b/src/sign.c @@ -431,7 +431,7 @@ _gpgme_op_sign_init_result (gpgme_ctx_t ctx) 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_data_t sig, gpgme_sig_mode_t flags) { gpgme_error_t err; @@ -446,8 +446,9 @@ sign_start (gpgme_ctx_t ctx, int synchronous, gpgme_data_t plain, if (err) return err; - if (mode != GPGME_SIG_MODE_NORMAL && mode != GPGME_SIG_MODE_DETACH - && mode != GPGME_SIG_MODE_CLEAR && mode != GPGME_SIG_MODE_ARCHIVE) + if (flags & ~(GPGME_SIG_MODE_DETACH + |GPGME_SIG_MODE_CLEAR + |GPGME_SIG_MODE_ARCHIVE)) return gpg_error (GPG_ERR_INV_VALUE); if (!plain) @@ -466,7 +467,7 @@ sign_start (gpgme_ctx_t ctx, int synchronous, gpgme_data_t plain, _gpgme_engine_set_status_handler (ctx->engine, sign_status_handler, ctx); - return _gpgme_engine_op_sign (ctx->engine, plain, sig, mode, ctx->use_armor, + return _gpgme_engine_op_sign (ctx->engine, plain, sig, flags, ctx->use_armor, ctx->use_textmode, ctx->include_certs, ctx /* FIXME */); } @@ -475,16 +476,16 @@ sign_start (gpgme_ctx_t ctx, int synchronous, gpgme_data_t plain, /* 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) + gpgme_sig_mode_t flags) { gpg_error_t err; TRACE_BEG (DEBUG_CTX, "gpgme_op_sign_start", ctx, - "plain=%p, sig=%p, mode=%i", plain, sig, mode); + "plain=%p, sig=%p, flags=%i", plain, sig, flags); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); - err = sign_start (ctx, 0, plain, sig, mode); + err = sign_start (ctx, 0, plain, sig, flags); return TRACE_ERR (err); } @@ -492,17 +493,17 @@ gpgme_op_sign_start (gpgme_ctx_t ctx, gpgme_data_t plain, gpgme_data_t sig, /* 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_sig_mode_t flags) { gpgme_error_t err; TRACE_BEG (DEBUG_CTX, "gpgme_op_sign", ctx, - "plain=%p, sig=%p, mode=%i", plain, sig, mode); + "plain=%p, sig=%p, flags=%i", plain, sig, flags); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); - err = sign_start (ctx, 1, plain, sig, mode); + err = sign_start (ctx, 1, plain, sig, flags); if (!err) err = _gpgme_wait_one (ctx); return TRACE_ERR (err); From 2faa031af24959d5093da430d5f10fe30d77a75d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ingo=20Kl=C3=B6cker?= Date: Mon, 30 Jan 2023 12:08:43 +0100 Subject: [PATCH 23/24] cpp: Support new archive signing flag * lang/cpp/src/global.h (enum SignatureMode): Add constant SignArchive. * lang/cpp/src/context.cpp (sigmode2sigmode): Rename to sigflags2sigflags (sigflags2sigflags): ... and rename argument mode to flags and treat it as flags. Adjust the callers. (operator<<): Change local CHECK macro to handle flags. Add new flag to debug stream. * lang/cpp/src/signingresult.cpp (CreatedSignature::mode): Handle new flags (even if it cannot occur currently). -- GnuPG-bug-id: 6342 --- lang/cpp/src/context.cpp | 32 ++++++++++++++++++-------------- lang/cpp/src/global.h | 7 ++++++- lang/cpp/src/signingresult.cpp | 1 + 3 files changed, 25 insertions(+), 15 deletions(-) diff --git a/lang/cpp/src/context.cpp b/lang/cpp/src/context.cpp index f7f6f54d..68b061db 100644 --- a/lang/cpp/src/context.cpp +++ b/lang/cpp/src/context.cpp @@ -1278,14 +1278,22 @@ std::vector Context::signatureNotations() const return result; } -static gpgme_sig_mode_t sigmode2sigmode(SignatureMode mode) +static gpgme_sig_mode_t sigflags2sigflags(SignatureMode flags) { - switch (mode) { - default: - case NormalSignatureMode: return GPGME_SIG_MODE_NORMAL; - case Detached: return GPGME_SIG_MODE_DETACH; - case Clearsigned: return GPGME_SIG_MODE_CLEAR; + unsigned int result = 0; + if (flags & SignatureMode::NormalSignatureMode) { + result |= GPGME_SIG_MODE_NORMAL; } + if (flags & SignatureMode::Detached) { + result |= GPGME_SIG_MODE_DETACH; + } + if (flags & SignatureMode::Clearsigned) { + result |= GPGME_SIG_MODE_CLEAR; + } + if (flags & SignatureMode::SignArchive) { + result |= GPGME_SIG_MODE_ARCHIVE; + } + return static_cast(result); } SigningResult Context::sign(const Data &plainText, Data &signature, SignatureMode mode) @@ -1293,7 +1301,7 @@ SigningResult Context::sign(const Data &plainText, Data &signature, SignatureMod d->lastop = Private::Sign; const Data::Private *const pdp = plainText.impl(); Data::Private *const sdp = signature.impl(); - d->lasterr = gpgme_op_sign(d->ctx, pdp ? pdp->data : nullptr, sdp ? sdp->data : nullptr, sigmode2sigmode(mode)); + d->lasterr = gpgme_op_sign(d->ctx, pdp ? pdp->data : nullptr, sdp ? sdp->data : nullptr, sigflags2sigflags(mode)); return SigningResult(d->ctx, Error(d->lasterr)); } @@ -1302,7 +1310,7 @@ Error Context::startSigning(const Data &plainText, Data &signature, SignatureMod d->lastop = Private::Sign; const Data::Private *const pdp = plainText.impl(); Data::Private *const sdp = signature.impl(); - return Error(d->lasterr = gpgme_op_sign_start(d->ctx, pdp ? pdp->data : nullptr, sdp ? sdp->data : nullptr, sigmode2sigmode(mode))); + return Error(d->lasterr = gpgme_op_sign_start(d->ctx, pdp ? pdp->data : nullptr, sdp ? sdp->data : nullptr, sigflags2sigflags(mode))); } SigningResult Context::signingResult() const @@ -1889,16 +1897,12 @@ std::ostream &operator<<(std::ostream &os, KeyListMode mode) std::ostream &operator<<(std::ostream &os, SignatureMode mode) { os << "GpgME::SignatureMode("; - switch (mode) { -#define CHECK( x ) case x: os << #x; break +#define CHECK( x ) if ( !(mode & (x)) ) {} else do { os << #x " "; } while (0) CHECK(NormalSignatureMode); CHECK(Detached); CHECK(Clearsigned); + CHECK(SignArchive); #undef CHECK - default: - os << "???" "(" << static_cast(mode) << ')'; - break; - } return os << ')'; } diff --git a/lang/cpp/src/global.h b/lang/cpp/src/global.h index 84c8d462..c9c65cdb 100644 --- a/lang/cpp/src/global.h +++ b/lang/cpp/src/global.h @@ -74,7 +74,12 @@ enum KeyListMode { KeyListModeMask = 0x3ff }; -enum SignatureMode { NormalSignatureMode, Detached, Clearsigned }; +enum SignatureMode { + NormalSignatureMode = 0, + Detached = 1, + Clearsigned = 2, + SignArchive = 4, +}; enum class RevocationReason { Unspecified = 0, diff --git a/lang/cpp/src/signingresult.cpp b/lang/cpp/src/signingresult.cpp index 6e0dd90a..06169cbc 100644 --- a/lang/cpp/src/signingresult.cpp +++ b/lang/cpp/src/signingresult.cpp @@ -199,6 +199,7 @@ GpgME::SignatureMode GpgME::CreatedSignature::mode() const case GPGME_SIG_MODE_NORMAL: return NormalSignatureMode; case GPGME_SIG_MODE_DETACH: return Detached; case GPGME_SIG_MODE_CLEAR: return Clearsigned; + case GPGME_SIG_MODE_ARCHIVE: return SignArchive; // cannot happen } } From 8d672b3b7e5c5d487f20fb742a29fbee21236885 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ingo=20Kl=C3=B6cker?= Date: Mon, 30 Jan 2023 12:56:54 +0100 Subject: [PATCH 24/24] qt: Add job for creating signed archives * lang/qt/src/signarchivejob.cpp, lang/qt/src/signarchivejob.h, lang/qt/src/signarchivejob_p.h, lang/qt/src/qgpgmesignarchivejob.cpp, lang/qt/src/qgpgmesignarchivejob.h: New. * lang/qt/src/protocol.h (class Protocol): Add pure virtual member function signArchiveJob * lang/qt/src/protocol_p.h (Protocol::signArchiveJob): ... and implement it. * lang/qt/src/Makefile.am: Update accordingly. * lang/qt/tests/run-signarchivejob.cpp: New. * lang/qt/tests/Makefile.am: Add new test runner. -- GnuPG-bug-id: 6342 --- lang/qt/src/Makefile.am | 8 ++ lang/qt/src/protocol.h | 2 + lang/qt/src/protocol_p.h | 13 +++ lang/qt/src/qgpgmesignarchivejob.cpp | 141 ++++++++++++++++++++++++++ lang/qt/src/qgpgmesignarchivejob.h | 76 ++++++++++++++ lang/qt/src/signarchivejob.cpp | 62 ++++++++++++ lang/qt/src/signarchivejob.h | 88 ++++++++++++++++ lang/qt/src/signarchivejob_p.h | 49 +++++++++ lang/qt/tests/Makefile.am | 5 +- lang/qt/tests/run-signarchivejob.cpp | 145 +++++++++++++++++++++++++++ 10 files changed, 588 insertions(+), 1 deletion(-) create mode 100644 lang/qt/src/qgpgmesignarchivejob.cpp create mode 100644 lang/qt/src/qgpgmesignarchivejob.h create mode 100644 lang/qt/src/signarchivejob.cpp create mode 100644 lang/qt/src/signarchivejob.h create mode 100644 lang/qt/src/signarchivejob_p.h create mode 100644 lang/qt/tests/run-signarchivejob.cpp diff --git a/lang/qt/src/Makefile.am b/lang/qt/src/Makefile.am index 240e5716..928b6913 100644 --- a/lang/qt/src/Makefile.am +++ b/lang/qt/src/Makefile.am @@ -49,6 +49,7 @@ qgpgme_sources = \ qgpgmerefreshsmimekeysjob.cpp \ qgpgmerevokekeyjob.cpp \ qgpgmesetprimaryuseridjob.cpp \ + qgpgmesignarchivejob.cpp \ qgpgmesignencryptjob.cpp \ qgpgmesignjob.cpp qgpgmesignkeyjob.cpp qgpgmeverifydetachedjob.cpp \ qgpgmeverifyopaquejob.cpp qgpgmewkdlookupjob.cpp threadedjobmixin.cpp \ @@ -56,6 +57,7 @@ qgpgme_sources = \ qgpgmetofupolicyjob.cpp qgpgmequickjob.cpp \ defaultkeygenerationjob.cpp qgpgmewkspublishjob.cpp \ qgpgmegpgcardjob.cpp changeexpiryjob.cpp encryptjob.cpp importjob.cpp \ + signarchivejob.cpp \ signencryptjob.cpp \ dn.cpp cryptoconfig.cpp wkdlookupresult.cpp \ util.cpp @@ -89,6 +91,7 @@ qgpgme_headers= \ revokekeyjob.h \ setprimaryuseridjob.h \ specialjob.h \ + signarchivejob.h \ signjob.h \ signkeyjob.h \ signencryptjob.h \ @@ -137,6 +140,7 @@ camelcase_headers= \ RevokeKeyJob \ SetPrimaryUserIDJob \ SpecialJob \ + SignArchiveJob \ SignJob \ SignKeyJob \ SignEncryptJob \ @@ -189,6 +193,7 @@ private_qgpgme_headers = \ qgpgmerefreshsmimekeysjob.h \ qgpgmerevokekeyjob.h \ qgpgmesetprimaryuseridjob.h \ + qgpgmesignarchivejob.h \ qgpgmesignencryptjob.h \ qgpgmesignjob.h \ qgpgmesignkeyjob.h \ @@ -200,6 +205,7 @@ private_qgpgme_headers = \ qgpgmetofupolicyjob.h \ qgpgmegpgcardjob.h \ qgpgmequickjob.h \ + signarchivejob_p.h \ signencryptjob_p.h \ threadedjobmixin.h \ util.h @@ -247,6 +253,7 @@ qgpgme_moc_sources = \ qgpgmerefreshsmimekeysjob.moc \ qgpgmerevokekeyjob.moc \ qgpgmesetprimaryuseridjob.moc \ + qgpgmesignarchivejob.moc \ qgpgmesignencryptjob.moc \ qgpgmesignjob.moc \ qgpgmesignkeyjob.moc \ @@ -260,6 +267,7 @@ qgpgme_moc_sources = \ refreshkeysjob.moc \ revokekeyjob.moc \ setprimaryuseridjob.moc \ + signarchivejob.moc \ signencryptjob.moc \ signjob.moc \ signkeyjob.moc \ diff --git a/lang/qt/src/protocol.h b/lang/qt/src/protocol.h index bb9f060b..0f3e5b28 100644 --- a/lang/qt/src/protocol.h +++ b/lang/qt/src/protocol.h @@ -53,6 +53,7 @@ class DeleteJob; class EncryptArchiveJob; class EncryptJob; class DecryptJob; +class SignArchiveJob; class SignJob; class SignKeyJob; class VerifyDetachedJob; @@ -192,6 +193,7 @@ public: virtual SetPrimaryUserIDJob *setPrimaryUserIDJob() const = 0; virtual EncryptArchiveJob *encryptArchiveJob(bool armor = false) const = 0; + virtual SignArchiveJob *signArchiveJob(bool armor = false) const = 0; }; /** Obtain a reference to the OpenPGP Protocol. diff --git a/lang/qt/src/protocol_p.h b/lang/qt/src/protocol_p.h index e6b2b8a8..73405c6d 100644 --- a/lang/qt/src/protocol_p.h +++ b/lang/qt/src/protocol_p.h @@ -48,6 +48,7 @@ #include "qgpgmesignencryptjob.h" #include "qgpgmeencryptarchivejob.h" #include "qgpgmeencryptjob.h" +#include "qgpgmesignarchivejob.h" #include "qgpgmesignjob.h" #include "qgpgmesignkeyjob.h" #include "qgpgmeexportjob.h" @@ -519,6 +520,18 @@ public: } return nullptr; } + + QGpgME::SignArchiveJob *signArchiveJob(bool armor) const override + { + if (mProtocol != GpgME::OpenPGP) { + return nullptr; + } + if (auto context = GpgME::Context::createForProtocol(mProtocol)) { + context->setArmor(armor); + return new QGpgME::QGpgMESignArchiveJob{context}; + } + return nullptr; + } }; } diff --git a/lang/qt/src/qgpgmesignarchivejob.cpp b/lang/qt/src/qgpgmesignarchivejob.cpp new file mode 100644 index 00000000..d9abec42 --- /dev/null +++ b/lang/qt/src/qgpgmesignarchivejob.cpp @@ -0,0 +1,141 @@ +/* + qgpgmesignarchivejob.cpp + + This file is part of qgpgme, the Qt API binding for gpgme + Copyright (c) 2004,2007,2008 Klarälvdalens Datakonsult AB + Copyright (c) 2016 by Bundesamt für Sicherheit in der Informationstechnik + Software engineering by Intevation GmbH + Copyright (c) 2022,2023 g10 Code GmbH + Software engineering by Ingo Klöcker + + QGpgME is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + QGpgME 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + In addition, as a special exception, the copyright holders give + permission to link the code of this program with any edition of + the Qt library by Trolltech AS, Norway (or with modified versions + of Qt that use the same license as Qt), and distribute linked + combinations including the two. You must obey the GNU General + Public License in all respects for all of the code used other than + Qt. If you modify this file, you may extend this exception to + your version of the file, but you are not obligated to do so. If + you do not wish to do so, delete this exception statement from + your version. +*/ + +#ifdef HAVE_CONFIG_H + #include "config.h" +#endif + +#include "qgpgmesignarchivejob.h" + +#include "dataprovider.h" +#include "signarchivejob_p.h" +#include "filelistdataprovider.h" + +#include + +using namespace QGpgME; +using namespace GpgME; + +namespace +{ + +class QGpgMESignArchiveJobPrivate : public SignArchiveJobPrivate +{ + QGpgMESignArchiveJob *q = nullptr; + +public: + QGpgMESignArchiveJobPrivate(QGpgMESignArchiveJob *qq) + : q{qq} + { + } + + ~QGpgMESignArchiveJobPrivate() override = default; + +private: + void start() override + { + q->run(); + } +}; + +} + +QGpgMESignArchiveJob::QGpgMESignArchiveJob(Context *context) + : mixin_type{context} +{ + setJobPrivate(this, std::unique_ptr{new QGpgMESignArchiveJobPrivate{this}}); + lateInitialization(); +} + +static QGpgMESignArchiveJob::result_type sign(Context *ctx, + QThread *thread, + const std::vector &signers, + const std::vector &paths, + const std::weak_ptr &output_, + const QString &baseDirectory) +{ + const std::shared_ptr output = output_.lock(); + const _detail::ToThreadMover ctMover(output, thread); + + QGpgME::FileListDataProvider in{paths}; + Data indata(&in); + if (!baseDirectory.isEmpty()) { + indata.setFileName(baseDirectory.toStdString()); + } + + QGpgME::QIODeviceDataProvider out{output}; + Data outdata(&out); + + ctx->clearSigningKeys(); + for (const Key &signer : signers) { + if (!signer.isNull()) { + if (const Error err = ctx->addSigningKey(signer)) { + return std::make_tuple(SigningResult{err}, QString{}, Error{}); + } + } + } + + const SigningResult res = ctx->sign(indata, outdata, GpgME::SignArchive); + Error ae; + const QString log = _detail::audit_log_as_html(ctx, ae); + return std::make_tuple(res, log, ae); +} + +GpgME::Error QGpgMESignArchiveJob::start(const std::vector &signers, + const std::vector &paths, + const std::shared_ptr &output) +{ + if (!output) { + return Error::fromCode(GPG_ERR_INV_VALUE); + } + + run(std::bind(&sign, + std::placeholders::_1, + std::placeholders::_2, + signers, + paths, + std::placeholders::_3, + baseDirectory()), + output); + return {}; +} + +void QGpgMESignArchiveJob::resultHook(const result_type &tuple) +{ + mResult = std::get<0>(tuple); +} + +#include "qgpgmesignarchivejob.moc" diff --git a/lang/qt/src/qgpgmesignarchivejob.h b/lang/qt/src/qgpgmesignarchivejob.h new file mode 100644 index 00000000..ade4e8dc --- /dev/null +++ b/lang/qt/src/qgpgmesignarchivejob.h @@ -0,0 +1,76 @@ +/* + qgpgmesignarchivejob.h + + This file is part of qgpgme, the Qt API binding for gpgme + Copyright (c) 2023 g10 Code GmbH + Software engineering by Ingo Klöcker + + QGpgME is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + QGpgME 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + In addition, as a special exception, the copyright holders give + permission to link the code of this program with any edition of + the Qt library by Trolltech AS, Norway (or with modified versions + of Qt that use the same license as Qt), and distribute linked + combinations including the two. You must obey the GNU General + Public License in all respects for all of the code used other than + Qt. If you modify this file, you may extend this exception to + your version of the file, but you are not obligated to do so. If + you do not wish to do so, delete this exception statement from + your version. +*/ + +#ifndef __QGPGME_QGPGMESIGNARCHIVEJOB_H__ +#define __QGPGME_QGPGMESIGNARCHIVEJOB_H__ + +#include "signarchivejob.h" + +#include "threadedjobmixin.h" + +#include +#include + +namespace QGpgME +{ + +class QGpgMESignArchiveJob +#ifdef Q_MOC_RUN + : public SignArchiveJob +#else + : public _detail::ThreadedJobMixin> +#endif +{ + Q_OBJECT +#ifdef Q_MOC_RUN +public Q_SLOTS: + void slotFinished(); +#endif +public: + explicit QGpgMESignArchiveJob(GpgME::Context *context); + ~QGpgMESignArchiveJob() = default; + + GpgME::Error start(const std::vector &signers, + const std::vector &paths, + const std::shared_ptr &output) override; + + /* from ThreadedJobMixin */ + void resultHook(const result_type &r) override; + +private: + GpgME::SigningResult mResult; +}; + +} + +#endif // __QGPGME_QGPGMESIGNARCHIVEJOB_H__ diff --git a/lang/qt/src/signarchivejob.cpp b/lang/qt/src/signarchivejob.cpp new file mode 100644 index 00000000..fcdf241d --- /dev/null +++ b/lang/qt/src/signarchivejob.cpp @@ -0,0 +1,62 @@ +/* + signarchivejob.cpp + + This file is part of qgpgme, the Qt API binding for gpgme + Copyright (c) 2023 g10 Code GmbH + Software engineering by Ingo Klöcker + + QGpgME is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + QGpgME 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + In addition, as a special exception, the copyright holders give + permission to link the code of this program with any edition of + the Qt library by Trolltech AS, Norway (or with modified versions + of Qt that use the same license as Qt), and distribute linked + combinations including the two. You must obey the GNU General + Public License in all respects for all of the code used other than + Qt. If you modify this file, you may extend this exception to + your version of the file, but you are not obligated to do so. If + you do not wish to do so, delete this exception statement from + your version. +*/ + +#ifdef HAVE_CONFIG_H + #include "config.h" +#endif + +#include "signarchivejob.h" +#include "signarchivejob_p.h" + +using namespace QGpgME; + +SignArchiveJob::SignArchiveJob(QObject *parent) + : Job{parent} +{ +} + +SignArchiveJob::~SignArchiveJob() = default; + +void SignArchiveJob::setBaseDirectory(const QString &baseDirectory) +{ + auto d = jobPrivate(this); + d->m_baseDirectory = baseDirectory; +} + +QString SignArchiveJob::baseDirectory() const +{ + auto d = jobPrivate(this); + return d->m_baseDirectory; +} + +#include "signarchivejob.moc" diff --git a/lang/qt/src/signarchivejob.h b/lang/qt/src/signarchivejob.h new file mode 100644 index 00000000..6b8cd175 --- /dev/null +++ b/lang/qt/src/signarchivejob.h @@ -0,0 +1,88 @@ +/* + signarchivejob.h + + This file is part of qgpgme, the Qt API binding for gpgme + Copyright (c) 2023 g10 Code GmbH + Software engineering by Ingo Klöcker + + QGpgME is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + QGpgME 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + In addition, as a special exception, the copyright holders give + permission to link the code of this program with any edition of + the Qt library by Trolltech AS, Norway (or with modified versions + of Qt that use the same license as Qt), and distribute linked + combinations including the two. You must obey the GNU General + Public License in all respects for all of the code used other than + Qt. If you modify this file, you may extend this exception to + your version of the file, but you are not obligated to do so. If + you do not wish to do so, delete this exception statement from + your version. +*/ + +#ifndef __QGPGME_SIGNARCHIVEJOB_H__ +#define __QGPGME_SIGNARCHIVEJOB_H__ + +#include "job.h" + +#ifdef BUILDING_QGPGME +# include "context.h" +#else +# include +#endif + +namespace GpgME +{ +class Key; +} + +namespace QGpgME +{ + +/** + * Abstract base class for job for creating signed archives + */ +class QGPGME_EXPORT SignArchiveJob : public Job +{ + Q_OBJECT +protected: + explicit SignArchiveJob(QObject *parent); +public: + ~SignArchiveJob() override; + + void setBaseDirectory(const QString &baseDirectory); + QString baseDirectory() const; + + /** + * Starts the creation of a signed archive. + * + * Creates a signed archive with the files and directories in \a paths. + * The archive is signed with the keys in \a signers or with the default + * key, if \a signers is empty. The signed archive is written to \a output. + * + * Emits result() when the job has finished. + */ + virtual GpgME::Error start(const std::vector &signers, + const std::vector &paths, + const std::shared_ptr &output) = 0; + +Q_SIGNALS: + void result(const GpgME::SigningResult &result, + const QString &auditLogAsHtml = {}, + const GpgME::Error &auditLogError = {}); +}; + +} + +#endif // __QGPGME_SIGNARCHIVEJOB_H__ diff --git a/lang/qt/src/signarchivejob_p.h b/lang/qt/src/signarchivejob_p.h new file mode 100644 index 00000000..9176e7b4 --- /dev/null +++ b/lang/qt/src/signarchivejob_p.h @@ -0,0 +1,49 @@ +/* + signarchivejob_p.h + + This file is part of qgpgme, the Qt API binding for gpgme + Copyright (c) 2023 g10 Code GmbH + Software engineering by Ingo Klöcker + + QGpgME is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + QGpgME 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + In addition, as a special exception, the copyright holders give + permission to link the code of this program with any edition of + the Qt library by Trolltech AS, Norway (or with modified versions + of Qt that use the same license as Qt), and distribute linked + combinations including the two. You must obey the GNU General + Public License in all respects for all of the code used other than + Qt. If you modify this file, you may extend this exception to + your version of the file, but you are not obligated to do so. If + you do not wish to do so, delete this exception statement from + your version. +*/ + +#ifndef __QGPGME_SIGNARCHIVEJOB_P_H__ +#define __QGPGME_SIGNARCHIVEJOB_P_H__ + +#include "job_p.h" + +namespace QGpgME +{ + +struct SignArchiveJobPrivate : public JobPrivate +{ + QString m_baseDirectory; +}; + +} + +#endif // __QGPGME_SIGNARCHIVEJOB_P_H__ diff --git a/lang/qt/tests/Makefile.am b/lang/qt/tests/Makefile.am index 97e2b417..4e43d986 100644 --- a/lang/qt/tests/Makefile.am +++ b/lang/qt/tests/Makefile.am @@ -92,6 +92,7 @@ run_importjob_SOURCES = run-importjob.cpp run_keyformailboxjob_SOURCES = run-keyformailboxjob.cpp run_receivekeysjob_SOURCES = run-receivekeysjob.cpp run_refreshkeysjob_SOURCES = run-refreshkeysjob.cpp +run_signarchivejob_SOURCES = run-signarchivejob.cpp nodist_t_keylist_SOURCES = $(moc_files) @@ -104,7 +105,9 @@ noinst_PROGRAMS = \ t-trustsignatures t-changeexpiryjob t-wkdlookup t-import t-revokekey \ t-setprimaryuserid \ run-encryptarchivejob \ - run-importjob run-exportjob run-receivekeysjob run-refreshkeysjob + run-importjob run-exportjob run-receivekeysjob run-refreshkeysjob \ + run-signarchivejob + CLEANFILES = secring.gpg pubring.gpg pubring.kbx trustdb.gpg dirmngr.conf \ gpg-agent.conf pubring.kbx~ S.gpg-agent gpg.conf pubring.gpg~ \ diff --git a/lang/qt/tests/run-signarchivejob.cpp b/lang/qt/tests/run-signarchivejob.cpp new file mode 100644 index 00000000..c426ba93 --- /dev/null +++ b/lang/qt/tests/run-signarchivejob.cpp @@ -0,0 +1,145 @@ +/* + run-signarchivejob.cpp + + This file is part of QGpgME's test suite. + Copyright (c) 2023 by g10 Code GmbH + Software engineering by Ingo Klöcker + + QGpgME is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License, + version 2, as published by the Free Software Foundation. + + QGpgME 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + In addition, as a special exception, the copyright holders give + permission to link the code of this program with any edition of + the Qt library by Trolltech AS, Norway (or with modified versions + of Qt that use the same license as Qt), and distribute linked + combinations including the two. You must obey the GNU General + Public License in all respects for all of the code used other than + Qt. If you modify this file, you may extend this exception to + your version of the file, but you are not obligated to do so. If + you do not wish to do so, delete this exception statement from + your version. +*/ + +#ifdef HAVE_CONFIG_H + #include "config.h" +#endif + +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include + +using namespace GpgME; + +std::ostream &operator<<(std::ostream &os, const QString &s) +{ + return os << s.toLocal8Bit().constData(); +} + +struct CommandLineOptions { + bool armor; + QString archiveName; + QString baseDirectory; + std::vector filesAndDirectories; +}; + +CommandLineOptions parseCommandLine(const QStringList &arguments) +{ + CommandLineOptions options; + + QCommandLineParser parser; + parser.setApplicationDescription("Test program for SignArchiveJob"); + parser.addHelpOption(); + parser.addOptions({ + {{"o", "output"}, "Write output to FILE.", "FILE"}, + {{"a", "armor"}, "Create ASCII armored output."}, + {{"C", "directory"}, "Change to DIRECTORY before creating the archive.", "DIRECTORY"}, + }); + parser.addPositionalArgument("files", "Files and directories to add to the archive", "[files] [directories]"); + + parser.process(arguments); + + const auto args = parser.positionalArguments(); + if (args.empty()) { + parser.showHelp(1); + } + + options.armor = parser.isSet("armor"); + options.archiveName = parser.value("output"); + options.baseDirectory = parser.value("directory"); + std::copy(args.begin(), args.end(), std::back_inserter(options.filesAndDirectories)); + + return options; +} + +std::shared_ptr createOutput(const QString &fileName) +{ + std::shared_ptr output; + + if (fileName.isEmpty()) { + output.reset(new QFile); + output->open(stdout, QIODevice::WriteOnly); + } else { + if (QFile::exists(fileName)) { + qCritical() << "File" << fileName << "exists. Bailing out."; + } else { + output.reset(new QFile{fileName}); + output->open(QIODevice::WriteOnly); + } + } + + return output; +} + +int main(int argc, char **argv) +{ + GpgME::initializeLibrary(); + + QCoreApplication app{argc, argv}; + app.setApplicationName("run-signarchivejob"); + + const auto options = parseCommandLine(app.arguments()); + + auto output = createOutput(options.archiveName); + if (!output) { + return 1; + } + + auto job = QGpgME::openpgp()->signArchiveJob(options.armor); + if (!job) { + std::cerr << "Error: Could not create job" << std::endl; + return 1; + } + job->setBaseDirectory(options.baseDirectory); + QObject::connect(job, &QGpgME::SignArchiveJob::result, &app, [](const GpgME::SigningResult &result, const QString &auditLog, const GpgME::Error &) { + std::cerr << "Diagnostics: " << auditLog << std::endl; + std::cerr << "Result: " << result << std::endl; + qApp->quit(); + }); + + const auto err = job->start({}, options.filesAndDirectories, output); + if (err) { + std::cerr << "Error: Starting the job failed: " << err.asString() << std::endl; + return 1; + } + + return app.exec(); +}