OpenSolaris_b135/lib/libsec/common/acl.y

%{
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 *
 * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#include <acl_common.h>
#include <aclutils.h>

extern int yyinteractive;
extern acl_t *yyacl;
%}

%union {
	char *str;
	int val;
	struct acl_perm_type acl_perm;
	ace_t ace;
	aclent_t aclent;
	acl_t *acl;
}


%token USER_TOK USER_SID_TOK GROUP_TOK GROUP_SID_TOK MASK_TOK OTHER_TOK
%token OWNERAT_TOK GROUPAT_TOK EVERYONEAT_TOK DEFAULT_USER_TOK 
%token DEFAULT_GROUP_TOK DEFAULT_MASK_TOK DEFAULT_OTHER_TOK
%token COLON COMMA NL SLASH
%token <str> ID IDNAME PERM_TOK INHERIT_TOK SID
%token <val> ERROR ACE_PERM ACE_INHERIT ENTRY_TYPE ACCESS_TYPE

%type <str> idname id
%type <acl_perm> perms perm aclent_perm ace_perms
%type <acl> acl_entry
%type <ace> ace 
%type <aclent> aclent
%type <val> iflags verbose_iflag compact_iflag access_type entry_type

%left ERROR COLON

%%

acl:	acl_entry NL 
	{ 
		yyacl = $1;
		return (0);
	} 

	/* This seems illegal, but the old aclfromtext() allows it */
	| acl_entry COMMA NL	
	{
		yyacl = $1;
		return (0);
	}
	| acl_entry COMMA acl 
	{ 
		yyacl = $1;
		return (0);
	}
	
acl_entry: ace 
	{
		ace_t *acep;

		if (yyacl == NULL) {
			yyacl = acl_alloc(ACE_T);
			if (yyacl == NULL) {
				yycleanup();
				return (EACL_MEM_ERROR);
			}
		} 

		$$ = yyacl;
		if ($$->acl_type == ACLENT_T) {
			acl_error(dgettext(TEXT_DOMAIN,
			    "Cannot have POSIX draft ACL entries"
			    " with NFSv4/ZFS ACL entries.\n"));
			acl_free(yyacl);
			yyacl = NULL;
			yycleanup();
			return (EACL_DIFF_TYPE);
		}
			
		$$->acl_aclp = realloc($$->acl_aclp,
		    ($$->acl_entry_size * ($$->acl_cnt + 1)));
		if ($$->acl_aclp == NULL) {
			free (yyacl);
			yycleanup();
			return (EACL_MEM_ERROR);	
		}
		acep = $$->acl_aclp;
		acep[$$->acl_cnt] = $1;
		$$->acl_cnt++;
		yycleanup();
	}
	| aclent
	{
		aclent_t *aclent;

		if (yyacl == NULL) {
			yyacl = acl_alloc(ACLENT_T);
			if (yyacl == NULL) {
				yycleanup();
				return (EACL_MEM_ERROR);
			}
		} 

		$$ = yyacl;
		if ($$->acl_type == ACE_T) {
			acl_error(dgettext(TEXT_DOMAIN,
			    "Cannot have NFSv4/ZFS ACL entries"
			    " with POSIX draft ACL entries.\n"));
			acl_free(yyacl);
			yyacl = NULL;
			yycleanup();
			return (EACL_DIFF_TYPE);
		}

		$$->acl_aclp = realloc($$->acl_aclp,
		    ($$->acl_entry_size  * ($$->acl_cnt +1)));
		if ($$->acl_aclp == NULL) {
			free (yyacl);
			yycleanup();
			return (EACL_MEM_ERROR);	
		}
		aclent = $$->acl_aclp;
		aclent[$$->acl_cnt] = $1;
		$$->acl_cnt++;
		yycleanup();
	}

