1 /* @(#)getperm.c	1.7 18/08/30 Copyright 2004-2018 J. Schilling */
2 #include <schily/mconfig.h>
3 #ifndef lint
4 static	UConst char sccsid[] =
5 	"@(#)getperm.c	1.7 18/08/30 Copyright 2004-2018 J. Schilling";
6 #endif
7 /*
8  *	Parser for chmod(1)/find(1)/umask(1)-perm, ....
9  *
10  *	Copyright (c) 2004-2018 J. Schilling
11  */
12 /*
13  * The contents of this file are subject to the terms of the
14  * Common Development and Distribution License, Version 1.0 only
15  * (the "License").  You may not use this file except in compliance
16  * with the License.
17  *
18  * See the file CDDL.Schily.txt in this distribution for details.
19  * A copy of the CDDL is also available via the Internet at
20  * http://www.opensource.org/licenses/cddl1.txt
21  *
22  * When distributing Covered Code, include this CDDL HEADER in each
23  * file and include the License file CDDL.Schily.txt from this distribution.
24  */
25 
26 #include <schily/stdio.h>
27 #include <schily/stat.h>
28 #include <schily/utypes.h>
29 #include <schily/standard.h>
30 #include <schily/schily.h>
31 
32 #include <schily/nlsdefs.h>
33 
34 EXPORT	int	getperm		__PR((FILE *f, char *perm, char *opname,
35 					mode_t *modep, int smode, int flag));
36 LOCAL	char	*getsperm	__PR((FILE *f, char *p, char *opname,
37 					mode_t *mp, int smode, int flag));
38 LOCAL	mode_t	iswho		__PR((int c));
39 LOCAL	int	isop		__PR((int c));
40 LOCAL	mode_t	isperm		__PR((int c, int isX));
41 
42 /*
43  * This is the mode bit translation code stolen from star...
44  */
45 #define	TSUID		04000	/* Set UID on execution */
46 #define	TSGID		02000	/* Set GID on execution */
47 #define	TSVTX		01000	/* On directories, restricted deletion flag */
48 #define	TUREAD		00400	/* Read by owner */
49 #define	TUWRITE		00200	/* Write by owner special */
50 #define	TUEXEC		00100	/* Execute/search by owner */
51 #define	TGREAD		00040	/* Read by group */
52 #define	TGWRITE		00020	/* Write by group */
53 #define	TGEXEC		00010	/* Execute/search by group */
54 #define	TOREAD		00004	/* Read by other */
55 #define	TOWRITE		00002	/* Write by other */
56 #define	TOEXEC		00001	/* Execute/search by other */
57 
58 #define	TALLMODES	07777	/* The low 12 bits mentioned in the standard */
59 
60 #define	S_ALLPERM	(S_IRWXU|S_IRWXG|S_IRWXO)
61 #define	S_ALLFLAGS	(S_ISUID|S_ISGID|S_ISVTX)
62 #define	S_ALLMODES	(S_ALLFLAGS | S_ALLPERM)
63 
64 #if	S_ISUID == TSUID && S_ISGID == TSGID && S_ISVTX == TSVTX && \
65 	S_IRUSR == TUREAD && S_IWUSR == TUWRITE && S_IXUSR == TUEXEC && \
66 	S_IRGRP == TGREAD && S_IWGRP == TGWRITE && S_IXGRP == TGEXEC && \
67 	S_IROTH == TOREAD && S_IWOTH == TOWRITE && S_IXOTH == TOEXEC
68 
69 #define	HAVE_POSIX_MODE_BITS	/* st_mode bits are equal to TAR mode bits */
70 #endif
71 
72 #ifdef	HAVE_POSIX_MODE_BITS	/* st_mode bits are equal to TAR mode bits */
73 #define	OSMODE(xmode)	    (xmode)
74 #else
75 #define	OSMODE(xmode)	    ((xmode & TSUID   ? S_ISUID : 0)  \
76 			    | (xmode & TSGID   ? S_ISGID : 0) \
77 			    | (xmode & TSVTX   ? S_ISVTX : 0) \
78 			    | (xmode & TUREAD  ? S_IRUSR : 0) \
79 			    | (xmode & TUWRITE ? S_IWUSR : 0) \
80 			    | (xmode & TUEXEC  ? S_IXUSR : 0) \
81 			    | (xmode & TGREAD  ? S_IRGRP : 0) \
82 			    | (xmode & TGWRITE ? S_IWGRP : 0) \
83 			    | (xmode & TGEXEC  ? S_IXGRP : 0) \
84 			    | (xmode & TOREAD  ? S_IROTH : 0) \
85 			    | (xmode & TOWRITE ? S_IWOTH : 0) \
86 			    | (xmode & TOEXEC  ? S_IXOTH : 0))
87 #endif
88 
89 EXPORT int
getperm(f,perm,opname,modep,smode,flag)90 getperm(f, perm, opname, modep, smode, flag)
91 	FILE	*f;			/* FILE * to print error messages to */
92 	char	*perm;			/* Perm string to parse		    */
93 	char	*opname;		/* find(1) operator name / NULL	    */
94 	mode_t	*modep;			/* The mode result		    */
95 	int	smode;			/* The start mode for the computation */
96 	int	flag;			/* Flags, see getperm() flag defs */
97 {
98 	char	*p;
99 	Llong	ll;
100 	mode_t	mm;
101 
102 	p = perm;
103 	if (p == NULL)
104 		return (-1);
105 
106 	if ((flag & GP_FPERM) && *p == '-')
107 		p++;
108 
109 	if (*p >= '0' && *p <= '7') {
110 		p = astollb(p, &ll, 8);
111 		if (*p) {
112 			if (f) {
113 				if (opname) {
114 					ferrmsgno(f, EX_BAD,
115 					gettext("Non octal character '%c' in '-%s %s'.\n"),
116 					*p, opname, perm);
117 				} else {
118 					ferrmsgno(f, EX_BAD,
119 					gettext("Non octal character '%c' in '%s'.\n"),
120 					*p, perm);
121 				}
122 			}
123 			return (-1);
124 		}
125 		mm = ll & TALLMODES;
126 		*modep = OSMODE(mm);
127 		return (0);
128 	}
129 	p = getsperm(f, p, opname, modep, smode, flag);
130 	if (p == NULL) {
131 		return (-1);
132 	} else if (*p != '\0') {
133 		if (f) {
134 			if (opname) {
135 				ferrmsgno(f, EX_BAD,
136 				gettext("Bad perm character '%c' found in '-%s %s'.\n"),
137 					*p, opname, perm);
138 			} else {
139 				ferrmsgno(f, EX_BAD,
140 				gettext("Bad perm character '%c' found in '%s'.\n"),
141 					*p, perm);
142 			}
143 		}
144 		return (-1);
145 	}
146 #ifdef	PERM_DEBUG
147 	error("GETPERM (%s) %4.4lo\n", perm, (long)*modep);
148 #endif
149 	return (0);
150 }
151 
152 LOCAL char *
getsperm(f,p,opname,mp,smode,flag)153 getsperm(f, p, opname, mp, smode, flag)
154 	FILE	*f;
155 	char	*p;		/* The perm input string		*/
156 	char	*opname;	/* find(1) operator name / NULL	    */
157 	mode_t	*mp;		/* To set the mode			*/
158 	int	smode;		/* The start mode for the computation	*/
159 	int	flag;		/* Flags, see getperm() flag defs	*/
160 {
161 #ifdef	OLD
162 	mode_t	permval = 0;	/* POSIX start value for "find" */
163 #else
164 	mode_t	permval = smode;	/* POSIX start value for "find" */
165 #endif
166 	mode_t	who;
167 	mode_t	who_mask = 0;	/* Useless init to calm down silly GCC */
168 	mode_t	m;
169 	int	op;
170 	mode_t	perms;
171 	mode_t	pm;
172 
173 nextclause:
174 #ifdef	PERM_DEBUG
175 	error("getsperm(%s, %o)\n", p, smode);
176 #endif
177 	who = 0;
178 	while ((m = iswho(*p)) != 0) {
179 		p++;
180 		who_mask = who |= m;
181 	}
182 	if (who == 0) {
183 		mode_t	mask;
184 
185 		/*
186 		 * If "who" has not been specified, select all bits for umask(1)
187 		 * or all reduced by umask(2) in the general case.
188 		 */
189 		if (flag & GP_UMASK) {
190 			who_mask =
191 			who = (S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO);
192 		} else {
193 			umask(mask = umask(0));
194 			who = ~mask;
195 			who &=
196 			    (S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO);
197 			who_mask =
198 			    (S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO);
199 		}
200 	}
201 #ifdef	PERM_DEBUG
202 	error("WHO %4.4lo\n", (long)who);
203 	error("getsperm(--->%s)\n", p);
204 #endif
205 
206 nextop:
207 	if ((op = isop(*p)) != '\0')
208 		p++;
209 #ifdef	PERM_DEBUG
210 	error("OP '%c'\n", op);
211 	error("getsperm(--->%s)\n", p);
212 #endif
213 	if (op == 0) {
214 		if (f) {
215 			if (opname) {
216 				ferrmsgno(f, EX_BAD,
217 				    gettext("Missing -%s op.\n"), opname);
218 			} else {
219 				ferrmsgno(f, EX_BAD,
220 				    gettext("Missing op in perm string.\n"));
221 			}
222 		}
223 		if (*p == '\0')
224 			return (NULL);
225 		return (p);
226 	}
227 
228 	flag &= ~(GP_FPERM|GP_UMASK);
229 	if (flag & GP_XERR)
230 		flag = -1;
231 	perms = 0;
232 	while ((pm = isperm(*p, flag)) != (mode_t)-1) {
233 		p++;
234 		perms |= pm;
235 	}
236 #ifdef	PERM_DEBUG
237 	error("PERM %4.4lo\n", (long)perms);
238 	error("START PERMVAL %4.4lo\n", (long)permval);
239 #endif
240 	if ((perms == 0) && (*p == 'u' || *p == 'g' || *p == 'o')) {
241 		mode_t	what = 0;
242 
243 		/*
244 		 * First select the bit triple we like to copy.
245 		 */
246 		switch (*p) {
247 
248 		case 'u':
249 			what = permval & S_IRWXU;
250 			break;
251 		case 'g':
252 			what = permval & S_IRWXG;
253 			break;
254 		case 'o':
255 			what = permval & S_IRWXO;
256 			break;
257 		}
258 		/*
259 		 * Now copy over bit by bit without relying on shifts
260 		 * that would make implicit assumptions on values.
261 		 */
262 		if (what & (S_IRUSR|S_IRGRP|S_IROTH))
263 			perms |= (who & (S_IRUSR|S_IRGRP|S_IROTH));
264 		if (what & (S_IWUSR|S_IWGRP|S_IWOTH))
265 			perms |= (who & (S_IWUSR|S_IWGRP|S_IWOTH));
266 		if (what & (S_IXUSR|S_IXGRP|S_IXOTH))
267 			perms |= (who & (S_IXUSR|S_IXGRP|S_IXOTH));
268 		p++;
269 #ifdef	PERM_DEBUG
270 		error("PERM %4.4lo (copy) WHAT %lo WHO %lo\n",
271 			(long)perms, (long)what, (long)who);
272 #endif
273 	}
274 #ifdef	PERM_DEBUG
275 	error("getsperm(--->%s)\n", p);
276 	error("PERMVAL %lo WHO %lo WHO_MASK %lo PERM %lo OP '%c'\n",
277 		(long)permval, (long)who, (long)who_mask, (long)perms, op);
278 #endif
279 	switch (op) {
280 
281 	case '=':
282 		permval &= ~who_mask;
283 		/* FALLTHRU */
284 	case '+':
285 		permval |= (who & perms);
286 		break;
287 
288 	case '-':
289 		permval &= ~(who & perms);
290 		break;
291 	}
292 #ifdef	PERM_DEBUG
293 	error("END PERMVAL %4.4lo\n", (long)permval);
294 #endif
295 	if (isop(*p))
296 		goto nextop;
297 	if (*p == ',') {
298 		p++;
299 		goto nextclause;
300 	}
301 	*mp = permval;
302 	return (p);
303 }
304 
305 LOCAL mode_t
iswho(c)306 iswho(c)
307 	int	c;
308 {
309 	switch (c) {
310 
311 	case 'u':					/* user */
312 		return (S_ISUID|S_ISVTX|S_IRWXU);
313 	case 'g':					/* group */
314 		return (S_ISGID|S_ISVTX|S_IRWXG);
315 	case 'o':					/* other */
316 		return (S_ISVTX|S_IRWXO);
317 	case 'a':					/* all */
318 		return (S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO);
319 	default:
320 		return (0);
321 	}
322 }
323 
324 LOCAL int
isop(c)325 isop(c)
326 	int	c;		/* The current perm character to parse	*/
327 {
328 	switch (c) {
329 
330 	case '+':
331 	case '-':
332 	case '=':
333 		return (c);
334 	default:
335 		return (0);
336 	}
337 }
338 
339 LOCAL mode_t
isperm(c,isX)340 isperm(c, isX)
341 	int	c;		/* The current perm character to parse	*/
342 	int	isX;		/* -1: Ignore X, 0: no dir/X 1: X OK	*/
343 {
344 	switch (c) {
345 
346 	case 'r':
347 		return (S_IRUSR|S_IRGRP|S_IROTH);
348 	case 'w':
349 		return (S_IWUSR|S_IWGRP|S_IWOTH);
350 	case 'X':
351 		if (isX < 0)
352 			return ((mode_t)-1);	/* Singnal parse error	*/
353 		if (isX == 0)
354 			return ((mode_t)0);	/* No 'X' handling here	*/
355 		/* FALLTHRU */
356 	case 'x':
357 		return (S_IXUSR|S_IXGRP|S_IXOTH);
358 	case 's':
359 		return (S_ISUID|S_ISGID);
360 	case 'l':
361 		return (S_ISGID);
362 	case 't':
363 		return (S_ISVTX);
364 	default:
365 		return ((mode_t)-1);
366 	}
367 }
368