1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 * 4 * Copyright (c) 1999 Marcel Moolenaar 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/cdefs.h> 30 __FBSDID("$FreeBSD$"); 31 32 #include <sys/param.h> 33 #include <sys/kernel.h> 34 #include <sys/sdt.h> 35 #include <sys/systm.h> 36 #include <sys/sysctl.h> 37 #include <sys/proc.h> 38 #include <sys/malloc.h> 39 #include <sys/mount.h> 40 #include <sys/jail.h> 41 #include <sys/lock.h> 42 #include <sys/sx.h> 43 44 #include <compat/linux/linux_mib.h> 45 #include <compat/linux/linux_misc.h> 46 47 struct linux_prison { 48 char pr_osname[LINUX_MAX_UTSNAME]; 49 char pr_osrelease[LINUX_MAX_UTSNAME]; 50 int pr_oss_version; 51 int pr_osrel; 52 }; 53 54 static struct linux_prison lprison0 = { 55 .pr_osname = "Linux", 56 .pr_osrelease = LINUX_VERSION_STR, 57 .pr_oss_version = 0x030600, 58 .pr_osrel = LINUX_VERSION_CODE 59 }; 60 61 static unsigned linux_osd_jail_slot; 62 63 SYSCTL_NODE(_compat, OID_AUTO, linux, CTLFLAG_RW, 0, "Linux mode"); 64 65 int linux_preserve_vstatus = 0; 66 SYSCTL_INT(_compat_linux, OID_AUTO, preserve_vstatus, CTLFLAG_RWTUN, 67 &linux_preserve_vstatus, 0, "Preserve VSTATUS termios(4) flag"); 68 69 static int linux_set_osname(struct thread *td, char *osname); 70 static int linux_set_osrelease(struct thread *td, char *osrelease); 71 static int linux_set_oss_version(struct thread *td, int oss_version); 72 73 static int 74 linux_sysctl_osname(SYSCTL_HANDLER_ARGS) 75 { 76 char osname[LINUX_MAX_UTSNAME]; 77 int error; 78 79 linux_get_osname(req->td, osname); 80 error = sysctl_handle_string(oidp, osname, LINUX_MAX_UTSNAME, req); 81 if (error != 0 || req->newptr == NULL) 82 return (error); 83 error = linux_set_osname(req->td, osname); 84 85 return (error); 86 } 87 88 SYSCTL_PROC(_compat_linux, OID_AUTO, osname, 89 CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_PRISON | CTLFLAG_MPSAFE, 90 0, 0, linux_sysctl_osname, "A", 91 "Linux kernel OS name"); 92 93 static int 94 linux_sysctl_osrelease(SYSCTL_HANDLER_ARGS) 95 { 96 char osrelease[LINUX_MAX_UTSNAME]; 97 int error; 98 99 linux_get_osrelease(req->td, osrelease); 100 error = sysctl_handle_string(oidp, osrelease, LINUX_MAX_UTSNAME, req); 101 if (error != 0 || req->newptr == NULL) 102 return (error); 103 error = linux_set_osrelease(req->td, osrelease); 104 105 return (error); 106 } 107 108 SYSCTL_PROC(_compat_linux, OID_AUTO, osrelease, 109 CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_PRISON | CTLFLAG_MPSAFE, 110 0, 0, linux_sysctl_osrelease, "A", 111 "Linux kernel OS release"); 112 113 static int 114 linux_sysctl_oss_version(SYSCTL_HANDLER_ARGS) 115 { 116 int oss_version; 117 int error; 118 119 oss_version = linux_get_oss_version(req->td); 120 error = sysctl_handle_int(oidp, &oss_version, 0, req); 121 if (error != 0 || req->newptr == NULL) 122 return (error); 123 error = linux_set_oss_version(req->td, oss_version); 124 125 return (error); 126 } 127 128 SYSCTL_PROC(_compat_linux, OID_AUTO, oss_version, 129 CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_PRISON | CTLFLAG_MPSAFE, 130 0, 0, linux_sysctl_oss_version, "I", 131 "Linux OSS version"); 132 133 /* 134 * Map the osrelease into integer 135 */ 136 static int 137 linux_map_osrel(char *osrelease, int *osrel) 138 { 139 char *sep, *eosrelease; 140 int len, v0, v1, v2, v; 141 142 len = strlen(osrelease); 143 eosrelease = osrelease + len; 144 v0 = strtol(osrelease, &sep, 10); 145 if (osrelease == sep || sep + 1 >= eosrelease || *sep != '.') 146 return (EINVAL); 147 osrelease = sep + 1; 148 v1 = strtol(osrelease, &sep, 10); 149 if (osrelease == sep || sep + 1 >= eosrelease || *sep != '.') 150 return (EINVAL); 151 osrelease = sep + 1; 152 v2 = strtol(osrelease, &sep, 10); 153 if (osrelease == sep || 154 (sep != eosrelease && (sep + 1 >= eosrelease || *sep != '-'))) 155 return (EINVAL); 156 157 v = LINUX_KERNVER(v0, v1, v2); 158 if (v < LINUX_KERNVER(1, 0, 0)) 159 return (EINVAL); 160 161 if (osrel != NULL) 162 *osrel = v; 163 164 return (0); 165 } 166 167 /* 168 * Find a prison with Linux info. 169 * Return the Linux info and the (locked) prison. 170 */ 171 static struct linux_prison * 172 linux_find_prison(struct prison *spr, struct prison **prp) 173 { 174 struct prison *pr; 175 struct linux_prison *lpr; 176 177 for (pr = spr;; pr = pr->pr_parent) { 178 mtx_lock(&pr->pr_mtx); 179 lpr = (pr == &prison0) 180 ? &lprison0 181 : osd_jail_get(pr, linux_osd_jail_slot); 182 if (lpr != NULL) 183 break; 184 mtx_unlock(&pr->pr_mtx); 185 } 186 *prp = pr; 187 188 return (lpr); 189 } 190 191 /* 192 * Ensure a prison has its own Linux info. If lprp is non-null, point it to 193 * the Linux info and lock the prison. 194 */ 195 static void 196 linux_alloc_prison(struct prison *pr, struct linux_prison **lprp) 197 { 198 struct prison *ppr; 199 struct linux_prison *lpr, *nlpr; 200 void **rsv; 201 202 /* If this prison already has Linux info, return that. */ 203 lpr = linux_find_prison(pr, &ppr); 204 if (ppr == pr) 205 goto done; 206 /* 207 * Allocate a new info record. Then check again, in case something 208 * changed during the allocation. 209 */ 210 mtx_unlock(&ppr->pr_mtx); 211 nlpr = malloc(sizeof(struct linux_prison), M_PRISON, M_WAITOK); 212 rsv = osd_reserve(linux_osd_jail_slot); 213 lpr = linux_find_prison(pr, &ppr); 214 if (ppr == pr) { 215 free(nlpr, M_PRISON); 216 osd_free_reserved(rsv); 217 goto done; 218 } 219 /* Inherit the initial values from the ancestor. */ 220 mtx_lock(&pr->pr_mtx); 221 (void)osd_jail_set_reserved(pr, linux_osd_jail_slot, rsv, nlpr); 222 bcopy(lpr, nlpr, sizeof(*lpr)); 223 lpr = nlpr; 224 mtx_unlock(&ppr->pr_mtx); 225 done: 226 if (lprp != NULL) 227 *lprp = lpr; 228 else 229 mtx_unlock(&pr->pr_mtx); 230 } 231 232 /* 233 * Jail OSD methods for Linux prison data. 234 */ 235 static int 236 linux_prison_create(void *obj, void *data) 237 { 238 struct prison *pr = obj; 239 struct vfsoptlist *opts = data; 240 int jsys; 241 242 if (vfs_copyopt(opts, "linux", &jsys, sizeof(jsys)) == 0 && 243 jsys == JAIL_SYS_INHERIT) 244 return (0); 245 /* 246 * Inherit a prison's initial values from its parent 247 * (different from JAIL_SYS_INHERIT which also inherits changes). 248 */ 249 linux_alloc_prison(pr, NULL); 250 return (0); 251 } 252 253 static int 254 linux_prison_check(void *obj __unused, void *data) 255 { 256 struct vfsoptlist *opts = data; 257 char *osname, *osrelease; 258 int error, jsys, len, oss_version; 259 260 /* Check that the parameters are correct. */ 261 error = vfs_copyopt(opts, "linux", &jsys, sizeof(jsys)); 262 if (error != ENOENT) { 263 if (error != 0) 264 return (error); 265 if (jsys != JAIL_SYS_NEW && jsys != JAIL_SYS_INHERIT) 266 return (EINVAL); 267 } 268 error = vfs_getopt(opts, "linux.osname", (void **)&osname, &len); 269 if (error != ENOENT) { 270 if (error != 0) 271 return (error); 272 if (len == 0 || osname[len - 1] != '\0') 273 return (EINVAL); 274 if (len > LINUX_MAX_UTSNAME) { 275 vfs_opterror(opts, "linux.osname too long"); 276 return (ENAMETOOLONG); 277 } 278 } 279 error = vfs_getopt(opts, "linux.osrelease", (void **)&osrelease, &len); 280 if (error != ENOENT) { 281 if (error != 0) 282 return (error); 283 if (len == 0 || osrelease[len - 1] != '\0') 284 return (EINVAL); 285 if (len > LINUX_MAX_UTSNAME) { 286 vfs_opterror(opts, "linux.osrelease too long"); 287 return (ENAMETOOLONG); 288 } 289 error = linux_map_osrel(osrelease, NULL); 290 if (error != 0) { 291 vfs_opterror(opts, "linux.osrelease format error"); 292 return (error); 293 } 294 } 295 error = vfs_copyopt(opts, "linux.oss_version", &oss_version, 296 sizeof(oss_version)); 297 298 if (error == ENOENT) 299 error = 0; 300 return (error); 301 } 302 303 static int 304 linux_prison_set(void *obj, void *data) 305 { 306 struct linux_prison *lpr; 307 struct prison *pr = obj; 308 struct vfsoptlist *opts = data; 309 char *osname, *osrelease; 310 int error, gotversion, jsys, len, oss_version; 311 312 /* Set the parameters, which should be correct. */ 313 error = vfs_copyopt(opts, "linux", &jsys, sizeof(jsys)); 314 if (error == ENOENT) 315 jsys = -1; 316 error = vfs_getopt(opts, "linux.osname", (void **)&osname, &len); 317 if (error == ENOENT) 318 osname = NULL; 319 else 320 jsys = JAIL_SYS_NEW; 321 error = vfs_getopt(opts, "linux.osrelease", (void **)&osrelease, &len); 322 if (error == ENOENT) 323 osrelease = NULL; 324 else 325 jsys = JAIL_SYS_NEW; 326 error = vfs_copyopt(opts, "linux.oss_version", &oss_version, 327 sizeof(oss_version)); 328 if (error == ENOENT) 329 gotversion = 0; 330 else { 331 gotversion = 1; 332 jsys = JAIL_SYS_NEW; 333 } 334 switch (jsys) { 335 case JAIL_SYS_INHERIT: 336 /* "linux=inherit": inherit the parent's Linux info. */ 337 mtx_lock(&pr->pr_mtx); 338 osd_jail_del(pr, linux_osd_jail_slot); 339 mtx_unlock(&pr->pr_mtx); 340 break; 341 case JAIL_SYS_NEW: 342 /* 343 * "linux=new" or "linux.*": 344 * the prison gets its own Linux info. 345 */ 346 linux_alloc_prison(pr, &lpr); 347 if (osrelease) { 348 (void)linux_map_osrel(osrelease, &lpr->pr_osrel); 349 strlcpy(lpr->pr_osrelease, osrelease, 350 LINUX_MAX_UTSNAME); 351 } 352 if (osname) 353 strlcpy(lpr->pr_osname, osname, LINUX_MAX_UTSNAME); 354 if (gotversion) 355 lpr->pr_oss_version = oss_version; 356 mtx_unlock(&pr->pr_mtx); 357 } 358 359 return (0); 360 } 361 362 SYSCTL_JAIL_PARAM_SYS_NODE(linux, CTLFLAG_RW, "Jail Linux parameters"); 363 SYSCTL_JAIL_PARAM_STRING(_linux, osname, CTLFLAG_RW, LINUX_MAX_UTSNAME, 364 "Jail Linux kernel OS name"); 365 SYSCTL_JAIL_PARAM_STRING(_linux, osrelease, CTLFLAG_RW, LINUX_MAX_UTSNAME, 366 "Jail Linux kernel OS release"); 367 SYSCTL_JAIL_PARAM(_linux, oss_version, CTLTYPE_INT | CTLFLAG_RW, 368 "I", "Jail Linux OSS version"); 369 370 static int 371 linux_prison_get(void *obj, void *data) 372 { 373 struct linux_prison *lpr; 374 struct prison *ppr; 375 struct prison *pr = obj; 376 struct vfsoptlist *opts = data; 377 int error, i; 378 379 static int version0; 380 381 /* See if this prison is the one with the Linux info. */ 382 lpr = linux_find_prison(pr, &ppr); 383 i = (ppr == pr) ? JAIL_SYS_NEW : JAIL_SYS_INHERIT; 384 error = vfs_setopt(opts, "linux", &i, sizeof(i)); 385 if (error != 0 && error != ENOENT) 386 goto done; 387 if (i) { 388 error = vfs_setopts(opts, "linux.osname", lpr->pr_osname); 389 if (error != 0 && error != ENOENT) 390 goto done; 391 error = vfs_setopts(opts, "linux.osrelease", lpr->pr_osrelease); 392 if (error != 0 && error != ENOENT) 393 goto done; 394 error = vfs_setopt(opts, "linux.oss_version", 395 &lpr->pr_oss_version, sizeof(lpr->pr_oss_version)); 396 if (error != 0 && error != ENOENT) 397 goto done; 398 } else { 399 /* 400 * If this prison is inheriting its Linux info, report 401 * empty/zero parameters. 402 */ 403 error = vfs_setopts(opts, "linux.osname", ""); 404 if (error != 0 && error != ENOENT) 405 goto done; 406 error = vfs_setopts(opts, "linux.osrelease", ""); 407 if (error != 0 && error != ENOENT) 408 goto done; 409 error = vfs_setopt(opts, "linux.oss_version", &version0, 410 sizeof(lpr->pr_oss_version)); 411 if (error != 0 && error != ENOENT) 412 goto done; 413 } 414 error = 0; 415 416 done: 417 mtx_unlock(&ppr->pr_mtx); 418 419 return (error); 420 } 421 422 static void 423 linux_prison_destructor(void *data) 424 { 425 426 free(data, M_PRISON); 427 } 428 429 void 430 linux_osd_jail_register(void) 431 { 432 struct prison *pr; 433 osd_method_t methods[PR_MAXMETHOD] = { 434 [PR_METHOD_CREATE] = linux_prison_create, 435 [PR_METHOD_GET] = linux_prison_get, 436 [PR_METHOD_SET] = linux_prison_set, 437 [PR_METHOD_CHECK] = linux_prison_check 438 }; 439 440 linux_osd_jail_slot = 441 osd_jail_register(linux_prison_destructor, methods); 442 /* Copy the system Linux info to any current prisons. */ 443 sx_slock(&allprison_lock); 444 TAILQ_FOREACH(pr, &allprison, pr_list) 445 linux_alloc_prison(pr, NULL); 446 sx_sunlock(&allprison_lock); 447 } 448 449 void 450 linux_osd_jail_deregister(void) 451 { 452 453 osd_jail_deregister(linux_osd_jail_slot); 454 } 455 456 void 457 linux_get_osname(struct thread *td, char *dst) 458 { 459 struct prison *pr; 460 struct linux_prison *lpr; 461 462 lpr = linux_find_prison(td->td_ucred->cr_prison, &pr); 463 bcopy(lpr->pr_osname, dst, LINUX_MAX_UTSNAME); 464 mtx_unlock(&pr->pr_mtx); 465 } 466 467 static int 468 linux_set_osname(struct thread *td, char *osname) 469 { 470 struct prison *pr; 471 struct linux_prison *lpr; 472 473 lpr = linux_find_prison(td->td_ucred->cr_prison, &pr); 474 strlcpy(lpr->pr_osname, osname, LINUX_MAX_UTSNAME); 475 mtx_unlock(&pr->pr_mtx); 476 477 return (0); 478 } 479 480 void 481 linux_get_osrelease(struct thread *td, char *dst) 482 { 483 struct prison *pr; 484 struct linux_prison *lpr; 485 486 lpr = linux_find_prison(td->td_ucred->cr_prison, &pr); 487 bcopy(lpr->pr_osrelease, dst, LINUX_MAX_UTSNAME); 488 mtx_unlock(&pr->pr_mtx); 489 } 490 491 int 492 linux_kernver(struct thread *td) 493 { 494 struct prison *pr; 495 struct linux_prison *lpr; 496 int osrel; 497 498 lpr = linux_find_prison(td->td_ucred->cr_prison, &pr); 499 osrel = lpr->pr_osrel; 500 mtx_unlock(&pr->pr_mtx); 501 502 return (osrel); 503 } 504 505 static int 506 linux_set_osrelease(struct thread *td, char *osrelease) 507 { 508 struct prison *pr; 509 struct linux_prison *lpr; 510 int error; 511 512 lpr = linux_find_prison(td->td_ucred->cr_prison, &pr); 513 error = linux_map_osrel(osrelease, &lpr->pr_osrel); 514 if (error == 0) 515 strlcpy(lpr->pr_osrelease, osrelease, LINUX_MAX_UTSNAME); 516 mtx_unlock(&pr->pr_mtx); 517 518 return (error); 519 } 520 521 int 522 linux_get_oss_version(struct thread *td) 523 { 524 struct prison *pr; 525 struct linux_prison *lpr; 526 int version; 527 528 lpr = linux_find_prison(td->td_ucred->cr_prison, &pr); 529 version = lpr->pr_oss_version; 530 mtx_unlock(&pr->pr_mtx); 531 532 return (version); 533 } 534 535 static int 536 linux_set_oss_version(struct thread *td, int oss_version) 537 { 538 struct prison *pr; 539 struct linux_prison *lpr; 540 541 lpr = linux_find_prison(td->td_ucred->cr_prison, &pr); 542 lpr->pr_oss_version = oss_version; 543 mtx_unlock(&pr->pr_mtx); 544 545 return (0); 546 } 547