/* * 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. * * Copyright 2022 RackTop Systems, Inc. */ %{ #include #include #include #include #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-]+ %% user: { BEGIN NS; yylval.val = USER_TOK; return (ENTRY_TYPE); } usersid: { BEGIN NS; yylval.val = USER_SID_TOK; return (ENTRY_TYPE); } owner@: { BEGIN PS; yylval.val = OWNERAT_TOK; return (ENTRY_TYPE); } group@: { BEGIN PS; yylval.val = GROUPAT_TOK; return (ENTRY_TYPE); } everyone@: { BEGIN PS; yylval.val = EVERYONEAT_TOK; return (ENTRY_TYPE); } group: { BEGIN NS; yylval.val = GROUP_TOK; return (ENTRY_TYPE); } groupsid: { BEGIN NS; yylval.val = GROUP_SID_TOK; return (ENTRY_TYPE); } sid: { BEGIN NS; yylval.val = BARE_SID_TOK; return (ENTRY_TYPE); } mask: { BEGIN PS; yylval.val = MASK_TOK; return (ENTRY_TYPE); } mask:: { BEGIN PS; yylval.val = MASK_TOK; return (ENTRY_TYPE); } other: { BEGIN PS; yylval.val = OTHER_TOK; return (ENTRY_TYPE); } other:: { BEGIN PS; yylval.val = OTHER_TOK; return (ENTRY_TYPE); } defaultuser: { BEGIN NS; yylval.val = DEFAULT_USER_TOK; return (ENTRY_TYPE); } default:user: { BEGIN NS; yylval.val = DEFAULT_USER_TOK; return (ENTRY_TYPE); } defaultgroup: { BEGIN NS; yylval.val = DEFAULT_GROUP_TOK; return (ENTRY_TYPE); } default:group: { BEGIN NS; yylval.val = DEFAULT_GROUP_TOK; return (ENTRY_TYPE); } defaultother: { BEGIN PS; yylval.val = DEFAULT_OTHER_TOK; return (ENTRY_TYPE); } defaultother:: { BEGIN PS; yylval.val = DEFAULT_OTHER_TOK; return (ENTRY_TYPE); } default:other: { BEGIN PS; yylval.val = DEFAULT_OTHER_TOK; return (ENTRY_TYPE); } defaultmask: { BEGIN PS; yylval.val = DEFAULT_MASK_TOK; return (ENTRY_TYPE); } defaultmask:: { BEGIN PS; yylval.val = DEFAULT_MASK_TOK; return (ENTRY_TYPE); } default:mask: { BEGIN PS; yylval.val = DEFAULT_MASK_TOK; return (ENTRY_TYPE); } "\n" { return (NL); } . { 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); } : { BEGIN PS; return (COLON); } {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); } "\n" { acl_error(dgettext(TEXT_DOMAIN, "Missing user/group name" " from ACL specification.\n")); yylval.val = EACL_MISSING_FIELDS; return (ERROR); } . { 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); } read_data/[:/,] { yylval.val = ACE_READ_DATA; return (ACE_PERM); } list_directory/[:/,] { yylval.val = ACE_LIST_DIRECTORY; return (ACE_PERM); } write_data/[:/,] { yylval.val = ACE_WRITE_DATA; return (ACE_PERM); } add_file/[:/,] { yylval.val = ACE_ADD_FILE; return (ACE_PERM); } append_data/[:/,] { yylval.val = ACE_APPEND_DATA; return (ACE_PERM); } add_subdirectory/[:/,] { yylval.val = ACE_ADD_SUBDIRECTORY; return (ACE_PERM); } read_xattr/[:/,] { yylval.val = ACE_READ_NAMED_ATTRS; return (ACE_PERM); } write_xattr/[:/,] { yylval.val = ACE_WRITE_NAMED_ATTRS; return (ACE_PERM); } execute/[:/,] { yylval.val = ACE_EXECUTE; return (ACE_PERM); } delete_child/[:/,] { yylval.val = ACE_DELETE_CHILD; return (ACE_PERM); } read_attributes/[:/,] { yylval.val = ACE_READ_ATTRIBUTES; return (ACE_PERM); } write_attributes/[:/,] { yylval.val = ACE_WRITE_ATTRIBUTES; return (ACE_PERM); } delete/[:/,] { yylval.val = ACE_DELETE; return (ACE_PERM); } read_acl/[:/,] { yylval.val = ACE_READ_ACL; return (ACE_PERM); } write_acl/[:/,] { yylval.val = ACE_WRITE_ACL; return (ACE_PERM); } write_owner/[:/,] { yylval.val = ACE_WRITE_OWNER; return (ACE_PERM); } synchronize/[:/,] { yylval.val = ACE_SYNCHRONIZE; return (ACE_PERM); } read_set/[:/,] { yylval.val = ACE_READ_PERMS; return (ACE_PERM); } write_set/[:/,] { yylval.val = ACE_WRITE_PERMS; return (ACE_PERM); } modify_set/[:/,] { yylval.val = ACE_MODIFY_PERMS; return (ACE_PERM); } full_set/[:/,] { yylval.val = ACE_ALL_PERMS; return (ACE_PERM); } {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); } "/:" { acl_error(dgettext(TEXT_DOMAIN, "Invalid permission /: specified.\n")); yylval.val = EACL_ENTRY_ERROR; return (ERROR); } : { int c; c = input(); unput(c); if (isdigit(c)) BEGIN (US); else BEGIN AIS; return (COLON); } "/" { return (SLASH); } "\n" { acl_error(dgettext(TEXT_DOMAIN, "ACL entry is missing " "permission fields.\n")); yylval.val = EACL_MISSING_FIELDS; return (ERROR); } "," { acl_error( dgettext(TEXT_DOMAIN, "The ',' is not a valid permission field " "separator.\nThe comma is used to separate " "access control entries.\nSee acl(7) for " "examples of specifying ACL entries.\n")); yylval.val = EACL_PERM_MASK_ERROR; return (ERROR); } . { 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); } 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); } 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); } 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); } 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); } : { 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); } "\n" { acl_error(dgettext(TEXT_DOMAIN, "ACL access type must be specified.\n")); yylval.val = EACL_INVALID_ACCESS_TYPE; return (ERROR); } . { 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); } 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); } 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); } 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); } 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); } file_inherit/[:/,] { yylval.val = ACE_FILE_INHERIT_ACE; return (ACE_INHERIT); } dir_inherit/[:/,] { yylval.val = ACE_DIRECTORY_INHERIT_ACE; return (ACE_INHERIT); } no_propagate/[/:,] { yylval.val = ACE_NO_PROPAGATE_INHERIT_ACE; return (ACE_INHERIT); } inherit_only/[/:,] { yylval.val = ACE_INHERIT_ONLY_ACE; return (ACE_INHERIT); } successful_access/[/:,] { yylval.val = ACE_SUCCESSFUL_ACCESS_ACE_FLAG; return (ACE_INHERIT); } failed_access/[/:,] { yylval.val = ACE_FAILED_ACCESS_ACE_FLAG; return (ACE_INHERIT); } inherited/[/:,] { yylval.val = ACE_INHERITED_ACE; return (ACE_INHERIT); } {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); } : { /* * Only inheritance fields should hit this. * allow/deny fields match on ":" as part * of the regexp. */ BEGIN AS; return (COLON); } "/" { return (SLASH); } "\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); } "," { acl_error( dgettext(TEXT_DOMAIN, "The ',' is not a valid inheritance field " "separator.\nThe comma is used to separate " "access control entries.\nSee acl(7) for " "examples of specifying ACL entries.\n")); yylval.val = EACL_INVALID_ACCESS_TYPE; return (ERROR); } . { 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); } {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); } {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); } : { return (COLON); } {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); } . { 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); } "\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); } "," { BEGIN TS; return (COMMA); } . { 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); } "\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; } } static int sid_isuser = 0; /* * return ACE entry type */ int ace_entry_type(int type) { int ret = -1; switch (type) { case BARE_SID_TOK: if (sid_isuser == 0) ret = ACE_IDENTIFIER_GROUP; else ret = 0; break; 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; case BARE_SID_TOK: if (sid_to_xid(name, &sid_isuser, 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; }