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