Add "use-agent" PAM option.
authorNIIBE Yutaka <gniibe@fsij.org>
Fri, 11 Nov 2016 08:50:12 +0000 (17:50 +0900)
committerNIIBE Yutaka <gniibe@fsij.org>
Fri, 11 Nov 2016 08:50:12 +0000 (17:50 +0900)
* configure.ac (GNUPG_DEFAULT_GPGCONF): New.
* src/pam/pam_poldi.c (pam_poldi_options_cb): Support the option.
(pam_sm_authenticate): Call scd_connect with use_agent option.
* src/scd/scd.c (get_agent_socket_name): New.
(agent_scd_getinfo_socket_name): Revert the change of removing this
function.  Clean it up.
(get_scd_socket_from_agent): New.
(scd_connect): Clean up and support use_agent.
--

Using Poldi for su/sudo with gpg-agent is questionable usage.
However, for backward compatibility, the feature is back.

Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
NEWS
configure.ac
src/pam/auth-support/ctx.h
src/pam/pam_poldi.c
src/scd/scd.c
src/scd/scd.h

diff --git a/NEWS b/NEWS
index a52f269..d3d8c46 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -5,15 +5,23 @@ Changes since version 0.4.1:
 * poldi-ctrl is removed
   Please use gpg-connect-agent instead.
 
-* Poldi always invokes scdaemon to connect it through pipe
+* New "use-agent" PAM option for backward compatibility
+  In GnuPG 2.1, the environment variable GPG_AGENT_INFO is gone.  And
+  now, Poldi's default is invoking scdaemon directly.  Still, there
+  are use cases (like su/sudo) which expect connecting user's
+  gpg-agent.  For this purpose, we now have "use-agent" option.  Don't
+  enable this option for login authentication.
+
+* Poldi invokes scdaemon to connect it through pipe
   Older Poldi has a feature of connecting to scdaemon with help of
   gpg-agent using the GPG_AGENT_INFO enviornment variable.  In GnuPG
-  2.1, the GPG_AGENT_INFO is gone and scdaemon no longer keeps locking
-  the reader after card removal, it is good to always invoke scdaemon
-  for the authentication.  If there is an existing scdaemon with card
-  inserted, a failure is expected and this is safer fallback.  That's
-  because Poldi should not connect to a smartcard which is in use for
-  other purpose and possibly already authenticated.
+  2.1, the environment variable GPG_AGENT_INFO is gone and scdaemon no
+  longer keeps locking the reader after card removal, it is good to
+  always invoke scdaemon for the authentication by default.  If there
+  is an existing scdaemon with card inserted, a failure is expected
+  and this is safer fallback.  That's because Poldi should not connect
+  to a smartcard which is in use for other purpose and possibly
+  already authenticated.
 
 * New option "scdaemon-options"
   Added a new option "scdaemon-options", which can be used to specify
index 1886e2c..0aba308 100644 (file)
@@ -68,6 +68,9 @@ AC_DEFINE_UNQUOTED(NEED_KSBA_VERSION, "$NEED_KSBA_VERSION",
 
 AH_BOTTOM([
 /* Setup the hardwired names of modules. */
+#ifndef GNUPG_DEFAULT_GPGCONF
+#define GNUPG_DEFAULT_GPGCONF    ( GNUPG_BINDIR "/gpgconf" )
+#endif
 #ifndef GNUPG_DEFAULT_SCD
 #define GNUPG_DEFAULT_SCD    ( GNUPG_LIBEXECDIR "/scdaemon" )
 #endif
index 98f1034..3de2407 100644 (file)
@@ -67,6 +67,7 @@ struct poldi_ctx_s
                                   PAM environment.  */
   int quiet;                   /* Be more quiet during PAM
                                   conversation with user. */
+  int use_agent;               /* Use gpg-agent to connect scdaemon.  */
 
   /* Scdaemon. */
   char *scdaemon_program;      /* Path of Scdaemon program to execute.  */
index 6ed3cc4..a27a3e9 100644 (file)
@@ -84,7 +84,8 @@ enum opt_ids
     opt_scdaemon_program,
     opt_scdaemon_options,
     opt_modify_environment,
-    opt_quiet
+    opt_quiet,
+    opt_use_agent,
   };
 
 /* Full specifications for options. */
@@ -104,6 +105,8 @@ static simpleparse_opt_spec_t opt_specs[] =
       0, SIMPLEPARSE_ARG_NONE, 0, "Set Poldi related variables in the PAM environment" },
     { opt_quiet, "quiet",
       0, SIMPLEPARSE_ARG_NONE, 0, "Be more quiet during PAM conversation with user" },
+    { opt_use_agent, "use-agent",
+      0, SIMPLEPARSE_ARG_NONE, 0, "Use gpg-agent for scdaemon" },
     { 0 }
   };
 
