From 64ee5ba5cad6298f72271539cb96e2de164dfc2f Mon Sep 17 00:00:00 2001
From: Mats Erik Andersson <gnu@gisladisker.se>
Date: Tue, 16 Feb 2016 23:29:25 +0100
Subject: [PATCH 18/60] traceroute: Subprivileged use case.

A fallback for ICMP tracing relevant to GNU/Linux is implemented,
allowing a rudimentary but suid-less use case.
---
 ChangeLog        | 18 ++++++++++++++++++
 src/traceroute.c | 30 +++++++++++++++++++++++++++++-
 2 files changed, 47 insertions(+), 1 deletion(-)

diff --git a/ChangeLog b/ChangeLog
index e79308bc..f01a330c 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,21 @@
+2016-02-16  Mats Erik Andersson  <gnu@gisladisker.se>
+
+	traceroute: Subprivileged use case.
+	A fallback for ICMP tracing relevant to GNU/Linux is implemented,
+	allowing a rudimentary but suid-less use case.  The ability to
+	identify intermediary hosts is missing, due to the crippled
+	capability of receiving ICMP packets other than ICMP_ECHOREPLY.
+
+	* src/traceroute.c (struct trace): New member `no_ident'.
+	(trace_init): Initiate `t->no_ident'.
+	<type TRACE_ICMP or TRACE_UDP>: In case a raw socket for an ICMP
+	protocol fails, fall back to a datagram socket.
+	(trace_read) <type TRACE_ICMP>: Short circuit `ic->icmp_id != pid'
+	with the conditional `t->no_ident == 0'.
+	(trace_write) <type TRACE_ICMP>: New variable I.  Initialize HDR
+	to its full extent.  If `t->no_ident' is non-zero, insert our
+	intended target as payload of HDR for trace identification.
+
 2016-02-12  Mats Erik Andersson  <gnu@gisladisker.se>
 
 	ping: Implement subprivileged echo method.
diff --git a/src/traceroute.c b/src/traceroute.c
index 785dc6f2..2a56a53d 100644
--- a/src/traceroute.c
+++ b/src/traceroute.c
@@ -75,6 +75,7 @@ typedef struct trace
 {
   int icmpfd, udpfd;
   enum trace_type type;
+  int no_ident;
   struct sockaddr_in to, from;
   int ttl;
   struct timeval tsent;
@@ -476,6 +477,7 @@ trace_init (trace_t * t, const struct sockaddr_in to,
   t->type = type;
   t->to = to;
   t->ttl = opt_ttl;
+  t->no_ident = 0;
 
   if (t->type == TRACE_UDP)
     {
@@ -494,6 +496,21 @@ trace_init (trace_t * t, const struct sockaddr_in to,
       if (protocol)
 	{
 	  t->icmpfd = socket (PF_INET, SOCK_RAW, protocol->p_proto);
+	  if (t->icmpfd < 0 && (errno == EPERM || errno == EACCES))
+	    {
+	      /* A subprivileged user on GNU/Linux might be allowed
+	       * to create ICMP packets from a datagram socket.
+	       * Such packets are always severely crippled.
+	       */
+	      errno = 0;
+	      t->icmpfd = socket (PF_INET, SOCK_DGRAM, protocol->p_proto);
+	      t->no_ident++;
+
+	      /* Recover error message for non-Linux systems.  */
+	      if (errno == EPROTONOSUPPORT)
+		errno = EPERM;
+	    }
+
 	  if (t->icmpfd < 0)
 	    error (EXIT_FAILURE, errno, "socket");
 
@@ -609,7 +626,7 @@ trace_read (trace_t * t, int * type, int * code)
 
       if (ic->icmp_type == ICMP_ECHOREPLY
 	  && (ntohs (ic->icmp_seq) != seqno
-	      || ntohs (ic->icmp_id) != pid))
+	      || (ntohs (ic->icmp_id) != pid && t->no_ident == 0)))
 	return -1;
 
       if (ic->icmp_type == ICMP_TIME_EXCEEDED
@@ -685,6 +702,17 @@ trace_write (trace_t * t)
     case TRACE_ICMP:
       {
 	icmphdr_t hdr;
+	int i;
+
+	/* Deposit deterministic values throughout the header!  */
+	for (i = 0; i < sizeof (hdr); ++i)
+	  *((char *) &hdr + i) = i;
+
+	/* The subprivileged use case of ICMP sent over datagram
+	 * sockets needs extra help with identification of target.
+	 */
+	if (t->no_ident)
+	  *((int *) &hdr + 12 / sizeof(int)) = dest.sin_addr.s_addr;
 
 	/* The sequence number is updated to a valid value!  */
 	if (icmp_echo_encode ((unsigned char *) &hdr, sizeof (hdr),
-- 
2.26.0.292.g33ef6b2f38