ace:	entry_type idname ace_perms access_type
	{
		int error;
		uid_t id;
		int mask;

		error = get_id($1, $2, &id);
		if (error) {
			bad_entry_type($1, $2);
			yycleanup();
			return (EACL_INVALID_USER_GROUP);
		}
			
		$$.a_who = id;
		$$.a_flags = ace_entry_type($1);
		error = ace_perm_mask(&$3, &$$.a_access_mask);
		if (error) {
			yycleanup();
			return (error);
		}
		$$.a_type = $4;

	}
	| entry_type idname ace_perms access_type COLON id
	{
		int error;
		uid_t id;

		if (yyinteractive) {
			acl_error(dgettext(TEXT_DOMAIN,
			    "Extra fields on the end of "
			    "ACL specification.\n"));
			yycleanup();
			return (EACL_UNKNOWN_DATA);
		}
		error = get_id($1, $2, &id);
		if (error) {
			$$.a_who = get_id_nofail($1, $6);
		} else {
			$$.a_who = id;
		}
		$$.a_flags = ace_entry_type($1);
		error = ace_perm_mask(&$3, &$$.a_access_mask);
		if (error) {
			yycleanup();
			return (error);
		}
		$$.a_type = $4;
	}
	| entry_type idname ace_perms iflags access_type 
	{
		int error;
		uid_t id;

		error = get_id($1, $2, &id);
		if (error) {
			bad_entry_type($1, $2);
			yycleanup();
			return (EACL_INVALID_USER_GROUP);
		}
		
		$$.a_who = id;
		$$.a_flags = ace_entry_type($1);
		error = ace_perm_mask(&$3, &$$.a_access_mask);
		if (error) {
			yycleanup();
			return (error);
		}
		$$.a_type = $5;
		$$.a_flags |= $4;
	}
	| entry_type idname ace_perms iflags access_type COLON id
	{
		int error;
		uid_t  id;

		if (yyinteractive) {
			acl_error(dgettext(TEXT_DOMAIN,
			    "Extra fields on the end of "
			    "ACL specification.\n"));
			yycleanup();
			return (EACL_UNKNOWN_DATA);
		}
		error = get_id($1, $2, &id);
		if (error) {
			$$.a_who = get_id_nofail($1, $7);
		} else {
			$$.a_who = id;
		}

		$$.a_flags = ace_entry_type($1);
		error = ace_perm_mask(&$3, &$$.a_access_mask);
		if (error) {
			yycleanup();
			return (error);
		}

		$$.a_type = $5;
		$$.a_flags |= $4;
	}
	| entry_type ace_perms access_type
	{ 
		int error;

		$$.a_who = -1;
		$$.a_flags = ace_entry_type($1);
		error = ace_perm_mask(&$2, &$$.a_access_mask);
		if (error) {
			yycleanup();
			return (error);
		}
		$$.a_type = $3;
	} 
	| entry_type ace_perms access_type COLON id
	{
		yycleanup();
		if (yyinteractive) {
			acl_error(dgettext(TEXT_DOMAIN,
			    "Extra fields on the end of "
			    "ACL specification.\n"));
			return (EACL_UNKNOWN_DATA);
		}

		return (EACL_ENTRY_ERROR);
	}
	| entry_type ace_perms iflags access_type 
	{
		int error;

		$$.a_who = -1;
		$$.a_flags = ace_entry_type($1);
		error = ace_perm_mask(&$2, &$$.a_access_mask);
		if (error) {
			yycleanup();
			return (error);
		}
		$$.a_type = $4;
		$$.a_flags |= $3;

	}
	| entry_type ace_perms iflags access_type COLON id
	{
		yycleanup();
		if (yyinteractive) {
			acl_error(dgettext(TEXT_DOMAIN,
			    "Extra fields on the end of "
			    "ACL specification.\n"));
			return (EACL_UNKNOWN_DATA);
		}
		return (EACL_ENTRY_ERROR);
	}

