From patchwork Mon Dec 9 07:06:12 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sargun Dhillon X-Patchwork-Id: 11278501 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 8B05F930 for ; Mon, 9 Dec 2019 07:06:16 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 6912F20726 for ; Mon, 9 Dec 2019 07:06:16 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=sargun.me header.i=@sargun.me header.b="XlxvMoil" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727127AbfLIHGP (ORCPT ); Mon, 9 Dec 2019 02:06:15 -0500 Received: from mail-il1-f196.google.com ([209.85.166.196]:39171 "EHLO mail-il1-f196.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726477AbfLIHGP (ORCPT ); Mon, 9 Dec 2019 02:06:15 -0500 Received: by mail-il1-f196.google.com with SMTP id a7so11801225ild.6 for ; Sun, 08 Dec 2019 23:06:15 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=sargun.me; s=google; h=date:from:to:cc:subject:message-id:mime-version:content-disposition :user-agent; bh=8Hi0NAqaq4yKaz2cZXCzyXUQt+Qgg5vxTIhLWeRkvII=; b=XlxvMoil8iFkye99hWCb3hagvwkUuuTyC5CcVqEfiGimGwgOT5I2XfhVXg5JvBoO3M BTX5Ty6OR6ldJ8hLR6bTFqOExX+whlZdxpH6ewqzXig+QMhRL3F6eAB1f4vwf+BTfaFP dDAi2d7HGAKK0pOOsdA+XGCWfiBcw5+GzGPYA= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:from:to:cc:subject:message-id:mime-version :content-disposition:user-agent; bh=8Hi0NAqaq4yKaz2cZXCzyXUQt+Qgg5vxTIhLWeRkvII=; b=Atf9zql9KLihmZAUZ71alloGD5TCLFaEGe1UdPv6WNXXMn1Hoa/WK68yS2MmbG++Ew ZSPIC/LqUZ5hOoMtOyERfiFyx6nMm+JefmGQmxnkiBcauNWM4ZlsXHofKb14lyVsbPRQ /+EPb/rz4W32b1SFOUCkGdi8HQs6rZHG71SGADhv5pREgEOWrXcclSM3hXzsQPpSD0Z0 Casso9wfpBWE3+ehge8ujBuhqniZOhsIk64NT0H/aGPcs2eExRKj2EJBL1mRhpVnw0Nc jq3zokLwEQjxKG0d/YAXfhuCVdifqI9CFLlvUBHjHS8ZtzGDsIVe/QP8w6DLW4CK9Dya 6uRg== X-Gm-Message-State: APjAAAXsIQjErnW337E0qWG60XQuPWeZ0rzmgOcyYcSPNVULYLSlQpmm vH4JllYmkmGdiotqRboKL1UlkQ== X-Google-Smtp-Source: APXvYqyOqXX1bhUMR582tDO+WsTTun/QGCfWJIczZ0NFlZLLlmYhs1rETYjTNDkO85Yx7I6fCcmqhQ== X-Received: by 2002:a92:b00f:: with SMTP id x15mr25026596ilh.248.1575875174527; Sun, 08 Dec 2019 23:06:14 -0800 (PST) Received: from ircssh-2.c.rugged-nimbus-611.internal (80.60.198.104.bc.googleusercontent.com. [104.198.60.80]) by smtp.gmail.com with ESMTPSA id v10sm163147iot.12.2019.12.08.23.06.13 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Sun, 08 Dec 2019 23:06:13 -0800 (PST) Date: Mon, 9 Dec 2019 07:06:12 +0000 From: Sargun Dhillon To: linux-kernel@vger.kernel.org, containers@lists.linux-foundation.org, linux-api@vger.kernel.org, linux-fsdevel@vger.kernel.org Cc: tycho@tycho.ws, jannh@google.com, cyphar@cyphar.com, christian.brauner@ubuntu.com, oleg@redhat.com, luto@amacapital.net, viro@zeniv.linux.org.uk Subject: [PATCH v2 1/4] vfs, fdtable: Add get_task_file helper Message-ID: <20191209070609.GA32438@ircssh-2.c.rugged-nimbus-611.internal> MIME-Version: 1.0 Content-Disposition: inline User-Agent: Mutt/1.5.24 (2015-08-30) Sender: linux-fsdevel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-fsdevel@vger.kernel.org This introduces a function which can be used to fetch a file, given an arbitrary task. As long as the user holds a reference (refcnt) to the task_struct it is safe to call, and will either return NULL on failure, or a pointer to the file, with a refcnt. Signed-off-by: Sargun Dhillon --- fs/file.c | 19 +++++++++++++++++++ include/linux/fdtable.h | 10 ++++++++++ 2 files changed, 29 insertions(+) diff --git a/fs/file.c b/fs/file.c index 3da91a112bab..98601a503a0f 100644 --- a/fs/file.c +++ b/fs/file.c @@ -1015,3 +1015,22 @@ int iterate_fd(struct files_struct *files, unsigned n, return res; } EXPORT_SYMBOL(iterate_fd); + +struct file *get_task_file(struct task_struct *task, unsigned int fd) +{ + struct file *file = NULL; + + task_lock(task); + rcu_read_lock(); + + if (task->files) { + file = fcheck_files(task->files, fd); + if (file && !get_file_rcu(file)) + file = NULL; + } + + rcu_read_unlock(); + task_unlock(task); + + return file; +} diff --git a/include/linux/fdtable.h b/include/linux/fdtable.h index f07c55ea0c22..eacb1a56df44 100644 --- a/include/linux/fdtable.h +++ b/include/linux/fdtable.h @@ -115,6 +115,16 @@ int iterate_fd(struct files_struct *, unsigned, int (*)(const void *, struct file *, unsigned), const void *); +/* + * get_task_file - get a reference to a file from another task + * @task: the task to get the file descriptor from + * @fd: the file descriptor number to fetch + * + * returns NULL on failure, or pointer to the file on success, with a reference + * It requires that the task is pinned prior to calling it. + */ +struct file *get_task_file(struct task_struct *task, unsigned int fd); + extern int __alloc_fd(struct files_struct *files, unsigned start, unsigned end, unsigned flags); extern void __fd_install(struct files_struct *files, From patchwork Mon Dec 9 07:06:24 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sargun Dhillon X-Patchwork-Id: 11278503 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 9186A6C1 for ; Mon, 9 Dec 2019 07:06:28 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 667262073D for ; Mon, 9 Dec 2019 07:06:28 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=sargun.me header.i=@sargun.me header.b="q837jNoB" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727201AbfLIHG1 (ORCPT ); Mon, 9 Dec 2019 02:06:27 -0500 Received: from mail-io1-f67.google.com ([209.85.166.67]:36283 "EHLO mail-io1-f67.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727074AbfLIHG1 (ORCPT ); Mon, 9 Dec 2019 02:06:27 -0500 Received: by mail-io1-f67.google.com with SMTP id l17so13647762ioj.3 for ; Sun, 08 Dec 2019 23:06:26 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=sargun.me; s=google; h=date:from:to:cc:subject:message-id:mime-version:content-disposition :user-agent; bh=x0zFCiNSlnKRNEU0PtYMiqhfPhNw5nhVhlLq429PWcE=; b=q837jNoBniTeMoYtnAR+6omfLc1FBnL86g8L/0jn/p82DR8/jzDstLy0yM1z6TrSzO karFYyVlUA5IGN9Fqoq9c0VCIO7ONjO0RGki513iNTkqC5ppxg3QC2qevsDJ9cg4bk8B SHuLKcfGDmk/Sw5K2eaL1R4Lcj45qqwhChcpE= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:from:to:cc:subject:message-id:mime-version :content-disposition:user-agent; bh=x0zFCiNSlnKRNEU0PtYMiqhfPhNw5nhVhlLq429PWcE=; b=ujfU/yAihBZS1/P3ryR9Rt99VEw/s2JMB1SQn9qcTe40RAds+8xcXMgspUBXCwtE4b PVZDjON3dN/UGJq4X/TJUNWeEywPNymY/8EofVCCsGMQWqZ8/j7ozBSPvGP0//8zwzr0 1MJYdEToY/LlaHS4CUhSmLcxUiwXE+mYn49UjpInNtEX/gbZLgOqjA/0UiTYbCnjroVS BXXRYcq44asx3N9x/MyE+kKh2e99PAHUZ9592sLn1iUnaZ+NQfbVKQaZFkflh42wPFKl 9yZI+eCANk/kfL9Pv2ktOokeey3TCZDObGF8U+KlWzJ0q8VU6XBQ6CAhtuUqn6zYsXnj jJJg== X-Gm-Message-State: APjAAAUEl6RFwlr07y5bAOMFRl2FQeyLKBNpO3I4ltGhjJRorSWt/5EH zGonPFU1TAUXm8zzMycf8ETG7Q== X-Google-Smtp-Source: APXvYqyDfQlM7pHEBJ/gVfNz5VDaQQGyeYboFaFJupGYMYurTWZgjUCsv1wSQCHxdF+eTACDu0iJ6w== X-Received: by 2002:a02:944b:: with SMTP id a69mr19733945jai.141.1575875186342; Sun, 08 Dec 2019 23:06:26 -0800 (PST) Received: from ircssh-2.c.rugged-nimbus-611.internal (80.60.198.104.bc.googleusercontent.com. [104.198.60.80]) by smtp.gmail.com with ESMTPSA id g64sm5304851ioa.78.2019.12.08.23.06.25 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Sun, 08 Dec 2019 23:06:25 -0800 (PST) Date: Mon, 9 Dec 2019 07:06:24 +0000 From: Sargun Dhillon To: linux-kernel@vger.kernel.org, containers@lists.linux-foundation.org, linux-api@vger.kernel.org, linux-fsdevel@vger.kernel.org Cc: tycho@tycho.ws, jannh@google.com, cyphar@cyphar.com, christian.brauner@ubuntu.com, oleg@redhat.com, luto@amacapital.net, viro@zeniv.linux.org.uk Subject: [PATCH v2 2/4] ptrace: add PTRACE_GETFD request to fetch file descriptors from tracees Message-ID: <20191209070621.GA32450@ircssh-2.c.rugged-nimbus-611.internal> MIME-Version: 1.0 Content-Disposition: inline User-Agent: Mutt/1.5.24 (2015-08-30) Sender: linux-fsdevel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-fsdevel@vger.kernel.org PTRACE_GETFD is a generic ptrace API that allows the tracer to get file descriptors from the tracee. One reason to use this is to allow sandboxers to take actions on file descriptors on the behalf of a tracee. For example, this can be combined with seccomp-bpf's user notification to ptrace on-demand and capture an fd without requiring the tracer to always be attached to the process. The tracer can then take a privileged action on behalf of the tracee, like binding a socket to a privileged port. It works whether or not the tracee is stopped. The only prior requirement is that the tracer is attached to the process via PTRACE_ATTACH or PTRACE_SEIZE. Stopping the process breaks certain runtimes that expect to be able to preempt syscalls (quickly). In addition, it is meant to be used in an on-demand fashion to avoid breaking debuggers. The ptrace call takes a pointer to ptrace_getfd_args in data, and the size of the structure in addr. There is an options field, which can be used to state whether the fd should be opened with CLOEXEC, or not. This options field may be extended in the future to include the ability to clear cgroup information about the file descriptor at a later point. If the structure is from a newer kernel, and includes members which make it larger than the structure that's known to this kernel version, E2BIG will be returned. The requirement that the tracer has attached to the tracee prior to the capture of the file descriptor may be lifted at a later point. Signed-off-by: Sargun Dhillon --- include/uapi/linux/ptrace.h | 15 +++++++++++++++ kernel/ptrace.c | 35 +++++++++++++++++++++++++++++++++-- 2 files changed, 48 insertions(+), 2 deletions(-) diff --git a/include/uapi/linux/ptrace.h b/include/uapi/linux/ptrace.h index a71b6e3b03eb..c84655bcc453 100644 --- a/include/uapi/linux/ptrace.h +++ b/include/uapi/linux/ptrace.h @@ -101,6 +101,21 @@ struct ptrace_syscall_info { }; }; +/* + * This gets a file descriptor from a process. It requires that the process + * has either been attached to. It does not require that the process is + * stopped. + */ +#define PTRACE_GETFD 0x420f + +/* options to pass in to ptrace_getfd_args */ +#define PTRACE_GETFD_O_CLOEXEC (1 << 0) /* open the fd with cloexec */ + +struct ptrace_getfd_args { + __u32 fd; /* the tracee's file descriptor to get */ + __u32 options; +} __attribute__((packed)); + /* * These values are stored in task->ptrace_message * by tracehook_report_syscall_* to describe the current syscall-stop. diff --git a/kernel/ptrace.c b/kernel/ptrace.c index cb9ddcc08119..8f619dceac6f 100644 --- a/kernel/ptrace.c +++ b/kernel/ptrace.c @@ -31,6 +31,7 @@ #include #include #include +#include #include /* for syscall_get_* */ @@ -994,6 +995,33 @@ ptrace_get_syscall_info(struct task_struct *child, unsigned long user_size, } #endif /* CONFIG_HAVE_ARCH_TRACEHOOK */ +static int ptrace_getfd(struct task_struct *child, unsigned long user_size, + void __user *datavp) +{ + struct ptrace_getfd_args args; + unsigned int fd_flags = 0; + struct file *file; + int ret; + + ret = copy_struct_from_user(&args, sizeof(args), datavp, user_size); + if (ret) + goto out; + if ((args.options & ~(PTRACE_GETFD_O_CLOEXEC)) != 0) + return -EINVAL; + if (args.options & PTRACE_GETFD_O_CLOEXEC) + fd_flags &= O_CLOEXEC; + file = get_task_file(child, args.fd); + if (!file) + return -EBADF; + ret = get_unused_fd_flags(fd_flags); + if (ret >= 0) + fd_install(ret, file); + else + fput(file); +out: + return ret; +} + int ptrace_request(struct task_struct *child, long request, unsigned long addr, unsigned long data) { @@ -1222,7 +1250,9 @@ int ptrace_request(struct task_struct *child, long request, case PTRACE_SECCOMP_GET_METADATA: ret = seccomp_get_metadata(child, addr, datavp); break; - + case PTRACE_GETFD: + ret = ptrace_getfd(child, addr, datavp); + break; default: break; } @@ -1265,7 +1295,8 @@ SYSCALL_DEFINE4(ptrace, long, request, long, pid, unsigned long, addr, } ret = ptrace_check_attach(child, request == PTRACE_KILL || - request == PTRACE_INTERRUPT); + request == PTRACE_INTERRUPT || + request == PTRACE_GETFD); if (ret < 0) goto out_put_task_struct; From patchwork Mon Dec 9 07:06:36 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sargun Dhillon X-Patchwork-Id: 11278505 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 359D26C1 for ; Mon, 9 Dec 2019 07:06:40 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 0A39E2075B for ; Mon, 9 Dec 2019 07:06:40 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=sargun.me header.i=@sargun.me header.b="fpv9sWXF" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727219AbfLIHGj (ORCPT ); Mon, 9 Dec 2019 02:06:39 -0500 Received: from mail-il1-f193.google.com ([209.85.166.193]:33203 "EHLO mail-il1-f193.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727109AbfLIHGj (ORCPT ); Mon, 9 Dec 2019 02:06:39 -0500 Received: by mail-il1-f193.google.com with SMTP id r81so11830461ilk.0 for ; Sun, 08 Dec 2019 23:06:38 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=sargun.me; s=google; h=date:from:to:cc:subject:message-id:mime-version:content-disposition :user-agent; bh=jNutB9PnCuNZ95WEy1zSnjhlufU614JfWZptXrTBQH4=; b=fpv9sWXFQ2/PGP4P/QapZP66fhvELnlc4zHG36qoeBauf6pkiAJYJ1XsmOFUWyKosR oRX6enHbgHQVAJsb/jIjE2CngV6wTzSfnqhBh1YDQsVDtkabiq5+l4RCL/hNf/Qp4qNt YGlsffCCDWhRcjRCbjVT8KCJ2/6M9uqEvDuUM= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:from:to:cc:subject:message-id:mime-version :content-disposition:user-agent; bh=jNutB9PnCuNZ95WEy1zSnjhlufU614JfWZptXrTBQH4=; b=cACNnK1D6ITylAMo3FQ5Lh3KIRF7H3Ors+A/DGkMT0H5zvFkjEf80U+IDo+ouS+jhT yTC9KYQdp5uAokOvOFRCurQlV0imGxJXyAxbLhF3wCEfeI4CB/wIUS8ZHUEm8YdSRxQ1 LuxA99GJd/R+r9/1wGAhvSJiEEy0mXl67tgO0Ek/4EKZv0smC1arvyAHenBxtKg/j9KL fiynvU4cUOdzdjyC0CgjGcveXMLFthQeqSPcmE8MoApEYPqNMYdBOz9X7I/0oZ4DyvbS GBiVXLImt0OJKpYAWKX+x5vJNoO7ZkMmwytkPMSBbCVehL3efZvqkw0xqfoTY/oEdIyy jO+A== X-Gm-Message-State: APjAAAUmUnZ6fdp2Obc/BdDJOya3wXRxVxbLsUUpMdM7UPqgumPZ6ZLT XdO003UX2hYaKDlixE0Y3ZBFw6jasiIq0g== X-Google-Smtp-Source: APXvYqxSqJ7f+ZelOo3pn5r2/AneG9Rtm2o5fX6va2H9Ub/xxzQ7nGGzWA8DGzBn+2dqEvqXj62gzQ== X-Received: by 2002:a92:49db:: with SMTP id k88mr27811257ilg.25.1575875198110; Sun, 08 Dec 2019 23:06:38 -0800 (PST) Received: from ircssh-2.c.rugged-nimbus-611.internal (80.60.198.104.bc.googleusercontent.com. [104.198.60.80]) by smtp.gmail.com with ESMTPSA id k11sm6509236ilf.84.2019.12.08.23.06.37 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Sun, 08 Dec 2019 23:06:37 -0800 (PST) Date: Mon, 9 Dec 2019 07:06:36 +0000 From: Sargun Dhillon To: linux-kernel@vger.kernel.org, containers@lists.linux-foundation.org, linux-api@vger.kernel.org, linux-fsdevel@vger.kernel.org Cc: tycho@tycho.ws, jannh@google.com, cyphar@cyphar.com, christian.brauner@ubuntu.com, oleg@redhat.com, luto@amacapital.net, viro@zeniv.linux.org.uk Subject: [PATCH v2 3/4] samples: split generalized user-trap code into helper file Message-ID: <20191209070633.GA32462@ircssh-2.c.rugged-nimbus-611.internal> MIME-Version: 1.0 Content-Disposition: inline User-Agent: Mutt/1.5.24 (2015-08-30) Sender: linux-fsdevel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-fsdevel@vger.kernel.org This moves the code for setting up a syscall interceptor with user notification and sending the user notification file descriptor over a socket using SCM_RIGHTS into a file that can be shared between multiple samples. Signed-off-by: Sargun Dhillon --- samples/seccomp/Makefile | 6 ++- samples/seccomp/user-trap-helper.c | 84 +++++++++++++++++++++++++++++ samples/seccomp/user-trap-helper.h | 13 +++++ samples/seccomp/user-trap.c | 85 +----------------------------- 4 files changed, 103 insertions(+), 85 deletions(-) create mode 100644 samples/seccomp/user-trap-helper.c create mode 100644 samples/seccomp/user-trap-helper.h diff --git a/samples/seccomp/Makefile b/samples/seccomp/Makefile index 009775b52538..82b7347318d1 100644 --- a/samples/seccomp/Makefile +++ b/samples/seccomp/Makefile @@ -16,9 +16,13 @@ HOSTCFLAGS_bpf-direct.o += -I$(objtree)/usr/include HOSTCFLAGS_bpf-direct.o += -idirafter $(objtree)/include bpf-direct-objs := bpf-direct.o + +HOSTCFLAGS_user-trap-helper.o += -I$(objtree)/usr/include +HOSTCFLAGS_user-trap-helper.o += -idirafter $(objtree)/include + HOSTCFLAGS_user-trap.o += -I$(objtree)/usr/include HOSTCFLAGS_user-trap.o += -idirafter $(objtree)/include -user-trap-objs := user-trap.o +user-trap-objs := user-trap.o user-trap-helper.o # Try to match the kernel target. ifndef CONFIG_64BIT diff --git a/samples/seccomp/user-trap-helper.c b/samples/seccomp/user-trap-helper.c new file mode 100644 index 000000000000..f91ae9d947c5 --- /dev/null +++ b/samples/seccomp/user-trap-helper.c @@ -0,0 +1,84 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "user-trap-helper.h" + +#define ARRAY_SIZE(x) (sizeof(x) / sizeof(*(x))) + +int user_trap_syscall(int nr, unsigned int flags) +{ + struct sock_filter filter[] = { + BPF_STMT(BPF_LD+BPF_W+BPF_ABS, + offsetof(struct seccomp_data, nr)), + BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, nr, 0, 1), + BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_USER_NOTIF), + BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_ALLOW), + }; + + struct sock_fprog prog = { + .len = (unsigned short)ARRAY_SIZE(filter), + .filter = filter, + }; + + return seccomp(SECCOMP_SET_MODE_FILTER, flags, &prog); +} + +int send_fd(int sock, int fd) +{ + struct msghdr msg = {}; + struct cmsghdr *cmsg; + char buf[CMSG_SPACE(sizeof(int))] = {0}, c = 'c'; + struct iovec io = { + .iov_base = &c, + .iov_len = 1, + }; + + msg.msg_iov = &io; + msg.msg_iovlen = 1; + msg.msg_control = buf; + msg.msg_controllen = sizeof(buf); + cmsg = CMSG_FIRSTHDR(&msg); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + cmsg->cmsg_len = CMSG_LEN(sizeof(int)); + *((int *)CMSG_DATA(cmsg)) = fd; + msg.msg_controllen = cmsg->cmsg_len; + + if (sendmsg(sock, &msg, 0) < 0) { + perror("sendmsg"); + return -1; + } + + return 0; +} + +int recv_fd(int sock) +{ + struct msghdr msg = {}; + struct cmsghdr *cmsg; + char buf[CMSG_SPACE(sizeof(int))] = {0}, c = 'c'; + struct iovec io = { + .iov_base = &c, + .iov_len = 1, + }; + + msg.msg_iov = &io; + msg.msg_iovlen = 1; + msg.msg_control = buf; + msg.msg_controllen = sizeof(buf); + + if (recvmsg(sock, &msg, 0) < 0) { + perror("recvmsg"); + return -1; + } + + cmsg = CMSG_FIRSTHDR(&msg); + + return *((int *)CMSG_DATA(cmsg)); +} diff --git a/samples/seccomp/user-trap-helper.h b/samples/seccomp/user-trap-helper.h new file mode 100644 index 000000000000..a5ebda25fdfe --- /dev/null +++ b/samples/seccomp/user-trap-helper.h @@ -0,0 +1,13 @@ +#include +#include +#include + +static inline int seccomp(unsigned int op, unsigned int flags, void *args) +{ + errno = 0; + return syscall(__NR_seccomp, op, flags, args); +} + +int user_trap_syscall(int nr, unsigned int flags); +int send_fd(int sock, int fd); +int recv_fd(int sock); diff --git a/samples/seccomp/user-trap.c b/samples/seccomp/user-trap.c index 6d0125ca8af7..1b6526587456 100644 --- a/samples/seccomp/user-trap.c +++ b/samples/seccomp/user-trap.c @@ -5,101 +5,18 @@ #include #include #include -#include #include #include #include #include #include #include -#include #include #include -#include #include #include -#include #include - -#define ARRAY_SIZE(x) (sizeof(x) / sizeof(*(x))) - -static int seccomp(unsigned int op, unsigned int flags, void *args) -{ - errno = 0; - return syscall(__NR_seccomp, op, flags, args); -} - -static int send_fd(int sock, int fd) -{ - struct msghdr msg = {}; - struct cmsghdr *cmsg; - char buf[CMSG_SPACE(sizeof(int))] = {0}, c = 'c'; - struct iovec io = { - .iov_base = &c, - .iov_len = 1, - }; - - msg.msg_iov = &io; - msg.msg_iovlen = 1; - msg.msg_control = buf; - msg.msg_controllen = sizeof(buf); - cmsg = CMSG_FIRSTHDR(&msg); - cmsg->cmsg_level = SOL_SOCKET; - cmsg->cmsg_type = SCM_RIGHTS; - cmsg->cmsg_len = CMSG_LEN(sizeof(int)); - *((int *)CMSG_DATA(cmsg)) = fd; - msg.msg_controllen = cmsg->cmsg_len; - - if (sendmsg(sock, &msg, 0) < 0) { - perror("sendmsg"); - return -1; - } - - return 0; -} - -static int recv_fd(int sock) -{ - struct msghdr msg = {}; - struct cmsghdr *cmsg; - char buf[CMSG_SPACE(sizeof(int))] = {0}, c = 'c'; - struct iovec io = { - .iov_base = &c, - .iov_len = 1, - }; - - msg.msg_iov = &io; - msg.msg_iovlen = 1; - msg.msg_control = buf; - msg.msg_controllen = sizeof(buf); - - if (recvmsg(sock, &msg, 0) < 0) { - perror("recvmsg"); - return -1; - } - - cmsg = CMSG_FIRSTHDR(&msg); - - return *((int *)CMSG_DATA(cmsg)); -} - -static int user_trap_syscall(int nr, unsigned int flags) -{ - struct sock_filter filter[] = { - BPF_STMT(BPF_LD+BPF_W+BPF_ABS, - offsetof(struct seccomp_data, nr)), - BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, nr, 0, 1), - BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_USER_NOTIF), - BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_ALLOW), - }; - - struct sock_fprog prog = { - .len = (unsigned short)ARRAY_SIZE(filter), - .filter = filter, - }; - - return seccomp(SECCOMP_SET_MODE_FILTER, flags, &prog); -} +#include "user-trap-helper.h" static int handle_req(struct seccomp_notif *req, struct seccomp_notif_resp *resp, int listener) From patchwork Mon Dec 9 07:06:49 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sargun Dhillon X-Patchwork-Id: 11278507 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id C42EF930 for ; Mon, 9 Dec 2019 07:06:53 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 9B37A2073D for ; Mon, 9 Dec 2019 07:06:53 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=sargun.me header.i=@sargun.me header.b="DmYJXSqo" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727249AbfLIHGx (ORCPT ); Mon, 9 Dec 2019 02:06:53 -0500 Received: from mail-io1-f66.google.com ([209.85.166.66]:34578 "EHLO mail-io1-f66.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727196AbfLIHGw (ORCPT ); Mon, 9 Dec 2019 02:06:52 -0500 Received: by mail-io1-f66.google.com with SMTP id z193so13621710iof.1 for ; Sun, 08 Dec 2019 23:06:52 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=sargun.me; s=google; h=date:from:to:cc:subject:message-id:mime-version:content-disposition :user-agent; bh=yIxXYG1YTWqah7KHNRVeSjqJgD6uhzEhEyRg4kk5RV8=; b=DmYJXSqoXIPZuTIXFp8vraozHx7BwI0R2pioNaaS278xlAiCJOGNC1W8GSHTyOai4i NefzvQyXwmeQCIwPo8vkLyBBJADfJye02qR5gSicDw60lfTlpOA6ZoIBHzy8TDN2hbmt 3IYOG0e9+e5WXoImrYVcDkfzezP6KYj/QSBc0= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:from:to:cc:subject:message-id:mime-version :content-disposition:user-agent; bh=yIxXYG1YTWqah7KHNRVeSjqJgD6uhzEhEyRg4kk5RV8=; b=kG4vPt8pJUQLizfuCr+htUpudqv0HOE07aH88M3KAwDbxBYTTZdyveQex2Q50f5kbN 2F40YffJqub49Ub+MENhwArS4qi3YqK9wtT8YXH8yjAdUZQ8+KUsCHcQjibG5mExhuQP HZHkw1EwsrDQ5jP4trun1aEiGP4fhy2N6Vh58EpWCnWOJwa+7o4Vgjm07ng7vTWja9Ro kFnPI55kW8rGNOmGr/Pjx1yL6iZ/7s0erb74/rgYgVkDvSq3vRDTAa1IQg4gNGejrBph 6H+Xvglef9GWjWFd5dSZnUK+thQvPPGA21Mz3H1xxsD6X2gUQ3rIRCjClCUD7e2UG78l t5xg== X-Gm-Message-State: APjAAAUviSAD3rbZVpOVf37Om2ouk4GK1/ytH/nIRhVkDDulDiukgBiX AHTVyITsfjNriY92Mqb5XYiKJw== X-Google-Smtp-Source: APXvYqyNz9gr9z8RBM1I8ttENofoW7KGGplVpf7ZnnsDxfp3uZFvu65tigRIs7Pq5n5ydDX0OV3SQA== X-Received: by 2002:a02:cdd9:: with SMTP id m25mr25419197jap.123.1575875211378; Sun, 08 Dec 2019 23:06:51 -0800 (PST) Received: from ircssh-2.c.rugged-nimbus-611.internal (80.60.198.104.bc.googleusercontent.com. [104.198.60.80]) by smtp.gmail.com with ESMTPSA id y11sm6519521ilp.46.2019.12.08.23.06.51 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Sun, 08 Dec 2019 23:06:51 -0800 (PST) Date: Mon, 9 Dec 2019 07:06:49 +0000 From: Sargun Dhillon To: linux-kernel@vger.kernel.org, containers@lists.linux-foundation.org, linux-api@vger.kernel.org, linux-fsdevel@vger.kernel.org Cc: tycho@tycho.ws, jannh@google.com, cyphar@cyphar.com, christian.brauner@ubuntu.com, oleg@redhat.com, luto@amacapital.net, viro@zeniv.linux.org.uk Subject: [PATCH v2 4/4] samples: Add example of using PTRACE_GETFD in conjunction with user trap Message-ID: <20191209070646.GA32477@ircssh-2.c.rugged-nimbus-611.internal> MIME-Version: 1.0 Content-Disposition: inline User-Agent: Mutt/1.5.24 (2015-08-30) Sender: linux-fsdevel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-fsdevel@vger.kernel.org This sample adds the usage of SECCOMP_RET_USER_NOTIF together with ptrace PTRACE_GETFD. It shows trapping a syscall, and handling it by extracting the FD into the parent process without stopping the child process. Although, in this example, there's no explicit policy separation in the two processes, it can be generalized into the example of a transparent proxy. Signed-off-by: Sargun Dhillon --- samples/seccomp/.gitignore | 1 + samples/seccomp/Makefile | 9 +- samples/seccomp/user-trap-ptrace.c | 193 +++++++++++++++++++++++++++++ 3 files changed, 202 insertions(+), 1 deletion(-) create mode 100644 samples/seccomp/user-trap-ptrace.c diff --git a/samples/seccomp/.gitignore b/samples/seccomp/.gitignore index d1e2e817d556..169bc130ec39 100644 --- a/samples/seccomp/.gitignore +++ b/samples/seccomp/.gitignore @@ -2,3 +2,4 @@ bpf-direct bpf-fancy dropper user-trap +user-trap-ptrace diff --git a/samples/seccomp/Makefile b/samples/seccomp/Makefile index 82b7347318d1..c0f3ef713f5b 100644 --- a/samples/seccomp/Makefile +++ b/samples/seccomp/Makefile @@ -1,6 +1,6 @@ # SPDX-License-Identifier: GPL-2.0 ifndef CROSS_COMPILE -hostprogs-y := bpf-fancy dropper bpf-direct user-trap +hostprogs-y := bpf-fancy dropper bpf-direct user-trap user-trap-ptrace HOSTCFLAGS_bpf-fancy.o += -I$(objtree)/usr/include HOSTCFLAGS_bpf-fancy.o += -idirafter $(objtree)/include @@ -24,6 +24,11 @@ HOSTCFLAGS_user-trap.o += -I$(objtree)/usr/include HOSTCFLAGS_user-trap.o += -idirafter $(objtree)/include user-trap-objs := user-trap.o user-trap-helper.o +HOSTCFLAGS_user-trap-ptrace.o += -I$(objtree)/usr/include +HOSTCFLAGS_user-trap-ptrace.o += -idirafter $(objtree)/include +user-trap-ptrace-objs := user-trap-ptrace.o user-trap-helper.o + + # Try to match the kernel target. ifndef CONFIG_64BIT @@ -39,10 +44,12 @@ HOSTCFLAGS_dropper.o += $(MFLAG) HOSTCFLAGS_bpf-helper.o += $(MFLAG) HOSTCFLAGS_bpf-fancy.o += $(MFLAG) HOSTCFLAGS_user-trap.o += $(MFLAG) +HOSTCFLAGS_user-trap-ptrace.o += $(MFLAG) HOSTLDLIBS_bpf-direct += $(MFLAG) HOSTLDLIBS_bpf-fancy += $(MFLAG) HOSTLDLIBS_dropper += $(MFLAG) HOSTLDLIBS_user-trap += $(MFLAG) +HOSTLDLIBS_user-trap-ptrace += $(MFLAG) endif always := $(hostprogs-y) endif diff --git a/samples/seccomp/user-trap-ptrace.c b/samples/seccomp/user-trap-ptrace.c new file mode 100644 index 000000000000..5cca1cb4916c --- /dev/null +++ b/samples/seccomp/user-trap-ptrace.c @@ -0,0 +1,193 @@ +// SPDX-License-Identifier: GPL-2.0 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "user-trap-helper.h" + +#define CHILD_PORT_TRY_BIND 80 +#define CHILD_PORT_ACTUAL_BIND 4998 + +static int ptrace(long request, long pid, void *addr, unsigned long data) +{ + errno = 0; + return syscall(__NR_ptrace, request, pid, addr, data); +} + +static int ptrace_getfd(long pid, struct ptrace_getfd_args *args) +{ + errno = 0; + return syscall(__NR_ptrace, PTRACE_GETFD, pid, sizeof(*args), args); +} + +static int tracee(void) +{ + struct sockaddr_in addr = { + .sin_family = AF_INET, + .sin_port = htons(CHILD_PORT_TRY_BIND), + .sin_addr = { + .s_addr = htonl(INADDR_ANY) + } + }; + socklen_t addrlen = sizeof(addr); + int sock, ret = 1; + + sock = socket(AF_INET, SOCK_STREAM, 0); + if (sock == -1) { + perror("socket"); + goto out; + } + + + if (bind(sock, (struct sockaddr *) &addr, sizeof(addr))) { + perror("bind"); + goto out; + } + + printf("Child successfully performed bind operation\n"); + if (getsockname(sock, (struct sockaddr *) &addr, &addrlen)) { + perror("getsockname"); + goto out; + } + + + printf("Socket bound to port %d\n", ntohs(addr.sin_port)); + assert(ntohs(addr.sin_port) == CHILD_PORT_ACTUAL_BIND); + + ret = 0; +out: + return ret; +} + +static int handle_req(int listener) +{ + struct sockaddr_in addr = { + .sin_family = AF_INET, + .sin_port = htons(4998), + .sin_addr = { + .s_addr = htonl(INADDR_LOOPBACK) + } + }; + struct ptrace_getfd_args getfd_args = { + .options = PTRACE_GETFD_O_CLOEXEC + }; + struct seccomp_notif_sizes sizes; + struct seccomp_notif_resp *resp; + struct seccomp_notif *req; + int fd, ret = 1; + + if (seccomp(SECCOMP_GET_NOTIF_SIZES, 0, &sizes) < 0) { + perror("seccomp(GET_NOTIF_SIZES)"); + goto out; + } + req = malloc(sizes.seccomp_notif); + if (!req) + goto out; + memset(req, 0, sizeof(*req)); + + resp = malloc(sizes.seccomp_notif_resp); + if (!resp) + goto out_free_req; + memset(resp, 0, sizeof(*resp)); + + if (ioctl(listener, SECCOMP_IOCTL_NOTIF_RECV, req)) { + perror("ioctl recv"); + goto out; + } + printf("Child tried to call bind with fd: %lld\n", req->data.args[0]); + getfd_args.fd = req->data.args[0]; + fd = ptrace_getfd(req->pid, &getfd_args); + if (fd == -1) { + perror("ptrace_getfd"); + goto out_free_resp; + } + if (bind(fd, (struct sockaddr *) &addr, sizeof(addr))) { + perror("bind"); + goto out_free_resp; + } + + resp->id = req->id; + resp->error = 0; + resp->val = 0; + if (ioctl(listener, SECCOMP_IOCTL_NOTIF_SEND, resp) < 0) { + perror("ioctl send"); + goto out_free_resp; + } + + ret = 0; +out_free_resp: + free(resp); +out_free_req: + free(req); +out: + return ret; +} + +int main(void) +{ + int listener, sk_pair[2], ret = 1; + pid_t pid; + + if (socketpair(PF_LOCAL, SOCK_SEQPACKET, 0, sk_pair) < 0) { + perror("socketpair"); + goto out; + } + + pid = fork(); + if (pid < 0) { + perror("fork"); + goto close_pair; + } + if (pid == 0) { + if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) { + perror("prctl(NO_NEW_PRIVS)"); + exit(1); + } + listener = user_trap_syscall(__NR_bind, + SECCOMP_FILTER_FLAG_NEW_LISTENER); + if (listener < 0) { + perror("seccomp"); + exit(1); + } + if (send_fd(sk_pair[1], listener) < 0) + exit(1); + close(listener); + exit(tracee()); + } + + if (ptrace(PTRACE_SEIZE, pid, 0, PTRACE_O_EXITKILL)) { + perror("ptrace(PTRACE_SEIZE)"); + goto kill_child; + } + + listener = recv_fd(sk_pair[0]); + if (listener < 0) + goto kill_child; + + if (handle_req(listener)) + goto kill_child; + + /* Wait for child to finish */ + waitpid(pid, NULL, 0); + + ret = 0; + goto close_pair; + +kill_child: + kill(pid, SIGKILL); +close_pair: + close(sk_pair[0]); + close(sk_pair[1]); +out: + return ret; +}