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