aclent: entry_type idname aclent_perm	/* user or group */
	{
		int error;
		uid_t id;

		error = get_id($1, $2, &id);
		if (error) {
			bad_entry_type($1, $2);
			yycleanup();
			return (EACL_INVALID_USER_GROUP);
		}

		error = compute_aclent_perms($3.perm_str, &$$.a_perm);
		if (error) {
			acl_error(dgettext(TEXT_DOMAIN,
			    "Invalid permission(s) '%s' specified.\n"),
			    $3.perm_str);
			yycleanup();
			return (error);
		}
		$$.a_id = id;
		error = aclent_entry_type($1, 0, &$$.a_type);
		if (error) {
			acl_error(
			    dgettext(TEXT_DOMAIN,
			    "Invalid ACL entry type '%s' specified.\n"), $1);
			yycleanup();
			return (error);
		}
	}
	| entry_type COLON aclent_perm		/* owner group other */
	{
		int error;

		error = compute_aclent_perms($3.perm_str, &$$.a_perm);
		if (error) {
			acl_error(dgettext(TEXT_DOMAIN,
			    "Invalid permission(s) '%s' specified.\n"),
			    $3.perm_str);
			yycleanup();
			return (error);
		}
		$$.a_id = -1;
		error = aclent_entry_type($1, 1, &$$.a_type);
		if (error) {
			acl_error(
			    dgettext(TEXT_DOMAIN,
			    "Invalid ACL entry type '%s' specified.\n"), $1);
			yycleanup();
			return (error);
		}
	}
	| entry_type COLON aclent_perm COLON id
	{ 
		yycleanup();
		if (yyinteractive) {
			acl_error(dgettext(TEXT_DOMAIN,
			    "Extra fields on the end of ACL specification.\n"));
			return (EACL_UNKNOWN_DATA);
		}
		return (EACL_ENTRY_ERROR);
	}
	| entry_type idname aclent_perm COLON id 	/* user or group */
	{	
		int error;
		uid_t id;

		if (yyinteractive) {
			acl_error(dgettext(TEXT_DOMAIN,
			    "Extra fields on the end of ACL specification.\n"));
			yycleanup();
			return (EACL_UNKNOWN_DATA);
		}
		error = compute_aclent_perms($3.perm_str, &$$.a_perm);
		if (error) {
			acl_error(dgettext(TEXT_DOMAIN,
			    "Invalid permission(s) '%s' specified.\n"),
			    $3.perm_str);
			yycleanup();
			return (error);
		}
		error = get_id($1, $2, &id);
		if (error) {
			$$.a_id = get_id_nofail($1, $5);
		} else 
			$$.a_id = id;

		error = aclent_entry_type($1, 0, &$$.a_type);
		if (error) {
			acl_error(
			    dgettext(TEXT_DOMAIN,
			    "Invalid ACL entry type '%s' specified.\n"), $1);
			yycleanup();
			return (error);
		}
	}
	| entry_type aclent_perm  /* mask entry */
	{
		int error;

		error = compute_aclent_perms($2.perm_str, &$$.a_perm);
		if (error) {
			acl_error(dgettext(TEXT_DOMAIN,
			    "Invalid permission(s) '%s' specified.\n"),
			    $2.perm_str);
			yycleanup();
			return (error);
		}
		$$.a_id = -1;
		error = aclent_entry_type($1, 0, &$$.a_type);
		if (error) {
			acl_error(
			    dgettext(TEXT_DOMAIN,
			    "Invalid ACL entry type specified %d.\n"),
			    error);
			yycleanup();
			return (error);
		}
	}
	| entry_type aclent_perm COLON id
	{
		yycleanup();
		if (yyinteractive) {
			acl_error(dgettext(TEXT_DOMAIN,
			    "Extra fields on the end of ACL specification.\n"));
			return (EACL_UNKNOWN_DATA);
		}
		return (EACL_ENTRY_ERROR);
	}

iflags: compact_iflag COLON {$$ = $1;}
	| verbose_iflag COLON {$$ = $1;}
	| COLON {$$ = 0;}

compact_iflag : INHERIT_TOK
	{
		int error;
		uint32_t iflags;

		error = compute_ace_inherit($1, &iflags);
		if (error) {
			acl_error(dgettext(TEXT_DOMAIN,
			    "Invalid inheritance flags '%s' specified.\n"), $1);
			yycleanup();
			return (error);
		}
		$$ = iflags;
	}
	| INHERIT_TOK SLASH verbose_iflag
	{
		acl_error(dgettext(TEXT_DOMAIN,
		    "Can't mix compact inherit flags with"
		    " verbose inheritance flags.\n"));
		yycleanup();
		return (EACL_INHERIT_ERROR);
	}

