diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig index d401ba5d5bf..10efe055e01 100644 --- a/arch/arm/mach-msm/Kconfig +++ b/arch/arm/mach-msm/Kconfig @@ -1493,6 +1493,16 @@ config MSM_IPC_ROUTER_SMD_XPRT help SMD Transport Layer for IPC Router +config MSM_IPC_ROUTER_SECURITY + depends on MSM_IPC_ROUTER + bool "MSM IPC Router Security support" + help + This feature of IPC Router will enforce security rules + configured by a security script from the user-space. IPC Router + once configured with the security rules will ensure that the + sender of the message to a service belongs to the relevant + Linux group as configured by the security script. + config MSM_ONCRPCROUTER_DEBUG depends on MSM_ONCRPCROUTER default y diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile index c3e60c4eded..63e99f5ddff 100644 --- a/arch/arm/mach-msm/Makefile +++ b/arch/arm/mach-msm/Makefile @@ -138,6 +138,7 @@ obj-$(CONFIG_MSM_ONCRPCROUTER) += smd_rpcrouter.o obj-$(CONFIG_MSM_ONCRPCROUTER) += smd_rpcrouter_device.o obj-$(CONFIG_MSM_IPC_ROUTER) += ipc_router.o obj-$(CONFIG_MSM_IPC_ROUTER)+= ipc_socket.o +obj-$(CONFIG_MSM_IPC_ROUTER_SECURITY)+= msm_ipc_router_security.o obj-$(CONFIG_DEBUG_FS) += smd_rpc_sym.o obj-$(CONFIG_MSM_ONCRPCROUTER) += smd_rpcrouter_servers.o obj-$(CONFIG_MSM_ONCRPCROUTER) += smd_rpcrouter_clients.o diff --git a/arch/arm/mach-msm/ipc_router.c b/arch/arm/mach-msm/ipc_router.c index d6f844e634b..ca07ae0dad9 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 @@ static wait_queue_head_t newserver_wait; 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 @@ struct msm_ipc_router_remote_port { 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 @@ static struct msm_ipc_router_remote_port *msm_ipc_router_create_remote_port( 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 @@ static struct msm_ipc_server *msm_ipc_router_create_server( } 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 @@ static void modem_reset_cleanup(struct msm_ipc_router_xprt_info *xprt_info) 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 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 @@ static int process_control_msg(struct msm_ipc_router_xprt_info *xprt_info, 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 @@ int msm_ipc_router_send_to(struct msm_ipc_port *src, 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 @@ static int __init msm_ipc_router_init(void) 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; } diff --git a/arch/arm/mach-msm/ipc_router.h b/arch/arm/mach-msm/ipc_router.h index 3dafa9fd02a..179e3de9976 100644 --- a/arch/arm/mach-msm/ipc_router.h +++ b/arch/arm/mach-msm/ipc_router.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved. +/* Copyright (c) 2011-2013, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -58,6 +58,9 @@ #define ALIGN_SIZE(x) ((4 - ((x) & 3)) & 3) +#define ALL_SERVICE 0xFFFFFFFF +#define ALL_INSTANCE 0xFFFFFFFF + enum { MSM_IPC_ROUTER_READ_CB = 0, MSM_IPC_ROUTER_WRITE_DONE, @@ -127,6 +130,7 @@ struct msm_ipc_port { void *endpoint; void (*notify)(unsigned event, void *data, void *addr, void *priv); + int (*check_send_permissions)(void *data); uint32_t num_tx; uint32_t num_rx; @@ -206,6 +210,10 @@ int msm_ipc_router_unregister_server(struct msm_ipc_port *server_port); int msm_ipc_router_init_sockets(void); void msm_ipc_router_exit_sockets(void); +void msm_ipc_sync_sec_rule(uint32_t service, uint32_t instance, void *rule); + +void msm_ipc_sync_default_sec_rule(void *rule); + #if defined CONFIG_MSM_IPC_ROUTER_SMD_XPRT extern void *msm_ipc_load_default_node(void); diff --git a/arch/arm/mach-msm/ipc_socket.c b/arch/arm/mach-msm/ipc_socket.c index 6f58dd127af..64b3db417e5 100644 --- a/arch/arm/mach-msm/ipc_socket.c +++ b/arch/arm/mach-msm/ipc_socket.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved. +/* Copyright (c) 2011-2013, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -21,16 +21,13 @@ #include #include -#ifdef CONFIG_ANDROID_PARANOID_NETWORK -#include -#endif - #include #include #include #include "ipc_router.h" +#include "msm_ipc_router_security.h" #define msm_ipc_sk(sk) ((struct msm_ipc_sock *)(sk)) #define msm_ipc_sk_port(sk) ((struct msm_ipc_port *)(msm_ipc_sk(sk)->port)) @@ -39,21 +36,6 @@ static int sockets_enabled; static struct proto msm_ipc_proto; static const struct proto_ops msm_ipc_proto_ops; -#ifdef CONFIG_ANDROID_PARANOID_NETWORK -static inline int check_permissions(void) -{ - int rc = 0; - if (!current_euid() || in_egroup_p(AID_NET_RAW)) - rc = 1; - return rc; -} -# else -static inline int check_permissions(void) -{ - return 1; -} -#endif - static struct sk_buff_head *msm_ipc_router_build_msg(unsigned int num_sect, struct iovec const *msg_sect, size_t total_len) @@ -191,7 +173,8 @@ static int msm_ipc_router_create(struct net *net, void *pil; if (!check_permissions()) { - pr_err("%s: Do not have permissions\n", __func__); + pr_err("%s: %s Do not have permissions\n", + __func__, current->comm); return -EPERM; } @@ -221,6 +204,7 @@ static int msm_ipc_router_create(struct net *net, return -ENOMEM; } + port_ptr->check_send_permissions = msm_ipc_check_send_permissions; sock->ops = &msm_ipc_proto_ops; sock_init_data(sock, sk); sk->sk_rcvtimeo = DEFAULT_RCV_TIMEO; @@ -443,6 +427,10 @@ static int msm_ipc_router_ioctl(struct socket *sock, ret = msm_ipc_router_bind_control_port(port_ptr); break; + case IPC_ROUTER_IOCTL_CONFIG_SEC_RULES: + ret = msm_ipc_config_sec_rules((void *)arg); + break; + default: ret = -EINVAL; } diff --git a/arch/arm/mach-msm/msm_ipc_router_security.c b/arch/arm/mach-msm/msm_ipc_router_security.c new file mode 100644 index 00000000000..7b2298ef295 --- /dev/null +++ b/arch/arm/mach-msm/msm_ipc_router_security.c @@ -0,0 +1,271 @@ +/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include "ipc_router.h" +#include "msm_ipc_router_security.h" + +#define SEC_RULES_HASH_SZ 32 +struct security_rule { + struct list_head list; + uint32_t service_id; + uint32_t instance_id; + unsigned reserved; + int num_group_info; + int *group_id; +}; + +static DEFINE_MUTEX(security_rules_lock); +static struct list_head security_rules[SEC_RULES_HASH_SZ]; + +/** + * check_permisions() - Check whether the process has permissions to + * create an interface handle with IPC Router + * + * @return: true if the process has permissions, else false. + */ +int check_permissions(void) +{ + int rc = 0; + if (!current_euid() || in_egroup_p(AID_NET_RAW)) + rc = 1; + return rc; +} +EXPORT_SYMBOL(check_permissions); + +/** + * msm_ipc_config_sec_rules() - Add a security rule to the database + * @arg: Pointer to the buffer containing the rule. + * + * @return: 0 if successfully added, < 0 for error. + * + * A security rule is defined using tuple. The rule + * implies that a user-space process in order to send a QMI message to + * service Service_ID should belong to the Linux group Group_ID. + */ +int msm_ipc_config_sec_rules(void *arg) +{ + struct config_sec_rules_args sec_rules_arg; + struct security_rule *rule, *temp_rule; + int key; + int ret; + + if (current_euid()) + return -EPERM; + + ret = copy_from_user(&sec_rules_arg, (void *)arg, + sizeof(sec_rules_arg)); + if (ret) + return -EFAULT; + + if (sec_rules_arg.num_group_info <= 0) + return -EINVAL; + + rule = kzalloc(sizeof(struct security_rule), GFP_KERNEL); + if (!rule) { + pr_err("%s: security_rule alloc failed\n", __func__); + return -ENOMEM; + } + + rule->group_id = kzalloc((sec_rules_arg.num_group_info * sizeof(int)), + GFP_KERNEL); + if (!rule->group_id) { + pr_err("%s: group_id alloc failed\n", __func__); + kfree(rule); + return -ENOMEM; + } + + rule->service_id = sec_rules_arg.service_id; + rule->instance_id = sec_rules_arg.instance_id; + rule->reserved = sec_rules_arg.reserved; + rule->num_group_info = sec_rules_arg.num_group_info; + ret = copy_from_user(rule->group_id, + ((void *)(arg + sizeof(sec_rules_arg))), + (rule->num_group_info * sizeof(uint32_t))); + if (ret) { + kfree(rule->group_id); + kfree(rule); + return -EFAULT; + } + + key = rule->service_id & (SEC_RULES_HASH_SZ - 1); + mutex_lock(&security_rules_lock); + if (rule->service_id == ALL_SERVICE) { + temp_rule = list_first_entry(&security_rules[key], + struct security_rule, list); + list_del(&temp_rule->list); + kfree(temp_rule->group_id); + kfree(temp_rule); + } + list_add_tail(&rule->list, &security_rules[key]); + mutex_unlock(&security_rules_lock); + + if (rule->service_id == ALL_SERVICE) + msm_ipc_sync_default_sec_rule((void *)rule); + else + msm_ipc_sync_sec_rule(rule->service_id, rule->instance_id, + (void *)rule); + + return 0; +} +EXPORT_SYMBOL(msm_ipc_config_sec_rules); + +/** + * msm_ipc_add_default_rule() - Add default security rule + * + * @return: 0 on success, < 0 on error/ + * + * This function is used to ensure the basic security, if there is no + * security rule defined for a service. It can be overwritten by the + * default security rule from user-space script. + */ +static int msm_ipc_add_default_rule(void) +{ + struct security_rule *rule; + int key; + + rule = kzalloc(sizeof(struct security_rule), GFP_KERNEL); + if (!rule) { + pr_err("%s: security_rule alloc failed\n", __func__); + return -ENOMEM; + } + + rule->group_id = kzalloc(sizeof(int), GFP_KERNEL); + if (!rule->group_id) { + pr_err("%s: group_id alloc failed\n", __func__); + kfree(rule); + return -ENOMEM; + } + + rule->service_id = ALL_SERVICE; + rule->instance_id = ALL_INSTANCE; + rule->num_group_info = 1; + *(rule->group_id) = AID_NET_RAW; + mutex_lock(&security_rules_lock); + key = (ALL_SERVICE & (SEC_RULES_HASH_SZ - 1)); + list_add_tail(&rule->list, &security_rules[key]); + mutex_unlock(&security_rules_lock); + return 0; +} + +/** + * msm_ipc_get_security_rule() - Get the security rule corresponding to a + * service + * @service_id: Service ID for which the rule has to be got. + * @instance_id: Instance ID for which the rule has to be got. + * + * @return: Returns the rule info on success, NULL on error. + * + * This function is used when the service comes up and gets registered with + * the IPC Router. + */ +void *msm_ipc_get_security_rule(uint32_t service_id, uint32_t instance_id) +{ + int key; + struct security_rule *rule; + + key = (service_id & (SEC_RULES_HASH_SZ - 1)); + mutex_lock(&security_rules_lock); + /* Return the rule for a specific , if found. */ + list_for_each_entry(rule, &security_rules[key], list) { + if ((rule->service_id == service_id) && + (rule->instance_id == instance_id)) { + mutex_unlock(&security_rules_lock); + return (void *)rule; + } + } + + /* Return the rule for a specific service, if found. */ + list_for_each_entry(rule, &security_rules[key], list) { + if ((rule->service_id == service_id) && + (rule->instance_id == ALL_INSTANCE)) { + mutex_unlock(&security_rules_lock); + return (void *)rule; + } + } + + /* Return the default rule, if no rule defined for a service. */ + key = (ALL_SERVICE & (SEC_RULES_HASH_SZ - 1)); + list_for_each_entry(rule, &security_rules[key], list) { + if ((rule->service_id == ALL_SERVICE) && + (rule->instance_id == ALL_INSTANCE)) { + mutex_unlock(&security_rules_lock); + return (void *)rule; + } + } + return NULL; +} +EXPORT_SYMBOL(msm_ipc_get_security_rule); + +/** + * msm_ipc_check_send_permissions() - Check if the sendng process has + * permissions specified as per the rule + * @data: Security rule to be checked. + * + * @return: true if the process has permissions, else false. + * + * This function is used to check if the current executing process has + * permissions to send message to the remote entity. The security rule + * corresponding to the remote entity is specified by "data" parameter + */ +int msm_ipc_check_send_permissions(void *data) +{ + int i; + struct security_rule *rule = (struct security_rule *)data; + + /* Source/Sender is Root user */ + if (!current_euid()) + return 1; + + /* Destination has no rules defined, possibly a client. */ + if (!rule) + return 1; + + for (i = 0; i < rule->num_group_info; i++) { + if (in_egroup_p(rule->group_id[i])) + return 1; + } + return 0; +} +EXPORT_SYMBOL(msm_ipc_check_send_permissions); + +/** + * msm_ipc_router_security_init() - Initialize the security rule database + * + * @return: 0 if successful, < 0 for error. + */ +int msm_ipc_router_security_init(void) +{ + int i; + + for (i = 0; i < SEC_RULES_HASH_SZ; i++) + INIT_LIST_HEAD(&security_rules[i]); + + msm_ipc_add_default_rule(); + return 0; +} +EXPORT_SYMBOL(msm_ipc_router_security_init); diff --git a/arch/arm/mach-msm/msm_ipc_router_security.h b/arch/arm/mach-msm/msm_ipc_router_security.h new file mode 100644 index 00000000000..d6283db3ecc --- /dev/null +++ b/arch/arm/mach-msm/msm_ipc_router_security.h @@ -0,0 +1,104 @@ +/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _MSM_IPC_ROUTER_SECURITY_H +#define _MSM_IPC_ROUTER_SECURITY_H + +#include +#include +#include + +#ifdef CONFIG_MSM_IPC_ROUTER_SECURITY +#include + +/** + * check_permisions() - Check whether the process has permissions to + * create an interface handle with IPC Router + * + * @return: true if the process has permissions, else false. + */ +int check_permissions(void); + +/** + * msm_ipc_config_sec_rules() - Add a security rule to the database + * @arg: Pointer to the buffer containing the rule. + * + * @return: 0 if successfully added, < 0 for error. + * + * A security rule is defined using tuple. The rule + * implies that a user-space process in order to send a QMI message to + * service Service_ID should belong to the Linux group Group_ID. + */ +int msm_ipc_config_sec_rules(void *arg); + +/** + * msm_ipc_get_security_rule() - Get the security rule corresponding to a + * service + * @service_id: Service ID for which the rule has to be got. + * @instance_id: Instance ID for which the rule has to be got. + * + * @return: Returns the rule info on success, NULL on error. + * + * This function is used when the service comes up and gets registered with + * the IPC Router. + */ +void *msm_ipc_get_security_rule(uint32_t service_id, uint32_t instance_id); + +/** + * msm_ipc_check_send_permissions() - Check if the sendng process has + * permissions specified as per the rule + * @data: Security rule to be checked. + * + * @return: true if the process has permissions, else false. + * + * This function is used to check if the current executing process has + * permissions to send message to the remote entity. The security rule + * corresponding to the remote entity is specified by "data" parameter + */ +int msm_ipc_check_send_permissions(void *data); + +/** + * msm_ipc_router_security_init() - Initialize the security rule database + * + * @return: 0 if successful, < 0 for error. + */ +int msm_ipc_router_security_init(void); + +#else + +static inline int check_permissions(void) +{ + return 1; +} + +static inline int msm_ipc_config_sec_rules(void *arg) +{ + return -ENODEV; +} + +static inline void *msm_ipc_get_security_rule(uint32_t service_id, + uint32_t instance_id) +{ + return NULL; +} + +static inline int msm_ipc_check_send_permissions(void *data) +{ + return 1; +} + +static inline int msm_ipc_router_security_init(void) +{ + return 0; +} +#endif +#endif diff --git a/include/linux/msm_ipc.h b/include/linux/msm_ipc.h index 44fa8ebfb2f..7b6bf41a07e 100644 --- a/include/linux/msm_ipc.h +++ b/include/linux/msm_ipc.h @@ -45,6 +45,14 @@ struct sockaddr_msm_ipc { unsigned char reserved; }; +struct config_sec_rules_args { + int num_group_info; + uint32_t service_id; + uint32_t instance_id; + unsigned reserved; + gid_t group_id[0]; +}; + #define IPC_ROUTER_IOCTL_MAGIC (0xC3) #define IPC_ROUTER_IOCTL_GET_VERSION \ @@ -62,6 +70,9 @@ struct sockaddr_msm_ipc { #define IPC_ROUTER_IOCTL_BIND_CONTROL_PORT \ _IOR(IPC_ROUTER_IOCTL_MAGIC, 4, unsigned int) +#define IPC_ROUTER_IOCTL_CONFIG_SEC_RULES \ + _IOR(IPC_ROUTER_IOCTL_MAGIC, 5, struct config_sec_rules_args) + struct msm_ipc_server_info { uint32_t node_id; uint32_t port_id;