11573Srgrimes/*
21573Srgrimes * Copyright (c) 1989, 1993, 1994
31573Srgrimes *	The Regents of the University of California.  All rights reserved.
41573Srgrimes *
51573Srgrimes * This code is derived from software contributed to Berkeley by
61573Srgrimes * Dave Borman at Cray Research, Inc.
71573Srgrimes *
81573Srgrimes * Redistribution and use in source and binary forms, with or without
91573Srgrimes * modification, are permitted provided that the following conditions
101573Srgrimes * are met:
111573Srgrimes * 1. Redistributions of source code must retain the above copyright
121573Srgrimes *    notice, this list of conditions and the following disclaimer.
131573Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
141573Srgrimes *    notice, this list of conditions and the following disclaimer in the
151573Srgrimes *    documentation and/or other materials provided with the distribution.
161573Srgrimes * 4. Neither the name of the University nor the names of its contributors
171573Srgrimes *    may be used to endorse or promote products derived from this software
181573Srgrimes *    without specific prior written permission.
191573Srgrimes *
201573Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
211573Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
221573Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
231573Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
241573Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
251573Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
261573Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
271573Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
281573Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
291573Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
301573Srgrimes * SUCH DAMAGE.
311573Srgrimes */
321573Srgrimes
331573Srgrimes#if defined(LIBC_SCCS) && !defined(lint)
341573Srgrimesstatic char sccsid[] = "@(#)setmode.c	8.2 (Berkeley) 3/25/94";
351573Srgrimes#endif /* LIBC_SCCS and not lint */
3690045Sobrien#include <sys/cdefs.h>
3790045Sobrien__FBSDID("$FreeBSD$");
381573Srgrimes
3971579Sdeischen#include "namespace.h"
401573Srgrimes#include <sys/types.h>
411573Srgrimes#include <sys/stat.h>
421573Srgrimes
431573Srgrimes#include <ctype.h>
441573Srgrimes#include <signal.h>
451573Srgrimes#include <stddef.h>
461573Srgrimes#include <stdlib.h>
47111273Smikeh#include <unistd.h>
481573Srgrimes
491573Srgrimes#ifdef SETMODE_DEBUG
501573Srgrimes#include <stdio.h>
511573Srgrimes#endif
5271579Sdeischen#include "un-namespace.h"
531573Srgrimes
541573Srgrimes#define	SET_LEN	6		/* initial # of bitcmd struct to malloc */
551573Srgrimes#define	SET_LEN_INCR 4		/* # of bitcmd structs to add as needed */
561573Srgrimes
571573Srgrimestypedef struct bitcmd {
581573Srgrimes	char	cmd;
591573Srgrimes	char	cmd2;
601573Srgrimes	mode_t	bits;
611573Srgrimes} BITCMD;
621573Srgrimes
631573Srgrimes#define	CMD2_CLR	0x01
641573Srgrimes#define	CMD2_SET	0x02
651573Srgrimes#define	CMD2_GBITS	0x04
661573Srgrimes#define	CMD2_OBITS	0x08
671573Srgrimes#define	CMD2_UBITS	0x10
681573Srgrimes
6990045Sobrienstatic BITCMD	*addcmd(BITCMD *, int, int, int, u_int);
7090045Sobrienstatic void	 compress_mode(BITCMD *);
711573Srgrimes#ifdef SETMODE_DEBUG
7290045Sobrienstatic void	 dumpmode(BITCMD *);
731573Srgrimes#endif
741573Srgrimes
751573Srgrimes/*
761573Srgrimes * Given the old mode and an array of bitcmd structures, apply the operations
771573Srgrimes * described in the bitcmd structures to the old mode, and return the new mode.
781573Srgrimes * Note that there is no '=' command; a strict assignment is just a '-' (clear
791573Srgrimes * bits) followed by a '+' (set bits).
801573Srgrimes */
811573Srgrimesmode_t
82144815Sstefanfgetmode(const void *bbox, mode_t omode)
831573Srgrimes{
84111273Smikeh	const BITCMD *set;
8590045Sobrien	mode_t clrval, newmode, value;
861573Srgrimes
87111273Smikeh	set = (const BITCMD *)bbox;
881573Srgrimes	newmode = omode;
891573Srgrimes	for (value = 0;; set++)
901573Srgrimes		switch(set->cmd) {
911573Srgrimes		/*
921573Srgrimes		 * When copying the user, group or other bits around, we "know"
931573Srgrimes		 * where the bits are in the mode so that we can do shifts to
941573Srgrimes		 * copy them around.  If we don't use shifts, it gets real
951573Srgrimes		 * grundgy with lots of single bit checks and bit sets.
961573Srgrimes		 */
971573Srgrimes		case 'u':
981573Srgrimes			value = (newmode & S_IRWXU) >> 6;
991573Srgrimes			goto common;
1001573Srgrimes
1011573Srgrimes		case 'g':
1021573Srgrimes			value = (newmode & S_IRWXG) >> 3;
1031573Srgrimes			goto common;
1041573Srgrimes
1051573Srgrimes		case 'o':
1061573Srgrimes			value = newmode & S_IRWXO;
1071573Srgrimescommon:			if (set->cmd2 & CMD2_CLR) {
1081573Srgrimes				clrval =
1091573Srgrimes				    (set->cmd2 & CMD2_SET) ?  S_IRWXO : value;
1101573Srgrimes				if (set->cmd2 & CMD2_UBITS)
1111573Srgrimes					newmode &= ~((clrval<<6) & set->bits);
1121573Srgrimes				if (set->cmd2 & CMD2_GBITS)
1131573Srgrimes					newmode &= ~((clrval<<3) & set->bits);
1141573Srgrimes				if (set->cmd2 & CMD2_OBITS)
1151573Srgrimes					newmode &= ~(clrval & set->bits);
1161573Srgrimes			}
1171573Srgrimes			if (set->cmd2 & CMD2_SET) {
1181573Srgrimes				if (set->cmd2 & CMD2_UBITS)
1191573Srgrimes					newmode |= (value<<6) & set->bits;
1201573Srgrimes				if (set->cmd2 & CMD2_GBITS)
1211573Srgrimes					newmode |= (value<<3) & set->bits;
1221573Srgrimes				if (set->cmd2 & CMD2_OBITS)
1231573Srgrimes					newmode |= value & set->bits;
1241573Srgrimes			}
1251573Srgrimes			break;
1261573Srgrimes
1271573Srgrimes		case '+':
1281573Srgrimes			newmode |= set->bits;
1291573Srgrimes			break;
1301573Srgrimes
1311573Srgrimes		case '-':
1321573Srgrimes			newmode &= ~set->bits;
1331573Srgrimes			break;
1341573Srgrimes
1351573Srgrimes		case 'X':
1361573Srgrimes			if (omode & (S_IFDIR|S_IXUSR|S_IXGRP|S_IXOTH))
1371573Srgrimes				newmode |= set->bits;
1381573Srgrimes			break;
1391573Srgrimes
1401573Srgrimes		case '\0':
1411573Srgrimes		default:
1421573Srgrimes#ifdef SETMODE_DEBUG
1431573Srgrimes			(void)printf("getmode:%04o -> %04o\n", omode, newmode);
1441573Srgrimes#endif
1451573Srgrimes			return (newmode);
1461573Srgrimes		}
1471573Srgrimes}
1481573Srgrimes
1491573Srgrimes#define	ADDCMD(a, b, c, d)						\
1501573Srgrimes	if (set >= endset) {						\
15190045Sobrien		BITCMD *newset;						\
1521573Srgrimes		setlen += SET_LEN_INCR;					\
1531573Srgrimes		newset = realloc(saveset, sizeof(BITCMD) * setlen);	\
154111274Smikeh		if (!newset) {						\
155111274Smikeh			if (saveset)					\
156111274Smikeh				free(saveset);				\
157111274Smikeh			saveset = NULL;					\
1581573Srgrimes			return (NULL);					\
159111274Smikeh		}							\
1601573Srgrimes		set = newset + (set - saveset);				\
1611573Srgrimes		saveset = newset;					\
1621573Srgrimes		endset = newset + (setlen - 2);				\
1631573Srgrimes	}								\
1641573Srgrimes	set = addcmd(set, (a), (b), (c), (d))
1651573Srgrimes
1661573Srgrimes#define	STANDARD_BITS	(S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO)
1671573Srgrimes
1681573Srgrimesvoid *
169144815Sstefanfsetmode(const char *p)
1701573Srgrimes{
17190045Sobrien	int perm, who;
172111274Smikeh	char op, *ep;
1731573Srgrimes	BITCMD *set, *saveset, *endset;
1741573Srgrimes	sigset_t sigset, sigoset;
1751573Srgrimes	mode_t mask;
17611659Sphk	int equalopdone=0, permXbits, setlen;
177111274Smikeh	long perml;
1781573Srgrimes
1791573Srgrimes	if (!*p)
1801573Srgrimes		return (NULL);
1811573Srgrimes
1821573Srgrimes	/*
1831573Srgrimes	 * Get a copy of the mask for the permissions that are mask relative.
1841573Srgrimes	 * Flip the bits, we want what's not set.  Since it's possible that
1851573Srgrimes	 * the caller is opening files inside a signal handler, protect them
1861573Srgrimes	 * as best we can.
1871573Srgrimes	 */
1881573Srgrimes	sigfillset(&sigset);
18971579Sdeischen        (void)_sigprocmask(SIG_BLOCK, &sigset, &sigoset);
1901573Srgrimes	(void)umask(mask = umask(0));
1911573Srgrimes	mask = ~mask;
19271579Sdeischen        (void)_sigprocmask(SIG_SETMASK, &sigoset, NULL);
1931573Srgrimes
1941573Srgrimes	setlen = SET_LEN + 2;
1958870Srgrimes
1961573Srgrimes	if ((set = malloc((u_int)(sizeof(BITCMD) * setlen))) == NULL)
1971573Srgrimes		return (NULL);
1981573Srgrimes	saveset = set;
1991573Srgrimes	endset = set + (setlen - 2);
2001573Srgrimes
2011573Srgrimes	/*
2021573Srgrimes	 * If an absolute number, get it and return; disallow non-octal digits
2031573Srgrimes	 * or illegal bits.
2041573Srgrimes	 */
20552862Sache	if (isdigit((unsigned char)*p)) {
206111274Smikeh		perml = strtol(p, &ep, 8);
207111274Smikeh		if (*ep || perml < 0 || perml & ~(STANDARD_BITS|S_ISTXT)) {
2081573Srgrimes			free(saveset);
2091573Srgrimes			return (NULL);
2101573Srgrimes		}
211111274Smikeh		perm = (mode_t)perml;
2121573Srgrimes		ADDCMD('=', (STANDARD_BITS|S_ISTXT), perm, mask);
213111274Smikeh		set->cmd = 0;
2141573Srgrimes		return (saveset);
2151573Srgrimes	}
2161573Srgrimes
2171573Srgrimes	/*
2181573Srgrimes	 * Build list of structures to set/clear/copy bits as described by
2191573Srgrimes	 * each clause of the symbolic mode.
2201573Srgrimes	 */
2211573Srgrimes	for (;;) {
2221573Srgrimes		/* First, find out which bits might be modified. */
2231573Srgrimes		for (who = 0;; ++p) {
2241573Srgrimes			switch (*p) {
2251573Srgrimes			case 'a':
2261573Srgrimes				who |= STANDARD_BITS;
2271573Srgrimes				break;
2281573Srgrimes			case 'u':
2291573Srgrimes				who |= S_ISUID|S_IRWXU;
2301573Srgrimes				break;
2311573Srgrimes			case 'g':
2321573Srgrimes				who |= S_ISGID|S_IRWXG;
2331573Srgrimes				break;
2341573Srgrimes			case 'o':
2351573Srgrimes				who |= S_IRWXO;
2361573Srgrimes				break;
2371573Srgrimes			default:
2381573Srgrimes				goto getop;
2391573Srgrimes			}
2401573Srgrimes		}
2411573Srgrimes
2421573Srgrimesgetop:		if ((op = *p++) != '+' && op != '-' && op != '=') {
2431573Srgrimes			free(saveset);
2441573Srgrimes			return (NULL);
2451573Srgrimes		}
2461573Srgrimes		if (op == '=')
2471573Srgrimes			equalopdone = 0;
2481573Srgrimes
2491573Srgrimes		who &= ~S_ISTXT;
2501573Srgrimes		for (perm = 0, permXbits = 0;; ++p) {
2511573Srgrimes			switch (*p) {
2521573Srgrimes			case 'r':
2531573Srgrimes				perm |= S_IRUSR|S_IRGRP|S_IROTH;
2541573Srgrimes				break;
2551573Srgrimes			case 's':
2561573Srgrimes				/* If only "other" bits ignore set-id. */
25751549Sru				if (!who || who & ~S_IRWXO)
2581573Srgrimes					perm |= S_ISUID|S_ISGID;
2591573Srgrimes				break;
2601573Srgrimes			case 't':
2611573Srgrimes				/* If only "other" bits ignore sticky. */
26251549Sru				if (!who || who & ~S_IRWXO) {
2631573Srgrimes					who |= S_ISTXT;
2641573Srgrimes					perm |= S_ISTXT;
2651573Srgrimes				}
2661573Srgrimes				break;
2671573Srgrimes			case 'w':
2681573Srgrimes				perm |= S_IWUSR|S_IWGRP|S_IWOTH;
2691573Srgrimes				break;
2701573Srgrimes			case 'X':
2711573Srgrimes				permXbits = S_IXUSR|S_IXGRP|S_IXOTH;
2721573Srgrimes				break;
2731573Srgrimes			case 'x':
2741573Srgrimes				perm |= S_IXUSR|S_IXGRP|S_IXOTH;
2751573Srgrimes				break;
2761573Srgrimes			case 'u':
2771573Srgrimes			case 'g':
2781573Srgrimes			case 'o':
2791573Srgrimes				/*
2801573Srgrimes				 * When ever we hit 'u', 'g', or 'o', we have
2811573Srgrimes				 * to flush out any partial mode that we have,
2821573Srgrimes				 * and then do the copying of the mode bits.
2831573Srgrimes				 */
2841573Srgrimes				if (perm) {
2851573Srgrimes					ADDCMD(op, who, perm, mask);
2861573Srgrimes					perm = 0;
2871573Srgrimes				}
2881573Srgrimes				if (op == '=')
2891573Srgrimes					equalopdone = 1;
2901573Srgrimes				if (op == '+' && permXbits) {
2911573Srgrimes					ADDCMD('X', who, permXbits, mask);
2921573Srgrimes					permXbits = 0;
2931573Srgrimes				}
2941573Srgrimes				ADDCMD(*p, who, op, mask);
2951573Srgrimes				break;
2961573Srgrimes
2971573Srgrimes			default:
2981573Srgrimes				/*
2991573Srgrimes				 * Add any permissions that we haven't already
3001573Srgrimes				 * done.
3011573Srgrimes				 */
3021573Srgrimes				if (perm || (op == '=' && !equalopdone)) {
3031573Srgrimes					if (op == '=')
3041573Srgrimes						equalopdone = 1;
3051573Srgrimes					ADDCMD(op, who, perm, mask);
3061573Srgrimes					perm = 0;
3071573Srgrimes				}
3081573Srgrimes				if (permXbits) {
3091573Srgrimes					ADDCMD('X', who, permXbits, mask);
3101573Srgrimes					permXbits = 0;
3111573Srgrimes				}
3121573Srgrimes				goto apply;
3131573Srgrimes			}
3141573Srgrimes		}
3151573Srgrimes
3161573Srgrimesapply:		if (!*p)
3171573Srgrimes			break;
3181573Srgrimes		if (*p != ',')
3191573Srgrimes			goto getop;
3201573Srgrimes		++p;
3211573Srgrimes	}
3221573Srgrimes	set->cmd = 0;
3231573Srgrimes#ifdef SETMODE_DEBUG
3241573Srgrimes	(void)printf("Before compress_mode()\n");
3251573Srgrimes	dumpmode(saveset);
3261573Srgrimes#endif
3271573Srgrimes	compress_mode(saveset);
3281573Srgrimes#ifdef SETMODE_DEBUG
3291573Srgrimes	(void)printf("After compress_mode()\n");
3301573Srgrimes	dumpmode(saveset);
3311573Srgrimes#endif
3321573Srgrimes	return (saveset);
3331573Srgrimes}
3341573Srgrimes
3351573Srgrimesstatic BITCMD *
336144815Sstefanfaddcmd(BITCMD *set, int op, int who, int oparg, u_int mask)
3371573Srgrimes{
3381573Srgrimes	switch (op) {
3391573Srgrimes	case '=':
3401573Srgrimes		set->cmd = '-';
3411573Srgrimes		set->bits = who ? who : STANDARD_BITS;
3421573Srgrimes		set++;
3431573Srgrimes
3441573Srgrimes		op = '+';
3451573Srgrimes		/* FALLTHROUGH */
3461573Srgrimes	case '+':
3471573Srgrimes	case '-':
3481573Srgrimes	case 'X':
3491573Srgrimes		set->cmd = op;
3501573Srgrimes		set->bits = (who ? who : mask) & oparg;
3511573Srgrimes		break;
3521573Srgrimes
3531573Srgrimes	case 'u':
3541573Srgrimes	case 'g':
3551573Srgrimes	case 'o':
3561573Srgrimes		set->cmd = op;
3571573Srgrimes		if (who) {
3581573Srgrimes			set->cmd2 = ((who & S_IRUSR) ? CMD2_UBITS : 0) |
3591573Srgrimes				    ((who & S_IRGRP) ? CMD2_GBITS : 0) |
3601573Srgrimes				    ((who & S_IROTH) ? CMD2_OBITS : 0);
361111273Smikeh			set->bits = (mode_t)~0;
3621573Srgrimes		} else {
3631573Srgrimes			set->cmd2 = CMD2_UBITS | CMD2_GBITS | CMD2_OBITS;
3641573Srgrimes			set->bits = mask;
3651573Srgrimes		}
3668870Srgrimes
3671573Srgrimes		if (oparg == '+')
3681573Srgrimes			set->cmd2 |= CMD2_SET;
3691573Srgrimes		else if (oparg == '-')
3701573Srgrimes			set->cmd2 |= CMD2_CLR;
3711573Srgrimes		else if (oparg == '=')
3721573Srgrimes			set->cmd2 |= CMD2_SET|CMD2_CLR;
3731573Srgrimes		break;
3741573Srgrimes	}
3751573Srgrimes	return (set + 1);
3761573Srgrimes}
3771573Srgrimes
3781573Srgrimes#ifdef SETMODE_DEBUG
3791573Srgrimesstatic void
380144815Sstefanfdumpmode(BITCMD *set)
3811573Srgrimes{
3821573Srgrimes	for (; set->cmd; ++set)
3831573Srgrimes		(void)printf("cmd: '%c' bits %04o%s%s%s%s%s%s\n",
3841573Srgrimes		    set->cmd, set->bits, set->cmd2 ? " cmd2:" : "",
3851573Srgrimes		    set->cmd2 & CMD2_CLR ? " CLR" : "",
3861573Srgrimes		    set->cmd2 & CMD2_SET ? " SET" : "",
3871573Srgrimes		    set->cmd2 & CMD2_UBITS ? " UBITS" : "",
3881573Srgrimes		    set->cmd2 & CMD2_GBITS ? " GBITS" : "",
3891573Srgrimes		    set->cmd2 & CMD2_OBITS ? " OBITS" : "");
3901573Srgrimes}
3911573Srgrimes#endif
3921573Srgrimes
3931573Srgrimes/*
3941573Srgrimes * Given an array of bitcmd structures, compress by compacting consecutive
3951573Srgrimes * '+', '-' and 'X' commands into at most 3 commands, one of each.  The 'u',
3968870Srgrimes * 'g' and 'o' commands continue to be separate.  They could probably be
3971573Srgrimes * compacted, but it's not worth the effort.
3981573Srgrimes */
39911659Sphkstatic void
400144815Sstefanfcompress_mode(BITCMD *set)
4011573Srgrimes{
40290045Sobrien	BITCMD *nset;
40390045Sobrien	int setbits, clrbits, Xbits, op;
4041573Srgrimes
4051573Srgrimes	for (nset = set;;) {
4061573Srgrimes		/* Copy over any 'u', 'g' and 'o' commands. */
4071573Srgrimes		while ((op = nset->cmd) != '+' && op != '-' && op != 'X') {
4081573Srgrimes			*set++ = *nset++;
4091573Srgrimes			if (!op)
4101573Srgrimes				return;
4111573Srgrimes		}
4121573Srgrimes
4131573Srgrimes		for (setbits = clrbits = Xbits = 0;; nset++) {
4141573Srgrimes			if ((op = nset->cmd) == '-') {
4151573Srgrimes				clrbits |= nset->bits;
4161573Srgrimes				setbits &= ~nset->bits;
4171573Srgrimes				Xbits &= ~nset->bits;
4181573Srgrimes			} else if (op == '+') {
4191573Srgrimes				setbits |= nset->bits;
4201573Srgrimes				clrbits &= ~nset->bits;
4211573Srgrimes				Xbits &= ~nset->bits;
4221573Srgrimes			} else if (op == 'X')
4231573Srgrimes				Xbits |= nset->bits & ~setbits;
4241573Srgrimes			else
4251573Srgrimes				break;
4261573Srgrimes		}
4271573Srgrimes		if (clrbits) {
4281573Srgrimes			set->cmd = '-';
4291573Srgrimes			set->cmd2 = 0;
4301573Srgrimes			set->bits = clrbits;
4311573Srgrimes			set++;
4321573Srgrimes		}
4331573Srgrimes		if (setbits) {
4341573Srgrimes			set->cmd = '+';
4351573Srgrimes			set->cmd2 = 0;
4361573Srgrimes			set->bits = setbits;
4371573Srgrimes			set++;
4381573Srgrimes		}
4391573Srgrimes		if (Xbits) {
4401573Srgrimes			set->cmd = 'X';
4411573Srgrimes			set->cmd2 = 0;
4421573Srgrimes			set->bits = Xbits;
4431573Srgrimes			set++;
4441573Srgrimes		}
4451573Srgrimes	}
4461573Srgrimes}
447