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 /* 27 * Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T 28 */ 29 30 #pragma ident "%Z%%M% %I% %E% SMI" 31 32 #include <sys/param.h> 33 #include <sys/types.h> 34 #include <sys/sysmacros.h> 35 #include <sys/systm.h> 36 #include <sys/tuneable.h> 37 #include <sys/cred_impl.h> 38 #include <sys/errno.h> 39 #include <sys/proc.h> 40 #include <sys/signal.h> 41 #include <sys/debug.h> 42 #include <sys/policy.h> 43 #include <sys/zone.h> 44 #include <sys/sid.h> 45 46 int 47 setuid(uid_t uid) 48 { 49 proc_t *p; 50 int error; 51 int do_nocd = 0; 52 int uidchge = 0; 53 cred_t *cr, *newcr; 54 uid_t oldruid = uid; 55 zoneid_t zoneid = getzoneid(); 56 ksid_t ksid, *ksp; 57 zone_t *zone = crgetzone(CRED()); 58 59 if (!VALID_UID(uid, zone)) 60 return (set_errno(EINVAL)); 61 62 if (uid > MAXUID) { 63 if (ksid_lookupbyuid(zone, uid, &ksid) != 0) 64 return (set_errno(EINVAL)); 65 ksp = &ksid; 66 } else { 67 ksp = NULL; 68 } 69 /* 70 * Need to pre-allocate the new cred structure before grabbing 71 * the p_crlock mutex. 72 */ 73 newcr = cralloc_ksid(); 74 75 p = ttoproc(curthread); 76 77 retry: 78 mutex_enter(&p->p_crlock); 79 cr = p->p_cred; 80 81 if ((uid == cr->cr_ruid || uid == cr->cr_suid) && 82 secpolicy_allow_setid(cr, uid, B_TRUE) != 0) { 83 error = 0; 84 crcopy_to(cr, newcr); 85 p->p_cred = newcr; 86 newcr->cr_uid = uid; 87 crsetsid(newcr, ksp, KSID_USER); 88 } else if ((error = secpolicy_allow_setid(cr, uid, B_FALSE)) == 0) { 89 if (!uidchge && uid != cr->cr_ruid) { 90 /* 91 * The ruid of the process is going to change. In order 92 * to avoid a race condition involving the 93 * process-count associated with the newly given ruid, 94 * we increment the count before assigning the 95 * credential to the process. 96 * To do that, we'll have to take pidlock, so we first 97 * release p_crlock. 98 */ 99 mutex_exit(&p->p_crlock); 100 uidchge = 1; 101 mutex_enter(&pidlock); 102 upcount_inc(uid, zoneid); 103 mutex_exit(&pidlock); 104 /* 105 * As we released p_crlock we can't rely on the cr 106 * we read. So retry the whole thing. 107 */ 108 goto retry; 109 } 110 /* 111 * A privileged process that gives up its privilege 112 * must be marked to produce no core dump. 113 */ 114 if (cr->cr_uid != uid || 115 cr->cr_ruid != uid || 116 cr->cr_suid != uid) 117 do_nocd = 1; 118 oldruid = cr->cr_ruid; 119 crcopy_to(cr, newcr); 120 p->p_cred = newcr; 121 newcr->cr_ruid = uid; 122 newcr->cr_suid = uid; 123 newcr->cr_uid = uid; 124 crsetsid(newcr, ksp, KSID_USER); 125 ASSERT(uid != oldruid ? uidchge : 1); 126 } else { 127 crfree(newcr); 128 if (ksp != NULL) 129 ksid_rele(ksp); 130 } 131 132 mutex_exit(&p->p_crlock); 133 134 /* 135 * We decrement the number of processes associated with the oldruid 136 * to match the increment above, even if the ruid of the process 137 * did not change or an error occurred (oldruid == uid). 138 */ 139 if (uidchge) { 140 mutex_enter(&pidlock); 141 upcount_dec(oldruid, zoneid); 142 mutex_exit(&pidlock); 143 } 144 145 if (error == 0) { 146 if (do_nocd) { 147 mutex_enter(&p->p_lock); 148 p->p_flag |= SNOCD; 149 mutex_exit(&p->p_lock); 150 } 151 crset(p, newcr); /* broadcast to process threads */ 152 return (0); 153 } 154 return (set_errno(error)); 155 } 156 157 int64_t 158 getuid(void) 159 { 160 rval_t r; 161 cred_t *cr; 162 163 cr = curthread->t_cred; 164 r.r_val1 = cr->cr_ruid; 165 r.r_val2 = cr->cr_uid; 166 return (r.r_vals); 167 } 168 169 int 170 seteuid(uid_t uid) 171 { 172 proc_t *p; 173 int error = EPERM; 174 int do_nocd = 0; 175 cred_t *cr, *newcr; 176 ksid_t ksid, *ksp; 177 zone_t *zone = crgetzone(CRED()); 178 179 if (!VALID_UID(uid, zone)) 180 return (set_errno(EINVAL)); 181 182 if (uid > MAXUID) { 183 if (ksid_lookupbyuid(zone, uid, &ksid) != 0) 184 return (set_errno(EINVAL)); 185 ksp = &ksid; 186 } else { 187 ksp = NULL; 188 } 189 190 /* 191 * Need to pre-allocate the new cred structure before grabbing 192 * the p_crlock mutex. 193 */ 194 newcr = cralloc_ksid(); 195 p = ttoproc(curthread); 196 mutex_enter(&p->p_crlock); 197 cr = p->p_cred; 198 199 if (uid == cr->cr_ruid || uid == cr->cr_uid || uid == cr->cr_suid || 200 (error = secpolicy_allow_setid(cr, uid, B_FALSE)) == 0) { 201 /* 202 * A privileged process that makes itself look like a 203 * set-uid process must be marked to produce no core dump, 204 * if the effective uid did changed. 205 */ 206 if (cr->cr_uid != uid && error == 0) 207 do_nocd = 1; 208 error = 0; 209 crcopy_to(cr, newcr); 210 p->p_cred = newcr; 211 newcr->cr_uid = uid; 212 crsetsid(newcr, ksp, KSID_USER); 213 } else { 214 crfree(newcr); 215 if (ksp != NULL) 216 ksid_rele(ksp); 217 } 218 219 mutex_exit(&p->p_crlock); 220 221 if (error == 0) { 222 if (do_nocd) { 223 mutex_enter(&p->p_lock); 224 p->p_flag |= SNOCD; 225 mutex_exit(&p->p_lock); 226 } 227 crset(p, newcr); /* broadcast to process threads */ 228 return (0); 229 } 230 return (set_errno(error)); 231 } 232 233 /* 234 * Buy-back from SunOS 4.x 235 * 236 * Like setuid() and seteuid() combined -except- that non-root users 237 * can change cr_ruid to cr_uid, and the semantics of cr_suid are 238 * subtly different. 239 */ 240 int 241 setreuid(uid_t ruid, uid_t euid) 242 { 243 proc_t *p; 244 int error = 0; 245 int do_nocd = 0; 246 int uidchge = 0; 247 uid_t oldruid = ruid; 248 cred_t *cr, *newcr; 249 zoneid_t zoneid = getzoneid(); 250 ksid_t ksid, *ksp; 251 zone_t *zone = crgetzone(CRED()); 252 253 if ((ruid != -1 && !VALID_UID(ruid, zone)) || 254 (euid != -1 && !VALID_UID(euid, zone))) 255 return (set_errno(EINVAL)); 256 257 if (euid != -1 && euid > MAXUID) { 258 if (ksid_lookupbyuid(zone, euid, &ksid) != 0) 259 return (set_errno(EINVAL)); 260 ksp = &ksid; 261 } else { 262 ksp = NULL; 263 } 264 265 /* 266 * Need to pre-allocate the new cred structure before grabbing 267 * the p_crlock mutex. 268 */ 269 newcr = cralloc_ksid(); 270 271 p = ttoproc(curthread); 272 273 retry: 274 mutex_enter(&p->p_crlock); 275 cr = p->p_cred; 276 277 if (ruid != -1 && ruid != cr->cr_ruid && ruid != cr->cr_uid && 278 secpolicy_allow_setid(cr, ruid, B_FALSE) != 0) { 279 error = EPERM; 280 } else if (euid != -1 && 281 euid != cr->cr_ruid && euid != cr->cr_uid && 282 euid != cr->cr_suid && secpolicy_allow_setid(cr, euid, B_FALSE)) { 283 error = EPERM; 284 } else { 285 if (!uidchge && ruid != -1 && cr->cr_ruid != ruid) { 286 /* 287 * The ruid of the process is going to change. In order 288 * to avoid a race condition involving the 289 * process-count associated with the newly given ruid, 290 * we increment the count before assigning the 291 * credential to the process. 292 * To do that, we'll have to take pidlock, so we first 293 * release p_crlock. 294 */ 295 mutex_exit(&p->p_crlock); 296 uidchge = 1; 297 mutex_enter(&pidlock); 298 upcount_inc(ruid, zoneid); 299 mutex_exit(&pidlock); 300 /* 301 * As we released p_crlock we can't rely on the cr 302 * we read. So retry the whole thing. 303 */ 304 goto retry; 305 } 306 crhold(cr); 307 crcopy_to(cr, newcr); 308 p->p_cred = newcr; 309 310 if (euid != -1) { 311 newcr->cr_uid = euid; 312 crsetsid(newcr, ksp, KSID_USER); 313 } 314 if (ruid != -1) { 315 oldruid = newcr->cr_ruid; 316 newcr->cr_ruid = ruid; 317 ASSERT(ruid != oldruid ? uidchge : 1); 318 } 319 /* 320 * "If the real uid is being changed, or the effective uid is 321 * being changed to a value not equal to the real uid, the 322 * saved uid is set to the new effective uid." 323 */ 324 if (ruid != -1 || 325 (euid != -1 && newcr->cr_uid != newcr->cr_ruid)) 326 newcr->cr_suid = newcr->cr_uid; 327 /* 328 * A process that gives up its privilege 329 * must be marked to produce no core dump. 330 */ 331 if ((cr->cr_uid != newcr->cr_uid || 332 cr->cr_ruid != newcr->cr_ruid || 333 cr->cr_suid != newcr->cr_suid)) 334 do_nocd = 1; 335 336 crfree(cr); 337 } 338 mutex_exit(&p->p_crlock); 339 340 /* 341 * We decrement the number of processes associated with the oldruid 342 * to match the increment above, even if the ruid of the process 343 * did not change or an error occurred (oldruid == uid). 344 */ 345 if (uidchge) { 346 ASSERT(oldruid != -1 && ruid != -1); 347 mutex_enter(&pidlock); 348 upcount_dec(oldruid, zoneid); 349 mutex_exit(&pidlock); 350 } 351 352 if (error == 0) { 353 if (do_nocd) { 354 mutex_enter(&p->p_lock); 355 p->p_flag |= SNOCD; 356 mutex_exit(&p->p_lock); 357 } 358 crset(p, newcr); /* broadcast to process threads */ 359 return (0); 360 } 361 crfree(newcr); 362 if (ksp != NULL) 363 ksid_rele(ksp); 364 return (set_errno(error)); 365 } 366