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 2006 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 30 #include <sys/types.h> 31 #include <sys/systm.h> 32 #include <sys/errno.h> 33 #include <sys/zone.h> 34 #include <sys/cred_impl.h> 35 #include <sys/policy.h> 36 37 typedef ushort_t l_uid16_t; 38 typedef ushort_t l_gid16_t; 39 typedef uint_t l_uid_t; 40 typedef uint_t l_gid_t; 41 42 #define LINUX_UID16_TO_UID32(uid16) \ 43 (((uid16) == (l_uid16_t)-1) ? ((l_uid_t)-1) : (l_uid_t)(uid16)) 44 45 #define LINUX_GID16_TO_GID32(gid16) \ 46 (((gid16) == (l_gid16_t)-1) ? ((l_gid_t)-1) : (l_gid_t)(gid16)) 47 48 #define LX_NGROUPS_MAX 32 49 extern int setgroups(int, gid_t *); 50 51 /* 52 * This function is based on setreuid in common/syscall/uid.c and exists 53 * because Solaris does not have a way to explicitly set the saved uid (suid) 54 * from any other system call. 55 */ 56 long 57 lx_setresuid(l_uid_t ruid, l_uid_t euid, l_uid_t suid) 58 { 59 proc_t *p; 60 int error = 0; 61 int do_nocd = 0; 62 int uidchge = 0; 63 uid_t oldruid = ruid; 64 cred_t *cr, *newcr; 65 zoneid_t zoneid = getzoneid(); 66 67 if ((ruid != -1 && (ruid > MAXUID)) || 68 (euid != -1 && (euid > MAXUID)) || 69 (suid != -1 && (suid > MAXUID))) { 70 error = EINVAL; 71 goto done; 72 } 73 74 /* 75 * Need to pre-allocate the new cred structure before grabbing 76 * the p_crlock mutex. 77 */ 78 newcr = cralloc(); 79 80 p = ttoproc(curthread); 81 82 retry: 83 mutex_enter(&p->p_crlock); 84 cr = p->p_cred; 85 86 if (ruid != -1 && 87 ruid != cr->cr_ruid && ruid != cr->cr_uid && 88 ruid != cr->cr_suid && secpolicy_allow_setid(cr, ruid, B_FALSE)) { 89 error = EPERM; 90 } else if (euid != -1 && 91 euid != cr->cr_ruid && euid != cr->cr_uid && 92 euid != cr->cr_suid && secpolicy_allow_setid(cr, euid, B_FALSE)) { 93 error = EPERM; 94 } else if (suid != -1 && 95 suid != cr->cr_ruid && suid != cr->cr_uid && 96 suid != cr->cr_suid && secpolicy_allow_setid(cr, suid, B_FALSE)) { 97 error = EPERM; 98 } else { 99 if (!uidchge && ruid != -1 && cr->cr_ruid != ruid) { 100 /* 101 * The ruid of the process is going to change. In order 102 * to avoid a race condition involving the 103 * process count associated with the newly given ruid, 104 * we increment the count before assigning the 105 * credential to the process. 106 * To do that, we'll have to take pidlock, so we first 107 * release p_crlock. 108 */ 109 mutex_exit(&p->p_crlock); 110 uidchge = 1; 111 mutex_enter(&pidlock); 112 upcount_inc(ruid, zoneid); 113 mutex_exit(&pidlock); 114 /* 115 * As we released p_crlock we can't rely on the cr 116 * we read. So retry the whole thing. 117 */ 118 goto retry; 119 } 120 crhold(cr); 121 crcopy_to(cr, newcr); 122 p->p_cred = newcr; 123 124 if (euid != -1) 125 newcr->cr_uid = euid; 126 if (suid != -1) 127 newcr->cr_suid = suid; 128 if (ruid != -1) { 129 oldruid = newcr->cr_ruid; 130 newcr->cr_ruid = ruid; 131 ASSERT(ruid != oldruid ? uidchge : 1); 132 } 133 134 /* 135 * A process that gives up its privilege 136 * must be marked to produce no core dump. 137 */ 138 if ((cr->cr_uid != newcr->cr_uid || 139 cr->cr_ruid != newcr->cr_ruid || 140 cr->cr_suid != newcr->cr_suid)) 141 do_nocd = 1; 142 143 crfree(cr); 144 } 145 mutex_exit(&p->p_crlock); 146 147 /* 148 * We decrement the number of processes associated with the oldruid 149 * to match the increment above, even if the ruid of the process 150 * did not change or an error occurred (oldruid == uid). 151 */ 152 if (uidchge) { 153 ASSERT(oldruid != -1 && ruid != -1); 154 mutex_enter(&pidlock); 155 upcount_dec(oldruid, zoneid); 156 mutex_exit(&pidlock); 157 } 158 159 if (error == 0) { 160 if (do_nocd) { 161 mutex_enter(&p->p_lock); 162 p->p_flag |= SNOCD; 163 mutex_exit(&p->p_lock); 164 } 165 crset(p, newcr); /* broadcast to process threads */ 166 goto done; 167 } 168 crfree(newcr); 169 done: 170 if (error) 171 return (set_errno(error)); 172 else 173 return (0); 174 } 175 176 long 177 lx_setresuid16(l_uid16_t ruid16, l_uid16_t euid16, l_uid16_t suid16) 178 { 179 long rval; 180 181 rval = lx_setresuid( 182 LINUX_UID16_TO_UID32(ruid16), 183 LINUX_UID16_TO_UID32(euid16), 184 LINUX_UID16_TO_UID32(suid16)); 185 186 return (rval); 187 } 188 189 /* 190 * This function is based on setregid in common/syscall/gid.c 191 */ 192 long 193 lx_setresgid(l_gid_t rgid, l_gid_t egid, l_gid_t sgid) 194 { 195 proc_t *p; 196 int error = 0; 197 int do_nocd = 0; 198 cred_t *cr, *newcr; 199 200 if ((rgid != -1 && (rgid > MAXUID)) || 201 (egid != -1 && (egid > MAXUID)) || 202 (sgid != -1 && (sgid > MAXUID))) { 203 error = EINVAL; 204 goto done; 205 } 206 207 /* 208 * Need to pre-allocate the new cred structure before grabbing 209 * the p_crlock mutex. 210 */ 211 newcr = cralloc(); 212 213 p = ttoproc(curthread); 214 mutex_enter(&p->p_crlock); 215 cr = p->p_cred; 216 217 if (rgid != -1 && 218 rgid != cr->cr_rgid && rgid != cr->cr_gid && 219 rgid != cr->cr_sgid && secpolicy_allow_setid(cr, -1, B_FALSE)) { 220 error = EPERM; 221 } else if (egid != -1 && 222 egid != cr->cr_rgid && egid != cr->cr_gid && 223 egid != cr->cr_sgid && secpolicy_allow_setid(cr, -1, B_FALSE)) { 224 error = EPERM; 225 } else if (sgid != -1 && 226 sgid != cr->cr_rgid && sgid != cr->cr_gid && 227 sgid != cr->cr_sgid && secpolicy_allow_setid(cr, -1, B_FALSE)) { 228 error = EPERM; 229 } else { 230 crhold(cr); 231 crcopy_to(cr, newcr); 232 p->p_cred = newcr; 233 234 if (egid != -1) 235 newcr->cr_gid = egid; 236 if (sgid != -1) 237 newcr->cr_sgid = sgid; 238 if (rgid != -1) 239 newcr->cr_rgid = rgid; 240 241 /* 242 * A process that gives up its privilege 243 * must be marked to produce no core dump. 244 */ 245 if ((cr->cr_gid != newcr->cr_gid || 246 cr->cr_rgid != newcr->cr_rgid || 247 cr->cr_sgid != newcr->cr_sgid)) 248 do_nocd = 1; 249 250 crfree(cr); 251 } 252 mutex_exit(&p->p_crlock); 253 254 if (error == 0) { 255 if (do_nocd) { 256 mutex_enter(&p->p_lock); 257 p->p_flag |= SNOCD; 258 mutex_exit(&p->p_lock); 259 } 260 crset(p, newcr); /* broadcast to process threads */ 261 goto done; 262 } 263 crfree(newcr); 264 done: 265 if (error) 266 return (set_errno(error)); 267 else 268 return (0); 269 } 270 271 long 272 lx_setresgid16(l_gid16_t rgid16, l_gid16_t egid16, l_gid16_t sgid16) 273 { 274 long rval; 275 276 rval = lx_setresgid( 277 LINUX_GID16_TO_GID32(rgid16), 278 LINUX_GID16_TO_GID32(egid16), 279 LINUX_GID16_TO_GID32(sgid16)); 280 281 return (rval); 282 } 283 284 /* 285 * Linux defines NGROUPS_MAX to be 32, but on Solaris it is only 16. We employ 286 * the terrible hack below so that tests may proceed, if only on DEBUG kernels. 287 */ 288 long 289 lx_setgroups(int ngroups, gid_t *grouplist) 290 { 291 #ifdef DEBUG 292 if (ngroups > ngroups_max && ngroups <= LX_NGROUPS_MAX) 293 ngroups = ngroups_max; 294 #endif /* DEBUG */ 295 296 return (setgroups(ngroups, grouplist)); 297 }