1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License"). You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22 /*
23 * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 */
26
27 /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
28 /* All Rights Reserved */
29
30 /*
31 * Portions of this source code were derived from Berkeley 4.3 BSD
32 * under license from the Regents of the University of California.
33 */
34
35 /*
36 * Use of this object by a utility (so far chmod, mkdir and mkfifo use
37 * it) requires that the utility implement an error-processing routine
38 * named errmsg(), with a prototype as specified below.
39 *
40 * This is necessary because the mode-parsing code here makes use of such
41 * a routine, located in chmod.c. The error-reporting style of the
42 * utilities sharing this code differs enough that it is difficult to
43 * implement a common version of this routine to be used by all.
44 */
45
46 /*
47 * Note that many convolutions are necessary
48 * due to the re-use of bits between locking
49 * and setgid
50 */
51
52 #include <ctype.h>
53 #include <stdio.h>
54 #include <sys/types.h>
55 #include <sys/stat.h>
56 #include <dirent.h>
57 #include <locale.h>
58 #include <string.h> /* strerror() */
59 #include <stdarg.h>
60 #include "msgs.h"
61
62 #define USER 05700 /* user's bits */
63 #define GROUP 02070 /* group's bits */
64 #define OTHER 00007 /* other's bits */
65 #define ALL 07777 /* all */
66
67 #define READ 00444 /* read permit */
68 #define WRITE 00222 /* write permit */
69 #define EXEC 00111 /* exec permit */
70 #define SETID 06000 /* set[ug]id */
71 #define LOCK 02000 /* lock permit */
72 #define STICKY 01000 /* sticky bit */
73
74 #define GROUP_RWX (GROUP & (READ | WRITE | EXEC))
75
76 #define WHO_EMPTY 0
77
78 static char *msp;
79
80 static int what(void);
81
82 static mode_t abs(mode_t, o_mode_t *, o_mode_t *);
83 static mode_t who(void);
84
85 mode_t
86 newmode_common(char *ms, mode_t new_mode, mode_t umsk, char *file, char *path,
87 o_mode_t *group_clear_bits, o_mode_t *group_set_bits);
88
89 /*
90 * Wrapper for newmode_common. This function is called by mkdir and
91 * mkfifo.
92 */
93 mode_t
newmode(char * ms,mode_t new_mode,mode_t umsk,char * file,char * path)94 newmode(char *ms, mode_t new_mode, mode_t umsk, char *file, char *path)
95 {
96 o_mode_t tmp1, tmp2;
97
98 return (newmode_common(ms, new_mode, umsk, file, path, &tmp1, &tmp2));
99 }
100
101 /*
102 * We are parsing a comma-separated list of mode expressions of the form:
103 *
104 * [<who>] <op> [<perms>]
105 */
106
107 /* ARGSUSED */
108 mode_t
newmode_common(char * ms,mode_t new_mode,mode_t umsk,char * file,char * path,o_mode_t * group_clear_bits,o_mode_t * group_set_bits)109 newmode_common(char *ms, mode_t new_mode, mode_t umsk, char *file, char *path,
110 o_mode_t *group_clear_bits, o_mode_t *group_set_bits)
111 {
112 /*
113 * new_mode contains the mode value constructed by parsing the
114 * expression pointed to by ms
115 * old_mode contains the mode provided by the caller
116 * oper contains +|-|= information
117 * perms_msk contains rwx(slt) information
118 * umsk contains the umask value to be assumed.
119 * who_empty is non-zero if the <who> clause did not appear.
120 * who_msk contains USER|GROUP|OTHER information
121 */
122
123 int oper; /* <op> */
124 int lcheck;
125 int scheck;
126 int xcheck;
127 int goon;
128
129 int operand_empty = 0;
130 int who_empty;
131
132 mode_t who_msk;
133 mode_t perms_msk;
134 mode_t old_mode = new_mode; /* save original mode */
135 mode_t grp_change;
136
137 msp = ms;
138
139 *group_clear_bits = 0;
140 *group_set_bits = 0;
141
142 if (isdigit(*msp))
143 return (abs(old_mode, group_clear_bits, group_set_bits));
144
145 do {
146 /*
147 * When <who> is empty, and <oper> == `=`, the umask is
148 * obeyed. So we need to make note of it here, for use
149 * later.
150 */
151
152 if ((who_msk = who()) == WHO_EMPTY) {
153 who_empty = 1;
154 who_msk = ALL;
155 } else {
156 who_empty = 0;
157 }
158
159 while (oper = what()) {
160 /*
161 * this section processes permissions
162 */
163
164 operand_empty++;
165 perms_msk = 0;
166 goon = 0;
167 lcheck = scheck = xcheck = 0;
168
169 switch (*msp) {
170 case 'u':
171 perms_msk = (new_mode & USER) >> 6;
172 goto dup;
173 case 'g':
174 perms_msk = (new_mode & GROUP) >> 3;
175 goto dup;
176 case 'o':
177 perms_msk = (new_mode & OTHER);
178 dup:
179 perms_msk &= (READ|WRITE|EXEC);
180 perms_msk |= (perms_msk << 3) |
181 (perms_msk << 6);
182 msp++;
183 goon = 1;
184 }
185
186 while (goon == 0) {
187 switch (*msp++) {
188 case 'r':
189 perms_msk |= READ;
190 continue;
191 case 'w':
192 perms_msk |= WRITE;
193 continue;
194 case 'x':
195 perms_msk |= EXEC;
196 xcheck = 1;
197 continue;
198 case 'X':
199 if (((old_mode & S_IFMT) == S_IFDIR) ||
200 (old_mode & EXEC)) {
201 perms_msk |= EXEC;
202 xcheck = 1;
203 }
204 continue;
205 case 'l':
206 perms_msk |= LOCK;
207 who_msk |= LOCK;
208 lcheck = 1;
209 continue;
210 case 's':
211 perms_msk |= SETID;
212 scheck = 1;
213 continue;
214 case 't':
215 perms_msk |= STICKY;
216 continue;
217 default:
218 msp--;
219 goon = 1;
220 }
221 }
222
223 perms_msk &= who_msk;
224
225 switch (oper) {
226 case '+':
227 if (who_empty) {
228 perms_msk &= ~umsk;
229 }
230
231
232 /* is group execution requested? */
233 if (xcheck == 1 &&
234 (perms_msk & GROUP & EXEC) ==
235 (GROUP & EXEC)) {
236 /* not locking, too! */
237 if (lcheck == 1 && !S_ISDIR(new_mode)) {
238 errmsg(1, 3,
239 gettext("Group execution "
240 "and locking not permitted "
241 "together\n"));
242 }
243
244 /*
245 * not if the file is already
246 * lockable.
247 */
248 if (((new_mode & GROUP &
249 (LOCK | EXEC)) == LOCK) &&
250 !S_ISDIR(new_mode)) {
251 errmsg(2, 0,
252 gettext("%s: Group "
253 "execution not permitted "
254 "on a lockable file\n"),
255 path);
256 return (old_mode);
257 }
258 }
259
260 /* is setgid on execution requested? */
261 if (scheck == 1 && (perms_msk & GROUP & SETID)
262 == (GROUP & SETID)) {
263 /* not locking, too! */
264 if (lcheck == 1 &&
265 ((perms_msk & GROUP & EXEC) ==
266 (GROUP & EXEC)) &&
267 !S_ISDIR(new_mode)) {
268 errmsg(1, 4,
269 gettext("Set-group-ID and "
270 "locking not permitted "
271 "together\n"));
272 }
273
274 /*
275 * not if the file is already
276 * lockable
277 */
278
279 if (((new_mode & GROUP &
280 (LOCK | EXEC)) == LOCK) &&
281 !S_ISDIR(new_mode)) {
282 errmsg(2, 0,
283 gettext("%s: Set-group-ID "
284 "not permitted on a "
285 "lockable file\n"), path);
286 return (old_mode);
287 }
288 }
289
290 /* is setid on execution requested? */
291 if ((scheck == 1) &&
292 ((new_mode & S_IFMT) != S_IFDIR)) {
293 /*
294 * the corresponding execution must
295 * be requested or already set
296 */
297 if (((new_mode | perms_msk) &
298 who_msk & EXEC & (USER | GROUP)) !=
299 (who_msk & EXEC & (USER | GROUP))) {
300 errmsg(2, 0,
301 gettext("%s: Execute "
302 "permission required "
303 "for set-ID on "
304 "execution \n"),
305 path);
306 return (old_mode);
307 }
308 }
309
310 /* is locking requested? */
311 if (lcheck == 1) {
312 /*
313 * not if the file has group execution
314 * set.
315 * NOTE: this also covers files with
316 * setgid
317 */
318 if ((new_mode & GROUP & EXEC) ==
319 (GROUP & EXEC) &&
320 !S_ISDIR(new_mode)) {
321 errmsg(2, 0,
322 gettext("%s: Locking not "
323 "permitted on "
324 "a group executable "
325 "file\n"),
326 path);
327 return (old_mode);
328 }
329 }
330
331 if ((grp_change = (perms_msk & GROUP_RWX) >> 3)
332 != 0) {
333 *group_clear_bits &= ~grp_change;
334 *group_set_bits |= grp_change;
335 }
336
337 /* create new mode */
338 new_mode |= perms_msk;
339 break;
340
341 case '-':
342 if (who_empty) {
343 perms_msk &= ~umsk;
344 }
345
346 /* don't turn off locking, unless it's on */
347 if (lcheck == 1 && scheck == 0 &&
348 (new_mode & GROUP & (LOCK | EXEC)) !=
349 LOCK) {
350 perms_msk &= ~LOCK;
351 }
352
353 /* don't turn off setgid, unless it's on */
354 if (scheck == 1 &&
355 ((new_mode & S_IFMT) != S_IFDIR) &&
356 lcheck == 0 &&
357 (new_mode & GROUP & (LOCK | EXEC)) ==
358 LOCK) {
359 perms_msk &= ~(GROUP & SETID);
360 }
361
362 /*
363 * if execution is being turned off and the
364 * corresponding setid is not, turn setid off,
365 * too & warn the user
366 */
367 if (xcheck == 1 && scheck == 0 &&
368 ((who_msk & GROUP) == GROUP ||
369 (who_msk & USER) == USER) &&
370 (new_mode & who_msk & (SETID | EXEC)) ==
371 (who_msk & (SETID | EXEC)) &&
372 !S_ISDIR(new_mode)) {
373 errmsg(2, 0,
374 gettext("%s: Corresponding set-ID "
375 "also disabled on file since "
376 "set-ID requires execute "
377 "permission\n"),
378 path);
379
380 if ((perms_msk & USER & SETID) !=
381 (USER & SETID) && (new_mode &
382 USER & (SETID | EXEC)) ==
383 (who_msk & USER &
384 (SETID | EXEC))) {
385 perms_msk |= USER & SETID;
386 }
387 if ((perms_msk & GROUP & SETID) !=
388 (GROUP & SETID) &&
389 (new_mode & GROUP &
390 (SETID | EXEC)) ==
391 (who_msk & GROUP &
392 (SETID | EXEC))) {
393 perms_msk |= GROUP & SETID;
394 }
395 }
396
397 if ((grp_change = (perms_msk & GROUP_RWX) >> 3)
398 != 0) {
399 *group_clear_bits |= grp_change;
400 *group_set_bits &= ~grp_change;
401 }
402
403 /* create new mode */
404 new_mode &= ~perms_msk;
405 break;
406
407 case '=':
408 if (who_empty) {
409 perms_msk &= ~umsk;
410 }
411 /* is locking requested? */
412 if (lcheck == 1) {
413 /* not group execution, too! */
414 if ((perms_msk & GROUP & EXEC) ==
415 (GROUP & EXEC) &&
416 !S_ISDIR(new_mode)) {
417 errmsg(1, 3,
418 gettext("Group execution "
419 "and locking not "
420 "permitted together\n"));
421 }
422
423 /*
424 * if the file has group execution set,
425 * turn it off!
426 */
427 if ((who_msk & GROUP) != GROUP) {
428 new_mode &= ~(GROUP & EXEC);
429 }
430 }
431
432 /*
433 * is setid on execution requested? the
434 * corresponding execution must be requested,
435 * too!
436 */
437 if (scheck == 1 &&
438 (perms_msk & EXEC & (USER | GROUP)) !=
439 (who_msk & EXEC & (USER | GROUP)) &&
440 !S_ISDIR(new_mode)) {
441 errmsg(1, 2,
442 gettext("Execute permission "
443 "required for set-ID on "
444 "execution\n"));
445 }
446
447 /*
448 * The ISGID bit on directories will not be
449 * changed when the mode argument is a string
450 * with "=".
451 */
452 if ((old_mode & S_IFMT) == S_IFDIR)
453 perms_msk = (perms_msk &
454 ~S_ISGID) | (old_mode & S_ISGID);
455
456 /*
457 * create new mode:
458 * clear the who_msk bits
459 * set the perms_mks bits (which have
460 * been trimmed to fit the who_msk.
461 */
462
463 if ((grp_change = (perms_msk & GROUP_RWX) >> 3)
464 != 0) {
465 *group_clear_bits = GROUP_RWX >> 3;
466 *group_set_bits = grp_change;
467 }
468
469 new_mode &= ~who_msk;
470 new_mode |= perms_msk;
471 break;
472 }
473 }
474 } while (*msp++ == ',');
475
476 if (*--msp || operand_empty == 0) {
477 errmsg(1, 5, gettext("invalid mode\n"));
478 }
479
480 return (new_mode);
481 }
482
483 mode_t
abs(mode_t mode,o_mode_t * group_clear_bits,o_mode_t * group_set_bits)484 abs(mode_t mode, o_mode_t *group_clear_bits, o_mode_t *group_set_bits)
485 {
486 int c;
487 mode_t i;
488
489 for (i = 0; (c = *msp) >= '0' && c <= '7'; msp++)
490 i = (mode_t)((i << 3) + (c - '0'));
491 if (*msp)
492 errmsg(1, 6, gettext("invalid mode\n"));
493
494 /*
495 * The ISGID bit on directories will not be changed when the mode argument is
496 * octal numeric. Only "g+s" and "g-s" arguments can change ISGID bit when
497 * applied to directories.
498 */
499 *group_clear_bits = GROUP_RWX >> 3;
500 *group_set_bits = (i & GROUP_RWX) >> 3;
501 if ((mode & S_IFMT) == S_IFDIR)
502 return ((i & ~S_ISGID) | (mode & S_ISGID));
503 return (i);
504 }
505
506 static mode_t
who(void)507 who(void)
508 {
509 mode_t m;
510
511 m = WHO_EMPTY;
512
513 for (; ; msp++) {
514 switch (*msp) {
515 case 'u':
516 m |= USER;
517 continue;
518 case 'g':
519 m |= GROUP;
520 continue;
521 case 'o':
522 m |= OTHER;
523 continue;
524 case 'a':
525 m |= ALL;
526 continue;
527 default:
528 return (m);
529 }
530 }
531 }
532
533 static int
what(void)534 what(void)
535 {
536 switch (*msp) {
537 case '+':
538 case '-':
539 case '=':
540 return (*msp++);
541 }
542 return (0);
543 }
544