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 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 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 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 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 534 what(void) 535 { 536 switch (*msp) { 537 case '+': 538 case '-': 539 case '=': 540 return (*msp++); 541 } 542 return (0); 543 } 544