Home | History | Annotate | Download | only in syscall
      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	"@(#)uid.c	1.23	08/01/04 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