/*
* 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 <sys/acl.h>
#include <aclutils.h>
#include <idmap.h>
#include <errno.h>
#include "acl.tab.h"
#ifdef input
#undef input
#endif
#ifdef unput
#undef unput
#endif
int grab_string(char *terminators);
static int input();
static void unput(int);
int
yyerror(const char *s)
{
return (0);
}
int
yywrap(void)
{
return (1);
}
extern char *yybuf;
int yybufpos;
/*
* Used for tracking allocated strings while walking through an ACL.
*/
struct yystrings {
char *y_logname; /* user/group name from LOGNAME */
char *y_perms; /* permssions from PERM_TOK */
char *y_iflags; /* iflags from INHERIT_TOK */
char *y_idstr; /* string of appened id */
} yystrings;
%}
%e 1500
%s TS NS PS AIS AS US ES
%p 5000
/*
* TS = type state
* NS = name state
* PS = Permission state
* AIS = Allow/deny/inheritance state
* AS = Allow state (only used when inheritance detected)
* US = UID/GID state
* ES = End state
*/
ID [0-9]+
SID S-[^:,\n]+
LOGNAME [^:]+:
PERM_STR [rRwWxpdDaAcCos-]+
INHERIT_STR [fdinFSI-]+
%%
<TS>user: {
BEGIN NS;
yylval.val = USER_TOK;
return (ENTRY_TYPE);
}
<TS>usersid: {
BEGIN NS;
yylval.val = USER_SID_TOK;
return (ENTRY_TYPE);
}
<TS>owner@: {
BEGIN PS;
yylval.val = OWNERAT_TOK;
return (ENTRY_TYPE);
}
<TS>group@: {
BEGIN PS;
yylval.val = GROUPAT_TOK;
return (ENTRY_TYPE);
}
<TS>everyone@: {
BEGIN PS;
yylval.val = EVERYONEAT_TOK;
return (ENTRY_TYPE);
}
<TS>group: {
BEGIN NS;
yylval.val = GROUP_TOK;
return (ENTRY_TYPE);
}
<TS>groupsid: {
BEGIN NS;
yylval.val = GROUP_SID_TOK;
return (ENTRY_TYPE);
}
<TS>sid: {
BEGIN NS;
yylval.val = GROUP_SID_TOK;
return (ENTRY_TYPE);
}
<TS>mask: {
BEGIN PS;
yylval.val = MASK_TOK;
return (ENTRY_TYPE);
}
<TS>mask:: {
BEGIN PS;
yylval.val = MASK_TOK;
return (ENTRY_TYPE);
}
<TS>other: {
BEGIN PS;
yylval.val = OTHER_TOK;
return (ENTRY_TYPE);
}
<TS>other:: {
BEGIN PS;
yylval.val = OTHER_TOK;
return (ENTRY_TYPE);
}
<TS>defaultuser: {
BEGIN NS;
yylval.val = DEFAULT_USER_TOK;
return (ENTRY_TYPE);
}
<TS>default:user: {
BEGIN NS;
yylval.val = DEFAULT_USER_TOK;
return (ENTRY_TYPE);
}
<TS>defaultgroup: {
BEGIN NS;
yylval.val = DEFAULT_GROUP_TOK;
return (ENTRY_TYPE);
}
<TS>default:group: {
BEGIN NS;
yylval.val = DEFAULT_GROUP_TOK;
return (ENTRY_TYPE);
}
<TS>defaultother: {
BEGIN PS;
yylval.val = DEFAULT_OTHER_TOK;
return (ENTRY_TYPE);
}
<TS>defaultother:: {
BEGIN PS;
yylval.val = DEFAULT_OTHER_TOK;
return (ENTRY_TYPE);
}
<TS>default:other: {
BEGIN PS;
yylval.val = DEFAULT_OTHER_TOK;
return (ENTRY_TYPE);
}
<TS>defaultmask: {
BEGIN PS;
yylval.val = DEFAULT_MASK_TOK;
return (ENTRY_TYPE);
}
<TS>defaultmask:: {
BEGIN PS;
yylval.val = DEFAULT_MASK_TOK;
return (ENTRY_TYPE);
}
<TS>default:mask: {
BEGIN PS;
yylval.val = DEFAULT_MASK_TOK;
return (ENTRY_TYPE);
}
<TS>"\n" {
return (NL);
}
<TS>. {
if (grab_string(":,\n") != 0) {
acl_error(dgettext(TEXT_DOMAIN,
"Failed to retrieve"
" error string.\n"));
yylval.val = EACL_MEM_ERROR;
return (ERROR);
}
acl_error(dgettext(TEXT_DOMAIN,
"Invalid ACL entry "
"type '%s' specified.\n"), yylval.str);
free(yylval.str);
yylval.val = EACL_ENTRY_ERROR;
return (ERROR);
}
<NS>: {
BEGIN PS;
return (COLON);
}
<NS>{LOGNAME} {
yylval.str = strdup(yytext);
if (yylval.str == NULL) {
yylval.val = EACL_MEM_ERROR;
return (ERROR);
}
yylval.str[strlen(yylval.str) -1] = '\0';
yystrings.y_logname = yylval.str;
BEGIN PS;
return (IDNAME);
}
<NS>"\n" {
acl_error(dgettext(TEXT_DOMAIN,
"Missing user/group name"
" from ACL specification.\n"));
yylval.val = EACL_MISSING_FIELDS;
return (ERROR);
}
<NS>. {
int error;
error = grab_string(":,\n");
if (error != 0) {
acl_error(dgettext(TEXT_DOMAIN,
"Invalid user/group "
"name specification.\n"));
yylval.val = EACL_INVALID_USER_GROUP;
} else {
acl_error(dgettext(TEXT_DOMAIN,
"User/Group name "
"'%s' not specified correctly.\n"),
yylval.str);
free(yylval.str);
yylval.val = EACL_ENTRY_ERROR;
}
return (ERROR);
}
<PS>read_data/[:/,] {
yylval.val = ACE_READ_DATA;
return (ACE_PERM);
}
<PS>list_directory/[:/,] {
yylval.val = ACE_LIST_DIRECTORY;
return (ACE_PERM);
}
<PS>write_data/[:/,] {
yylval.val = ACE_WRITE_DATA;
return (ACE_PERM);
}
<PS>add_file/[:/,] {
yylval.val = ACE_ADD_FILE;
return (ACE_PERM);
}
<PS>append_data/[:/,] {
yylval.val = ACE_APPEND_DATA;
return (ACE_PERM);
}
<PS>add_subdirectory/[:/,] {
yylval.val = ACE_ADD_SUBDIRECTORY;
return (ACE_PERM);
}
<PS>read_xattr/[:/,] {
yylval.val = ACE_READ_NAMED_ATTRS;
return (ACE_PERM);
}
<PS>write_xattr/[:/,] {
yylval.val = ACE_WRITE_NAMED_ATTRS;
return (ACE_PERM);
}
<PS>execute/[:/,] {
yylval.val = ACE_EXECUTE;
return (ACE_PERM);
}
<PS>delete_child/[:/,] {
yylval.val = ACE_DELETE_CHILD;
return (ACE_PERM);
}
<PS>read_attributes/[:/,] {
yylval.val = ACE_READ_ATTRIBUTES;
return (ACE_PERM);
}
<PS>write_attributes/[:/,] {
yylval.val = ACE_WRITE_ATTRIBUTES;
return (ACE_PERM);
}
<PS>delete/[:/,] {
yylval.val = ACE_DELETE;
return (ACE_PERM);
}
<PS>read_acl/[:/,] {
yylval.val = ACE_READ_ACL;
return (ACE_PERM);
}
<PS>write_acl/[:/,] {
yylval.val = ACE_WRITE_ACL;
return (ACE_PERM);
}
<PS>write_owner/[:/,] {
yylval.val = ACE_WRITE_OWNER;
return (ACE_PERM);
}
<PS>synchronize/[:/,] {
yylval.val = ACE_SYNCHRONIZE;
return (ACE_PERM);
}
<PS>read_set/[:/,] {
yylval.val = ACE_READ_PERMS;
return (ACE_PERM);
}
<PS>write_set/[:/,] {
yylval.val = ACE_WRITE_PERMS;
return (ACE_PERM);
}
<PS>modify_set/[:/,] {
yylval.val = ACE_MODIFY_PERMS;
return (ACE_PERM);
}
<PS>full_set/[:/,] {
yylval.val = ACE_ALL_PERMS;
return (ACE_PERM);
}
<PS>{PERM_STR}/[:,\n] {
int c;
c = input();
unput(c);
yylval.str = strdup(yytext);
if (yylval.str == NULL) {
yylval.val = EACL_MEM_ERROR;
return (ERROR);
}
yystrings.y_perms = yylval.str;
/*
* aclent are done after permissions.
*/
if (isdigit(c))
BEGIN US;
else if (c != ':')
BEGIN ES;
return (PERM_TOK);
}
<PS>"/:" {
acl_error(dgettext(TEXT_DOMAIN,
"Invalid permission /: specified.\n"));
yylval.val = EACL_ENTRY_ERROR;
return (ERROR);
}
<PS>: {
int c;
c = input();
unput(c);
if (isdigit(c))
BEGIN (US);
else
BEGIN AIS;
return (COLON);
}
<PS>"/" {
return (SLASH);
}
<PS>"\n" {
acl_error(dgettext(TEXT_DOMAIN,
"ACL entry is missing "
"permission fields.\n"));
yylval.val = EACL_MISSING_FIELDS;
return (ERROR);
}
<PS>"," {
acl_error(
dgettext(TEXT_DOMAIN,
"The ',' is not a valid permission field "
"separator.\nThe comma is used to separate "
"access control entries.\nSee acl(5) for "
"examples of specifying ACL entries.\n"));
yylval.val = EACL_PERM_MASK_ERROR;
return (ERROR);
}
<PS>. {
if (grab_string("/:,\n") != 0) {
acl_error(dgettext(TEXT_DOMAIN,
"Failed to retrieve"
" error string.\n"));
yylval.val = EACL_MEM_ERROR;
return (ERROR);
}
acl_error(dgettext(TEXT_DOMAIN,
"Invalid permission(s) '%s' "
"specified.\n"), yylval.str);
free(yylval.str);
yylval.val = EACL_PERM_MASK_ERROR;
return (ERROR);
}
<AS>allow/[:,\n] {
int c;
c = input();
unput(c);
if (c == ',' || c == '\n')
BEGIN ES;
else
BEGIN US;
yylval.val = ACE_ACCESS_ALLOWED_ACE_TYPE;
return (ACCESS_TYPE);
}
<AS>deny/[:,\n] {
int c;
c = input();
unput(c);
if (c == ',' || c == '\n')
BEGIN ES;
else
BEGIN US;
yylval.val = ACE_ACCESS_DENIED_ACE_TYPE;
return (ACCESS_TYPE);
}
<AS>audit/[:,\n] {
int c;
c = input();
unput(c);
if (c == ',' || c == '\n')
BEGIN ES;
else
BEGIN US;
yylval.val = ACE_SYSTEM_AUDIT_ACE_TYPE;
return (ACCESS_TYPE);
}
<AS>alarm/[:,\n] {
int c;
c = input();
unput(c);
if (c == ',' || c == '\n')
BEGIN ES;
else
BEGIN US;
yylval.val = ACE_SYSTEM_ALARM_ACE_TYPE;
return (ACCESS_TYPE);
}
<AS>: {
acl_error(dgettext(TEXT_DOMAIN,
"Invalid Access type "
"specified.\nThe field is blank, when"
" it should be either allow or deny.\n"));
yylval.val = EACL_INVALID_ACCESS_TYPE;
return (ERROR);
}
<AS>"\n" {
acl_error(dgettext(TEXT_DOMAIN,
"ACL access type must be specified.\n"));
yylval.val = EACL_INVALID_ACCESS_TYPE;
return (ERROR);
}
<AS>. {
if (yytext[0] != '\n' && yytext[0] != '\0') {
if (grab_string(":,\n") != 0) {
acl_error(dgettext(TEXT_DOMAIN,
"Failed to "
"retrieve error "
"string.\n"));
yylval.val = EACL_MEM_ERROR;
return (ERROR);
}
acl_error(
dgettext(TEXT_DOMAIN,
"Invalid access "
"type '%s' specified.\n"),
yylval.str);
} else {
acl_error(
dgettext(TEXT_DOMAIN,
"No access "
"type specified.\n"), yylval.str);
}
free(yylval.str);
yylval.val = EACL_INVALID_ACCESS_TYPE;
return (ERROR);
}
<AIS>allow/[:,\n] {
int c;
c = input();
unput(c);
if (c == ',' || c == '\n')
BEGIN ES;
else
BEGIN US;
yylval.val = ACE_ACCESS_ALLOWED_ACE_TYPE;
return (ACCESS_TYPE);
}
<AIS>deny/[:,\n] {
int c;
c = input();
unput(c);
if (c == ',' || c == '\n')
BEGIN ES;
else
BEGIN US;
yylval.val = ACE_ACCESS_DENIED_ACE_TYPE;
return (ACCESS_TYPE);
}
<AIS>audit/[:,\n] {
int c;
c = input();
unput(c);
if (c == ',' || c == '\n')
BEGIN ES;
else
BEGIN US;
yylval.val = ACE_SYSTEM_AUDIT_ACE_TYPE;
return (ACCESS_TYPE);
}
<AIS>alarm/[:,\n] {
int c;
c = input();
unput(c);
if (c == ',' || c == '\n')
BEGIN ES;
else
BEGIN US;
yylval.val = ACE_SYSTEM_ALARM_ACE_TYPE;
return (ACCESS_TYPE);
}
<AIS>file_inherit/[:/,] {
yylval.val = ACE_FILE_INHERIT_ACE;
return (ACE_INHERIT);
}
<AIS>dir_inherit/[:/,] {
yylval.val = ACE_DIRECTORY_INHERIT_ACE;
return (ACE_INHERIT);
}
<AIS>no_propagate/[/:,] {
yylval.val = ACE_NO_PROPAGATE_INHERIT_ACE;
return (ACE_INHERIT);
}
<AIS>inherit_only/[/:,] {
yylval.val = ACE_INHERIT_ONLY_ACE;
return (ACE_INHERIT);
}
<AIS>successful_access/[/:,] {
yylval.val = ACE_SUCCESSFUL_ACCESS_ACE_FLAG;
return (ACE_INHERIT);
}
<AIS>failed_access/[/:,] {
yylval.val = ACE_FAILED_ACCESS_ACE_FLAG;
return (ACE_INHERIT);
}
<AIS>inherited/[/:,] {
yylval.val = ACE_INHERITED_ACE;
return (ACE_INHERIT);
}
<AIS>{INHERIT_STR}/[:] {
yylval.str = strdup(yytext);
if (yylval.str == NULL) {
yylval.val = EACL_MEM_ERROR;
return (ERROR);
}
yystrings.y_iflags = yylval.str;
return (INHERIT_TOK);
}
<AIS>: {
/*
* Only inheritance fields should hit this.
* allow/deny fields match on ":" as part
* of the regexp.
*/
BEGIN AS;
return (COLON);
}
<AIS>"/" {
return (SLASH);
}
<AIS>"\n" {
acl_error(
dgettext(TEXT_DOMAIN,
"Invalid ACL specification."
"\nWas expecting to find"
" access type or inheritance flags.\n"),
yylval.str);
yylval.val = EACL_UNKNOWN_DATA;
return (ERROR);
}
<AIS>"," {
acl_error(
dgettext(TEXT_DOMAIN,
"The ',' is not a valid inheritance field "
"separator.\nThe comma is used to separate "
"access control entries.\nSee acl(5) for "
"examples of specifying ACL entries.\n"));
yylval.val = EACL_INVALID_ACCESS_TYPE;
return (ERROR);
}
<AIS>. {
if (yytext[0] != '\n' && yytext[0] != '\0') {
if (grab_string(":,\n") != 0) {
acl_error(dgettext(TEXT_DOMAIN,
"Failed to "
"retrieve error "
"string.\n"));
yylval.val = EACL_MEM_ERROR;
return (ERROR);
}
acl_error(
dgettext(TEXT_DOMAIN,
"Invalid inheritance or"
" access type '%s' specified.\n"),
yylval.str);
} else {
acl_error(
dgettext(TEXT_DOMAIN,
"No inheritance or "
"access type specified.\n"),
yylval.str);
}
free(yylval.str);
yylval.val = EACL_INVALID_ACCESS_TYPE;
return (ERROR);
}
<US>{ID}/[,\n] {
BEGIN ES;
yylval.str = strdup(yytext);
if (yylval.str == NULL) {
yylval.val = EACL_MEM_ERROR;
return (ERROR);
}
yystrings.y_idstr = yylval.str;
return (ID);
}
<US>{SID}/[,\n] {
BEGIN ES;
yylval.str = strdup(yytext);
if (yylval.str == NULL) {
yylval.val = EACL_MEM_ERROR;
return (ERROR);
}
yystrings.y_idstr = yylval.str;
return (SID);
}
<US>: {
return (COLON);
}
<US>{INHERIT_STR} { /*
* Catch specific error to produce
* nice message for users who are trying
* to use old syntax format which had
* inheritance flags as the last field.
*/
acl_error(dgettext(TEXT_DOMAIN,
"Access type should be final"
" field in ACL specification.\n"));
yylval.val = EACL_ENTRY_ERROR;
return (ERROR);
}
<US>. {
if (grab_string(",\n") != 0) {
acl_error(dgettext(TEXT_DOMAIN,
"Failed to retrieve"
" error string.\n"));
yylval.val = EACL_MEM_ERROR;
return (ERROR);
}
acl_error(
dgettext(TEXT_DOMAIN,
"Invalid data ':%s' specified"
" on end of ACL.\n"), yylval.str);
free(yylval.str);
yylval.val = EACL_ENTRY_ERROR;
return (ERROR);
}
<US>"\n" {
acl_error(dgettext(TEXT_DOMAIN,
"Missing fields in ACL "
"specification.\nWas expecting to find "
"uid/gid.\n"));
yylval.val = EACL_ENTRY_ERROR;
return (ERROR);
}
<ES>"," {
BEGIN TS;
return (COMMA);
}
<ES>. {
if (grab_string("/:,\n") != 0) {
acl_error(
dgettext(TEXT_DOMAIN,
"Failed to retrieve error"
" string.\n"));
yylval.val = EACL_MEM_ERROR;
return (ERROR);
}
acl_error(
dgettext(TEXT_DOMAIN,
"Unrecognized data '%s' found"
" in ACL specification.\n"), yylval.str);
free(yylval.str);
yylval.val = EACL_UNKNOWN_DATA;
return (ERROR);
}
<ES>"\n" {
return (NL);
}
%%
/*
* Pull string up to terminator off of input string.
* used for retrieving illegal data in ACL specification.
*
* The first set of characters is retrieved from yytext.
* subsequent characters are pulled from the input stream,
* until either EOF or one of the requested terminators is scene.
* Result is returned in yylval.str which is malloced.
*/
int
grab_string(char *terminators)
{
int c;
int done = 0;
int cnt;
int alloced;
int error = 0;
char *ptr;
cnt = strlen(yytext);
yylval.str = calloc(cnt + 1, sizeof (char));
if (yylval.str == NULL) {
return (1);
}
alloced = cnt + 1;
strcpy(yylval.str, yytext);
do {
c = input();
if (c == EOF)
break;
for (ptr = terminators; *ptr; ptr++) {
if (c == *ptr) {
done = 1;
break;
}
}
if (done)
break;
if (cnt + 1 >= alloced) {
yylval.str = realloc(yylval.str,
alloced + 80);
alloced += 80;
if (yylval.str == NULL)
return (1);
memset(yylval.str + cnt, 0,
alloced - strlen(yylval.str));
}
yylval.str[strlen(yylval.str)] = c;
cnt++;
} while (!done);
return (error);
}
static int
input(void)
{
int c;
c = yybuf[yybufpos++];
if (c == '\0') {
return (EOF);
}
return (c);
}
static void
unput(int c)
{
if (c == '\0') {
return;
}
if (yybufpos > 0) {
--yybufpos;
}
}
/*
* return ACE entry type
*/
int
ace_entry_type(int type)
{
int ret = -1;
switch (type) {
case USER_TOK:
case USER_SID_TOK:
ret = 0;
break;
case GROUP_TOK:
case GROUP_SID_TOK:
ret = ACE_IDENTIFIER_GROUP;
break;
case OWNERAT_TOK:
ret = ACE_OWNER;
break;
case GROUPAT_TOK:
ret = ACE_IDENTIFIER_GROUP | ACE_GROUP;
break;
case EVERYONEAT_TOK:
ret = ACE_EVERYONE;
break;
}
return (ret);
}
/*
* return aclent entry type
*/
int
aclent_entry_type(int type, int owning, int *ret)
{
*ret = 0;
switch (type) {
case USER_TOK:
*ret = (owning == 0) ? USER : USER_OBJ;
break;
case GROUP_TOK:
*ret = (owning == 0) ? GROUP : GROUP_OBJ;
break;
case OTHER_TOK:
*ret = OTHER_OBJ;
break;
case MASK_TOK:
*ret = CLASS_OBJ;
break;
case DEFAULT_USER_TOK:
*ret = (owning == 0) ? DEF_USER : DEF_USER_OBJ;
break;
case DEFAULT_GROUP_TOK:
*ret = (owning == 0) ? DEF_GROUP : DEF_GROUP_OBJ;
break;
case DEFAULT_MASK_TOK:
*ret = DEF_CLASS_OBJ;
break;
case DEFAULT_OTHER_TOK:
*ret = DEF_OTHER_OBJ;
break;
default:
return (EACL_ENTRY_ERROR);
}
return (0);
}
/*
* convert string into numeric id.
*/
static int
acl_str_to_id(char *str, uid_t *id)
{
char *end;
uid_t value;
errno = 0;
value = strtoul(str, &end, 10);
if (errno != 0 || *end != '\0')
return (EACL_INVALID_USER_GROUP);
*id = value;
return (0);
}
/*
* determine either uid/gid for given entry type
*/
int
get_id(int entry_type, char *name, uid_t *id)
{
struct passwd *pw;
struct group *gr;
int error = 0;
switch (entry_type) {
case USER_TOK:
case DEFAULT_USER_TOK:
if ((error = acl_str_to_id(name, id)) == 0)
break;
pw = getpwnam(name);
if (pw) {
*id = pw->pw_uid;
error = 0;
}
break;
case GROUP_TOK:
case DEFAULT_GROUP_TOK:
if ((error = acl_str_to_id(name, id)) == 0)
break;
gr = getgrnam(name);
if (gr) {
*id = gr->gr_gid;
error = 0;
}
break;
case USER_SID_TOK:
if (sid_to_id(name, B_TRUE, id))
error = EACL_INVALID_USER_GROUP;
break;
case GROUP_SID_TOK:
if (sid_to_id(name, B_FALSE, id))
error = EACL_INVALID_USER_GROUP;
break;
}
return (error);
}
int
get_id_nofail(int entry_type, char *name)
{
uid_t id;
if (get_id(entry_type, name, &id))
return (UID_NOBODY);
else
return (id);
}
/*
* reset beginning state to TS and set character position
* back to zero.
*/
void
yyreset()
{
yybufpos = 0;
memset(&yystrings, 0, sizeof (yystrings));
BEGIN TS;
}
void
yycleanup()
{
if (yystrings.y_logname)
free(yystrings.y_logname);
if (yystrings.y_perms)
free(yystrings.y_perms);
if (yystrings.y_iflags)
free(yystrings.y_iflags);
if (yystrings.y_idstr)
free(yystrings.y_idstr);
yystrings.y_logname = NULL;
yystrings.y_perms = NULL;
yystrings.y_iflags = NULL;
yystrings.y_idstr = NULL;
}