diff --git a/NEWS b/NEWS index 59322e5f..0a7816f1 100644 --- a/NEWS +++ b/NEWS @@ -3,6 +3,8 @@ Noteworthy changes in version 1.14.0 (unreleased) * New keylist mode to force the engine to return the keygrip. [#4820] + * New export mode to export as OpenSSH public key. [#4310] + * New context flag "extended-edit" to enable expert key edit. [#4734] * Deprecate the anyway non working trustlist functions. [#4834] @@ -23,6 +25,7 @@ Noteworthy changes in version 1.14.0 (unreleased) * Interface changes relative to the 1.13.1 release: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ GPGME_KEYLIST_MODE_WITH_KEYGRIP NEW. + GPGME_EXPORT_MODE_SSH NEW. gpgme_user_id_t EXTENDED: New field 'uidhash'. cpp: UserID::remark NEW. cpp: UserID::remarks NEW. diff --git a/doc/gpgme.texi b/doc/gpgme.texi index 81c43ff4..6c1d0622 100644 --- a/doc/gpgme.texi +++ b/doc/gpgme.texi @@ -4679,6 +4679,14 @@ If this bit is set, the smallest possible key is exported. For OpenPGP keys it removes all signatures except for the latest self-signatures. For X.509 keys it has no effect. +@item GPGME_EXPORT_MODE_SSH +@since{1.4.0} + +If this bit is set, the latest authentication key of the requested +OpenPGP key is exported in the OpenSSH public key format. This +accepts just a single key; to force the export of a specific subkey +a fingerprint pattern with an appended exclamation mark may be used. + @item GPGME_EXPORT_MODE_SECRET @since{1.6.0} diff --git a/src/engine-gpg.c b/src/engine-gpg.c index 9362e297..af2533d8 100644 --- a/src/engine-gpg.c +++ b/src/engine-gpg.c @@ -2330,6 +2330,7 @@ export_common (engine_gpg_t gpg, gpgme_export_mode_t mode, if ((mode & ~(GPGME_EXPORT_MODE_EXTERN |GPGME_EXPORT_MODE_MINIMAL + |GPGME_EXPORT_MODE_SSH |GPGME_EXPORT_MODE_SECRET))) return gpg_error (GPG_ERR_NOT_SUPPORTED); @@ -2345,6 +2346,15 @@ export_common (engine_gpg_t gpg, gpgme_export_mode_t mode, if (err) ; + else if ((mode & GPGME_EXPORT_MODE_SSH)) + { + if (have_gpg_version (gpg, "2.1.11")) + err = add_arg (gpg, "--export-ssh-key"); + else + err = gpg_error (GPG_ERR_NOT_SUPPORTED); + if (!err) + err = add_data (gpg, keydata, 1, 1); + } else if ((mode & GPGME_EXPORT_MODE_EXTERN)) { err = add_arg (gpg, "--send-keys"); diff --git a/src/export.c b/src/export.c index abf260ad..4d576679 100644 --- a/src/export.c +++ b/src/export.c @@ -218,6 +218,7 @@ export_ext_start (gpgme_ctx_t ctx, int synchronous, const char *pattern[], if ((mode & ~(GPGME_EXPORT_MODE_EXTERN |GPGME_EXPORT_MODE_MINIMAL |GPGME_EXPORT_MODE_SECRET + |GPGME_EXPORT_MODE_SSH |GPGME_EXPORT_MODE_RAW |GPGME_EXPORT_MODE_PKCS12))) return gpg_error (GPG_ERR_INV_VALUE); /* Invalid flags in MODE. */ @@ -478,4 +479,3 @@ gpgme_op_export_keys (gpgme_ctx_t ctx, return TRACE_ERR (err); } - diff --git a/src/gpgme.h.in b/src/gpgme.h.in index 2decc551..b4f817b4 100644 --- a/src/gpgme.h.in +++ b/src/gpgme.h.in @@ -407,6 +407,7 @@ gpgme_pinentry_mode_t; #define GPGME_EXPORT_MODE_RAW 32 #define GPGME_EXPORT_MODE_PKCS12 64 #define GPGME_EXPORT_MODE_NOUID 128 /* Experimental(!)*/ +#define GPGME_EXPORT_MODE_SSH 256 typedef unsigned int gpgme_export_mode_t; diff --git a/tests/run-export.c b/tests/run-export.c index ecce80dc..623c7331 100644 --- a/tests/run-export.c +++ b/tests/run-export.c @@ -38,14 +38,25 @@ static int verbose; +static gpg_error_t +status_cb (void *opaque, const char *keyword, const char *value) +{ + (void)opaque; + fprintf (stderr, "status_cb: %s %s\n", keyword, value); + return 0; +} + + static int show_usage (int ex) { fputs ("usage: " PGM " [options] USERIDS\n\n" "Options:\n" " --verbose run in verbose mode\n" + " --status print status lines from the backend\n" " --openpgp use OpenPGP protocol (default)\n" " --cms use X.509 protocol\n" + " --ssh export as ssh public key\n" " --extern send keys to the keyserver (TAKE CARE!)\n" " --secret export secret keys instead of public keys\n" " --raw use PKCS#1 as secret key format\n" @@ -67,6 +78,7 @@ main (int argc, char **argv) gpgme_data_t out; gpgme_protocol_t protocol = GPGME_PROTOCOL_OpenPGP; gpgme_export_mode_t mode = 0; + int print_status = 0; if (argc) { argc--; argv++; } @@ -86,6 +98,11 @@ main (int argc, char **argv) verbose = 1; argc--; argv++; } + else if (!strcmp (*argv, "--status")) + { + print_status = 1; + argc--; argv++; + } else if (!strcmp (*argv, "--openpgp")) { protocol = GPGME_PROTOCOL_OpenPGP; @@ -101,6 +118,11 @@ main (int argc, char **argv) mode |= GPGME_EXPORT_MODE_EXTERN; argc--; argv++; } + else if (!strcmp (*argv, "--ssh")) + { + mode |= GPGME_EXPORT_MODE_SSH; + argc--; argv++; + } else if (!strcmp (*argv, "--secret")) { mode |= GPGME_EXPORT_MODE_SECRET; @@ -129,52 +151,68 @@ main (int argc, char **argv) err = gpgme_new (&ctx); fail_if_err (err); gpgme_set_protocol (ctx, protocol); - - /* Lookup the keys. */ - err = gpgme_op_keylist_ext_start (ctx, (const char**)argv, 0, 0); - fail_if_err (err); - - while (!(err = gpgme_op_keylist_next (ctx, &key))) + if (print_status) { - printf ("keyid: %s (fpr: %s)\n", - key->subkeys?nonnull (key->subkeys->keyid):"?", - key->subkeys?nonnull (key->subkeys->fpr):"?"); - - if (keyidx < DIM (keyarray)-1) - keyarray[keyidx++] = key; - else - { - fprintf (stderr, PGM": too many keys" - "- skipping this key\n"); - gpgme_key_unref (key); - } + gpgme_set_status_cb (ctx, status_cb, NULL); + gpgme_set_ctx_flag (ctx, "full-status", "1"); } - if (gpgme_err_code (err) != GPG_ERR_EOF) - fail_if_err (err); - err = gpgme_op_keylist_end (ctx); - fail_if_err (err); - keyarray[keyidx] = NULL; - - result = gpgme_op_keylist_result (ctx); - if (result->truncated) - { - fprintf (stderr, PGM ": key listing unexpectedly truncated\n"); - exit (1); - } - - /* Now for the actual export. */ - if ((mode & GPGME_EXPORT_MODE_EXTERN)) - printf ("sending keys to keyserver\n"); - if ((mode & GPGME_EXPORT_MODE_SECRET)) - printf ("exporting secret keys!\n"); err = gpgme_data_new (&out); fail_if_err (err); - gpgme_set_armor (ctx, 1); - err = gpgme_op_export_keys (ctx, keyarray, mode, - (mode & GPGME_KEYLIST_MODE_EXTERN)? NULL:out); - fail_if_err (err); + if ((mode & GPGME_EXPORT_MODE_SSH)) + { + mode = GPGME_EXPORT_MODE_SSH; /* Set only this bit for this test. */ + keyarray[0] = NULL; + + err = gpgme_op_export_ext (ctx, (const char**)argv, mode, out); + fail_if_err (err); + } + else + { + /* Lookup the keys as required by the export_keys function. */ + err = gpgme_op_keylist_ext_start (ctx, (const char**)argv, 0, 0); + fail_if_err (err); + + while (!(err = gpgme_op_keylist_next (ctx, &key))) + { + printf ("keyid: %s (fpr: %s)\n", + key->subkeys?nonnull (key->subkeys->keyid):"?", + key->subkeys?nonnull (key->subkeys->fpr):"?"); + + if (keyidx < DIM (keyarray)-1) + keyarray[keyidx++] = key; + else + { + fprintf (stderr, PGM": too many keys" + "- skipping this key\n"); + gpgme_key_unref (key); + } + } + if (gpgme_err_code (err) != GPG_ERR_EOF) + fail_if_err (err); + err = gpgme_op_keylist_end (ctx); + fail_if_err (err); + keyarray[keyidx] = NULL; + + result = gpgme_op_keylist_result (ctx); + if (result->truncated) + { + fprintf (stderr, PGM ": key listing unexpectedly truncated\n"); + exit (1); + } + + /* Now for the actual export. */ + if ((mode & GPGME_EXPORT_MODE_EXTERN)) + printf ("sending keys to keyserver\n"); + if ((mode & GPGME_EXPORT_MODE_SECRET)) + printf ("exporting secret keys!\n"); + + gpgme_set_armor (ctx, 1); + err = gpgme_op_export_keys (ctx, keyarray, mode, + (mode & GPGME_KEYLIST_MODE_EXTERN)? NULL:out); + fail_if_err (err); + } fflush (NULL); if (!(mode & GPGME_KEYLIST_MODE_EXTERN))