Add patches for gpgsm offline mode
authorAndre Heinecke <aheinecke@intevation.de>
Tue, 7 Jul 2015 14:52:53 +0000 (16:52 +0200)
committerAndre Heinecke <aheinecke@intevation.de>
Tue, 7 Jul 2015 14:52:53 +0000 (16:52 +0200)
* Makefile.am (EXTRA_DIST): Add patches.
* patches/gnupg2-2.0.28/0006-gpgsm-Add-command-option-offline.patch,
patches/gpgme-1.5.5/
0001-Add-offline-mode-support-for-CMS-keylisting.patch: New.

--

The patch to gnupg is a backport from GnuPG 2.1.

Makefile.am
patches/gnupg2-2.0.28/0006-gpgsm-Add-command-option-offline.patch [new file with mode: 0755]
patches/gpgme-1.5.5/0001-Add-offline-mode-support-for-CMS-keylisting.patch [new file with mode: 0755]

index 3cc8bff..5d28770 100644 (file)
@@ -37,9 +37,11 @@ EXTRA_DIST = autogen.sh README.GIT ONEWS \
         patches/claws-mail-3.9.1/80-src-makefile.postcfg-build \
         patches/dirmngr-1.1.1/dirmngr-pth.patch \
         patches/glib-2.41.5/01-socket.patch \
+        patches/gpgme-1.5.5/0001-Add-offline-mode-support-for-CMS-keylisting.patch \
         patches/gnupg2-2.0.28/0001-Enable-wildcard-expansion-with-mingw-w64.patch \
         patches/gnupg2-2.0.28/0002-Let-wchar_to_native-convert-to-console-codepage.patch \
         patches/gnupg2-2.0.28/0005-Fix-gpgtar-8-bit-encoding-handling-on-Win32.patch \
+        patches/gnupg2-2.0.28/0006-gpgsm-Add-command-option-offline.patch \
         patches/gnupg2/01-version.patch \
         patches/gnupg2/01-version.patch.in \
         patches/gnutls-2.12.23/01-openssl-wincrypt.patch \
