The Cherry XX44 keyboard's PINpad does now work.
authorWerner Koch <wk@gnupg.org>
Wed, 7 Mar 2007 20:55:14 +0000 (20:55 +0000)
committerWerner Koch <wk@gnupg.org>
Wed, 7 Mar 2007 20:55:14 +0000 (20:55 +0000)
DINSIG and NKS card applications are now also PIN pad aware.

15 files changed:
NEWS
TODO
doc/ChangeLog
doc/Makefile.am
doc/tools.texi
jnlib/ChangeLog
jnlib/argparse.c
scd/ChangeLog
scd/apdu.c
scd/app-dinsig.c
scd/app-nks.c
scd/ccid-driver.c
tools/ChangeLog
tools/Makefile.am
tools/applygnupgdefaults [new file with mode: 0755]

diff --git a/NEWS b/NEWS
index 59d4633..33a18b1 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -11,7 +11,11 @@ Noteworthy changes in version 2.0.3
  * New --verify-option show-primary-uid-only. 
 
  * gpgconf may now read a global configuration file to select which
-   options are changeable by a frontend.
+   options are changeable by a frontend.  The new applygnupgdefaults
+   tool may be used by an admin to set default options for all users.
+
+ * The PIN pad of the Cherry XX44 keyboard is now supported.  The
+   DINSIG and the NKS applications are now also aware of PIN pads.
 
 
 Noteworthy changes in version 2.0.2 (2007-01-31)
@@ -471,7 +475,7 @@ Noteworthy changes in version 1.9.0 (2003-08-05)
    development branch.
 
 
- Copyright 2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc.
+ Copyright 2002, 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc.
 
  This file is free software; as a special exception the author gives
  unlimited permission to copy and/or distribute it, with or without
diff --git a/TODO b/TODO
index 6733d1c..2e0f545 100644 (file)
--- a/TODO
+++ b/TODO
 
 * UTF-8
   UTF-8 specific TODO.
+
+* Pinpad Reader
+  We do not yet support P15 applications.  The trivial thing using
+  ASCII characters will be easy to implement but the otehr cases need
+  some more work.
index 7999069..7bd0b10 100644 (file)
@@ -1,3 +1,7 @@
+2007-03-07  Werner Koch  <wk@g10code.com>
+
+       * tools.texi (applygnupgdefaults): New.
+
 2007-03-06  Werner Koch  <wk@g10code.com>
 
        * examples/gpgconf.conf: New.
index 9614b22..70a83d4 100644 (file)
@@ -55,7 +55,7 @@ myman_sources = gnupg7.texi gpg.texi gpgsm.texi gpg-agent.texi \
 myman_pages   = gpg2.1 gpgsm.1 gpg-agent.1 scdaemon.1 gpgv2.1 \
                 watchgnupg.1 gpgconf.1 addgnupghome.8 gpg-preset-passphrase.1 \
                gpg-connect-agent.1 gpgparsemail.1 symcryptrun.1 \
-               gpgsm-gencert.sh.1
+               gpgsm-gencert.sh.1 applygnupgdefaults.8
 
 man_MANS = $(myman_pages)
 noinst_MANS = gnupg.7
index 1e386f5..d10b806 100644 (file)
@@ -12,6 +12,7 @@ GnuPG comes with a couple of smaller tools:
 * gpgv::                  Verify OpenPGP signatures.
 * addgnupghome::          Create .gnupg home directories.
 * gpgconf::               Modify .gnupg home directories.
+* applygnupgdefaults::    Run gpgconf for all users.
 * gpgsm-gencert.sh::      Generate an X.509 certificate request.
 * gpg-preset-passphrase:: Put a passphrase into the cache.
 * gpg-connect-agent::     Communicate with a running agent.
@@ -82,10 +83,12 @@ Display a brief help page and exit
 @end table
 
 @mansect see also
+@ifset isman
 @command{gpg}(1), 
 @command{gpgsm}(1), 
 @command{gpg-agent}(1), 
 @command{scdaemon}(1)
+@end ifset
 @include see-also-note.texi
 
 
@@ -680,7 +683,7 @@ effect.
 
 @table @file
 
-@item /etc/gnupg/gpg-agent.conf
+@item /etc/gnupg/gpgconf.conf
 @cindex gpgconf.conf
   If this file exists, it is processed as a global configuration file.
   A commented example can be found in the @file{examples} directory of
@@ -689,15 +692,51 @@ effect.
 
 
 @mansect see also
