1 /*- 2 * Copyright (c) 2001 The FreeBSD Project 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 */ 26 27 #include <sys/cdefs.h> 28 __FBSDID("$FreeBSD$"); 29 30 #include "opt_compat.h" 31 32 #include <sys/fcntl.h> 33 #include <sys/param.h> 34 #include <sys/kernel.h> 35 #include <sys/lock.h> 36 #include <sys/malloc.h> 37 #include <sys/mutex.h> 38 #include <sys/priv.h> 39 #include <sys/proc.h> 40 #include <sys/sdt.h> 41 #include <sys/syscallsubr.h> 42 #include <sys/sysproto.h> 43 #include <sys/systm.h> 44 45 #ifdef COMPAT_LINUX32 46 #include <machine/../linux32/linux.h> 47 #include <machine/../linux32/linux32_proto.h> 48 #else 49 #include <machine/../linux/linux.h> 50 #include <machine/../linux/linux_proto.h> 51 #endif 52 53 #include <compat/linux/linux_dtrace.h> 54 #include <compat/linux/linux_util.h> 55 56 /* DTrace init */ 57 LIN_SDT_PROVIDER_DECLARE(LINUX_DTRACE); 58 59 /** 60 * DTrace probes in this module. 61 */ 62 LIN_SDT_PROBE_DEFINE3(uid16, linux_chown16, entry, "char *", "l_uid16_t", 63 "l_gid16_t"); 64 LIN_SDT_PROBE_DEFINE1(uid16, linux_chown16, conv_path, "char *"); 65 LIN_SDT_PROBE_DEFINE1(uid16, linux_chown16, return, "int"); 66 LIN_SDT_PROBE_DEFINE3(uid16, linux_lchown16, entry, "char *", "l_uid16_t", 67 "l_gid16_t"); 68 LIN_SDT_PROBE_DEFINE1(uid16, linux_lchown16, conv_path, "char *"); 69 LIN_SDT_PROBE_DEFINE1(uid16, linux_lchown16, return, "int"); 70 LIN_SDT_PROBE_DEFINE2(uid16, linux_setgroups16, entry, "l_uint", "l_gid16_t *"); 71 LIN_SDT_PROBE_DEFINE1(uid16, linux_setgroups16, copyin_error, "int"); 72 LIN_SDT_PROBE_DEFINE1(uid16, linux_setgroups16, priv_check_cred_error, "int"); 73 LIN_SDT_PROBE_DEFINE1(uid16, linux_setgroups16, return, "int"); 74 LIN_SDT_PROBE_DEFINE2(uid16, linux_getgroups16, entry, "l_uint", "l_gid16_t *"); 75 LIN_SDT_PROBE_DEFINE1(uid16, linux_getgroups16, copyout_error, "int"); 76 LIN_SDT_PROBE_DEFINE1(uid16, linux_getgroups16, return, "int"); 77 LIN_SDT_PROBE_DEFINE0(uid16, linux_getgid16, entry); 78 LIN_SDT_PROBE_DEFINE1(uid16, linux_getgid16, return, "int"); 79 LIN_SDT_PROBE_DEFINE0(uid16, linux_getuid16, entry); 80 LIN_SDT_PROBE_DEFINE1(uid16, linux_getuid16, return, "int"); 81 LIN_SDT_PROBE_DEFINE0(uid16, linux_getegid16, entry); 82 LIN_SDT_PROBE_DEFINE1(uid16, linux_getegid16, return, "int"); 83 LIN_SDT_PROBE_DEFINE0(uid16, linux_geteuid16, entry); 84 LIN_SDT_PROBE_DEFINE1(uid16, linux_geteuid16, return, "int"); 85 LIN_SDT_PROBE_DEFINE1(uid16, linux_setgid16, entry, "l_gid16_t"); 86 LIN_SDT_PROBE_DEFINE1(uid16, linux_setgid16, return, "int"); 87 LIN_SDT_PROBE_DEFINE1(uid16, linux_setuid16, entry, "l_uid16_t"); 88 LIN_SDT_PROBE_DEFINE1(uid16, linux_setuid16, return, "int"); 89 LIN_SDT_PROBE_DEFINE2(uid16, linux_setregid16, entry, "l_gid16_t", "l_gid16_t"); 90 LIN_SDT_PROBE_DEFINE1(uid16, linux_setregid16, return, "int"); 91 LIN_SDT_PROBE_DEFINE2(uid16, linux_setreuid16, entry, "l_uid16_t", "l_uid16_t"); 92 LIN_SDT_PROBE_DEFINE1(uid16, linux_setreuid16, return, "int"); 93 LIN_SDT_PROBE_DEFINE3(uid16, linux_setresgid16, entry, "l_gid16_t", "l_gid16_t", 94 "l_gid16_t"); 95 LIN_SDT_PROBE_DEFINE1(uid16, linux_setresgid16, return, "int"); 96 LIN_SDT_PROBE_DEFINE3(uid16, linux_setresuid16, entry, "l_uid16_t", "l_uid16_t", 97 "l_uid16_t"); 98 LIN_SDT_PROBE_DEFINE1(uid16, linux_setresuid16, return, "int"); 99 100 DUMMY(setfsuid16); 101 DUMMY(setfsgid16); 102 DUMMY(getresuid16); 103 DUMMY(getresgid16); 104 105 #define CAST_NOCHG(x) ((x == 0xFFFF) ? -1 : x) 106 107 int 108 linux_chown16(struct thread *td, struct linux_chown16_args *args) 109 { 110 char *path; 111 int error; 112 113 LCONVPATHEXIST(td, args->path, &path); 114 115 /* 116 * The DTrace probes have to be after the LCONVPATHEXIST, as 117 * LCONVPATHEXIST may return on its own and we do not want to 118 * have a stray entry without the corresponding return. 119 */ 120 LIN_SDT_PROBE3(uid16, linux_chown16, entry, args->path, args->uid, 121 args->gid); 122 LIN_SDT_PROBE1(uid16, linux_chown16, conv_path, path); 123 124 error = kern_fchownat(td, AT_FDCWD, path, UIO_SYSSPACE, 125 CAST_NOCHG(args->uid), CAST_NOCHG(args->gid), 0); 126 LFREEPATH(path); 127 128 LIN_SDT_PROBE1(uid16, linux_chown16, return, error); 129 return (error); 130 } 131 132 int 133 linux_lchown16(struct thread *td, struct linux_lchown16_args *args) 134 { 135 char *path; 136 int error; 137 138 LCONVPATHEXIST(td, args->path, &path); 139 140 /* 141 * The DTrace probes have to be after the LCONVPATHEXIST, as 142 * LCONVPATHEXIST may return on its own and we do not want to 143 * have a stray entry without the corresponding return. 144 */ 145 LIN_SDT_PROBE3(uid16, linux_lchown16, entry, args->path, args->uid, 146 args->gid); 147 LIN_SDT_PROBE1(uid16, linux_lchown16, conv_path, path); 148 149 error = kern_fchownat(td, AT_FDCWD, path, UIO_SYSSPACE, 150 CAST_NOCHG(args->uid), CAST_NOCHG(args->gid), AT_SYMLINK_NOFOLLOW); 151 LFREEPATH(path); 152 153 LIN_SDT_PROBE1(uid16, linux_lchown16, return, error); 154 return (error); 155 } 156 157 int 158 linux_setgroups16(struct thread *td, struct linux_setgroups16_args *args) 159 { 160 struct ucred *newcred, *oldcred; 161 l_gid16_t *linux_gidset; 162 gid_t *bsd_gidset; 163 int ngrp, error; 164 struct proc *p; 165 166 LIN_SDT_PROBE2(uid16, linux_setgroups16, entry, args->gidsetsize, 167 args->gidset); 168 169 ngrp = args->gidsetsize; 170 if (ngrp < 0 || ngrp >= ngroups_max + 1) { 171 LIN_SDT_PROBE1(uid16, linux_setgroups16, return, EINVAL); 172 return (EINVAL); 173 } 174 linux_gidset = malloc(ngrp * sizeof(*linux_gidset), M_LINUX, M_WAITOK); 175 error = copyin(args->gidset, linux_gidset, ngrp * sizeof(l_gid16_t)); 176 if (error) { 177 LIN_SDT_PROBE1(uid16, linux_setgroups16, copyin_error, error); 178 LIN_SDT_PROBE1(uid16, linux_setgroups16, return, error); 179 free(linux_gidset, M_LINUX); 180 return (error); 181 } 182 newcred = crget(); 183 p = td->td_proc; 184 PROC_LOCK(p); 185 oldcred = crcopysafe(p, newcred); 186 187 /* 188 * cr_groups[0] holds egid. Setting the whole set from 189 * the supplied set will cause egid to be changed too. 190 * Keep cr_groups[0] unchanged to prevent that. 191 */ 192 193 if ((error = priv_check_cred(oldcred, PRIV_CRED_SETGROUPS, 0)) != 0) { 194 PROC_UNLOCK(p); 195 crfree(newcred); 196 197 LIN_SDT_PROBE1(uid16, linux_setgroups16, priv_check_cred_error, 198 error); 199 goto out; 200 } 201 202 if (ngrp > 0) { 203 newcred->cr_ngroups = ngrp + 1; 204 205 bsd_gidset = newcred->cr_groups; 206 ngrp--; 207 while (ngrp >= 0) { 208 bsd_gidset[ngrp + 1] = linux_gidset[ngrp]; 209 ngrp--; 210 } 211 } 212 else 213 newcred->cr_ngroups = 1; 214 215 setsugid(td->td_proc); 216 proc_set_cred(p, newcred); 217 PROC_UNLOCK(p); 218 crfree(oldcred); 219 error = 0; 220 out: 221 free(linux_gidset, M_LINUX); 222 223 LIN_SDT_PROBE1(uid16, linux_setgroups16, return, error); 224 return (error); 225 } 226 227 int 228 linux_getgroups16(struct thread *td, struct linux_getgroups16_args *args) 229 { 230 struct ucred *cred; 231 l_gid16_t *linux_gidset; 232 gid_t *bsd_gidset; 233 int bsd_gidsetsz, ngrp, error; 234 235 LIN_SDT_PROBE2(uid16, linux_getgroups16, entry, args->gidsetsize, 236 args->gidset); 237 238 cred = td->td_ucred; 239 bsd_gidset = cred->cr_groups; 240 bsd_gidsetsz = cred->cr_ngroups - 1; 241 242 /* 243 * cr_groups[0] holds egid. Returning the whole set 244 * here will cause a duplicate. Exclude cr_groups[0] 245 * to prevent that. 246 */ 247 248 if ((ngrp = args->gidsetsize) == 0) { 249 td->td_retval[0] = bsd_gidsetsz; 250 251 LIN_SDT_PROBE1(uid16, linux_getgroups16, return, 0); 252 return (0); 253 } 254 255 if (ngrp < bsd_gidsetsz) { 256 LIN_SDT_PROBE1(uid16, linux_getgroups16, return, EINVAL); 257 return (EINVAL); 258 } 259 260 ngrp = 0; 261 linux_gidset = malloc(bsd_gidsetsz * sizeof(*linux_gidset), 262 M_LINUX, M_WAITOK); 263 while (ngrp < bsd_gidsetsz) { 264 linux_gidset[ngrp] = bsd_gidset[ngrp + 1]; 265 ngrp++; 266 } 267 268 error = copyout(linux_gidset, args->gidset, ngrp * sizeof(l_gid16_t)); 269 free(linux_gidset, M_LINUX); 270 if (error) { 271 LIN_SDT_PROBE1(uid16, linux_getgroups16, copyout_error, error); 272 LIN_SDT_PROBE1(uid16, linux_getgroups16, return, error); 273 return (error); 274 } 275 276 td->td_retval[0] = ngrp; 277 278 LIN_SDT_PROBE1(uid16, linux_getgroups16, return, 0); 279 return (0); 280 } 281 282 /* 283 * The FreeBSD native getgid(2) and getuid(2) also modify td->td_retval[1] 284 * when COMPAT_43 is defined. This clobbers registers that are assumed to 285 * be preserved. The following lightweight syscalls fixes this. See also 286 * linux_getpid(2), linux_getgid(2) and linux_getuid(2) in linux_misc.c 287 * 288 * linux_getgid16() - MP SAFE 289 * linux_getuid16() - MP SAFE 290 */ 291 292 int 293 linux_getgid16(struct thread *td, struct linux_getgid16_args *args) 294 { 295 296 LIN_SDT_PROBE0(uid16, linux_getgid16, entry); 297 298 td->td_retval[0] = td->td_ucred->cr_rgid; 299 300 LIN_SDT_PROBE1(uid16, linux_getgid16, return, 0); 301 return (0); 302 } 303 304 int 305 linux_getuid16(struct thread *td, struct linux_getuid16_args *args) 306 { 307 308 LIN_SDT_PROBE0(uid16, linux_getuid16, entry); 309 310 td->td_retval[0] = td->td_ucred->cr_ruid; 311 312 LIN_SDT_PROBE1(uid16, linux_getuid16, return, 0); 313 return (0); 314 } 315 316 int 317 linux_getegid16(struct thread *td, struct linux_getegid16_args *args) 318 { 319 struct getegid_args bsd; 320 int error; 321 322 LIN_SDT_PROBE0(uid16, linux_getegid16, entry); 323 324 error = sys_getegid(td, &bsd); 325 326 LIN_SDT_PROBE1(uid16, linux_getegid16, return, error); 327 return (error); 328 } 329 330 int 331 linux_geteuid16(struct thread *td, struct linux_geteuid16_args *args) 332 { 333 struct geteuid_args bsd; 334 int error; 335 336 LIN_SDT_PROBE0(uid16, linux_geteuid16, entry); 337 338 error = sys_geteuid(td, &bsd); 339 340 LIN_SDT_PROBE1(uid16, linux_geteuid16, return, error); 341 return (error); 342 } 343 344 int 345 linux_setgid16(struct thread *td, struct linux_setgid16_args *args) 346 { 347 struct setgid_args bsd; 348 int error; 349 350 LIN_SDT_PROBE1(uid16, linux_setgid16, entry, args->gid); 351 352 bsd.gid = args->gid; 353 error = sys_setgid(td, &bsd); 354 355 LIN_SDT_PROBE1(uid16, linux_setgid16, return, error); 356 return (error); 357 } 358 359 int 360 linux_setuid16(struct thread *td, struct linux_setuid16_args *args) 361 { 362 struct setuid_args bsd; 363 int error; 364 365 LIN_SDT_PROBE1(uid16, linux_setuid16, entry, args->uid); 366 367 bsd.uid = args->uid; 368 error = sys_setuid(td, &bsd); 369 370 LIN_SDT_PROBE1(uid16, linux_setuid16, return, error); 371 return (error); 372 } 373 374 int 375 linux_setregid16(struct thread *td, struct linux_setregid16_args *args) 376 { 377 struct setregid_args bsd; 378 int error; 379 380 LIN_SDT_PROBE2(uid16, linux_setregid16, entry, args->rgid, args->egid); 381 382 bsd.rgid = CAST_NOCHG(args->rgid); 383 bsd.egid = CAST_NOCHG(args->egid); 384 error = sys_setregid(td, &bsd); 385 386 LIN_SDT_PROBE1(uid16, linux_setregid16, return, error); 387 return (error); 388 } 389 390 int 391 linux_setreuid16(struct thread *td, struct linux_setreuid16_args *args) 392 { 393 struct setreuid_args bsd; 394 int error; 395 396 LIN_SDT_PROBE2(uid16, linux_setreuid16, entry, args->ruid, args->euid); 397 398 bsd.ruid = CAST_NOCHG(args->ruid); 399 bsd.euid = CAST_NOCHG(args->euid); 400 error = sys_setreuid(td, &bsd); 401 402 LIN_SDT_PROBE1(uid16, linux_setreuid16, return, error); 403 return (error); 404 } 405 406 int 407 linux_setresgid16(struct thread *td, struct linux_setresgid16_args *args) 408 { 409 struct setresgid_args bsd; 410 int error; 411 412 LIN_SDT_PROBE3(uid16, linux_setresgid16, entry, args->rgid, args->egid, 413 args->sgid); 414 415 bsd.rgid = CAST_NOCHG(args->rgid); 416 bsd.egid = CAST_NOCHG(args->egid); 417 bsd.sgid = CAST_NOCHG(args->sgid); 418 error = sys_setresgid(td, &bsd); 419 420 LIN_SDT_PROBE1(uid16, linux_setresgid16, return, error); 421 return (error); 422 } 423 424 int 425 linux_setresuid16(struct thread *td, struct linux_setresuid16_args *args) 426 { 427 struct setresuid_args bsd; 428 int error; 429 430 LIN_SDT_PROBE3(uid16, linux_setresuid16, entry, args->ruid, args->euid, 431 args->suid); 432 433 bsd.ruid = CAST_NOCHG(args->ruid); 434 bsd.euid = CAST_NOCHG(args->euid); 435 bsd.suid = CAST_NOCHG(args->suid); 436 error = sys_setresuid(td, &bsd); 437 438 LIN_SDT_PROBE1(uid16, linux_setresuid16, return, error); 439 return (error); 440 } 441