validate the source of uevent messages

Bug: 2844206
Change-Id: If2eee54181abfc6c7fda0232f98fa6bb5d12c60c
diff --git a/init/devices.c b/init/devices.c
index bde906b..530d6d7 100644
--- a/init/devices.c
+++ b/init/devices.c
@@ -53,6 +53,7 @@
 {
     struct sockaddr_nl addr;
     int sz = 64*1024; // XXX larger? udev uses 16MB!
+    int on = 1;
     int s;
 
     memset(&addr, 0, sizeof(addr));
@@ -65,6 +66,7 @@
         return -1;
 
     setsockopt(s, SOL_SOCKET, SO_RCVBUFFORCE, &sz, sizeof(sz));
+    setsockopt(s, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on));
 
     if(bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
         close(s);
@@ -571,18 +573,42 @@
 #define UEVENT_MSG_LEN  1024
 void handle_device_fd(int fd)
 {
-    char msg[UEVENT_MSG_LEN+2];
-    int n;
+    for(;;) {
+        char msg[UEVENT_MSG_LEN+2];
+        char cred_msg[CMSG_SPACE(sizeof(struct ucred))];
+        struct iovec iov = {msg, sizeof(msg)};
+        struct sockaddr_nl snl;
+        struct msghdr hdr = {&snl, sizeof(snl), &iov, 1, cred_msg, sizeof(cred_msg), 0};
 
-    while((n = recv(fd, msg, UEVENT_MSG_LEN, 0)) > 0) {
-        struct uevent uevent;
+        ssize_t n = recvmsg(fd, &hdr, 0);
+        if (n <= 0) {
+            break;
+        }
 
-        if(n == UEVENT_MSG_LEN)   /* overflow -- discard */
+        if ((snl.nl_groups != 1) || (snl.nl_pid != 0)) {
+            /* ignoring non-kernel netlink multicast message */
+            continue;
+        }
+
+        struct cmsghdr * cmsg = CMSG_FIRSTHDR(&hdr);
+        if (cmsg == NULL || cmsg->cmsg_type != SCM_CREDENTIALS) {
+            /* no sender credentials received, ignore message */
+            continue;
+        }
+
+        struct ucred * cred = (struct ucred *)CMSG_DATA(cmsg);
+        if (cred->uid != 0) {
+            /* message from non-root user, ignore */
+            continue;
+        }
+
+        if(n >= UEVENT_MSG_LEN)   /* overflow -- discard */
             continue;
 
         msg[n] = '\0';
         msg[n+1] = '\0';
 
+        struct uevent uevent;
         parse_event(msg, &uevent);
 
         handle_device_event(&uevent);