@@ -202,6 +205,10 @@ pam_poldi_options_cb (void *cookie, simpleparse_opt_spec_t spec, const char *arg
       /* QUIET.  */
       ctx->quiet = 1;
     }
+  else if (!strcmp (spec.long_opt, "use-agent"))
+    {
+      ctx->use_agent = 1;
+    }
 
   return gpg_error (err);
 }
@@ -549,7 +556,8 @@ pam_sm_authenticate (pam_handle_t *pam_handle,
 
   /*** Connect to Scdaemon. ***/
 
-  err = scd_connect (&scd_ctx, ctx->scdaemon_program, ctx->scdaemon_options,
+  err = scd_connect (&scd_ctx, ctx->use_agent,
+                    ctx->scdaemon_program, ctx->scdaemon_options,
                     ctx->loghandle);
   if (err)
     goto out;
index a565f78..06a26d8 100644 (file)
@@ -94,6 +94,111 @@ static gpg_error_t scd_serialno_internal (assuan_context_t ctx,
                                          char **r_serialno);
 
 \f
+
+/* Get the socket of GPG-AGENT by gpgconf. */
+static gpg_error_t
+get_agent_socket_name (char **gpg_agent_sockname)
+{
+  gpg_error_t err = 0;
+  FILE *input;
+  char *result;
+  size_t len;
+
+  *gpg_agent_sockname = NULL;
+
+  result = xtrymalloc (256);
+  if (!result)
+    return gpg_error_from_syserror ();
+
+  input = popen ("gpgconf --list-dirs agent-socket", "r");
+  if (input == NULL)
+    {
+      xfree (result);
+      return gpg_error (GPG_ERR_NOT_FOUND);
+    }
+
+  len = fread (result, 1, 256, input);
+  fclose (input);
+
+  if (len)
+    {
+      *gpg_agent_sockname = result;
+      result[len-1] = 0;       /* Chop off the newline.  */
+    }
+  else
+    {
+      xfree (result);
+      err =  gpg_error (GPG_ERR_NOT_FOUND);
+    }
+
+  return err;
+}
+
+/* Helper function for get_scd_socket_from_agent(), which is used by
+   scd_connect().
+
+   Try to retrieve the SCDaemons socket name from the gpg-agent
+   context CTX.  On success, *SOCKET_NAME is filled with a copy ot the
+   socket name.  Return proper error code or zero on success. */
+static gpg_error_t
+agent_scd_getinfo_socket_name (assuan_context_t ctx, char **socket_name)
+{
+  membuf_t data;
+  gpg_error_t err = 0;
+  unsigned char *databuf;
+  size_t datalen;
+
+  init_membuf (&data, 256);
+  *socket_name = NULL;
+
+  err = assuan_transact (ctx, "SCD GETINFO socket_name", membuf_data_cb, &data,
+                        NULL, NULL, NULL, NULL);
+  databuf = get_membuf (&data, &datalen);
+  if (!err)
+    {
+      if (databuf && datalen)
+       {
+         char *res = xtrymalloc (datalen + 1);
+         if (!res)
+           err = gpg_error_from_syserror ();
+         else
+           {
+             memcpy (res, databuf, datalen);
+             res[datalen] = 0;
+             *socket_name = res;
+           }
+       }
+    }
+
+  xfree (databuf);
+
+  return err;
+}
+
+/* Retrieve SCDaemons socket name through a running gpg-agent.  On
+   Success, *SOCKET_NAME contains a copy of the socket name.  Returns
+   proper error code or zero on success.  */
+static gpg_error_t
+get_scd_socket_from_agent (char **socket_name)
+{
+  assuan_context_t ctx = NULL;
+  gpg_error_t err;
+  char *gpg_agent_sockname;
+
+  err = get_agent_socket_name (&gpg_agent_sockname);
+  if (err)
+    return err;
+
+  err = assuan_socket_connect (&ctx, gpg_agent_sockname, 0);
+  xfree (gpg_agent_sockname);
+  if (!err)
+    err = agent_scd_getinfo_socket_name (ctx, socket_name);
+
+  assuan_disconnect (ctx);
+
+  return err;
+}
+
 /* Send a RESTART to SCDaemon.  */
 static void
 restart_scd (scd_context_t ctx)
@@ -107,7 +212,7 @@ restart_scd (scd_context_t ctx)
 /* Fork off scdaemon and work by pipes.  Returns proper error code or
    zero on success.  */
 gpg_error_t
-scd_connect (scd_context_t *scd_ctx, const char *scd_path,
+scd_connect (scd_context_t *scd_ctx, int use_agent, const char *scd_path,
             const char *scd_options, log_handle_t loghandle)
 {
   assuan_context_t assuan_ctx;
@@ -116,40 +221,58 @@ scd_connect (scd_context_t *scd_ctx, const char *scd_path,
 
   assuan_ctx = NULL;
 
+  if (fflush (NULL))
+    {
+      rc = gpg_error_from_syserror ();
+      log_msg_error (loghandle,
+                    _("error flushing pending output: %s"),
+                    strerror (errno));
+      return rc;
+    }
+
   ctx = xtrymalloc (sizeof (*ctx));
-  if (! ctx)
+  if (!ctx)
     {
       rc = gpg_error_from_syserror ();
-      goto out;
+      return rc;
     }
 
   ctx->assuan_ctx = NULL;
   ctx->flags = 0;
 
-  if (1)
+  if (use_agent)
     {
-      const char *pgmname;
-      const char *argv[5];
-      int no_close_list[3];
-      int i;
+      /* Retrieve a scdaemon socket name from gpg-agent.  */
+      char *scd_socket_name = NULL;
 
-#if 0
+      rc = get_scd_socket_from_agent (&scd_socket_name);
+      if (!rc)
+       rc = assuan_socket_connect (&assuan_ctx, scd_socket_name, 0);
+
+      if (!rc)
        log_msg_debug (loghandle,
-                      _("no running scdaemon - starting one"));
-#endif
+                      _("got scdaemon socket name from gpg-agent, "
+                        "connected to socket '%s'"), scd_socket_name);
 
-      if (fflush (NULL))
-        {
-          rc = gpg_error_from_syserror ();
+      xfree (scd_socket_name);
+
+      if (rc)
+       {
          log_msg_error (loghandle,
-                        _("error flushing pending output: %s"),
-                        strerror (errno));
-         goto out;
-        }
+                        _("could not connect to scdaemon: %s"),
+                        gpg_strerror (rc));
+       }
+    }
+  else
+    {
+      const char *pgmname;
+      const char *argv[5];
+      int no_close_list[3];
+      int i;
 
       if (!scd_path || !*scd_path)
         scd_path = GNUPG_DEFAULT_SCD;
-      if ( !(pgmname = strrchr (scd_path, '/')))
+      if (!(pgmname = strrchr (scd_path, '/')))
         pgmname = scd_path;
       else
         pgmname++;
@@ -168,9 +291,9 @@ scd_connect (scd_context_t *scd_ctx, const char *scd_path,
 
       i=0;
 
+#if 0
       /* FIXME! Am I right in assumung that we do not need this?
         -mo */
-#if 0
       if (log_get_fd () != -1)
         no_close_list[i++] = log_get_fd ();
 #endif
@@ -181,26 +304,24 @@ scd_connect (scd_context_t *scd_ctx, const char *scd_path,
 
       /* connect to the scdaemon and perform initial handshaking */
       rc = assuan_pipe_connect (&assuan_ctx, scd_path, argv, no_close_list);
-      if (!rc)
+      if (rc)
+       {
+         log_msg_error (loghandle,
+                        _("could not spawn scdaemon: %s"),
+                        gpg_strerror (rc));
+       }
+      else
        {
          log_msg_debug (loghandle,
                         _("spawned a new scdaemon (path: '%s')"),
                         scd_path);
-         goto out;
        }
     }
 
-  log_msg_error (loghandle,
-                _("could not connect to any scdaemon: %s"),
-                gpg_strerror (rc));
-
- out:
-
   if (rc)
     {
       assuan_disconnect (assuan_ctx);
       xfree (ctx);
-
     }
   else
     {
@@ -212,10 +333,6 @@ scd_connect (scd_context_t *scd_ctx, const char *scd_path,
       ctx->flags = 0;
       ctx->loghandle = loghandle;
       *scd_ctx = ctx;
-#if 0
-       log_msg_debug (loghandle,
-                      _("connection to scdaemon established"));
-#endif
     }
 
   return rc;
index 25680f8..8910497 100644 (file)
@@ -49,8 +49,9 @@ typedef struct scd_cardinfo scd_cardinfo_t;
 
 /* Fork it off and work by pipes.  Returns proper error code or zero
    on success.  */
-gpg_error_t scd_connect (scd_context_t *scd_ctx, const char *scd_path,
-                        const char *scd_options, log_handle_t loghandle);
+gpg_error_t scd_connect (scd_context_t *scd_ctx, int use_agent,
+                        const char *scd_path, const char *scd_options,
+                        log_handle_t loghandle);
 
 /* Disconnect from SCDaemon; destroy the context SCD_CTX.  */
 void scd_disconnect (scd_context_t scd_ctx);