1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * eCryptfs: Linux filesystem encryption layer |
4 | * |
5 | * Copyright (C) 2008 International Business Machines Corp. |
6 | * Author(s): Michael A. Halcrow <mahalcro@us.ibm.com> |
7 | */ |
8 | |
9 | #include <linux/kthread.h> |
10 | #include <linux/freezer.h> |
11 | #include <linux/slab.h> |
12 | #include <linux/wait.h> |
13 | #include <linux/mount.h> |
14 | #include "ecryptfs_kernel.h" |
15 | |
16 | struct ecryptfs_open_req { |
17 | struct file **lower_file; |
18 | struct path path; |
19 | struct completion done; |
20 | struct list_head kthread_ctl_list; |
21 | }; |
22 | |
23 | static struct ecryptfs_kthread_ctl { |
24 | #define ECRYPTFS_KTHREAD_ZOMBIE 0x00000001 |
25 | u32 flags; |
26 | struct mutex mux; |
27 | struct list_head req_list; |
28 | wait_queue_head_t wait; |
29 | } ecryptfs_kthread_ctl; |
30 | |
31 | static struct task_struct *ecryptfs_kthread; |
32 | |
33 | /** |
34 | * ecryptfs_threadfn |
35 | * @ignored: ignored |
36 | * |
37 | * The eCryptfs kernel thread that has the responsibility of getting |
38 | * the lower file with RW permissions. |
39 | * |
40 | * Returns zero on success; non-zero otherwise |
41 | */ |
42 | static int ecryptfs_threadfn(void *ignored) |
43 | { |
44 | set_freezable(); |
45 | while (1) { |
46 | struct ecryptfs_open_req *req; |
47 | |
48 | wait_event_freezable( |
49 | ecryptfs_kthread_ctl.wait, |
50 | (!list_empty(&ecryptfs_kthread_ctl.req_list) |
51 | || kthread_should_stop())); |
52 | mutex_lock(&ecryptfs_kthread_ctl.mux); |
53 | if (ecryptfs_kthread_ctl.flags & ECRYPTFS_KTHREAD_ZOMBIE) { |
54 | mutex_unlock(lock: &ecryptfs_kthread_ctl.mux); |
55 | goto out; |
56 | } |
57 | while (!list_empty(head: &ecryptfs_kthread_ctl.req_list)) { |
58 | req = list_first_entry(&ecryptfs_kthread_ctl.req_list, |
59 | struct ecryptfs_open_req, |
60 | kthread_ctl_list); |
61 | list_del(entry: &req->kthread_ctl_list); |
62 | *req->lower_file = dentry_open(path: &req->path, |
63 | flags: (O_RDWR | O_LARGEFILE), current_cred()); |
64 | complete(&req->done); |
65 | } |
66 | mutex_unlock(lock: &ecryptfs_kthread_ctl.mux); |
67 | } |
68 | out: |
69 | return 0; |
70 | } |
71 | |
72 | int __init ecryptfs_init_kthread(void) |
73 | { |
74 | int rc = 0; |
75 | |
76 | mutex_init(&ecryptfs_kthread_ctl.mux); |
77 | init_waitqueue_head(&ecryptfs_kthread_ctl.wait); |
78 | INIT_LIST_HEAD(list: &ecryptfs_kthread_ctl.req_list); |
79 | ecryptfs_kthread = kthread_run(&ecryptfs_threadfn, NULL, |
80 | "ecryptfs-kthread" ); |
81 | if (IS_ERR(ptr: ecryptfs_kthread)) { |
82 | rc = PTR_ERR(ptr: ecryptfs_kthread); |
83 | printk(KERN_ERR "%s: Failed to create kernel thread; rc = [%d]" |
84 | "\n" , __func__, rc); |
85 | } |
86 | return rc; |
87 | } |
88 | |
89 | void ecryptfs_destroy_kthread(void) |
90 | { |
91 | struct ecryptfs_open_req *req, *tmp; |
92 | |
93 | mutex_lock(&ecryptfs_kthread_ctl.mux); |
94 | ecryptfs_kthread_ctl.flags |= ECRYPTFS_KTHREAD_ZOMBIE; |
95 | list_for_each_entry_safe(req, tmp, &ecryptfs_kthread_ctl.req_list, |
96 | kthread_ctl_list) { |
97 | list_del(entry: &req->kthread_ctl_list); |
98 | *req->lower_file = ERR_PTR(error: -EIO); |
99 | complete(&req->done); |
100 | } |
101 | mutex_unlock(lock: &ecryptfs_kthread_ctl.mux); |
102 | kthread_stop(k: ecryptfs_kthread); |
103 | wake_up(&ecryptfs_kthread_ctl.wait); |
104 | } |
105 | |
106 | /** |
107 | * ecryptfs_privileged_open |
108 | * @lower_file: Result of dentry_open by root on lower dentry |
109 | * @lower_dentry: Lower dentry for file to open |
110 | * @lower_mnt: Lower vfsmount for file to open |
111 | * @cred: credential to use for this call |
112 | * |
113 | * This function gets a r/w file opened against the lower dentry. |
114 | * |
115 | * Returns zero on success; non-zero otherwise |
116 | */ |
117 | int ecryptfs_privileged_open(struct file **lower_file, |
118 | struct dentry *lower_dentry, |
119 | struct vfsmount *lower_mnt, |
120 | const struct cred *cred) |
121 | { |
122 | struct ecryptfs_open_req req; |
123 | int flags = O_LARGEFILE; |
124 | int rc = 0; |
125 | |
126 | init_completion(x: &req.done); |
127 | req.lower_file = lower_file; |
128 | req.path.dentry = lower_dentry; |
129 | req.path.mnt = lower_mnt; |
130 | |
131 | /* Corresponding dput() and mntput() are done when the |
132 | * lower file is fput() when all eCryptfs files for the inode are |
133 | * released. */ |
134 | flags |= IS_RDONLY(d_inode(lower_dentry)) ? O_RDONLY : O_RDWR; |
135 | (*lower_file) = dentry_open(path: &req.path, flags, creds: cred); |
136 | if (!IS_ERR(ptr: *lower_file)) |
137 | goto out; |
138 | if ((flags & O_ACCMODE) == O_RDONLY) { |
139 | rc = PTR_ERR(ptr: (*lower_file)); |
140 | goto out; |
141 | } |
142 | mutex_lock(&ecryptfs_kthread_ctl.mux); |
143 | if (ecryptfs_kthread_ctl.flags & ECRYPTFS_KTHREAD_ZOMBIE) { |
144 | rc = -EIO; |
145 | mutex_unlock(lock: &ecryptfs_kthread_ctl.mux); |
146 | printk(KERN_ERR "%s: We are in the middle of shutting down; " |
147 | "aborting privileged request to open lower file\n" , |
148 | __func__); |
149 | goto out; |
150 | } |
151 | list_add_tail(new: &req.kthread_ctl_list, head: &ecryptfs_kthread_ctl.req_list); |
152 | mutex_unlock(lock: &ecryptfs_kthread_ctl.mux); |
153 | wake_up(&ecryptfs_kthread_ctl.wait); |
154 | wait_for_completion(&req.done); |
155 | if (IS_ERR(ptr: *lower_file)) |
156 | rc = PTR_ERR(ptr: *lower_file); |
157 | out: |
158 | return rc; |
159 | } |
160 | |