core: New function gpgme_op_setexpire.

* src/gpgme.h.in (gpgme_op_setexpire_start, gpgme_op_setexpire): New.
* src/libgpgme.vers, src/gpgme.def: Add new functions.
* src/genkey.c (setexpire): New.
(gpgme_op_setexpire_start, gpgme_op_setexpire): New.
* src/engine.h, src/engine.c: (_gpgme_engine_op_setexpire): New.
* src/engine-backend.h (engine_ops): Add 'setexpire' and adjust all
engine initializers.
* src/engine-gpg.c (gpg_setexpire): New.
(_gpgme_engine_ops_gpg): Set setexpire to gpg_setexpire.
* doc/gpgme.texi: Document new functions.
* tests/run-genkey.c: Add option --setexpire.
--

This extends GPGME to support the --quick-set-expire command
added by GnuPG 2.1.22. This allows changing subkeys expiry
date without going through the editinteractor interface.

Co-authored-by: Andre Heinecke <aheinecke@gnupg.org>
GnuPG-bug-id: 4999
This commit is contained in:
Ingo Klöcker 2020-08-04 14:32:31 +02:00
parent 81db412245
commit db82e99a8a
17 changed files with 308 additions and 2 deletions

9
NEWS
View File

@ -1,6 +1,15 @@
Noteworthy changes in version 1.14.1 (unreleased)
-------------------------------------------------
* New function gpgme_op_setexpire to make changing the expiration
easier (requires GnuPG 2.1.22). [#4999]
* Interface changes relative to the 1.14.0 release:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
gpgme_op_setexpire_start NEW.
gpgme_op_setexpire NEW.
Noteworthy changes in version 1.14.0 (2020-07-16)
-------------------------------------------------

View File

@ -4016,6 +4016,81 @@ The function @code{gpgme_key_unref} releases a reference for the key
and all resources associated to it will be released.
@end deftypefun
@c
@c gpgme_op_setexpire
@c
@deftypefun gpgme_error_t gpgme_op_setexpire @
(@w{gpgme_ctx_t @var{ctx}}, @
@w{gpgme_key_t @var{key}}, @
@w{unsigned long @var{expires}}, @
@w{const char *@var{subfprs}}, @
@w{unsigned int @var{reserved}});
@since{1.14.1}
The function @code{gpgme_op_setexpire} sets the expiration time of
the key @var{key} or of the specified subkeys.
This function requires at least version 2.1.22 of GnuPG.
@var{key} specifies the key to operate on.
@var{expires} specifies the expiration time in seconds from now.
To be similar to other usages where expiration times are provided
in unsigned long this is similar to the key creation date
and so it is in seconds from NOW.
The common case is to use 0 to not set an expiration time.
Note that this parameter takes an unsigned long value and not
a @code{time_t} to avoid problems on systems which use a signed
32 bit @code{time_t}. Note further that the OpenPGP protocol
uses 32 bit values for timestamps and thus can
only encode dates up to the year 2106.
@var{subfprs} selects the subkey(s) for which the expiration time
should be set. If @var{subfprs} is set to @code{NULL}, then the
expiration time of the primary key is set. If @var{subfprs} is
an asterisk (@code{*}), then the expiration times of all non-revoked
and not yet expired subkeys are set. To select more than one subkey
put all subkey fingerprints into one string separated by linefeeds
characters (@code{\n}).
@var{reserved} is reserved for later use and must be @code{0}.
@end deftypefun
@deftypefun gpgme_error_t gpgme_op_setexpire_start @
(@w{gpgme_ctx_t @var{ctx}}, @
@w{gpgme_key_t @var{key}}, @
@w{unsigned long @var{expires}}, @
@w{const char *@var{subfprs}}, @
@w{unsigned int @var{flags}});
@since{1.14.1}
The function @code{gpgme_op_setexpire_start} initiates a
@code{gpgme_op_setexpire} operation; see there for details. It must
be completed by calling @code{gpgme_wait} on the context.
@xref{Waiting For Completion}.
@end deftypefun
@deftypefun gpgme_error_t gpgme_op_revuid_start @
(@w{gpgme_ctx_t @var{ctx}}, @
@w{gpgme_key_t @var{key}}, @
@w{const char *@var{userid}}, @
@w{unsigned int @var{flags}});
@since{1.14.1}
The function @code{gpgme_op_setexpire_start} initiates a
@code{gpgme_op_setexpire} operation; see there for details. It must
be completed by calling @code{gpgme_wait} on the context.
@xref{Waiting For Completion}.
@end deftypefun
@node Generating Keys
@subsection Generating Keys

View File

@ -828,6 +828,7 @@ struct engine_ops _gpgme_engine_ops_assuan =
NULL, /* sign */
NULL, /* verify */
NULL, /* getauditlog */
NULL, /* setexpire */
llass_transact, /* opassuan_transact */
NULL, /* conf_load */
NULL, /* conf_save */

View File

@ -120,6 +120,9 @@ struct engine_ops
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,
unsigned long expires, const char *subfprs,
unsigned int reserved);
gpgme_error_t (*opassuan_transact) (void *engine,
const char *command,
gpgme_assuan_data_cb_t data_cb,

View File

@ -808,6 +808,7 @@ struct engine_ops _gpgme_engine_ops_g13 =
NULL, /* sign */
NULL, /* verify */
NULL, /* getauditlog */
NULL, /* setexpire */
g13_transact,
NULL, /* conf_load */
NULL, /* conf_save */

View File

@ -3426,6 +3426,59 @@ gpg_getauditlog (void *engine, gpgme_data_t output, unsigned int flags)
#undef MYBUFLEN
}
static gpgme_error_t
gpg_setexpire (void *engine, gpgme_key_t key, unsigned long expires,
const char *subfprs, unsigned int reserved)
{
engine_gpg_t gpg = engine;
gpgme_error_t err;
const char *s;
if (reserved)
return gpg_error (GPG_ERR_INV_VALUE);
if (!key || !key->fpr)
return gpg_error (GPG_ERR_INV_ARG);
if (!have_gpg_version (gpg, "2.1.22"))
return gpg_error (GPG_ERR_NOT_SUPPORTED);
err = add_arg (gpg, "--quick-set-expire");
if (!err)
err = add_arg (gpg, "--");
if (!err)
err = add_arg (gpg, key->fpr);
if (!err)
{
char tmpbuf[8+20];
snprintf (tmpbuf, sizeof tmpbuf, "seconds=%lu", expires);
err = add_arg (gpg, tmpbuf);
}
if (!err && subfprs)
{
for (; !err && (s = strchr (subfprs, '\n')); subfprs = s + 1)
{
if ((s - subfprs))
{
err = add_arg_len (gpg, NULL, subfprs, s - subfprs);
}
}
if (!err && *subfprs)
{
err = add_arg (gpg, subfprs);
}
}
if (!err)
err = start (gpg);
return err;
}
struct engine_ops _gpgme_engine_ops_gpg =
@ -3464,6 +3517,7 @@ struct engine_ops _gpgme_engine_ops_gpg =
gpg_sign,
gpg_verify,
gpg_getauditlog,
gpg_setexpire,
NULL, /* opassuan_transact */
NULL, /* conf_load */
NULL, /* conf_save */

