%{ /* * 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 extern int yyinteractive; extern acl_t *yyacl; extern int yylex(void); static void bad_entry_type(int, char *); %} %union { char *str; int val; struct acl_perm_type acl_perm; ace_t ace; aclent_t aclent; acl_t *acl; } %token BARE_SID_TOK %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 ID IDNAME PERM_TOK INHERIT_TOK SID %token ERROR ACE_PERM ACE_INHERIT ENTRY_TYPE ACCESS_TYPE %type idname id %type perms perm aclent_perm ace_perms %type acl_entry %type ace %type aclent %type 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); break; case BARE_SID_TOK: acl_error(dgettext(TEXT_DOMAIN, "Invalid SID %s specified.\n"), str); break; } } /* * 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; } /* * 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. */ /*LINTLIBRARY*/ /* * aclcheck(): check validity of an ACL * A valid ACL is defined as follows: * There must be exactly one USER_OBJ, GROUP_OBJ, and OTHER_OBJ entry. * If there are any USER entries, then the user id must be unique. * If there are any GROUP entries, then the group id must be unique. * If there are any GROUP or USER entries, there must be exactly one * CLASS_OBJ entry. * The same rules apply to default ACL entries. */ #include #include #include #include #include #include struct entry { int count; uid_t *id; }; struct entry_stat { struct entry user_obj; struct entry user; struct entry group_obj; struct entry group; struct entry other_obj; struct entry class_obj; struct entry def_user_obj; struct entry def_user; struct entry def_group_obj; struct entry def_group; struct entry def_other_obj; struct entry def_class_obj; }; static void free_mem(struct entry_stat *); static int check_dup(int, uid_t *, uid_t, struct entry_stat *); static int aclent_aclcheck(aclent_t *aclbufp, int nentries, int *which, int isdir) { struct entry_stat tally; aclent_t *aclentp; uid_t **idp; int cnt; *which = -1; memset(&tally, '\0', sizeof (tally)); for (aclentp = aclbufp; nentries > 0; nentries--, aclentp++) { switch (aclentp->a_type) { case USER_OBJ: /* check uniqueness */ if (tally.user_obj.count > 0) { *which = (int)(aclentp - aclbufp); (void) free_mem(&tally); errno = EINVAL; return (EACL_USER_ERROR); } tally.user_obj.count = 1; break; case GROUP_OBJ: /* check uniqueness */ if (tally.group_obj.count > 0) { *which = (int)(aclentp - aclbufp); (void) free_mem(&tally); errno = EINVAL; return (EACL_GRP_ERROR); } tally.group_obj.count = 1; break; case OTHER_OBJ: /* check uniqueness */ if (tally.other_obj.count > 0) { *which = (int)(aclentp - aclbufp); (void) free_mem(&tally); errno = EINVAL; return (EACL_OTHER_ERROR); } tally.other_obj.count = 1; break; case CLASS_OBJ: /* check uniqueness */ if (tally.class_obj.count > 0) { *which = (int)(aclentp - aclbufp); (void) free_mem(&tally); errno = EINVAL; return (EACL_CLASS_ERROR); } tally.class_obj.count = 1; break; case USER: case GROUP: case DEF_USER: case DEF_GROUP: /* check duplicate */ if (aclentp->a_type == DEF_USER) { cnt = (tally.def_user.count)++; idp = &(tally.def_user.id); } else if (aclentp->a_type == DEF_GROUP) { cnt = (tally.def_group.count)++; idp = &(tally.def_group.id); } else if (aclentp->a_type == USER) { cnt = (tally.user.count)++; idp = &(tally.user.id); } else { cnt = (tally.group.count)++; idp = &(tally.group.id); } if (cnt == 0) { *idp = calloc(nentries, sizeof (uid_t)); if (*idp == NULL) return (EACL_MEM_ERROR); } else { if (check_dup(cnt, *idp, aclentp->a_id, &tally) == -1) { *which = (int)(aclentp - aclbufp); return (EACL_DUPLICATE_ERROR); } } (*idp)[cnt] = aclentp->a_id; break; case DEF_USER_OBJ: /* check uniqueness */ if (tally.def_user_obj.count > 0) { *which = (int)(aclentp - aclbufp); (void) free_mem(&tally); errno = EINVAL; return (EACL_USER_ERROR); } tally.def_user_obj.count = 1; break; case DEF_GROUP_OBJ: /* check uniqueness */ if (tally.def_group_obj.count > 0) { *which = (int)(aclentp - aclbufp); (void) free_mem(&tally); errno = EINVAL; return (EACL_GRP_ERROR); } tally.def_group_obj.count = 1; break; case DEF_OTHER_OBJ: /* check uniqueness */ if (tally.def_other_obj.count > 0) { *which = (int)(aclentp - aclbufp); (void) free_mem(&tally); errno = EINVAL; return (EACL_OTHER_ERROR); } tally.def_other_obj.count = 1; break; case DEF_CLASS_OBJ: /* check uniqueness */ if (tally.def_class_obj.count > 0) { *which = (int)(aclentp - aclbufp); (void) free_mem(&tally); errno = EINVAL; return (EACL_CLASS_ERROR); } tally.def_class_obj.count = 1; break; default: (void) free_mem(&tally); errno = EINVAL; *which = (int)(aclentp - aclbufp); return (EACL_ENTRY_ERROR); } } /* If there are group or user entries, there must be one class entry */ if (tally.user.count > 0 || tally.group.count > 0) if (tally.class_obj.count != 1) { (void) free_mem(&tally); errno = EINVAL; return (EACL_MISS_ERROR); } /* same is true for default entries */ if (tally.def_user.count > 0 || tally.def_group.count > 0) if (tally.def_class_obj.count != 1) { (void) free_mem(&tally); errno = EINVAL; return (EACL_MISS_ERROR); } /* there must be exactly one user_obj, group_obj, and other_obj entry */ if (tally.user_obj.count != 1 || tally.group_obj.count != 1 || tally.other_obj.count != 1) { (void) free_mem(&tally); errno = EINVAL; return (EACL_MISS_ERROR); } /* has default? same rules apply to default entries */ if (tally.def_user.count > 0 || tally.def_user_obj.count > 0 || tally.def_group.count > 0 || tally.def_group_obj.count > 0 || tally.def_class_obj.count > 0 || tally.def_other_obj.count > 0) { /* * Can't have default ACL's on non-directories */ if (isdir == 0) { (void) free_mem(&tally); errno = EINVAL; return (EACL_INHERIT_NOTDIR); } if (tally.def_user_obj.count != 1 || tally.def_group_obj.count != 1 || tally.def_other_obj.count != 1) { (void) free_mem(&tally); errno = EINVAL; return (EACL_MISS_ERROR); } } (void) free_mem(&tally); return (0); } int aclcheck(aclent_t *aclbufp, int nentries, int *which) { return (aclent_aclcheck(aclbufp, nentries, which, 1)); } static void free_mem(struct entry_stat *tallyp) { if ((tallyp->user).count > 0) free((tallyp->user).id); if ((tallyp->group).count > 0) free((tallyp->group).id); if ((tallyp->def_user).count > 0) free((tallyp->def_user).id); if ((tallyp->def_group).count > 0) free((tallyp->def_group).id); } static int check_dup(int count, uid_t *ids, uid_t newid, struct entry_stat *tallyp) { int i; for (i = 0; i < count; i++) { if (ids[i] == newid) { errno = EINVAL; (void) free_mem(tallyp); return (-1); } } return (0); } #define IFLAGS (ACE_FILE_INHERIT_ACE|ACE_DIRECTORY_INHERIT_ACE| \ ACE_NO_PROPAGATE_INHERIT_ACE|ACE_INHERIT_ONLY_ACE) static int ace_aclcheck(acl_t *aclp, int isdir) { ace_t *acep; int i; int error = 0; /* * step through all valid flags. */ if (aclp->acl_cnt <= 0 || aclp->acl_cnt > MAX_ACL_ENTRIES) return (EACL_COUNT_ERROR); for (i = 0, acep = aclp->acl_aclp; i != aclp->acl_cnt && error == 0; i++, acep++) { switch (acep->a_flags & 0xf040) { case 0: case ACE_OWNER: case ACE_EVERYONE: case ACE_IDENTIFIER_GROUP: case ACE_GROUP|ACE_IDENTIFIER_GROUP: break; default: errno = EINVAL; return (EACL_FLAGS_ERROR); } /* * INHERIT_ONLY/NO_PROPAGATE need a to INHERIT_FILE * or INHERIT_DIR also */ if (acep->a_flags & (ACE_INHERIT_ONLY_ACE|ACE_NO_PROPAGATE_INHERIT_ACE)) { if ((acep->a_flags & (ACE_FILE_INHERIT_ACE| ACE_DIRECTORY_INHERIT_ACE)) == 0) { errno = EINVAL; return (EACL_INHERIT_ERROR); } break; } switch (acep->a_type) { case ACE_ACCESS_ALLOWED_ACE_TYPE: case ACE_ACCESS_DENIED_ACE_TYPE: case ACE_SYSTEM_AUDIT_ACE_TYPE: case ACE_SYSTEM_ALARM_ACE_TYPE: break; default: errno = EINVAL; return (EACL_ENTRY_ERROR); } if (acep->a_access_mask > ACE_ALL_PERMS) { errno = EINVAL; return (EACL_PERM_MASK_ERROR); } } return (0); } int acl_check(acl_t *aclp, int flag) { int error; int where; switch (aclp->acl_type) { case ACLENT_T: error = aclent_aclcheck(aclp->acl_aclp, aclp->acl_cnt, &where, flag); break; case ACE_T: error = ace_aclcheck(aclp, flag); break; default: errno = EINVAL; error = EACL_ENTRY_ERROR; } return (error); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (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 (c) 1993-1997 by Sun Microsystems, Inc. * All rights reserved */ /* LINTLIBRARY */ /* * Convert ACL to/from permission bits */ #include #include int acltomode(aclent_t *aclbufp, int nentries, mode_t *modep) { aclent_t *tp; unsigned long mode; unsigned long grpmode; unsigned long mask; int which; int got_mask = 0; *modep = 0; if (aclcheck(aclbufp, nentries, &which) != 0) { errno = EINVAL; return (-1); /* errno is set in aclcheck() */ } for (tp = aclbufp; nentries--; tp++) { if (tp->a_type == USER_OBJ) { mode = tp->a_perm; if (mode > 07) return (-1); *modep |= (mode << 6); continue; } if (tp->a_type == GROUP_OBJ) { grpmode = tp->a_perm; if (grpmode > 07) return (-1); continue; } if (tp->a_type == CLASS_OBJ) { got_mask = 1; mask = tp->a_perm; if (mask > 07) return (-1); *modep |= (mask << 3); continue; } if (tp->a_type == OTHER_OBJ) { mode = tp->a_perm; if (mode > 07) return (-1); *modep |= mode; continue; /* we may break here if it is sorted */ } } if (!got_mask) *modep |= (grpmode << 3); return (0); } int aclfrommode(aclent_t *aclbufp, int nentries, mode_t *modep) { aclent_t *tp; aclent_t *savp; mode_t mode; mode_t grpmode; int which; int got_mask = 0; if (aclcheck(aclbufp, nentries, &which) != 0) { errno = EINVAL; return (-1); /* errno is set in aclcheck() */ } for (tp = aclbufp; nentries--; tp++) { if (tp->a_type == USER_OBJ) { mode = (*modep & 0700); tp->a_perm = (mode >> 6); continue; } if (tp->a_type == GROUP_OBJ) { grpmode = (*modep & 070); savp = tp; continue; } if (tp->a_type == CLASS_OBJ) { got_mask = 1; mode = (*modep & 070); tp->a_perm = (mode >> 3); continue; } if (tp->a_type == OTHER_OBJ) { mode = (*modep & 07); tp->a_perm = (o_mode_t)mode; continue; /* we may break here if it is sorted */ } } if (!got_mask) savp->a_perm = (grpmode >> 3); return (0); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (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 (c) 1993-1997 by Sun Microsystems, Inc. * All rights reserved */ /*LINTLIBRARY*/ /* * aclsort(): * Sort an ACL by entry type according to the following order: * USER_OBJ, USER, GROUP_OBJ, GROUP, CLASS_OBJ, OTHER_OBJ * DEF_USER_OBJ, DEF_USER, DEF_GROUP_OBJ, DEF_GROUP, DEF_CLASS_OBJ, * DEF_OTHER_OBJ. * For USER, GROUP, DEF_USER, and DEF_GROUP entries, the entries * are further sorted by ids. */ #include #include #define TOTAL_ENTRY_TYPES 12 /* * This maps the entry defined value to a value for sorting. * These values may not be the same. It is easier to add an * entry type with this map. * * Because the defines and sorting order are not the same, * the following map_to_sort table is needed. */ struct map { int sort_order; int entry_type; }; static struct map map_to_sort[] = { {0, 0}, /* UNUSED */ {1, USER_OBJ}, {2, USER}, {3, GROUP_OBJ}, {4, GROUP}, {5, CLASS_OBJ}, {6, OTHER_OBJ}, {7, DEF_USER_OBJ}, {8, DEF_USER}, {9, DEF_GROUP_OBJ}, {10, DEF_GROUP}, {11, DEF_CLASS_OBJ}, {12, DEF_OTHER_OBJ}, }; static int entrycmp(const aclent_t *, const aclent_t *); static int idcmp(const aclent_t *, const aclent_t *); static void sortid(aclent_t *, int, int); int aclsort(int nentries, int calcmask, aclent_t *aclbufp) { aclent_t *tp; unsigned int newmask = 0; int which; int i; int k; /* check validity first before sorting */ if (aclcheck(aclbufp, nentries, &which) != 0) return (-1); /* * Performance enhancement: * We change entry type to sort order in the ACL, do the sorting. * We then change sort order back to entry type. * This makes entrycmp() very "light" and thus improves performance. * Contrast to original implementation that had to find out * the sorting order each time it is called. */ for (tp = aclbufp, i = 0; i < nentries; tp++, i++) { for (k = 1; k <= TOTAL_ENTRY_TYPES; k++) { if (tp->a_type == map_to_sort[k].entry_type) { tp->a_type = map_to_sort[k].sort_order; break; } } } /* typecast to remove incompatible type warning */ qsort(aclbufp, nentries, sizeof (aclent_t), (int (*)(const void *, const void *))entrycmp); for (tp = aclbufp, i = 0; i < nentries; tp++, i++) { for (k = 1; k <= TOTAL_ENTRY_TYPES; k++) { if (tp->a_type == map_to_sort[k].sort_order) { tp->a_type = map_to_sort[k].entry_type; break; } } } /* * Start sorting id within USER and GROUP * sortid() could return a pointer and entries left * so that we dont have to search from the beginning * every time it calls */ sortid(aclbufp, nentries, USER); sortid(aclbufp, nentries, GROUP); sortid(aclbufp, nentries, DEF_USER); sortid(aclbufp, nentries, DEF_GROUP); /* * Recalculate mask entry */ if (calcmask != 0) { /* * At this point, ACL is valid and sorted. We may find a * CLASS_OBJ entry and stop. Because of the case of minimum ACL, * we still have to check till OTHER_OBJ entry is shown. */ for (tp = aclbufp; tp->a_type != OTHER_OBJ; tp++) { if (tp->a_type == USER || tp->a_type == GROUP || tp->a_type == GROUP_OBJ) newmask |= tp->a_perm; if (tp->a_type == CLASS_OBJ) break; } if (tp->a_type == CLASS_OBJ) tp->a_perm = (unsigned char)newmask; } return (0); } /* * sortid() sorts the ids with the same entry type in increasing order */ static void sortid(aclent_t *ap, int cnt, int type) { aclent_t *tp; aclent_t *startp; /* start of the desired entry type */ int howmany; for (tp = ap; cnt-- > 0; tp++) { if (tp->a_type != type) continue; startp = tp; howmany = 1; for (tp++, cnt--; cnt > 0 && tp->a_type == type; tp++, cnt--) howmany++; /* typecast to remove incompatible type warning */ qsort(startp, howmany, sizeof (aclent_t), (int (*)(const void*, const void*))idcmp); } } /* * compare the field a_type */ static int entrycmp(const aclent_t *i, const aclent_t *j) { return ((int)(i->a_type) - (int)(j->a_type)); } /* * compare the field a_id */ static int idcmp(const aclent_t *i, const aclent_t *j) { return ((int)(i->a_id) - (int)(j->a_id)); } /* * 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 (c) 1993, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright 2016 Nexenta Systems, Inc. All rights reserved. * Copyright 2024 RackTop Systems, Inc. */ /*LINTLIBRARY*/ #include #include #include #include #include #include #include #include #include #include #include #include #include /* * Unix names returned for UID and GID values can actually be longer than * the traditional 8 characters, or even 32 characters, etc. When using * "ad" in nsswitch.conf they can be quite long. Let's use a reasonable * compromise value here (80) which is large enough to hold SIDs in the * numeric form (eg. "S-1-5-21-wwwwwwwww-xxxxxxxxx-yyyyyyyyy-zzzzzzzzz") * but not necessarily every crazy long user name. In addition, places * below that might truncate a long name should detect truncation and * fall-back to a more compact value (SID or UID/GID et). */ #define ID_STR_MAX 80 #define APPENDED_ID_MAX ID_STR_MAX + 1 /* id + colon */ /* * yyinteractive controls whether yyparse should print out * error messages to stderr, and whether or not id's should be * allowed from acl_fromtext(). */ int yyinteractive; acl_t *yyacl; char *yybuf; mutex_t yymutex; extern acl_t *acl_alloc(enum acl_type); /* * dynamic string that will increase in size on an * as needed basis. */ typedef struct dynaclstr { size_t d_bufsize; /* current size of aclexport */ char *d_aclexport; int d_pos; } dynaclstr_t; static int str_append(dynaclstr_t *, char *); static int aclent_perm_txt(dynaclstr_t *, o_mode_t); static void aclent_perms(int perm, char *txt_perms) { if (perm & S_IROTH) txt_perms[0] = 'r'; else txt_perms[0] = '-'; if (perm & S_IWOTH) txt_perms[1] = 'w'; else txt_perms[1] = '-'; if (perm & S_IXOTH) txt_perms[2] = 'x'; else txt_perms[2] = '-'; txt_perms[3] = '\0'; } /* * Get the user name into the passed buffer. If it can't be found, * or if the noresolve flag is set, or if what we found will not fit * in the buffer without truncation, just put a numeric ID. */ static char * pruname(uid_t uid, char *uidp, size_t buflen, int noresolve) { struct passwd *passwdp = NULL; if (noresolve == 0) passwdp = getpwuid(uid); if (passwdp != NULL && strlen(passwdp->pw_name) < buflen) { (void) strlcpy(uidp, passwdp->pw_name, buflen); } else { /* display uid instead */ (void) snprintf(uidp, buflen, "%u", uid); } return (uidp); } /* * Get the group name into the passed buffer. If it can't be found, * or if the noresolve flag is set, or if what we found will not fit * in the buffer without truncation, just put a numeric ID. */ static char * prgname(gid_t gid, char *gidp, size_t buflen, int noresolve) { struct group *groupp = NULL; if (noresolve == 0) groupp = getgrgid(gid); if (groupp != NULL && strlen(groupp->gr_name) < buflen) { (void) strlcpy(gidp, groupp->gr_name, buflen); } else { /* display gid instead */ (void) snprintf(gidp, buflen, "%u", gid); } return (gidp); } static int getsidname(uid_t who, boolean_t user, char **sidp, boolean_t noresolve) { idmap_get_handle_t *get_hdl = NULL; idmap_stat status; idmap_rid_t rid; int error = IDMAP_ERR_NORESULT; int len; char *domain = NULL; *sidp = NULL; /* * First try and get windows name */ if (!noresolve) { if (user) error = idmap_getwinnamebyuid(who, IDMAP_REQ_FLG_USE_CACHE, sidp, NULL); else error = idmap_getwinnamebygid(who, IDMAP_REQ_FLG_USE_CACHE, sidp, NULL); } if (error != IDMAP_SUCCESS) { if (idmap_get_create(&get_hdl) == IDMAP_SUCCESS) { if (user) error = idmap_get_sidbyuid(get_hdl, who, IDMAP_REQ_FLG_USE_CACHE, &domain, &rid, &status); else error = idmap_get_sidbygid(get_hdl, who, IDMAP_REQ_FLG_USE_CACHE, &domain, &rid, &status); if (error == IDMAP_SUCCESS && idmap_get_mappings(get_hdl) == 0) { if (status == IDMAP_SUCCESS) { len = snprintf(NULL, 0, "%s-%d", domain, rid); if (*sidp = malloc(len + 1)) { (void) snprintf(*sidp, len + 1, "%s-%d", domain, rid); } } } } if (get_hdl) idmap_get_destroy(get_hdl); } free(domain); return (*sidp ? 0 : 1); } /* * sid_string_by_id() is an exposed interface via -lsec */ int sid_string_by_id(uid_t who, boolean_t user, char **sidp, boolean_t noresolve) { return (getsidname(who, user, sidp, noresolve)); } static void aclent_printacl(acl_t *aclp) { aclent_t *tp; int aclcnt; int mask; int slot = 0; char perm[4]; char uidp[ID_STR_MAX]; char gidp[ID_STR_MAX]; /* display ACL: assume it is sorted. */ aclcnt = aclp->acl_cnt; for (tp = aclp->acl_aclp; tp && aclcnt--; tp++) { if (tp->a_type == CLASS_OBJ) mask = tp->a_perm; } aclcnt = aclp->acl_cnt; for (tp = aclp->acl_aclp; aclcnt--; tp++) { (void) printf(" %d:", slot++); switch (tp->a_type) { case USER: aclent_perms(tp->a_perm, perm); (void) printf("user:%s:%s\t\t", pruname(tp->a_id, uidp, sizeof (uidp), 0), perm); aclent_perms((tp->a_perm & mask), perm); (void) printf("#effective:%s\n", perm); break; case USER_OBJ: /* no need to display uid */ aclent_perms(tp->a_perm, perm); (void) printf("user::%s\n", perm); break; case GROUP: aclent_perms(tp->a_perm, perm); (void) printf("group:%s:%s\t\t", prgname(tp->a_id, gidp, sizeof (gidp), 0), perm); aclent_perms(tp->a_perm & mask, perm); (void) printf("#effective:%s\n", perm); break; case GROUP_OBJ: aclent_perms(tp->a_perm, perm); (void) printf("group::%s\t\t", perm); aclent_perms(tp->a_perm & mask, perm); (void) printf("#effective:%s\n", perm); break; case CLASS_OBJ: aclent_perms(tp->a_perm, perm); (void) printf("mask:%s\n", perm); break; case OTHER_OBJ: aclent_perms(tp->a_perm, perm); (void) printf("other:%s\n", perm); break; case DEF_USER: aclent_perms(tp->a_perm, perm); (void) printf("default:user:%s:%s\n", pruname(tp->a_id, uidp, sizeof (uidp), 0), perm); break; case DEF_USER_OBJ: aclent_perms(tp->a_perm, perm); (void) printf("default:user::%s\n", perm); break; case DEF_GROUP: aclent_perms(tp->a_perm, perm); (void) printf("default:group:%s:%s\n", prgname(tp->a_id, gidp, sizeof (gidp), 0), perm); break; case DEF_GROUP_OBJ: aclent_perms(tp->a_perm, perm); (void) printf("default:group::%s\n", perm); break; case DEF_CLASS_OBJ: aclent_perms(tp->a_perm, perm); (void) printf("default:mask:%s\n", perm); break; case DEF_OTHER_OBJ: aclent_perms(tp->a_perm, perm); (void) printf("default:other:%s\n", perm); break; default: (void) fprintf(stderr, dgettext(TEXT_DOMAIN, "unrecognized entry\n")); break; } } } static void split_line(char *str, int cols) { char *ptr; int len; int i; int last_split; char *pad = ""; int pad_len; len = strlen(str); ptr = str; pad_len = 0; ptr = str; last_split = 0; for (i = 0; i != len; i++) { if ((i + pad_len + 4) >= cols) { (void) printf("%s%.*s\n", pad, last_split, ptr); ptr = &ptr[last_split]; len = strlen(ptr); i = 0; pad_len = 4; pad = " "; } else { if (ptr[i] == '/' || ptr[i] == ':') { last_split = i; } } } if (i == len) { (void) printf("%s%s\n", pad, ptr); } } /* * compute entry type string, such as user:joe, group:staff,... */ static int aclent_type_txt(dynaclstr_t *dstr, aclent_t *aclp, int flags) { char idp[ID_STR_MAX]; int error; switch (aclp->a_type) { case DEF_USER_OBJ: case USER_OBJ: if (aclp->a_type == USER_OBJ) error = str_append(dstr, "user::"); else error = str_append(dstr, "defaultuser::"); break; case DEF_USER: case USER: if (aclp->a_type == USER) error = str_append(dstr, "user:"); else error = str_append(dstr, "defaultuser:"); if (error) break; error = str_append(dstr, pruname(aclp->a_id, idp, sizeof (idp), flags & ACL_NORESOLVE)); if (error == 0) error = str_append(dstr, ":"); break; case DEF_GROUP_OBJ: case GROUP_OBJ: if (aclp->a_type == GROUP_OBJ) error = str_append(dstr, "group::"); else error = str_append(dstr, "defaultgroup::"); break; case DEF_GROUP: case GROUP: if (aclp->a_type == GROUP) error = str_append(dstr, "group:"); else error = str_append(dstr, "defaultgroup:"); if (error) break; error = str_append(dstr, prgname(aclp->a_id, idp, sizeof (idp), flags & ACL_NORESOLVE)); if (error == 0) error = str_append(dstr, ":"); break; case DEF_CLASS_OBJ: case CLASS_OBJ: if (aclp->a_type == CLASS_OBJ) error = str_append(dstr, "mask:"); else error = str_append(dstr, "defaultmask:"); break; case DEF_OTHER_OBJ: case OTHER_OBJ: if (aclp->a_type == OTHER_OBJ) error = str_append(dstr, "other:"); else error = str_append(dstr, "defaultother:"); break; default: error = 1; break; } return (error); } /* * compute entry type string such as, owner@:, user:joe, group:staff,... */ static int ace_type_txt(dynaclstr_t *dynstr, ace_t *acep, int flags) { char idp[ID_STR_MAX]; int error; char *sidp = NULL; switch (acep->a_flags & ACE_TYPE_FLAGS) { case ACE_OWNER: error = str_append(dynstr, OWNERAT_TXT); break; case ACE_GROUP|ACE_IDENTIFIER_GROUP: error = str_append(dynstr, GROUPAT_TXT); break; case ACE_IDENTIFIER_GROUP: if ((flags & ACL_SID_FMT) && acep->a_who > MAXUID) { if (error = str_append(dynstr, GROUPSID_TXT)) break; if (error = getsidname(acep->a_who, B_FALSE, &sidp, flags & ACL_NORESOLVE)) break; error = str_append(dynstr, sidp); } else { if (error = str_append(dynstr, GROUP_TXT)) break; error = str_append(dynstr, prgname(acep->a_who, idp, sizeof (idp), flags & ACL_NORESOLVE)); } if (error == 0) error = str_append(dynstr, ":"); break; case ACE_EVERYONE: error = str_append(dynstr, EVERYONEAT_TXT); break; case 0: if ((flags & ACL_SID_FMT) && acep->a_who > MAXUID) { if (error = str_append(dynstr, USERSID_TXT)) break; if (error = getsidname(acep->a_who, B_TRUE, &sidp, flags & ACL_NORESOLVE)) break; error = str_append(dynstr, sidp); } else { if (error = str_append(dynstr, USER_TXT)) break; error = str_append(dynstr, pruname(acep->a_who, idp, sizeof (idp), flags & ACL_NORESOLVE)); } if (error == 0) error = str_append(dynstr, ":"); break; default: error = 0; break; } if (sidp) free(sidp); return (error); } /* * compute string of permissions, such as read_data/write_data or * rwxp,... * The format depends on the flags field which indicates whether the compact * or verbose format should be used. */ static int ace_perm_txt(dynaclstr_t *dstr, uint32_t mask, uint32_t iflags, int isdir, int flags) { int error = 0; if (flags & ACL_COMPACT_FMT) { char buf[16]; if (mask & ACE_READ_DATA) buf[0] = 'r'; else buf[0] = '-'; if (mask & ACE_WRITE_DATA) buf[1] = 'w'; else buf[1] = '-'; if (mask & ACE_EXECUTE) buf[2] = 'x'; else buf[2] = '-'; if (mask & ACE_APPEND_DATA) buf[3] = 'p'; else buf[3] = '-'; if (mask & ACE_DELETE) buf[4] = 'd'; else buf[4] = '-'; if (mask & ACE_DELETE_CHILD) buf[5] = 'D'; else buf[5] = '-'; if (mask & ACE_READ_ATTRIBUTES) buf[6] = 'a'; else buf[6] = '-'; if (mask & ACE_WRITE_ATTRIBUTES) buf[7] = 'A'; else buf[7] = '-'; if (mask & ACE_READ_NAMED_ATTRS) buf[8] = 'R'; else buf[8] = '-'; if (mask & ACE_WRITE_NAMED_ATTRS) buf[9] = 'W'; else buf[9] = '-'; if (mask & ACE_READ_ACL) buf[10] = 'c'; else buf[10] = '-'; if (mask & ACE_WRITE_ACL) buf[11] = 'C'; else buf[11] = '-'; if (mask & ACE_WRITE_OWNER) buf[12] = 'o'; else buf[12] = '-'; if (mask & ACE_SYNCHRONIZE) buf[13] = 's'; else buf[13] = '-'; buf[14] = ':'; buf[15] = '\0'; error = str_append(dstr, buf); } else { /* * If ACE is a directory, but inheritance indicates its * for a file then print permissions for file rather than * dir. */ if (isdir) { if (mask & ACE_LIST_DIRECTORY) { if (iflags == ACE_FILE_INHERIT_ACE) { error = str_append(dstr, READ_DATA_TXT); } else { error = str_append(dstr, READ_DIR_TXT); } } if (error == 0 && (mask & ACE_ADD_FILE)) { if (iflags == ACE_FILE_INHERIT_ACE) { error = str_append(dstr, WRITE_DATA_TXT); } else { error = str_append(dstr, ADD_FILE_TXT); } } if (error == 0 && (mask & ACE_ADD_SUBDIRECTORY)) { if (iflags == ACE_FILE_INHERIT_ACE) { error = str_append(dstr, APPEND_DATA_TXT); } else { error = str_append(dstr, ADD_DIR_TXT); } } } else { if (mask & ACE_READ_DATA) { error = str_append(dstr, READ_DATA_TXT); } if (error == 0 && (mask & ACE_WRITE_DATA)) { error = str_append(dstr, WRITE_DATA_TXT); } if (error == 0 && (mask & ACE_APPEND_DATA)) { error = str_append(dstr, APPEND_DATA_TXT); } } if (error == 0 && (mask & ACE_READ_NAMED_ATTRS)) { error = str_append(dstr, READ_XATTR_TXT); } if (error == 0 && (mask & ACE_WRITE_NAMED_ATTRS)) { error = str_append(dstr, WRITE_XATTR_TXT); } if (error == 0 && (mask & ACE_EXECUTE)) { error = str_append(dstr, EXECUTE_TXT); } if (error == 0 && (mask & ACE_DELETE_CHILD)) { error = str_append(dstr, DELETE_CHILD_TXT); } if (error == 0 && (mask & ACE_READ_ATTRIBUTES)) { error = str_append(dstr, READ_ATTRIBUTES_TXT); } if (error == 0 && (mask & ACE_WRITE_ATTRIBUTES)) { error = str_append(dstr, WRITE_ATTRIBUTES_TXT); } if (error == 0 && (mask & ACE_DELETE)) { error = str_append(dstr, DELETE_TXT); } if (error == 0 && (mask & ACE_READ_ACL)) { error = str_append(dstr, READ_ACL_TXT); } if (error == 0 && (mask & ACE_WRITE_ACL)) { error = str_append(dstr, WRITE_ACL_TXT); } if (error == 0 && (mask & ACE_WRITE_OWNER)) { error = str_append(dstr, WRITE_OWNER_TXT); } if (error == 0 && (mask & ACE_SYNCHRONIZE)) { error = str_append(dstr, SYNCHRONIZE_TXT); } if (error == 0 && dstr->d_aclexport[dstr->d_pos-1] == '/') { dstr->d_aclexport[--dstr->d_pos] = '\0'; } if (error == 0) error = str_append(dstr, ":"); } return (error); } /* * compute string of access type, such as allow, deny, ... */ static int ace_access_txt(dynaclstr_t *dstr, int type) { int error; if (type == ACE_ACCESS_ALLOWED_ACE_TYPE) error = str_append(dstr, ALLOW_TXT); else if (type == ACE_ACCESS_DENIED_ACE_TYPE) error = str_append(dstr, DENY_TXT); else if (type == ACE_SYSTEM_AUDIT_ACE_TYPE) error = str_append(dstr, AUDIT_TXT); else if (type == ACE_SYSTEM_ALARM_ACE_TYPE) error = str_append(dstr, ALARM_TXT); else error = str_append(dstr, UNKNOWN_TXT); return (error); } static int ace_inherit_txt(dynaclstr_t *dstr, uint32_t iflags, int flags) { int error = 0; if (flags & ACL_COMPACT_FMT) { char buf[9]; if (iflags & ACE_FILE_INHERIT_ACE) buf[0] = 'f'; else buf[0] = '-'; if (iflags & ACE_DIRECTORY_INHERIT_ACE) buf[1] = 'd'; else buf[1] = '-'; if (iflags & ACE_INHERIT_ONLY_ACE) buf[2] = 'i'; else buf[2] = '-'; if (iflags & ACE_NO_PROPAGATE_INHERIT_ACE) buf[3] = 'n'; else buf[3] = '-'; if (iflags & ACE_SUCCESSFUL_ACCESS_ACE_FLAG) buf[4] = 'S'; else buf[4] = '-'; if (iflags & ACE_FAILED_ACCESS_ACE_FLAG) buf[5] = 'F'; else buf[5] = '-'; if (iflags & ACE_INHERITED_ACE) buf[6] = 'I'; else buf[6] = '-'; buf[7] = ':'; buf[8] = '\0'; error = str_append(dstr, buf); } else { if (iflags & ACE_FILE_INHERIT_ACE) { error = str_append(dstr, FILE_INHERIT_TXT); } if (error == 0 && (iflags & ACE_DIRECTORY_INHERIT_ACE)) { error = str_append(dstr, DIR_INHERIT_TXT); } if (error == 0 && (iflags & ACE_NO_PROPAGATE_INHERIT_ACE)) { error = str_append(dstr, NO_PROPAGATE_TXT); } if (error == 0 && (iflags & ACE_INHERIT_ONLY_ACE)) { error = str_append(dstr, INHERIT_ONLY_TXT); } if (error == 0 && (iflags & ACE_SUCCESSFUL_ACCESS_ACE_FLAG)) { error = str_append(dstr, SUCCESSFUL_ACCESS_TXT); } if (error == 0 && (iflags & ACE_FAILED_ACCESS_ACE_FLAG)) { error = str_append(dstr, FAILED_ACCESS_TXT); } if (error == 0 && (iflags & ACE_INHERITED_ACE)) { error = str_append(dstr, INHERITED_ACE_TXT); } if (error == 0 && dstr->d_aclexport[dstr->d_pos-1] == '/') { dstr->d_aclexport[--dstr->d_pos] = '\0'; error = str_append(dstr, ":"); } } return (error); } /* * Convert internal acl representation to external representation. * * The length of a non-owning user name or non-owning group name ie entries * of type DEF_USER, USER, DEF_GROUP or GROUP, can exceed LOGNAME_MAX. We * thus check the length of these entries, and if greater than LOGNAME_MAX, * we realloc() via increase_length(). * * The LOGNAME_MAX, ENTRYTYPELEN and PERMS limits are otherwise always * adhered to. */ /* * acltotext() converts each ACL entry to look like this: * * entry_type:uid^gid^name:perms[:id] * * The maximum length of entry_type is 14 ("defaultgroup::" and * "defaultother::") hence ENTRYTYPELEN is set to 14. * * The max length of a uid^gid^name entry (in theory) is 8, hence we use, * however the ID could be a number so we therefore use ID_STR_MAX * * The length of a perms entry is 4 to allow for the comma appended to each * to each acl entry. Hence PERMS is set to 4. */ #define ENTRYTYPELEN 14 #define PERMS 4 #define ACL_ENTRY_SIZE (ENTRYTYPELEN + ID_STR_MAX + PERMS + APPENDED_ID_MAX) char * aclent_acltotext(aclent_t *aclp, int aclcnt, int flags) { dynaclstr_t *dstr; char *aclexport = NULL; int i; int error = 0; if (aclp == NULL) return (NULL); if ((dstr = malloc(sizeof (dynaclstr_t))) == NULL) return (NULL); dstr->d_bufsize = aclcnt * ACL_ENTRY_SIZE; if ((dstr->d_aclexport = malloc(dstr->d_bufsize)) == NULL) { free(dstr); return (NULL); } *dstr->d_aclexport = '\0'; dstr->d_pos = 0; for (i = 0; i < aclcnt; i++, aclp++) { if (error = aclent_type_txt(dstr, aclp, flags)) break; if (error = aclent_perm_txt(dstr, aclp->a_perm)) break; if ((flags & ACL_APPEND_ID) && ((aclp->a_type == USER) || (aclp->a_type == DEF_USER) || (aclp->a_type == GROUP) || (aclp->a_type == DEF_GROUP))) { char id[ID_STR_MAX], *idstr; if (error = str_append(dstr, ":")) break; id[ID_STR_MAX - 1] = '\0'; /* null terminate buffer */ idstr = lltostr(aclp->a_id, &id[ID_STR_MAX - 1]); if (error = str_append(dstr, idstr)) break; } if (i < aclcnt - 1) if (error = str_append(dstr, ",")) break; } if (error) { if (dstr->d_aclexport) free(dstr->d_aclexport); } else { aclexport = dstr->d_aclexport; } free(dstr); return (aclexport); } char * acltotext(aclent_t *aclp, int aclcnt) { return (aclent_acltotext(aclp, aclcnt, 0)); } aclent_t * aclfromtext(char *aclstr, int *aclcnt) { acl_t *aclp; aclent_t *aclentp; int error; error = acl_fromtext(aclstr, &aclp); if (error) return (NULL); aclentp = aclp->acl_aclp; aclp->acl_aclp = NULL; *aclcnt = aclp->acl_cnt; acl_free(aclp); return (aclentp); } /* * Append string onto dynaclstr_t. * * Return 0 on success, 1 for failure. */ static int str_append(dynaclstr_t *dstr, char *newstr) { size_t len = strlen(newstr); if ((len + dstr->d_pos) >= dstr->d_bufsize) { dstr->d_aclexport = realloc(dstr->d_aclexport, dstr->d_bufsize + len + 1); if (dstr->d_aclexport == NULL) return (1); dstr->d_bufsize += len; } (void) strcat(&dstr->d_aclexport[dstr->d_pos], newstr); dstr->d_pos += len; return (0); } static int aclent_perm_txt(dynaclstr_t *dstr, o_mode_t perm) { char buf[4]; if (perm & S_IROTH) buf[0] = 'r'; else buf[0] = '-'; if (perm & S_IWOTH) buf[1] = 'w'; else buf[1] = '-'; if (perm & S_IXOTH) buf[2] = 'x'; else buf[2] = '-'; buf[3] = '\0'; return (str_append(dstr, buf)); } /* * ace_acltotext() convert each ace formatted acl to look like this: * * entry_type:uid^gid^name:perms[:flags]:[:id][,] * * The maximum length of entry_type is 5 ("group") * * The max length of a uid^gid^name entry (in theory) is 8, * however id could be a number so we therefore use ID_STR_MAX * * The length of a perms entry is 144 i.e read_data/write_data... * to each acl entry. * * iflags: file_inherit/dir_inherit/inherit_only/no_propagate/successful_access * /failed_access * */ #define ACE_ENTRYTYPLEN 6 #define IFLAGS_STR "file_inherit/dir_inherit/inherit_only/no_propagate/" \ "successful_access/failed_access/inherited" #define IFLAGS_SIZE (sizeof (IFLAGS_STR) - 1) #define ACCESS_TYPE_SIZE 7 /* if unknown */ #define COLON_CNT 3 #define PERMS_LEN 216 #define ACE_ENTRY_SIZE (ACE_ENTRYTYPLEN + ID_STR_MAX + PERMS_LEN + \ ACCESS_TYPE_SIZE + IFLAGS_SIZE + COLON_CNT + APPENDED_ID_MAX) static char * ace_acltotext(acl_t *aceaclp, int flags) { ace_t *aclp = aceaclp->acl_aclp; int aclcnt = aceaclp->acl_cnt; int i; int error = 0; int isdir = (aceaclp->acl_flags & ACL_IS_DIR); dynaclstr_t *dstr; char *aclexport = NULL; char *rawsidp = NULL; if (aclp == NULL) return (NULL); if ((dstr = malloc(sizeof (dynaclstr_t))) == NULL) return (NULL); dstr->d_bufsize = aclcnt * ACL_ENTRY_SIZE; if ((dstr->d_aclexport = malloc(dstr->d_bufsize)) == NULL) { free(dstr); return (NULL); } *dstr->d_aclexport = '\0'; dstr->d_pos = 0; for (i = 0; i < aclcnt; i++, aclp++) { if (error = ace_type_txt(dstr, aclp, flags)) break; if (error = ace_perm_txt(dstr, aclp->a_access_mask, aclp->a_flags, isdir, flags)) break; if (error = ace_inherit_txt(dstr, aclp->a_flags, flags)) break; if (error = ace_access_txt(dstr, aclp->a_type)) break; if ((flags & ACL_APPEND_ID) && (((aclp->a_flags & ACE_TYPE_FLAGS) == 0) || ((aclp->a_flags & ACE_TYPE_FLAGS) == ACE_IDENTIFIER_GROUP))) { char id[ID_STR_MAX], *idstr; if (error = str_append(dstr, ":")) break; rawsidp = NULL; id[ID_STR_MAX -1] = '\0'; /* null terminate */ if (aclp->a_who > MAXUID && (flags & ACL_SID_FMT)) { error = getsidname(aclp->a_who, ((aclp->a_flags & ACE_TYPE_FLAGS) == 0) ? B_TRUE : B_FALSE, &idstr, 1); rawsidp = idstr; if (error) break; } else if (aclp->a_who > MAXUID && !(flags & ACL_NORESOLVE)) { idstr = lltostr(UID_NOBODY, &id[ID_STR_MAX - 1]); } else { idstr = lltostr(aclp->a_who, &id[ID_STR_MAX - 1]); } if (error = str_append(dstr, idstr)) break; if (rawsidp) { free(rawsidp); rawsidp = NULL; } } if (i < aclcnt - 1) { if (error = str_append(dstr, ",")) break; } } if (rawsidp) free(rawsidp); if (error) { if (dstr->d_aclexport) free(dstr->d_aclexport); } else { aclexport = dstr->d_aclexport; } free(dstr); return (aclexport); } char * acl_totext(acl_t *aclp, int flags) { char *txtp; if (aclp == NULL) return (NULL); switch (aclp->acl_type) { case ACE_T: txtp = ace_acltotext(aclp, flags); break; case ACLENT_T: txtp = aclent_acltotext(aclp->acl_aclp, aclp->acl_cnt, flags); break; } return (txtp); } int acl_fromtext(const char *acltextp, acl_t **ret_aclp) { int error; char *buf; buf = malloc(strlen(acltextp) + 2); if (buf == NULL) return (EACL_MEM_ERROR); strcpy(buf, acltextp); strcat(buf, "\n"); (void) mutex_lock(&yymutex); yybuf = buf; yyreset(); error = yyparse(); free(buf); if (yyacl) { if (error == 0) *ret_aclp = yyacl; else { acl_free(yyacl); } yyacl = NULL; } (void) mutex_unlock(&yymutex); return (error); } int acl_parse(const char *acltextp, acl_t **aclp) { int error; yyinteractive = 1; error = acl_fromtext(acltextp, aclp); yyinteractive = 0; return (error); } static void ace_compact_printacl(acl_t *aclp, int flgs) { int cnt; ace_t *acep; dynaclstr_t *dstr; int len; if ((dstr = malloc(sizeof (dynaclstr_t))) == NULL) return; dstr->d_bufsize = ACE_ENTRY_SIZE; if ((dstr->d_aclexport = malloc(dstr->d_bufsize)) == NULL) { free(dstr); return; } *dstr->d_aclexport = '\0'; dstr->d_pos = 0; for (cnt = 0, acep = aclp->acl_aclp; cnt != aclp->acl_cnt; cnt++, acep++) { dstr->d_aclexport[0] = '\0'; dstr->d_pos = 0; if (ace_type_txt(dstr, acep, flgs)) break; len = strlen(&dstr->d_aclexport[0]); if (ace_perm_txt(dstr, acep->a_access_mask, acep->a_flags, aclp->acl_flags & ACL_IS_DIR, ACL_COMPACT_FMT)) break; if (ace_inherit_txt(dstr, acep->a_flags, ACL_COMPACT_FMT)) break; if (ace_access_txt(dstr, acep->a_type) == -1) break; (void) printf(" %20.*s%s\n", len, dstr->d_aclexport, &dstr->d_aclexport[len]); } if (dstr->d_aclexport) free(dstr->d_aclexport); free(dstr); } static void ace_printacl(acl_t *aclp, int cols, int flgs) { int slot = 0; char *token; char *acltext; if (flgs & ACL_COMPACT_FMT) { ace_compact_printacl(aclp, flgs); return; } acltext = acl_totext(aclp, flgs); if (acltext == NULL) return; token = strtok(acltext, ","); if (token == NULL) { free(acltext); return; } do { (void) printf(" %d:", slot++); split_line(token, cols - 5); } while (token = strtok(NULL, ",")); free(acltext); } /* * pretty print an ACL. * For aclent_t ACL's the format is * similar to the old format used by getfacl, * with the addition of adding a "slot" number * before each entry. * * for ace_t ACL's the cols variable will break up * the long lines into multiple lines and will also * print a "slot" number. */ void acl_printacl2(acl_t *aclp, int cols, int flgs) { switch (aclp->acl_type) { case ACLENT_T: aclent_printacl(aclp); break; case ACE_T: ace_printacl(aclp, cols, flgs); break; } } /* * Historical, compatibility version of the above. */ void acl_printacl(acl_t *aclp, int cols, int compact) { int flgs = compact ? ACL_COMPACT_FMT : 0; switch (aclp->acl_type) { case ACLENT_T: aclent_printacl(aclp); break; case ACE_T: ace_printacl(aclp, cols, flgs); break; } } typedef struct value_table { char p_letter; /* perm letter such as 'r' */ uint32_t p_value; /* value for perm when pletter found */ } value_table_t; /* * The permission tables are laid out in positional order * a '-' character will indicate a permission at a given * position is not specified. The '-' is not part of the * table, but will be checked for in the permission computation * routine. */ value_table_t ace_perm_table[] = { { 'r', ACE_READ_DATA}, { 'w', ACE_WRITE_DATA}, { 'x', ACE_EXECUTE}, { 'p', ACE_APPEND_DATA}, { 'd', ACE_DELETE}, { 'D', ACE_DELETE_CHILD}, { 'a', ACE_READ_ATTRIBUTES}, { 'A', ACE_WRITE_ATTRIBUTES}, { 'R', ACE_READ_NAMED_ATTRS}, { 'W', ACE_WRITE_NAMED_ATTRS}, { 'c', ACE_READ_ACL}, { 'C', ACE_WRITE_ACL}, { 'o', ACE_WRITE_OWNER}, { 's', ACE_SYNCHRONIZE} }; #define ACE_PERM_COUNT (sizeof (ace_perm_table) / sizeof (value_table_t)) value_table_t aclent_perm_table[] = { { 'r', S_IROTH}, { 'w', S_IWOTH}, { 'x', S_IXOTH} }; #define ACLENT_PERM_COUNT (sizeof (aclent_perm_table) / sizeof (value_table_t)) value_table_t inherit_table[] = { {'f', ACE_FILE_INHERIT_ACE}, {'d', ACE_DIRECTORY_INHERIT_ACE}, {'i', ACE_INHERIT_ONLY_ACE}, {'n', ACE_NO_PROPAGATE_INHERIT_ACE}, {'S', ACE_SUCCESSFUL_ACCESS_ACE_FLAG}, {'F', ACE_FAILED_ACCESS_ACE_FLAG}, {'I', ACE_INHERITED_ACE} }; #define IFLAG_COUNT (sizeof (inherit_table) / sizeof (value_table_t)) #define IFLAG_COUNT_V1 6 /* Older version compatibility */ /* * compute value from a permission table or inheritance table * based on string passed in. If positional is set then * string must match order in permtab, otherwise any order * is allowed. */ int compute_values(value_table_t *permtab, int count, char *permstr, int positional, uint32_t *mask) { uint32_t perm_val = 0; char *pstr; int i, found; if (count < 0) return (1); if (positional) { for (i = 0, pstr = permstr; i != count && pstr && *pstr; i++, pstr++) { if (*pstr == permtab[i].p_letter) { perm_val |= permtab[i].p_value; } else if (*pstr != '-') { return (1); } } } else { /* random order single letters with no '-' */ for (pstr = permstr; pstr && *pstr; pstr++) { for (found = 0, i = 0; i != count; i++) { if (*pstr == permtab[i].p_letter) { perm_val |= permtab[i].p_value; found = 1; break; } } if (found == 0) return (1); } } *mask = perm_val; return (0); } int ace_inherit_helper(char *str, uint32_t *imask, int table_length) { int rc = 0; if (strlen(str) == table_length) { /* * If the string == table_length then first check to see it's * in positional format. If that fails then see if it's in * non-positional format. */ if (compute_values(inherit_table, table_length, str, 1, imask) && compute_values(inherit_table, table_length, str, 0, imask)) { rc = 1; } } else { rc = compute_values(inherit_table, table_length, str, 0, imask); } return (rc ? EACL_INHERIT_ERROR : 0); } /* * compute value for inheritance flags. */ int compute_ace_inherit(char *str, uint32_t *imask) { int rc = 0; rc = ace_inherit_helper(str, imask, IFLAG_COUNT); if (rc && strlen(str) != IFLAG_COUNT) { /* is it an old formatted inherit string? */ rc = ace_inherit_helper(str, imask, IFLAG_COUNT_V1); } return (rc); } /* * compute value for ACE permissions. */ int compute_ace_perms(char *str, uint32_t *mask) { int positional = 0; int error; if (strlen(str) == ACE_PERM_COUNT) positional = 1; error = compute_values(ace_perm_table, ACE_PERM_COUNT, str, positional, mask); if (error && positional) { /* * If positional was set, then make sure permissions * aren't actually valid in non positional case where * all permissions are specified, just in random order. */ error = compute_values(ace_perm_table, ACE_PERM_COUNT, str, 0, mask); } if (error) error = EACL_PERM_MASK_ERROR; return (error); } /* * compute values for aclent permissions. */ int compute_aclent_perms(char *str, o_mode_t *mask) { int error; uint32_t pmask; if (strlen(str) != ACLENT_PERM_COUNT) return (EACL_PERM_MASK_ERROR); *mask = 0; error = compute_values(aclent_perm_table, ACLENT_PERM_COUNT, str, 1, &pmask); if (error == 0) { *mask = (o_mode_t)pmask; } else error = EACL_PERM_MASK_ERROR; return (error); } /* * determine ACE permissions. */ int ace_perm_mask(struct acl_perm_type *aclperm, uint32_t *mask) { int error; if (aclperm->perm_style == PERM_TYPE_EMPTY) { *mask = 0; return (0); } if (aclperm->perm_style == PERM_TYPE_ACE) { *mask = aclperm->perm_val; return (0); } error = compute_ace_perms(aclperm->perm_str, mask); if (error) { acl_error(dgettext(TEXT_DOMAIN, "Invalid permission(s) '%s' specified\n"), aclperm->perm_str); return (EACL_PERM_MASK_ERROR); } return (0); } /* * 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 (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright 2011 Nexenta Systems, Inc. All rights reserved. * * Copyright 2022 RackTop Systems, Inc. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define ACL_PATH 0 #define ACL_FD 1 typedef union { const char *file; int fd; } acl_inp; /* * Determine whether a file has a trivial ACL * returns: 0 = trivial * 1 = nontrivial * <0 some other system failure, such as ENOENT or EPERM */ int acl_trivial(const char *filename) { int acl_flavor; int aclcnt; int cntcmd; int val = 0; ace_t *acep; acl_flavor = pathconf(filename, _PC_ACL_ENABLED); if (acl_flavor == _ACL_ACE_ENABLED) cntcmd = ACE_GETACLCNT; else cntcmd = GETACLCNT; aclcnt = acl(filename, cntcmd, 0, NULL); if (aclcnt > 0) { if (acl_flavor == _ACL_ACE_ENABLED) { acep = malloc(sizeof (ace_t) * aclcnt); if (acep == NULL) return (-1); if (acl(filename, ACE_GETACL, aclcnt, acep) < 0) { free(acep); return (-1); } val = ace_trivial(acep, aclcnt); free(acep); } else if (aclcnt > MIN_ACL_ENTRIES) val = 1; } return (val); } static int cacl_get(acl_inp inp, int get_flag, int type, acl_t **aclp) { const char *fname; int fd; int ace_acl = 0; int error; int getcmd, cntcmd; acl_t *acl_info; int save_errno; int stat_error; struct stat64 statbuf; *aclp = NULL; if (type == ACL_PATH) { fname = inp.file; ace_acl = pathconf(fname, _PC_ACL_ENABLED); } else { fd = inp.fd; ace_acl = fpathconf(fd, _PC_ACL_ENABLED); } /* * if acl's aren't supported then * send it through the old GETACL interface */ if (ace_acl == 0 || ace_acl == -1) { ace_acl = _ACL_ACLENT_ENABLED; } if (ace_acl & _ACL_ACE_ENABLED) { cntcmd = ACE_GETACLCNT; getcmd = ACE_GETACL; acl_info = acl_alloc(ACE_T); } else { cntcmd = GETACLCNT; getcmd = GETACL; acl_info = acl_alloc(ACLENT_T); } if (acl_info == NULL) return (-1); if (type == ACL_PATH) { acl_info->acl_cnt = acl(fname, cntcmd, 0, NULL); } else { acl_info->acl_cnt = facl(fd, cntcmd, 0, NULL); } save_errno = errno; if (acl_info->acl_cnt < 0) { acl_free(acl_info); errno = save_errno; return (-1); } if (acl_info->acl_cnt == 0) { acl_free(acl_info); errno = save_errno; return (0); } acl_info->acl_aclp = malloc(acl_info->acl_cnt * acl_info->acl_entry_size); save_errno = errno; if (acl_info->acl_aclp == NULL) { acl_free(acl_info); errno = save_errno; return (-1); } if (type == ACL_PATH) { stat_error = stat64(fname, &statbuf); error = acl(fname, getcmd, acl_info->acl_cnt, acl_info->acl_aclp); } else { stat_error = fstat64(fd, &statbuf); error = facl(fd, getcmd, acl_info->acl_cnt, acl_info->acl_aclp); } save_errno = errno; if (error == -1) { acl_free(acl_info); errno = save_errno; return (-1); } if (stat_error == 0) { acl_info->acl_flags = (S_ISDIR(statbuf.st_mode) ? ACL_IS_DIR : 0); } else acl_info->acl_flags = 0; switch (acl_info->acl_type) { case ACLENT_T: if (acl_info->acl_cnt <= MIN_ACL_ENTRIES) acl_info->acl_flags |= ACL_IS_TRIVIAL; break; case ACE_T: if (ace_trivial(acl_info->acl_aclp, acl_info->acl_cnt) == 0) acl_info->acl_flags |= ACL_IS_TRIVIAL; break; default: errno = EINVAL; acl_free(acl_info); return (-1); } if ((acl_info->acl_flags & ACL_IS_TRIVIAL) && (get_flag & ACL_NO_TRIVIAL)) { acl_free(acl_info); errno = 0; return (0); } *aclp = acl_info; return (0); } /* * return -1 on failure, otherwise the number of acl * entries is returned */ int acl_get(const char *path, int get_flag, acl_t **aclp) { acl_inp acl_inp; acl_inp.file = path; return (cacl_get(acl_inp, get_flag, ACL_PATH, aclp)); } int facl_get(int fd, int get_flag, acl_t **aclp) { acl_inp acl_inp; acl_inp.fd = fd; return (cacl_get(acl_inp, get_flag, ACL_FD, aclp)); } /* * Set an ACL, translates acl to ace_t when appropriate. */ static int cacl_set(acl_inp *acl_inp, acl_t *aclp, int type) { int error = 0; int acl_flavor_target; struct stat64 statbuf; int stat_error; int isdir; if (type == ACL_PATH) { stat_error = stat64(acl_inp->file, &statbuf); if (stat_error) return (-1); acl_flavor_target = pathconf(acl_inp->file, _PC_ACL_ENABLED); } else { stat_error = fstat64(acl_inp->fd, &statbuf); if (stat_error) return (-1); acl_flavor_target = fpathconf(acl_inp->fd, _PC_ACL_ENABLED); } /* * If target returns an error or 0 from pathconf call then * fall back to UFS/POSIX Draft interface. * In the case of 0 we will then fail in either acl(2) or * acl_translate(). We could erroneously get 0 back from * a file system that is using fs_pathconf() and not answering * the _PC_ACL_ENABLED question itself. */ if (acl_flavor_target == 0 || acl_flavor_target == -1) acl_flavor_target = _ACL_ACLENT_ENABLED; isdir = S_ISDIR(statbuf.st_mode); if ((error = acl_translate(aclp, acl_flavor_target, isdir, statbuf.st_uid, statbuf.st_gid)) != 0) { return (error); } if (type == ACL_PATH) { error = acl(acl_inp->file, (aclp->acl_type == ACE_T) ? ACE_SETACL : SETACL, aclp->acl_cnt, aclp->acl_aclp); } else { error = facl(acl_inp->fd, (aclp->acl_type == ACE_T) ? ACE_SETACL : SETACL, aclp->acl_cnt, aclp->acl_aclp); } return (error); } int acl_set(const char *path, acl_t *aclp) { acl_inp acl_inp; acl_inp.file = path; return (cacl_set(&acl_inp, aclp, ACL_PATH)); } int facl_set(int fd, acl_t *aclp) { acl_inp acl_inp; acl_inp.fd = fd; return (cacl_set(&acl_inp, aclp, ACL_FD)); } int acl_cnt(acl_t *aclp) { return (aclp->acl_cnt); } int acl_type(acl_t *aclp) { return (aclp->acl_type); } acl_t * acl_dup(acl_t *aclp) { acl_t *newaclp; newaclp = acl_alloc(aclp->acl_type); if (newaclp == NULL) return (NULL); newaclp->acl_aclp = malloc(aclp->acl_entry_size * aclp->acl_cnt); if (newaclp->acl_aclp == NULL) { acl_free(newaclp); return (NULL); } (void) memcpy(newaclp->acl_aclp, aclp->acl_aclp, aclp->acl_entry_size * aclp->acl_cnt); newaclp->acl_cnt = aclp->acl_cnt; return (newaclp); } int acl_flags(acl_t *aclp) { return (aclp->acl_flags); } void * acl_data(acl_t *aclp) { return (aclp->acl_aclp); } /* * Take an acl array and build an acl_t. */ acl_t * acl_to_aclp(enum acl_type type, void *acl, int count) { acl_t *aclp; aclp = acl_alloc(type); if (aclp == NULL) return (aclp); aclp->acl_aclp = acl; aclp->acl_cnt = count; return (aclp); } /* * Remove an ACL from a file and create a trivial ACL based * off of the mode argument. After acl has been set owner/group * are updated to match owner,group arguments */ int acl_strip(const char *file, uid_t owner, gid_t group, mode_t mode) { int error = 0; aclent_t min_acl[MIN_ACL_ENTRIES]; ace_t *min_ace_acl; int acl_flavor; int aclcnt; struct stat64 statbuf; acl_flavor = pathconf(file, _PC_ACL_ENABLED); if (stat64(file, &statbuf) != 0) { error = 1; return (error); } /* * force it through aclent flavor when file system doesn't * understand question */ if (acl_flavor == 0 || acl_flavor == -1) acl_flavor = _ACL_ACLENT_ENABLED; if (acl_flavor & _ACL_ACLENT_ENABLED) { min_acl[0].a_type = USER_OBJ; min_acl[0].a_id = owner; min_acl[0].a_perm = ((mode & 0700) >> 6); min_acl[1].a_type = GROUP_OBJ; min_acl[1].a_id = group; min_acl[1].a_perm = ((mode & 0070) >> 3); min_acl[2].a_type = CLASS_OBJ; min_acl[2].a_id = (uid_t)-1; min_acl[2].a_perm = ((mode & 0070) >> 3); min_acl[3].a_type = OTHER_OBJ; min_acl[3].a_id = (uid_t)-1; min_acl[3].a_perm = (mode & 0007); aclcnt = 4; error = acl(file, SETACL, aclcnt, min_acl); } else if (acl_flavor & _ACL_ACE_ENABLED) { if ((error = acl_trivial_create(mode, S_ISDIR(statbuf.st_mode), &min_ace_acl, &aclcnt)) != 0) return (error); error = acl(file, ACE_SETACL, aclcnt, min_ace_acl); free(min_ace_acl); } else { errno = EINVAL; error = 1; } if (error == 0) error = chown(file, owner, group); return (error); } static int ace_match(void *entry1, void *entry2) { ace_t *p1 = (ace_t *)entry1; ace_t *p2 = (ace_t *)entry2; ace_t ace1, ace2; ace1 = *p1; ace2 = *p2; /* * Need to fixup who field for abstrations for * accurate comparison, since field is undefined. */ if (ace1.a_flags & (ACE_OWNER|ACE_GROUP|ACE_EVERYONE)) ace1.a_who = (uid_t)-1; if (ace2.a_flags & (ACE_OWNER|ACE_GROUP|ACE_EVERYONE)) ace2.a_who = (uid_t)-1; return (memcmp(&ace1, &ace2, sizeof (ace_t))); } static int aclent_match(void *entry1, void *entry2) { aclent_t *aclent1 = (aclent_t *)entry1; aclent_t *aclent2 = (aclent_t *)entry2; return (memcmp(aclent1, aclent2, sizeof (aclent_t))); } /* * Find acl entries in acl that correspond to removeacl. Search * is started from slot. The flag argument indicates whether to * remove all matches or just the first match. */ int acl_removeentries(acl_t *acl, acl_t *removeacl, int start_slot, int flag) { int i, j; int match; int (*acl_match)(void *acl1, void *acl2); void *acl_entry, *remove_entry; void *start; int found = 0; if (flag != ACL_REMOVE_ALL && flag != ACL_REMOVE_FIRST) flag = ACL_REMOVE_FIRST; if (acl == NULL || removeacl == NULL) return (EACL_NO_ACL_ENTRY); if (acl->acl_type != removeacl->acl_type) return (EACL_DIFF_TYPE); if (acl->acl_type == ACLENT_T) acl_match = aclent_match; else acl_match = ace_match; for (i = 0, remove_entry = removeacl->acl_aclp; i != removeacl->acl_cnt; i++) { j = 0; acl_entry = (char *)acl->acl_aclp + (acl->acl_entry_size * start_slot); for (;;) { match = acl_match(acl_entry, remove_entry); if (match == 0) { found++; /* avoid memmove if last entry */ if (acl->acl_cnt == (j + 1)) { acl->acl_cnt--; break; } start = (char *)acl_entry + acl->acl_entry_size; (void) memmove(acl_entry, start, acl->acl_entry_size * (acl->acl_cnt-- - (j + 1))); if (flag == ACL_REMOVE_FIRST) break; /* * List has changed, just continue so this * slot gets checked with it's new contents. */ continue; } acl_entry = ((char *)acl_entry + acl->acl_entry_size); if (++j >= acl->acl_cnt) { break; } } remove_entry = (char *)remove_entry + removeacl->acl_entry_size; } return ((found == 0) ? EACL_NO_ACL_ENTRY : 0); } /* * Replace entires entries in acl1 with the corresponding entries * in newentries. The where argument specifies where to begin * the replacement. If the where argument is 1 greater than the * number of acl entries in acl1 then they are appended. If the * where argument is 2+ greater than the number of acl entries then * EACL_INVALID_SLOT is returned. */ int acl_modifyentries(acl_t *acl1, acl_t *newentries, int where) { int slot; int slots_needed; int slots_left; int newsize; if (acl1 == NULL || newentries == NULL) return (EACL_NO_ACL_ENTRY); if (where < 0 || where >= acl1->acl_cnt) return (EACL_INVALID_SLOT); if (acl1->acl_type != newentries->acl_type) return (EACL_DIFF_TYPE); slot = where; slots_left = acl1->acl_cnt - slot + 1; if (slots_left < newentries->acl_cnt) { slots_needed = newentries->acl_cnt - slots_left; newsize = (acl1->acl_entry_size * acl1->acl_cnt) + (acl1->acl_entry_size * slots_needed); acl1->acl_aclp = realloc(acl1->acl_aclp, newsize); if (acl1->acl_aclp == NULL) return (-1); } (void) memcpy((char *)acl1->acl_aclp + (acl1->acl_entry_size * slot), newentries->acl_aclp, newentries->acl_entry_size * newentries->acl_cnt); /* * Did ACL grow? */ if ((slot + newentries->acl_cnt) > acl1->acl_cnt) { acl1->acl_cnt = slot + newentries->acl_cnt; } return (0); } /* * Add acl2 entries into acl1. The where argument specifies where * to add the entries. */ int acl_addentries(acl_t *acl1, acl_t *acl2, int where) { int newsize; int len; void *start; void *to; if (acl1 == NULL || acl2 == NULL) return (EACL_NO_ACL_ENTRY); if (acl1->acl_type != acl2->acl_type) return (EACL_DIFF_TYPE); /* * allow where to specify 1 past last slot for an append operation * but anything greater is an error. */ if (where < 0 || where > acl1->acl_cnt) return (EACL_INVALID_SLOT); newsize = (acl2->acl_entry_size * acl2->acl_cnt) + (acl1->acl_entry_size * acl1->acl_cnt); acl1->acl_aclp = realloc(acl1->acl_aclp, newsize); if (acl1->acl_aclp == NULL) return (-1); /* * first push down entries where new ones will be inserted */ to = (void *)((char *)acl1->acl_aclp + ((where + acl2->acl_cnt) * acl1->acl_entry_size)); start = (void *)((char *)acl1->acl_aclp + where * acl1->acl_entry_size); if (where < acl1->acl_cnt) { len = (acl1->acl_cnt - where) * acl1->acl_entry_size; (void) memmove(to, start, len); } /* * now stick in new entries. */ (void) memmove(start, acl2->acl_aclp, acl2->acl_cnt * acl2->acl_entry_size); acl1->acl_cnt += acl2->acl_cnt; return (0); } /* * return text for an ACL error. */ char * acl_strerror(int errnum) { switch (errnum) { case EACL_GRP_ERROR: return (dgettext(TEXT_DOMAIN, "There is more than one group or default group entry")); case EACL_USER_ERROR: return (dgettext(TEXT_DOMAIN, "There is more than one user or default user entry")); case EACL_OTHER_ERROR: return (dgettext(TEXT_DOMAIN, "There is more than one other entry")); case EACL_CLASS_ERROR: return (dgettext(TEXT_DOMAIN, "There is more than one mask entry")); case EACL_DUPLICATE_ERROR: return (dgettext(TEXT_DOMAIN, "Duplicate user or group entries")); case EACL_MISS_ERROR: return (dgettext(TEXT_DOMAIN, "Missing user/group owner, other, mask entry")); case EACL_MEM_ERROR: return (dgettext(TEXT_DOMAIN, "Memory error")); case EACL_ENTRY_ERROR: return (dgettext(TEXT_DOMAIN, "Unrecognized entry type")); case EACL_INHERIT_ERROR: return (dgettext(TEXT_DOMAIN, "Invalid inheritance flags")); case EACL_FLAGS_ERROR: return (dgettext(TEXT_DOMAIN, "Unrecognized entry flags")); case EACL_PERM_MASK_ERROR: return (dgettext(TEXT_DOMAIN, "Invalid ACL permissions")); case EACL_COUNT_ERROR: return (dgettext(TEXT_DOMAIN, "Invalid ACL count")); case EACL_INVALID_SLOT: return (dgettext(TEXT_DOMAIN, "Invalid ACL entry number specified")); case EACL_NO_ACL_ENTRY: return (dgettext(TEXT_DOMAIN, "ACL entry doesn't exist")); case EACL_DIFF_TYPE: return (dgettext(TEXT_DOMAIN, "Different file system ACL types cannot be merged")); case EACL_INVALID_USER_GROUP: return (dgettext(TEXT_DOMAIN, "Invalid user or group")); case EACL_INVALID_STR: return (dgettext(TEXT_DOMAIN, "ACL string is invalid")); case EACL_FIELD_NOT_BLANK: return (dgettext(TEXT_DOMAIN, "Field expected to be blank")); case EACL_INVALID_ACCESS_TYPE: return (dgettext(TEXT_DOMAIN, "Invalid access type")); case EACL_UNKNOWN_DATA: return (dgettext(TEXT_DOMAIN, "Unrecognized entry")); case EACL_MISSING_FIELDS: return (dgettext(TEXT_DOMAIN, "ACL specification missing required fields")); case EACL_INHERIT_NOTDIR: return (dgettext(TEXT_DOMAIN, "Inheritance flags are only allowed on directories")); case -1: return (strerror(errno)); default: errno = EINVAL; return (dgettext(TEXT_DOMAIN, "Unknown error")); } } extern int yyinteractive; /* PRINTFLIKE1 */ void acl_error(const char *fmt, ...) { va_list va; if (yyinteractive == 0) return; va_start(va, fmt); (void) vfprintf(stderr, fmt, va); va_end(va); } typedef enum id_type { UID_TYPE, GID_TYPE, PID_TYPE } id_type_t; static int sid_to_id_impl(char *sid, id_type_t type, int *is_user, uid_t *id) { idmap_get_handle_t *get_hdl; char *rid_start; idmap_stat rv; idmap_rid_t rid; const char *errstr; idmap_stat status; int error = 1; rid_start = strrchr(sid, '-'); if (rid_start == NULL) return (error); rid = strtonum(rid_start + 1, 0, UINT32_MAX, &errstr); if (errstr != NULL) return (error); if (idmap_get_create(&get_hdl) != IDMAP_SUCCESS) return (error); /* * When these functions return success, the &status output is * indeterminate. We only care about rv==success in this caller, * so just ignore &status. */ /* We need sid prefix. Insert NUL on '-', restore it later. */ *rid_start = '\0'; switch (type) { case UID_TYPE: rv = idmap_get_uidbysid(get_hdl, sid, rid, IDMAP_REQ_FLG_USE_CACHE, id, &status); break; case GID_TYPE: rv = idmap_get_gidbysid(get_hdl, sid, rid, IDMAP_REQ_FLG_USE_CACHE, id, &status); break; case PID_TYPE: rv = idmap_get_pidbysid(get_hdl, sid, rid, IDMAP_REQ_FLG_USE_CACHE, id, is_user, &status); break; } *rid_start = '-'; /* putback character removed earlier */ if (rv == IDMAP_SUCCESS && idmap_get_mappings(get_hdl) == IDMAP_SUCCESS) { error = 0; } idmap_get_destroy(get_hdl); return (error); } int sid_to_id(char *sid, boolean_t user, uid_t *id) { char *domain_start; int error = 1; if ((domain_start = strchr(sid, '@')) == NULL) { error = sid_to_id_impl(sid, user ? UID_TYPE : GID_TYPE, NULL, id); } else { char *name = sid; idmap_stat rv; *domain_start++ = '\0'; if (user) rv = idmap_getuidbywinname(name, domain_start, IDMAP_REQ_FLG_USE_CACHE, id); else rv = idmap_getgidbywinname(name, domain_start, IDMAP_REQ_FLG_USE_CACHE, id); *--domain_start = '@'; if (rv == IDMAP_SUCCESS) error = 0; } return (error); } /* * Variant of sid_to_id() called when we don't know whether the SID * is a user or group. 2nd arg gets the type (0:group, 1:user) * Returns zero for success, 1 for errors. */ int sid_to_xid(char *sid, int *is_user, uid_t *id) { if ((strchr(sid, '@')) != NULL) return (1); return (sid_to_id_impl(sid, PID_TYPE, is_user, id)); } /* * 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 2016 Nexenta Systems, Inc. All rights reserved. * Copyright 2022 RackTop Systems, Inc. */ #ifndef _ACLUTILS_H #define _ACLUTILS_H #include #include #include #include #include #include #include #ifdef __cplusplus extern "C" { #endif #define ACL_REMOVE_ALL 0x0 #define ACL_REMOVE_FIRST 0x1 /* * Hint for whether acl_totext() should use * mnemonics: * read_data/list_directory * write_data/add_file or * append_data/add_subdirectory * when object of ACL is known. */ #define PERM_TYPE_ACE 0x1 /* permissions are of ACE type */ #define PERM_TYPE_UNKNOWN 0x2 /* permission type not yet known */ #define PERM_TYPE_EMPTY 0x4 /* no permissions are specified */ struct acl_perm_type { int perm_style; /* type of perm style, see above */ char *perm_str; /* string value being returned */ uint32_t perm_val; /* numeric value being returned */ }; /* * Textual representation of ace_t's access mask */ #define READ_DATA_TXT "read_data/" #define WRITE_DATA_TXT "write_data/" #define EXECUTE_TXT "execute/" #define READ_XATTR_TXT "read_xattr/" #define WRITE_XATTR_TXT "write_xattr/" #define READ_ATTRIBUTES_TXT "read_attributes/" #define WRITE_ATTRIBUTES_TXT "write_attributes/" #define DELETE_TXT "delete/" #define DELETE_CHILD_TXT "delete_child/" #define WRITE_OWNER_TXT "write_owner/" #define READ_ACL_TXT "read_acl/" #define WRITE_ACL_TXT "write_acl/" #define APPEND_DATA_TXT "append_data/" #define READ_DIR_TXT "list_directory/read_data/" #define ADD_DIR_TXT "add_subdirectory/append_data/" #define ADD_FILE_TXT "add_file/write_data/" #define SYNCHRONIZE_TXT "synchronize/" /* * ace_t's entry types */ #define OWNERAT_TXT "owner@:" #define GROUPAT_TXT "group@:" #define EVERYONEAT_TXT "everyone@:" #define GROUP_TXT "group:" #define USER_TXT "user:" #define USERSID_TXT "usersid:" #define GROUPSID_TXT "groupsid:" /* * ace_t's access types */ #define ALLOW_TXT "allow" #define DENY_TXT "deny" #define ALARM_TXT "alarm" #define AUDIT_TXT "audit" #define UNKNOWN_TXT "unknown" /* * ace_t's inheritance types */ #define FILE_INHERIT_TXT "file_inherit/" #define DIR_INHERIT_TXT "dir_inherit/" #define NO_PROPAGATE_TXT "no_propagate/" #define INHERIT_ONLY_TXT "inherit_only/" #define INHERITED_ACE_TXT "inherited/" #define SUCCESSFUL_ACCESS_TXT "successful_access/" #define FAILED_ACCESS_TXT "failed_access/" extern char *yybuf; extern acl_t *yyacl; extern int yyerror(const char *); extern int get_id(int entry_type, char *name, uid_t *id); extern int get_id_nofail(int entry_type, char *name); extern int ace_entry_type(int entry_type); extern int aclent_entry_type(int type, int owning, int *ret); extern int ace_perm_mask(struct acl_perm_type *, uint32_t *mask); extern int compute_aclent_perms(char *str, o_mode_t *mask); extern int compute_ace_inherit(char *str, uint32_t *imask); extern int acl_addentries(acl_t *, acl_t *, int); extern int acl_removeentries(acl_t *, acl_t *, int, int); extern int acl_modifyentries(acl_t *, acl_t *, int); extern void acl_printacl(acl_t *, int, int); extern void acl_printacl2(acl_t *, int, int); extern char *acl_strerror(int); extern acl_t *acl_dup(acl_t *); extern int acl_type(acl_t *); extern int acl_cnt(acl_t *); extern int acl_flags(acl_t *); extern void *acl_data(acl_t *); extern void acl_error(const char *, ...); extern int acl_parse(const char *, acl_t **); extern int yyparse(void); extern void yyreset(void); extern void yycleanup(void); extern acl_t *acl_to_aclp(enum acl_type, void *, int); extern int sid_string_by_id(uid_t, boolean_t, char **, boolean_t); extern int sid_to_id(char *, boolean_t, uid_t *); extern int sid_to_xid(char *, int *, uid_t *); #ifdef __cplusplus } #endif #endif /* _ACLUTILS_H */ # # 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 (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved. # Copyright 2016 Nexenta Systems, Inc. All rights reserved. # # # MAPFILE HEADER START # # WARNING: STOP NOW. DO NOT MODIFY THIS FILE. # Object versioning must comply with the rules detailed in # # usr/src/lib/README.mapfiles # # You should not be making modifications here until you've read the most current # copy of that file. If you need help, contact a gatekeeper for guidance. # # MAPFILE HEADER END # $mapfile_version 2 SYMBOL_VERSION SUNW_1.2 { global: acl_check; acl_free; acl_fromtext; acl_get; acl_set; acl_strip; acl_totext; acl_trivial; facl_get; facl_set; } SUNW_1.1; # Due to mistakes made early in the history of this library, there are # no SUNW_1.1 symbols, but the version is now kept as a placeholder. # Don't add any symbols to this version. SYMBOL_VERSION SUNW_1.1 { global: SUNW_1.1; } SUNW_0.9; SYMBOL_VERSION SUNW_0.9 { global: aclcheck; aclfrommode; aclfromtext; aclsort; acltomode; acltotext; }; SYMBOL_VERSION SUNWprivate_1.1 { global: acl_addentries; acl_alloc; acl_cnt; acl_data; acl_dup; acl_error; acl_flags; acl_modifyentries; acl_parse; acl_printacl; acl_printacl2; acl_removeentries; acl_strerror; acl_to_aclp; acl_type; sid_string_by_id; sid_to_id; local: *; };