+@ifset isman
 @command{gpg}(1), 
 @command{gpgsm}(1), 
 @command{gpg-agent}(1), 
 @command{scdaemon}(1),
 @command{dirmngr}(1)
+@end ifset
 @include see-also-note.texi
 
 
-@manpause
+
+@c
+@c    APPLYGNUPGDEFAULTS
+@c
+@manpage applygnupgdefaults.8
+@node applygnupgdefaults
+@section Run gpgconf for all users.
+@ifset manverb
+.B applygnupgdefaults
+\- Run gpgconf --apply-defaults for all users.
+@end ifset
+
+@mansect synopsis
+@ifset manverb
+.B  applygnupgdefaults
+@end ifset
+
+@mansect description
+This script is a wrapper around @command{gpgconf} to run it with the
+command @code{--apply-defaults} for all real users with an existing
+GnuPG home directory.  Admins might want to use this script to update he
+GnuPG configuration files for all users after
+@file{/etc/gnupg/gpgconf.conf} has been changed.  This allows to enforce
+certain policies for all users.  Note, that this is not a bulletproof of
+forcing a user to use certain options.  A user may always directly edit
+the configuration files and bypass gpgconf.
+
+@noindent
+@command{applygnupgdefaults} is invoked by root as:
+
+@example
+applygnupgdefaults
+@end example
+
+
 @c
 @c    GPGSM-GENCERT.SH
 @c
@@ -725,9 +764,11 @@ which will be printed to stdout.
 @samp{gpgsm-cencert.sh}
 
 @mansect see also
+@ifset isman
 @command{gpgsm}(1), 
 @command{gpg-agent}(1), 
 @command{scdaemon}(1)
+@end ifset
 @include see-also-note.texi
 
 
@@ -818,10 +859,12 @@ for other users.
 @end table
 
 @mansect see also
+@ifset isman
 @command{gpg}(1), 
 @command{gpgsm}(1), 
 @command{gpg-agent}(1), 
 @command{scdaemon}(1)
+@end ifset
 @include see-also-note.texi
 
 
@@ -1103,8 +1146,10 @@ The possible exit status codes of @command{symcryptrun} are:
 @end table
 
 @mansect see also
+@ifset isman
 @command{gpg}(1), 
 @command{gpgsm}(1), 
 @command{gpg-agent}(1), 
+@end ifset
 @include see-also-note.texi
 
index 3f9ef15..3168b8a 100644 (file)
@@ -1,3 +1,7 @@
+2007-03-07  Werner Koch  <wk@g10code.com>
+
+       * argparse.c (strusage): Set copyright year to 2007.
+
 2007-01-25  Werner Koch  <wk@g10code.com>
 
        * stringhelp.c (utf8_charcount): New.