View File

@ -1306,6 +1306,7 @@ struct engine_ops _gpgme_engine_ops_gpgconf =
NULL, /* sign */
NULL, /* verify */
NULL, /* getauditlog */
NULL, /* setexpire */
NULL, /* opassuan_transact */
gpgconf_conf_load,
gpgconf_conf_save,

View File

@ -2326,6 +2326,7 @@ struct engine_ops _gpgme_engine_ops_gpgsm =
gpgsm_sign,
gpgsm_verify,
gpgsm_getauditlog,
NULL, /* setexpire */
NULL, /* opassuan_transact */
NULL, /* conf_load */
NULL, /* conf_save */

View File

@ -468,6 +468,7 @@ struct engine_ops _gpgme_engine_ops_spawn =
NULL, /* sign */
NULL, /* verify */
NULL, /* getauditlog */
NULL, /* setexpire */
NULL, /* opassuan_transact */
NULL, /* conf_load */
NULL, /* conf_save */

View File

@ -1439,6 +1439,7 @@ struct engine_ops _gpgme_engine_ops_uiserver =
uiserver_sign,
uiserver_verify,
NULL, /* getauditlog */
NULL, /* setexpire */
NULL, /* opassuan_transact */
NULL, /* conf_load */
NULL, /* conf_save */

