agent: Add include files.
[gnupg.git] / agent / command-ssh.c
index 95cef41..382f9e6 100644 (file)
@@ -1,4 +1,4 @@
-/* command-ssh.c - gpg-agent's ssh-agent emulation layer
+/* command-ssh.c - gpg-agent's implementation of the ssh-agent protocol.
  * Copyright (C) 2004-2006, 2009, 2012 Free Software Foundation, Inc.
  * Copyright (C) 2004-2006, 2009, 2012-2014 Werner Koch
  *
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <assert.h>
+#ifndef HAVE_W32_SYSTEM
+#include <sys/socket.h>
+#include <sys/un.h>
+#endif /*!HAVE_W32_SYSTEM*/
+#ifdef HAVE_UCRED_H
+#include <ucred.h>
+#endif
 
 #include "agent.h"
 
@@ -2382,6 +2389,34 @@ ssh_key_grip (gcry_sexp_t key, unsigned char *buffer)
 }
 
 
+static gpg_error_t
+card_key_list (ctrl_t ctrl, char **r_serialno, strlist_t *result)
+{
+  gpg_error_t err;
+
+  *r_serialno = NULL;
+  *result = NULL;
+
+  err = agent_card_serialno (ctrl, r_serialno, NULL);
+  if (err)
+    {
+      if (gpg_err_code (err) != GPG_ERR_ENODEV && opt.verbose)
+        log_info (_("error getting serial number of card: %s\n"),
+                  gpg_strerror (err));
+
+      /* Nothing available.  */
+      return 0;
+    }
+
+  err = agent_card_cardlist (ctrl, result);
+  if (err)
+    {
+      xfree (*r_serialno);
+      *r_serialno = NULL;
+    }
+  return err;
+}
+
 /* Check whether a smartcard is available and whether it has a usable
    key.  Store a copy of that key at R_PK and return 0.  If no key is
    available store NULL at R_PK and return an error code.  If CARDSN
@@ -2408,7 +2443,7 @@ card_key_available (ctrl_t ctrl, gcry_sexp_t *r_pk, char **cardsn)
   if ( gpg_err_code (err) == GPG_ERR_CARD_REMOVED )
     {
       /* Ask for the serial number to reset the card.  */
