1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 #include <sys/param.h> 29 #include <sys/types.h> 30 #include <sys/sysmacros.h> 31 #include <sys/systm.h> 32 #include <sys/cred_impl.h> 33 #include <sys/errno.h> 34 #include <sys/klpd.h> 35 #include <sys/proc.h> 36 #include <sys/priv_impl.h> 37 #include <sys/policy.h> 38 #include <sys/ddi.h> 39 #include <sys/thread.h> 40 #include <c2/audit.h> 41 42 /* 43 * System call support for manipulating privileges. 44 * 45 * 46 * setppriv(2) - set process privilege set 47 * getppriv(2) - get process privilege set 48 * getprivimplinfo(2) - get process privilege implementation information 49 * setpflags(2) - set process (privilege) flags 50 * getpflags(2) - get process (privilege) flags 51 */ 52 53 /* 54 * setppriv (priv_op_t, priv_ptype_t, priv_set_t) 55 */ 56 static int 57 setppriv(priv_op_t op, priv_ptype_t type, priv_set_t *in_pset) 58 { 59 priv_set_t pset, *target; 60 cred_t *cr, *pcr; 61 proc_t *p; 62 boolean_t donocd = B_FALSE; 63 64 if (!PRIV_VALIDSET(type) || !PRIV_VALIDOP(op)) 65 return (set_errno(EINVAL)); 66 67 if (copyin(in_pset, &pset, sizeof (priv_set_t))) 68 return (set_errno(EFAULT)); 69 70 p = ttoproc(curthread); 71 cr = cralloc(); 72 mutex_enter(&p->p_crlock); 73 74 retry: 75 pcr = p->p_cred; 76 77 if (audit_active) 78 audit_setppriv(op, type, &pset, pcr); 79 80 /* 81 * Filter out unallowed request (bad op and bad type) 82 */ 83 switch (op) { 84 case PRIV_ON: 85 case PRIV_SET: 86 /* 87 * Turning on privileges; the limit set cannot grow, 88 * other sets can but only as long as they remain subsets 89 * of P. Only immediately after exec holds that P <= L. 90 */ 91 if (type == PRIV_LIMIT && 92 !priv_issubset(&pset, &CR_LPRIV(pcr))) { 93 mutex_exit(&p->p_crlock); 94 crfree(cr); 95 return (set_errno(EPERM)); 96 } 97 if (!priv_issubset(&pset, &CR_OPPRIV(pcr)) && 98 !priv_issubset(&pset, priv_getset(pcr, type))) { 99 mutex_exit(&p->p_crlock); 100 /* Policy override should not grow beyond L either */ 101 if (type != PRIV_INHERITABLE || 102 !priv_issubset(&pset, &CR_LPRIV(pcr)) || 103 secpolicy_require_privs(CRED(), &pset) != 0) { 104 crfree(cr); 105 return (set_errno(EPERM)); 106 } 107 mutex_enter(&p->p_crlock); 108 if (pcr != p->p_cred) 109 goto retry; 110 donocd = B_TRUE; 111 } 112 break; 113 114 case PRIV_OFF: 115 /* PRIV_OFF is always allowed */ 116 break; 117 } 118 119 /* 120 * OK! everything is cool. 121 * Do cred COW. 122 */ 123 crcopy_to(pcr, cr); 124 125 /* 126 * If we change the effective, permitted or limit set, we attain 127 * "privilege awareness". 128 */ 129 if (type != PRIV_INHERITABLE) 130 priv_set_PA(cr); 131 132 target = &(CR_PRIVS(cr)->crprivs[type]); 133 134 switch (op) { 135 case PRIV_ON: 136 priv_union(&pset, target); 137 break; 138 case PRIV_OFF: 139 priv_inverse(&pset); 140 priv_intersect(target, &pset); 141 142 /* 143 * Fall-thru to set target and change other process 144 * privilege sets. 145 */ 146 /*FALLTHRU*/ 147 148 case PRIV_SET: 149 *target = pset; 150 151 /* 152 * Take privileges no longer permitted out 153 * of other effective sets as well. 154 * Limit set is enforced at exec() time. 155 */ 156 if (type == PRIV_PERMITTED) 157 priv_intersect(&pset, &CR_EPRIV(cr)); 158 break; 159 } 160 161 /* 162 * When we give up privileges not in the inheritable set, 163 * set SNOCD if not already set; first we compute the 164 * privileges removed from P using Diff = (~P') & P 165 * and then we check whether the removed privileges are 166 * a subset of I. If we retain uid 0, all privileges 167 * are required anyway so don't set SNOCD. 168 */ 169 if (type == PRIV_PERMITTED && (p->p_flag & SNOCD) == 0 && 170 cr->cr_uid != 0 && cr->cr_ruid != 0 && cr->cr_suid != 0) { 171 priv_set_t diff = CR_OPPRIV(cr); 172 priv_inverse(&diff); 173 priv_intersect(&CR_OPPRIV(pcr), &diff); 174 donocd = !priv_issubset(&diff, &CR_IPRIV(cr)); 175 } 176 177 p->p_cred = cr; 178 mutex_exit(&p->p_crlock); 179 180 if (donocd) { 181 mutex_enter(&p->p_lock); 182 p->p_flag |= SNOCD; 183 mutex_exit(&p->p_lock); 184 } 185 186 crset(p, cr); /* broadcast to process threads */ 187 188 return (0); 189 } 190 191 /* 192 * getppriv (priv_ptype_t, priv_set_t *) 193 */ 194 static int 195 getppriv(priv_ptype_t type, priv_set_t *pset) 196 { 197 if (!PRIV_VALIDSET(type)) 198 return (set_errno(EINVAL)); 199 200 if (copyout(priv_getset(CRED(), type), pset, sizeof (priv_set_t)) != 0) 201 return (set_errno(EFAULT)); 202 203 return (0); 204 } 205 206 static int 207 getprivimplinfo(void *buf, size_t bufsize) 208 { 209 int err; 210 211 err = copyout(priv_hold_implinfo(), buf, min(bufsize, privinfosize)); 212 213 priv_release_implinfo(); 214 215 if (err) 216 return (set_errno(EFAULT)); 217 218 return (0); 219 } 220 221 /* 222 * Set process flags in the given target cred. If NULL is specified, then 223 * CRED() is used; otherwise the cred is assumed to be modifiable (i.e. newly 224 * crdup'ed, or equivalent). Some flags are set in the proc rather than cred; 225 * for these, curproc is always used. 226 * 227 * For now we cheat: the flags are actually bit masks so we can simplify 228 * some; we do make sure that the arguments are valid, though. 229 */ 230 231 int 232 setpflags(uint_t flag, uint_t val, cred_t *tcr) 233 { 234 cred_t *cr, *pcr; 235 proc_t *p = curproc; 236 uint_t newflags; 237 boolean_t use_curcred = (tcr == NULL); 238 239 if (val > 1 || (flag != PRIV_DEBUG && flag != PRIV_AWARE && 240 flag != NET_MAC_AWARE && flag != NET_MAC_AWARE_INHERIT && 241 flag != __PROC_PROTECT && flag != PRIV_XPOLICY)) { 242 return (EINVAL); 243 } 244 245 if (flag == __PROC_PROTECT) { 246 mutex_enter(&p->p_lock); 247 if (val == 0) 248 p->p_flag &= ~SNOCD; 249 else 250 p->p_flag |= SNOCD; 251 mutex_exit(&p->p_lock); 252 return (0); 253 } 254 255 if (use_curcred) { 256 cr = cralloc(); 257 mutex_enter(&p->p_crlock); 258 pcr = p->p_cred; 259 } else { 260 cr = pcr = tcr; 261 } 262 263 newflags = CR_FLAGS(pcr); 264 265 if (val != 0) 266 newflags |= flag; 267 else 268 newflags &= ~flag; 269 270 /* No change */ 271 if (CR_FLAGS(pcr) == newflags) { 272 if (use_curcred) { 273 mutex_exit(&p->p_crlock); 274 crfree(cr); 275 } 276 return (0); 277 } 278 279 /* 280 * Setting either the NET_MAC_AWARE or NET_MAC_AWARE_INHERIT 281 * flags is a restricted operation. 282 * 283 * When invoked via the PRIVSYS_SETPFLAGS syscall 284 * we require that the current cred has the net_mac_aware 285 * privilege in its effective set. 286 * 287 * When called from within the kernel by label-aware 288 * services such as NFS, we don't require a privilege check. 289 * 290 */ 291 if ((flag == NET_MAC_AWARE || flag == NET_MAC_AWARE_INHERIT) && 292 (val == 1) && use_curcred) { 293 if (secpolicy_net_mac_aware(pcr) != 0) { 294 mutex_exit(&p->p_crlock); 295 crfree(cr); 296 return (EPERM); 297 } 298 } 299 300 /* Trying to unset PA; if we can't, return an error */ 301 if (flag == PRIV_AWARE && val == 0 && !priv_can_clear_PA(pcr)) { 302 if (use_curcred) { 303 mutex_exit(&p->p_crlock); 304 crfree(cr); 305 } 306 return (EPERM); 307 } 308 309 /* Committed to changing the flag */ 310 if (use_curcred) 311 crcopy_to(pcr, cr); 312 if (flag == PRIV_AWARE) { 313 if (val != 0) 314 priv_set_PA(cr); 315 else 316 priv_adjust_PA(cr); 317 } else { 318 CR_FLAGS(cr) = newflags; 319 } 320 321 /* 322 * Unsetting the flag has as side effect getting rid of 323 * the per-credential policy. 324 */ 325 if (flag == PRIV_XPOLICY && val == 0) 326 crsetcrklpd(cr, NULL); 327 328 if (use_curcred) { 329 p->p_cred = cr; 330 mutex_exit(&p->p_crlock); 331 crset(p, cr); 332 } 333 334 return (0); 335 } 336 337 /* 338 * Getpflags. Currently only implements single bit flags. 339 */ 340 uint_t 341 getpflags(uint_t flag, const cred_t *cr) 342 { 343 if (flag != PRIV_DEBUG && flag != PRIV_AWARE && 344 flag != NET_MAC_AWARE && flag != NET_MAC_AWARE_INHERIT && 345 flag != PRIV_XPOLICY) 346 return ((uint_t)-1); 347 348 return ((CR_FLAGS(cr) & flag) != 0); 349 } 350 351 /* 352 * Privilege system call entry point 353 */ 354 int 355 privsys(int code, priv_op_t op, priv_ptype_t type, void *buf, size_t bufsize, 356 int itype) 357 { 358 int retv; 359 extern int issetugid(void); 360 361 switch (code) { 362 case PRIVSYS_SETPPRIV: 363 if (bufsize < sizeof (priv_set_t)) 364 return (set_errno(ENOMEM)); 365 return (setppriv(op, type, buf)); 366 case PRIVSYS_GETPPRIV: 367 if (bufsize < sizeof (priv_set_t)) 368 return (set_errno(ENOMEM)); 369 return (getppriv(type, buf)); 370 case PRIVSYS_GETIMPLINFO: 371 return (getprivimplinfo(buf, bufsize)); 372 case PRIVSYS_SETPFLAGS: 373 retv = setpflags((uint_t)op, (uint_t)type, NULL); 374 return (retv != 0 ? set_errno(retv) : 0); 375 case PRIVSYS_GETPFLAGS: 376 retv = (int)getpflags((uint_t)op, CRED()); 377 return (retv == -1 ? set_errno(EINVAL) : retv); 378 case PRIVSYS_ISSETUGID: 379 return (issetugid()); 380 case PRIVSYS_KLPD_REG: 381 if (bufsize < sizeof (priv_set_t)) 382 return (set_errno(ENOMEM)); 383 return ((int)klpd_reg((int)op, (idtype_t)itype, (id_t)type, 384 buf)); 385 case PRIVSYS_KLPD_UNREG: 386 return ((int)klpd_unreg((int)op, (idtype_t)itype, (id_t)type)); 387 } 388 return (set_errno(EINVAL)); 389 } 390 391 #ifdef _SYSCALL32_IMPL 392 int 393 privsys32(int code, priv_op_t op, priv_ptype_t type, caddr32_t buf, 394 size32_t bufsize, int itype) 395 { 396 return (privsys(code, op, type, (void *)(uintptr_t)buf, 397 (size_t)bufsize, itype)); 398 } 399 #endif 400