1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2025 James Gritton.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29 #include <sys/param.h>
30 #include <sys/fcntl.h>
31 #include <sys/file.h>
32 #include <sys/filedesc.h>
33 #include <sys/kernel.h>
34 #include <sys/jail.h>
35 #include <sys/jaildesc.h>
36 #include <sys/lock.h>
37 #include <sys/malloc.h>
38 #include <sys/mutex.h>
39 #include <sys/priv.h>
40 #include <sys/stat.h>
41 #include <sys/sysproto.h>
42 #include <sys/systm.h>
43 #include <sys/ucred.h>
44 #include <sys/vnode.h>
45
46 MALLOC_DEFINE(M_JAILDESC, "jaildesc", "jail descriptors");
47
48 static fo_stat_t jaildesc_stat;
49 static fo_close_t jaildesc_close;
50 static fo_chmod_t jaildesc_chmod;
51 static fo_chown_t jaildesc_chown;
52 static fo_fill_kinfo_t jaildesc_fill_kinfo;
53 static fo_cmp_t jaildesc_cmp;
54
55 static struct fileops jaildesc_ops = {
56 .fo_read = invfo_rdwr,
57 .fo_write = invfo_rdwr,
58 .fo_truncate = invfo_truncate,
59 .fo_ioctl = invfo_ioctl,
60 .fo_poll = invfo_poll,
61 .fo_kqfilter = invfo_kqfilter,
62 .fo_stat = jaildesc_stat,
63 .fo_close = jaildesc_close,
64 .fo_chmod = jaildesc_chmod,
65 .fo_chown = jaildesc_chown,
66 .fo_sendfile = invfo_sendfile,
67 .fo_fill_kinfo = jaildesc_fill_kinfo,
68 .fo_cmp = jaildesc_cmp,
69 .fo_flags = DFLAG_PASSABLE,
70 };
71
72 /*
73 * Given a jail descriptor number, return the jaildesc, its prison,
74 * and its credential. The jaildesc will be returned locked, and
75 * prison and the credential will be returned held.
76 */
77 int
jaildesc_find(struct thread * td,int fd,struct jaildesc ** jdp,struct prison ** prp,struct ucred ** ucredp)78 jaildesc_find(struct thread *td, int fd, struct jaildesc **jdp,
79 struct prison **prp, struct ucred **ucredp)
80 {
81 struct file *fp;
82 struct jaildesc *jd;
83 struct prison *pr;
84 int error;
85
86 error = fget(td, fd, &cap_no_rights, &fp);
87 if (error != 0)
88 return (error);
89 if (fp->f_type != DTYPE_JAILDESC) {
90 error = EBADF;
91 goto out;
92 }
93 jd = fp->f_data;
94 JAILDESC_LOCK(jd);
95 pr = jd->jd_prison;
96 if (pr == NULL || !prison_isvalid(pr)) {
97 error = ENOENT;
98 JAILDESC_UNLOCK(jd);
99 goto out;
100 }
101 prison_hold(pr);
102 *prp = pr;
103 if (jdp != NULL)
104 *jdp = jd;
105 else
106 JAILDESC_UNLOCK(jd);
107 if (ucredp != NULL)
108 *ucredp = crhold(fp->f_cred);
109 out:
110 fdrop(fp, td);
111 return (error);
112 }
113
114 /*
115 * Allocate a new jail decriptor, not yet associated with a prison.
116 * Return the file pointer (with a reference held) and the descriptor
117 * number.
118 */
119 int
jaildesc_alloc(struct thread * td,struct file ** fpp,int * fdp,int owning)120 jaildesc_alloc(struct thread *td, struct file **fpp, int *fdp, int owning)
121 {
122 struct file *fp;
123 struct jaildesc *jd;
124 int error;
125 mode_t mode;
126
127 if (owning) {
128 error = priv_check(td, PRIV_JAIL_REMOVE);
129 if (error != 0)
130 return (error);
131 mode = S_ISTXT;
132 } else
133 mode = 0;
134 jd = malloc(sizeof(*jd), M_JAILDESC, M_WAITOK | M_ZERO);
135 error = falloc_caps(td, &fp, fdp, 0, NULL);
136 if (error != 0) {
137 free(jd, M_JAILDESC);
138 return (error);
139 }
140 finit(fp, priv_check_cred(fp->f_cred, PRIV_JAIL_SET) == 0
141 ? FREAD | FWRITE : FREAD, DTYPE_JAILDESC, jd, &jaildesc_ops);
142 JAILDESC_LOCK_INIT(jd);
143 jd->jd_uid = fp->f_cred->cr_uid;
144 jd->jd_gid = fp->f_cred->cr_gid;
145 jd->jd_mode = S_IFREG | S_IRUSR | S_IRGRP | S_IROTH | mode
146 | (priv_check(td, PRIV_JAIL_SET) == 0 ? S_IWUSR | S_IXUSR : 0)
147 | (priv_check(td, PRIV_JAIL_ATTACH) == 0 ? S_IXUSR : 0);
148 *fpp = fp;
149 return (0);
150 }
151
152 /*
153 * Assocate a jail descriptor with its prison.
154 */
155 void
jaildesc_set_prison(struct file * fp,struct prison * pr)156 jaildesc_set_prison(struct file *fp, struct prison *pr)
157 {
158 struct jaildesc *jd;
159
160 mtx_assert(&pr->pr_mtx, MA_OWNED);
161 jd = fp->f_data;
162 JAILDESC_LOCK(jd);
163 jd->jd_prison = pr;
164 LIST_INSERT_HEAD(&pr->pr_descs, jd, jd_list);
165 prison_hold(pr);
166 JAILDESC_UNLOCK(jd);
167 }
168
169 /*
170 * Detach the all jail descriptors from a prison.
171 */
172 void
jaildesc_prison_cleanup(struct prison * pr)173 jaildesc_prison_cleanup(struct prison *pr)
174 {
175 struct jaildesc *jd;
176
177 mtx_assert(&pr->pr_mtx, MA_OWNED);
178 while ((jd = LIST_FIRST(&pr->pr_descs))) {
179 JAILDESC_LOCK(jd);
180 LIST_REMOVE(jd, jd_list);
181 jd->jd_prison = NULL;
182 JAILDESC_UNLOCK(jd);
183 prison_free(pr);
184 }
185 }
186
187 static int
jaildesc_close(struct file * fp,struct thread * td)188 jaildesc_close(struct file *fp, struct thread *td)
189 {
190 struct jaildesc *jd;
191 struct prison *pr;
192
193 jd = fp->f_data;
194 fp->f_data = NULL;
195 if (jd != NULL) {
196 JAILDESC_LOCK(jd);
197 pr = jd->jd_prison;
198 if (pr != NULL) {
199 /*
200 * Free or remove the associated prison.
201 * This requires a second check after re-
202 * ordering locks. This jaildesc can remain
203 * unlocked once we have a prison reference,
204 * because that prison is the only place that
205 * still points back to it.
206 */
207 prison_hold(pr);
208 JAILDESC_UNLOCK(jd);
209 if (jd->jd_mode & S_ISTXT) {
210 sx_xlock(&allprison_lock);
211 prison_lock(pr);
212 if (jd->jd_prison != NULL) {
213 /*
214 * Unlink the prison, but don't free
215 * it; that will be done as part of
216 * of prison_remove.
217 */
218 LIST_REMOVE(jd, jd_list);
219 prison_remove(pr);
220 } else {
221 prison_unlock(pr);
222 sx_xunlock(&allprison_lock);
223 }
224 } else {
225 prison_lock(pr);
226 if (jd->jd_prison != NULL) {
227 LIST_REMOVE(jd, jd_list);
228 prison_free(pr);
229 }
230 prison_unlock(pr);
231 }
232 prison_free(pr);
233 }
234 JAILDESC_LOCK_DESTROY(jd);
235 free(jd, M_JAILDESC);
236 }
237 return (0);
238 }
239
240 static int
jaildesc_stat(struct file * fp,struct stat * sb,struct ucred * active_cred)241 jaildesc_stat(struct file *fp, struct stat *sb, struct ucred *active_cred)
242 {
243 struct jaildesc *jd;
244
245 bzero(sb, sizeof(struct stat));
246 jd = fp->f_data;
247 JAILDESC_LOCK(jd);
248 if (jd->jd_prison != NULL) {
249 sb->st_ino = jd->jd_prison ? jd->jd_prison->pr_id : 0;
250 sb->st_uid = jd->jd_uid;
251 sb->st_gid = jd->jd_gid;
252 sb->st_mode = jd->jd_mode;
253 } else
254 sb->st_mode = S_IFREG;
255 JAILDESC_UNLOCK(jd);
256 return (0);
257 }
258
259 static int
jaildesc_chmod(struct file * fp,mode_t mode,struct ucred * active_cred,struct thread * td)260 jaildesc_chmod(struct file *fp, mode_t mode, struct ucred *active_cred,
261 struct thread *td)
262 {
263 struct jaildesc *jd;
264 int error;
265
266 /* Reject permissions that the creator doesn't have. */
267 if (((mode & (S_IWUSR | S_IWGRP | S_IWOTH))
268 && priv_check_cred(fp->f_cred, PRIV_JAIL_SET) != 0)
269 || ((mode & (S_IXUSR | S_IXGRP | S_IXOTH))
270 && priv_check_cred(fp->f_cred, PRIV_JAIL_ATTACH) != 0
271 && priv_check_cred(fp->f_cred, PRIV_JAIL_SET) != 0)
272 || ((mode & S_ISTXT)
273 && priv_check_cred(fp->f_cred, PRIV_JAIL_REMOVE) != 0))
274 return (EPERM);
275 if (mode & (S_ISUID | S_ISGID))
276 return (EINVAL);
277 jd = fp->f_data;
278 JAILDESC_LOCK(jd);
279 error = vaccess(VREG, jd->jd_mode, jd->jd_uid, jd->jd_gid, VADMIN,
280 active_cred);
281 if (error == 0)
282 jd->jd_mode = S_IFREG | (mode & ALLPERMS);
283 JAILDESC_UNLOCK(jd);
284 return (error);
285 }
286
287 static int
jaildesc_chown(struct file * fp,uid_t uid,gid_t gid,struct ucred * active_cred,struct thread * td)288 jaildesc_chown(struct file *fp, uid_t uid, gid_t gid, struct ucred *active_cred,
289 struct thread *td)
290 {
291 struct jaildesc *jd;
292 int error;
293
294 error = 0;
295 jd = fp->f_data;
296 JAILDESC_LOCK(jd);
297 if (uid == (uid_t)-1)
298 uid = jd->jd_uid;
299 if (gid == (gid_t)-1)
300 gid = jd->jd_gid;
301 if ((uid != jd->jd_uid && uid != active_cred->cr_uid) ||
302 (gid != jd->jd_gid && !groupmember(gid, active_cred)))
303 error = priv_check_cred(active_cred, PRIV_VFS_CHOWN);
304 if (error == 0) {
305 jd->jd_uid = uid;
306 jd->jd_gid = gid;
307 }
308 JAILDESC_UNLOCK(jd);
309 return (error);
310 }
311
312 static int
jaildesc_fill_kinfo(struct file * fp,struct kinfo_file * kif,struct filedesc * fdp)313 jaildesc_fill_kinfo(struct file *fp, struct kinfo_file *kif,
314 struct filedesc *fdp)
315 {
316 return (EINVAL);
317 }
318
319 static int
jaildesc_cmp(struct file * fp1,struct file * fp2,struct thread * td)320 jaildesc_cmp(struct file *fp1, struct file *fp2, struct thread *td)
321 {
322 struct jaildesc *jd1, *jd2;
323 int jid1, jid2;
324
325 if (fp2->f_type != DTYPE_JAILDESC)
326 return (3);
327 jd1 = fp1->f_data;
328 JAILDESC_LOCK(jd1);
329 jid1 = jd1->jd_prison ? (uintptr_t)jd1->jd_prison->pr_id : 0;
330 JAILDESC_UNLOCK(jd1);
331 jd2 = fp2->f_data;
332 JAILDESC_LOCK(jd2);
333 jid2 = jd2->jd_prison ? (uintptr_t)jd2->jd_prison->pr_id : 0;
334 JAILDESC_UNLOCK(jd2);
335 return (kcmp_cmp(jid1, jid2));
336 }
337