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