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