diff --git a/patches/gnupg2-2.0.28/0006-gpgsm-Add-command-option-offline.patch b/patches/gnupg2-2.0.28/0006-gpgsm-Add-command-option-offline.patch
new file mode 100755 (executable)
index 0000000..4427417
--- /dev/null
@@ -0,0 +1,340 @@
+#! /bin/sh
+patch -p1 -l -f $* < $0
+exit $?
+
+From fa16fba7c5e6c139bbc0556d6316d32c4d9f1e22 Mon Sep 17 00:00:00 2001
+From: Werner Koch <wk@gnupg.org>
+Date: Mon, 29 Jun 2015 11:03:58 +0200
+Subject: [PATCH] gpgsm: Add command option "offline".
+
+* sm/server.c (option_handler): Add "offline".
+(cmd_getinfo): Ditto.
+* sm/certchain.c (is_cert_still_valid):
+(do_validate_chain):
+* sm/gpgsm.c (gpgsm_init_default_ctrl): Default "offline" to the value
+of --disable-dirmngr.
+* sm/call-dirmngr.c (start_dirmngr_ext): Better also check for
+ctrl->offline.
+--
+
+Adding this option makes it easier to implement the corresponding
+feature in gpgme.
+
+Signed-off-by: Werner Koch <wk@gnupg.org>
+(cherry picked from commit 2c9c46e2a2b8f9a1bdc1ef46a135b5fc7d1a8073)
+
+Conflicts:
+       sm/gpgsm.h
+---
+ doc/gpgsm.texi    | 130 +++++++++++++++++++++++++++++++++++++++++++++++++++++-
+ sm/call-dirmngr.c |   2 +-
+ sm/certchain.c    |   6 +--
+ sm/gpgsm.c        |   1 +
+ sm/gpgsm.h        |   1 +
+ sm/server.c       |  19 +++++++-
+ 6 files changed, 151 insertions(+), 8 deletions(-)
+
+diff --git a/doc/gpgsm.texi b/doc/gpgsm.texi
+index b0882b8..32fbe38 100644
+--- a/doc/gpgsm.texi
++++ b/doc/gpgsm.texi
+@@ -462,6 +462,7 @@ will not have on your local keybox), the operator can tell both your IP
+ address and the time when you verified the signature.
+
+
++@anchor{gpgsm-option --validation-model}
+ @item --validation-model @var{name}
+ @opindex validation-model
+ This option changes the default validation model.  The only possible
+@@ -554,6 +555,7 @@ may be given (@pxref{how-to-specify-a-user-id}).
+ Write output to @var{file}.  The default is to write it to stdout.
+
+
++@anchor{gpgsm-option --with-key-data}
+ @item --with-key-data
+ @opindex with-key-data
+ Displays extra information with the @code{--list-keys} commands.  Especially
+@@ -561,6 +563,7 @@ a line tagged @code{grp} is printed which tells you the keygrip of a
+ key.  This string is for example used as the file name of the
+ secret key.
+
++@anchor{gpgsm-option --with-validation}
+ @item --with-validation
+ @opindex with-validation
+ When doing a key listing, do a full validation check for each key and
+@@ -1160,7 +1163,9 @@ Assuan manual for details.
+ * GPGSM EXPORT::          Export certificates.
+ * GPGSM IMPORT::          Import certificates.
+ * GPGSM DELETE::          Delete certificates.
++* GPGSM GETAUDITLOG::     Retrieve an audit log.
+ * GPGSM GETINFO::         Information about the process
++* GPGSM OPTION::          Session options.
+ @end menu
+
+
+@@ -1350,6 +1355,7 @@ may be issued as a progress indicator.
+
+ @node GPGSM LISTKEYS
+ @subsection List available keys
++@anchor{gpgsm-cmd listkeys}
+
+ To list the keys in the internal database or using an external key
+ provider, the command:
+@@ -1449,6 +1455,23 @@ this requires that the usual escape quoting rules are done.
+ The certificates must be specified unambiguously otherwise an error is
+ returned.
+
++@node GPGSM GETAUDITLOG
++@subsection Retrieve an audit log.
++@anchor{gpgsm-cmd getauditlog}
++
++This command is used to retrieve an audit log.
++
++@example
++GETAUDITLOG [--data] [--html]
++@end example
++
++If @option{--data} is used, the audit log is send using D-lines
++instead of being sent to the file descriptor given by an OUTPUT
++command.  If @option{--html} is used, the output is formated as an
++XHTML block. This is designed to be incorporated into a HTML
++document.
++
++
+ @node GPGSM GETINFO
+ @subsection  Return information about the process
+
+@@ -1465,10 +1488,113 @@ Return the version of the program.
+ @item pid
+ Return the process id of the process.
+ @item agent-check
+-Return success if the agent is running.
++Return OK if the agent is running.
+ @item cmd_has_option @var{cmd} @var{opt}
+-Return success if the command @var{cmd} implements the option @var{opt}.
++Return OK if the command @var{cmd} implements the option @var{opt}.
+ The leading two dashes usually used with @var{opt} shall not be given.
++@item offline
++Return OK if the connection is in offline mode.  This may be either
++due to a @code{OPTION offline=1} or due to @command{gpgsm} being
++started with option @option{--disable-dirmngr}.
++@end table
++
++@node GPGSM OPTION
++@subsection  Session options.
++
++The standard Assuan option handler supports these options.
++
++@example
++OPTION @var{name}[=@var{value}]
++@end example
++
++These @var{name}s are recognized:
++
++@table @code
++
++@item putenv
++Change the session's environment to be passed via gpg-agent to
++Pinentry.  @var{value} is a string of the form
++@code{<KEY>[=[<STRING>]]}.  If only @code{<KEY>} is given the
++environment variable @code{<KEY>} is removed from the session
++environment, if @code{<KEY>=} is given that environment variable is
++set to the empty string, and if @code{<STRING>} is given it is set to
++that string.
++
++@item display
++Set the session environment variable @code{DISPLAY} is set to @var{value}.
++@item ttyname
++Set the session environment variable @code{GPG_TTY} is set to @var{value}.
++@item ttytype
++Set the session environment variable @code{TERM} is set to @var{value}.
++@item lc-ctype
++Set the session environment variable @code{LC_CTYPE} is set to @var{value}.
++@item lc-messages
++Set the session environment variable @code{LC_MESSAGES} is set to @var{value}.
++@item xauthority
++Set the session environment variable @code{XAUTHORITY} is set to @var{value}.
++@item pinentry-user-data
++Set the session environment variable @code{PINENTRY_USER_DATA} is set
++to @var{value}.
++
++@item include-certs
++This option overrides the command line option
++@option{--include-certs}.  A @var{value} of -2 includes all
++certificates except for the root certificate, -1 includes all
++certicates, 0 does not include any certicates, 1 includes only the
++signers certicate and all other positive values include up to
++@var{value} certificates starting with the signer cert.
++
++@item list-mode
++@xref{gpgsm-cmd listkeys}.
++
++@item list-to-output
++If @var{value} is true the output of the list commands
++(@pxref{gpgsm-cmd listkeys}) is written to the file descriptor set
++with the last OUTPUT command.  If @var{value} is false the output is
++written via data lines; this is the default.
++
++@item with-validation
++If @var{value} is true for each listed certificate the validation
++status is printed.  This may result in the download of a CRL or the
++user being asked about the trustworthiness of a root certificate.  The
++default is given by a command line option (@pxref{gpgsm-option
++--with-validation}).
++
++
++@item with-secret
++If @var{value} is true certificates with a corresponding private key
++are marked by the list commands.
++
++@item validation-model
++This option overrides the command line option
++@option{validation-model} for the session.
++(@pxref{gpgsm-option --validation-model}.)
++
++@item with-key-data
++This option globally enables the command line option
++@option{--with-key-data}.  (@pxref{gpgsm-option --with-key-data}.)
++
++@item enable-audit-log
++If @var{value} is true data to write an audit log is gathered.
++(@pxref{gpgsm-cmd getauditlog}.)
++
++@item allow-pinentry-notify
++If this option is used notifications about the launch of a Pinentry
++are passed back to the client.
++
++@item with-ephemeral-keys
++If @var{value} is true ephemeral certificates are included in the
++output of the list commands.
++
++@item no-encrypt-to
++If this option is used all keys set by the command line option
++@option{--encrypt-to} are ignored.
++
++@item offline
++If @var{value} is true or @var{value} is not given all network access
++is disabled for this session.  This is the same as the command line
++option @option{--disable-dirmngr}.
++
+ @end table
+
+ @mansect see also
+diff --git a/sm/call-dirmngr.c b/sm/call-dirmngr.c
+index 6540a8f..2517e7d 100644
+--- a/sm/call-dirmngr.c
++++ b/sm/call-dirmngr.c
+@@ -201,7 +201,7 @@ start_dirmngr_ext (ctrl_t ctrl, assuan_context_t *ctx_r)
+   assuan_context_t ctx = NULL;
+   int try_default = 0;
+
+-  if (opt.disable_dirmngr)
++  if (opt.disable_dirmngr || ctrl->offline)
+     return gpg_error (GPG_ERR_NO_DIRMNGR);
+
+   if (*ctx_r)
+diff --git a/sm/certchain.c b/sm/certchain.c
+index 1fbe9ca..9d7a9d8 100644
+--- a/sm/certchain.c
++++ b/sm/certchain.c
+@@ -959,7 +959,7 @@ is_cert_still_valid (ctrl_t ctrl, int force_ocsp, int lm, estream_t fp,
+ {
+   gpg_error_t err;
+
+-  if (opt.no_crl_check && !ctrl->use_ocsp)
++  if (ctrl->offline || (opt.no_crl_check && !ctrl->use_ocsp))
+     {
+       audit_log_ok (ctrl->audit, AUDIT_CRL_CHECK,
+                     gpg_error (GPG_ERR_NOT_ENABLED));
+@@ -1727,9 +1727,9 @@ do_validate_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t checktime_arg,
+       if (opt.no_policy_check)
+         log_info ("policies not checked due to %s option\n",
+                   "--disable-policy-checks");
+-      if (opt.no_crl_check && !ctrl->use_ocsp)
++      if (ctrl->offline || (opt.no_crl_check && !ctrl->use_ocsp))
+         log_info ("CRLs not checked due to %s option\n",
+-                  "--disable-crl-checks");
++                  ctrl->offline ? "offline" : "--disable-crl-checks");
+     }
+
+   if (!rc)
+diff --git a/sm/gpgsm.c b/sm/gpgsm.c
+index ef01a5c..9069bc7 100644
+--- a/sm/gpgsm.c
++++ b/sm/gpgsm.c
+@@ -1991,6 +1991,7 @@ gpgsm_init_default_ctrl (struct server_control_s *ctrl)
+   ctrl->include_certs = default_include_certs;
+   ctrl->use_ocsp = opt.enable_ocsp;
+   ctrl->validation_model = default_validation_model;
++  ctrl->offline = opt.disable_dirmngr;
+ }
+
+
+diff --git a/sm/gpgsm.h b/sm/gpgsm.h
+index 25a2e5b..98ab81c 100644
+--- a/sm/gpgsm.h
++++ b/sm/gpgsm.h
+@@ -196,6 +196,7 @@ struct server_control_s
+                          signer) */
+   int use_ocsp;       /* Set to true if OCSP should be used. */
+   int validation_model; /* Set to 1 for the chain model.  */
++  int offline;        /* If true gpgsm won't do any network access.  */
+ };
+
+
+diff --git a/sm/server.c b/sm/server.c
+index 6ba5e58..7adb09a 100644
+--- a/sm/server.c
++++ b/sm/server.c
+@@ -301,6 +301,16 @@ option_handler (assuan_context_t ctx, const char *key, const char *value)
+     {
+       ctrl->server_local->no_encrypt_to = 1;
+     }
++  else if (!strcmp (key, "offline"))
++    {
++      /* We ignore this option if gpgsm has been started with
++         --disable-dirmngr (which also sets offline).  */
++      if (!opt.disable_dirmngr)
++        {
++          int i = *value? !!atoi (value) : 1;
++          ctrl->offline = i;
++        }
++    }
+   else
+     err = gpg_error (GPG_ERR_UNKNOWN_OPTION);
+
+@@ -1079,10 +1089,12 @@ static const char hlp_getinfo[] =
+   "  pid         - Return the process id of the server.\n"
+   "  agent-check - Return success if the agent is running.\n"
+   "  cmd_has_option CMD OPT\n"
+-  "              - Returns OK if the command CMD implements the option OPT.";
++  "              - Returns OK if the command CMD implements the option OPT.\n"
++  "  offline     - Returns OK if the conenction is in offline mode.";
+ static gpg_error_t
+ cmd_getinfo (assuan_context_t ctx, char *line)
+ {
++  ctrl_t ctrl = assuan_get_pointer (ctx);
+   int rc = 0;
+
+   if (!strcmp (line, "version"))
+@@ -1099,7 +1111,6 @@ cmd_getinfo (assuan_context_t ctx, char *line)
+     }
+   else if (!strcmp (line, "agent-check"))
+     {
+-      ctrl_t ctrl = assuan_get_pointer (ctx);
+       rc = gpgsm_agent_send_nop (ctrl);
+     }
+   else if (!strncmp (line, "cmd_has_option", 14)
+@@ -1134,6 +1145,10 @@ cmd_getinfo (assuan_context_t ctx, char *line)
+             }
+         }
+     }
++  else if (!strcmp (line, "offline"))
++    {
++      rc = ctrl->offline? 0 : gpg_error (GPG_ERR_GENERAL);
++    }
+   else
+     rc = set_error (GPG_ERR_ASS_PARAMETER, "unknown value for WHAT");
+
+--
+1.9.1
diff --git a/patches/gpgme-1.5.5/0001-Add-offline-mode-support-for-CMS-keylisting.patch b/patches/gpgme-1.5.5/0001-Add-offline-mode-support-for-CMS-keylisting.patch
new file mode 100755 (executable)
index 0000000..bc78a9b
--- /dev/null
@@ -0,0 +1,450 @@
+#! /bin/sh
+patch -p1 -l -f $* < $0
+exit $?
+
+From c6744a9ecbbc9628ad0f9fe5893bb54154b31373 Mon Sep 17 00:00:00 2001
+From: Andre Heinecke <aheinecke@intevation.de>
+Date: Thu, 2 Jul 2015 10:19:04 +0200
+Subject: [PATCH] Add offline mode support for CMS keylisting
+
+* doc/gpgme.texi: Document offline mode.
+* src/context.h (gpgme_context): Add offline.
+* src/engine-backend.h (keylist, keylist_ext): Add engine_flags.
+* src/engine.c, src/engine.h (_gpgme_engine_op_keylist): Ditto.
+  (_gpgme_engine_op_keylist_ext): Ditto.
+* src/engine.h (GPGME_ENGINE_FLAG_OFFLINE): New.
+* src/engine-gpg.c (gpg_keylist, gpg_keylist_ext): Ditto.
+* src/engine-gpgsm.c (gpgsm_keylist): Handle engine_flags.
+  (gpgsm_keylist_ext): Ditto.
+* src/gpgme.c (gpgme_set_offline, gpgme_get_offline): New.
+* src/gpgme.def (gpgme_set_offline, gpgme_get_offline): New.
+* src/gpgme.h.in (gpgme_set_offline, gpgme_get_offline): New.
+* src/libgpgme.vers (gpgme_set_offline, gpgme_get_offline): New.
+* src/keylist.c (gpgme_op_keylist_start): Set offline flag.
+  (gpgme_op_keylist_ext_start): Ditto.
+* tests/run-keylist.c (show_usage, main): Add offline argument.
+
+--
+The offline engine option was introduced with gpgsm 2.1.6
+it is mainly useful for a full keylisting that includes
+the certificate validation but does not depend on external
+information that could take an indefinite amount of time to
+collect.
+
+Signed-off-by: Andre Heinecke <aheinecke@intevation.de>
+---
+ doc/gpgme.texi       | 33 +++++++++++++++++++++++++++++++++
+ src/context.h        |  3 +++
+ src/engine-backend.h |  6 ++++--
+ src/engine-gpg.c     |  4 ++--
+ src/engine-gpgsm.c   | 15 ++++++++++++---
+ src/engine.c         | 10 ++++++----
+ src/engine.h         |  9 +++++++--
+ src/gpgme.c          | 24 ++++++++++++++++++++++++
+ src/gpgme.def        |  3 +++
+ src/gpgme.h.in       |  6 ++++++
+ src/keylist.c        | 13 +++++++++++--
+ src/libgpgme.vers    |  3 +++
+ tests/run-keylist.c  |  9 +++++++++
+ 13 files changed, 123 insertions(+), 15 deletions(-)
+
+diff --git a/doc/gpgme.texi b/doc/gpgme.texi
+index 45c359d..ef4936d 100644
+--- a/doc/gpgme.texi
++++ b/doc/gpgme.texi
+@@ -189,6 +189,7 @@ Context Attributes
+ * Crypto Engine::                 Configuring the crypto engine.
+ * ASCII Armor::                   Requesting @acronym{ASCII} armored output.
+ * Text Mode::                     Choosing canonical text mode.
++* Offline Mode::                  Choosing offline mode.
+ * Included Certificates::         Including a number of certificates.
+ * Key Listing Mode::              Selecting key listing mode.
+ * Passphrase Callback::           Getting the passphrase from the user.
+@@ -2285,6 +2286,7 @@ started.  In fact, these references are accessed through the
+ * Crypto Engine::                 Configuring the crypto engine.
+ * ASCII Armor::                   Requesting @acronym{ASCII} armored output.
+ * Text Mode::                     Choosing canonical text mode.
++* Offline Mode::                  Choosing offline mode.
+ * Included Certificates::         Including a number of certificates.
+ * Key Listing Mode::              Selecting key listing mode.
+ * Passphrase Callback::           Getting the passphrase from the user.
+@@ -2413,6 +2415,37 @@ valid pointer.
+ @end deftypefun
+
+
++@node Offline Mode
++@subsection Offline Mode
++@cindex context, offline mode
++@cindex offline mode
++
++@deftypefun void gpgme_set_offline (@w{gpgme_ctx_t @var{ctx}}, @w{int @var{yes}})
++The function @code{gpgme_set_offline} specifies if offline mode
++should be used.  By default, offline mode is not used.
++
++The offline mode specifies if dirmngr should be used to do additional
++validation that might require connections to external services.
++(e.g. CRL / OCSP checks).
++
++Offline mode only affects the keylist mode @code{GPGME_KEYLIST_MODE_VALIDATE}
++and is only relevant to the CMS crypto engine. Offline mode
++is ignored otherwise.
++
++This option may be extended in the future to completely disable
++the use of dirmngr for any engine.
++
++Offline mode is disabled if @var{yes} is zero, and enabled
++otherwise.
++@end deftypefun
++
++@deftypefun int gpgme_get_offline (@w{gpgme_ctx_t @var{ctx}})
++The function @code{gpgme_get_offline} returns 1 if offline
++mode is enabled, and @code{0} if it is not, or if @var{ctx} is not a
++valid pointer.
++@end deftypefun
++
++
+ @node Included Certificates
+ @subsection Included Certificates
+ @cindex certificates, included
+diff --git a/src/context.h b/src/context.h
+index 745ffa8..8cd86e9 100644
+--- a/src/context.h
++++ b/src/context.h
+@@ -98,6 +98,9 @@ struct gpgme_context
+   /* True if text mode should be used.  */
+   unsigned int use_textmode : 1;
+
++  /* True if offline mode should be used.  */
++  unsigned int offline : 1;
++
+   /* Flags for keylist mode.  */
+   gpgme_keylist_mode_t keylist_mode;
+
+diff --git a/src/engine-backend.h b/src/engine-backend.h
+index b3cc412..4f4519c 100644
+--- a/src/engine-backend.h
++++ b/src/engine-backend.h
+@@ -85,10 +85,12 @@ struct engine_ops
+   gpgme_error_t (*import) (void *engine, gpgme_data_t keydata,
+                            gpgme_key_t *keyarray);
+   gpgme_error_t (*keylist) (void *engine, const char *pattern,
+-                          int secret_only, gpgme_keylist_mode_t mode);
++                          int secret_only, gpgme_keylist_mode_t mode,
++                          int engine_flags);
+   gpgme_error_t (*keylist_ext) (void *engine, const char *pattern[],
+                               int secret_only, int reserved,
+-                              gpgme_keylist_mode_t mode);
++                              gpgme_keylist_mode_t mode,
++                              int engine_flags);
+   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-gpg.c b/src/engine-gpg.c
+index e14fd8d..510dfd9 100644
+--- a/src/engine-gpg.c
++++ b/src/engine-gpg.c
+@@ -2279,7 +2279,7 @@ gpg_keylist_build_options (engine_gpg_t gpg, int secret_only,
+
+ static gpgme_error_t
+ gpg_keylist (void *engine, const char *pattern, int secret_only,
+-           gpgme_keylist_mode_t mode)
++           gpgme_keylist_mode_t mode, int engine_flags)
+ {
+   engine_gpg_t gpg = engine;
+   gpgme_error_t err;
+@@ -2298,7 +2298,7 @@ gpg_keylist (void *engine, const char *pattern, int secret_only,
+
+ static gpgme_error_t
+ gpg_keylist_ext (void *engine, const char *pattern[], int secret_only,
+-               int reserved, gpgme_keylist_mode_t mode)
++               int reserved, gpgme_keylist_mode_t mode, int engine_flags)
+ {
+   engine_gpg_t gpg = engine;
+   gpgme_error_t err;
+diff --git a/src/engine-gpgsm.c b/src/engine-gpgsm.c
+index ac6c5fc..3771157 100644
+--- a/src/engine-gpgsm.c
++++ b/src/engine-gpgsm.c
+@@ -1542,7 +1542,7 @@ gpgsm_import (void *engine, gpgme_data_t keydata, gpgme_key_t *keyarray)
+
+ static gpgme_error_t
+ gpgsm_keylist (void *engine, const char *pattern, int secret_only,
+-             gpgme_keylist_mode_t mode)
++             gpgme_keylist_mode_t mode, int engine_flags)
+ {
+   engine_gpgsm_t gpgsm = engine;
+   char *line;
+@@ -1599,6 +1599,11 @@ gpgsm_keylist (void *engine, const char *pattern, int secret_only,
+                                "OPTION with-secret=1":
+                                "OPTION with-secret=0" ,
+                                NULL, NULL);
++  gpgsm_assuan_simple_command (gpgsm->assuan_ctx,
++                               (engine_flags & GPGME_ENGINE_FLAG_OFFLINE)?
++                               "OPTION offline=1":
++                               "OPTION offline=0" ,
++                               NULL, NULL);
+
+
+   /* Length is "LISTSECRETKEYS " + p + '\0'.  */
+@@ -1629,7 +1634,7 @@ gpgsm_keylist (void *engine, const char *pattern, int secret_only,
+
+ static gpgme_error_t
+ gpgsm_keylist_ext (void *engine, const char *pattern[], int secret_only,
+-                 int reserved, gpgme_keylist_mode_t mode)
++                 int reserved, gpgme_keylist_mode_t mode, int engine_flags)
+ {
+   engine_gpgsm_t gpgsm = engine;
+   char *line;
+@@ -1669,7 +1674,11 @@ gpgsm_keylist_ext (void *engine, const char *pattern[], int secret_only,
+                                "OPTION with-secret=1":
+                                "OPTION with-secret=0" ,
+                                NULL, NULL);
+-
++  gpgsm_assuan_simple_command (gpgsm->assuan_ctx,
++                               (engine_flags & GPGME_ENGINE_FLAG_OFFLINE)?
++                               "OPTION offline=1":
++                               "OPTION offline=0" ,
++                               NULL, NULL);
+
+   if (pattern && *pattern)
+     {
+diff --git a/src/engine.c b/src/engine.c
+index ff015c0..8e84da9 100644
+--- a/src/engine.c
++++ b/src/engine.c
+@@ -726,7 +726,8 @@ _gpgme_engine_op_import (engine_t engine, gpgme_data_t keydata,
+
+ gpgme_error_t
+ _gpgme_engine_op_keylist (engine_t engine, const char *pattern,
+-                        int secret_only, gpgme_keylist_mode_t mode)
++                        int secret_only, gpgme_keylist_mode_t mode,
++                        int engine_flags)
+ {
+   if (!engine)
+     return gpg_error (GPG_ERR_INV_VALUE);
+@@ -734,14 +735,15 @@ _gpgme_engine_op_keylist (engine_t engine, const char *pattern,
+   if (!engine->ops->keylist)
+     return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
+
+-  return (*engine->ops->keylist) (engine->engine, pattern, secret_only, mode);
++  return (*engine->ops->keylist) (engine->engine, pattern, secret_only, mode,
++                                  engine_flags);
+ }
+
+
+ gpgme_error_t
+ _gpgme_engine_op_keylist_ext (engine_t engine, const char *pattern[],
+                             int secret_only, int reserved,
+-                            gpgme_keylist_mode_t mode)
++                            gpgme_keylist_mode_t mode, int engine_flags)
+ {
+   if (!engine)
+     return gpg_error (GPG_ERR_INV_VALUE);
+@@ -750,7 +752,7 @@ _gpgme_engine_op_keylist_ext (engine_t engine, const char *pattern[],
+     return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
+
+   return (*engine->ops->keylist_ext) (engine->engine, pattern, secret_only,
+-                                    reserved, mode);
++                                    reserved, mode, engine_flags);
+ }
+
+
+diff --git a/src/engine.h b/src/engine.h
+index bbf009d..56fcc42 100644
+--- a/src/engine.h
++++ b/src/engine.h
+@@ -113,12 +113,14 @@ gpgme_error_t _gpgme_engine_op_import (engine_t engine,
+ gpgme_error_t _gpgme_engine_op_keylist (engine_t engine,
+                                       const char *pattern,
+                                       int secret_only,
+-                                      gpgme_keylist_mode_t mode);
++                                      gpgme_keylist_mode_t mode,
++                                      int engine_flags);
+ gpgme_error_t _gpgme_engine_op_keylist_ext (engine_t engine,
+                                           const char *pattern[],
+                                           int secret_only,
+                                           int reserved,
+-                                          gpgme_keylist_mode_t mode);
++                                          gpgme_keylist_mode_t mode,
++                                          int engine_flags);
+ 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, int use_textmode,
+@@ -170,5 +172,8 @@ gpgme_error_t _gpgme_engine_op_spawn (engine_t engine,
+                                       gpgme_data_t dataerr,
+                                       unsigned int flags);
+
++/* The available engine option flags.  */
++#define GPGME_ENGINE_FLAG_OFFLINE        1
++
+
+ #endif /* ENGINE_H */
+diff --git a/src/gpgme.c b/src/gpgme.c
+index 628cdae..c24b620 100644
+--- a/src/gpgme.c
++++ b/src/gpgme.c
+@@ -472,6 +472,30 @@ gpgme_get_textmode (gpgme_ctx_t ctx)
+ }
+
+
++/* Enable offline mode for this context. In offline mode dirmngr
++  will be disabled. */
++void
++gpgme_set_offline (gpgme_ctx_t ctx, int offline)
++{
++  TRACE2 (DEBUG_CTX, "gpgme_set_offline", ctx, "offline=%i (%s)",
++          offline, offline ? "yes" : "no");
++
++  if (!ctx)
++    return;
++
++  ctx->offline = offline;
++}
++
++/* Return the state of the offline flag.  */
++int
++gpgme_get_offline (gpgme_ctx_t ctx)
++{
++  TRACE2 (DEBUG_CTX, "gpgme_get_offline", ctx, "ctx->offline=%i (%s)",
++          ctx->offline, ctx->offline ? "yes" : "no");
++  return ctx->offline;
++}
++
++
+ /* Set the number of certifications to include in an S/MIME message.
+    The default is GPGME_INCLUDE_CERTS_DEFAULT.  -1 means all certs,
+    and -2 means all certs except the root cert.  */
+diff --git a/src/gpgme.def b/src/gpgme.def
+index dc18948..cf167b4 100644
+--- a/src/gpgme.def
++++ b/src/gpgme.def
+@@ -217,5 +217,8 @@ EXPORTS
+
+     gpgme_op_spawn_start                  @163
+     gpgme_op_spawn                        @164
++
++    gpgme_set_offline                     @165
++    gpgme_get_offline                     @166
+ ; END
+
+diff --git a/src/gpgme.h.in b/src/gpgme.h.in
+index 15ed803..099cc8a 100644
+--- a/src/gpgme.h.in
++++ b/src/gpgme.h.in
+@@ -887,6 +887,12 @@ void gpgme_set_textmode (gpgme_ctx_t ctx, int yes);
+ /* Return non-zero if text mode is set in CTX.  */
+ int gpgme_get_textmode (gpgme_ctx_t ctx);
+
++/* If YES is non-zero, enable offline mode in CTX, disable it otherwise.  */
++void gpgme_set_offline (gpgme_ctx_t ctx, int yes);
++
++/* Return non-zero if offline mode is set in CTX.  */
++int gpgme_get_offline (gpgme_ctx_t ctx);
++
+ /* Use whatever the default of the backend crypto engine is.  */
+ #define GPGME_INCLUDE_CERTS_DEFAULT   -256
+
+diff --git a/src/keylist.c b/src/keylist.c
+index 36ee3ea..fcf574f 100644
+--- a/src/keylist.c
++++ b/src/keylist.c
+@@ -889,6 +889,7 @@ gpgme_op_keylist_start (gpgme_ctx_t ctx, const char *pattern, int secret_only)
+   gpgme_error_t err;
+   void *hook;
+   op_data_t opd;
++  int flags = 0;
+
+   TRACE_BEG2 (DEBUG_CTX, "gpgme_op_keylist_start", ctx,
+             "pattern=%s, secret_only=%i", pattern, secret_only);
+@@ -913,8 +914,11 @@ gpgme_op_keylist_start (gpgme_ctx_t ctx, const char *pattern, int secret_only)
+   if (err)
+     return TRACE_ERR (err);
+
++  if (ctx->offline)
++    flags |= GPGME_ENGINE_FLAG_OFFLINE;
++
+   err = _gpgme_engine_op_keylist (ctx->engine, pattern, secret_only,
+-                                ctx->keylist_mode);
++                                ctx->keylist_mode, flags);
+   return TRACE_ERR (err);
+ }
+
+@@ -929,6 +933,7 @@ gpgme_op_keylist_ext_start (gpgme_ctx_t ctx, const char *pattern[],
+   gpgme_error_t err;
+   void *hook;
+   op_data_t opd;
++  int flags = 0;
+
+   TRACE_BEG2 (DEBUG_CTX, "gpgme_op_keylist_ext_start", ctx,
+             "secret_only=%i, reserved=0x%x", secret_only, reserved);
+@@ -952,8 +957,12 @@ gpgme_op_keylist_ext_start (gpgme_ctx_t ctx, const char *pattern[],
+   if (err)
+     return TRACE_ERR (err);
+
++  if (ctx->offline)
++    flags |= GPGME_ENGINE_FLAG_OFFLINE;
++
+   err = _gpgme_engine_op_keylist_ext (ctx->engine, pattern, secret_only,
+-                                    reserved, ctx->keylist_mode);
++                                    reserved, ctx->keylist_mode,
++                                    flags);
+   return TRACE_ERR (err);
+ }
+
+diff --git a/src/libgpgme.vers b/src/libgpgme.vers
+index 39663c1..fc2920f 100644
+--- a/src/libgpgme.vers
++++ b/src/libgpgme.vers
+@@ -92,6 +92,9 @@ GPGME_1.1 {
+
+     gpgme_op_spawn_start;
+     gpgme_op_spawn;
++
++    gpgme_set_offline;
++    gpgme_get_offline;
+ };
+
+
+diff --git a/tests/run-keylist.c b/tests/run-keylist.c
+index 07c6fa1..8abdf43 100644
+--- a/tests/run-keylist.c
++++ b/tests/run-keylist.c
+@@ -53,6 +53,7 @@ show_usage (int ex)
+          "  --ephemeral      use GPGME_KEYLIST_MODE_EPHEMERAL\n"
+          "  --validate       use GPGME_KEYLIST_MODE_VALIDATE\n"
+          "  --import         import all keys\n"
++         "  --offline        use offline mode\n"
+          , stderr);
+   exit (ex);
+ }
+@@ -72,6 +73,7 @@ main (int argc, char **argv)
+   int keyidx = 0;
+   gpgme_protocol_t protocol = GPGME_PROTOCOL_OpenPGP;
+   int only_secret = 0;
++  int offline = 0;
+
+   if (argc)
+     { argc--; argv++; }
+@@ -141,6 +143,11 @@ main (int argc, char **argv)
+           import = 1;
+           argc--; argv++;
+         }
++      else if (!strcmp (*argv, "--offline"))
++        {
++          offline = 1;
++          argc--; argv++;
++        }
+       else if (!strncmp (*argv, "--", 2))
+         show_usage (1);
+
+@@ -157,6 +164,8 @@ main (int argc, char **argv)
+
+   gpgme_set_keylist_mode (ctx, mode);
+
++  gpgme_set_offline (ctx, offline);
++
+   err = gpgme_op_keylist_start (ctx, argc? argv[0]:NULL, only_secret);
+   fail_if_err (err);
+
+--
+1.9.1