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