setmode.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462
  1. /* $OpenBSD: setmode.c,v 1.17 2005/08/08 08:05:34 espie Exp $ */
  2. /* $NetBSD: setmode.c,v 1.15 1997/02/07 22:21:06 christos Exp $ */
  3. /*
  4. * Copyright (c) 1989, 1993, 1994
  5. * The Regents of the University of California. All rights reserved.
  6. *
  7. * This code is derived from software contributed to Berkeley by
  8. * Dave Borman at Cray Research, Inc.
  9. *
  10. * Redistribution and use in source and binary forms, with or without
  11. * modification, are permitted provided that the following conditions
  12. * are met:
  13. * 1. Redistributions of source code must retain the above copyright
  14. * notice, this list of conditions and the following disclaimer.
  15. * 2. Redistributions in binary form must reproduce the above copyright
  16. * notice, this list of conditions and the following disclaimer in the
  17. * documentation and/or other materials provided with the distribution.
  18. * 3. Neither the name of the University nor the names of its contributors
  19. * may be used to endorse or promote products derived from this software
  20. * without specific prior written permission.
  21. *
  22. * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
  23. * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  24. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  25. * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
  26. * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  27. * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  28. * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  29. * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  30. * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  31. * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  32. * SUCH DAMAGE.
  33. */
  34. #include <sys/types.h>
  35. #include <sys/stat.h>
  36. #include <ctype.h>
  37. #include <errno.h>
  38. #include <signal.h>
  39. #include <stdint.h>
  40. #include <stdlib.h>
  41. #include <unistd.h>
  42. #ifdef SETMODE_DEBUG
  43. #include <stdio.h>
  44. #endif
  45. __SCCSID("@(#)setmode.c 8.2 (Berkeley) 3/25/94");
  46. __RCSID("$MirOS: src/lib/libc/gen/setmode.c,v 1.15 2010/10/08 17:57:00 tg Exp $");
  47. #define SET_LEN 6 /* initial # of bitcmd struct to malloc */
  48. #define SET_LEN_INCR 4 /* # of bitcmd structs to add as needed */
  49. #if !defined(S_ISTXT) && defined(S_ISVTX)
  50. #define S_ISTXT S_ISVTX
  51. #endif
  52. typedef struct bitcmd {
  53. mode_t bits;
  54. char cmd;
  55. char cmd2;
  56. } BITCMD;
  57. #define CMD2_CLR 0x01
  58. #define CMD2_SET 0x02
  59. #define CMD2_GBITS 0x04
  60. #define CMD2_OBITS 0x08
  61. #define CMD2_UBITS 0x10
  62. static BITCMD *addcmd(BITCMD *, int, int, int, u_int);
  63. static void compress_mode(BITCMD *);
  64. #ifdef SETMODE_DEBUG
  65. static void dumpmode(BITCMD *);
  66. #endif
  67. /*
  68. * Given the old mode and an array of bitcmd structures, apply the operations
  69. * described in the bitcmd structures to the old mode, and return the new mode.
  70. * Note that there is no '=' command; a strict assignment is just a '-' (clear
  71. * bits) followed by a '+' (set bits).
  72. */
  73. mode_t
  74. getmode(const void *bbox, mode_t omode)
  75. {
  76. const BITCMD *set;
  77. mode_t clrval, newmode, value;
  78. set = (const BITCMD *)bbox;
  79. newmode = omode;
  80. for (value = 0;; set++)
  81. switch(set->cmd) {
  82. /*
  83. * When copying the user, group or other bits around, we "know"
  84. * where the bits are in the mode so that we can do shifts to
  85. * copy them around. If we don't use shifts, it gets real
  86. * grundgy with lots of single bit checks and bit sets.
  87. */
  88. case 'u':
  89. value = (newmode & S_IRWXU) >> 6;
  90. goto common;
  91. case 'g':
  92. value = (newmode & S_IRWXG) >> 3;
  93. goto common;
  94. case 'o':
  95. value = newmode & S_IRWXO;
  96. common:
  97. if (set->cmd2 & CMD2_CLR) {
  98. clrval =
  99. (set->cmd2 & CMD2_SET) ? S_IRWXO : value;
  100. if (set->cmd2 & CMD2_UBITS)
  101. newmode &= ~((clrval<<6) & set->bits);
  102. if (set->cmd2 & CMD2_GBITS)
  103. newmode &= ~((clrval<<3) & set->bits);
  104. if (set->cmd2 & CMD2_OBITS)
  105. newmode &= ~(clrval & set->bits);
  106. }
  107. if (set->cmd2 & CMD2_SET) {
  108. if (set->cmd2 & CMD2_UBITS)
  109. newmode |= (value<<6) & set->bits;
  110. if (set->cmd2 & CMD2_GBITS)
  111. newmode |= (value<<3) & set->bits;
  112. if (set->cmd2 & CMD2_OBITS)
  113. newmode |= value & set->bits;
  114. }
  115. break;
  116. case '+':
  117. newmode |= set->bits;
  118. break;
  119. case '-':
  120. newmode &= ~set->bits;
  121. break;
  122. case 'X':
  123. if (omode & (S_IFDIR|S_IXUSR|S_IXGRP|S_IXOTH))
  124. newmode |= set->bits;
  125. break;
  126. case '\0':
  127. default:
  128. #ifdef SETMODE_DEBUG
  129. (void)printf("getmode:%04o -> %04o\n", omode, newmode);
  130. #endif
  131. return (newmode);
  132. }
  133. }
  134. #define notoktomul(a, b) ((a) && (b) && (SIZE_MAX / (a) < (b)))
  135. #define ADDCMD(a, b, c, d) \
  136. if (set >= endset) { \
  137. BITCMD *newset; \
  138. setlen += SET_LEN_INCR; \
  139. if (notoktomul(setlen, sizeof(BITCMD)) || \
  140. (newset = realloc(saveset, setlen * \
  141. sizeof(BITCMD))) == NULL) { \
  142. free(saveset); \
  143. return (NULL); \
  144. } \
  145. set = newset + (set - saveset); \
  146. saveset = newset; \
  147. endset = newset + (setlen - 2); \
  148. } \
  149. set = addcmd(set, (a), (b), (c), (d))
  150. #define STANDARD_BITS (S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO)
  151. void *
  152. setmode(const char *p)
  153. {
  154. int perm, who;
  155. char op, *ep;
  156. BITCMD *set, *saveset, *endset;
  157. sigset_t signset, sigoset;
  158. mode_t mask;
  159. int equalopdone = 0, permXbits, setlen;
  160. u_long perml;
  161. if (!*p)
  162. return (NULL);
  163. /*
  164. * Get a copy of the mask for the permissions that are mask relative.
  165. * Flip the bits, we want what's not set. Since it's possible that
  166. * the caller is opening files inside a signal handler, protect them
  167. * as best we can.
  168. */
  169. sigfillset(&signset);
  170. (void)sigprocmask(SIG_BLOCK, &signset, &sigoset);
  171. (void)umask(mask = umask(0));
  172. mask = ~mask;
  173. (void)sigprocmask(SIG_SETMASK, &sigoset, NULL);
  174. setlen = SET_LEN + 2;
  175. if (notoktomul(setlen, sizeof(BITCMD)) ||
  176. (set = malloc(setlen * sizeof(BITCMD))) == NULL)
  177. return (NULL);
  178. saveset = set;
  179. endset = set + (setlen - 2);
  180. /*
  181. * If an absolute number, get it and return; disallow non-octal digits
  182. * or illegal bits.
  183. */
  184. if (isdigit((unsigned char)*p)) {
  185. perml = strtoul(p, &ep, 8);
  186. /* The test on perml will also catch overflow. */
  187. if (*ep != '\0' || (perml & ~(STANDARD_BITS|S_ISTXT))) {
  188. free(saveset);
  189. errno = ERANGE;
  190. return (NULL);
  191. }
  192. perm = (mode_t)perml;
  193. ADDCMD('=', (STANDARD_BITS|S_ISTXT), perm, mask);
  194. set->cmd = 0;
  195. return (saveset);
  196. }
  197. /*
  198. * Build list of structures to set/clear/copy bits as described by
  199. * each clause of the symbolic mode.
  200. */
  201. for (;;) {
  202. /* First, find out which bits might be modified. */
  203. for (who = 0;; ++p) {
  204. switch (*p) {
  205. case 'a':
  206. who |= STANDARD_BITS;
  207. break;
  208. case 'u':
  209. who |= S_ISUID|S_IRWXU;
  210. break;
  211. case 'g':
  212. who |= S_ISGID|S_IRWXG;
  213. break;
  214. case 'o':
  215. who |= S_IRWXO;
  216. break;
  217. default:
  218. goto getop;
  219. }
  220. }
  221. getop:
  222. if ((op = *p++) != '+' && op != '-' && op != '=') {
  223. free(saveset);
  224. return (NULL);
  225. }
  226. if (op == '=')
  227. equalopdone = 0;
  228. who &= ~S_ISTXT;
  229. for (perm = 0, permXbits = 0;; ++p) {
  230. switch (*p) {
  231. case 'r':
  232. perm |= S_IRUSR|S_IRGRP|S_IROTH;
  233. break;
  234. case 's':
  235. /*
  236. * If specific bits where requested and
  237. * only "other" bits ignore set-id.
  238. */
  239. if (who == 0 || (who & ~S_IRWXO))
  240. perm |= S_ISUID|S_ISGID;
  241. break;
  242. case 't':
  243. /*
  244. * If specific bits where requested and
  245. * only "other" bits ignore sticky.
  246. */
  247. if (who == 0 || (who & ~S_IRWXO)) {
  248. who |= S_ISTXT;
  249. perm |= S_ISTXT;
  250. }
  251. break;
  252. case 'w':
  253. perm |= S_IWUSR|S_IWGRP|S_IWOTH;
  254. break;
  255. case 'X':
  256. permXbits = S_IXUSR|S_IXGRP|S_IXOTH;
  257. break;
  258. case 'x':
  259. perm |= S_IXUSR|S_IXGRP|S_IXOTH;
  260. break;
  261. case 'u':
  262. case 'g':
  263. case 'o':
  264. /*
  265. * When ever we hit 'u', 'g', or 'o', we have
  266. * to flush out any partial mode that we have,
  267. * and then do the copying of the mode bits.
  268. */
  269. if (perm) {
  270. ADDCMD(op, who, perm, mask);
  271. perm = 0;
  272. }
  273. if (op == '=')
  274. equalopdone = 1;
  275. if (op == '+' && permXbits) {
  276. ADDCMD('X', who, permXbits, mask);
  277. permXbits = 0;
  278. }
  279. ADDCMD(*p, who, op, mask);
  280. break;
  281. default:
  282. /*
  283. * Add any permissions that we haven't already
  284. * done.
  285. */
  286. if (perm || (op == '=' && !equalopdone)) {
  287. if (op == '=')
  288. equalopdone = 1;
  289. ADDCMD(op, who, perm, mask);
  290. perm = 0;
  291. }
  292. if (permXbits) {
  293. ADDCMD('X', who, permXbits, mask);
  294. permXbits = 0;
  295. }
  296. goto apply;
  297. }
  298. }
  299. apply:
  300. if (!*p)
  301. break;
  302. if (*p != ',')
  303. goto getop;
  304. ++p;
  305. }
  306. set->cmd = 0;
  307. #ifdef SETMODE_DEBUG
  308. (void)printf("Before compress_mode()\n");
  309. dumpmode(saveset);
  310. #endif
  311. compress_mode(saveset);
  312. #ifdef SETMODE_DEBUG
  313. (void)printf("After compress_mode()\n");
  314. dumpmode(saveset);
  315. #endif
  316. return (saveset);
  317. }
  318. static BITCMD *
  319. addcmd(BITCMD *set, int op, int who, int oparg, u_int mask)
  320. {
  321. switch (op) {
  322. case '=':
  323. set->cmd = '-';
  324. set->bits = who ? who : STANDARD_BITS;
  325. set++;
  326. op = '+';
  327. /* FALLTHROUGH */
  328. case '+':
  329. case '-':
  330. case 'X':
  331. set->cmd = op;
  332. set->bits = (who ? who : (int)mask) & oparg;
  333. break;
  334. case 'u':
  335. case 'g':
  336. case 'o':
  337. set->cmd = op;
  338. if (who) {
  339. set->cmd2 = ((who & S_IRUSR) ? CMD2_UBITS : 0) |
  340. ((who & S_IRGRP) ? CMD2_GBITS : 0) |
  341. ((who & S_IROTH) ? CMD2_OBITS : 0);
  342. set->bits = (mode_t)~0;
  343. } else {
  344. set->cmd2 = CMD2_UBITS | CMD2_GBITS | CMD2_OBITS;
  345. set->bits = mask;
  346. }
  347. if (oparg == '+')
  348. set->cmd2 |= CMD2_SET;
  349. else if (oparg == '-')
  350. set->cmd2 |= CMD2_CLR;
  351. else if (oparg == '=')
  352. set->cmd2 |= CMD2_SET|CMD2_CLR;
  353. break;
  354. }
  355. return (set + 1);
  356. }
  357. #ifdef SETMODE_DEBUG
  358. static void
  359. dumpmode(BITCMD *set)
  360. {
  361. for (; set->cmd; ++set)
  362. (void)printf("cmd: '%c' bits %04o%s%s%s%s%s%s\n",
  363. set->cmd, set->bits, set->cmd2 ? " cmd2:" : "",
  364. set->cmd2 & CMD2_CLR ? " CLR" : "",
  365. set->cmd2 & CMD2_SET ? " SET" : "",
  366. set->cmd2 & CMD2_UBITS ? " UBITS" : "",
  367. set->cmd2 & CMD2_GBITS ? " GBITS" : "",
  368. set->cmd2 & CMD2_OBITS ? " OBITS" : "");
  369. }
  370. #endif
  371. /*
  372. * Given an array of bitcmd structures, compress by compacting consecutive
  373. * '+', '-' and 'X' commands into at most 3 commands, one of each. The 'u',
  374. * 'g' and 'o' commands continue to be separate. They could probably be
  375. * compacted, but it's not worth the effort.
  376. */
  377. static void
  378. compress_mode(BITCMD *set)
  379. {
  380. BITCMD *nset;
  381. int setbits, clrbits, Xbits, op;
  382. for (nset = set;;) {
  383. /* Copy over any 'u', 'g' and 'o' commands. */
  384. while ((op = nset->cmd) != '+' && op != '-' && op != 'X') {
  385. *set++ = *nset++;
  386. if (!op)
  387. return;
  388. }
  389. for (setbits = clrbits = Xbits = 0;; nset++) {
  390. if ((op = nset->cmd) == '-') {
  391. clrbits |= nset->bits;
  392. setbits &= ~nset->bits;
  393. Xbits &= ~nset->bits;
  394. } else if (op == '+') {
  395. setbits |= nset->bits;
  396. clrbits &= ~nset->bits;
  397. Xbits &= ~nset->bits;
  398. } else if (op == 'X')
  399. Xbits |= nset->bits & ~setbits;
  400. else
  401. break;
  402. }
  403. if (clrbits) {
  404. set->cmd = '-';
  405. set->cmd2 = 0;
  406. set->bits = clrbits;
  407. set++;
  408. }
  409. if (setbits) {
  410. set->cmd = '+';
  411. set->cmd2 = 0;
  412. set->bits = setbits;
  413. set++;
  414. }
  415. if (Xbits) {
  416. set->cmd = 'X';
  417. set->cmd2 = 0;
  418. set->bits = Xbits;
  419. set++;
  420. }
  421. }
  422. }