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