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, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 /* 28 * Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T 29 */ 30 31 #pragma ident "%Z%%M% %I% %E% SMI" 32 33 #include <sys/param.h> 34 #include <sys/types.h> 35 #include <sys/sysmacros.h> 36 #include <sys/systm.h> 37 #include <sys/tuneable.h> 38 #include <sys/cred_impl.h> 39 #include <sys/errno.h> 40 #include <sys/proc.h> 41 #include <sys/signal.h> 42 #include <sys/debug.h> 43 #include <sys/policy.h> 44 #include <sys/zone.h> 45 46 int 47 setuid(uid_t uid) 48 { 49 register 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 57 if (uid < 0 || uid > MAXUID) 58 return (set_errno(EINVAL)); 59 60 /* 61 * Need to pre-allocate the new cred structure before grabbing 62 * the p_crlock mutex. 63 */ 64 newcr = cralloc(); 65 66 p = ttoproc(curthread); 67 68 retry: 69 mutex_enter(&p->p_crlock); 70 cr = p->p_cred; 71 72 if ((uid == cr->cr_ruid || uid == cr->cr_suid) && 73 secpolicy_allow_setid(cr, uid, B_TRUE) != 0) { 74 error = 0; 75 crcopy_to(cr, newcr); 76 p->p_cred = newcr; 77 newcr->cr_uid = uid; 78 } else if ((error = secpolicy_allow_setid(cr, uid, B_FALSE)) == 0) { 79 if (!uidchge && uid != cr->cr_ruid) { 80 /* 81 * The ruid of the process is going to change. In order 82 * to avoid a race condition involving the 83 * process-count associated with the newly given ruid, 84 * we increment the count before assigning the 85 * credential to the process. 86 * To do that, we'll have to take pidlock, so we first 87 * release p_crlock. 88 */ 89 mutex_exit(&p->p_crlock); 90 uidchge = 1; 91 mutex_enter(&pidlock); 92 upcount_inc(uid, zoneid); 93 mutex_exit(&pidlock); 94 /* 95 * As we released p_crlock we can't rely on the cr 96 * we read. So retry the whole thing. 97 */ 98 goto retry; 99 } 100 /* 101 * A privileged process that gives up its privilege 102 * must be marked to produce no core dump. 103 */ 104 if (cr->cr_uid != uid || 105 cr->cr_ruid != uid || 106 cr->cr_suid != uid) 107 do_nocd = 1; 108 oldruid = cr->cr_ruid; 109 crcopy_to(cr, newcr); 110 p->p_cred = newcr; 111 newcr->cr_ruid = uid; 112 newcr->cr_suid = uid; 113 newcr->cr_uid = uid; 114 ASSERT(uid != oldruid ? uidchge : 1); 115 } else 116 crfree(newcr); 117 118 mutex_exit(&p->p_crlock); 119 120 /* 121 * We decrement the number of processes associated with the oldruid 122 * to match the increment above, even if the ruid of the process 123 * did not change or an error occurred (oldruid == uid). 124 */ 125 if (uidchge) { 126 mutex_enter(&pidlock); 127 upcount_dec(oldruid, zoneid); 128 mutex_exit(&pidlock); 129 } 130 131 if (error == 0) { 132 if (do_nocd) { 133 mutex_enter(&p->p_lock); 134 p->p_flag |= SNOCD; 135 mutex_exit(&p->p_lock); 136 } 137 crset(p, newcr); /* broadcast to process threads */ 138 return (0); 139 } 140 return (set_errno(error)); 141 } 142 143 int64_t 144 getuid(void) 145 { 146 rval_t r; 147 cred_t *cr; 148 149 cr = curthread->t_cred; 150 r.r_val1 = cr->cr_ruid; 151 r.r_val2 = cr->cr_uid; 152 return (r.r_vals); 153 } 154 155 int 156 seteuid(uid_t uid) 157 { 158 register proc_t *p; 159 int error = EPERM; 160 int do_nocd = 0; 161 cred_t *cr, *newcr; 162 163 if (uid < 0 || uid > MAXUID) 164 return (set_errno(EINVAL)); 165 166 /* 167 * Need to pre-allocate the new cred structure before grabbing 168 * the p_crlock mutex. 169 */ 170 newcr = cralloc(); 171 p = ttoproc(curthread); 172 mutex_enter(&p->p_crlock); 173 cr = p->p_cred; 174 175 if (uid == cr->cr_ruid || uid == cr->cr_uid || uid == cr->cr_suid || 176 (error = secpolicy_allow_setid(cr, uid, B_FALSE)) == 0) { 177 /* 178 * A privileged process that makes itself look like a 179 * set-uid process must be marked to produce no core dump, 180 * if the effective uid did changed. 181 */ 182 if (cr->cr_uid != uid && error == 0) 183 do_nocd = 1; 184 error = 0; 185 crcopy_to(cr, newcr); 186 p->p_cred = newcr; 187 newcr->cr_uid = uid; 188 } else 189 crfree(newcr); 190 191 mutex_exit(&p->p_crlock); 192 193 if (error == 0) { 194 if (do_nocd) { 195 mutex_enter(&p->p_lock); 196 p->p_flag |= SNOCD; 197 mutex_exit(&p->p_lock); 198 } 199 crset(p, newcr); /* broadcast to process threads */ 200 return (0); 201 } 202 return (set_errno(error)); 203 } 204 205 /* 206 * Buy-back from SunOS 4.x 207 * 208 * Like setuid() and seteuid() combined -except- that non-root users 209 * can change cr_ruid to cr_uid, and the semantics of cr_suid are 210 * subtly different. 211 */ 212 int 213 setreuid(uid_t ruid, uid_t euid) 214 { 215 proc_t *p; 216 int error = 0; 217 int do_nocd = 0; 218 int uidchge = 0; 219 uid_t oldruid = ruid; 220 cred_t *cr, *newcr; 221 zoneid_t zoneid = getzoneid(); 222 223 if ((ruid != -1 && (ruid < 0 || ruid > MAXUID)) || 224 (euid != -1 && (euid < 0 || euid > MAXUID))) 225 return (set_errno(EINVAL)); 226 227 /* 228 * Need to pre-allocate the new cred structure before grabbing 229 * the p_crlock mutex. 230 */ 231 newcr = cralloc(); 232 233 p = ttoproc(curthread); 234 235 retry: 236 mutex_enter(&p->p_crlock); 237 cr = p->p_cred; 238 239 if (ruid != -1 && ruid != cr->cr_ruid && ruid != cr->cr_uid && 240 secpolicy_allow_setid(cr, ruid, B_FALSE) != 0) { 241 error = EPERM; 242 } else if (euid != -1 && 243 euid != cr->cr_ruid && euid != cr->cr_uid && 244 euid != cr->cr_suid && secpolicy_allow_setid(cr, euid, B_FALSE)) { 245 error = EPERM; 246 } else { 247 if (!uidchge && ruid != -1 && cr->cr_ruid != ruid) { 248 /* 249 * The ruid of the process is going to change. In order 250 * to avoid a race condition involving the 251 * process-count associated with the newly given ruid, 252 * we increment the count before assigning the 253 * credential to the process. 254 * To do that, we'll have to take pidlock, so we first 255 * release p_crlock. 256 */ 257 mutex_exit(&p->p_crlock); 258 uidchge = 1; 259 mutex_enter(&pidlock); 260 upcount_inc(ruid, zoneid); 261 mutex_exit(&pidlock); 262 /* 263 * As we released p_crlock we can't rely on the cr 264 * we read. So retry the whole thing. 265 */ 266 goto retry; 267 } 268 crhold(cr); 269 crcopy_to(cr, newcr); 270 p->p_cred = newcr; 271 272 if (euid != -1) 273 newcr->cr_uid = euid; 274 if (ruid != -1) { 275 oldruid = newcr->cr_ruid; 276 newcr->cr_ruid = ruid; 277 ASSERT(ruid != oldruid ? uidchge : 1); 278 } 279 /* 280 * "If the real uid is being changed, or the effective uid is 281 * being changed to a value not equal to the real uid, the 282 * saved uid is set to the new effective uid." 283 */ 284 if (ruid != -1 || 285 (euid != -1 && newcr->cr_uid != newcr->cr_ruid)) 286 newcr->cr_suid = newcr->cr_uid; 287 /* 288 * A process that gives up its privilege 289 * must be marked to produce no core dump. 290 */ 291 if ((cr->cr_uid != newcr->cr_uid || 292 cr->cr_ruid != newcr->cr_ruid || 293 cr->cr_suid != newcr->cr_suid)) 294 do_nocd = 1; 295 296 crfree(cr); 297 } 298 mutex_exit(&p->p_crlock); 299 300 /* 301 * We decrement the number of processes associated with the oldruid 302 * to match the increment above, even if the ruid of the process 303 * did not change or an error occurred (oldruid == uid). 304 */ 305 if (uidchge) { 306 ASSERT(oldruid != -1 && ruid != -1); 307 mutex_enter(&pidlock); 308 upcount_dec(oldruid, zoneid); 309 mutex_exit(&pidlock); 310 } 311 312 if (error == 0) { 313 if (do_nocd) { 314 mutex_enter(&p->p_lock); 315 p->p_flag |= SNOCD; 316 mutex_exit(&p->p_lock); 317 } 318 crset(p, newcr); /* broadcast to process threads */ 319 return (0); 320 } 321 crfree(newcr); 322 return (set_errno(error)); 323 } 324