+#ifdef HAVE_POSIX_ACLS
+
+static uint32_t posix_permset_to_darwin_rights(acl_entry_t e, int is_dir)
+{
+ EC_INIT;
+ uint32_t rights = 0;
+ acl_permset_t permset;
+
+ EC_ZERO_LOG(acl_get_permset(e, &permset));
+
+ if (acl_get_perm(permset, ACL_READ))
+ rights = DARWIN_ACE_READ_DATA
+ | DARWIN_ACE_READ_EXTATTRIBUTES
+ | DARWIN_ACE_READ_ATTRIBUTES
+ | DARWIN_ACE_READ_SECURITY;
+ if (acl_get_perm(permset, ACL_WRITE)) {
+ rights |= DARWIN_ACE_WRITE_DATA
+ | DARWIN_ACE_APPEND_DATA
+ | DARWIN_ACE_WRITE_EXTATTRIBUTES
+ | DARWIN_ACE_WRITE_ATTRIBUTES;
+ if (is_dir)
+ rights |= DARWIN_ACE_DELETE_CHILD;
+ }
+ if (acl_get_perm(permset, ACL_EXECUTE))
+ rights |= DARWIN_ACE_EXECUTE;
+
+EC_CLEANUP:
+ LOG(log_maxdebug, logtype_afpd, "mapped rights: 0x%08x", rights);
+ return rights;
+}
+
+/*!
+ * Compile access rights for a user to one file-system object
+ *
+ * This combines combines all access rights for a user to one fs-object and
+ * returns the result as a Darwin allowed rights ACE.
+ * This must honor trivial ACEs which are a mode_t mapping.
+ *
+ * @param path (r) path to filesystem object
+ * @param sb (r) struct stat of path
+ * @param result (rw) resulting Darwin allow ACE
+ *
+ * @returns 0 or -1 on error
+ */
+static int posix_acl_rights(const char *path,
+ const struct stat *sb,
+ uint32_t *result)
+{
+ EC_INIT;
+ int havemask = 0;
+ int entry_id = ACL_FIRST_ENTRY;
+ uint32_t rights = 0, maskrights = 0;
+ uid_t *uid = NULL;
+ gid_t *gid = NULL;
+ acl_t acl = NULL;
+ acl_entry_t e;
+ acl_tag_t tag;
+
+ EC_NULL_LOG(acl = acl_get_file(path, ACL_TYPE_ACCESS));
+
+ /* itereate through all ACEs to get the mask */
+ while (!havemask && acl_get_entry(acl, entry_id, &e) == 1) {
+ entry_id = ACL_NEXT_ENTRY;
+ EC_ZERO_LOG(acl_get_tag_type(e, &tag));
+ switch (tag) {
+ case ACL_MASK:
+ maskrights = posix_permset_to_darwin_rights(e, S_ISDIR(sb->st_mode));
+ LOG(log_maxdebug, logtype_afpd, "maskrights: 0x%08x", maskrights);
+ havemask = 1;
+ break;
+ default:
+ continue;
+ }
+ }
+
+ /* itereate through all ACEs */
+ entry_id = ACL_FIRST_ENTRY;
+ while (acl_get_entry(acl, entry_id, &e) == 1) {
+ entry_id = ACL_NEXT_ENTRY;
+ EC_ZERO_LOG(acl_get_tag_type(e, &tag));
+ switch (tag) {
+ case ACL_USER:
+ EC_NULL_LOG(uid = (uid_t *)acl_get_qualifier(e));
+ if (*uid == uuid) {
+ LOG(log_maxdebug, logtype_afpd, "ACL_USER: %u", *uid);
+ rights |= posix_permset_to_darwin_rights(e, S_ISDIR(sb->st_mode));
+ }
+ acl_free(uid);
+ uid = NULL;
+ break;
+ case ACL_USER_OBJ:
+ if (sb->st_uid == uuid) {
+ LOG(log_maxdebug, logtype_afpd, "ACL_USER_OBJ: %u", sb->st_uid);
+ rights |= posix_permset_to_darwin_rights(e, S_ISDIR(sb->st_mode));
+ }
+ break;
+ case ACL_GROUP:
+ EC_NULL_LOG(gid = (gid_t *)acl_get_qualifier(e));
+ if (gmem(*gid)) {
+ LOG(log_maxdebug, logtype_afpd, "ACL_GROUP: %u", *gid);
+ rights |= (posix_permset_to_darwin_rights(e, S_ISDIR(sb->st_mode)) & maskrights);
+ }
+ acl_free(gid);
+ gid = NULL;
+ break;
+ case ACL_GROUP_OBJ:
+ if (gmem(sb->st_gid)) {
+ LOG(log_maxdebug, logtype_afpd, "ACL_GROUP_OBJ: %u", sb->st_gid);
+ rights |= posix_permset_to_darwin_rights(e, S_ISDIR(sb->st_mode));
+ }
+ break;
+ case ACL_OTHER:
+ LOG(log_maxdebug, logtype_afpd, "ACL_OTHER");
+ rights |= posix_permset_to_darwin_rights(e, S_ISDIR(sb->st_mode));
+ break;
+ default:
+ continue;
+ }
+ } /* while */
+
+ *result |= rights;
+
+EC_CLEANUP:
+ if (acl) acl_free(acl);
+ if (uid) acl_free(uid);
+ if (gid) acl_free(gid);
+ EC_EXIT;
+}
+
+/*!
+ * Add entries of one acl to another acl
+ *
+ * @param aclp (rw) destination acl where new aces will be added
+ * @param acl (r) source acl where aces will be copied from
+ *
+ * @returns 0 on success, -1 on error
+ */
+static int acl_add_acl(acl_t *aclp, const acl_t acl)
+{
+ EC_INIT;
+ int id;
+ acl_entry_t se, de;
+
+ for (id = ACL_FIRST_ENTRY; acl_get_entry(acl, id, &se) == 1; id = ACL_NEXT_ENTRY) {
+ EC_ZERO_LOG_ERR(acl_create_entry(aclp, &de), AFPERR_MISC);
+ EC_ZERO_LOG_ERR(acl_copy_entry(de, se), AFPERR_MISC);
+ }
+
+EC_CLEANUP:
+ EC_EXIT;
+}
+
+/*!
+ * Map Darwin ACE rights to POSIX 1e perm
+ *
+ * We can only map few rights:
+ * DARWIN_ACE_READ_DATA -> ACL_READ
+ * DARWIN_ACE_WRITE_DATA -> ACL_WRITE
+ * DARWIN_ACE_DELETE_CHILD & (is_dir == 1) -> ACL_WRITE
+ * DARWIN_ACE_EXECUTE -> ACL_EXECUTE
+ *
+ * @param entry (rw) result of the mapping
+ * @param is_dir (r) 1 for dirs, 0 for files
+ *
+ * @returns mapping result as acl_perm_t, -1 on error
+ */
+static acl_perm_t map_darwin_right_to_posix_permset(uint32_t darwin_ace_rights, int is_dir)
+{
+ acl_perm_t perm = 0;
+
+ if (darwin_ace_rights & DARWIN_ACE_READ_DATA)
+ perm |= ACL_READ;
+
+ if (darwin_ace_rights & (DARWIN_ACE_WRITE_DATA | (DARWIN_ACE_DELETE_CHILD & is_dir)))
+ perm |= ACL_WRITE;
+
+ if (darwin_ace_rights & DARWIN_ACE_EXECUTE)
+ perm |= ACL_EXECUTE;
+
+ return perm;
+}
+
+/*!
+ * Add a ACL_USER or ACL_GROUP permission to an ACL, extending existing ACEs
+ *
+ * Add a permission of "type" for user or group "id" to an ACL. Scan the ACL
+ * for existing permissions for this type/id, if there is one add the perm,
+ * otherwise create a new ACL entry.
+ * perm can be or'ed ACL_READ, ACL_WRITE and ACL_EXECUTE.
+ *
+ * @param aclp (rw) pointer to ACL
+ * @param type (r) acl_tag_t of ACL_USER or ACL_GROUP
+ * @param id (r) uid_t uid for ACL_USER, or gid casted to uid_t for ACL_GROUP
+ * @param perm (r) acl_perm_t permissions to add
+ *
+ * @returns 0 on success, -1 on failure
+ */
+static int posix_acl_add_perm(acl_t *aclp, acl_tag_t type, uid_t id, acl_perm_t perm)
+{
+ EC_INIT;
+ uid_t *eid = NULL;
+ acl_entry_t e;
+ acl_tag_t tag;
+ int entry_id = ACL_FIRST_ENTRY;
+ acl_permset_t permset;
+
+ int found = 0;
+ for ( ; (! found) && acl_get_entry(*aclp, entry_id, &e) == 1; entry_id = ACL_NEXT_ENTRY) {
+ EC_ZERO_LOG(acl_get_tag_type(e, &tag));
+ if (tag != ACL_USER && tag != ACL_GROUP)
+ continue;
+ EC_NULL_LOG(eid = (uid_t *)acl_get_qualifier(e));
+ if ((*eid == id) && (type == tag)) {
+ /* found an ACE for this type/id */
+ found = 1;
+ EC_ZERO_LOG(acl_get_permset(e, &permset));
+ EC_ZERO_LOG(acl_add_perm(permset, perm));
+ }
+
+ acl_free(eid);
+ eid = NULL;
+ }
+
+ if ( ! found) {
+ /* there was no existing ACE for this type/id */
+ EC_ZERO_LOG(acl_create_entry(aclp, &e));
+ EC_ZERO_LOG(acl_set_tag_type(e, type));
+ EC_ZERO_LOG(acl_set_qualifier(e, &id));
+ EC_ZERO_LOG(acl_get_permset(e, &permset));
+ EC_ZERO_LOG(acl_clear_perms(permset));
+ EC_ZERO_LOG(acl_add_perm(permset, perm));
+ EC_ZERO_LOG(acl_set_permset(e, permset));
+ }
+
+EC_CLEANUP:
+ if (eid) acl_free(eid);
+
+ EC_EXIT;
+}
+
+/*!
+ * Map Darwin ACL to POSIX ACL.
+ *
+ * aclp must point to a acl_init'ed acl_t or an acl_t that can eg contain default ACEs.
+ * Mapping pecularities:
+ * - we create a default ace (which inherits to files and dirs) if either
+ DARWIN_ACE_FLAGS_FILE_INHERIT or DARWIN_ACE_FLAGS_DIRECTORY_INHERIT is requested
+ * - we throw away DARWIN_ACE_FLAGS_LIMIT_INHERIT (can't be mapped), thus the ACL will
+ * not be limited
+ *
+ * @param darwin_aces (r) pointer to darwin_aces buffer
+ * @param def_aclp (rw) directories: pointer to an initialized acl_t with the default acl
+ * files: *def_aclp will be NULL
+ * @param acc_aclp (rw) pointer to an initialized acl_t with the access acl
+ * @param ace_count (r) number of ACEs in darwin_aces buffer
+ *
+ * @returns 0 on success storing the result in aclp, -1 on error.
+ */
+static int map_aces_darwin_to_posix(const darwin_ace_t *darwin_aces,
+ acl_t *def_aclp,
+ acl_t *acc_aclp,
+ int ace_count)
+{
+ EC_INIT;
+ char *name = NULL;
+ uuidtype_t uuidtype;
+ struct passwd *pwd;
+ struct group *grp;
+ uid_t id;
+ uint32_t darwin_ace_flags, darwin_ace_rights;
+ acl_tag_t tag;
+ acl_perm_t perm;
+
+ for ( ; ace_count != 0; ace_count--, darwin_aces++) {
+ /* type: allow/deny, posix only has allow */
+ darwin_ace_flags = ntohl(darwin_aces->darwin_ace_flags);
+ if ( ! (darwin_ace_flags & DARWIN_ACE_FLAGS_PERMIT))
+ continue;
+
+ darwin_ace_rights = ntohl(darwin_aces->darwin_ace_rights);
+ perm = map_darwin_right_to_posix_permset(darwin_ace_rights, (*def_aclp != NULL));
+ if (perm == 0)
+ continue; /* dont add empty perm */
+
+ LOG(log_debug, logtype_afpd, "map_ace: no: %u, flags: %08x, darwin: %08x, posix: %02x",
+ ace_count, darwin_ace_flags, darwin_ace_rights, perm);
+
+ /* uid/gid */
+ EC_ZERO_LOG(getnamefromuuid(darwin_aces->darwin_ace_uuid, &name, &uuidtype));
+ switch (uuidtype) {
+ case UUID_LOCAL:
+ free(name);
+ name = NULL;
+ continue;
+ case UUID_USER:
+ EC_NULL_LOG(pwd = getpwnam(name));
+ tag = ACL_USER;
+ id = pwd->pw_uid;
+ LOG(log_debug, logtype_afpd, "map_ace: name: %s, uid: %u", name, id);
+ break;
+ case UUID_GROUP:
+ EC_NULL_LOG(grp = getgrnam(name));
+ tag = ACL_GROUP;
+ id = (uid_t)(grp->gr_gid);
+ LOG(log_debug, logtype_afpd, "map_ace: name: %s, gid: %u", name, id);
+ break;
+ }
+ free(name);
+ name = NULL;
+
+ if (darwin_ace_flags & DARWIN_ACE_INHERIT_CONTROL_FLAGS) {
+ if (*def_aclp == NULL) {
+ /* ace request inheritane but we haven't got a default acl pointer */
+ LOG(log_warning, logtype_afpd, "map_acl: unexpected ACE, flags: 0x%04x",
+ darwin_ace_flags);
+ EC_FAIL;
+ }
+ /* add it as default ace */
+ EC_ZERO_LOG(posix_acl_add_perm(def_aclp, tag, id, perm));
+
+
+ if (! (darwin_ace_flags & DARWIN_ACE_FLAGS_ONLY_INHERIT))
+ /* if it not a "inherit only" ace, it must be added as access aces too */
+ EC_ZERO_LOG(posix_acl_add_perm(acc_aclp, tag, id, perm));
+ } else {
+ EC_ZERO_LOG(posix_acl_add_perm(acc_aclp, tag, id, perm));
+ }
+ }
+
+EC_CLEANUP:
+ if (name)
+ free(name);
+
+ EC_EXIT;
+}
+
+/*
+ * Map ACEs from POSIX to Darwin.
+ * type is either POSIX_DEFAULT_2_DARWIN or POSIX_ACCESS_2_DARWIN, cf. acl_get_file.
+ * Return number of mapped ACES, -1 on error.
+ */
+static int map_acl_posix_to_darwin(int type, const acl_t acl, darwin_ace_t *darwin_aces)
+{
+ EC_INIT;
+ int mapped_aces = 0;
+ int entry_id = ACL_FIRST_ENTRY;
+ acl_entry_t e;
+ acl_tag_t tag;
+ uid_t *uid = NULL;
+ gid_t *gid = NULL;
+ struct passwd *pwd = NULL;
+ struct group *grp = NULL;
+ uint32_t flags;
+ uint32_t rights, maskrights = 0;
+ darwin_ace_t *saved_darwin_aces = darwin_aces;
+
+ LOG(log_maxdebug, logtype_afpd, "map_aces_posix_to_darwin(%s)",
+ (type & MAP_MASK) == POSIX_DEFAULT_2_DARWIN ?
+ "POSIX_DEFAULT_2_DARWIN" : "POSIX_ACCESS_2_DARWIN");
+
+ /* itereate through all ACEs */
+ while (acl_get_entry(acl, entry_id, &e) == 1) {
+ entry_id = ACL_NEXT_ENTRY;
+
+ /* get ACE type */
+ EC_ZERO_LOG(acl_get_tag_type(e, &tag));
+
+ /* we return user and group ACE */
+ switch (tag) {
+ case ACL_USER:
+ EC_NULL_LOG(uid = (uid_t *)acl_get_qualifier(e));
+ EC_NULL_LOG(pwd = getpwuid(*uid));
+ LOG(log_debug, logtype_afpd, "map_aces_posix_to_darwin: uid: %d -> name: %s",
+ *uid, pwd->pw_name);
+ EC_ZERO_LOG(getuuidfromname(pwd->pw_name, UUID_USER, darwin_aces->darwin_ace_uuid));
+ acl_free(uid);
+ uid = NULL;
+ break;
+
+ case ACL_GROUP:
+ EC_NULL_LOG(gid = (gid_t *)acl_get_qualifier(e));
+ EC_NULL_LOG(grp = getgrgid(*gid));
+ LOG(log_debug, logtype_afpd, "map_aces_posix_to_darwin: gid: %d -> name: %s",
+ *gid, grp->gr_name);
+ EC_ZERO_LOG(getuuidfromname(grp->gr_name, UUID_GROUP, darwin_aces->darwin_ace_uuid));
+ acl_free(gid);
+ gid = NULL;
+ break;
+
+ case ACL_MASK:
+ maskrights = posix_permset_to_darwin_rights(e, type & IS_DIR);
+ continue;
+
+ default:
+ continue;
+ }
+
+ /* flags */
+ flags = DARWIN_ACE_FLAGS_PERMIT;
+ if ((type & MAP_MASK) == POSIX_DEFAULT_2_DARWIN)
+ flags |= DARWIN_ACE_FLAGS_FILE_INHERIT
+ | DARWIN_ACE_FLAGS_DIRECTORY_INHERIT
+ | DARWIN_ACE_FLAGS_ONLY_INHERIT;
+ darwin_aces->darwin_ace_flags = htonl(flags);
+
+ /* rights */
+ rights = posix_permset_to_darwin_rights(e, type & IS_DIR);
+ darwin_aces->darwin_ace_rights = htonl(rights);
+
+ darwin_aces++;
+ mapped_aces++;
+ } /* while */
+
+ /* Loop through the mapped ACE buffer once again, applying the mask */
+ for (int i = mapped_aces; i > 0; i--) {
+ saved_darwin_aces->darwin_ace_rights &= htonl(maskrights);
+ saved_darwin_aces++;
+ }
+
+ EC_STATUS(mapped_aces);
+
+EC_CLEANUP:
+ if (uid) acl_free(uid);
+ if (gid) acl_free(gid);
+ EC_EXIT;
+}
+#endif
+
+/*
+ * Multiplex ACL mapping (SOLARIS_2_DARWIN, DARWIN_2_SOLARIS, POSIX_2_DARWIN, DARWIN_2_POSIX).
+ * Reads from 'aces' buffer, writes to 'rbuf' buffer.
+ * Caller must provide buffer.
+ * Darwin ACEs are read and written in network byte order.
+ * Needs to know how many ACEs are in the ACL (ace_count) for Solaris ACLs.
+ * Ignores trivial ACEs.
+ * Return no of mapped ACEs or -1 on error.
+ */
+static int map_acl(int type, void *acl, darwin_ace_t *buf, int ace_count)