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