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