-      err = agent_card_serialno (ctrl, &serialno);
+      err = agent_card_serialno (ctrl, &serialno, NULL);
       if (err)
         {
           if (opt.verbose)
@@ -2539,7 +2574,6 @@ ssh_handler_request_identities (ctrl_t ctrl,
   gpg_error_t err;
   int ret;
   ssh_control_file_t cf = NULL;
-  char *cardsn;
   gpg_error_t ret_err;
 
   (void)request;
@@ -2561,19 +2595,57 @@ ssh_handler_request_identities (ctrl_t ctrl,
      reader - this should be allowed even without being listed in
      sshcontrol. */
 
-  if (!opt.disable_scdaemon
-      && !card_key_available (ctrl, &key_public, &cardsn))
+  if (!opt.disable_scdaemon)
     {
-      err = ssh_send_key_public (key_blobs, key_public, cardsn);
-      gcry_sexp_release (key_public);
-      key_public = NULL;
-      xfree (cardsn);
+      char *serialno;
+      strlist_t card_list, sl;
+
+      err = card_key_list (ctrl, &serialno, &card_list);
       if (err)
-        goto out;
+        {
+          if (opt.verbose)
+            log_info (_("error getting list of cards: %s\n"),
+                      gpg_strerror (err));
+          goto scd_out;
+        }
 
-      key_counter++;
+      for (sl = card_list; sl; sl = sl->next)
+        {
+          char *serialno0;
+          char *cardsn;
+
+          err = agent_card_serialno (ctrl, &serialno0, sl->d);
+          if (err)
+            {
+              if (opt.verbose)
+                log_info (_("error getting serial number of card: %s\n"),
+                          gpg_strerror (err));
+              continue;
+            }
+
+          xfree (serialno0);
+          if (card_key_available (ctrl, &key_public, &cardsn))
+            continue;
+
+          err = ssh_send_key_public (key_blobs, key_public, cardsn);
+          gcry_sexp_release (key_public);
+          key_public = NULL;
+          xfree (cardsn);
+          if (err)
+            {
+              xfree (serialno);
+              free_strlist (card_list);
+              goto out;
+            }
+
+          key_counter++;
+        }
+
+      xfree (serialno);
+      free_strlist (card_list);
     }
 
+ scd_out:
   /* Then look at all the registered and non-disabled keys. */
   err = open_control_file (&cf, 0);
   if (err)
@@ -2655,7 +2727,7 @@ data_hash (unsigned char *data, size_t data_n,
 }
 
 
-/* This function signs the data described by CTRL. If HASH is is not
+/* This function signs the data described by CTRL. If HASH is not
    NULL, (HASH,HASHLEN) overrides the hash stored in CTRL.  This is to
    allow the use of signature algorithms that implement the hashing
    internally (e.g. Ed25519).  On success the created signature is
@@ -3216,7 +3288,7 @@ ssh_identities_remove_all (void)
   err = 0;
 
   /* FIXME: shall we remove _all_ cache entries or only those
-     registered through the ssh emulation?  */
+     registered through the ssh-agent protocol?  */
 
   return err;
 }
@@ -3491,6 +3563,62 @@ ssh_request_process (ctrl_t ctrl, estream_t stream_sock)
 }
 
 
+/* Return the peer's pid.  */
+static unsigned long
+get_client_pid (int fd)
+{
+  pid_t client_pid = (pid_t)(-1);
+
+#ifdef SO_PEERCRED
+  {
+#ifdef HAVE_STRUCT_SOCKPEERCRED_PID
+    struct sockpeercred cr;
+#else
+    struct ucred cr;
+#endif
+    socklen_t cl = sizeof cr;
+
+    if ( !getsockopt (fd, SOL_SOCKET, SO_PEERCRED, &cr, &cl))
+      {
+#if defined (HAVE_STRUCT_SOCKPEERCRED_PID) || defined (HAVE_STRUCT_UCRED_PID)
+        client_pid = cr.pid;
+#elif defined (HAVE_STRUCT_UCRED_CR_PID)
+        client_pid = cr.cr_pid;
+#else
+#error "Unknown SO_PEERCRED struct"
+#endif
+      }
+  }
+#elif defined (LOCAL_PEERPID)
+  {
+    socklen_t len = sizeof (pid_t);
+
+    getsockopt(fd, SOL_LOCAL, LOCAL_PEERPID, &client_pid, &len);
+  }
+#elif defined (LOCAL_PEEREID)
+  {
+    struct unpcbid unp;
+    socklen_t unpl = sizeof unp;
+
+    if (getsockopt (fd, 0, LOCAL_PEEREID, &unp, &unpl) != -1)
+      client_pid = unp.unp_pid;
+  }
+#elif defined (HAVE_GETPEERUCRED)
+  {
+    ucred_t *ucred = NULL;
+
+    if (getpeerucred (fd, &ucred) != -1)
+      {
+        client_pid= ucred_getpid (ucred);
+        ucred_free (ucred);
+      }
+  }
+#endif
+
+  return client_pid == (pid_t)(-1)? 0 : (unsigned long)client_pid;
+}
+
+
 /* Start serving client on SOCK_CLIENT.  */
 void
 start_command_handler_ssh (ctrl_t ctrl, gnupg_fd_t sock_client)
@@ -3503,6 +3631,8 @@ start_command_handler_ssh (ctrl_t ctrl, gnupg_fd_t sock_client)
   if (err)
     goto out;
 
+  ctrl->client_pid = get_client_pid (FD2INT(sock_client));
+
   /* Create stream from socket.  */
   stream_sock = es_fdopen (FD2INT(sock_client), "r+");
   if (!stream_sock)
@@ -3548,7 +3678,7 @@ start_command_handler_ssh (ctrl_t ctrl, gnupg_fd_t sock_client)
 
 #ifdef HAVE_W32_SYSTEM
 /* Serve one ssh-agent request.  This is used for the Putty support.
-   REQUEST is the the mmapped memory which may be accessed up to a
+   REQUEST is the mmapped memory which may be accessed up to a
    length of MAXREQLEN.  Returns 0 on success which also indicates
    that a valid SSH response message is now in REQUEST.  */
 int
@@ -3643,7 +3773,7 @@ serve_mmapped_ssh_request (ctrl_t ctrl,
     size_t response_size;
 
     /* NB: In contrast to the request-stream, the response stream
-       includes the the message type byte.  */
+       includes the message type byte.  */
     if (es_fclose_snatch (response_stream, &response_data, &response_size))
       {
         log_error ("snatching ssh response failed: %s",