diff options
Diffstat (limited to '')
-rw-r--r-- | src/Makefile.am | 2 | ||||
-rw-r--r-- | src/context.h | 2 | ||||
-rw-r--r-- | src/engine-assuan.c | 1 | ||||
-rw-r--r-- | src/engine-backend.h | 4 | ||||
-rw-r--r-- | src/engine-g13.c | 1 | ||||
-rw-r--r-- | src/engine-gpg.c | 102 | ||||
-rw-r--r-- | src/engine-gpgconf.c | 1 | ||||
-rw-r--r-- | src/engine-gpgsm.c | 1 | ||||
-rw-r--r-- | src/engine-spawn.c | 1 | ||||
-rw-r--r-- | src/engine-uiserver.c | 1 | ||||
-rw-r--r-- | src/engine.c | 16 | ||||
-rw-r--r-- | src/engine.h | 5 | ||||
-rw-r--r-- | src/gpgme.def | 2 | ||||
-rw-r--r-- | src/gpgme.h.in | 24 | ||||
-rw-r--r-- | src/keysign.c | 218 | ||||
-rw-r--r-- | src/libgpgme.vers | 2 |
16 files changed, 371 insertions, 12 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index d541f87c..39752b30 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -84,7 +84,7 @@ main_sources = \ op-support.c \ encrypt.c encrypt-sign.c decrypt.c decrypt-verify.c verify.c \ sign.c passphrase.c progress.c \ - key.c keylist.c trust-item.c trustlist.c \ + key.c keylist.c keysign.c trust-item.c trustlist.c \ import.c export.c genkey.c delete.c edit.c getauditlog.c \ opassuan.c passwd.c spawn.c assuan-support.c \ engine.h engine-backend.h engine.c engine-gpg.c status-table.c \ diff --git a/src/context.h b/src/context.h index de69a7a6..c099d668 100644 --- a/src/context.h +++ b/src/context.h @@ -38,7 +38,7 @@ typedef enum OPDATA_DECRYPT, OPDATA_SIGN, OPDATA_ENCRYPT, OPDATA_PASSPHRASE, OPDATA_IMPORT, OPDATA_GENKEY, OPDATA_KEYLIST, OPDATA_EDIT, OPDATA_VERIFY, OPDATA_TRUSTLIST, OPDATA_ASSUAN, OPDATA_VFS_MOUNT, - OPDATA_PASSWD, OPDATA_EXPORT + OPDATA_PASSWD, OPDATA_EXPORT, OPDATA_KEYSIGN } ctx_op_data_id_t; diff --git a/src/engine-assuan.c b/src/engine-assuan.c index 5f0ea06d..6f11cc02 100644 --- a/src/engine-assuan.c +++ b/src/engine-assuan.c @@ -775,6 +775,7 @@ struct engine_ops _gpgme_engine_ops_assuan = NULL, /* import */ NULL, /* keylist */ NULL, /* keylist_ext */ + NULL, /* keysign */ NULL, /* sign */ NULL, /* trustlist */ NULL, /* verify */ diff --git a/src/engine-backend.h b/src/engine-backend.h index ec1a58c5..ed3e3031 100644 --- a/src/engine-backend.h +++ b/src/engine-backend.h @@ -98,6 +98,10 @@ struct engine_ops int secret_only, int reserved, gpgme_keylist_mode_t mode, int engine_flags); + gpgme_error_t (*keysign) (void *engine, + gpgme_key_t key, const char *userid, + unsigned long expires, unsigned int flags, + gpgme_ctx_t ctx); gpgme_error_t (*sign) (void *engine, gpgme_data_t in, gpgme_data_t out, gpgme_sig_mode_t mode, int use_armor, int use_textmode, int include_certs, diff --git a/src/engine-g13.c b/src/engine-g13.c index 7f225eb2..0da00f7b 100644 --- a/src/engine-g13.c +++ b/src/engine-g13.c @@ -792,6 +792,7 @@ struct engine_ops _gpgme_engine_ops_g13 = NULL, /* import */ NULL, /* keylist */ NULL, /* keylist_ext */ + NULL, /* keysign */ NULL, /* sign */ NULL, /* trustlist */ NULL, /* verify */ diff --git a/src/engine-gpg.c b/src/engine-gpg.c index 534d5d10..dc0906dd 100644 --- a/src/engine-gpg.c +++ b/src/engine-gpg.c @@ -206,14 +206,16 @@ close_notify_handler (int fd, void *opaque) /* If FRONT is true, push at the front of the list. Use this for options added late in the process. */ static gpgme_error_t -_add_arg (engine_gpg_t gpg, const char *arg, int front, int *arg_locp) +_add_arg (engine_gpg_t gpg, const char *prefix, const char *arg, size_t arglen, + int front, int *arg_locp) { struct arg_and_data_s *a; + size_t prefixlen = prefix? strlen (prefix) : 0; assert (gpg); assert (arg); - a = malloc (sizeof *a + strlen (arg)); + a = malloc (sizeof *a + prefixlen + arglen); if (!a) return gpg_error_from_syserror (); @@ -221,7 +223,10 @@ _add_arg (engine_gpg_t gpg, const char *arg, int front, int *arg_locp) a->dup_to = -1; a->arg_locp = arg_locp; - strcpy (a->arg, arg); + if (prefixlen) + memcpy (a->arg, prefix, prefixlen); + memcpy (a->arg + prefixlen, arg, arglen); + a->arg[prefixlen + arglen] = 0; if (front) { a->next = gpg->arglist; @@ -243,24 +248,36 @@ _add_arg (engine_gpg_t gpg, const char *arg, int front, int *arg_locp) return 0; } + static gpgme_error_t add_arg_ext (engine_gpg_t gpg, const char *arg, int front) { - return _add_arg (gpg, arg, front, NULL); + return _add_arg (gpg, NULL, arg, strlen (arg), front, NULL); } - static gpgme_error_t add_arg_with_locp (engine_gpg_t gpg, const char *arg, int *locp) { - return _add_arg (gpg, arg, 0, locp); + return _add_arg (gpg, NULL, arg, strlen (arg), 0, locp); } - static gpgme_error_t add_arg (engine_gpg_t gpg, const char *arg) { - return add_arg_ext (gpg, arg, 0); + return _add_arg (gpg, NULL, arg, strlen (arg), 0, NULL); +} + +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); +} + +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); } @@ -1606,7 +1623,8 @@ append_args_from_signers (engine_gpg_t gpg, gpgme_ctx_t ctx /* FIXME */) err = add_arg (gpg, s); } gpgme_key_unref (key); - if (err) break; + if (err) + break; } return err; } @@ -2644,6 +2662,71 @@ gpg_keylist_ext (void *engine, const char *pattern[], int secret_only, static gpgme_error_t +gpg_keysign (void *engine, gpgme_key_t key, const char *userid, + unsigned long expire, unsigned int flags, + gpgme_ctx_t ctx) +{ + engine_gpg_t gpg = engine; + gpgme_error_t err; + const char *s; + + if (!key || !key->fpr) + return gpg_error (GPG_ERR_INV_ARG); + + if (!have_gpg_version (gpg, "2.1.12")) + return gpg_error (GPG_ERR_NOT_SUPPORTED); + + if ((flags & GPGME_KEYSIGN_LOCAL)) + err = add_arg (gpg, "--quick-lsign-key"); + else + err = add_arg (gpg, "--quick-sign-key"); + + if (!err) + err = append_args_from_signers (gpg, ctx); + + /* If an expiration time has been given use that. If none has been + * given the default from gpg.conf is used. To make sure not to set + * an expiration time at all the flag GPGME_KEYSIGN_NOEXPIRE can be + * used. */ + if (!err && (expire || (flags & GPGME_KEYSIGN_NOEXPIRE))) + { + char tmpbuf[8+20]; + + if ((flags & GPGME_KEYSIGN_NOEXPIRE)) + expire = 0; + snprintf (tmpbuf, sizeof tmpbuf, "seconds=%lu", expire); + err = add_arg (gpg, "--default-cert-expire"); + if (!err) + err = add_arg (gpg, tmpbuf); + } + + if (!err) + err = add_arg (gpg, "--"); + + if (!err) + err = add_arg (gpg, key->fpr); + if (!err && userid) + { + if ((flags & GPGME_KEYSIGN_LFSEP)) + { + for (; !err && (s = strchr (userid, '\n')); userid = s + 1) + if ((s - userid)) + err = add_arg_len (gpg, "=", userid, s - userid); + if (!err && *userid) + err = add_arg_pfx (gpg, "=", userid); + } + else + err = add_arg_pfx (gpg, "=", userid); + } + + if (!err) + err = start (gpg); + + return err; +} + + +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, int include_certs, gpgme_ctx_t ctx /* FIXME */) @@ -2816,6 +2899,7 @@ struct engine_ops _gpgme_engine_ops_gpg = gpg_import, gpg_keylist, gpg_keylist_ext, + gpg_keysign, gpg_sign, gpg_trustlist, gpg_verify, diff --git a/src/engine-gpgconf.c b/src/engine-gpgconf.c index 015ef001..8be76cb2 100644 --- a/src/engine-gpgconf.c +++ b/src/engine-gpgconf.c @@ -957,6 +957,7 @@ struct engine_ops _gpgme_engine_ops_gpgconf = NULL, /* import */ NULL, /* keylist */ NULL, /* keylist_ext */ + NULL, /* keysign */ NULL, /* sign */ NULL, /* trustlist */ NULL, /* verify */ diff --git a/src/engine-gpgsm.c b/src/engine-gpgsm.c index 071626a9..aae9d28d 100644 --- a/src/engine-gpgsm.c +++ b/src/engine-gpgsm.c @@ -2074,6 +2074,7 @@ struct engine_ops _gpgme_engine_ops_gpgsm = gpgsm_import, gpgsm_keylist, gpgsm_keylist_ext, + NULL, /* keysign */ gpgsm_sign, NULL, /* trustlist */ gpgsm_verify, diff --git a/src/engine-spawn.c b/src/engine-spawn.c index e2ee8ba6..82dbc0bf 100644 --- a/src/engine-spawn.c +++ b/src/engine-spawn.c @@ -460,6 +460,7 @@ struct engine_ops _gpgme_engine_ops_spawn = NULL, /* import */ NULL, /* keylist */ NULL, /* keylist_ext */ + NULL, /* keysign */ NULL, /* sign */ NULL, /* trustlist */ NULL, /* verify */ diff --git a/src/engine-uiserver.c b/src/engine-uiserver.c index 2817527f..827c3475 100644 --- a/src/engine-uiserver.c +++ b/src/engine-uiserver.c @@ -1364,6 +1364,7 @@ struct engine_ops _gpgme_engine_ops_uiserver = NULL, /* import */ NULL, /* keylist */ NULL, /* keylist_ext */ + NULL, /* keysign */ uiserver_sign, NULL, /* trustlist */ uiserver_verify, diff --git a/src/engine.c b/src/engine.c index b3bbcab7..47bb23ce 100644 --- a/src/engine.c +++ b/src/engine.c @@ -795,6 +795,22 @@ _gpgme_engine_op_genkey (engine_t engine, gpgme_error_t +_gpgme_engine_op_keysign (engine_t engine, gpgme_key_t key, const char *userid, + unsigned long expires, unsigned int flags, + gpgme_ctx_t ctx) +{ + if (!engine) + return gpg_error (GPG_ERR_INV_VALUE); + + if (!engine->ops->keysign) + return gpg_error (GPG_ERR_NOT_IMPLEMENTED); + + return (*engine->ops->keysign) (engine->engine, + key, userid, expires, flags, ctx); +} + + +gpgme_error_t _gpgme_engine_op_import (engine_t engine, gpgme_data_t keydata, gpgme_key_t *keyarray) { diff --git a/src/engine.h b/src/engine.h index eb37da8f..6f338351 100644 --- a/src/engine.h +++ b/src/engine.h @@ -121,6 +121,11 @@ gpgme_error_t _gpgme_engine_op_genkey (engine_t engine, unsigned int extraflags, gpgme_data_t pubkey, gpgme_data_t seckey); +gpgme_error_t _gpgme_engine_op_keysign (engine_t engine, + gpgme_key_t key, const char *userid, + unsigned long expires, + unsigned int flags, + gpgme_ctx_t ctx); gpgme_error_t _gpgme_engine_op_import (engine_t engine, gpgme_data_t keydata, gpgme_key_t *keyarray); diff --git a/src/gpgme.def b/src/gpgme.def index 4c7ff414..f987b38d 100644 --- a/src/gpgme.def +++ b/src/gpgme.def @@ -237,6 +237,8 @@ EXPORTS gpgme_op_adduid @177 gpgme_op_revuid_start @178 gpgme_op_revuid @179 + gpgme_op_keysign_start @180 + gpgme_op_keysign @181 ; END diff --git a/src/gpgme.h.in b/src/gpgme.h.in index a26b4324..121e2ce1 100644 --- a/src/gpgme.h.in +++ b/src/gpgme.h.in @@ -1921,10 +1921,32 @@ gpgme_error_t gpgme_op_delete_start (gpgme_ctx_t ctx, const gpgme_key_t key, gpgme_error_t gpgme_op_delete (gpgme_ctx_t ctx, const gpgme_key_t key, int allow_secret); + +/* + * Key signing interface + */ + +/* Flags for the key signing functions. */ +#define GPGME_KEYSIGN_LOCAL (1 << 7) /* Create a local signature. */ +#define GPGME_KEYSIGN_LFSEP (1 << 8) /* Indicate LF separated user ids. */ +#define GPGME_KEYSIGN_NOEXPIRE (1 << 9) /* Force no expiration. */ + + +/* Sign the USERID of KEY using the current set of signers. */ +gpgme_error_t gpgme_op_keysign_start (gpgme_ctx_t ctx, + gpgme_key_t key, const char *userid, + unsigned long expires, + unsigned int flags); +gpgme_error_t gpgme_op_keysign (gpgme_ctx_t ctx, + gpgme_key_t key, const char *userid, + unsigned long expires, + unsigned int flags); + + /* - * Key Edit interface + * Key edit interface */ /* Edit the key KEY. Send status and command requests to FNC and diff --git a/src/keysign.c b/src/keysign.c new file mode 100644 index 00000000..7d08c11e --- /dev/null +++ b/src/keysign.c @@ -0,0 +1,218 @@ +/* keysign.c - OpenPGP key signing + * Copyright (C) 2016 g10 Code GmbH + * + * This file is part of GPGME. + * + * GPGME is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * GPGME is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#if HAVE_CONFIG_H +#include <config.h> +#endif +#include <stdlib.h> +#include <string.h> +#include <errno.h> + +#include "gpgme.h" +#include "debug.h" +#include "context.h" +#include "ops.h" +#include "util.h" + + +typedef struct +{ + /* The error code from a FAILURE status line or 0. */ + gpg_error_t failure_code; + + /* The error code from certain ERROR status lines or 0. */ + gpg_error_t error_code; + +} *op_data_t; + + +static void +release_op_data (void *hook) +{ + op_data_t opd = (op_data_t) hook; + + (void)opd; +} + + +/* Parse an error status line. Return the error location and the + error code. The function may modify ARGS. */ +static char * +parse_error (char *args, gpg_error_t *r_err) +{ + char *where = strchr (args, ' '); + char *which; + + if (where) + { + *where = '\0'; + which = where + 1; + + where = strchr (which, ' '); + if (where) + *where = '\0'; + + where = args; + } + else + { + *r_err = trace_gpg_error (GPG_ERR_INV_ENGINE); + return NULL; + } + + *r_err = atoi (which); + + return where; +} + + +static gpgme_error_t +keysign_status_handler (void *priv, gpgme_status_code_t code, char *args) +{ + gpgme_ctx_t ctx = (gpgme_ctx_t) priv; + gpgme_error_t err; + void *hook; + op_data_t opd; + char *loc; + + /* Pipe the status code through the progress status handler. */ + err = _gpgme_progress_status_handler (ctx, code, args); + if (err) + return err; + + err = _gpgme_op_data_lookup (ctx, OPDATA_KEYSIGN, &hook, -1, NULL); + opd = hook; + if (err) + return err; + + switch (code) + { + case GPGME_STATUS_ERROR: + loc = parse_error (args, &err); + if (!loc) + return err; + if (!opd->error_code) + opd->error_code = err; + break; + + case GPGME_STATUS_FAILURE: + opd->failure_code = _gpgme_parse_failure (args); + break; + + case GPGME_STATUS_EOF: + if (opd->error_code) + return opd->error_code; + else if (opd->failure_code) + return opd->failure_code; + break; + + case GPGME_STATUS_INQUIRE_MAXLEN: + if (ctx->status_cb && !ctx->full_status) + { + err = ctx->status_cb (ctx->status_cb_value, "INQUIRE_MAXLEN", args); + if (err) + return err; + } + break; + + default: + break; + } + return 0; +} + + +/* Sign the USERID of KEY using the current set of signers. If USERID + * is NULL, sign all user ids. To put several user ids into USERID, + * separate them by LF and set the flag GPGME_KEYSIGN_LFSEP. */ +static gpgme_error_t +keysign_start (gpgme_ctx_t ctx, int synchronous, + gpgme_key_t key, const char *userid, + unsigned long expires, unsigned int flags) +{ + gpgme_error_t err; + void *hook; + op_data_t opd; + + if (ctx->protocol != GPGME_PROTOCOL_OPENPGP) + return gpgme_error (GPG_ERR_UNSUPPORTED_PROTOCOL); + + err = _gpgme_op_reset (ctx, synchronous); + if (err) + return err; + + if (!key) + return gpg_error (GPG_ERR_INV_ARG); + + err = _gpgme_op_data_lookup (ctx, OPDATA_KEYSIGN, &hook, + sizeof (*opd), release_op_data); + opd = hook; + if (err) + return err; + + _gpgme_engine_set_status_handler (ctx->engine, keysign_status_handler, ctx); + + if (ctx->passphrase_cb) + { + err = _gpgme_engine_set_command_handler + (ctx->engine, _gpgme_passphrase_command_handler, ctx, NULL); + if (err) + return err; + } + + return _gpgme_engine_op_keysign (ctx->engine, + key, userid, expires, flags, ctx); +} + + +/* Sign the USERID of KEY using the current set of signers. */ +gpgme_error_t +gpgme_op_keysign_start (gpgme_ctx_t ctx, gpgme_key_t key, const char *userid, + unsigned long expires, unsigned int flags) +{ + gpgme_error_t err; + + TRACE_BEG3 (DEBUG_CTX, "gpgme_op_keysign_start", ctx, + "key=%p, uid='%s' flags=0x%x", key, userid, flags); + + if (!ctx) + return TRACE_ERR (gpg_error (GPG_ERR_INV_ARG)); + + err = keysign_start (ctx, 0, key, userid, expires, flags); + return TRACE_ERR (err); +} + + +gpgme_error_t +gpgme_op_keysign (gpgme_ctx_t ctx, gpgme_key_t key, const char *userid, + unsigned long expires, unsigned int flags) +{ + gpgme_error_t err; + + TRACE_BEG3 (DEBUG_CTX, "gpgme_op_keysign", ctx, + "key=%p, uid='%s' flags=0x%x", key, userid, flags); + + if (!ctx) + return TRACE_ERR (gpg_error (GPG_ERR_INV_ARG)); + + err = keysign_start (ctx, 1, key, userid, expires, flags); + if (!err) + err = _gpgme_wait_one (ctx); + return TRACE_ERR (err); +} diff --git a/src/libgpgme.vers b/src/libgpgme.vers index 8193ee4d..d86eee8f 100644 --- a/src/libgpgme.vers +++ b/src/libgpgme.vers @@ -111,6 +111,8 @@ GPGME_1.1 { gpgme_op_adduid; gpgme_op_revuid_start; gpgme_op_revuid; + gpgme_op_keysign_start; + gpgme_op_keysign; }; |