View File

@ -1128,3 +1128,17 @@ _gpgme_engine_op_spawn (engine_t engine,
return (*engine->ops->opspawn) (engine->engine, file, argv,
datain, dataout, dataerr, flags);
}
gpgme_error_t
_gpgme_engine_op_setexpire (engine_t engine, gpgme_key_t key,
unsigned long expires, const char *subfprs,
unsigned int reserved)
{
if (!engine)
return gpg_error (GPG_ERR_INV_VALUE);
if (!engine->ops->setexpire)
return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
return (*engine->ops->setexpire) (engine->engine, key, expires, subfprs, reserved);
}

View File

@ -210,6 +210,11 @@ gpgme_error_t _gpgme_engine_op_spawn (engine_t engine,
gpgme_data_t dataout,
gpgme_data_t dataerr,
unsigned int flags);
gpgme_error_t _gpgme_engine_op_setexpire (engine_t engine,
gpgme_key_t key,
unsigned long expires,
const char *subfprs,
unsigned int reserved);
/* The available engine option flags. */
#define GPGME_ENGINE_FLAG_OFFLINE 1

View File

@ -663,3 +663,55 @@ gpgme_op_set_uid_flag (gpgme_ctx_t ctx,
{
return set_uid_flag (ctx, 1, key, userid, name, value);
}
/* Set the expiration time of a key or its subkeys. See
--quick-set-expire in the gnupg documentation. */
static gpg_error_t
setexpire (gpgme_ctx_t ctx, int synchronous,
gpgme_key_t key,
unsigned long expires,
const char *subfprs,
unsigned int reserved)
{
gpgme_error_t err = 0;
TRACE_BEG (DEBUG_CTX, "gpgme_op_setexpire", ctx,
"%d key=%p expiry: %lu subkeys: '%s' reserved=0x%x",
synchronous, key, expires, subfprs, reserved);
if (!ctx || !key)
return TRACE_ERR (gpg_error (GPG_ERR_INV_ARG));
err = _gpgme_op_reset (ctx, synchronous);
if (err)
return err;
err = _gpgme_engine_op_setexpire (ctx->engine, key, expires, subfprs, reserved);
if (synchronous && !err)
err = _gpgme_wait_one (ctx);
return TRACE_ERR (err);
}
/* See setexpire. */
gpgme_error_t
gpgme_op_setexpire_start (gpgme_ctx_t ctx,
gpgme_key_t key,
unsigned long expires,
const char *subfprs,
unsigned int reserved)
{
return setexpire (ctx, 0, key, expires, subfprs, reserved);
}
/* See setexpire. This is the synchronous variant. */
gpgme_error_t
gpgme_op_setexpire (gpgme_ctx_t ctx,
gpgme_key_t key,
unsigned long expires,
const char *subfprs,
unsigned int reserved)
{
return setexpire (ctx, 1, key, expires, subfprs, reserved);
}

View File

@ -274,5 +274,8 @@ EXPORTS
gpgme_data_new_from_estream @204
gpgme_op_setexpire @205
gpgme_op_setexpire_start @206
; END

View File

@ -1868,6 +1868,13 @@ gpgme_error_t gpgme_op_set_uid_flag (gpgme_ctx_t ctx,
gpgme_key_t key, const char *userid,
const char *name, const char *value);
/* Change the expiry of a key. */
gpgme_error_t gpgme_op_setexpire_start (gpgme_ctx_t ctx,
gpgme_key_t key, unsigned long expires,
const char *subfprs, unsigned int reserved);
gpgme_error_t gpgme_op_setexpire (gpgme_ctx_t ctx,
gpgme_key_t key, unsigned long expires,
const char *subfprs, unsigned int reserved);
/* Retrieve a pointer to the result of a genkey, createkey, or
* createsubkey operation. */

View File

@ -273,6 +273,9 @@ GPGME_1.0 {
gpgme_err_code_from_syserror;
gpgme_err_set_errno;
gpgme_op_setexpire;
gpgme_op_setexpire_start;
local:
*;

View File

@ -205,12 +205,15 @@ show_usage (int ex)
" for addkey: FPR [ALGO [USAGE [EXPIRESECONDS]]]\n"
" for adduid: FPR USERID\n"
" for revuid: FPR USERID\n"
" for setexpire: FPR EXPIRE [SUBFPRS]\n"
" for set-primary: FPR USERID\n"
"Options:\n"
" --addkey add a subkey to the key with FPR\n"
" --adduid add a user id to the key with FPR\n"
" --revuid revoke a user id from the key with FPR\n"
" --set-primary set the primary key flag on USERID\n"
" --setexpire set the expiration time of the key FPR\n"
" or of its subkeys SUBFPRS\n"
" --verbose run in verbose mode\n"
" --status print status lines from the backend\n"
" --progress print progress info\n"
@ -238,12 +241,17 @@ main (int argc, char **argv)
int adduid = 0;
int revuid = 0;
int setpri = 0;
int setexpire = 0;
const char *userid;
const char *algo = NULL;
const char *newuserid = NULL;
const char *subfprs = NULL;
unsigned int flags = 0;
unsigned long expire = 0;
gpgme_genkey_result_t result;
int i;
size_t n;
char *subfprs_buffer = NULL;
if (argc)
{ argc--; argv++; }
@ -264,6 +272,7 @@ main (int argc, char **argv)
adduid = 0;
revuid = 0;
setpri = 0;
setexpire = 0;
argc--; argv++;
}
else if (!strcmp (*argv, "--adduid"))
@ -272,6 +281,7 @@ main (int argc, char **argv)
adduid = 1;
revuid = 0;
setpri = 0;
setexpire = 0;
argc--; argv++;
}
else if (!strcmp (*argv, "--revuid"))
@ -280,6 +290,7 @@ main (int argc, char **argv)
adduid = 0;
revuid = 1;
setpri = 0;
setexpire = 0;
argc--; argv++;
}
else if (!strcmp (*argv, "--set-primary"))
@ -288,6 +299,16 @@ main (int argc, char **argv)
adduid = 0;
revuid = 0;
setpri = 1;
setexpire = 0;
argc--; argv++;
}
else if (!strcmp (*argv, "--setexpire"))
{
addkey = 0;
adduid = 0;
revuid = 0;
setpri = 0;
setexpire = 1;
argc--; argv++;
}
else if (!strcmp (*argv, "--verbose"))
@ -341,6 +362,48 @@ main (int argc, char **argv)
userid = argv[0];
newuserid = argv[1];
}
else if (setexpire)
{
if (argc < 2)
{
show_usage (1);
}
userid = argv[0];
argc--; argv++;
expire = parse_expire_string (argv[0]);
argc--; argv++;
if (argc > 1)
{
/* Several subkey fprs given */
for (i=0, n = 0; i < argc; i++)
n += strlen (argv[1]) + 1;
n++;
subfprs_buffer = malloc (n);
if (!subfprs_buffer)
{
fprintf (stderr, PGM ": malloc failed: %s\n",
gpg_strerror (gpg_error_from_syserror ()));
exit (1);
}
*subfprs_buffer = 0;
for (i=0; i < argc; i++)
{
strcat (subfprs_buffer, argv[i]);
strcat (subfprs_buffer, "\n");
}
subfprs = subfprs_buffer;
}
else if (argc)
{
/* One subkey fpr (or '*') given */
subfprs = *argv;
}
else
{
/* No subkey fpr given. */
subfprs = NULL;
}
}
else
{
if (!argc || argc > 4)
@ -373,7 +436,8 @@ main (int argc, char **argv)
gpgme_set_passphrase_cb (ctx, passphrase_cb, NULL);
}
if (addkey || adduid || revuid || setpri)
if (addkey || adduid || revuid || setpri || setexpire)
{
gpgme_key_t akey;
@ -425,6 +489,17 @@ main (int argc, char **argv)
exit (1);
}
}
else if (setexpire)
{
err = gpgme_op_setexpire (ctx, akey, expire, subfprs, 0);
if (err)
{
fprintf (stderr, PGM ": gpgme_op_setexpire failed: %s\n",
gpg_strerror (err));
exit (1);
}
}
gpgme_key_unref (akey);
}
else
@ -438,7 +513,7 @@ main (int argc, char **argv)
}
}
if (!setpri)
if (!setpri && !setexpire)
{
result = gpgme_op_genkey_result (ctx);
if (!result)