verbose_iflag: ACE_INHERIT	{$$ |= $1;}
	| ACE_INHERIT SLASH verbose_iflag {$$ = $1 | $3;}
	| ACE_INHERIT SLASH compact_iflag
	{
		acl_error(dgettext(TEXT_DOMAIN,
		    "Can't mix verbose inherit flags with"
		    " compact inheritance flags.\n"));
		yycleanup();
		return (EACL_INHERIT_ERROR);
	}
	| ACE_INHERIT SLASH ACCESS_TYPE
	{
		acl_error(dgettext(TEXT_DOMAIN,
		    "Inheritance flags can't be mixed with access type.\n"));
		yycleanup();
		return (EACL_INHERIT_ERROR);
	}
	| ACE_INHERIT SLASH ERROR
	{
		yycleanup();
		return ($3);
	}
	
aclent_perm: PERM_TOK
	{
		$$.perm_style = PERM_TYPE_UNKNOWN;
		$$.perm_str = $1;
		$$.perm_val = 0;
	}
	| PERM_TOK ERROR 
	{
		acl_error(dgettext(TEXT_DOMAIN,
		    "ACL entry permissions are incorrectly specified.\n"));
		yycleanup();
		return ($2);
	}

access_type: ACCESS_TYPE {$$ = $1;}	
	| ERROR
	{
		yycleanup();
		return ($1);
	}

id: ID {$$ = $1;}
	| SID {$$ = $1;}
  	| COLON
	{
		acl_error(dgettext(TEXT_DOMAIN,
		    "Invalid uid/gid specified.\nThe field"
		    " should be a numeric value.\n")); 
		yycleanup();
		return (EACL_UNKNOWN_DATA);
	}
	| ERROR
	{
		yycleanup();
		return ($1);
	}

ace_perms: perm {$$ = $1;}
	| aclent_perm COLON {$$ = $1;}
	| ERROR
	{
		yycleanup();
		return ($1);
	}

perm: perms COLON {$$ = $1;}
    	| COLON {$$.perm_style = PERM_TYPE_EMPTY;}

perms: ACE_PERM 
     	{
		$$.perm_style = PERM_TYPE_ACE;
		$$.perm_val |= $1;
	}
	| ACE_PERM SLASH perms
	{
		$$.perm_style = PERM_TYPE_ACE;
		$$.perm_val = $1 | $3.perm_val;
	}
	| ACE_PERM SLASH aclent_perm
	{

		acl_error(dgettext(TEXT_DOMAIN,
		   "Can't mix verbose permissions with"
		    " compact permission.\n"));
		yycleanup();
		return (EACL_PERM_MASK_ERROR);

	}
	| ACE_PERM SLASH ERROR
	{
		yycleanup();
		return ($3);
	}
		

idname: IDNAME {$$ = $1;}

entry_type: ENTRY_TYPE {$$ = $1;}
	| ERROR
	{
		yycleanup();
		return ($1);
	}

%%
static void
bad_entry_type(int toketype, char *str)
{
	switch(toketype) {
	case USER_TOK:
	case DEFAULT_USER_TOK:
		acl_error(dgettext(TEXT_DOMAIN,
		    "Invalid user %s specified.\n"), str);
		break;

	case GROUP_TOK:
	case DEFAULT_GROUP_TOK:
		acl_error(dgettext(TEXT_DOMAIN,
		    "Invalid group %s specified.\n"), str);
		break;
	
	case USER_SID_TOK:
		acl_error(dgettext(TEXT_DOMAIN,
		    "Invalid user SID %s specified.\n"), str);
		break;

	case GROUP_SID_TOK:
		acl_error(dgettext(TEXT_DOMAIN,
		    "Invalid group SID %s specified.\n"), str);
	}

}