index c4fb057..c9cb7d3 100644 (file)
@@ -905,7 +905,7 @@ strusage( int level )
     switch( level ) {
       case 11: p = "foo"; break;
       case 13: p = "0.0"; break;
-      case 14: p = "Copyright (C) 2006 Free Software Foundation, Inc."; break;
+      case 14: p = "Copyright (C) 2007 Free Software Foundation, Inc."; break;
       case 15: p =
 "This program comes with ABSOLUTELY NO WARRANTY.\n"
 "This is free software, and you are welcome to redistribute it\n"
index 38ef4f2..5714575 100644 (file)
@@ -1,3 +1,14 @@
+2007-03-07  Werner Koch  <wk@g10code.com>
+
+       * app-dinsig.c: Include i18n.h.
+       (verify_pin): Support PIN pads.
+       * app-nks.c (verify_pin): Ditto.
+
+       * ccid-driver.c (bulk_in): Handle time extension before checking
+       the message type.
+       (ccid_transceive_secure): Support the Cherry XX44 keyboard.
+       Kudos to the nice folks at Cherry for helping with that.
+
 2007-02-18  Werner Koch  <wk@g10code.com>
 
        * scdaemon.c (DEFAULT_PCSC_DRIVER): Add a default for OS X.
index ac2f3c4..a45f0a3 100644 (file)
@@ -90,7 +90,7 @@
 #define MAX_OPEN_FDS 20
 #endif
 
-/* Helper to pass patrameters related to keypad based operations. */
+/* Helper to pass parameters related to keypad based operations. */
 struct pininfo_s
 {
   int mode;
index 5cab428..9815688 100644 (file)
@@ -1,5 +1,5 @@
 /* app-dinsig.c - The DINSIG (DIN V 66291-1) card application.
- *     Copyright (C) 2002, 2004, 2005 Free Software Foundation, Inc.
+ * Copyright (C) 2002, 2004, 2005, 2007 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
@@ -81,6 +81,7 @@
 
 #include "scdaemon.h"
 
+#include "i18n.h"
 #include "iso7816.h"
 #include "app-common.h"
 #include "tlv.h"
@@ -279,11 +280,37 @@ verify_pin (app_t app,
             gpg_error_t (*pincb)(void*, const char *, char **),
             void *pincb_arg)
 {
-  if (!app->did_chv1 || app->force_chv1 ) 
+  const char *s;
+  int rc;
+  iso7816_pininfo_t pininfo;
+
+  if ( app->did_chv1 && !app->force_chv1 ) 
+    return 0;  /* No need to verify it again.  */
+
+  memset (&pininfo, 0, sizeof pininfo);
+  pininfo.mode = 1;
+  pininfo.minlen = 6;
+  pininfo.maxlen = 8;
+
+  if (!opt.disable_keypad
+      && !iso7816_check_keypad (app->slot, ISO7816_VERIFY, &pininfo) )
+    {
+      rc = pincb (pincb_arg,
+                  _("||Please enter your PIN at the reader's keypad"),
+                  NULL);
+      if (rc)
+        {
+          log_info (_("PIN callback returned error: %s\n"),
+                    gpg_strerror (rc));
+          return rc;
+        }
+      rc = iso7816_verify_kp (app->slot, 0x81, "", 0, &pininfo); 
+      /* Dismiss the prompt. */
+      pincb (pincb_arg, NULL, NULL);
+    }
+  else  /* No Keypad.  */
     {
-      const char *s;
       char *pinvalue;
-      int rc;
 
       rc = pincb (pincb_arg, "PIN", &pinvalue);
       if (rc)
@@ -303,15 +330,17 @@ verify_pin (app_t app,
           return gpg_error (GPG_ERR_BAD_PIN);
         }
 
-      if (strlen (pinvalue) < 6)
+      if (strlen (pinvalue) < pininfo.minlen)
         {
-          log_error ("PIN is too short; minimum length is 6\n");
+          log_error ("PIN is too short; minimum length is %d\n",
+                     pininfo.minlen);
           xfree (pinvalue);
           return gpg_error (GPG_ERR_BAD_PIN);
         }
-      else if (strlen (pinvalue) > 8)
+      else if (strlen (pinvalue) > pininfo.maxlen)
         {
-          log_error ("PIN is too large; maximum length is 8\n");
+          log_error ("PIN is too large; maximum length is %d\n",
+                     pininfo.maxlen);
           xfree (pinvalue);
           return gpg_error (GPG_ERR_BAD_PIN);
         }
@@ -326,7 +355,7 @@ verify_pin (app_t app,
              this. */
           char paddedpin[8];
           int i, ndigits;
-
+          
           for (ndigits=0, s=pinvalue; *s; ndigits++, s++)
             ;
           i = 0;
@@ -336,19 +365,18 @@ verify_pin (app_t app,
           if (i < sizeof paddedpin && *s)
             paddedpin[i++] = (((*s - '0') << 4) | 0x0f);
           while (i < sizeof paddedpin)
-              paddedpin[i++] = 0xff;
+            paddedpin[i++] = 0xff;
           rc = iso7816_verify (app->slot, 0x81, paddedpin, sizeof paddedpin);
         }
-      if (rc)
-        {
-          log_error ("verify PIN failed\n");
-          xfree (pinvalue);
-          return rc;
-        }
-      app->did_chv1 = 1;
       xfree (pinvalue);
     }
 
+  if (rc)
+    {
+      log_error ("verify PIN failed\n");
+      return rc;
+    }
+  app->did_chv1 = 1;
   return 0;
 }
 
index 3c64fe4..5feabc1 100644 (file)
@@ -1,5 +1,5 @@
 /* app-nks.c - The Telesec NKS 2.0 card application.
- *     Copyright (C) 2004 Free Software Foundation, Inc.
+ * Copyright (C) 2004, 2007 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
@@ -306,14 +306,43 @@ verify_pin (app_t app,
             gpg_error_t (*pincb)(void*, const char *, char **),
             void *pincb_arg)
 {
+  iso7816_pininfo_t pininfo;
+  int rc;
+
   /* Note that force_chv1 is never set but we do it here anyway so
      that other applications may reuse this function.  For example it
      makes sense to set force_chv1 for German signature law cards.
      NKS is very similar to the DINSIG draft standard. */
-  if (!app->did_chv1 || app->force_chv1 ) 
+  if ( app->did_chv1 && !app->force_chv1 ) 
+    return 0;  /* No need to verify it again.  */
+
+  memset (&pininfo, 0, sizeof pininfo);
+  pininfo.mode = 1;
+  pininfo.minlen = 6;
+  pininfo.maxlen = 16;
+
+  if (!opt.disable_keypad
+      && !iso7816_check_keypad (app->slot, ISO7816_VERIFY, &pininfo) )
+    {
+      rc = pincb (pincb_arg,
+                  _("||Please enter your PIN at the reader's keypad"),
+                  NULL);
+      if (rc)
+        {
+          log_info (_("PIN callback returned error: %s\n"),
+                    gpg_strerror (rc));
+          return rc;
+        }
+      /* Although it is possible to use a local PIN, we use the global
+         PIN for this application.  */
+      rc = iso7816_verify_kp (app->slot, 0, "", 0, &pininfo); 
+      /* Dismiss the prompt. */
+      pincb (pincb_arg, NULL, NULL);
+    }
+  else
     {
       char *pinvalue;
-      int rc;
 
       rc = pincb (pincb_arg, "PIN", &pinvalue); 
       if (rc)
@@ -324,35 +353,37 @@ verify_pin (app_t app,
 
       /* The following limits are due to TCOS but also defined in the
          NKS specs. */
-      if (strlen (pinvalue) < 6)
+      if (strlen (pinvalue) < pininfo.minlen)
         {
-          log_error ("PIN is too short; minimum length is 6\n");
+          log_error ("PIN is too short; minimum length is %d\n",
+                     pininfo.minlen);
           xfree (pinvalue);
           return gpg_error (GPG_ERR_BAD_PIN);
         }
-      else if (strlen (pinvalue) > 16)
+      else if (strlen (pinvalue) > pininfo.maxlen)
         {
-          log_error ("PIN is too large; maximum length is 16\n");
+          log_error ("PIN is too large; maximum length is %d\n",
+                     pininfo.maxlen);
           xfree (pinvalue);
           return gpg_error (GPG_ERR_BAD_PIN);
         }
 
-      /* Also it is possible to use a local PIN, we use the gloabl
+      /* Although it is possible to use a local PIN, we use the global
          PIN for this application.  */
       rc = iso7816_verify (app->slot, 0, pinvalue, strlen (pinvalue));
-      if (rc)
-        {
-          if ( gpg_error (rc) == GPG_ERR_USE_CONDITIONS )
-            log_error (_("the NullPIN has not yet been changed\n"));
-          else
-            log_error ("verify PIN failed\n");
-          xfree (pinvalue);
-          return rc;
-        }
-      app->did_chv1 = 1;
       xfree (pinvalue);
     }
 
+  if (rc)
+    {
+      if ( gpg_err_code (rc) == GPG_ERR_USE_CONDITIONS )
+        log_error (_("the NullPIN has not yet been changed\n"));
+      else
+        log_error ("verify PIN failed\n");
+      return rc;
+    }
+  app->did_chv1 = 1;
+
   return 0;
 }
 
@@ -457,7 +488,7 @@ do_decipher (app_t app, const char *keyidstr,
   if (!keyidstr || !*keyidstr || !indatalen)
     return gpg_error (GPG_ERR_INV_VALUE);
 
-  /* Check that the provided ID is vaid.  This is not really needed
+  /* Check that the provided ID is valid.  This is not really needed
      but we do it to to enforce correct usage by the caller. */
   if (strncmp (keyidstr, "NKS-DF01.", 9) ) 
     return gpg_error (GPG_ERR_INV_ID);
index bbb8862..8ad329d 100644 (file)
@@ -1,6 +1,6 @@
 /* ccid-driver.c - USB ChipCardInterfaceDevices driver
- *     Copyright (C) 2003, 2004, 2005, 2006 Free Software Foundation, Inc.
- *      Written by Werner Koch.
+ * Copyright (C) 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc.
+ * Written by Werner Koch.
  *
  * This file is part of GnuPG.
  *
@@ -1458,11 +1458,6 @@ bulk_in (ccid_driver_t handle, unsigned char *buffer, size_t length,
       DEBUGOUT_1 ("bulk-in msg too short (%u)\n", (unsigned int)msglen);
       return CCID_DRIVER_ERR_INV_VALUE;
     }
-  if (buffer[0] != expected_type)
-    {
-      DEBUGOUT_1 ("unexpected bulk-in msg type (%02x)\n", buffer[0]);
-      return CCID_DRIVER_ERR_INV_VALUE;
-    }
   if (buffer[5] != 0)    
     {
       DEBUGOUT_1 ("unexpected bulk-in slot (%d)\n", buffer[5]);
@@ -1475,6 +1470,10 @@ bulk_in (ccid_driver_t handle, unsigned char *buffer, size_t length,
       return CCID_DRIVER_ERR_INV_VALUE;
     }
 
+  /* We need to handle the time extension request before we check that
+     we go the expected message type.  This is in particular required
+     for the Cherry keyboard which sends a time extension request for
+     each key hit.  */
   if ( !(buffer[7] & 0x03) && (buffer[7] & 0xC0) == 0x80)
     { 
       /* Card present and active, time extension requested. */
@@ -1483,6 +1482,13 @@ bulk_in (ccid_driver_t handle, unsigned char *buffer, size_t length,
       goto retry;
     }
 
+  if (buffer[0] != expected_type)
+    {
+      DEBUGOUT_1 ("unexpected bulk-in msg type (%02x)\n", buffer[0]);
+      return CCID_DRIVER_ERR_INV_VALUE;
+    }
+
+
   if (!no_debug)
     {
       DEBUGOUT_3 ("status: %02X  error: %02X  octet[9]: %02X\n"
@@ -2330,6 +2336,7 @@ ccid_transceive_secure (ccid_driver_t handle,
   int i;
   size_t dummy_nresp;
   int testmode;
+  int cherry_mode = 0;
 
   testmode = !resp && !nresp;
 
@@ -2368,14 +2375,16 @@ ccid_transceive_secure (ccid_driver_t handle,
     case VENDOR_SCM:  /* Tested with SPR 532. */
     case VENDOR_KAAN: /* Tested with KAAN Advanced (1.02). */
       break;
-      /* The CHERRY XX44 does not yet work. I have not investigated it
-         closer because there is another problem: It echos a "*" for
-         each entered character and we somehow need to arrange that it
-         doesn't get to the tty at all.  Given that we are running
-         without a control terminal there is not much we can do about.
-         A weird hack using pinentry comes in mind but I doubt that
-         this is a clean solution.  Need to contact Cherry.
-       */
+    case VENDOR_CHERRY:
+      /* The CHERRY XX44 keyboard echos an asterisk for each entered
+         character on the keyboard channel.  We use a special variant
+         of PC_to_RDR_Secure which directs these characters to the
+         smart card's bulk-in channel.  We also need to append a zero
+         Lc byte to the APDU.  It seems that it will be replaced with
+         the actual length instead of being appended before the APDU
+         is send to the card. */
+      cherry_mode = 1;
+      break;
     default:
      return CCID_DRIVER_ERR_NOT_SUPPORTED;
     }
@@ -2393,7 +2402,7 @@ ccid_transceive_secure (ccid_driver_t handle,
         return rc;
     }
 
-  msg[0] = PC_to_RDR_Secure;
+  msg[0] = cherry_mode? 0x89 : PC_to_RDR_Secure;
   msg[5] = 0; /* slot */
   msg[6] = seqno = handle->seqno++;
   msg[7] = 0; /* bBWI */
@@ -2405,7 +2414,7 @@ ccid_transceive_secure (ccid_driver_t handle,
   if (handle->id_vendor == VENDOR_SCM)
     {
       /* For the SPR532 the next 2 bytes need to be zero.  We do this
-         for all SCM product. Kudos to Martin Paljak for this
+         for all SCM products.  Kudos to Martin Paljak for this
          hint.  */
       msg[13] = msg[14] = 0;
     }
@@ -2419,7 +2428,7 @@ ccid_transceive_secure (ccid_driver_t handle,
     }
 
   /* The following is a little endian word. */
-  msg[15] = pinlen_max;   /* wPINMaxExtraDigit-Maximum. */
+  msg[15] = pinlen_max;   /* wPINMaxExtraDigit-Maximum.  */
   msg[16] = pinlen_min;   /* wPINMaxExtraDigit-Minimum.  */
 
   msg[17] = 0x02; /* bEntryValidationCondition:
@@ -2440,6 +2449,8 @@ ccid_transceive_secure (ccid_driver_t handle,
   msg[27] = apdu_buf[2]; /* P1 */
   msg[28] = apdu_buf[3]; /* P2 */
   msglen = 29;
+  if (cherry_mode)
+    msg[msglen++] = 0;
   /* An EDC is not required. */
   set_msg_len (msg, msglen - 10);
 
index 56247c6..c369506 100644 (file)
@@ -1,3 +1,8 @@
+2007-03-07  Werner Koch  <wk@g10code.com>
+
+       * applygnupgdefaults: New.
+       * Makefile.am (sbin_SCRIPTS): Add it
+
 2007-03-06  Werner Koch  <wk@g10code.com>
 
        * gpgconf-comp.c: Include pwd.h and grp.h.
index 11abca7..d3c3556 100644 (file)
@@ -1,5 +1,5 @@
 # Makefile.am - Tools directory
-#      Copyright (C) 2003 Free Software Foundation, Inc.
+#      Copyright (C) 2003, 2007 Free Software Foundation, Inc.
 # 
 # This file is part of GnuPG.
 # 
@@ -20,7 +20,7 @@
 
 EXTRA_DIST = \
        Manifest watchgnupg.c \
-       addgnupghome gpgsm-gencert.sh \
+       addgnupghome applygnupgdefaults gpgsm-gencert.sh \
        lspgpot mail-signed-keys convert-from-106
 
 
@@ -29,7 +29,7 @@ include $(top_srcdir)/am/cmacros.am
 
 AM_CFLAGS = $(LIBGCRYPT_CFLAGS) $(GPG_ERROR_CFLAGS) $(LIBASSUAN_CFLAGS)
 
-sbin_SCRIPTS = addgnupghome
+sbin_SCRIPTS = addgnupghome applygnupgdefaults
 
 bin_SCRIPTS = gpgsm-gencert.sh
 if HAVE_USTAR
diff --git a/tools/applygnupgdefaults b/tools/applygnupgdefaults
new file mode 100755 (executable)
index 0000000..882189c
--- /dev/null
@@ -0,0 +1,83 @@
+#!/bin/sh                                               
+# Apply defaults from/etc/gnupg/gpg.conf to all users             -*- sh -*-
+#
+# Copyright 2007 Free Software Foundation, Inc.
+#
+# This file is free software; as a special exception the author gives
+# unlimited permission to copy and/or distribute it, with or without
+# modifications, as long as this notice is preserved.
+#
+# This file is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
+# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+PGM=applygnupgdefaults
+errorfile=
+
+error () {
+  echo "$PGM: $*" >&2
+  echo "$PGM: $*" >>$errorfile
+}
+
+info () {
+  echo "$PGM: $*" >&2
+}
+
+if [ -n "$1" ]; then 
+    echo "usage: $PGM" >&2
+    exit 1
+fi
+
+# Cleanup on exit
+cleanup ()
+{
+    [ -n "$errorfile" -a -f "$errorfile" ] && rm "$errorfile"
+}
+trap cleanup EXIT SIGINT SIGHUP SIGPIPE
+errorfile="/tmp/$PGM.$$.log"
+: >$errorfile
+
+
+# Check whether we can use getent
+if getent --help </dev/null >/dev/null 2>&1 ; then
+    cat_passwd='getent passwd'
+else
+    cat_passwd='cat /etc/passwd'
+    info "please note that only users from /etc/passwd are processed"
+fi
+
+if [ ! -f /etc/gnupg/gpgconf.conf ]; then
+    error "global configuration file \`/etc/gnupg/gpgconf.conf' does not exist"
+    exit 1
+fi
+if [ ! -f /etc/shells ]; then
+    error "missing file \`/etc/shells'"
+    exit 1
+fi
+
+if [ $(id -u) -ne 0 ]; then
+    error "needs to be run as root"
+    exit 1
+fi
+
+${cat_passwd} \
+  | while IFS=: read -r user dmy_a uid dmy_c dmy_d home shell dmy_rest; do
+    # Process only entires with a valid login shell
+    grep </etc/shells "^$shell" 2>/dev/null >/dev/null || continue
+    # and with an existant gnupg home directory
+    [ -d "$home/.gnupg" ] || continue
+    # but not root
+    [ "${uid:-0}" -eq 0 ] && continue
+    info "running \"gpgconf --apply-defaults\" for $user"
+    if su -l -s /bin/sh \
+       -c 'gpgconf --apply-defaults && echo SUCCESS' $user \
+       | tail -1 | grep ^SUCCESS >/dev/null ; then
+      :
+    else
+      error "failed to update gnupg defaults for $user"
+    fi
+done
+
+[ "$(wc -c <$errorfile)" -gt 0 ] && exit 1
+exit 0
+