xref: /illumos-gate/usr/src/lib/libsec/common/acl.y (revision 9b9d39d2a32ff806d2431dbcc50968ef1e6d46b2)
1 %{
2 /*
3  * CDDL HEADER START
4  *
5  * The contents of this file are subject to the terms of the
6  * Common Development and Distribution License (the "License").
7  * You may not use this file except in compliance 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  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  *
25  * Copyright 2022 RackTop Systems, Inc.
26  */
27 
28 #include <acl_common.h>
29 #include <aclutils.h>
30 
31 extern int yyinteractive;
32 extern acl_t *yyacl;
33 extern int yylex(void);
34 
35 %}
36 
37 %union {
38 	char *str;
39 	int val;
40 	struct acl_perm_type acl_perm;
41 	ace_t ace;
42 	aclent_t aclent;
43 	acl_t *acl;
44 }
45 
46 %token BARE_SID_TOK
47 %token USER_TOK USER_SID_TOK GROUP_TOK GROUP_SID_TOK MASK_TOK OTHER_TOK
48 %token OWNERAT_TOK GROUPAT_TOK EVERYONEAT_TOK DEFAULT_USER_TOK
49 %token DEFAULT_GROUP_TOK DEFAULT_MASK_TOK DEFAULT_OTHER_TOK
50 %token COLON COMMA NL SLASH
51 %token <str> ID IDNAME PERM_TOK INHERIT_TOK SID
52 %token <val> ERROR ACE_PERM ACE_INHERIT ENTRY_TYPE ACCESS_TYPE
53 
54 %type <str> idname id
55 %type <acl_perm> perms perm aclent_perm ace_perms
56 %type <acl> acl_entry
57 %type <ace> ace
58 %type <aclent> aclent
59 %type <val> iflags verbose_iflag compact_iflag access_type entry_type
60 
61 %left ERROR COLON
62 
63 %%
64 
65 acl:	acl_entry NL
66 	{
67 		yyacl = $1;
68 		return (0);
69 	}
70 
71 	/* This seems illegal, but the old aclfromtext() allows it */
72 	| acl_entry COMMA NL
73 	{
74 		yyacl = $1;
75 		return (0);
76 	}
77 	| acl_entry COMMA acl
78 	{
79 		yyacl = $1;
80 		return (0);
81 	}
82 
83 acl_entry: ace
84 	{
85 		ace_t *acep;
86 
87 		if (yyacl == NULL) {
88 			yyacl = acl_alloc(ACE_T);
89 			if (yyacl == NULL) {
90 				yycleanup();
91 				return (EACL_MEM_ERROR);
92 			}
93 		}
94 
95 		$$ = yyacl;
96 		if ($$->acl_type == ACLENT_T) {
97 			acl_error(dgettext(TEXT_DOMAIN,
98 			    "Cannot have POSIX draft ACL entries"
99 			    " with NFSv4/ZFS ACL entries.\n"));
100 			acl_free(yyacl);
101 			yyacl = NULL;
102 			yycleanup();
103 			return (EACL_DIFF_TYPE);
104 		}
105 
106 		$$->acl_aclp = realloc($$->acl_aclp,
107 		    ($$->acl_entry_size * ($$->acl_cnt + 1)));
108 		if ($$->acl_aclp == NULL) {
109 			free (yyacl);
110 			yycleanup();
111 			return (EACL_MEM_ERROR);
112 		}
113 		acep = $$->acl_aclp;
114 		acep[$$->acl_cnt] = $1;
115 		$$->acl_cnt++;
116 		yycleanup();
117 	}
118 	| aclent
119 	{
120 		aclent_t *aclent;
121 
122 		if (yyacl == NULL) {
123 			yyacl = acl_alloc(ACLENT_T);
124 			if (yyacl == NULL) {
125 				yycleanup();
126 				return (EACL_MEM_ERROR);
127 			}
128 		}
129 
130 		$$ = yyacl;
131 		if ($$->acl_type == ACE_T) {
132 			acl_error(dgettext(TEXT_DOMAIN,
133 			    "Cannot have NFSv4/ZFS ACL entries"
134 			    " with POSIX draft ACL entries.\n"));
135 			acl_free(yyacl);
136 			yyacl = NULL;
137 			yycleanup();
138 			return (EACL_DIFF_TYPE);
139 		}
140 
141 		$$->acl_aclp = realloc($$->acl_aclp,
142 		    ($$->acl_entry_size  * ($$->acl_cnt +1)));
143 		if ($$->acl_aclp == NULL) {
144 			free (yyacl);
145 			yycleanup();
146 			return (EACL_MEM_ERROR);
147 		}
148 		aclent = $$->acl_aclp;
149 		aclent[$$->acl_cnt] = $1;
150 		$$->acl_cnt++;
151 		yycleanup();
152 	}
153 
154 ace:	entry_type idname ace_perms access_type
155 	{
156 		int error;
157 		uid_t id;
158 		int mask;
159 
160 		error = get_id($1, $2, &id);
161 		if (error) {
162 			bad_entry_type($1, $2);
163 			yycleanup();
164 			return (EACL_INVALID_USER_GROUP);
165 		}
166 
167 		$$.a_who = id;
168 		$$.a_flags = ace_entry_type($1);
169 		error = ace_perm_mask(&$3, &$$.a_access_mask);
170 		if (error) {
171 			yycleanup();
172 			return (error);
173 		}
174 		$$.a_type = $4;
175 
176 	}
177 	| entry_type idname ace_perms access_type COLON id
178 	{
179 		int error;
180 		uid_t id;
181 
182 		if (yyinteractive) {
183 			acl_error(dgettext(TEXT_DOMAIN,
184 			    "Extra fields on the end of "
185 			    "ACL specification.\n"));
186 			yycleanup();
187 			return (EACL_UNKNOWN_DATA);
188 		}
189 		error = get_id($1, $2, &id);
190 		if (error) {
191 			$$.a_who = get_id_nofail($1, $6);
192 		} else {
193 			$$.a_who = id;
194 		}
195 		$$.a_flags = ace_entry_type($1);
196 		error = ace_perm_mask(&$3, &$$.a_access_mask);
197 		if (error) {
198 			yycleanup();
199 			return (error);
200 		}
201 		$$.a_type = $4;
202 	}
203 	| entry_type idname ace_perms iflags access_type
204 	{
205 		int error;
206 		uid_t id;
207 
208 		error = get_id($1, $2, &id);
209 		if (error) {
210 			bad_entry_type($1, $2);
211 			yycleanup();
212 			return (EACL_INVALID_USER_GROUP);
213 		}
214 
215 		$$.a_who = id;
216 		$$.a_flags = ace_entry_type($1);
217 		error = ace_perm_mask(&$3, &$$.a_access_mask);
218 		if (error) {
219 			yycleanup();
220 			return (error);
221 		}
222 		$$.a_type = $5;
223 		$$.a_flags |= $4;
224 	}
225 	| entry_type idname ace_perms iflags access_type COLON id
226 	{
227 		int error;
228 		uid_t  id;
229 
230 		if (yyinteractive) {
231 			acl_error(dgettext(TEXT_DOMAIN,
232 			    "Extra fields on the end of "
233 			    "ACL specification.\n"));
234 			yycleanup();
235 			return (EACL_UNKNOWN_DATA);
236 		}
237 		error = get_id($1, $2, &id);
238 		if (error) {
239 			$$.a_who = get_id_nofail($1, $7);
240 		} else {
241 			$$.a_who = id;
242 		}
243 
244 		$$.a_flags = ace_entry_type($1);
245 		error = ace_perm_mask(&$3, &$$.a_access_mask);
246 		if (error) {
247 			yycleanup();
248 			return (error);
249 		}
250 
251 		$$.a_type = $5;
252 		$$.a_flags |= $4;
253 	}
254 	| entry_type ace_perms access_type
255 	{
256 		int error;
257 
258 		$$.a_who = -1;
259 		$$.a_flags = ace_entry_type($1);
260 		error = ace_perm_mask(&$2, &$$.a_access_mask);
261 		if (error) {
262 			yycleanup();
263 			return (error);
264 		}
265 		$$.a_type = $3;
266 	}
267 	| entry_type ace_perms access_type COLON id
268 	{
269 		yycleanup();
270 		if (yyinteractive) {
271 			acl_error(dgettext(TEXT_DOMAIN,
272 			    "Extra fields on the end of "
273 			    "ACL specification.\n"));
274 			return (EACL_UNKNOWN_DATA);
275 		}
276 
277 		return (EACL_ENTRY_ERROR);
278 	}
279 	| entry_type ace_perms iflags access_type
280 	{
281 		int error;
282 
283 		$$.a_who = -1;
284 		$$.a_flags = ace_entry_type($1);
285 		error = ace_perm_mask(&$2, &$$.a_access_mask);
286 		if (error) {
287 			yycleanup();
288 			return (error);
289 		}
290 		$$.a_type = $4;
291 		$$.a_flags |= $3;
292 
293 	}
294 	| entry_type ace_perms iflags access_type COLON id
295 	{
296 		yycleanup();
297 		if (yyinteractive) {
298 			acl_error(dgettext(TEXT_DOMAIN,
299 			    "Extra fields on the end of "
300 			    "ACL specification.\n"));
301 			return (EACL_UNKNOWN_DATA);
302 		}
303 		return (EACL_ENTRY_ERROR);
304 	}
305 
306 aclent: entry_type idname aclent_perm	/* user or group */
307 	{
308 		int error;
309 		uid_t id;
310 
311 		error = get_id($1, $2, &id);
312 		if (error) {
313 			bad_entry_type($1, $2);
314 			yycleanup();
315 			return (EACL_INVALID_USER_GROUP);
316 		}
317 
318 		error = compute_aclent_perms($3.perm_str, &$$.a_perm);
319 		if (error) {
320 			acl_error(dgettext(TEXT_DOMAIN,
321 			    "Invalid permission(s) '%s' specified.\n"),
322 			    $3.perm_str);
323 			yycleanup();
324 			return (error);
325 		}
326 		$$.a_id = id;
327 		error = aclent_entry_type($1, 0, &$$.a_type);
328 		if (error) {
329 			acl_error(
330 			    dgettext(TEXT_DOMAIN,
331 			    "Invalid ACL entry type '%s' specified.\n"), $1);
332 			yycleanup();
333 			return (error);
334 		}
335 	}
336 	| entry_type COLON aclent_perm		/* owner group other */
337 	{
338 		int error;
339 
340 		error = compute_aclent_perms($3.perm_str, &$$.a_perm);
341 		if (error) {
342 			acl_error(dgettext(TEXT_DOMAIN,
343 			    "Invalid permission(s) '%s' specified.\n"),
344 			    $3.perm_str);
345 			yycleanup();
346 			return (error);
347 		}
348 		$$.a_id = -1;
349 		error = aclent_entry_type($1, 1, &$$.a_type);
350 		if (error) {
351 			acl_error(
352 			    dgettext(TEXT_DOMAIN,
353 			    "Invalid ACL entry type '%s' specified.\n"), $1);
354 			yycleanup();
355 			return (error);
356 		}
357 	}
358 	| entry_type COLON aclent_perm COLON id
359 	{
360 		yycleanup();
361 		if (yyinteractive) {
362 			acl_error(dgettext(TEXT_DOMAIN,
363 			    "Extra fields on the end of ACL specification.\n"));
364 			return (EACL_UNKNOWN_DATA);
365 		}
366 		return (EACL_ENTRY_ERROR);
367 	}
368 	| entry_type idname aclent_perm COLON id	/* user or group */
369 	{
370 		int error;
371 		uid_t id;
372 
373 		if (yyinteractive) {
374 			acl_error(dgettext(TEXT_DOMAIN,
375 			    "Extra fields on the end of ACL specification.\n"));
376 			yycleanup();
377 			return (EACL_UNKNOWN_DATA);
378 		}
379 		error = compute_aclent_perms($3.perm_str, &$$.a_perm);
380 		if (error) {
381 			acl_error(dgettext(TEXT_DOMAIN,
382 			    "Invalid permission(s) '%s' specified.\n"),
383 			    $3.perm_str);
384 			yycleanup();
385 			return (error);
386 		}
387 		error = get_id($1, $2, &id);
388 		if (error) {
389 			$$.a_id = get_id_nofail($1, $5);
390 		} else
391 			$$.a_id = id;
392 
393 		error = aclent_entry_type($1, 0, &$$.a_type);
394 		if (error) {
395 			acl_error(
396 			    dgettext(TEXT_DOMAIN,
397 			    "Invalid ACL entry type '%s' specified.\n"), $1);
398 			yycleanup();
399 			return (error);
400 		}
401 	}
402 	| entry_type aclent_perm  /* mask entry */
403 	{
404 		int error;
405 
406 		error = compute_aclent_perms($2.perm_str, &$$.a_perm);
407 		if (error) {
408 			acl_error(dgettext(TEXT_DOMAIN,
409 			    "Invalid permission(s) '%s' specified.\n"),
410 			    $2.perm_str);
411 			yycleanup();
412 			return (error);
413 		}
414 		$$.a_id = -1;
415 		error = aclent_entry_type($1, 0, &$$.a_type);
416 		if (error) {
417 			acl_error(
418 			    dgettext(TEXT_DOMAIN,
419 			    "Invalid ACL entry type specified %d.\n"),
420 			    error);
421 			yycleanup();
422 			return (error);
423 		}
424 	}
425 	| entry_type aclent_perm COLON id
426 	{
427 		yycleanup();
428 		if (yyinteractive) {
429 			acl_error(dgettext(TEXT_DOMAIN,
430 			    "Extra fields on the end of ACL specification.\n"));
431 			return (EACL_UNKNOWN_DATA);
432 		}
433 		return (EACL_ENTRY_ERROR);
434 	}
435 
436 iflags: compact_iflag COLON {$$ = $1;}
437 	| verbose_iflag COLON {$$ = $1;}
438 	| COLON {$$ = 0;}
439 
440 compact_iflag : INHERIT_TOK
441 	{
442 		int error;
443 		uint32_t iflags;
444 
445 		error = compute_ace_inherit($1, &iflags);
446 		if (error) {
447 			acl_error(dgettext(TEXT_DOMAIN,
448 			    "Invalid inheritance flags '%s' specified.\n"), $1);
449 			yycleanup();
450 			return (error);
451 		}
452 		$$ = iflags;
453 	}
454 	| INHERIT_TOK SLASH verbose_iflag
455 	{
456 		acl_error(dgettext(TEXT_DOMAIN,
457 		    "Can't mix compact inherit flags with"
458 		    " verbose inheritance flags.\n"));
459 		yycleanup();
460 		return (EACL_INHERIT_ERROR);
461 	}
462 
463 verbose_iflag: ACE_INHERIT	{$$ |= $1;}
464 	| ACE_INHERIT SLASH verbose_iflag {$$ = $1 | $3;}
465 	| ACE_INHERIT SLASH compact_iflag
466 	{
467 		acl_error(dgettext(TEXT_DOMAIN,
468 		    "Can't mix verbose inherit flags with"
469 		    " compact inheritance flags.\n"));
470 		yycleanup();
471 		return (EACL_INHERIT_ERROR);
472 	}
473 	| ACE_INHERIT SLASH ACCESS_TYPE
474 	{
475 		acl_error(dgettext(TEXT_DOMAIN,
476 		    "Inheritance flags can't be mixed with access type.\n"));
477 		yycleanup();
478 		return (EACL_INHERIT_ERROR);
479 	}
480 	| ACE_INHERIT SLASH ERROR
481 	{
482 		yycleanup();
483 		return ($3);
484 	}
485 
486 aclent_perm: PERM_TOK
487 	{
488 		$$.perm_style = PERM_TYPE_UNKNOWN;
489 		$$.perm_str = $1;
490 		$$.perm_val = 0;
491 	}
492 	| PERM_TOK ERROR
493 	{
494 		acl_error(dgettext(TEXT_DOMAIN,
495 		    "ACL entry permissions are incorrectly specified.\n"));
496 		yycleanup();
497 		return ($2);
498 	}
499 
500 access_type: ACCESS_TYPE {$$ = $1;}
501 	| ERROR
502 	{
503 		yycleanup();
504 		return ($1);
505 	}
506 
507 id: ID {$$ = $1;}
508 	| SID {$$ = $1;}
509 	| COLON
510 	{
511 		acl_error(dgettext(TEXT_DOMAIN,
512 		    "Invalid uid/gid specified.\nThe field"
513 		    " should be a numeric value.\n"));
514 		yycleanup();
515 		return (EACL_UNKNOWN_DATA);
516 	}
517 	| ERROR
518 	{
519 		yycleanup();
520 		return ($1);
521 	}
522 
523 ace_perms: perm {$$ = $1;}
524 	| aclent_perm COLON {$$ = $1;}
525 	| ERROR
526 	{
527 		yycleanup();
528 		return ($1);
529 	}
530 
531 perm: perms COLON {$$ = $1;}
532 	| COLON {$$.perm_style = PERM_TYPE_EMPTY;}
533 
534 perms: ACE_PERM
535 	{
536 		$$.perm_style = PERM_TYPE_ACE;
537 		$$.perm_val |= $1;
538 	}
539 	| ACE_PERM SLASH perms
540 	{
541 		$$.perm_style = PERM_TYPE_ACE;
542 		$$.perm_val = $1 | $3.perm_val;
543 	}
544 	| ACE_PERM SLASH aclent_perm
545 	{
546 
547 		acl_error(dgettext(TEXT_DOMAIN,
548 		   "Can't mix verbose permissions with"
549 		    " compact permission.\n"));
550 		yycleanup();
551 		return (EACL_PERM_MASK_ERROR);
552 
553 	}
554 	| ACE_PERM SLASH ERROR
555 	{
556 		yycleanup();
557 		return ($3);
558 	}
559 
560 
561 idname: IDNAME {$$ = $1;}
562 
563 entry_type: ENTRY_TYPE {$$ = $1;}
564 	| ERROR
565 	{
566 		yycleanup();
567 		return ($1);
568 	}
569 
570 %%
571 static void
572 bad_entry_type(int toketype, char *str)
573 {
574 	switch(toketype) {
575 	case USER_TOK:
576 	case DEFAULT_USER_TOK:
577 		acl_error(dgettext(TEXT_DOMAIN,
578 		    "Invalid user %s specified.\n"), str);
579 		break;
580 
581 	case GROUP_TOK:
582 	case DEFAULT_GROUP_TOK:
583 		acl_error(dgettext(TEXT_DOMAIN,
584 		    "Invalid group %s specified.\n"), str);
585 		break;
586 
587 	case USER_SID_TOK:
588 		acl_error(dgettext(TEXT_DOMAIN,
589 		    "Invalid user SID %s specified.\n"), str);
590 		break;
591 
592 	case GROUP_SID_TOK:
593 		acl_error(dgettext(TEXT_DOMAIN,
594 		    "Invalid group SID %s specified.\n"), str);
595 		break;
596 
597 	case BARE_SID_TOK:
598 		acl_error(dgettext(TEXT_DOMAIN,
599 		    "Invalid SID %s specified.\n"), str);
600 		break;
601 	}
602 }
603