From patchwork Mon Nov 27 17:18:34 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Djalal Harouni X-Patchwork-Id: 841796 X-Patchwork-Delegate: davem@davemloft.net Return-Path: X-Original-To: patchwork-incoming@ozlabs.org Delivered-To: patchwork-incoming@ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=netdev-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="AvhxGHts"; dkim-atps=neutral Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 3yltqG3clyz9sRW for ; Tue, 28 Nov 2017 04:20:42 +1100 (AEDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S932139AbdK0RTS (ORCPT ); Mon, 27 Nov 2017 12:19:18 -0500 Received: from mail-wm0-f65.google.com ([74.125.82.65]:36538 "EHLO mail-wm0-f65.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1753597AbdK0RTK (ORCPT ); Mon, 27 Nov 2017 12:19:10 -0500 Received: by mail-wm0-f65.google.com with SMTP id r68so36698972wmr.1; Mon, 27 Nov 2017 09:19:09 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=3DXqO672+Sc/HCHGS3geajug2NhcZt1tvhE0nQjKIEo=; b=AvhxGHtsN2H8cJI8Lk/sLSPGre5xRnsv3a+r1bkfSXMKSltXIyoAn6iuY7veSVNymk Rcyo5t9wvYV26LVBn+k+ZHhPLAJ72G914if5izC0bEHryt8ApqR/hajLy2T3GviDDnMK 2OXvfYX6wjvWBaaoQqQFKoytZA08gQty+fp5pEZBCx51Xy1z0vO3LlbFrxCJ4q7IPufC LUyDjirkCNwM/rtJ+FTbmFEABSKPavA1IhG6LdDzbzz5tsscaQ4wWRvpnTmLEsFVVCjh h1jiB409sVCzscaCNNHD526nFJK00jo8m4XwgEULXfdc3p01Xt5DlBqo0lFMONTJyzw/ B0qA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=3DXqO672+Sc/HCHGS3geajug2NhcZt1tvhE0nQjKIEo=; b=QmxOPyiMQmPedm7l4ciYtZtsAF+oAdBGCBg8ARYz3lvOYEcUTeA88mZv4TFu8w18Q/ HasRH8LLV/4WywXA95slmCY7aSmjTjIab3L1fy+Ckr4VdgyUHWt2IoZKOJ6C5ypoR0Qd NBLIBPCyBuZe+q45e3RbtoN35kEF3RbS8jajVepXComXcpjfUD5V/3a6zY/uJ+cebX9s n6QuP70+J/pCAGTCfsjLC4nRSPuZHCYdhkUDpq6u8DjIynWfLfFn5LWU5iA7+4irDEz1 hDLAVc01O3q4zPyvREpSe2mUxOH1JA9nqNXzQPbQXi4GqRIY8/sZQ0nTLz6GNhoSHfUz 55ZQ== X-Gm-Message-State: AJaThX7DygFszTGl2R2FwcokIpYInBSgeMiDeGKV6LJWFEMdydle/0bb ZI8bCr5Ihdlkk2UAMrlH+hQ= X-Google-Smtp-Source: AGs4zMaaGMLu1n+rSKFlljSqopvrDbpcTHrp9vzid64j59HYq7/AYgCTNHkGVgH735ATmgeCU9vuTg== X-Received: by 10.80.170.87 with SMTP id p23mr55669371edc.289.1511803149156; Mon, 27 Nov 2017 09:19:09 -0800 (PST) Received: from localhost.localdomain (ip-109-45-1-111.web.vodafone.de. [109.45.1.111]) by smtp.gmail.com with ESMTPSA id z56sm19356496edb.72.2017.11.27.09.19.06 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Mon, 27 Nov 2017 09:19:08 -0800 (PST) From: Djalal Harouni To: Kees Cook , Andy Lutomirski , Andrew Morton , "Luis R. Rodriguez" , James Morris , Ben Hutchings , Solar Designer , Serge Hallyn , Jessica Yu , Rusty Russell , linux-kernel@vger.kernel.org, linux-security-module@vger.kernel.org, kernel-hardening@lists.openwall.com Cc: Jonathan Corbet , Ingo Molnar , "David S. Miller" , netdev@vger.kernel.org, Peter Zijlstra , Linus Torvalds , Djalal Harouni Subject: [PATCH v5 next 1/5] modules:capabilities: add request_module_cap() Date: Mon, 27 Nov 2017 18:18:34 +0100 Message-Id: <1511803118-2552-2-git-send-email-tixxdz@gmail.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1511803118-2552-1-git-send-email-tixxdz@gmail.com> References: <1511803118-2552-1-git-send-email-tixxdz@gmail.com> Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org This is a preparation patch to improve the module auto-load infrastructure. We need this patch to have more control on module auto-load operations. The operation by default is allowed unless enduser or the calling code requests that we need to perform futher permission checks. With this change subsystems will be able to decide if module auto-load feature first will have to do a capability check and load the module if the permission check succeeds or deny the operation. As an example "netdev-%s" modules, they are allowed to be loaded if CAP_NET_ADMIN is set. Therefore, in order to not break this assumption, and allow userspace to load "netdev-%s" modules with CAP_NET_ADMIN, we have added: request_module_cap(required_cap, prefix, fmt...) This new function will take: '@required_cap': Required capability to load the module '@prefix': The module prefix if any, otherwise NULL '@fmt': printf style format string for the name of the module with its arguments if any ex: request_module_cap(CAP_NET_ADMIN, "netdev", "%s", mod); After a discussion with Rusty Russell [1], the suggestion was to pass the capability from request_module() to security_kernel_module_request() for 'netdev-%s' modules that need CAP_NET_ADMIN, and after review from Kees Cook [2] and experimenting with the code, the patch now does the following: * Adds the request_module_cap() function. * Updates the __request_module() to take the "required_cap" argument with the "prefix". This patch also updates SELinux which is currently the only user of security_kernel_module_request(), the security hook now accepts 'required_cap' and 'prefix' as arguments. Based on patch by Rusty Russell and discussion with Kees Cook: [1] https://lkml.org/lkml/2017/4/26/735 [2] https://lkml.org/lkml/2017/5/23/775 Cc: Serge Hallyn Cc: Andy Lutomirski Suggested-by: Rusty Russell Suggested-by: Kees Cook Signed-off-by: Djalal Harouni --- include/linux/kmod.h | 65 ++++++++++++++++++++++++++++++++++++++++++----- include/linux/lsm_hooks.h | 6 ++++- include/linux/security.h | 7 +++-- kernel/kmod.c | 29 ++++++++++++++++----- security/security.c | 6 +++-- security/selinux/hooks.c | 3 ++- 6 files changed, 97 insertions(+), 19 deletions(-) diff --git a/include/linux/kmod.h b/include/linux/kmod.h index 40c89ad..ccd6a1c 100644 --- a/include/linux/kmod.h +++ b/include/linux/kmod.h @@ -33,16 +33,67 @@ extern char modprobe_path[]; /* for sysctl */ /* modprobe exit status on success, -ve on error. Return value * usually useless though. */ -extern __printf(2, 3) -int __request_module(bool wait, const char *name, ...); -#define request_module(mod...) __request_module(true, mod) -#define request_module_nowait(mod...) __request_module(false, mod) +extern __printf(4, 5) +int __request_module(bool wait, int required_cap, + const char *prefix, const char *name, ...); #define try_then_request_module(x, mod...) \ - ((x) ?: (__request_module(true, mod), (x))) + ((x) ?: (__request_module(true, -1, NULL, mod), (x))) #else -static inline int request_module(const char *name, ...) { return -ENOSYS; } -static inline int request_module_nowait(const char *name, ...) { return -ENOSYS; } +static inline __printf(4, 5) +int __request_module(bool wait, int required_cap, + const char *prefix, const char *name, ...) +{ return -ENOSYS; } #define try_then_request_module(x, mod...) (x) #endif +/** + * request_module Try to load a kernel module + * + * Automatically loads the request module. + * + * @mod...: The module name + */ +#define request_module(mod...) __request_module(true, -1, NULL, mod) + +#define request_module_nowait(mod...) __request_module(false, -1, NULL, mod) + +/** + * request_module_cap Load kernel module only if the required capability is set + * + * Automatically load a module if the required capability is set and it + * corresponds to the appropriate subsystem that is indicated by prefix. + * + * This allows to load aliased modules like 'netdev-%s' with CAP_NET_ADMIN. + * + * ex: + * request_module_cap(CAP_NET_ADMIN, "netdev", "%s", mod); + * + * @required_cap: Required capability to load the module + * @prefix: The module prefix if any, otherwise NULL + * @fmt: printf style format string for the name of the module with its + * arguments if any + * + * If '@required_cap' is positive, the security subsystem will check if + * '@prefix' is set and if caller has the required capability then the + * operation is allowed. + * The security subsystem can not make assumption about the boundaries + * of other subsystems, it is their responsability to make a call with + * the right capability and module alias. + * + * If '@required_cap' is positive and '@prefix' is NULL then we assume + * that the '@required_cap' is CAP_SYS_MODULE. + * + * If '@required_cap' is negative then there are no permission checks, this + * is the equivalent to request_module() function. + * + * This function trust callers to pass the right capability with the + * appropriate prefix. + * + * Note: the permission checks may still fail, even if the required + * capability is negative, this is due to module loading restrictions + * that are controlled by the enduser. + */ +#define request_module_cap(required_cap, prefix, fmt...) \ + __request_module(true, required_cap, prefix, fmt) + #endif /* __LINUX_KMOD_H__ */ diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h index 7161d8e..d898bd0 100644 --- a/include/linux/lsm_hooks.h +++ b/include/linux/lsm_hooks.h @@ -571,6 +571,9 @@ * Ability to trigger the kernel to automatically upcall to userspace for * userspace to load a kernel module with the given name. * @kmod_name name of the module requested by the kernel + * @required_cap If positive, the required capability to automatically load + * the correspondig kernel module. + * @prefix The prefix of the module if any. Can be NULL. * Return 0 if successful. * @kernel_read_file: * Read a file specified by userspace. @@ -1543,7 +1546,8 @@ union security_list_options { void (*cred_transfer)(struct cred *new, const struct cred *old); int (*kernel_act_as)(struct cred *new, u32 secid); int (*kernel_create_files_as)(struct cred *new, struct inode *inode); - int (*kernel_module_request)(char *kmod_name); + int (*kernel_module_request)(char *kmod_name, int required_cap, + const char *prefix); int (*kernel_read_file)(struct file *file, enum kernel_read_file_id id); int (*kernel_post_read_file)(struct file *file, char *buf, loff_t size, enum kernel_read_file_id id); diff --git a/include/linux/security.h b/include/linux/security.h index 73f1ef6..41e700a 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -326,7 +326,8 @@ int security_prepare_creds(struct cred *new, const struct cred *old, gfp_t gfp); void security_transfer_creds(struct cred *new, const struct cred *old); int security_kernel_act_as(struct cred *new, u32 secid); int security_kernel_create_files_as(struct cred *new, struct inode *inode); -int security_kernel_module_request(char *kmod_name); +int security_kernel_module_request(char *kmod_name, int required_cap, + const char *prefix); int security_kernel_read_file(struct file *file, enum kernel_read_file_id id); int security_kernel_post_read_file(struct file *file, char *buf, loff_t size, enum kernel_read_file_id id); @@ -919,7 +920,9 @@ static inline int security_kernel_create_files_as(struct cred *cred, return 0; } -static inline int security_kernel_module_request(char *kmod_name) +static inline int security_kernel_module_request(char *kmod_name, + int required_cap, + const char *prefix) { return 0; } diff --git a/kernel/kmod.c b/kernel/kmod.c index bc6addd..679d401 100644 --- a/kernel/kmod.c +++ b/kernel/kmod.c @@ -109,6 +109,8 @@ static int call_modprobe(char *module_name, int wait) /** * __request_module - try to load a kernel module * @wait: wait (or not) for the operation to complete + * @required_cap: required capability to load the module + * @prefix: module prefix if any otherwise NULL * @fmt: printf style format string for the name of the module * @...: arguments as specified in the format string * @@ -119,14 +121,20 @@ static int call_modprobe(char *module_name, int wait) * must check that the service they requested is now available not blindly * invoke it. * - * If module auto-loading support is disabled then this function - * becomes a no-operation. + * If "required_cap" is positive, The security subsystem will trust the caller + * that if it has "required_cap" then it may allow to load some modules that + * have a specific alias. + * + * If module auto-loading support is disabled by clearing the modprobe path + * then this function becomes a no-operation. */ -int __request_module(bool wait, const char *fmt, ...) +int __request_module(bool wait, int required_cap, + const char *prefix, const char *fmt, ...) { va_list args; char module_name[MODULE_NAME_LEN]; int ret; + int len = 0; /* * We don't allow synchronous module loading from async. Module @@ -139,13 +147,22 @@ int __request_module(bool wait, const char *fmt, ...) if (!modprobe_path[0]) return 0; + /* + * Lets attach the prefix to the module name + */ + if (prefix != NULL && *prefix != '\0') { + len += snprintf(module_name, MODULE_NAME_LEN, "%s-", prefix); + if (len >= MODULE_NAME_LEN) + return -ENAMETOOLONG; + } + va_start(args, fmt); - ret = vsnprintf(module_name, MODULE_NAME_LEN, fmt, args); + ret = vsnprintf(module_name + len, MODULE_NAME_LEN - len, fmt, args); va_end(args); - if (ret >= MODULE_NAME_LEN) + if (ret >= MODULE_NAME_LEN - len) return -ENAMETOOLONG; - ret = security_kernel_module_request(module_name); + ret = security_kernel_module_request(module_name, required_cap, prefix); if (ret) return ret; diff --git a/security/security.c b/security/security.c index 1cd8526..91ecebd 100644 --- a/security/security.c +++ b/security/security.c @@ -1015,9 +1015,11 @@ int security_kernel_create_files_as(struct cred *new, struct inode *inode) return call_int_hook(kernel_create_files_as, 0, new, inode); } -int security_kernel_module_request(char *kmod_name) +int security_kernel_module_request(char *kmod_name, int required_cap, + const char *prefix) { - return call_int_hook(kernel_module_request, 0, kmod_name); + return call_int_hook(kernel_module_request, 0, kmod_name, + required_cap, prefix); } int security_kernel_read_file(struct file *file, enum kernel_read_file_id id) diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index d07299d..69f25da 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -3889,7 +3889,8 @@ static int selinux_kernel_create_files_as(struct cred *new, struct inode *inode) return ret; } -static int selinux_kernel_module_request(char *kmod_name) +static int selinux_kernel_module_request(char *kmod_name, int required_cap, + const char *prefix) { struct common_audit_data ad; From patchwork Mon Nov 27 17:18:35 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Djalal Harouni X-Patchwork-Id: 841794 X-Patchwork-Delegate: davem@davemloft.net Return-Path: X-Original-To: patchwork-incoming@ozlabs.org Delivered-To: patchwork-incoming@ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=netdev-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="TlzPoVPy"; dkim-atps=neutral Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 3yltpY61mdz9sRg for ; Tue, 28 Nov 2017 04:20:05 +1100 (AEDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S932223AbdK0RTU (ORCPT ); Mon, 27 Nov 2017 12:19:20 -0500 Received: from mail-wm0-f67.google.com ([74.125.82.67]:46583 "EHLO mail-wm0-f67.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1753773AbdK0RTN (ORCPT ); Mon, 27 Nov 2017 12:19:13 -0500 Received: by mail-wm0-f67.google.com with SMTP id u83so35866432wmb.5; Mon, 27 Nov 2017 09:19:12 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=iDvvJ1JYnDduZRCoBQfGyFwA6kMXuKxwHPeddDtjMoI=; b=TlzPoVPyUM8g92nxwyLsSdSq0UJq1hZM/SWTwujk7EiEDNPDqt+sDotVw0N0VHs8kD zTUxptzxIaD60wVf9QPf1luEWaqS2RLWctcY0a970E45YMrIqAA7GxpQ6YyaxnYSFMve oDwmFSHhyNzXT9RT9Th9R/U5fNvS3tMTIaki5XBw6CA308ittxO1ZX1pm/mkQR+Nw0/X MmHnF21GUINXHMNLThKNCZKDy8r12Cf7Z4zMaKEAWxXyxvBOiGCQlkvC2YyqHAKum1xj FSaG0aRVzc6OayiIltbHYF9PjrbdK1edtJpjqSsEJqeWJ5Mm0V5+oSBfpiOzDjxf/BkI fZYQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=iDvvJ1JYnDduZRCoBQfGyFwA6kMXuKxwHPeddDtjMoI=; b=jEb9yEuwj22lmEwBJI4Jenpg2dEoxtgXDSGsskA0ARUN1HDcfBrOxTLJPhnDaFzMRf Syhd6R4GtHm53CQSXwb53egddIjmdC9wrcpRcoguN4NAIhiGlWJTShFCzlLusZdFdLJC Nms+nBBvC0EWv+2sQOdzApL61aecgLqEKXat71r0N9ekQkDUmC0ELeCZUMaeuS9n4xxc dhxX3s+s2+PyvpfuYSgcIkHJN1lqgrhCk7g8asEjQBX8m8VSRNdEQexKrzqD1Uhz/Xgc DsSuYRe1ne9ffhHI0AYeuFKZ63TgDJl6sXM8wSoXEeCmpvq2o/QcZnpNI4CdII/7JGbJ MASw== X-Gm-Message-State: AJaThX5Mnn6VUNgUEvS0zho/NFKEXRou/I199YmNrZvMCEG97BK32iIb Rxk7c/AjXm8+nL1ZQSpi3sU= X-Google-Smtp-Source: AGs4zMZcX1C4F7xBGP0FFQPY1mEdBEcs7ZIUKuSH/T1tZk1RR0k6sTjHO+rPdffhVX0NOLvyqLaV9g== X-Received: by 10.80.244.132 with SMTP id s4mr53552844edm.294.1511803151917; Mon, 27 Nov 2017 09:19:11 -0800 (PST) Received: from localhost.localdomain (ip-109-45-1-111.web.vodafone.de. [109.45.1.111]) by smtp.gmail.com with ESMTPSA id z56sm19356496edb.72.2017.11.27.09.19.09 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Mon, 27 Nov 2017 09:19:11 -0800 (PST) From: Djalal Harouni To: Kees Cook , Andy Lutomirski , Andrew Morton , "Luis R. Rodriguez" , James Morris , Ben Hutchings , Solar Designer , Serge Hallyn , Jessica Yu , Rusty Russell , linux-kernel@vger.kernel.org, linux-security-module@vger.kernel.org, kernel-hardening@lists.openwall.com Cc: Jonathan Corbet , Ingo Molnar , "David S. Miller" , netdev@vger.kernel.org, Peter Zijlstra , Linus Torvalds , Djalal Harouni Subject: [PATCH v5 next 2/5] modules:capabilities: add cap_kernel_module_request() permission check Date: Mon, 27 Nov 2017 18:18:35 +0100 Message-Id: <1511803118-2552-3-git-send-email-tixxdz@gmail.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1511803118-2552-1-git-send-email-tixxdz@gmail.com> References: <1511803118-2552-1-git-send-email-tixxdz@gmail.com> Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org This is a preparation patch to improve for the module auto-load infrastrucutre. With this change, subsystems that want to autoload modules and implement onsite capability checks, can defer the checks to the capability subsystem by passing the required capabilities with the appropriate modules alias. The capability subsystem will trust callers about the passed values and perform a capability check to either allow module auto-loading or deny it. This patch changes: * Adds cap_kernel_module_request() capability hook. * Adds an empty may_autoload_module() that will be updated in the next patch. Cc: James Morris Cc: Serge Hallyn Cc: Andy Lutomirski Cc: Ben Hutchings Suggested-by: Rusty Russell Suggested-by: Kees Cook Signed-off-by: Djalal Harouni --- include/linux/module.h | 10 ++++++++++ include/linux/security.h | 4 +++- kernel/module.c | 23 +++++++++++++++++++++++ security/commoncap.c | 26 ++++++++++++++++++++++++++ 4 files changed, 62 insertions(+), 1 deletion(-) diff --git a/include/linux/module.h b/include/linux/module.h index c69b49a..5cbb239 100644 --- a/include/linux/module.h +++ b/include/linux/module.h @@ -497,6 +497,10 @@ bool __is_module_percpu_address(unsigned long addr, unsigned long *can_addr); bool is_module_percpu_address(unsigned long addr); bool is_module_text_address(unsigned long addr); +/* Determine whether a module auto-load operation is permitted. */ +int may_autoload_module(char *kmod_name, int required_cap, + const char *kmod_prefix); + static inline bool within_module_core(unsigned long addr, const struct module *mod) { @@ -643,6 +647,12 @@ bool is_module_sig_enforced(void); #else /* !CONFIG_MODULES... */ +static inline int may_autoload_module(char *kmod_name, int required_cap, + const char *kmod_prefix) +{ + return -ENOSYS; +} + static inline struct module *__module_address(unsigned long addr) { return NULL; diff --git a/include/linux/security.h b/include/linux/security.h index 41e700a..9bb53b5 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -102,6 +102,8 @@ extern int cap_task_setscheduler(struct task_struct *p); extern int cap_task_setioprio(struct task_struct *p, int ioprio); extern int cap_task_setnice(struct task_struct *p, int nice); extern int cap_vm_enough_memory(struct mm_struct *mm, long pages); +extern int cap_kernel_module_request(char *kmod_name, int required_cap, + const char *kmod_prefix); struct msghdr; struct sk_buff; @@ -924,7 +926,7 @@ static inline int security_kernel_module_request(char *kmod_name, int required_cap, const char *prefix) { - return 0; + return cap_kernel_module_request(kmod_name, required_cap, prefix); } static inline int security_kernel_read_file(struct file *file, diff --git a/kernel/module.c b/kernel/module.c index f0411a2..3380d39 100644 --- a/kernel/module.c +++ b/kernel/module.c @@ -4340,6 +4340,29 @@ struct module *__module_text_address(unsigned long addr) } EXPORT_SYMBOL_GPL(__module_text_address); +/** + * may_autoload_module - Determine whether a module auto-load operation + * is permitted + * @kmod_name: The module name + * @required_cap: if positive, may allow to auto-load the module if this + * capability is set + * @kmod_prefix: The module prefix if any, otherwise NULL + * + * Determine whether a module auto-load operation is allowed or not. + * + * This allows to have more control on automatic module loading, and align it + * with explicit load/unload module operations. The kernel contains several + * modules, some of them are not updated often and may contain bugs and + * vulnerabilities. + * + * Returns 0 if the module request is allowed or -EPERM if not. + */ +int may_autoload_module(char *kmod_name, int required_cap, + const char *kmod_prefix) +{ + return 0; +} + /* Don't grab lock, we're oopsing. */ void print_modules(void) { diff --git a/security/commoncap.c b/security/commoncap.c index 4f8e093..236e573 100644 --- a/security/commoncap.c +++ b/security/commoncap.c @@ -1340,6 +1340,31 @@ int cap_mmap_file(struct file *file, unsigned long reqprot, return 0; } +/** + * cap_kernel_module_request - Determine whether a module auto-load is permitted + * @kmod_name: The module name + * @required_cap: if positive, may allow to auto-load the module if this + * capability is set + * @kmod_prefix: the module prefix if any, otherwise NULL + * + * Determine whether a module should be automatically loaded. + * Returns 0 if the module request should be allowed, -EPERM if not. + */ +int cap_kernel_module_request(char *kmod_name, int required_cap, + const char *kmod_prefix) +{ + int ret; + char comm[sizeof(current->comm)]; + + ret = may_autoload_module(kmod_name, required_cap, kmod_prefix); + if (ret < 0) + pr_notice_ratelimited( + "module: automatic module loading of %.64s by \"%s\"[%d] was denied\n", + kmod_name, get_task_comm(comm, current), current->pid); + + return ret; +} + #ifdef CONFIG_SECURITY struct security_hook_list capability_hooks[] __lsm_ro_after_init = { @@ -1361,6 +1386,7 @@ struct security_hook_list capability_hooks[] __lsm_ro_after_init = { LSM_HOOK_INIT(task_setioprio, cap_task_setioprio), LSM_HOOK_INIT(task_setnice, cap_task_setnice), LSM_HOOK_INIT(vm_enough_memory, cap_vm_enough_memory), + LSM_HOOK_INIT(kernel_module_request, cap_kernel_module_request), }; void __init capability_add_hooks(void) From patchwork Mon Nov 27 17:18:36 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Djalal Harouni X-Patchwork-Id: 841798 X-Patchwork-Delegate: davem@davemloft.net Return-Path: X-Original-To: patchwork-incoming@ozlabs.org Delivered-To: patchwork-incoming@ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=netdev-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="hLRITNkQ"; dkim-atps=neutral Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 3yltr84v9Jz9t0F for ; Tue, 28 Nov 2017 04:21:28 +1100 (AEDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S932396AbdK0RUu (ORCPT ); Mon, 27 Nov 2017 12:20:50 -0500 Received: from mail-wm0-f51.google.com ([74.125.82.51]:42193 "EHLO mail-wm0-f51.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S932097AbdK0RTR (ORCPT ); Mon, 27 Nov 2017 12:19:17 -0500 Received: by mail-wm0-f51.google.com with SMTP id 124so18935180wmv.1; Mon, 27 Nov 2017 09:19:16 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=wWXkHDezyZMrvq0p2G2sbIzYBU58BGNRfdzr7GBzlu4=; b=hLRITNkQeohoz6NIV8xORiNAbllFjGv9YHWs81nQ+9pR667H9X/1wbKGuum/o7JYpP zalQ9X47fLPkBkNXV8ftJXcsh5ekP1aqEezeDM2muIdmALSRS81+Z3vLQeUyBMZPmDtR 0Bsi88wrw57tgKK5cAfaFW3AkHF5+5DKWlHxRh3d35eNEZai9kKzA1MwtezvThe/F2mc 9s8OKTGqnS/i4hKD81zm6M8qom0xNgyYF5NfxEaa5k3SICYVtAbjrEZvVnyj/a+irF4d LmbGoTQ3ouFItGm25OdnjEyzaGm94biHlsjzcbZ6NMLDN9k9g6jTyn1pha7+Rb3JW7JA nJGQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=wWXkHDezyZMrvq0p2G2sbIzYBU58BGNRfdzr7GBzlu4=; b=Gc/AwSEwY7TFwDtS0oNPlbqvicYlHFS6exYqHF5P0enEzFUuPE33nzBo56ufSwqDnf MvB6USiWIav8cPs0sqM1eCsdvx9cbwkiJ8UFBNv4Y/vKJq3WlZ5lAqdTk3Okskj74Z67 zRMAUOJfrNqYXb1hKPFT5CxHjnpzMAfkRYjgzgqkArdbix0ltrXz1ovY7nMBoDvwICea 6W6BqjeHeZlYaeq2rnubdC0nD7ksHRN9DOQ3xqP+dzf8nbpjTe8dKUqHFMtvtUSvRebV zJuCc2vXb3w3cUXtqTHwoeGx6nAqJQBajEVLayXicqwVVdDiy4I9nLBli5enE58i3x28 U0pw== X-Gm-Message-State: AJaThX4a6JreWBwVc3wnh3HCdgp0JDGxwMA6nV2ke8B0qanqdJJuwmob DcpICIyl7x4L0P/suifZjN8= X-Google-Smtp-Source: AGs4zMZUMEIl6dF27vlOczBxdjqpxWbaH8E1Kvku/Jj7sz74Cd8DfTe1c6gQJ2PMrgocQ3woSeS3Mg== X-Received: by 10.80.191.1 with SMTP id f1mr16526913edk.82.1511803155601; Mon, 27 Nov 2017 09:19:15 -0800 (PST) Received: from localhost.localdomain (ip-109-45-1-111.web.vodafone.de. [109.45.1.111]) by smtp.gmail.com with ESMTPSA id z56sm19356496edb.72.2017.11.27.09.19.12 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Mon, 27 Nov 2017 09:19:14 -0800 (PST) From: Djalal Harouni To: Kees Cook , Andy Lutomirski , Andrew Morton , "Luis R. Rodriguez" , James Morris , Ben Hutchings , Solar Designer , Serge Hallyn , Jessica Yu , Rusty Russell , linux-kernel@vger.kernel.org, linux-security-module@vger.kernel.org, kernel-hardening@lists.openwall.com Cc: Jonathan Corbet , Ingo Molnar , "David S. Miller" , netdev@vger.kernel.org, Peter Zijlstra , Linus Torvalds , Djalal Harouni Subject: [PATCH v5 next 3/5] modules:capabilities: automatic module loading restriction Date: Mon, 27 Nov 2017 18:18:36 +0100 Message-Id: <1511803118-2552-4-git-send-email-tixxdz@gmail.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1511803118-2552-1-git-send-email-tixxdz@gmail.com> References: <1511803118-2552-1-git-send-email-tixxdz@gmail.com> Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org Currently, an explicit call to load or unload kernel modules require CAP_SYS_MODULE capability. However unprivileged users have always been able to load some modules using the implicit auto-load operation. An automatic module loading happens when programs request a kernel feature from a module that is not loaded. In order to satisfy userspace, the kernel then automatically load all these required modules. Historically, the kernel was always able to automatically load modules if they are not blacklisted. This is one of the most important and transparent operations of Linux, it allows to provide numerous other features as they are needed which is crucial for a better user experience. However, as Linux is popular now and used for different appliances some of these may need to control such operations. For such systems, recent needs showed that in some cases allowing to control automatic module loading is as important as the operation itself. Restricting unprivileged programs or attackers that abuse this feature to load unused modules or modules that contain bugs is a significant security measure. This allows administrators or some special programs to have the appropriate time to update and deny module autoloading in advance, then blacklist the corresponding ones. Not doing so may affect the global state of the machine, especially containers where some apps are moved from one context to another and not having such mechanisms may allow to expose and exploit the vulnerable parts to escape the container sandbox. Embedded or IoT devices also started to ship as containers using generic distros, some vendors do not have the appropriate time to make their own OS, hence, using base images is getting popular. These setups may include unnecessary modules that the final applications will not need. Untrusted access may abuse the module auto-load feature to expose vulnerabilities. As every code contains bugs or vulnerabilties, the following vulnerabilities that affected some features that are often compiled as modules could have been completely blocked, by restricting autoloading modules if the system does not need them. Past months: * DCCP use after free CVE-2017-6074 [1] [2] Unprivileged to local root. * XFRM framework CVE-2017-7184 [3] As advertised it seems it was used to break Ubuntu on a security contest. * n_hldc CVE-2017-2636 [4] [5] Local privilege escalation. * L2TPv3 CVE-2016-10200 The list is longer. To improve the current status, this patch introduces "modules_autoload_mode" kernel sysctl flag. The flag controls modules auto-load feature and complements "modules_disabled" which apply to all modules operations. This new flag allows to control only automatic module loading and if it is allowed or not, aligning in the process the implicit operation with the explicit one where both now are covered by capabilities checks. The three modes that "modules_autoload_mode" support allow to provide restrictions on automatic module loading without breaking user experience. The sysctl flag is available at "/proc/sys/kernel/modules_autoload_mode" When modules_autoload_mode is set to (0), the default, there are no restrictions. When modules_autoload_mode is set to (1), processes must have CAP_SYS_MODULE to be able to trigger a module auto-load operation, or CAP_NET_ADMIN for modules with a 'netdev-%s' alias, or other capabilities for specific aliased modules. When modules_autoload_mode is set to (2), automatic module loading is disabled for all. Notes on relation between "modules_disabled=0" and "modules_autoload_mode=2": 1) Once "modules_disabled=1" set, it needs a reboot to undo the setting. 2) Restricting automatic module loading does not interfere with explicit module load or unload operations. 3) New features provided by modules can be made available without rebooting the system. 4) A bad version of a module can be unloaded and replaced with a better one without rebooting the system. The idea of module auto-load restriction was inspired from grsecurity 'GRKERNSEC_MODHARDEN' config option. Upstream Linux implementation is more focused on the run-time behavior with a three mode switch. Testing ------- Example 1) Before: $ lsmod | grep ipip - $ sudo ip tunnel add mytun mode ipip remote 10.0.2.100 local 10.0.2.15 ttl 255 $ lsmod | grep ipip - ipip 16384 0 tunnel4 16384 1 ipip ip_tunnel 28672 1 ipip $ cat /proc/sys/kernel/modules_autoload_mode 0 After: $ lsmod | grep ipip - $ sudo ip tunnel add mytun mode ipip remote 10.0.2.100 local 10.0.2.15 ttl 255 add tunnel "tunl0" failed: No such device $ dmesg ... [ 1876.378389] module: automatic module loading of netdev-tunl0 by "ip"[1453] was denied [ 1876.380994] module: automatic module loading of tunl0 by "ip"[1453] was denied ... $ lsmod | grep ipip - $ Example 2) DCCP use after free CVE-2017-6074: The code path can be triggered by unprivileged, using the trigger.c program for DCCP use after free [2] and that was fixed by commit 5edabca9d4cff7f "dccp: fix freeing skb too early for IPV6_RECVPKTINFO". Before: $ lsmod | grep dccp $ strace ./dccp_trigger ... socket(AF_INET6, SOCK_DCCP, IPPROTO_IP) = 3 ... $ lsmod | grep dccp dccp_ipv6 24576 5 dccp_ipv4 24576 5 dccp_ipv6 dccp 102400 2 dccp_ipv6,dccp_ipv4 After: Only privileged: $ lsmod | grep dccp $ strace ./dccp_trigger ... socket(AF_INET6, SOCK_DCCP, IPPROTO_IP) = -1 ESOCKTNOSUPPORT (Socket type not supported) ... $ lsmod | grep dccp $ dmesg ... [ 175.945063] module: automatic module loading of net-pf-10-proto-0-type-6 by "dccp_trigger"[1390] was denied [ 175.947952] module: automatic module loading of net-pf-10-proto-0 by "dccp_trigger"[1390] was denied [ 175.956061] module: automatic module loading of net-pf-10-proto-0-type-6 by "dccp_trigger"[1390] was denied [ 175.959733] module: automatic module loading of net-pf-10-proto-0 by "dccp_trigger"[1390] was denied $ sudo strace ./dccp_trigger ... socket(AF_INET6, SOCK_DCCP, IPPROTO_IP) = 3 ... $ lsmod | grep dccp dccp_ipv6 24576 6 dccp_ipv4 24576 5 dccp_ipv6 dccp 102400 2 dccp_ipv6,dccp_ipv4 Disable automatic module loading: $ lsmod | grep dccp $ su - root ... socket(AF_INET6, SOCK_DCCP, IPPROTO_IP) = -1 ESOCKTNOSUPPORT (Socket type not supported) ... $ lsmod | grep dccp $ dmesg ... [ 126.596545] module: automatic module loading of net-pf-10-proto-0-type-6 by "dccp_trigger"[1291] was denied [ 126.598800] module: automatic module loading of net-pf-10-proto-0 by "dccp_trigger"[1291] was denied [ 126.601264] module: automatic module loading of net-pf-10-proto-0-type-6 by "dccp_trigger"[1291] was denied [ 126.602839] module: automatic module loading of net-pf-10-proto-0 by "dccp_trigger"[1291] was denied As an example, this blocks abuses, DCCP still can be explicilty loaded by an administrator using modprobe, at same time automatic module loading is disabled forever. [1] http://www.openwall.com/lists/oss-security/2017/02/22/3 [2] https://github.com/xairy/kernel-exploits/tree/master/CVE-2017-6074 [3] http://www.openwall.com/lists/oss-security/2017/03/29/2 [4] http://www.openwall.com/lists/oss-security/2017/03/07/6 [5] https://a13xp0p0v.github.io/2017/03/24/CVE-2017-2636.html Cc: Rusty Russell Cc: James Morris Cc: Serge Hallyn Cc: Ben Hutchings Cc: Solar Designer Cc: Andy Lutomirski Suggested-by: Kees Cook Signed-off-by: Djalal Harouni --- Documentation/sysctl/kernel.txt | 54 +++++++++++++++++++++++++++ include/linux/module.h | 11 +++++- kernel/module.c | 81 ++++++++++++++++++++++++++++++++++++++++- kernel/sysctl.c | 28 ++++++++++++++ 4 files changed, 172 insertions(+), 2 deletions(-) diff --git a/Documentation/sysctl/kernel.txt b/Documentation/sysctl/kernel.txt index 694968c..dc44075 100644 --- a/Documentation/sysctl/kernel.txt +++ b/Documentation/sysctl/kernel.txt @@ -43,6 +43,7 @@ show up in /proc/sys/kernel: - l2cr [ PPC only ] - modprobe ==> Documentation/debugging-modules.txt - modules_disabled +- modules_autoload_mode - msg_next_id [ sysv ipc ] - msgmax - msgmnb @@ -413,6 +414,59 @@ to false. Generally used with the "kexec_load_disabled" toggle. ============================================================== +modules_autoload_mode: + +A sysctl to control if modules auto-load feature is allowed or not. +This sysctl complements "modules_disabled" which is for all module +operations where this flag applies only to automatic module loading. +Automatic module loading happens when programs request a kernel +feature that is implemented by an unloaded module, the kernel +automatically runs the program pointed by "modprobe" sysctl in order +to load the corresponding module. + +Historically, the kernel was always able to automatically load modules +if they are not blacklisted. This is one of the most important and +transparent operations of Linux, it allows to provide numerous other +features as they are needed which is crucial for a better user experience. +However, as Linux is popular now and used for different appliances some +of these may need to control such operations. For such systems, recent +needs showed that in some cases allowing to control automatic module +loading is as important as the operation itself. Restricting unprivileged +programs or attackers that abuse this feature to load unused modules or +modules that contain bugs is a significant security measure. + +The three modes that "modules_autoload_mode" support allow to provide +restrictions on automatic module loading without breaking user +experience. + +When modules_autoload_mode is set to (0), the default, there are no +restrictions. + +When modules_autoload_mode is set to (1), processes must have +CAP_SYS_MODULE to be able to trigger a module auto-load operation, +CAP_NET_ADMIN for modules with a 'netdev-%s' alias, or other +capabilities for specific aliased modules. + +When modules_autoload_mode is set to (2), automatic module loading +is disabled for all. + + +Notes on relation between "modules_disabled=0" and +"modules_autoload_mode=2": +1) Once "modules_disabled=1" set, it needs a reboot to undo the +setting. +2) Restricting automatic module loading does not interfere with +explicit module load or unload operations. +3) New features provided by modules can be made available without +rebooting the system. +4) A bad version of a module can still be unloaded and replaced with +a better one without rebooting the system. + +The idea of module auto-load restriction was inspired from grsecurity +'GRKERNSEC_MODHARDEN' config option. + +============================================================== + msg_next_id, sem_next_id, and shm_next_id: These three toggles allows to specify desired id for next allocated IPC diff --git a/include/linux/module.h b/include/linux/module.h index 5cbb239..c36aed8 100644 --- a/include/linux/module.h +++ b/include/linux/module.h @@ -261,7 +261,16 @@ struct notifier_block; #ifdef CONFIG_MODULES -extern int modules_disabled; /* for sysctl */ +enum { + MODULES_AUTOLOAD_ALLOWED = 0, + MODULES_AUTOLOAD_PRIVILEGED = 1, + MODULES_AUTOLOAD_DISABLED = 2, +}; + +extern int modules_disabled; /* sysctl for explicit module load/unload */ +extern int modules_autoload_mode; /* sysctl for automatic module loading */ +extern const int modules_autoload_max; /* max value for modules_autoload_mode */ + /* Get/put a kernel symbol (calls must be symmetric) */ void *__symbol_get(const char *symbol); void *__symbol_get_gpl(const char *symbol); diff --git a/kernel/module.c b/kernel/module.c index 3380d39..a7205fb 100644 --- a/kernel/module.c +++ b/kernel/module.c @@ -290,6 +290,8 @@ EXPORT_SYMBOL(is_module_sig_enforced); /* Block module loading/unloading? */ int modules_disabled = 0; +int modules_autoload_mode = MODULES_AUTOLOAD_ALLOWED; +const int modules_autoload_max = MODULES_AUTOLOAD_DISABLED; core_param(nomodule, modules_disabled, bint, 0); /* Waiting for a module to finish initializing? */ @@ -4355,12 +4357,89 @@ EXPORT_SYMBOL_GPL(__module_text_address); * modules, some of them are not updated often and may contain bugs and * vulnerabilities. * + * If "@required_cap" is positive and a valid capability then it is checked + * together with the "@kmod_prefix" to either allow or deny automatic module + * loading. + * + * However even if the caller has the required capability, the operation can + * still be denied due to the global "modules_autoload_mode" sysctl mode. Unless + * set by enduser, the operation is always allowed which is the default. + * + * The permission check is performed in this order: + * 1) If the global sysctl "modules_autoload_mode" is set to 'disabled', then + * operation is denied. + * + * 2) If the global sysctl "modules_autoload_mode" is set to 'privileged', then: + * + * 2.1) If "@required_cap" is positive and "@kmod_prefix" is set, then + * if the caller has the capability, the operation is allowed. + * + * 2.2) If "@required_cap" is positive and "@kmod_prefix" is NULL, then we + * fallback to check if caller has CAP_SYS_MODULE, if so, operation is + * allowed. + * + * 2.3) If caller passes "@required_cap" as a negative then we fallback to + * check if caller has CAP_SYS_MODULE, if so, operation is allowed. + * + * We require capabilities to autoload modules here, and CAP_SYS_MODULE here is + * the default. + * + * 2.4) Otherwise operation is denied. + * + * 3) If the global sysctl "modules_autoload_mode" is set to 'allowed' which is + * the default, then: + * + * 3.1) If "@required_cap" is positive and "@kmod_prefix" is set, we check if + * caller has the capability, if so, operation is allowed. + * In this case the calling subsystem requires the capability to be set before + * allowing modules autoload operations and we have to honor that. + * + * 3.2) If "@required_cap" is positive and "@kmod_prefix" is NULL, then we + * fallback to check if caller has CAP_SYS_MODULE, if so, operation is + * allowed. + * + * 3.3) If caller passes "@required_cap" as a negative then operation is + * allowed. This is the most common case as it is used now by + * request_module() function. + * + * 3.4) Otherwise operation is denied. + * * Returns 0 if the module request is allowed or -EPERM if not. */ int may_autoload_module(char *kmod_name, int required_cap, const char *kmod_prefix) { - return 0; + int module_require_cap = CAP_SYS_MODULE; + unsigned int autoload = modules_autoload_mode; + + /* Short-cut for most use cases where kmod auto-loading is allowed */ + if (autoload == MODULES_AUTOLOAD_ALLOWED && required_cap < 0) + return 0; + + /* If autoload is disabled then fail here */ + if (autoload == MODULES_AUTOLOAD_DISABLED) + return -EPERM; + + /* If caller requires privileges */ + if (required_cap > 0) { + /* + * If '@kmod_prefix' is set then use the '@required_cap'. + * This allows to cover 'netdev-%s' alias modules and others + * with their corresponding capability + */ + if (kmod_prefix != NULL && *kmod_prefix != '\0') + module_require_cap = required_cap; + } + + /* + * We require privileges if '@required_cap' was set or if the + * 'modules_autoload_mode' is set to 'privileged' mode. + */ + if (capable(module_require_cap)) + return 0; + + /* Otherwise fail */ + return -EPERM; } /* Don't grab lock, we're oopsing. */ diff --git a/kernel/sysctl.c b/kernel/sysctl.c index 2fb4e27..0b6f0c8 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -207,6 +207,11 @@ static int proc_taint(struct ctl_table *table, int write, void __user *buffer, size_t *lenp, loff_t *ppos); #endif +#ifdef CONFIG_MODULES +static int modules_autoload_dointvec_minmax(struct ctl_table *table, int write, + void __user *buffer, size_t *lenp, loff_t *ppos); +#endif + #ifdef CONFIG_PRINTK static int proc_dointvec_minmax_sysadmin(struct ctl_table *table, int write, void __user *buffer, size_t *lenp, loff_t *ppos); @@ -683,6 +688,15 @@ static struct ctl_table kern_table[] = { .extra1 = &one, .extra2 = &one, }, + { + .procname = "modules_autoload_mode", + .data = &modules_autoload_mode, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = modules_autoload_dointvec_minmax, + .extra1 = &zero, + .extra2 = (void *)&modules_autoload_max, + }, #endif #ifdef CONFIG_UEVENT_HELPER { @@ -2499,6 +2513,20 @@ static int proc_dointvec_minmax_sysadmin(struct ctl_table *table, int write, } #endif +#ifdef CONFIG_MODULES +static int modules_autoload_dointvec_minmax(struct ctl_table *table, int write, + void __user *buffer, size_t *lenp, loff_t *ppos) +{ + /* + * Only CAP_SYS_MODULE in init user namespace are allowed to change this + */ + if (write && !capable(CAP_SYS_MODULE)) + return -EPERM; + + return proc_dointvec_minmax(table, write, buffer, lenp, ppos); +} +#endif + struct do_proc_dointvec_minmax_conv_param { int *min; int *max; From patchwork Mon Nov 27 17:18:37 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Djalal Harouni X-Patchwork-Id: 841795 X-Patchwork-Delegate: davem@davemloft.net Return-Path: X-Original-To: patchwork-incoming@ozlabs.org Delivered-To: patchwork-incoming@ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=netdev-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="suqKWI6H"; dkim-atps=neutral Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 3yltpr6WFyz9sRW for ; Tue, 28 Nov 2017 04:20:20 +1100 (AEDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S932340AbdK0RUF (ORCPT ); Mon, 27 Nov 2017 12:20:05 -0500 Received: from mail-wm0-f68.google.com ([74.125.82.68]:37929 "EHLO mail-wm0-f68.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S932204AbdK0RTU (ORCPT ); Mon, 27 Nov 2017 12:19:20 -0500 Received: by mail-wm0-f68.google.com with SMTP id n74so10906047wmi.3; Mon, 27 Nov 2017 09:19:19 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=LAVcs87W3eATQpGOvtOv5MLFMlmIt3ZWqlQlbVVWTSU=; b=suqKWI6HECbaNC4m/l06qugq1NJXv9T9mF1DEDRGKT+6umnC6bYv4+AKWMGzI/29jZ NM9TVvGSlUZkfd6wUf66Kmckq4cpKyvddNdjHucFd38iEmkzzqfayrPC7Zc1pL91RtqS nhVjg+TJAwCt7C3n3JgKICAdDKydxhHo2hq6eIN9Q6Jjf2yW3WXQLM8WObiISALxY+1g X39+lPZSsBx2EZe1WwukKYkUEe4EcXf1ZnbzYN6F6GGlD2Er2g761rsXyH/ol+uS0ify okYU3Yll8qhA0FQJ5/xPkxSyuVJzd2h8n5OPelBuh8OO/Z2NsbxkAquBAGZ1aN1AKSrf Vlyw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=LAVcs87W3eATQpGOvtOv5MLFMlmIt3ZWqlQlbVVWTSU=; b=iIRlNsf/UjJMPTLJd7Clb2OAHFfQsDbNTKdbOuObR9zkYR+jR0v+JwnQDZIn4z3F5D y0kZ29ANcYEalc1zYpAMNxUcRjGN6OGxUN8MYKABqIK+pusouVb3YdHMUiRVB3tux7T4 9GSBCgZyO+YRM2jVchtjdS2OR3A2x3lm2CuJQzPevx3PU6TF1Mc6JM7ifz1mBs1yINyY cmn4hKYrqDmhNP4VSkWXct8B+7SJ+ACcxAXIzE47dxOrv1LF+o76KoWuqN/vTgf3JsK8 B/LAU7f0zWphqhUJkzXEBg3jEKKCMr5MRrFpahMTm9TbbkrM2wRLMirtnqCbFN4TzLz3 5kFQ== X-Gm-Message-State: AJaThX4SZt5x3GB4W0eUyjpJZwCmcfL/ZUDqaadnuNZuX4lmMV99EXUe uAg3t9IS/CBQ5skMMyQsphc= X-Google-Smtp-Source: AGs4zMbhsQiYIFHrhFknExWh+WzeS79SS/WQaUEFmlqZniuVh29rYdPICen8qu9fCxfBEO90j282QA== X-Received: by 10.80.171.132 with SMTP id u4mr40131755edc.193.1511803158450; Mon, 27 Nov 2017 09:19:18 -0800 (PST) Received: from localhost.localdomain (ip-109-45-1-111.web.vodafone.de. [109.45.1.111]) by smtp.gmail.com with ESMTPSA id z56sm19356496edb.72.2017.11.27.09.19.15 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Mon, 27 Nov 2017 09:19:17 -0800 (PST) From: Djalal Harouni To: Kees Cook , Andy Lutomirski , Andrew Morton , "Luis R. Rodriguez" , James Morris , Ben Hutchings , Solar Designer , Serge Hallyn , Jessica Yu , Rusty Russell , linux-kernel@vger.kernel.org, linux-security-module@vger.kernel.org, kernel-hardening@lists.openwall.com Cc: Jonathan Corbet , Ingo Molnar , "David S. Miller" , netdev@vger.kernel.org, Peter Zijlstra , Linus Torvalds , Djalal Harouni Subject: [PATCH v5 next 4/5] modules:capabilities: add a per-task modules auto-load mode Date: Mon, 27 Nov 2017 18:18:37 +0100 Message-Id: <1511803118-2552-5-git-send-email-tixxdz@gmail.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1511803118-2552-1-git-send-email-tixxdz@gmail.com> References: <1511803118-2552-1-git-send-email-tixxdz@gmail.com> Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org Previous patches added the global sysctl "modules_autoload_mode". This patch make it possible to support process trees, containers, and sandboxes by providing an inherited per-task "modules_autoload_mode" flag that cannot be re-enabled once disabled. This allows to improve automatic module loading without affecting the rest of the system. Why we need this ? Usually a request to a kernel feature that is implemented by a module that is not loaded may trigger automatic module loading feature, allowing to transparently satisfy userspace, and provide numeours features as they are needed. In this case an implicit kernel module load operation happens. In most cases to load or unload a kernel module, an explicit operation happens where programs are required to have CAP_SYS_MODULE capability to perform so. However, in general with implicit module loading, no capabilities are required as automatic module loading is one of the most important and transparent operations of Linux. Recent vulnerabilities showed that automatic module loading can be abused in order to expose more bugs. Some of these vulnerabilities are: * DCCP use after free CVE-2017-6074 [1] [2] Unprivileged to local root PoC. * XFRM framework CVE-2017-7184 [3] As advertised it seems it was used to break Ubuntu at a security contest. * n_hldc CVE-2017-2636 [4] [5] Local privilege escalation. * L2TPv3 CVE-2016-10200 Currently most of Linux code is in a form of modules, and not all modules are written or maintained in the same way. In a container or sandbox world, apps can be moved from one context to another or from one Linux system to another one, the ability to restrict some of these apps to load extra kernel modules will prevent exposing some kernel interfaces that have not been updated withing such systems. The DCCP vulnerability CVE-2017-6074 that can be triggered by unprivileged, or CVE-2017-7184 in the XFRM framework are some recent real examples. CVE-2017-7184 was used to break Ubuntu at a security contest. Ubuntu is more of desktop distro, using a global switch to disable automatic module loading will harm users. Actually this design will always end up being ignored by such kind of systems that need to offer a competitive and interactive solution for their users. From this and from observing how apps are being run, this patch introduces a per-task "modules_autoload_mode" to restrict automatic module loading. This offers the following advantages: 1) Allows to abstract in userspace as something like: DenyNewFeatures=yes 2) Automatic module loading is still available to the rest of the system. 2) It is easy to use in containers and sandboxes. DCCP example could have been used to escape containers. The XFRM framework CVE-2017-7184 needs CAP_NET_ADMIN, but attackers may start to target CAP_NET_ADMIN, a per-task flag will make it harder. 3) Suitable for desktop and more interactive Linux systems. 4) Will allow in future to implement a per user policy. The user database format is old and not extensible, as discussed maybe with a modern format we may achieve the following: User=djalal DenyNewFeatures=no Which means that interactive user will be allowed to load extra Linux features. Others, volatile accounts or guests can be easily blocked from doing so. 5) CAP_NET_ADMIN is useful, it handles lot of operations, at same time it started to look more like CAP_SYS_ADMIN which is overloaded. We need CAP_NET_ADMIN, containers need it, but at same time maybe we do not want programs running with it to load 'netdev-%s' modules. Having an extra per-task flag allow to discharge CAP_NET_ADMIN and other capabilities, it is clearly targeted to automatic module loading operations and from a higher view to 'load new kernel features schema'. Usage: ------ To set the per-task "modules_autoload_mode": prctl(PR_SET_MODULES_AUTOLOAD_MODE, mode, 0, 0, 0); When a module auto-load request is triggered by current task, then the operation has first to satisfy the per-task access mode before attempting to implicitly load the module. Once set, this setting is inherited across fork, clone and execve. Prior to use, the task must call prctl(PR_SET_NO_NEW_PRIVS, 1) or run with CAP_SYS_ADMIN privileges in its namespace. If these are not true, -EACCES will be returned. This requirement ensures that unprivileged programs cannot affect the behaviour or surprise privileged children. The per-task "modules_autoload_mode" supports the following values: 0 There are no restrictions, usually the default unless set by parent. 1 The task must have CAP_SYS_MODULE to be able to trigger a module auto-load operation, or CAP_NET_ADMIN for modules with a 'netdev-%s' alias. 2 Automatic modules loading is disabled for the current task. The mode may only be increased, never decreased, thus ensuring that once applied, processes can never relax their setting. This make it easy for developers and users to handle. Note that even if the per-task "modules_autoload_mode" allows to auto-load the corresponding modules, automatic module loading may still fail due to the global sysctl "modules_autoload_mode". For more details please see Documentation/sysctl/kernel.txt, section "modules_autoload_mode". When a request to a kernel module is denied, the module name with the corresponding process name and its pid are logged. Administrators can use such information to explicitly load the appropriate modules. Testing per-task or per container setup --------------------------------------- The following tool can be used to test the feature: https://gist.githubusercontent.com/tixxdz/cf567e4275714199a32c4a80de4ea63a/raw/13e52ea0ee65772871bcf10fb6c94fedd349f5c1/pr_modules_autoload_mode_test.c Example 1) Before patch: $ lsmod | grep ipip - $ sudo ip tunnel add mytun mode ipip remote 10.0.2.100 local 10.0.2.15 ttl 255 $ lsmod | grep ipip - ipip 16384 0 tunnel4 16384 1 ipip ip_tunnel 28672 1 ipip $ grep Modules /proc/self/status ModulesAutoloadMode: 0 After patch: Set task "modules_autoload_mode" to disabled. $ lsmod | grep ipip - $ grep Modules /proc/self/status ModulesAutoloadMode: 0 $ su - root # ./pr_modules_autoload_mode_test 2 # grep Modules /proc/self/status ModulesAutoloadMode: 2 # ip tunnel add mytun mode ipip remote 10.0.2.100 local 10.0.2.15 ttl 255 add tunnel "tunl0" failed: No such device ... [ 634.954652] module: automatic module loading of netdev-tunl0 by "ip"[1560] was denied [ 634.955775] module: automatic module loading of tunl0 by "ip"[1560] was denied ... Example 2) Sample with XFRM tunnel mode. Before patch: $ lsmod | grep xfrm - $ grep Modules /proc/self/status ModulesAutoloadMode: 0 $ sudo ip xfrm state add src 10.0.2.100 dst 10.0.1.100 proto esp spi $id1 \ > reqid $id2 mode tunnel auth "hmac(sha256)" $key1 enc "cbc(aes)" $key2 $ lsmod | grep xfrm xfrm4_mode_tunnel 16384 2 After patch: Set task "modules_autoload_mode" to disabled. $ lsmod | grep xfrm - $ grep Modules /proc/self/status ModulesAutoloadMode: 0 $ su - root # ./pr_modules_autoload_mode_test 2 # grep Modules /proc/self/status ModulesAutoloadMode: 2 # ip xfrm state add src 10.0.2.100 dst 10.0.1.100 proto esp spi $id1 \ > reqid $id2 mode tunnel auth "hmac(sha256)" $key1 enc "cbc(aes)" $key2 RTNETLINK answers: Protocol not supported ... [ 3458.139490] module: automatic module loading of xfrm-mode-2-1 by "ip"[1506] was denied ... Example 3) Here we use DCCP as an example since the public PoC was against it. DCCP use after free CVE-2017-6074 (unprivileged to local root): The code path can be triggered by unprivileged, using the trigger.c program for DCCP use after free [2] and that was fixed by commit 5edabca9d4cff7f "dccp: fix freeing skb too early for IPV6_RECVPKTINFO". Before patch: $ lsmod | grep dccp $ strace ./dccp_trigger ... socket(AF_INET6, SOCK_DCCP, IPPROTO_IP) = 3 ... $ lsmod | grep dccp dccp_ipv6 24576 5 dccp_ipv4 24576 5 dccp_ipv6 dccp 102400 2 dccp_ipv6,dccp_ipv4 $ grep Modules /proc/self/status ModulesAutoloadMode: 0 After patch: Set task "modules_autoload_mode" to 1, privileged mode. $ lsmod | grep dccp $ ./pr_set_no_new_privs $ grep NoNewPrivs /proc/self/status NoNewPrivs: 1 $ ./pr_modules_autoload_mode_test 1 $ grep Modules /proc/self/status ModulesAutoloadMode: 1 $ strace ./dccp_trigger ... socket(AF_INET6, SOCK_DCCP, IPPROTO_IP) = -1 ESOCKTNOSUPPORT (Socket type not supported) ... $ lsmod | grep dccp $ dmesg ... [ 4662.171994] module: automatic module loading of net-pf-10-proto-0-type-6 by "dccp_trigger"[1759] was denied [ 4662.177284] module: automatic module loading of net-pf-10-proto-0 by "dccp_trigger"[1759] was denied [ 4662.180181] module: automatic module loading of net-pf-10-proto-0-type-6 by "dccp_trigger"[1759] was denied [ 4662.181709] module: automatic module loading of net-pf-10-proto-0 by "dccp_trigger"[1759] was denied Now task "modules_autoload_mode" to 2, disabled mode. $ lsmod | grep dccp $ grep Modules /proc/self/status ModulesAutoloadMode: 0 $ su - root # ./pr_modules_autoload_mode_test 2 # grep Modules /proc/self/status ModulesAutoloadMode: 2 # strace ./dccp_trigger ... socket(AF_INET6, SOCK_DCCP, IPPROTO_IP) = -1 ESOCKTNOSUPPORT (Socket type not supported) ... ... [ 5154.218740] module: automatic module loading of net-pf-10-proto-0-type-6 by "dccp_trigger"[1873] was denied [ 5154.219828] module: automatic module loading of net-pf-10-proto-0 by "dccp_trigger"[1873] was denied [ 5154.221814] module: automatic module loading of net-pf-10-proto-0-type-6 by "dccp_trigger"[1873] was denied [ 5154.222731] module: automatic module loading of net-pf-10-proto-0 by "dccp_trigger"[1873] was denied As showed, this blocks automatic module loading per-task. This allows to provide a usable system, where only some sandboxed apps or containers will be restricted to trigger automatic module loading, other parts of the system can continue to use the feature as it is which is the case of the desktop and userfriendly machines. [1] http://www.openwall.com/lists/oss-security/2017/02/22/3 [2] https://github.com/xairy/kernel-exploits/tree/master/CVE-2017-6074 [3] http://www.openwall.com/lists/oss-security/2017/03/29/2 [4] http://www.openwall.com/lists/oss-security/2017/03/07/6 [5] https://a13xp0p0v.github.io/2017/03/24/CVE-2017-2636.html Cc: Ben Hutchings Cc: Rusty Russell Cc: James Morris Cc: Serge Hallyn Cc: Solar Designer Cc: Andy Lutomirski Cc: Kees Cook Signed-off-by: Djalal Harouni --- Documentation/filesystems/proc.txt | 3 + Documentation/userspace-api/index.rst | 1 + .../userspace-api/modules_autoload_mode.rst | 116 +++++++++++++++++++++ fs/proc/array.c | 6 ++ include/linux/init_task.h | 8 ++ include/linux/module.h | 20 ++++ include/linux/sched.h | 5 + include/uapi/linux/prctl.h | 8 ++ kernel/module.c | 83 ++++++++++++--- security/commoncap.c | 36 +++++++ 10 files changed, 270 insertions(+), 16 deletions(-) create mode 100644 Documentation/userspace-api/modules_autoload_mode.rst diff --git a/Documentation/filesystems/proc.txt b/Documentation/filesystems/proc.txt index 2a84bb3..1974cb6 100644 --- a/Documentation/filesystems/proc.txt +++ b/Documentation/filesystems/proc.txt @@ -195,6 +195,7 @@ read the file /proc/PID/status: CapBnd: ffffffffffffffff NoNewPrivs: 0 Seccomp: 0 + ModulesAutoloadMode: 0 voluntary_ctxt_switches: 0 nonvoluntary_ctxt_switches: 1 @@ -269,6 +270,8 @@ Table 1-2: Contents of the status files (as of 4.8) CapBnd bitmap of capabilities bounding set NoNewPrivs no_new_privs, like prctl(PR_GET_NO_NEW_PRIV, ...) Seccomp seccomp mode, like prctl(PR_GET_SECCOMP, ...) + ModulesAutoloadMode modules auto-load mode, like + prctl(PR_GET_MODULES_AUTOLOAD_MODE, ...) Cpus_allowed mask of CPUs on which this process may run Cpus_allowed_list Same as previous, but in "list format" Mems_allowed mask of memory nodes allowed to this process diff --git a/Documentation/userspace-api/index.rst b/Documentation/userspace-api/index.rst index 7b2eb1b..bfd51b7 100644 --- a/Documentation/userspace-api/index.rst +++ b/Documentation/userspace-api/index.rst @@ -17,6 +17,7 @@ place where this information is gathered. :maxdepth: 2 no_new_privs + modules_autoload_mode seccomp_filter unshare diff --git a/Documentation/userspace-api/modules_autoload_mode.rst b/Documentation/userspace-api/modules_autoload_mode.rst new file mode 100644 index 0000000..1153c35 --- /dev/null +++ b/Documentation/userspace-api/modules_autoload_mode.rst @@ -0,0 +1,116 @@ +====================================== +Per-task module auto-load restrictions +====================================== + + +Introduction +============ + +Usually a request to a kernel feature that is implemented by a module +that is not loaded may trigger automatic module loading feature, allowing +to transparently satisfy userspace, and provide numerous other features +as they are needed. In this case an implicit kernel module load +operation happens. + +In most cases to load or unload a kernel module, an explicit operation +happens where programs are required to have ``CAP_SYS_MODULE`` capability +to perform so. However, with implicit module loading, no capabilities are +required, or only ``CAP_NET_ADMIN`` in rare cases where the module has the +'netdev-%s' alias. Historically this was always the case as automatic +module loading is one of the most important and transparent operations +of Linux, users expect that their programs just work, yet, recent cases +showed that this can be abused by unprivileged users or attackers to load +modules that were not updated, or modules that contain bugs and +vulnerabilities. + +Currently most of Linux code is in a form of modules, hence, allowing to +control automatic module loading in some cases is as important as the +operation itself, especially in the context where Linux is used in +different appliances. + +Restricting automatic module loading allows administratros to have the +appropriate time to update or deny module autoloading in advance. In a +container or sandbox world where apps can be moved from one context to +another, the ability to restrict some containers or apps to load extra +kernel modules will prevent exposing some kernel interfaces that may not +receive the same care as some other parts of the core. The DCCP vulnerability +CVE-2017-6074 that can be triggered by unprivileged, or CVE-2017-7184 +in the XFRM framework are some real examples where users or programs are +able to expose such kernel interfaces and escape their sandbox. + +The per-task ``modules_autoload_mode`` allow to restrict automatic module +loading per task, preventing the kernel from exposing more of its +interface. This is particularly useful for containers and sandboxes as +noted above, they are restricted from affecting the rest of the system +without affecting its functionality, automatic module loading is still +available for others. + + +Usage +===== + +When the kernel is compiled with modules support ``CONFIG_MODULES``, then: + +``PR_SET_MODULES_AUTOLOAD_MODE``: + Set the current task ``modules_autoload_mode``. When a module + auto-load request is triggered by current task, then the + operation has first to satisfy the per-task access mode before + attempting to implicitly load the module. As an example, + automatic loading of modules that contain bugs or vulnerabilities + can be restricted and unprivileged users can no longer abuse such + interfaces. Once set, this setting is inherited across ``fork(2)``, + ``clone(2)`` and ``execve(2)``. + + Prior to use, the task must call ``prctl(PR_SET_NO_NEW_PRIVS, 1)`` + or run with ``CAP_SYS_ADMIN`` privileges in its namespace. If + these are not true, ``-EACCES`` will be returned. This requirement + ensures that unprivileged programs cannot affect the behaviour or + surprise privileged children. + + Usage: + ``prctl(PR_SET_MODULES_AUTOLOAD_MODE, mode, 0, 0, 0);`` + + The 'mode' argument supports the following values: + 0 There are no restrictions, usually the default unless set + by parent. + 1 The task must have ``CAP_SYS_MODULE`` to be able to trigger a + module auto-load operation, or ``CAP_NET_ADMIN`` for modules + with a 'netdev-%s' alias. + 2 Automatic modules loading is disabled for the current task. + + The mode may only be increased, never decreased, thus ensuring + that once applied, processes can never relax their setting. + + + Returned values: + 0 On success. + ``-EINVAL`` If 'mode' is not valid, or the operation is not + supported. + ``-EACCES`` If task does not have ``CAP_SYS_ADMIN`` in its namespace + or is not running with ``no_new_privs``. + ``-EPERM`` If 'mode' is less strict than current task + ``modules_autoload_mode``. + + + Note that even if the per-task ``modules_autoload_mode`` allows to + auto-load the corresponding modules, automatic module loading + may still fail due to the global sysctl ``modules_autoload_mode``. + The default mode of ``modules_autoload_mode`` is to always allow + automatic module loading. For more details, please see + Documentation/sysctl/kernel.txt, section "modules_autoload_mode". + + + When a request to a kernel module is denied, the module name with the + corresponding process name and its pid are logged. Administrators can + use such information to explicitly load the appropriate modules. + + +``PR_GET_MODULES_AUTOLOAD_MODE``: + Return the current task ``modules_autoload_mode``. + + Usage: + ``prctl(PR_GET_MODULES_AUTOLOAD_MODE, 0, 0, 0, 0);`` + + Returned values: + mode The task's ``modules_autoload_mode`` + ``-ENOSYS`` If the kernel was compiled without ``CONFIG_MODULES``. diff --git a/fs/proc/array.c b/fs/proc/array.c index 79375fc..57b6cc5 100644 --- a/fs/proc/array.c +++ b/fs/proc/array.c @@ -90,6 +90,7 @@ #include #include #include +#include #include #include @@ -343,10 +344,15 @@ static inline void task_cap(struct seq_file *m, struct task_struct *p) static inline void task_seccomp(struct seq_file *m, struct task_struct *p) { + int autoload = task_modules_autoload_mode(p); + seq_put_decimal_ull(m, "NoNewPrivs:\t", task_no_new_privs(p)); #ifdef CONFIG_SECCOMP seq_put_decimal_ull(m, "\nSeccomp:\t", p->seccomp.mode); #endif + if (autoload != -ENOSYS) + seq_put_decimal_ull(m, "\nModulesAutoloadMode:\t", autoload); + seq_putc(m, '\n'); } diff --git a/include/linux/init_task.h b/include/linux/init_task.h index 6a53262..f564b41 100644 --- a/include/linux/init_task.h +++ b/include/linux/init_task.h @@ -153,6 +153,13 @@ extern struct cred init_cred; # define INIT_CGROUP_SCHED(tsk) #endif +#ifdef CONFIG_MODULES +# define INIT_MODULES_AUTOLOAD_MODE(tsk) \ + .modules_autoload_mode = 0, +#else +# define INIT_MODULES_AUTOLOAD_MODE(tsk) +#endif + #ifdef CONFIG_PERF_EVENTS # define INIT_PERF_EVENTS(tsk) \ .perf_event_mutex = \ @@ -250,6 +257,7 @@ extern struct cred init_cred; .tasks = LIST_HEAD_INIT(tsk.tasks), \ INIT_PUSHABLE_TASKS(tsk) \ INIT_CGROUP_SCHED(tsk) \ + INIT_MODULES_AUTOLOAD_MODE(tsk) \ .ptraced = LIST_HEAD_INIT(tsk.ptraced), \ .ptrace_entry = LIST_HEAD_INIT(tsk.ptrace_entry), \ .real_parent = &tsk, \ diff --git a/include/linux/module.h b/include/linux/module.h index c36aed8..1d742d3 100644 --- a/include/linux/module.h +++ b/include/linux/module.h @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -510,6 +511,15 @@ bool is_module_text_address(unsigned long addr); int may_autoload_module(char *kmod_name, int required_cap, const char *kmod_prefix); +/* Set 'modules_autoload_mode' of current task */ +int task_set_modules_autoload_mode(unsigned long value); + +/* Read task's 'modules_autoload_mode' */ +static inline int task_modules_autoload_mode(struct task_struct *task) +{ + return task->modules_autoload_mode; +} + static inline bool within_module_core(unsigned long addr, const struct module *mod) { @@ -662,6 +672,16 @@ static inline int may_autoload_module(char *kmod_name, int required_cap, return -ENOSYS; } +static inline int task_set_modules_autoload_mode(unsigned long value) +{ + return -ENOSYS; +} + +static inline int task_modules_autoload_mode(struct task_struct *task) +{ + return -ENOSYS; +} + static inline struct module *__module_address(unsigned long addr) { return NULL; diff --git a/include/linux/sched.h b/include/linux/sched.h index e5a2fbc..1b8cf78 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -658,6 +658,11 @@ struct task_struct { struct restart_block restart_block; +#ifdef CONFIG_MODULES + /* per-task modules auto-load mode */ + unsigned modules_autoload_mode:2; +#endif + pid_t pid; pid_t tgid; diff --git a/include/uapi/linux/prctl.h b/include/uapi/linux/prctl.h index 3165863..5baf9ae 100644 --- a/include/uapi/linux/prctl.h +++ b/include/uapi/linux/prctl.h @@ -211,4 +211,12 @@ struct prctl_mm_map { #define PR_SET_PDEATHSIG_PROC 48 #define PR_GET_PDEATHSIG_PROC 49 +/* + * Control the per-task modules auto-load mode + * + * See Documentation/prctl/modules_autoload_mode.txt for more details. + */ +#define PR_SET_MODULES_AUTOLOAD_MODE 50 +#define PR_GET_MODULES_AUTOLOAD_MODE 51 + #endif /* _LINUX_PRCTL_H */ diff --git a/kernel/module.c b/kernel/module.c index a7205fb..5c24ac4b 100644 --- a/kernel/module.c +++ b/kernel/module.c @@ -4345,6 +4345,7 @@ EXPORT_SYMBOL_GPL(__module_text_address); /** * may_autoload_module - Determine whether a module auto-load operation * is permitted + * * @kmod_name: The module name * @required_cap: if positive, may allow to auto-load the module if this * capability is set @@ -4362,47 +4363,51 @@ EXPORT_SYMBOL_GPL(__module_text_address); * loading. * * However even if the caller has the required capability, the operation can - * still be denied due to the global "modules_autoload_mode" sysctl mode. Unless - * set by enduser, the operation is always allowed which is the default. + * still be denied due to the per-task "modules_autoload_mode" mode and the + * global "modules_autoload_mode" sysctl one. Unless set by enduser, the + * operation is always allowed which is the default. * * The permission check is performed in this order: - * 1) If the global sysctl "modules_autoload_mode" is set to 'disabled', then - * operation is denied. + * 1) We calculate the strict mode of both: + * per-task 'modules_autoload_mode' and global sysctl 'modules_autoload_mode' + * + * We follow up with the result mode as "modules_autoload_mode": * - * 2) If the global sysctl "modules_autoload_mode" is set to 'privileged', then: + * 2) If "modules_autoload_mode" is set to 'disabled', then operation is denied. * - * 2.1) If "@required_cap" is positive and "@kmod_prefix" is set, then + * 3) If "modules_autoload_mode" is set to 'privileged', then: + * + * 3.1) If "@required_cap" is positive and "@kmod_prefix" is set, then * if the caller has the capability, the operation is allowed. * - * 2.2) If "@required_cap" is positive and "@kmod_prefix" is NULL, then we + * 3.2) If "@required_cap" is positive and "@kmod_prefix" is NULL, then we * fallback to check if caller has CAP_SYS_MODULE, if so, operation is * allowed. * - * 2.3) If caller passes "@required_cap" as a negative then we fallback to + * 3.3) If caller passes "@required_cap" as a negative then we fallback to * check if caller has CAP_SYS_MODULE, if so, operation is allowed. * * We require capabilities to autoload modules here, and CAP_SYS_MODULE here is * the default. * - * 2.4) Otherwise operation is denied. + * 3.4) Otherwise operation is denied. * - * 3) If the global sysctl "modules_autoload_mode" is set to 'allowed' which is - * the default, then: + * 4) If "modules_autoload_mode" is set to 'allowed' which is the default, then: * - * 3.1) If "@required_cap" is positive and "@kmod_prefix" is set, we check if + * 4.1) If "@required_cap" is positive and "@kmod_prefix" is set, we check if * caller has the capability, if so, operation is allowed. * In this case the calling subsystem requires the capability to be set before * allowing modules autoload operations and we have to honor that. * - * 3.2) If "@required_cap" is positive and "@kmod_prefix" is NULL, then we + * 4.2) If "@required_cap" is positive and "@kmod_prefix" is NULL, then we * fallback to check if caller has CAP_SYS_MODULE, if so, operation is * allowed. * - * 3.3) If caller passes "@required_cap" as a negative then operation is + * 4.3) If caller passes "@required_cap" as a negative then operation is * allowed. This is the most common case as it is used now by * request_module() function. * - * 3.4) Otherwise operation is denied. + * 4.4) Otherwise operation is denied. * * Returns 0 if the module request is allowed or -EPERM if not. */ @@ -4410,7 +4415,8 @@ int may_autoload_module(char *kmod_name, int required_cap, const char *kmod_prefix) { int module_require_cap = CAP_SYS_MODULE; - unsigned int autoload = modules_autoload_mode; + unsigned int autoload = max_t(unsigned int, modules_autoload_mode, + current->modules_autoload_mode); /* Short-cut for most use cases where kmod auto-loading is allowed */ if (autoload == MODULES_AUTOLOAD_ALLOWED && required_cap < 0) @@ -4442,6 +4448,51 @@ int may_autoload_module(char *kmod_name, int required_cap, return -EPERM; } +/** + * task_set_modules_autoload_mode - Set per-task modules auto-load mode + * @value: Value to set "modules_autoload_mode" of current task + * + * Set current task "modules_autoload_mode". The task has to have + * CAP_SYS_ADMIN in its namespace or be running with no_new_privs. This + * avoids scenarios where unprivileged tasks can affect the behaviour of + * privilged children by restricting module or kernel features. + * + * The task's "modules_autoload_mode" may only be increased, never decreased. + * + * Returns 0 on success, -EINVAL if @value is not valid, -EACCES if task does + * not have CAP_SYS_ADMIN in its namespace or is not running with no_new_privs, + * and finally -EPERM if @value is less strict than current task + * "modules_autoload_mode". + * + */ +int task_set_modules_autoload_mode(unsigned long value) +{ + if (value > MODULES_AUTOLOAD_DISABLED) + return -EINVAL; + + /* + * To set task "modules_autoload_mode" requires that the task has + * CAP_SYS_ADMIN in its namespace or be running with no_new_privs. + * This avoids scenarios where unprivileged tasks can affect the + * behaviour of privileged children by restricting module features. + */ + if (!task_no_new_privs(current) && + security_capable_noaudit(current_cred(), current_user_ns(), + CAP_SYS_ADMIN) != 0) + return -EACCES; + + /* + * The "modules_autoload_mode" may only be increased, never decreased, + * ensuring that once applied, processes can never relax their settings. + */ + if (current->modules_autoload_mode > value) + return -EPERM; + else if (current->modules_autoload_mode < value) + current->modules_autoload_mode = value; + + return 0; +} + /* Don't grab lock, we're oopsing. */ void print_modules(void) { diff --git a/security/commoncap.c b/security/commoncap.c index 236e573..67a235c 100644 --- a/security/commoncap.c +++ b/security/commoncap.c @@ -1157,6 +1157,36 @@ static int cap_prctl_drop(unsigned long cap) return commit_creds(new); } +/* + * Implement PR_SET_MODULES_AUTOLOAD_MODE. + * + * Returns 0 on success, -ve on error. + */ +static int pr_set_modules_autoload_mode(unsigned long arg2, unsigned long arg3, + unsigned long arg4, unsigned long arg5) +{ + if (arg3 || arg4 || arg5) + return -EINVAL; + + return task_set_modules_autoload_mode(arg2); +} + +/* + * Implement PR_GET_MODULES_AUTOLOAD_MODE. + * + * Return current task "modules_autoload_mode", -ve on error. + */ +static inline int pr_get_modules_autoload_mode(unsigned long arg2, + unsigned long arg3, + unsigned long arg4, + unsigned long arg5) +{ + if (arg2 || arg3 || arg4 || arg5) + return -EINVAL; + + return task_modules_autoload_mode(current); +} + /** * cap_task_prctl - Implement process control functions for this security module * @option: The process control function requested @@ -1287,6 +1317,12 @@ int cap_task_prctl(int option, unsigned long arg2, unsigned long arg3, return commit_creds(new); } + case PR_SET_MODULES_AUTOLOAD_MODE: + return pr_set_modules_autoload_mode(arg2, arg3, arg4, arg5); + + case PR_GET_MODULES_AUTOLOAD_MODE: + return pr_get_modules_autoload_mode(arg2, arg3, arg4, arg5); + default: /* No functionality available - continue with default */ return -ENOSYS; From patchwork Mon Nov 27 17:18:38 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Djalal Harouni X-Patchwork-Id: 841793 X-Patchwork-Delegate: davem@davemloft.net Return-Path: X-Original-To: patchwork-incoming@ozlabs.org Delivered-To: patchwork-incoming@ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=netdev-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="AXw9X9I/"; dkim-atps=neutral Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 3yltpF0d8Jz9s0g for ; Tue, 28 Nov 2017 04:19:49 +1100 (AEDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753825AbdK0RTh (ORCPT ); Mon, 27 Nov 2017 12:19:37 -0500 Received: from mail-wm0-f65.google.com ([74.125.82.65]:45560 "EHLO mail-wm0-f65.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S932263AbdK0RTW (ORCPT ); Mon, 27 Nov 2017 12:19:22 -0500 Received: by mail-wm0-f65.google.com with SMTP id 9so35661517wme.4; Mon, 27 Nov 2017 09:19:21 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=XSL+L5EPWRMMfUP8YuGz7oFWd3tU0M0yPScTD8vqSH4=; b=AXw9X9I/6ShQI6uk49qKA8CVkFcEUntqGOv/p4ecIIxyCE6lk5PhIvsGtBGY6n8ld2 6jvwfR4a+W8xbNgsujQOwKQaRqjDtPjRmRBVVXRoy2lwBJjeAjcgVoWEqmv1r5e7KBWC 0OmiszJwB+dPvrsFTeu+ltDZKOTpzrJT8Eq7QNsNCQ+loyzi9x4yhJKxw+SbVoKYL04E P+yMI0NL14Eudh9Ve7w7k4vZ235lp06+/0FjriWcD5U/S3WbWnsbhpSiRSgqn73he+lx ig5mhXNkuWREBfeD4Zc4vJi3/Nu6OslA4wDvZ5zmGFT8KhqsD91kj8KlHlop12jmpIgw +j/A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=XSL+L5EPWRMMfUP8YuGz7oFWd3tU0M0yPScTD8vqSH4=; b=eqigeM+Ct5EFVvHct38o5fer0YmHmf+uTW19sdcqtyT+dBIhswRxKtqLATW8A647M1 fm42dolpAxF/1HeGK9cfK0Oo7N4RJHLSSQqOKkMA28yvE7RCQVy/jzoaEJ/+yfrWzw0t h5vY9LVGObK+q8hI8ldTTNtAFsBXGK6K9gB0hJN9AUoa+vIdm6bym/s8xzPIMaPiDIAr 9kbN9pFNqULBrbQf8IXZiKTEPJMlwieP2TfVffbYAy4UhlQ4ilD4dZiCLndOTj/FY+e8 qiwpzbb7plYVYTs/FMgXPfAF0BrCrzE85VyhSQ33n+03k9YtbECuO3H78PXsX5Wqx2EM s6bw== X-Gm-Message-State: AJaThX7omn91svSQSJPjWFjdKoZbq6z8WXM6UDkTnCe/IpUinHWg6GZ7 awvmx/e0KcJpWkionVmD/JU= X-Google-Smtp-Source: AGs4zMaYFOXJssKxhw0ZIjojD5I8tfw6nIIjUxHIaKLZ0XN+3DdSdMiLWbgVyyWEiDkdDIG3BjTwrg== X-Received: by 10.80.174.143 with SMTP id e15mr55154850edd.10.1511803160689; Mon, 27 Nov 2017 09:19:20 -0800 (PST) Received: from localhost.localdomain (ip-109-45-1-111.web.vodafone.de. [109.45.1.111]) by smtp.gmail.com with ESMTPSA id z56sm19356496edb.72.2017.11.27.09.19.18 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Mon, 27 Nov 2017 09:19:19 -0800 (PST) From: Djalal Harouni To: Kees Cook , Andy Lutomirski , Andrew Morton , "Luis R. Rodriguez" , James Morris , Ben Hutchings , Solar Designer , Serge Hallyn , Jessica Yu , Rusty Russell , linux-kernel@vger.kernel.org, linux-security-module@vger.kernel.org, kernel-hardening@lists.openwall.com Cc: Jonathan Corbet , Ingo Molnar , "David S. Miller" , netdev@vger.kernel.org, Peter Zijlstra , Linus Torvalds , Djalal Harouni Subject: [PATCH v5 next 5/5] net: modules: use request_module_cap() to load 'netdev-%s' modules Date: Mon, 27 Nov 2017 18:18:38 +0100 Message-Id: <1511803118-2552-6-git-send-email-tixxdz@gmail.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1511803118-2552-1-git-send-email-tixxdz@gmail.com> References: <1511803118-2552-1-git-send-email-tixxdz@gmail.com> Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org This uses the new request_module_cap() facility to directly propagate CAP_NET_ADMIN capability and the 'netdev' module prefix to the capability subsystem as it was suggested. We do not remove the explicit capable(CAP_NET_ADMIN) check here, but we may remove it in future versions since it is also performed by the capability subsystem. This allows to have a better interface where other subsystems will just use this call and let the capability subsystem handles the permission checks, if the modules should be loaded or not. This is also an infrastructure fix since historically Linux always allowed to auto-load modules without privileges, and later the net code started to check capabilities and prefixes, adapted the CAP_NET_ADMIN check with the 'netdev' prefix to prevent abusing the capability by loading non-netdev modules. However from a bigger picture we want to continue to support automatic module loading as non privileged but also implement easy policy solutions like: User=djalal DenyNewFeatures=no Which will translate to allow the interactive user djalal to load extra Linux features. Others, volatile accounts or guests can be easily blocked from doing so. We have introduced in previous patches the necessary infrastructure and now with this change we start to use the new request_module_cap() function to explicitly tell the capability subsystem that we want to auto-load modules with CAP_NET_ADMIN if they are prefixed. This is also based on suggestions from Rusty Russel and Kees Cook [1] [1] https://lkml.org/lkml/2017/4/26/735 Cc: Ben Hutchings Cc: James Morris Cc: Serge Hallyn Cc: Solar Designer Cc: Andy Lutomirski Suggested-by: Rusty Russell Suggested-by: Kees Cook Signed-off-by: Djalal Harouni --- net/core/dev_ioctl.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/net/core/dev_ioctl.c b/net/core/dev_ioctl.c index 7e690d0..fdd8560 100644 --- a/net/core/dev_ioctl.c +++ b/net/core/dev_ioctl.c @@ -382,8 +382,10 @@ void dev_load(struct net *net, const char *name) rcu_read_unlock(); no_module = !dev; + /* "netdev-%s" modules are allowed if CAP_NET_ADMIN is set */ if (no_module && capable(CAP_NET_ADMIN)) - no_module = request_module("netdev-%s", name); + no_module = request_module_cap(CAP_NET_ADMIN, "netdev", + "%s", name); if (no_module && capable(CAP_SYS_MODULE)) request_module("%s", name); }