1 /*
2   Copyright 2021 Northern.tech AS
3 
4   This file is part of CFEngine 3 - written and maintained by Northern.tech AS.
5 
6   This program is free software; you can redistribute it and/or modify it
7   under the terms of the GNU General Public License as published by the
8   Free Software Foundation; version 3.
9 
10   This program is distributed in the hope that it will be useful,
11   but WITHOUT ANY WARRANTY; without even the implied warranty of
12   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13   GNU General Public License for more details.
14 
15   You should have received a copy of the GNU General Public License
16   along with this program; if not, write to the Free Software
17   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA
18 
19   To the extent this program is licensed as part of the Enterprise
20   versions of CFEngine, the applicable Commercial Open Source License
21   (COSL) may apply to this file if you as a licensee so wish it. See
22   included file COSL.txt.
23 */
24 
25 #include <cf3.defs.h>
26 
27 /***************************************************************/
28 
29 enum modestate
30 {
31     wild,
32     who,
33     which
34 };
35 
36 enum modesort
37 {
38     unknown,
39     numeric,
40     symbolic
41 };
42 
43 /*******************************************************************/
44 
45 static bool CheckModeState(enum modestate stateA, enum modestate stateB, enum modesort modeA, enum modesort modeB,
46                           char ch);
47 static bool SetModeMask(char action, int value, int affected, mode_t *p, mode_t *m);
48 
49 /***************************************************************/
50 
ParseModeString(const char * modestring,mode_t * plusmask,mode_t * minusmask)51 bool ParseModeString(const char *modestring, mode_t *plusmask, mode_t *minusmask)
52 {
53     int affected = 0, value = 0, gotaction;
54     bool no_error = true;
55     char action = '=';
56     enum modestate state = wild;
57     enum modesort found_sort = unknown; /* Already found "sort" of mode */
58     enum modesort sort = unknown;       /* Sort of started but not yet finished mode */
59 
60     *plusmask = *minusmask = 0;
61 
62     if (modestring == NULL)
63     {
64         return true;
65     }
66 
67     gotaction = false;
68 
69     for (const char *sp = modestring; true; sp++)
70     {
71         switch (*sp)
72         {
73         case 'a':
74             no_error = CheckModeState(who, state, symbolic, sort, *sp);
75             affected |= 07777;
76             sort = symbolic;
77             break;
78 
79         case 'u':
80             no_error = CheckModeState(who, state, symbolic, sort, *sp);
81             affected |= 04700;
82             sort = symbolic;
83             break;
84 
85         case 'g':
86             no_error = CheckModeState(who, state, symbolic, sort, *sp);
87             affected |= 02070;
88             sort = symbolic;
89             break;
90 
91         case 'o':
92             no_error = CheckModeState(who, state, symbolic, sort, *sp);
93             affected |= 00007;
94             sort = symbolic;
95             break;
96 
97         case '+':
98         case '-':
99         case '=':
100             if (gotaction)
101             {
102                 Log(LOG_LEVEL_ERR, "Too many +-= in mode string");
103                 return false;
104             }
105 
106             no_error = CheckModeState(who, state, symbolic, sort, *sp);
107             action = *sp;
108             state = which;
109             gotaction = true;
110             sort = unknown;
111             break;
112 
113         case 'r':
114             no_error = CheckModeState(which, state, symbolic, sort, *sp);
115             value |= 0444 & affected;
116             sort = symbolic;
117             break;
118 
119         case 'w':
120             no_error = CheckModeState(which, state, symbolic, sort, *sp);
121             value |= 0222 & affected;
122             sort = symbolic;
123             break;
124 
125         case 'x':
126             no_error = CheckModeState(which, state, symbolic, sort, *sp);
127             value |= 0111 & affected;
128             sort = symbolic;
129             break;
130 
131         case 's':
132             no_error = CheckModeState(which, state, symbolic, sort, *sp);
133             value |= 06000 & affected;
134             sort = symbolic;
135             break;
136 
137         case 't':
138             no_error = CheckModeState(which, state, symbolic, sort, *sp);
139             value |= 01000;
140             sort = symbolic;
141             break;
142 
143         case '0':
144         case '1':
145         case '2':
146         case '3':
147         case '4':
148         case '5':
149         case '6':
150         case '7':
151             no_error = CheckModeState(which, state, numeric, sort, *sp);
152             sort = numeric;
153             gotaction = true;
154             state = which;
155             affected = 07777;   /* TODO: Hard-coded; see below */
156             sscanf(sp, "%o", &value);
157 
158             if (value & S_IFMT)
159             {
160                 Log(LOG_LEVEL_INFO, "Mode-Value is not entirely within the system's allowed permissions (octal %o) and will be filtered accordingly : %s",
161                     S_IFMT, modestring);
162             }
163 
164             /* stat() returns the file types in the mode, but they
165              * can't be set.  So we clear the file-type as per POSIX
166              * 2001 instead of erroring out, leaving just the
167              * permissions. */
168             value &= ~S_IFMT;
169 
170             if (value > 07777)  /* TODO: Hardcoded !
171                                    Is this correct for all sorts of Unix ?
172                                    What about NT ?
173                                    Any (POSIX)-constants ??
174                                  */
175             {
176                 Log(LOG_LEVEL_ERR, "Mode-Value too big : %s", modestring);
177                 return false;
178             }
179 
180             while ((isdigit((int) *sp)) && (*sp != '\0'))
181             {
182                 sp++;
183             }
184             sp--;
185             break;
186 
187         case ',':
188             if (!SetModeMask(action, value, affected, plusmask, minusmask))
189             {
190                 return false;
191             }
192 
193             if ((found_sort != unknown) && (found_sort != sort))
194             {
195                 Log(LOG_LEVEL_INFO, "Symbolic and numeric form for modes mixed");
196             }
197 
198             found_sort = sort;
199             sort = unknown;
200             action = '=';
201             affected = 0;
202             value = 0;
203             gotaction = false;
204             state = who;
205             break;
206 
207         case '\0':
208             if ((state == who) || (value == 0))
209             {
210                 if ((strcmp(modestring, "0000") != 0) && (strcmp(modestring, "000") != 0))
211                 {
212                     Log(LOG_LEVEL_ERR, "mode string is incomplete");
213                     return false;
214                 }
215             }
216 
217             if (!SetModeMask(action, value, affected, plusmask, minusmask))
218             {
219                 return false;
220             }
221 
222             if ((found_sort != unknown) && (found_sort != sort))
223             {
224                 Log(LOG_LEVEL_INFO, "Symbolic and numeric form for modes mixed");
225             }
226 
227             Log(LOG_LEVEL_DEBUG,
228                 "Modestring [PLUS = %jo] [MINUS = %jo]",
229                 (uintmax_t) *plusmask, (uintmax_t) *minusmask);
230             return true;
231 
232         default:
233             Log(LOG_LEVEL_ERR, "Invalid mode string (%s)", modestring);
234             return false;
235         }
236     }
237 
238     if (!no_error)
239     {
240         Log(LOG_LEVEL_ERR, "Error validating mode string %s", modestring);
241     }
242 
243     return no_error;
244 }
245 
246 /*********************************************************/
247 
CheckModeState(enum modestate stateA,enum modestate stateB,enum modesort sortA,enum modesort sortB,char ch)248 static bool CheckModeState(enum modestate stateA, enum modestate stateB, enum modesort sortA, enum modesort sortB,
249                           char ch)
250 {
251     if ((stateA != wild) && (stateB != wild) && (stateA != stateB))
252     {
253         Log(LOG_LEVEL_ERR, "Mode string constant (%c) used out of context", ch);
254         return false;
255     }
256 
257     if ((sortA != unknown) && (sortB != unknown) && (sortA != sortB))
258     {
259         Log(LOG_LEVEL_ERR, "Symbolic and numeric filemodes mixed within expression");
260         return false;
261     }
262 
263     return true;
264 }
265 
266 /*********************************************************/
267 
SetModeMask(char action,int value,int affected,mode_t * p,mode_t * m)268 static bool SetModeMask(char action, int value, int affected, mode_t *p, mode_t *m)
269 {
270     switch (action)
271     {
272     case '+':
273         *p |= value;
274         *m |= 0;
275         return true;
276     case '-':
277         *p |= 0;
278         *m |= value;
279         return true;
280     case '=':
281         *p |= value;
282         *m |= ((~value) & 07777 & affected);
283         return true;
284     default:
285         Log(LOG_LEVEL_ERR, "Mode directive %c is unknown", action);
286         return false;
287     }
288 }
289