Add SELinux checks when adding a service.

Add a MAC check to the svc_can_register function in
service_manager. The types are defined in
external/sepolicy/service.te and the mapping from service
names is defined in external/sepolicy/service_contexts.
Currently uses the property context backend to parse the
contexts file.

Bug: 12909011
Change-Id: I5d90a614263c60571c7c70c2882e6fa929911ca5
diff --git a/cmds/servicemanager/service_manager.c b/cmds/servicemanager/service_manager.c
index 79ce6ed..939920a 100644
--- a/cmds/servicemanager/service_manager.c
+++ b/cmds/servicemanager/service_manager.c
@@ -8,6 +8,8 @@
 
 #include <private/android_filesystem_config.h>
 
+#include <selinux/android.h>
+
 #include "binder.h"
 
 #if 0
@@ -76,16 +78,67 @@
     return 1;
 }
 
-int svc_can_register(uid_t uid, const uint16_t *name)
+static struct selabel_handle* sehandle;
+
+static bool check_mac_perms(const char *name, pid_t spid)
+{
+    if (is_selinux_enabled() <= 0) {
+        return true;
+    }
+
+    bool allowed = false;
+
+    const char *class = "service_manager";
+    const char *perm = "add";
+
+    char *tctx = NULL;
+    char *sctx = NULL;
+
+    if (!sehandle) {
+        ALOGE("SELinux: Failed to find sehandle %s.\n", name);
+        return false;
+    }
+
+    if (getpidcon(spid, &sctx) < 0) {
+        ALOGE("SELinux: getpidcon failed to retrieve pid context.\n");
+        return false;
+    }
+
+    if (!sctx) {
+        ALOGE("SELinux: Failed to find sctx for %s.\n", name);
+        return false;
+    }
+
+    if (selabel_lookup(sehandle, &tctx, name, 1) != 0) {
+        ALOGE("SELinux: selabel_lookup failed to set tctx for %s.\n", name);
+        freecon(sctx);
+        return false;
+    }
+
+    if (!tctx) {
+        ALOGE("SELinux: Failed to find tctx for %s.\n", name);
+        freecon(sctx);
+        return false;
+    }
+
+    int result = selinux_check_access(sctx, tctx, class, perm, (void *) name);
+    allowed = (result == 0);
+
+    freecon(sctx);
+    freecon(tctx);
+    return allowed;
+}
+
+static int svc_can_register(uid_t uid, const uint16_t *name, pid_t spid)
 {
     size_t n;
 
     if ((uid == 0) || (uid == AID_SYSTEM))
-        return 1;
+        return check_mac_perms(str8(name), spid) ? 1 : 0;
 
     for (n = 0; n < sizeof(allowed) / sizeof(allowed[0]); n++)
         if ((uid == allowed[n].uid) && str16eq(name, allowed[n].name))
-            return 1;
+            return check_mac_perms(str8(name), spid) ? 1 : 0;
 
     return 0;
 }
@@ -155,7 +208,8 @@
 
 int do_add_service(struct binder_state *bs,
                    const uint16_t *s, size_t len,
-                   uint32_t handle, uid_t uid, int allow_isolated)
+                   uint32_t handle, uid_t uid, int allow_isolated,
+                   pid_t spid)
 {
     struct svcinfo *si;
 
@@ -165,7 +219,7 @@
     if (!handle || (len == 0) || (len > 127))
         return -1;
 
-    if (!svc_can_register(uid, s)) {
+    if (!svc_can_register(uid, s, spid)) {
         ALOGE("add_service('%s',%x) uid=%d - PERMISSION DENIED\n",
              str8(s), handle, uid);
         return -1;
@@ -235,6 +289,14 @@
         return -1;
     }
 
+    if (sehandle && selinux_status_updated() > 0) {
+        struct selabel_handle *tmp_sehandle = selinux_android_service_context_handle();
+        if (tmp_sehandle) {
+            selabel_close(sehandle);
+            sehandle = tmp_sehandle;
+        }
+    }
+
     switch(txn->code) {
     case SVC_MGR_GET_SERVICE:
     case SVC_MGR_CHECK_SERVICE:
@@ -249,7 +311,8 @@
         s = bio_get_string16(msg, &len);
         handle = bio_get_ref(msg);
         allow_isolated = bio_get_uint32(msg) ? 1 : 0;
-        if (do_add_service(bs, s, len, handle, txn->sender_euid, allow_isolated))
+        if (do_add_service(bs, s, len, handle, txn->sender_euid,
+            allow_isolated, txn->sender_pid))
             return -1;
         break;
 
@@ -274,6 +337,13 @@
     return 0;
 }
 
+
+static int audit_callback(void *data, security_class_t cls, char *buf, size_t len)
+{
+    snprintf(buf, len, "service=%s", !data ? "NULL" : (char *)data);
+    return 0;
+}
+
 int main(int argc, char **argv)
 {
     struct binder_state *bs;
@@ -289,6 +359,14 @@
         return -1;
     }
 
+    sehandle = selinux_android_service_context_handle();
+
+    union selinux_callback cb;
+    cb.func_audit = audit_callback;
+    selinux_set_callback(SELINUX_CB_AUDIT, cb);
+    cb.func_log = selinux_log_callback;
+    selinux_set_callback(SELINUX_CB_LOG, cb);
+
     svcmgr_handle = BINDER_SERVICE_MANAGER;
     binder_loop(bs, svcmgr_handler);