msm: ipc: Security updates to IPC Router
In order to provide fine-grained access control to QMI services,
a security script from user-space will feed the security rules to
IPC Router. The security rule implies that in order to send a QMI
message to a service, a client process should belong to a specific
Linux/Android group. IPC Router, after receiving the security rules,
will enforce the access control rules.
Change-Id: I49f8d7c0067fc37cb0b4de2ccb46a575905ef64f
Signed-off-by: Karthikeyan Ramasubramanian <kramasub@codeaurora.org>
diff --git a/arch/arm/mach-msm/ipc_router.c b/arch/arm/mach-msm/ipc_router.c
index d6f844e..ca07ae0 100644
--- a/arch/arm/mach-msm/ipc_router.c
+++ b/arch/arm/mach-msm/ipc_router.c
@@ -36,6 +36,7 @@
#include "ipc_router.h"
#include "modem_notifier.h"
+#include "msm_ipc_router_security.h"
enum {
SMEM_LOG = 1U << 0,
@@ -111,6 +112,7 @@
struct msm_ipc_server {
struct list_head list;
struct msm_ipc_port_name name;
+ int synced_sec_rule;
struct list_head server_port_list;
};
@@ -129,6 +131,7 @@
wait_queue_head_t quota_wait;
uint32_t tx_quota_cnt;
struct mutex quota_lock;
+ void *sec_rule;
};
struct msm_ipc_router_xprt_info {
@@ -552,6 +555,7 @@
rport_ptr->port_id = port_id;
rport_ptr->node_id = node_id;
rport_ptr->restart_state = RESTART_NORMAL;
+ rport_ptr->sec_rule = NULL;
rport_ptr->tx_quota_cnt = 0;
init_waitqueue_head(&rport_ptr->quota_wait);
mutex_init(&rport_ptr->quota_lock);
@@ -667,6 +671,7 @@
}
server->name.service = service;
server->name.instance = instance;
+ server->synced_sec_rule = 0;
INIT_LIST_HEAD(&server->server_port_list);
list_add_tail(&server->list, &server_list[key]);
@@ -1199,6 +1204,95 @@
msm_ipc_cleanup_routing_table(xprt_info);
}
+/**
+ * sync_sec_rule() - Synchrnoize the security rule into the server structure
+ * @server: Server structure where the rule has to be synchronized.
+ * @rule: Security tule to be synchronized.
+ *
+ * This function is used to update the server structure with the security
+ * rule configured for the <service:instance> corresponding to that server.
+ */
+static void sync_sec_rule(struct msm_ipc_server *server, void *rule)
+{
+ struct msm_ipc_server_port *server_port;
+ struct msm_ipc_router_remote_port *rport_ptr = NULL;
+
+ list_for_each_entry(server_port, &server->server_port_list, list) {
+ rport_ptr = msm_ipc_router_lookup_remote_port(
+ server_port->server_addr.node_id,
+ server_port->server_addr.port_id);
+ if (!rport_ptr)
+ continue;
+ rport_ptr->sec_rule = rule;
+ }
+ server->synced_sec_rule = 1;
+}
+
+/**
+ * msm_ipc_sync_sec_rule() - Sync the security rule to the service
+ * @service: Service for which the rule has to be synchronized.
+ * @instance: Instance for which the rule has to be synchronized.
+ * @rule: Security rule to be synchronized.
+ *
+ * This function is used to syncrhonize the security rule with the server
+ * hash table, if the user-space script configures the rule after the service
+ * has come up. This function is used to synchronize the security rule to a
+ * specific service and optionally a specific instance.
+ */
+void msm_ipc_sync_sec_rule(uint32_t service, uint32_t instance, void *rule)
+{
+ int key = (service & (SRV_HASH_SIZE - 1));
+ struct msm_ipc_server *server;
+
+ mutex_lock(&server_list_lock);
+ list_for_each_entry(server, &server_list[key], list) {
+ if (server->name.service != service)
+ continue;
+
+ if (server->name.instance != instance &&
+ instance != ALL_INSTANCE)
+ continue;
+
+ /*
+ * If the rule applies to all instances and if the specific
+ * instance of a service has a rule synchronized already,
+ * do not apply the rule for that specific instance.
+ */
+ if (instance == ALL_INSTANCE && server->synced_sec_rule)
+ continue;
+
+ sync_sec_rule(server, rule);
+ }
+ mutex_unlock(&server_list_lock);
+}
+
+/**
+ * msm_ipc_sync_default_sec_rule() - Default security rule to all services
+ * @rule: Security rule to be synchronized.
+ *
+ * This function is used to syncrhonize the security rule with the server
+ * hash table, if the user-space script configures the rule after the service
+ * has come up. This function is used to synchronize the security rule that
+ * applies to all services, if the concerned service do not have any rule
+ * defined.
+ */
+void msm_ipc_sync_default_sec_rule(void *rule)
+{
+ int key;
+ struct msm_ipc_server *server;
+
+ mutex_lock(&server_list_lock);
+ for (key = 0; key < SRV_HASH_SIZE; key++) {
+ list_for_each_entry(server, &server_list[key], list) {
+ if (server->synced_sec_rule)
+ continue;
+
+ sync_sec_rule(server, rule);
+ }
+ }
+ mutex_unlock(&server_list_lock);
+}
+
static int process_hello_msg(struct msm_ipc_router_xprt_info *xprt_info,
struct rr_header *hdr)
{
@@ -1379,6 +1473,9 @@
if (!rport_ptr)
pr_err("%s: Remote port create "
"failed\n", __func__);
+ rport_ptr->sec_rule =
+ msm_ipc_get_security_rule(
+ msg->srv.service, msg->srv.instance);
}
wake_up(&newserver_wait);
}
@@ -1895,6 +1992,15 @@
return -ENOMEM;
}
+ if (src->check_send_permissions) {
+ ret = src->check_send_permissions(rport_ptr->sec_rule);
+ if (ret <= 0) {
+ pr_err("%s: permission failure for %s\n",
+ __func__, current->comm);
+ return -EPERM;
+ }
+ }
+
pkt = create_pkt(data);
if (!pkt) {
pr_err("%s: Pkt creation failed\n", __func__);
@@ -2640,6 +2746,10 @@
if (ret < 0)
pr_err("%s: Init sockets failed\n", __func__);
+ ret = msm_ipc_router_security_init();
+ if (ret < 0)
+ pr_err("%s: Security Init failed\n", __func__);
+
complete_all(&msm_ipc_local_router_up);
return ret;
}