/* * 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*/ #include #include #include #include #include #include #include #include #include #include #include #include #define ID_STR_MAX 20 /* digits in LONG_MAX */ #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; 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'; } 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 == (struct passwd *)NULL) { /* could not get passwd information: display uid instead */ (void) snprintf(uidp, buflen, "%u", uid); } else { (void) strlcpy(uidp, passwdp->pw_name, buflen); } return (uidp); } 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 == (struct group *)NULL) { /* could not get group information: display gid instead */ (void) snprintf(gidp, buflen, "%u", gid); } else { (void) strlcpy(gidp, groupp->gr_name, buflen); } return (gidp); } static char * prsidname(uid_t who, boolean_t user, char **sidp, int noresolve) { idmap_handle_t *idmap_hdl = NULL; idmap_get_handle_t *get_hdl = NULL; idmap_stat status; idmap_rid_t rid; int error = 1; int len; char *domain; char *name; if (noresolve) { len = snprintf(NULL, 0, "%u", who); *sidp = malloc(len + 1); (void) snprintf(*sidp, len + 1, "%u", who); return (*sidp); } /* * First try and get windows name */ if (user) error = idmap_getwinnamebyuid(who, IDMAP_REQ_FLG_USE_CACHE, &name, &domain); else error = idmap_getwinnamebygid(who, IDMAP_REQ_FLG_USE_CACHE, &name, &domain); if (error) { if (idmap_init(&idmap_hdl) == 0 && idmap_get_create(idmap_hdl, &get_hdl) == 0) { 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 == 0) error = idmap_get_mappings(get_hdl); } if (error == 0) { len = snprintf(NULL, 0, "%s-%d", domain, rid); *sidp = malloc(len + 1); (void) snprintf(*sidp, len + 1, "%s-%d", domain, rid); } else { *sidp = NULL; } if (get_hdl) idmap_get_destroy(get_hdl); if (idmap_hdl) (void) idmap_fini(idmap_hdl); } else { int len; len = snprintf(NULL, 0, "%s@%d", name, domain); *sidp = malloc(len + 1); (void) snprintf(*sidp, len + 1, "%s@%s", name, domain); } return (*sidp); } 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; error = str_append(dynstr, prsidname(acep->a_who, B_FALSE, &sidp, flags & ACL_NORESOLVE)); } 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; error = str_append(dynstr, prsidname(acep->a_who, B_TRUE, &sidp, flags & ACL_NORESOLVE)); } 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; 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); } /* * returns a character position index of the start of the newly * appended string. Returns -1 if operation couldn't be completed. */ 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; 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; id[ID_STR_MAX -1] = '\0'; /* null terminate buffer */ idstr = lltostr((aclp->a_who > MAXUID && !(flags & ACL_NORESOLVE)) ? UID_NOBODY : aclp->a_who, &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 * 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"); yybuf = buf; yyreset(); error = yyparse(); free(buf); if (yyacl) { if (error == 0) *ret_aclp = yyacl; else { acl_free(yyacl); } yyacl = NULL; } 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 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, 0)) 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 compact) { int slot = 0; char *token; char *acltext; if (compact) { ace_compact_printacl(aclp); return; } acltext = acl_totext(aclp, 0); 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_printacl(acl_t *aclp, int cols, int compact) { switch (aclp->acl_type) { case ACLENT_T: aclent_printacl(aclp); break; case ACE_T: ace_printacl(aclp, cols, compact); 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); }