/* * 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; }