Allow processes to interact with a queue using their euid

Change-Id: I3abe572a2167b9712a51ebfd0c67a0e404a77100
diff --git a/aos/ipc_lib/lockless_queue.cc b/aos/ipc_lib/lockless_queue.cc
index f31d80d..ccbe887 100644
--- a/aos/ipc_lib/lockless_queue.cc
+++ b/aos/ipc_lib/lockless_queue.cc
@@ -276,6 +276,50 @@
   // Everything should be zero initialized already.  So we just need to fill
   // everything out properly.
 
+  // This is the UID we will use for checking signal-sending permission
+  // compatibility.
+  //
+  // The manpage says:
+  //   For a process to have permission to send a signal, it must either be
+  //   privileged [...], or the real or effective user ID of the sending process
+  //   must equal the real or saved set-user-ID of the target process.
+  //
+  // Processes typically initialize a queue in random order as they start up.
+  // This means we need an algorithm for verifying all processes have
+  // permissions to send each other signals which gives the same answer no
+  // matter what order they attach in. We would also like to avoid maintaining a
+  // shared list of the UIDs of all processes.
+  //
+  // To do this while still giving sufficient flexibility for all current use
+  // cases, we track a single UID for the queue. All processes with a matching
+  // euid+suid must have this UID. Any processes with distinct euid/suid must
+  // instead have a matching ruid.  This guarantees signals can be sent between
+  // all processes attached to the queue.
+  //
+  // In particular, this allows a process to change only its euid (to interact
+  // with a queue) while still maintaining privileges via its ruid. However, it
+  // can only use privileges in ways that do not require changing the euid back,
+  // because while the euid is different it will not be able to receive signals.
+  // We can't actually verify that, but we can sanity check that things are
+  // valid when the queue is initialized.
+
+  uid_t uid;
+  {
+    uid_t ruid, euid, suid;
+    PCHECK(getresuid(&ruid, &euid, &suid) == 0);
+    // If these are equal, then use them, even if that's different from the real
+    // UID. This allows processes to keep a real UID of 0 (to have permissions
+    // to perform system-level changes) while still being able to communicate
+    // with processes running unprivileged as a distinct user.
+    if (euid == suid) {
+      uid = euid;
+      VLOG(1) << "Using euid==suid " << uid;
+    } else {
+      uid = ruid;
+      VLOG(1) << "Using ruid " << ruid;
+    }
+  }
+
   // Grab the mutex.  We don't care if the previous reader died.  We are going
   // to check everything anyways.
   GrabQueueSetupLockOrDie grab_queue_setup_lock(memory);
@@ -306,7 +350,7 @@
     }
 
     memory->next_queue_index.Invalidate();
-    memory->uid = getuid();
+    memory->uid = uid;
 
     for (size_t i = 0; i < memory->num_senders(); ++i) {
       ::aos::ipc_lib::Sender *s = memory->GetSender(i);
@@ -321,7 +365,7 @@
     // redo initialization.
     memory->initialized = true;
   } else {
-    CHECK_EQ(getuid(), memory->uid) << ": UIDs must match for all processes";
+    CHECK_EQ(uid, memory->uid) << ": UIDs must match for all processes";
   }
 
   return memory;