From 7f02c7f2269b3672e007556d93a5c9667925004c Mon Sep 17 00:00:00 2001
From: Mats Erik Andersson <gnu@gisladisker.se>
Date: Fri, 12 Feb 2016 14:45:33 +0100
Subject: [PATCH 17/60] ping: Implement subprivileged echo method.

The change implements a setuid-less use case of echo pinging
for use on GNU/Linux systems.  The new mode is the fallback
when a raw socket mode fails.
---
 ChangeLog      | 14 ++++++++++++++
 ping/libping.c | 30 ++++++++++++++++++++++++++----
 2 files changed, 40 insertions(+), 4 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 27a031d3..e79308bc 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,17 @@
+2016-02-12  Mats Erik Andersson  <gnu@gisladisker.se>
+
+	ping: Implement subprivileged echo method.
+	Add fallback code usable by GNU/Linux to create a datagram
+	socket for sending ICMP ECHOREQUEST.  The change is related
+	to issue #37252.
+
+	* ping/libping.c (useless_ident): New variable.
+	(ping_init): When a raw socket fails, fall back to a datagram
+	socket.  In case of success, increase `useless_ident' since
+	the kernel choses a packet identity, overwriting our choice.
+	(my_echo_reply, ping_recv): Short circuit the tests for packet
+	identity with the value of `useless_identity'.
+
 2016-02-09  Mats Erik Andersson  <gnu@gisladisker.se>
 
 	* configure.ac (missing ipv6 or icmp6): Disable `ping6' using
diff --git a/ping/libping.c b/ping/libping.c
index 4a9f73de..9ae5d671 100644
--- a/ping/libping.c
+++ b/ping/libping.c
@@ -39,6 +39,8 @@
 
 #include "ping.h"
 
+static int useless_ident = 0;	/* Relevant at least for Linux.  */
+
 static size_t _ping_packetsize (PING * p);
 
 size_t
@@ -72,8 +74,28 @@ ping_init (int type, int ident)
   if (fd < 0)
     {
       if (errno == EPERM || errno == EACCES)
-	fprintf (stderr, "ping: Lacking privilege for raw socket.\n");
-      return NULL;
+	{
+	  errno = 0;
+
+	  /* At least Linux can allow subprivileged users to send ICMP
+	   * packets formally encapsulated and built as a datagram socket,
+	   * but then the identity number is set by the kernel itself.
+	   */
+	  fd = socket (AF_INET, SOCK_DGRAM, proto->p_proto);
+	  if (fd < 0)
+	    {
+	      if (errno == EPERM || errno == EACCES || errno == EPROTONOSUPPORT)
+		fprintf (stderr, "ping: Lacking privilege for icmp socket.\n");
+	      else
+		fprintf (stderr, "ping: %s\n", strerror (errno));
+
+	      return NULL;
+	    }
+
+	  useless_ident++;	/* SOCK_DGRAM overrides our set identity.  */
+	}
+      else
+	return NULL;
     }
 
   /* Allocate PING structure and initialize it to default values */
@@ -172,7 +194,7 @@ my_echo_reply (PING * p, icmphdr_t * icmp)
   return (orig_ip->ip_dst.s_addr == p->ping_dest.ping_sockaddr.sin_addr.s_addr
 	  && orig_ip->ip_p == IPPROTO_ICMP
 	  && orig_icmp->icmp_type == ICMP_ECHO
-	  && ntohs (orig_icmp->icmp_id) == p->ping_ident);
+	  && (ntohs (orig_icmp->icmp_id) == p->ping_ident || useless_ident));
 }
 
 int
@@ -206,7 +228,7 @@ ping_recv (PING * p)
     case ICMP_ADDRESSREPLY:
       /*    case ICMP_ROUTERADV: */
 
-      if (ntohs (icmp->icmp_id) != p->ping_ident)
+      if (ntohs (icmp->icmp_id) != p->ping_ident && useless_ident == 0)
 	return -1;
 
       if (rc)
-- 
2.26.0.292.g33ef6b2f38

