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 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 = EINVAL; 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 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 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 all the jail descriptors from a prison. 171 */ 172 void 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 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 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 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 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 313 jaildesc_fill_kinfo(struct file *fp, struct kinfo_file *kif, 314 struct filedesc *fdp) 315 { 316 return (EINVAL); 317 } 318 319 static int 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