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