/* Remove all perms specified in modifier from rentry*/ int subtract_from_entry(acl_entry_t rentry, acl_entry_t modifier, int* valid_perms) { acl_permset_t rperms, mperms; acl_flagset_t rflags, mflags; if (valid_perms) *valid_perms = 0; int i; if ((acl_get_permset(rentry, &rperms) != 0) || (acl_get_flagset_np(rentry, &rflags) != 0) || (acl_get_permset(modifier, &mperms) != 0) || (acl_get_flagset_np(modifier, &mflags) != 0)) { // err(1, "error computing ACL modification"); fprintf(stderr, "chmod: error computing ACL modification: %s\n", strerror(errno)); pthread_exit(NULL); } for (i = 0; acl_perms[i].name != NULL; i++) { if (acl_get_perm_np(mperms, acl_perms[i].perm)) acl_delete_perm(rperms, acl_perms[i].perm); else if (valid_perms && acl_get_perm_np(rperms, acl_perms[i].perm)) (*valid_perms)++; } for (i = 0; acl_flags[i].name != NULL; i++) { if (acl_get_flag_np(mflags, acl_flags[i].flag)) acl_delete_flag_np(rflags, acl_flags[i].flag); } acl_set_permset(rentry, rperms); acl_set_flagset_np(rentry, rflags); return 0; }
/* Add the perms specified in modifier to rentry */ static int merge_entry_perms(acl_entry_t rentry, acl_entry_t modifier) { acl_permset_t rperms, mperms; acl_flagset_t rflags, mflags; int i; if ((acl_get_permset(rentry, &rperms) != 0) || (acl_get_flagset_np(rentry, &rflags) != 0) || (acl_get_permset(modifier, &mperms) != 0) || (acl_get_flagset_np(modifier, &mflags) != 0)) { // err(1, "error computing ACL modification"); fprintf(stderr, "chmod: error computing ACL modification: %s\n", strerror(errno)); pthread_exit(NULL); } for (i = 0; acl_perms[i].name != NULL; i++) { if (acl_get_perm_np(mperms, acl_perms[i].perm)) acl_add_perm(rperms, acl_perms[i].perm); } for (i = 0; acl_flags[i].name != NULL; i++) { if (acl_get_flag_np(mflags, acl_flags[i].flag)) acl_add_flag_np(rflags, acl_flags[i].flag); } acl_set_permset(rentry, rperms); acl_set_flagset_np(rentry, rflags); return 0; }
/* Add the perms specified in modifier to rentry */ static int merge_entry_perms(acl_entry_t rentry, acl_entry_t modifier) { acl_permset_t rperms, mperms; acl_flagset_t rflags, mflags; int i; if ((acl_get_permset(rentry, &rperms) != 0) || (acl_get_flagset_np(rentry, &rflags) != 0) || (acl_get_permset(modifier, &mperms) != 0) || (acl_get_flagset_np(modifier, &mflags) != 0)) err(1, "error computing ACL modification"); for (i = 0; acl_perms[i].name != NULL; i++) { if (acl_get_perm_np(mperms, acl_perms[i].perm)) acl_add_perm(rperms, acl_perms[i].perm); } for (i = 0; acl_flags[i].name != NULL; i++) { if (acl_get_flag_np(mflags, acl_flags[i].flag)) acl_add_flag_np(rflags, acl_flags[i].flag); } acl_set_permset(rentry, rperms); acl_set_flagset_np(rentry, rflags); return 0; }
/* Compares two ACL entries for equality */ int compare_acl_entries(acl_entry_t a, acl_entry_t b) { acl_tag_t atag, btag; acl_permset_t aperms, bperms; acl_flagset_t aflags, bflags; int pcmp = 0, fcmp = 0; void *aqual, *bqual; aqual = acl_get_qualifier(a); bqual = acl_get_qualifier(b); int compare = compare_acl_qualifiers(aqual, bqual); acl_free(aqual); acl_free(bqual); if (compare != 0) return MATCH_NONE; if (0 != acl_get_tag_type(a, &atag)) { // err(1, "No tag type present in entry"); fprintf(stderr, "chmod: No tag type present in entry: %s\n", strerror(errno)); pthread_exit(NULL); } if (0!= acl_get_tag_type(b, &btag)) { // err(1, "No tag type present in entry"); fprintf(stderr, "chmod: No tag type present in entry: %s\n", strerror(errno)); pthread_exit(NULL); } if (atag != btag) return MATCH_NONE; if ((acl_get_permset(a, &aperms) != 0) || (acl_get_flagset_np(a, &aflags) != 0) || (acl_get_permset(b, &bperms) != 0) || (acl_get_flagset_np(b, &bflags) != 0)) { // err(1, "error fetching permissions"); fprintf(stderr, "chmod: error fetching permissions: %s\n", strerror(errno)); pthread_exit(NULL); } pcmp = compare_acl_permsets(aperms, bperms); fcmp = compare_acl_flagsets(aflags, bflags); if ((pcmp == MATCH_NONE) || (fcmp == MATCH_NONE)) return(MATCH_PARTIAL); else return(MATCH_EXACT); }
/* only directories can have inherit flags set */ static int remove_inherit_flags(acl_t *acl) { int entry_id; acl_entry_t acl_entry; acl_flagset_t acl_flags; entry_id = ACL_FIRST_ENTRY; while (acl_get_entry(*acl, entry_id, &acl_entry) > 0) { entry_id = ACL_NEXT_ENTRY; if (acl_get_flagset_np(acl_entry, &acl_flags) < 0) err(EX_OSERR, "acl_get_flagset_np() failed"); acl_delete_flag_np(acl_flags, ACL_ENTRY_FILE_INHERIT); acl_delete_flag_np(acl_flags, ACL_ENTRY_DIRECTORY_INHERIT); acl_delete_flag_np(acl_flags, ACL_ENTRY_NO_PROPAGATE_INHERIT); acl_delete_flag_np(acl_flags, ACL_ENTRY_INHERIT_ONLY); if (acl_set_flagset_np(acl_entry, acl_flags) < 0) err(EX_OSERR, "acl_set_flagset_np() failed"); } return (0); }
int find_matching_entry (acl_t acl, acl_entry_t modifier, acl_entry_t *rentryp, unsigned match_inherited) { acl_entry_t entry = NULL; unsigned aindex; int cmp, fcmp = MATCH_NONE; for (aindex = 0; acl_get_entry(acl, entry == NULL ? ACL_FIRST_ENTRY : ACL_NEXT_ENTRY, &entry) == 0; aindex++) { cmp = compare_acl_entries(entry, modifier); if ((cmp == MATCH_EXACT) || (cmp == MATCH_PARTIAL)) { if (match_inherited) { acl_flagset_t eflags, mflags; if (0 != acl_get_flagset_np(modifier, &mflags)) { // err(1, "Unable to get flagset"); fprintf(stderr, "chmod: Unable to get flagset: %s\n", strerror(errno)); pthread_exit(NULL); } if (0 != acl_get_flagset_np(entry, &eflags)) { // err(1, "Unable to get flagset"); fprintf(stderr, "chmod: Unable to get flagset: %s\n", strerror(errno)); pthread_exit(NULL); } if (compare_acl_flagsets(mflags, eflags) == MATCH_EXACT) { *rentryp = entry; fcmp = cmp; } } else { *rentryp = entry; fcmp = cmp; } } if (fcmp == MATCH_EXACT) break; } return fcmp; }
static int acl_match(acl_entry_t aclent, struct myacl_t *myacl) { gid_t g, *gp; uid_t u, *up; acl_tag_t tag_type; acl_permset_t opaque_ps; acl_flagset_t opaque_fs; int perms; acl_get_tag_type(aclent, &tag_type); /* translate the silly opaque permset to a bitmap */ acl_get_permset(aclent, &opaque_ps); acl_get_flagset_np(aclent, &opaque_fs); perms = acl_permset_to_bitmap(opaque_ps) | acl_flagset_to_bitmap(opaque_fs); if (perms != myacl->permset) return (0); switch (tag_type) { case ACL_USER_OBJ: if (myacl->tag != ARCHIVE_ENTRY_ACL_USER_OBJ) return (0); break; case ACL_USER: if (myacl->tag != ARCHIVE_ENTRY_ACL_USER) return (0); up = acl_get_qualifier(aclent); u = *up; acl_free(up); if ((uid_t)myacl->qual != u) return (0); break; case ACL_GROUP_OBJ: if (myacl->tag != ARCHIVE_ENTRY_ACL_GROUP_OBJ) return (0); break; case ACL_GROUP: if (myacl->tag != ARCHIVE_ENTRY_ACL_GROUP) return (0); gp = acl_get_qualifier(aclent); g = *gp; acl_free(gp); if ((gid_t)myacl->qual != g) return (0); break; case ACL_MASK: if (myacl->tag != ARCHIVE_ENTRY_ACL_MASK) return (0); break; case ACL_EVERYONE: if (myacl->tag != ARCHIVE_ENTRY_ACL_EVERYONE) return (0); break; } return (1); }
int score_acl_entry(acl_entry_t entry) { acl_tag_t tag; acl_flagset_t flags; acl_permset_t perms; int score = 0; if (entry == NULL) return (MINIMUM_TIER); if (acl_get_tag_type(entry, &tag) != 0) { // err(1, "Malformed ACL entry, no tag present"); fprintf(stderr, "chmod: Malformed ACL entry, no tag present: %s\n", strerror(errno)); pthread_exit(NULL); } if (acl_get_flagset_np(entry, &flags) != 0){ // err(1, "Unable to obtain flagset"); fprintf(stderr, "chmod: Unable to obtain flagset: %s\n", strerror(errno)); pthread_exit(NULL); } if (acl_get_permset(entry, &perms) != 0) { // err(1, "Malformed ACL entry, no permt present"); fprintf(stderr, "chmod: Malformed ACL entry, no permt present: %s\n", strerror(errno)); pthread_exit(NULL); } switch(tag) { case ACL_EXTENDED_ALLOW: break; case ACL_EXTENDED_DENY: score++; break; default: // errx(1, "Unknown tag type %d present in ACL entry", tag); fprintf(stderr, "chmod: Unknown tag type %d present in ACL entry\n", tag); pthread_exit(NULL); /* NOTREACHED */ } if (acl_get_flag_np(flags, ACL_ENTRY_INHERITED)) score += get_inheritance_level(entry) * INHERITANCE_TIER; return score; }
static int merge_user_group(acl_entry_t *entry, acl_entry_t *entry_new, int acl_brand) { acl_permset_t permset; acl_entry_type_t entry_type; acl_flagset_t flagset; int have_entry; uid_t *id, *id_new; have_entry = 0; id = acl_get_qualifier(*entry); if (id == NULL) err(1, "acl_get_qualifier() failed"); id_new = acl_get_qualifier(*entry_new); if (id_new == NULL) err(1, "acl_get_qualifier() failed"); if (*id == *id_new) { /* any other matches */ if (acl_get_permset(*entry, &permset) == -1) err(1, "acl_get_permset() failed"); if (acl_set_permset(*entry_new, permset) == -1) err(1, "acl_set_permset() failed"); if (acl_brand == ACL_BRAND_NFS4) { if (acl_get_entry_type_np(*entry, &entry_type)) err(1, "acl_get_entry_type_np() failed"); if (acl_set_entry_type_np(*entry_new, entry_type)) err(1, "acl_set_entry_type_np() failed"); if (acl_get_flagset_np(*entry, &flagset)) err(1, "acl_get_flagset_np() failed"); if (acl_set_flagset_np(*entry_new, flagset)) err(1, "acl_set_flagset_np() failed"); } have_entry = 1; } acl_free(id); acl_free(id_new); return (have_entry); }
/* merge two acl entries together */ static int merge_acl_entries(acl_entry_t *entry1, acl_entry_t *entry2) { acl_permset_t permset; acl_entry_type_t entry_type; acl_flagset_t flagset; if (acl_get_permset(*entry1, &permset) < 0) err(EX_OSERR, "acl_get_permset() failed"); if (acl_set_permset(*entry2, permset) < 0) err(EX_OSERR, "acl_set_permset() failed"); if (acl_get_entry_type_np(*entry1, &entry_type) < 0) err(EX_OSERR, "acl_get_entry_type_np() failed"); if (acl_set_entry_type_np(*entry2, entry_type) < 0) err(EX_OSERR, "acl_set_entry_type_np() failed"); if (acl_get_flagset_np(*entry1, &flagset) < 0) err(EX_OSERR, "acl_get_flagset_np() failed"); if (acl_set_flagset_np(*entry2, flagset) < 0) err(EX_OSERR, "acl_set_flagset_np() failed"); return (0); }
static int set_acl(struct archive *a, int fd, const char *name, struct archive_acl *abstract_acl, acl_type_t acl_type, int ae_requested_type, const char *tname) { acl_t acl; acl_entry_t acl_entry; acl_permset_t acl_permset; #ifdef ACL_TYPE_NFS4 acl_flagset_t acl_flagset; int r; #endif int ret; int ae_type, ae_permset, ae_tag, ae_id; uid_t ae_uid; gid_t ae_gid; const char *ae_name; int entries; int i; ret = ARCHIVE_OK; entries = archive_acl_reset(abstract_acl, ae_requested_type); if (entries == 0) return (ARCHIVE_OK); acl = acl_init(entries); while (archive_acl_next(a, abstract_acl, ae_requested_type, &ae_type, &ae_permset, &ae_tag, &ae_id, &ae_name) == ARCHIVE_OK) { acl_create_entry(&acl, &acl_entry); switch (ae_tag) { case ARCHIVE_ENTRY_ACL_USER: acl_set_tag_type(acl_entry, ACL_USER); ae_uid = archive_write_disk_uid(a, ae_name, ae_id); acl_set_qualifier(acl_entry, &ae_uid); break; case ARCHIVE_ENTRY_ACL_GROUP: acl_set_tag_type(acl_entry, ACL_GROUP); ae_gid = archive_write_disk_gid(a, ae_name, ae_id); acl_set_qualifier(acl_entry, &ae_gid); break; case ARCHIVE_ENTRY_ACL_USER_OBJ: acl_set_tag_type(acl_entry, ACL_USER_OBJ); break; case ARCHIVE_ENTRY_ACL_GROUP_OBJ: acl_set_tag_type(acl_entry, ACL_GROUP_OBJ); break; case ARCHIVE_ENTRY_ACL_MASK: acl_set_tag_type(acl_entry, ACL_MASK); break; case ARCHIVE_ENTRY_ACL_OTHER: acl_set_tag_type(acl_entry, ACL_OTHER); break; #ifdef ACL_TYPE_NFS4 case ARCHIVE_ENTRY_ACL_EVERYONE: acl_set_tag_type(acl_entry, ACL_EVERYONE); break; #endif default: /* XXX */ break; } #ifdef ACL_TYPE_NFS4 switch (ae_type) { case ARCHIVE_ENTRY_ACL_TYPE_ALLOW: acl_set_entry_type_np(acl_entry, ACL_ENTRY_TYPE_ALLOW); break; case ARCHIVE_ENTRY_ACL_TYPE_DENY: acl_set_entry_type_np(acl_entry, ACL_ENTRY_TYPE_DENY); break; case ARCHIVE_ENTRY_ACL_TYPE_AUDIT: acl_set_entry_type_np(acl_entry, ACL_ENTRY_TYPE_AUDIT); break; case ARCHIVE_ENTRY_ACL_TYPE_ALARM: acl_set_entry_type_np(acl_entry, ACL_ENTRY_TYPE_ALARM); break; case ARCHIVE_ENTRY_ACL_TYPE_ACCESS: case ARCHIVE_ENTRY_ACL_TYPE_DEFAULT: // These don't translate directly into the system ACL. break; default: // XXX error handling here. break; } #endif acl_get_permset(acl_entry, &acl_permset); acl_clear_perms(acl_permset); for (i = 0; i < (int)(sizeof(acl_perm_map) / sizeof(acl_perm_map[0])); ++i) { if (ae_permset & acl_perm_map[i].archive_perm) acl_add_perm(acl_permset, acl_perm_map[i].platform_perm); } #ifdef ACL_TYPE_NFS4 // XXX acl_get_flagset_np on FreeBSD returns EINVAL for // non-NFSv4 ACLs r = acl_get_flagset_np(acl_entry, &acl_flagset); if (r == 0) { acl_clear_flags_np(acl_flagset); for (i = 0; i < (int)(sizeof(acl_inherit_map) / sizeof(acl_inherit_map[0])); ++i) { if (ae_permset & acl_inherit_map[i].archive_inherit) acl_add_flag_np(acl_flagset, acl_inherit_map[i].platform_inherit); } } #endif } /* Try restoring the ACL through 'fd' if we can. */ #if HAVE_ACL_SET_FD if (fd >= 0 && acl_type == ACL_TYPE_ACCESS && acl_set_fd(fd, acl) == 0) ret = ARCHIVE_OK; else #else #if HAVE_ACL_SET_FD_NP if (fd >= 0 && acl_set_fd_np(fd, acl, acl_type) == 0) ret = ARCHIVE_OK; else #endif #endif #if HAVE_ACL_SET_LINK_NP if (acl_set_link_np(name, acl_type, acl) != 0) { archive_set_error(a, errno, "Failed to set %s acl", tname); ret = ARCHIVE_WARN; } #else /* TODO: Skip this if 'name' is a symlink. */ if (acl_set_file(name, acl_type, acl) != 0) { archive_set_error(a, errno, "Failed to set %s acl", tname); ret = ARCHIVE_WARN; } #endif acl_free(acl); return (ret); }
int modify_file_acl(unsigned int optflags, const char *path, acl_t modifier, int position, int inheritance_level, int follow) { acl_t oacl = NULL; unsigned aindex = 0, flag_new_acl = 0; acl_entry_t newent = NULL; acl_entry_t entry = NULL; unsigned retval = 0; extern int chmod_fflag; /* XXX acl_get_file() returns a zero entry ACL if an ACL was previously * associated with the file, and has had its entries removed. * However, POSIX 1003.1e states that a zero entry ACL should be * returned if the caller asks for ACL_TYPE_DEFAULT, and no ACL is * associated with the path; it * does not specifically state that a request for ACL_TYPE_EXTENDED * should not return a zero entry ACL, however. */ /* Determine if we've been given a zero entry ACL, or create an ACL if * none exists. There are some issues to consider here: Should we create * a zero-entry ACL for a delete or check canonicity operation? */ if (path == NULL) chmod_usage(); if (optflags & ACL_CLEAR_FLAG) { filesec_t fsec = filesec_init(); if (fsec == NULL) { // err(1, "filesec_init() failed"); fprintf(stderr, "chmod: filesec_init() failed: %s\n", strerror(errno)); pthread_exit(NULL); } if (filesec_set_property(fsec, FILESEC_ACL, _FILESEC_REMOVE_ACL) != 0) { // err(1, "filesec_set_property() failed"); fprintf(stderr, "chmod: filesec_set_property() failed: %s\n", strerror(errno)); pthread_exit(NULL); } if (follow) { if (chmodx_np(path, fsec) != 0) { if (!chmod_fflag) { // warn("Failed to clear ACL on file %s", path); fprintf(stderr, "chmod: Failed to clear ACL on file %s: %s\n", path, strerror(errno)); } retval = 1; } } else { int fd = open(path, O_SYMLINK); if (fd != -1) { if (fchmodx_np(fd, fsec) != 0) { if (!chmod_fflag) { fprintf(stderr, "chmod: Failed to clear ACL on file %s: %s\n", path, strerror(errno)); // warn("Failed to clear ACL on file %s", path); } retval = 1; } close(fd); } else { if (!chmod_fflag) { // warn("Failed to open file %s", path); fprintf(stderr, "chmod: Failed to open file %s: %s\n", path, strerror(errno)); } retval = 1; } } filesec_free(fsec); return (retval); } if (optflags & ACL_FROM_STDIN) { oacl = acl_dup(modifier); } else { if (follow) { oacl = acl_get_file(path, ACL_TYPE_EXTENDED); } else { int fd = open(path, O_SYMLINK); if (fd != -1) { oacl = acl_get_fd_np(fd, ACL_TYPE_EXTENDED); close(fd); } } if ((oacl == NULL) || (acl_get_entry(oacl,ACL_FIRST_ENTRY, &newent) != 0)) { if ((oacl = acl_init(1)) == NULL) { // err(1, "acl_init() failed"); fprintf(stderr, "chmod: acl_init() failed: %s\n", strerror(errno)); pthread_exit(NULL); } flag_new_acl = 1; position = 0; } if ((0 == flag_new_acl) && (optflags & (ACL_REMOVE_INHERIT_FLAG | ACL_REMOVE_INHERITED_ENTRIES))) { acl_t facl = NULL; if ((facl = acl_init(1)) == NULL) { //err(1, "acl_init() failed"); fprintf(stderr, "chmod: acl_init() failed: %s\n", strerror(errno)); pthread_exit(NULL); } for (aindex = 0; acl_get_entry(oacl, (entry == NULL ? ACL_FIRST_ENTRY : ACL_NEXT_ENTRY), &entry) == 0; aindex++) { acl_flagset_t eflags; acl_entry_t fent = NULL; if (acl_get_flagset_np(entry, &eflags) != 0) { fprintf(stderr, "chmod: Unable to obtain flagset: %s\n", strerror(errno)); pthread_exit(NULL); // err(1, "Unable to obtain flagset"); } if (acl_get_flag_np(eflags, ACL_ENTRY_INHERITED)) { if (optflags & ACL_REMOVE_INHERIT_FLAG) { acl_delete_flag_np(eflags, ACL_ENTRY_INHERITED); acl_set_flagset_np(entry, eflags); acl_create_entry(&facl, &fent); acl_copy_entry(fent, entry); } } else { acl_create_entry(&facl, &fent); acl_copy_entry(fent, entry); } } if (oacl) acl_free(oacl); oacl = facl; } else if (optflags & ACL_TO_STDOUT) { ssize_t len; /* need to get printacl() from ls(1) */ char *text = acl_to_text(oacl, &len); puts(text); acl_free(text); } else if (optflags & ACL_CHECK_CANONICITY) { if (flag_new_acl) { // warnx("No ACL currently associated with file '%s'", path); fprintf(stderr, "chmod: No ACL currently associated with file '%s'\n", path); } retval = is_canonical(oacl); } else if ((optflags & ACL_SET_FLAG) && (position == -1) && (!is_canonical(oacl))) { // warnx("The specified file '%s' does not have an ACL in canonical order, please specify a position with +a# ", path); fprintf(stderr, "chmod: The specified file '%s' does not have an ACL in canonical order, please specify a position with +a# \n", path); retval = 1; } else if (((optflags & ACL_DELETE_FLAG) && (position != -1)) || (optflags & ACL_CHECK_CANONICITY)) { retval = modify_acl(&oacl, NULL, optflags, position, inheritance_level, flag_new_acl, path); } else if ((optflags & (ACL_REMOVE_INHERIT_FLAG|ACL_REMOVE_INHERITED_ENTRIES)) && flag_new_acl) { // warnx("No ACL currently associated with file '%s'", path); fprintf(stderr, "chmod: No ACL currently associated with file '%s'\n", path); retval = 1; } else { if (!modifier) { /* avoid bus error in acl_get_entry */ // errx(1, "Internal error: modifier should not be NULL"); fprintf(stderr, "Internal error: modifier should not be NULL\n"); pthread_exit(NULL); } for (aindex = 0; acl_get_entry(modifier, (entry == NULL ? ACL_FIRST_ENTRY : ACL_NEXT_ENTRY), &entry) == 0; aindex++) { retval += modify_acl(&oacl, entry, optflags, position, inheritance_level, flag_new_acl, path); } } } /* XXX Potential race here, since someone else could've modified or * read the ACL on this file (with the intention of modifying it) in * the interval from acl_get_file() to acl_set_file(); we can * minimize one aspect of this window by comparing the original acl * to a fresh one from acl_get_file() but we could consider a * "changeset" mechanism, common locking strategy, or kernel * supplied reservation mechanism to prevent this race. */ if (!(optflags & (ACL_TO_STDOUT|ACL_CHECK_CANONICITY))) { int status = -1; if (follow) { status = acl_set_file(path, ACL_TYPE_EXTENDED, oacl); } else { int fd = open(path, O_SYMLINK); if (fd != -1) { status = acl_set_fd_np(fd, oacl, ACL_TYPE_EXTENDED); close(fd); } } if (status != 0) { if (!chmod_fflag) fprintf(stderr, "chmod: Failed to set ACL on file '%s': %s\n", path, strerror(errno)); // warn("Failed to set ACL on file '%s'", path); retval = 1; } } if (oacl) acl_free(oacl); return retval; }
acl_t acl_from_text(const char *buf_p) { int i, error = 0, need_tag, ug_tag; char *buf, *orig_buf; char *entry, *field, *sub; uuid_t *uu = NULL; struct passwd *tpass = NULL; struct group *tgrp = NULL; acl_entry_t acl_entry; acl_flagset_t flags = NULL; acl_permset_t perms = NULL; acl_tag_t tag; acl_t acl_ret; if (buf_p == NULL) { errno = EINVAL; return NULL; } if ((buf = strdup(buf_p)) == NULL) return NULL; if ((acl_ret = acl_init(1)) == NULL) return NULL; orig_buf = buf; /* global acl flags * format: !#acl <version> [<flags>] */ if ((entry = strsep(&buf, "\n")) != NULL && *entry) { /* field 1: !#acl */ field = strsep(&entry, " "); if (*field && strncmp(field, "!#acl", strlen("!#acl"))) { error = EINVAL; goto exit; } /* field 2: <version> * currently only accepts 1 */ field = strsep(&entry, " "); errno = 0; if (!*field || strtol(field, NULL, 0) != 1) { error = EINVAL; goto exit; } /* field 3: <flags> * optional */ if((field = strsep(&entry, " ")) != NULL && *field) { acl_get_flagset_np(acl_ret, &flags); while ((sub = strsep(&field, ",")) && *sub) { for (i = 0; acl_flags[i].name != NULL; ++i) { if (acl_flags[i].type & ACL_TYPE_ACL && !strcmp(acl_flags[i].name, sub)) { acl_add_flag_np(flags, acl_flags[i].flag); break; } } if (acl_flags[i].name == NULL) { /* couldn't find flag */ error = EINVAL; goto exit; } } } } else { error = EINVAL; goto exit; } /* parse each acl line * format: <user|group>: * [<uuid>]: * [<user|group>]: * [<uid|gid>]: * <allow|deny>[,<flags>] * [:<permissions>[,<permissions>]] * * only one of the user/group identifies is required * the first one found is used */ while ((entry = strsep(&buf, "\n")) && *entry) { need_tag = 1; ug_tag = -1; /* field 1: <user|group> */ field = strsep(&entry, ":"); if(uu) bzero(uu, sizeof(uuid_t)); else if((uu = calloc(1, sizeof(uuid_t))) == NULL) { error = errno; goto exit; } if(acl_create_entry(&acl_ret, &acl_entry)) { error = errno; goto exit; } if (-1 == acl_get_flagset_np(acl_entry, &flags) || -1 == acl_get_permset(acl_entry, &perms)) { error = errno; goto exit; } switch(*field) { case 'u': if(!strcmp(field, "user")) ug_tag = ID_TYPE_UID; break; case 'g': if(!strcmp(field, "group")) ug_tag = ID_TYPE_GID; break; default: error = EINVAL; goto exit; } /* field 2: <uuid> */ if ((field = strsep(&entry, ":")) != NULL && *field) { uuid_parse(field, *uu); need_tag = 0; } /* field 3: <username|groupname> */ if ((field = strsep(&entry, ":")) != NULL && *field && need_tag) { switch(ug_tag) { case ID_TYPE_UID: if((tpass = getpwnam(field)) != NULL) if (mbr_uid_to_uuid(tpass->pw_uid, *uu) != 0) { error = EINVAL; goto exit; } break; case ID_TYPE_GID: if ((tgrp = getgrnam(field)) != NULL) if (mbr_gid_to_uuid(tgrp->gr_gid, *uu) != 0) { error = EINVAL; goto exit; } break; default: error = EINVAL; goto exit; } need_tag = 0; } /* field 4: <uid|gid> */ if ((field = strsep(&entry, ":")) != NULL && *field && need_tag) { uid_t id; error = 0; if((id = strtol(field, NULL, 10)) == 0 && error) { error = EINVAL; goto exit; } switch(ug_tag) { case ID_TYPE_UID: if((tpass = getpwuid((uid_t)id)) != NULL) if (mbr_uid_to_uuid(tpass->pw_uid, *uu) != 0) { error = EINVAL; goto exit; } break; case ID_TYPE_GID: if ((tgrp = getgrgid((gid_t)id)) != NULL) if (mbr_gid_to_uuid(tgrp->gr_gid, *uu) != 0) { error = EINVAL; goto exit; } break; } need_tag = 0; } /* sanity check: nothing set as qualifier */ if (need_tag) { error = EINVAL; goto exit; } /* field 5: <flags> */ if((field = strsep(&entry, ":")) == NULL || !*field) { error = EINVAL; goto exit; } for (tag = 0; (sub = strsep(&field, ",")) && *sub;) { if (!tag) { if (!strcmp(sub, "allow")) tag = ACL_EXTENDED_ALLOW; else if (!strcmp(sub, "deny")) tag = ACL_EXTENDED_DENY; else { error = EINVAL; goto exit; } continue; } for (i = 0; acl_flags[i].name != NULL; ++i) { if (acl_flags[i].type & (ACL_TYPE_FILE | ACL_TYPE_DIR) && !strcmp(acl_flags[i].name, sub)) { acl_add_flag_np(flags, acl_flags[i].flag); break; } } if (acl_flags[i].name == NULL) { /* couldn't find perm */ error = EINVAL; goto exit; } } /* field 6: <perms> (can be empty) */ if((field = strsep(&entry, ":")) != NULL && *field) { while ((sub = strsep(&field, ",")) && *sub) { for (i = 0; acl_perms[i].name != NULL; i++) { if (acl_perms[i].type & (ACL_TYPE_FILE | ACL_TYPE_DIR) && !strcmp(acl_perms[i].name, sub)) { acl_add_perm(perms, acl_perms[i].perm); break; } } if (acl_perms[i].name == NULL) { /* couldn't find perm */ error = EINVAL; goto exit; } } } acl_set_tag_type(acl_entry, tag); acl_set_qualifier(acl_entry, *uu); } exit: if(uu) free(uu); free(orig_buf); if (error) { acl_free(acl_ret); acl_ret = NULL; errno = error; } return acl_ret; }
/* Convert an acl entry in string form to an acl_entry_t */ int parse_entry(char *entrybuf, acl_entry_t newent) { char *tok; char *pebuf; uuid_t *entryg; acl_tag_t tag; acl_permset_t perms; acl_flagset_t flags; unsigned permcount = 0; unsigned pindex = 0; char *delimiter = " "; int nametype = NAME_EITHER; acl_get_permset(newent, &perms); acl_get_flagset_np(newent, &flags); pebuf = entrybuf; if (0 == strncmp(entrybuf, "user:"******"group:", 6)) { nametype = NAME_GROUP; pebuf += 6; } if (strchr(pebuf, ':')) /* User/Group names can have spaces */ delimiter = ":"; tok = strsep(&pebuf, delimiter); if ((tok == NULL) || *tok == '\0') { // errx(1, "Invalid entry format -- expected user or group name"); fprintf(stderr, "chmod: Invalid entry format -- expected user or group name\n"); pthread_exit(NULL); } /* parse the name into a qualifier */ entryg = name_to_uuid(tok, nametype); tok = strsep(&pebuf, ": "); /* Stick with delimiter? */ if ((tok == NULL) || *tok == '\0') { // errx(1, "Invalid entry format -- expected allow or deny"); fprintf(stderr, "chmod: Invalid entry format -- expected allow or deny\n"); pthread_exit(NULL); } /* is the verb 'allow' or 'deny'? */ if (!strcmp(tok, "allow")) { tag = ACL_EXTENDED_ALLOW; } else if (!strcmp(tok, "deny")) { tag = ACL_EXTENDED_DENY; } else { // errx(1, "Unknown tag type '%s'", tok); fprintf(stderr, "chmod: Unknown tag type '%s'\n", tok); pthread_exit(NULL); } /* parse permissions */ for (; (tok = strsep(&pebuf, ",")) != NULL;) { if (*tok != '\0') { /* is it a permission? */ for (pindex = 0; acl_perms[pindex].name != NULL; pindex++) { if (!strcmp(acl_perms[pindex].name, tok)) { /* got one */ acl_add_perm(perms, acl_perms[pindex].perm); permcount++; goto found; } } /* is it a flag? */ for (pindex = 0; acl_flags[pindex].name != NULL; pindex++) { if (!strcmp(acl_flags[pindex].name, tok)) { /* got one */ acl_add_flag_np(flags, acl_flags[pindex].flag); permcount++; goto found; } } // errx(1,"Invalid permission type '%s'", tok); fprintf(stderr,"chmod: Invalid permission type '%s'\n", tok); pthread_exit(NULL); found: continue; } } if (0 == permcount) { // errx(1, "No permissions specified"); fprintf(stderr, "chmod: No permissions specified\n"); pthread_exit(NULL); } acl_set_tag_type(newent, tag); acl_set_qualifier(newent, entryg); acl_set_permset(newent, perms); acl_set_flagset_np(newent, flags); free(entryg); entryg = NULL; return(0); }
char * acl_to_text(acl_t acl, ssize_t *len_p) { acl_tag_t tag; acl_entry_t entry = NULL; acl_flagset_t flags; acl_permset_t perms; uid_t id; int i, first; int isgid; size_t bufsize = 1024; char *buf = NULL; if (!_ACL_VALID_ACL(acl)) { errno = EINVAL; return NULL; } if (len_p == NULL) if ((len_p = alloca(sizeof(ssize_t))) == NULL) goto err_nomem; *len_p = 0; if ((buf = malloc(bufsize)) == NULL) goto err_nomem; if (!raosnprintf(&buf, &bufsize, len_p, "!#acl %d", 1)) goto err_nomem; if (acl_get_flagset_np(acl, &flags) == 0) { for (i = 0, first = 0; acl_flags[i].name != NULL; ++i) { if (acl_flags[i].type & ACL_TYPE_ACL && acl_get_flag_np(flags, acl_flags[i].flag) != 0) { if(!raosnprintf(&buf, &bufsize, len_p, "%s%s", first++ ? "," : " ", acl_flags[i].name)) goto err_nomem; } } } for (;acl_get_entry(acl, entry == NULL ? ACL_FIRST_ENTRY : ACL_NEXT_ENTRY, &entry) == 0;) { int valid; uuid_t *uu; char *str, uu_str[37]; if (((uu = (uuid_t *) acl_get_qualifier(entry)) == NULL) || (acl_get_tag_type(entry, &tag) != 0) || (acl_get_flagset_np(entry, &flags) != 0) || (acl_get_permset(entry, &perms) != 0)) { if (uu != NULL) acl_free(uu); continue; } uuid_unparse_upper(*uu, uu_str); if ((str = uuid_to_name(uu, &id, &isgid)) != NULL) { valid = raosnprintf(&buf, &bufsize, len_p, "\n%s:%s:%s:%d:%s", isgid ? "group" : "user", uu_str, str, id, (tag == ACL_EXTENDED_ALLOW) ? "allow" : "deny"); } else { valid = raosnprintf(&buf, &bufsize, len_p, "\nuser:%s:::%s", uu_str, (tag == ACL_EXTENDED_ALLOW) ? "allow" : "deny"); } free(str); acl_free(uu); if (!valid) goto err_nomem; for (i = 0; acl_flags[i].name != NULL; ++i) { if (acl_flags[i].type & (ACL_TYPE_DIR | ACL_TYPE_FILE)) { if(acl_get_flag_np(flags, acl_flags[i].flag) != 0) { if(!raosnprintf(&buf, &bufsize, len_p, ",%s", acl_flags[i].name)) goto err_nomem; } } } for (i = 0, first = 0; acl_perms[i].name != NULL; ++i) { if (acl_perms[i].type & (ACL_TYPE_DIR | ACL_TYPE_FILE)) { if(acl_get_perm_np(perms, acl_perms[i].perm) != 0) { if(!raosnprintf(&buf, &bufsize, len_p, "%s%s", first++ ? "," : ":", acl_perms[i].name)) goto err_nomem; } } } } buf[(*len_p)++] = '\n'; buf[(*len_p)] = 0; return buf; err_nomem: if (buf != NULL) free(buf); errno = ENOMEM; return NULL; }
/* * merge an ACL into existing file's ACL */ int merge_acl(acl_t acl, acl_t *prev_acl, const char *filename) { acl_entry_t entry, entry_new; acl_permset_t permset; acl_t acl_new; acl_tag_t tag, tag_new; acl_entry_type_t entry_type, entry_type_new; acl_flagset_t flagset; int entry_id, entry_id_new, have_entry, had_entry, entry_number = 0; int acl_brand, prev_acl_brand; acl_get_brand_np(acl, &acl_brand); acl_get_brand_np(*prev_acl, &prev_acl_brand); if (branding_mismatch(acl_brand, prev_acl_brand)) { warnx("%s: branding mismatch; existing ACL is %s, " "entry to be merged is %s", filename, brand_name(prev_acl_brand), brand_name(acl_brand)); return (-1); } acl_new = acl_dup(*prev_acl); if (acl_new == NULL) err(1, "%s: acl_dup() failed", filename); entry_id = ACL_FIRST_ENTRY; while (acl_get_entry(acl, entry_id, &entry) == 1) { entry_id = ACL_NEXT_ENTRY; have_entry = 0; had_entry = 0; /* keep track of existing ACL_MASK entries */ if (acl_get_tag_type(entry, &tag) == -1) err(1, "%s: acl_get_tag_type() failed - " "invalid ACL entry", filename); if (tag == ACL_MASK) have_mask = 1; /* check against the existing ACL entries */ entry_id_new = ACL_FIRST_ENTRY; while (acl_get_entry(acl_new, entry_id_new, &entry_new) == 1) { entry_id_new = ACL_NEXT_ENTRY; if (acl_get_tag_type(entry, &tag) == -1) err(1, "%s: acl_get_tag_type() failed", filename); if (acl_get_tag_type(entry_new, &tag_new) == -1) err(1, "%s: acl_get_tag_type() failed", filename); if (tag != tag_new) continue; /* * For NFSv4, in addition to "tag" and "id" we also * compare "entry_type". */ if (acl_brand == ACL_BRAND_NFS4) { if (acl_get_entry_type_np(entry, &entry_type)) err(1, "%s: acl_get_entry_type_np() " "failed", filename); if (acl_get_entry_type_np(entry_new, &entry_type_new)) err(1, "%s: acl_get_entry_type_np() " "failed", filename); if (entry_type != entry_type_new) continue; } switch(tag) { case ACL_USER: case ACL_GROUP: have_entry = merge_user_group(&entry, &entry_new, acl_brand); if (have_entry == 0) break; /* FALLTHROUGH */ case ACL_USER_OBJ: case ACL_GROUP_OBJ: case ACL_OTHER: case ACL_MASK: case ACL_EVERYONE: if (acl_get_permset(entry, &permset) == -1) err(1, "%s: acl_get_permset() failed", filename); if (acl_set_permset(entry_new, permset) == -1) err(1, "%s: acl_set_permset() failed", filename); if (acl_brand == ACL_BRAND_NFS4) { if (acl_get_entry_type_np(entry, &entry_type)) err(1, "%s: acl_get_entry_type_np() failed", filename); if (acl_set_entry_type_np(entry_new, entry_type)) err(1, "%s: acl_set_entry_type_np() failed", filename); if (acl_get_flagset_np(entry, &flagset)) err(1, "%s: acl_get_flagset_np() failed", filename); if (acl_set_flagset_np(entry_new, flagset)) err(1, "%s: acl_set_flagset_np() failed", filename); } had_entry = have_entry = 1; break; default: /* should never be here */ errx(1, "%s: invalid tag type: %i", filename, tag); break; } } /* if this entry has not been found, it must be new */ if (had_entry == 0) { /* * NFSv4 ACL entries must be prepended to the ACL. * Appending them at the end makes no sense, since * in most cases they wouldn't even get evaluated. */ if (acl_brand == ACL_BRAND_NFS4) { if (acl_create_entry_np(&acl_new, &entry_new, entry_number) == -1) { warn("%s: acl_create_entry_np() failed", filename); acl_free(acl_new); return (-1); } /* * Without this increment, adding several * entries at once, for example * "setfacl -m user:1:r:allow,user:2:r:allow", * would make them appear in reverse order. */ entry_number++; } else { if (acl_create_entry(&acl_new, &entry_new) == -1) { warn("%s: acl_create_entry() failed", filename); acl_free(acl_new); return (-1); } } if (acl_copy_entry(entry_new, entry) == -1) err(1, "%s: acl_copy_entry() failed", filename); } } acl_free(*prev_acl); *prev_acl = acl_new; return (0); }
static int translate_acl(struct archive_read_disk *a, struct archive_entry *entry, acl_t acl, int default_entry_acl_type) { acl_tag_t acl_tag; acl_entry_type_t acl_type; acl_flagset_t acl_flagset; acl_entry_t acl_entry; acl_permset_t acl_permset; int brand, i, r, entry_acl_type; int s, ae_id, ae_tag, ae_perm; const char *ae_name; // FreeBSD "brands" ACLs as POSIX.1e or NFSv4 // Make sure the "brand" on this ACL is consistent // with the default_entry_acl_type bits provided. acl_get_brand_np(acl, &brand); switch (brand) { case ACL_BRAND_POSIX: switch (default_entry_acl_type) { case ARCHIVE_ENTRY_ACL_TYPE_ACCESS: case ARCHIVE_ENTRY_ACL_TYPE_DEFAULT: break; default: // XXX set warning message? return ARCHIVE_FAILED; } break; case ACL_BRAND_NFS4: if (default_entry_acl_type & ~ARCHIVE_ENTRY_ACL_TYPE_NFS4) { // XXX set warning message? return ARCHIVE_FAILED; } break; default: // XXX set warning message? return ARCHIVE_FAILED; break; } s = acl_get_entry(acl, ACL_FIRST_ENTRY, &acl_entry); while (s == 1) { ae_id = -1; ae_name = NULL; ae_perm = 0; acl_get_tag_type(acl_entry, &acl_tag); switch (acl_tag) { case ACL_USER: ae_id = (int)*(uid_t *)acl_get_qualifier(acl_entry); ae_name = archive_read_disk_uname(&a->archive, ae_id); ae_tag = ARCHIVE_ENTRY_ACL_USER; break; case ACL_GROUP: ae_id = (int)*(gid_t *)acl_get_qualifier(acl_entry); ae_name = archive_read_disk_gname(&a->archive, ae_id); ae_tag = ARCHIVE_ENTRY_ACL_GROUP; break; case ACL_MASK: ae_tag = ARCHIVE_ENTRY_ACL_MASK; break; case ACL_USER_OBJ: ae_tag = ARCHIVE_ENTRY_ACL_USER_OBJ; break; case ACL_GROUP_OBJ: ae_tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ; break; case ACL_OTHER: ae_tag = ARCHIVE_ENTRY_ACL_OTHER; break; case ACL_EVERYONE: ae_tag = ARCHIVE_ENTRY_ACL_EVERYONE; break; default: /* Skip types that libarchive can't support. */ s = acl_get_entry(acl, ACL_NEXT_ENTRY, &acl_entry); continue; } // XXX acl type maps to allow/deny/audit/YYYY bits // XXX acl_get_entry_type_np on FreeBSD returns EINVAL for // non-NFSv4 ACLs entry_acl_type = default_entry_acl_type; r = acl_get_entry_type_np(acl_entry, &acl_type); if (r == 0) { switch (acl_type) { case ACL_ENTRY_TYPE_ALLOW: entry_acl_type = ARCHIVE_ENTRY_ACL_TYPE_ALLOW; break; case ACL_ENTRY_TYPE_DENY: entry_acl_type = ARCHIVE_ENTRY_ACL_TYPE_DENY; break; case ACL_ENTRY_TYPE_AUDIT: entry_acl_type = ARCHIVE_ENTRY_ACL_TYPE_AUDIT; break; case ACL_ENTRY_TYPE_ALARM: entry_acl_type = ARCHIVE_ENTRY_ACL_TYPE_ALARM; break; } } /* * Libarchive stores "flag" (NFSv4 inheritance bits) * in the ae_perm bitmap. */ acl_get_flagset_np(acl_entry, &acl_flagset); for (i = 0; i < (int)(sizeof(acl_inherit_map) / sizeof(acl_inherit_map[0])); ++i) { if (acl_get_flag_np(acl_flagset, acl_inherit_map[i].platform_inherit)) ae_perm |= acl_inherit_map[i].archive_inherit; } acl_get_permset(acl_entry, &acl_permset); for (i = 0; i < (int)(sizeof(acl_perm_map) / sizeof(acl_perm_map[0])); ++i) { /* * acl_get_perm() is spelled differently on different * platforms; see above. */ if (ACL_GET_PERM(acl_permset, acl_perm_map[i].platform_perm)) ae_perm |= acl_perm_map[i].archive_perm; } archive_entry_acl_add_entry(entry, entry_acl_type, ae_perm, ae_tag, ae_id, ae_name); s = acl_get_entry(acl, ACL_NEXT_ENTRY, &acl_entry); } return (ARCHIVE_OK); }
int modify_acl(acl_t *oaclp, acl_entry_t modifier, unsigned int optflags, int position, int inheritance_level, unsigned flag_new_acl, const char* path) { unsigned cpos = 0; acl_entry_t newent = NULL; int dmatch = 0; acl_entry_t rentry = NULL; unsigned retval = 0; acl_t oacl = *oaclp; /* Add the inherited flag if requested by the user*/ if (modifier && (optflags & ACL_INHERIT_FLAG)) { acl_flagset_t mflags; acl_get_flagset_np(modifier, &mflags); acl_add_flag_np(mflags, ACL_ENTRY_INHERITED); acl_set_flagset_np(modifier, mflags); } if (optflags & ACL_SET_FLAG) { if (position != -1) { if (0 != acl_create_entry_np(&oacl, &newent, position)) { // err(1, "acl_create_entry() failed"); fprintf(stderr, "chmod: acl_create_entry() failed: %s\n", strerror(errno)); pthread_exit(NULL); } acl_copy_entry(newent, modifier); } else { /* If an entry exists, add the new permissions to it, else add an * entry in the canonical position. */ /* First, check for a matching entry - if one exists, merge flags */ dmatch = find_matching_entry(oacl, modifier, &rentry, 1); if (dmatch != MATCH_NONE) { if (dmatch == MATCH_EXACT) /* Nothing to be done */ goto ma_exit; if (dmatch == MATCH_PARTIAL) { merge_entry_perms(rentry, modifier); goto ma_exit; } } /* Insert the entry in canonical order */ cpos = find_canonical_position(oacl, modifier); if (0!= acl_create_entry_np(&oacl, &newent, cpos)) { // err(1, "acl_create_entry() failed"); fprintf(stderr, "chmod: acl_create_entry() failed: %s\n", strerror(errno)); pthread_exit(NULL); } acl_copy_entry(newent, modifier); } } else if (optflags & ACL_DELETE_FLAG) { if (flag_new_acl) { fprintf(stderr, "chmod: No ACL present '%s'\n", path); // warnx("No ACL present '%s'", path); retval = 1; } else if (position != -1 ) { if (0 != acl_get_entry(oacl, position, &rentry)) { fprintf(stderr, "chmod: Invalid entry number '%s'\n", path); // warnx("Invalid entry number '%s'", path); retval = 1; } else { acl_delete_entry(oacl, rentry); } } else { unsigned match_found = 0, aindex; for (aindex = 0; acl_get_entry(oacl, rentry == NULL ? ACL_FIRST_ENTRY : ACL_NEXT_ENTRY, &rentry) == 0; aindex++) { unsigned cmp; cmp = compare_acl_entries(rentry, modifier); if ((cmp == MATCH_EXACT) || (cmp == MATCH_PARTIAL)) { match_found++; if (cmp == MATCH_EXACT) acl_delete_entry(oacl, rentry); else { int valid_perms; /* In the event of a partial match, remove the specified perms from the * entry */ subtract_from_entry(rentry, modifier, &valid_perms); /* if no perms survived then delete the entry */ if (valid_perms == 0) acl_delete_entry(oacl, rentry); } } } if (0 == match_found) { fprintf(stderr, "chmod: Entry not found when attempting delete '%s'\n",path); // warnx("Entry not found when attempting delete '%s'",path); retval = 1; } } } else if (optflags & ACL_REWRITE_FLAG) { acl_entry_t rentry; if (-1 == position) { chmod_usage(); } if (0 == flag_new_acl) { if (0 != acl_get_entry(oacl, position, &rentry)) { // err(1, "Invalid entry number '%s'", path); fprintf(stderr, "chmod: Invalid entry number '%s': %s\n", path, strerror(errno)); pthread_exit(NULL); } if (0 != acl_delete_entry(oacl, rentry)) { // err(1, "Unable to delete entry '%s'", path); fprintf(stderr, "chmod: Unable to delete entry '%s': %s\n", path, strerror(errno)); pthread_exit(NULL); } } if (0!= acl_create_entry_np(&oacl, &newent, position)) { // err(1, "acl_create_entry() failed"); fprintf(stderr, "chmod: acl_create_entry() failed: %s\n", strerror(errno)); pthread_exit(NULL); } acl_copy_entry(newent, modifier); } ma_exit: *oaclp = oacl; return retval; }