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 (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 * 21 * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 22 * Use is subject to license terms. 23 */ 24 25 #pragma ident "%Z%%M% %I% %E% SMI" 26 27 %{ 28 #include <sys/acl.h> 29 #include <aclutils.h> 30 #include <errno.h> 31 #include "acl.tab.h" 32 33 #ifdef input 34 #undef input 35 #endif 36 37 #ifdef unput 38 #undef unput 39 #endif 40 41 int grab_string(char *terminators); 42 static int input(); 43 static void unput(int); 44 45 int 46 yyerror(const char *s) 47 { 48 return (0); 49 } 50 51 int 52 yywrap(void) 53 { 54 return (1); 55 } 56 57 extern char *yybuf; 58 int yybufpos; 59 60 %} 61 62 %s TS NS PS AIS AS US ES 63 /* 64 * TS = type state 65 * NS = name state 66 * PS = Permission state 67 * AIS = Allow/deny/inheritance state 68 * AS = Allow state (only used when inheritance detected) 69 * US = UID/GID state 70 * ES = End state 71 */ 72 73 ID [1-9][0-9]* 74 LOGNAME [\.a-z0-9A-Z\-_]+: 75 PERM_STR [rRwWxpdDaAcCos-]+ 76 INHERIT_STR [fdinFS-]+ 77 78 %% 79 80 <TS>user: { 81 BEGIN NS; 82 yylval.val = USER_TOK; 83 return (ENTRY_TYPE); 84 } 85 <TS>owner@: { 86 BEGIN PS; 87 yylval.val = OWNERAT_TOK; 88 return (ENTRY_TYPE); 89 } 90 <TS>group@: { 91 BEGIN PS; 92 yylval.val = GROUPAT_TOK; 93 return (ENTRY_TYPE); 94 } 95 <TS>everyone@: { 96 BEGIN PS; 97 yylval.val = EVERYONEAT_TOK; 98 return (ENTRY_TYPE); 99 } 100 <TS>group: { 101 BEGIN NS; 102 yylval.val = GROUP_TOK; 103 return (ENTRY_TYPE); 104 } 105 <TS>mask: { 106 BEGIN PS; 107 yylval.val = MASK_TOK; 108 return (ENTRY_TYPE); 109 } 110 <TS>mask:: { 111 BEGIN PS; 112 yylval.val = MASK_TOK; 113 return (ENTRY_TYPE); 114 } 115 <TS>other: { 116 BEGIN PS; 117 yylval.val = OTHER_TOK; 118 return (ENTRY_TYPE); 119 } 120 <TS>other:: { 121 BEGIN PS; 122 yylval.val = OTHER_TOK; 123 return (ENTRY_TYPE); 124 } 125 <TS>defaultuser: { 126 BEGIN NS; 127 yylval.val = DEFAULT_USER_TOK; 128 return (ENTRY_TYPE); 129 } 130 <TS>default:user: { 131 BEGIN NS; 132 yylval.val = DEFAULT_USER_TOK; 133 return (ENTRY_TYPE); 134 } 135 <TS>defaultgroup: { 136 BEGIN NS; 137 yylval.val = DEFAULT_GROUP_TOK; 138 return (ENTRY_TYPE); 139 } 140 <TS>default:group: { 141 BEGIN NS; 142 yylval.val = DEFAULT_GROUP_TOK; 143 return (ENTRY_TYPE); 144 } 145 <TS>defaultother: { 146 BEGIN PS; 147 yylval.val = DEFAULT_OTHER_TOK; 148 return (ENTRY_TYPE); 149 } 150 <TS>defaultother:: { 151 BEGIN PS; 152 yylval.val = DEFAULT_OTHER_TOK; 153 return (ENTRY_TYPE); 154 } 155 <TS>default:other: { 156 BEGIN PS; 157 yylval.val = DEFAULT_OTHER_TOK; 158 return (ENTRY_TYPE); 159 } 160 <TS>defaultmask: { 161 BEGIN PS; 162 yylval.val = DEFAULT_MASK_TOK; 163 return (ENTRY_TYPE); 164 } 165 <TS>defaultmask:: { 166 BEGIN PS; 167 yylval.val = DEFAULT_MASK_TOK; 168 return (ENTRY_TYPE); 169 } 170 <TS>default:mask: { 171 BEGIN PS; 172 yylval.val = DEFAULT_MASK_TOK; 173 return (ENTRY_TYPE); 174 } 175 <TS>"\n" { 176 return (NL); 177 } 178 <TS>. { 179 if (grab_string(":,\n") != 0) { 180 acl_error(dgettext(TEXT_DOMAIN, 181 "Failed to retrieve" 182 " error string.\n")); 183 yylval.val = EACL_MEM_ERROR; 184 return (ERROR); 185 } 186 acl_error(dgettext(TEXT_DOMAIN, 187 "Invalid ACL entry " 188 "type '%s' specified.\n"), yylval.str); 189 free(yylval.str); 190 yylval.val = EACL_ENTRY_ERROR; 191 return (ERROR); 192 } 193 <NS>: { 194 BEGIN PS; 195 return (COLON); 196 } 197 <NS>{LOGNAME} { 198 yylval.str = strdup(yytext); 199 if (yylval.str == NULL) { 200 yylval.val = EACL_MEM_ERROR; 201 return (ERROR); 202 } 203 yylval.str[strlen(yylval.str) -1] = '\0'; 204 BEGIN PS; 205 return (IDNAME); 206 } 207 <NS>"\n" { 208 acl_error(dgettext(TEXT_DOMAIN, 209 "Missing user/group name" 210 " from ACL specification.\n")); 211 yylval.val = EACL_MISSING_FIELDS; 212 return (ERROR); 213 } 214 <NS>. { 215 int error; 216 217 error = grab_string(":,\n"); 218 if (error != 0) { 219 acl_error(dgettext(TEXT_DOMAIN, 220 "Invalid user/group " 221 "name specification.\n")); 222 yylval.val = EACL_INVALID_USER_GROUP; 223 } else { 224 acl_error(dgettext(TEXT_DOMAIN, 225 "User/Group name " 226 "'%s' not specified correctly.\n"), 227 yylval.str); 228 free(yylval.str); 229 yylval.val = EACL_ENTRY_ERROR; 230 } 231 return (ERROR); 232 } 233 <PS>read_data/[:/,] { 234 yylval.val = ACE_READ_DATA; 235 return (ACE_PERM); 236 } 237 <PS>list_directory/[:/,] { 238 yylval.val = ACE_LIST_DIRECTORY; 239 return (ACE_PERM); 240 } 241 <PS>write_data/[:/,] { 242 yylval.val = ACE_WRITE_DATA; 243 return (ACE_PERM); 244 } 245 <PS>add_file/[:/,] { 246 yylval.val = ACE_ADD_FILE; 247 return (ACE_PERM); 248 } 249 <PS>append_data/[:/,] { 250 yylval.val = ACE_APPEND_DATA; 251 return (ACE_PERM); 252 } 253 <PS>add_subdirectory/[:/,] { 254 yylval.val = ACE_ADD_SUBDIRECTORY; 255 return (ACE_PERM); 256 } 257 <PS>read_xattr/[:/,] { 258 yylval.val = ACE_READ_NAMED_ATTRS; 259 return (ACE_PERM); 260 } 261 <PS>write_xattr/[:/,] { 262 yylval.val = ACE_WRITE_NAMED_ATTRS; 263 return (ACE_PERM); 264 } 265 <PS>execute/[:/,] { 266 yylval.val = ACE_EXECUTE; 267 return (ACE_PERM); 268 } 269 <PS>delete_child/[:/,] { 270 yylval.val = ACE_DELETE_CHILD; 271 return (ACE_PERM); 272 } 273 <PS>read_attributes/[:/,] { 274 yylval.val = ACE_READ_ATTRIBUTES; 275 return (ACE_PERM); 276 } 277 <PS>write_attributes/[:/,] { 278 yylval.val = ACE_WRITE_ATTRIBUTES; 279 return (ACE_PERM); 280 } 281 <PS>delete/[:/,] { 282 yylval.val = ACE_DELETE; 283 return (ACE_PERM); 284 } 285 <PS>read_acl/[:/,] { 286 yylval.val = ACE_READ_ACL; 287 return (ACE_PERM); 288 } 289 <PS>write_acl/[:/,] { 290 yylval.val = ACE_WRITE_ACL; 291 return (ACE_PERM); 292 } 293 <PS>write_owner/[:/,] { 294 yylval.val = ACE_WRITE_OWNER; 295 return (ACE_PERM); 296 } 297 <PS>synchronize/[:/,] { 298 yylval.val = ACE_SYNCHRONIZE; 299 return (ACE_PERM); 300 } 301 <PS>{PERM_STR}/[:,\n] { 302 int c; 303 304 c = input(); 305 unput(c); 306 yylval.str = strdup(yytext); 307 if (yylval.str == NULL) { 308 yylval.val = EACL_MEM_ERROR; 309 return (ERROR); 310 } 311 312 /* 313 * aclent are done after permissions. 314 */ 315 if (isdigit(c)) 316 BEGIN US; 317 else if (c != ':') 318 BEGIN ES; 319 320 return (PERM_TOK); 321 } 322 <PS>"/:" { 323 acl_error(dgettext(TEXT_DOMAIN, 324 "Invalid permission /: specified.\n")); 325 yylval.val = EACL_ENTRY_ERROR; 326 return (ERROR); 327 } 328 <PS>: { 329 int c; 330 331 c = input(); 332 unput(c); 333 if (isdigit(c)) 334 BEGIN (US); 335 else 336 BEGIN AIS; 337 return (COLON); 338 } 339 <PS>"/" { 340 return (SLASH); 341 } 342 <PS>"\n" { 343 acl_error(dgettext(TEXT_DOMAIN, 344 "ACL entry is missing " 345 "permission fields.\n")); 346 yylval.val = EACL_MISSING_FIELDS; 347 return (ERROR); 348 } 349 <PS>"," { 350 acl_error( 351 dgettext(TEXT_DOMAIN, 352 "The ',' is not a valid permission field " 353 "separator.\nThe comma is used to separate " 354 "access control entries.\nSee acl(5) for " 355 "examples of specifying ACL entries.\n")); 356 yylval.val = EACL_PERM_MASK_ERROR; 357 return (ERROR); 358 } 359 <PS>. { 360 if (grab_string("/:,\n") != 0) { 361 acl_error(dgettext(TEXT_DOMAIN, 362 "Failed to retrieve" 363 " error string.\n")); 364 yylval.val = EACL_MEM_ERROR; 365 return (ERROR); 366 } 367 acl_error(dgettext(TEXT_DOMAIN, 368 "Invalid permission(s) '%s' " 369 "specified.\n"), yylval.str); 370 free(yylval.str); 371 yylval.val = EACL_PERM_MASK_ERROR; 372 return (ERROR); 373 } 374 <AS>allow/[:,\n] { 375 376 int c; 377 378 c = input(); 379 unput(c); 380 if (c == ',' || c == '\n') 381 BEGIN ES; 382 else 383 BEGIN US; 384 yylval.val = ACE_ACCESS_ALLOWED_ACE_TYPE; 385 return (ACCESS_TYPE); 386 } 387 <AS>deny/[:,\n] { 388 389 int c; 390 391 c = input(); 392 unput(c); 393 if (c == ',' || c == '\n') 394 BEGIN ES; 395 else 396 BEGIN US; 397 398 yylval.val = ACE_ACCESS_DENIED_ACE_TYPE; 399 return (ACCESS_TYPE); 400 } 401 <AS>: { 402 403 acl_error(dgettext(TEXT_DOMAIN, 404 "Invalid Access type " 405 "specified.\nThe field is blank, when" 406 " it should be either allow or deny.\n")); 407 yylval.val = EACL_INVALID_ACCESS_TYPE; 408 return (ERROR); 409 } 410 <AS>"\n" { 411 acl_error(dgettext(TEXT_DOMAIN, 412 "ACL access type must be specified.\n")); 413 yylval.val = EACL_INVALID_ACCESS_TYPE; 414 return (ERROR); 415 } 416 <AS>. { 417 if (yytext[0] != '\n' && yytext[0] != '\0') { 418 if (grab_string(":,\n") != 0) { 419 acl_error(dgettext(TEXT_DOMAIN, 420 "Failed to " 421 "retrieve error " 422 "string.\n")); 423 yylval.val = EACL_MEM_ERROR; 424 return (ERROR); 425 } 426 acl_error( 427 dgettext(TEXT_DOMAIN, 428 "Invalid access " 429 "type '%s' specified.\n"), 430 yylval.str); 431 } else { 432 acl_error( 433 dgettext(TEXT_DOMAIN, 434 "No access " 435 "type specified.\n"), yylval.str); 436 } 437 438 free(yylval.str); 439 yylval.val = EACL_INVALID_ACCESS_TYPE; 440 return (ERROR); 441 } 442 <AIS>allow/[:,\n] { 443 444 int c; 445 446 c = input(); 447 unput(c); 448 if (c == ',' || c == '\n') 449 BEGIN ES; 450 else 451 BEGIN US; 452 yylval.val = ACE_ACCESS_ALLOWED_ACE_TYPE; 453 return (ACCESS_TYPE); 454 } 455 <AIS>deny/[:,\n] { 456 457 int c; 458 459 c = input(); 460 unput(c); 461 if (c == ',' || c == '\n') 462 BEGIN ES; 463 else 464 BEGIN US; 465 466 yylval.val = ACE_ACCESS_DENIED_ACE_TYPE; 467 return (ACCESS_TYPE); 468 } 469 <AIS>file_inherit/[:/,] { 470 yylval.val = ACE_FILE_INHERIT_ACE; 471 return (ACE_INHERIT); 472 } 473 <AIS>dir_inherit/[:/,] { 474 yylval.val = ACE_DIRECTORY_INHERIT_ACE; 475 return (ACE_INHERIT); 476 } 477 <AIS>no_propagate/[/:,] { 478 yylval.val = ACE_NO_PROPAGATE_INHERIT_ACE; 479 return (ACE_INHERIT); 480 } 481 <AIS>inherit_only/[/:,] { 482 yylval.val = ACE_INHERIT_ONLY_ACE; 483 return (ACE_INHERIT); 484 } 485 <AIS>{INHERIT_STR}/[:] { 486 yylval.str = strdup(yytext); 487 if (yylval.str == NULL) { 488 yylval.val = EACL_MEM_ERROR; 489 return (ERROR); 490 } 491 return (INHERIT_TOK); 492 } 493 <AIS>: { 494 /* 495 * Only inheritance fields should hit this. 496 * allow/deny fields match on ":" as part 497 * of the regexp. 498 */ 499 BEGIN AS; 500 return (COLON); 501 } 502 <AIS>"/" { 503 return (SLASH); 504 } 505 <AIS>"\n" { 506 acl_error( 507 dgettext(TEXT_DOMAIN, 508 "Invalid ACL specification." 509 "\nWas expecting to find" 510 " access type or inheritance flags.\n"), 511 yylval.str); 512 yylval.val = EACL_UNKNOWN_DATA; 513 return (ERROR); 514 } 515 <AIS>"," { 516 acl_error( 517 dgettext(TEXT_DOMAIN, 518 "The ',' is not a valid inheritance field " 519 "separator.\nThe comma is used to separate " 520 "access control entries.\nSee acl(5) for " 521 "examples of specifying ACL entries.\n")); 522 yylval.val = EACL_INVALID_ACCESS_TYPE; 523 return (ERROR); 524 } 525 <AIS>. { 526 if (yytext[0] != '\n' && yytext[0] != '\0') { 527 if (grab_string(":,\n") != 0) { 528 acl_error(dgettext(TEXT_DOMAIN, 529 "Failed to " 530 "retrieve error " 531 "string.\n")); 532 yylval.val = EACL_MEM_ERROR; 533 return (ERROR); 534 } 535 acl_error( 536 dgettext(TEXT_DOMAIN, 537 "Invalid inheritance or" 538 " access type '%s' specified.\n"), 539 yylval.str); 540 } else { 541 acl_error( 542 dgettext(TEXT_DOMAIN, 543 "No inheritance or " 544 "access type specified.\n"), 545 yylval.str); 546 } 547 548 free(yylval.str); 549 yylval.val = EACL_INVALID_ACCESS_TYPE; 550 return (ERROR); 551 } 552 <US>{ID}/[,\n] { 553 BEGIN ES; 554 yylval.val = atoi(yytext); 555 return (ID); 556 } 557 <US>: { 558 return (COLON); 559 } 560 <US>{INHERIT_STR} { /* 561 * Catch specific error to produce 562 * nice message for users who are trying 563 * to use old syntax format which had 564 * inheritance flags as the last field. 565 */ 566 acl_error(dgettext(TEXT_DOMAIN, 567 "Access type should be final" 568 " field in ACL specification.\n")); 569 yylval.val = EACL_ENTRY_ERROR; 570 return (ERROR); 571 } 572 <US>. { 573 if (grab_string(",\n") != 0) { 574 acl_error(dgettext(TEXT_DOMAIN, 575 "Failed to retrieve" 576 " error string.\n")); 577 yylval.val = EACL_MEM_ERROR; 578 return (ERROR); 579 } 580 acl_error( 581 dgettext(TEXT_DOMAIN, 582 "Invalid data ':%s' specified" 583 " on end of ACL.\n"), yylval.str); 584 free(yylval.str); 585 yylval.val = EACL_ENTRY_ERROR; 586 return (ERROR); 587 } 588 <US>"\n" { 589 acl_error(dgettext(TEXT_DOMAIN, 590 "Missing fields in ACL " 591 "specification.\nWas expecting to find " 592 "uid/gid.\n")); 593 yylval.val = EACL_ENTRY_ERROR; 594 return (ERROR); 595 } 596 <ES>"," { 597 BEGIN TS; 598 return (COMMA); 599 } 600 <ES>. { 601 if (grab_string("/:,\n") != 0) { 602 acl_error( 603 dgettext(TEXT_DOMAIN, 604 "Failed to retrieve error" 605 " string.\n")); 606 yylval.val = EACL_MEM_ERROR; 607 return (ERROR); 608 } 609 acl_error( 610 dgettext(TEXT_DOMAIN, 611 "Unrecognized data '%s' found" 612 " in ACL specification.\n"), yylval.str); 613 free(yylval.str); 614 yylval.val = EACL_UNKNOWN_DATA; 615 return (ERROR); 616 } 617 <ES>"\n" { 618 return (NL); 619 } 620 %% 621 622 623 /* 624 * Pull string up to terminator off of input string. 625 * used for retrieving illegal data in ACL specification. 626 * 627 * The first set of characters is retrieved from yytext. 628 * subseequent characters are pulled from the input stream, 629 * until either EOF or one of the requested terminators is scene. 630 * Result is returned in yylval.str which is malloced. 631 */ 632 int 633 grab_string(char *terminators) 634 { 635 int c; 636 int done = 0; 637 int cnt; 638 int alloced; 639 int error = 0; 640 char *ptr; 641 642 cnt = strlen(yytext); 643 yylval.str = calloc(cnt + 1, sizeof (char)); 644 if (yylval.str == NULL) { 645 return (1); 646 } 647 alloced = cnt + 1; 648 strcpy(yylval.str, yytext); 649 650 do { 651 c = input(); 652 if (c == EOF) 653 break; 654 655 for (ptr = terminators; *ptr; ptr++) { 656 if (c == *ptr) { 657 done = 1; 658 break; 659 } 660 } 661 662 if (done) 663 break; 664 665 if (cnt + 1 >= alloced) { 666 yylval.str = realloc(yylval.str, 667 alloced + 80); 668 alloced += 80; 669 if (yylval.str == NULL) 670 return (1); 671 672 memset(yylval.str + cnt, 0, 673 alloced - strlen(yylval.str)); 674 } 675 yylval.str[strlen(yylval.str)] = c; 676 cnt++; 677 } while (!done); 678 679 return (error); 680 } 681 682 static int 683 input(void) 684 { 685 int c; 686 687 c = yybuf[yybufpos++]; 688 if (c == '\0') { 689 return (EOF); 690 } 691 692 return (c); 693 } 694 695 static void 696 unput(int c) 697 { 698 if (c == '\0') { 699 return; 700 } 701 702 if (yybufpos > 0) { 703 --yybufpos; 704 } 705 } 706 707 /* 708 * return ACE entry type 709 */ 710 int 711 ace_entry_type(int type) 712 { 713 int ret = -1; 714 switch (type) { 715 case USER_TOK: 716 ret = 0; 717 break; 718 case GROUP_TOK: 719 ret = ACE_IDENTIFIER_GROUP; 720 break; 721 case OWNERAT_TOK: 722 ret = ACE_OWNER; 723 break; 724 case GROUPAT_TOK: 725 ret = ACE_IDENTIFIER_GROUP | ACE_GROUP; 726 break; 727 case EVERYONEAT_TOK: 728 ret = ACE_EVERYONE; 729 break; 730 } 731 return (ret); 732 } 733 734 735 /* 736 * return aclent entry type 737 */ 738 int 739 aclent_entry_type(int type, int owning, int *ret) 740 { 741 742 *ret = 0; 743 744 switch (type) { 745 case USER_TOK: 746 *ret = (owning == 0) ? USER : USER_OBJ; 747 break; 748 case GROUP_TOK: 749 *ret = (owning == 0) ? GROUP : GROUP_OBJ; 750 break; 751 case OTHER_TOK: 752 *ret = OTHER_OBJ; 753 break; 754 case MASK_TOK: 755 *ret = CLASS_OBJ; 756 break; 757 case DEFAULT_USER_TOK: 758 *ret = (owning == 0) ? DEF_USER : DEF_USER_OBJ; 759 break; 760 case DEFAULT_GROUP_TOK: 761 *ret = (owning == 0) ? DEF_GROUP : DEF_GROUP_OBJ; 762 break; 763 case DEFAULT_MASK_TOK: 764 *ret = DEF_CLASS_OBJ; 765 break; 766 case DEFAULT_OTHER_TOK: 767 *ret = DEF_OTHER_OBJ; 768 break; 769 default: 770 return (EACL_ENTRY_ERROR); 771 } 772 773 return (0); 774 } 775 776 /* 777 * convert string into numeric id. 778 */ 779 static int 780 acl_str_to_id(char *str, int *id) 781 { 782 char *end; 783 uid_t value; 784 785 errno = 0; 786 value = strtol(str, &end, 10); 787 788 if (errno != 0 || *end != '\0') 789 return (EACL_INVALID_USER_GROUP); 790 791 *id = value; 792 793 return (0); 794 } 795 796 /* 797 * determine either uid/gid for given entry type 798 */ 799 int 800 get_id(int entry_type, char *name, int *id) 801 { 802 struct passwd *pw; 803 struct group *gr; 804 int error; 805 806 /* 807 * First see if uid/gid is an all-numeric value 808 * otherwise, try getpwnam/getgrnam. 809 */ 810 error = acl_str_to_id(name, id); 811 812 if (error != 0) { 813 if (entry_type == USER_TOK || entry_type == DEFAULT_USER_TOK) { 814 pw = getpwnam(name); 815 if (pw) { 816 *id = pw->pw_uid; 817 error = 0; 818 } 819 } else { 820 gr = getgrnam(name); 821 if (gr) { 822 *id = gr->gr_gid; 823 error = 0; 824 } 825 } 826 } 827 828 return (error); 829 } 830 /* 831 * reset beginning state to TS and set character position 832 * back to zero. 833 */ 834 void 835 yyreset() 836 { 837 yybufpos = 0; 838 BEGIN TS; 839 } 840 841