Example #1
0
int
add_acl(acl_t acl, uint entry_number, acl_t *prev_acl, const char *filename)
{
	acl_entry_t entry, entry_new;
	acl_t acl_new;
	int entry_id, acl_brand, prev_acl_brand;

	acl_get_brand_np(acl, &acl_brand);
	acl_get_brand_np(*prev_acl, &prev_acl_brand);

	if (prev_acl_brand != ACL_BRAND_NFS4) {
		warnx("%s: the '-a' option is only applicable to NFSv4 ACLs",
		    filename);
		return (-1);
	}

	if (branding_mismatch(acl_brand, ACL_BRAND_NFS4)) {
		warnx("%s: branding mismatch; existing ACL is NFSv4, "
		    "entry to be added is %s", filename,
		    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;

		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++;

		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);
}
Example #2
0
int fpm_unix_set_socket_premissions(struct fpm_worker_pool_s *wp, const char *path) /* {{{ */
{
#ifdef HAVE_FPM_ACL
    if (wp->socket_acl) {
        acl_t aclfile, aclconf;
        acl_entry_t entryfile, entryconf;
        int i;

        /* Read the socket ACL */
        aclconf = wp->socket_acl;
        aclfile = acl_get_file (path, ACL_TYPE_ACCESS);
        if (!aclfile) {
            zlog(ZLOG_SYSERROR, "[pool %s] failed to read the ACL of the socket '%s'", wp->config->name, path);
            return -1;
        }
        /* Copy the new ACL entry from config */
        for (i=ACL_FIRST_ENTRY ; acl_get_entry(aclconf, i, &entryconf) ; i=ACL_NEXT_ENTRY) {
            if (0 > acl_create_entry (&aclfile, &entryfile) ||
                    0 > acl_copy_entry(entryfile, entryconf)) {
                zlog(ZLOG_SYSERROR, "[pool %s] failed to add entry to the ACL of the socket '%s'", wp->config->name, path);
                acl_free(aclfile);
                return -1;
            }
        }
        /* Write the socket ACL */
        if (0 > acl_calc_mask (&aclfile) ||
                0 > acl_valid (aclfile) ||
                0 > acl_set_file (path, ACL_TYPE_ACCESS, aclfile)) {
            zlog(ZLOG_SYSERROR, "[pool %s] failed to write the ACL of the socket '%s'", wp->config->name, path);
            acl_free(aclfile);
            return -1;
        } else {
            zlog(ZLOG_DEBUG, "[pool %s] ACL of the socket '%s' is set", wp->config->name, path);
        }

        acl_free(aclfile);
        return 0;
    }
    /* When listen.users and listen.groups not configured, continue with standard right */
#endif

    if (wp->socket_uid != -1 || wp->socket_gid != -1) {
        if (0 > chown(path, wp->socket_uid, wp->socket_gid)) {
            zlog(ZLOG_SYSERROR, "[pool %s] failed to chown() the socket '%s'", wp->config->name, wp->config->listen_address);
            return -1;
        }
    }
    return 0;
}
static int
clone_entry (acl_t  from_acl, acl_tag_t from_type,
	     acl_t *to_acl,   acl_tag_t to_type)
{
        acl_entry_t from_entry;
	acl_entry_t to_entry;
	
        from_entry = find_entry(from_acl, from_type, ACL_UNDEFINED_ID);
        if (from_entry == NULL) return 1;
	
	if (acl_create_entry(to_acl, &to_entry) != 0)
		return -1;
	
	acl_copy_entry(to_entry, from_entry);
	acl_set_tag_type(to_entry, to_type);
	
	return 0;
}
Example #4
0
static int check_facl(pool *p, const char *path, int mode, void *acl, int nents,
    struct stat *st, uid_t uid, gid_t gid, array_header *suppl_gids) {
# if defined(HAVE_BSD_POSIX_ACL) || defined(HAVE_LINUX_POSIX_ACL)
  register unsigned int i;
  int have_access_entry = FALSE, res = -1;
  pool *acl_pool;
  acl_t facl = acl;
  acl_entry_t ae;
  acl_tag_t ae_type;
  acl_entry_t acl_user_entry = NULL;
  acl_entry_t acl_group_entry = NULL;
  acl_entry_t acl_other_entry = NULL;
  acl_entry_t acl_mask_entry = NULL;
  array_header *acl_groups;
  array_header *acl_users;

  /* Iterate through all of the ACL entries, sorting them for later
   * checking.
   */
  res = acl_get_entry(facl, ACL_FIRST_ENTRY, &ae);
  if (res < 0) {
    pr_log_debug(DEBUG10, "FS: unable to retrieve first ACL entry for '%s': %s",
      path, strerror(errno));
    errno = EACCES;
    return -1;
  }

  if (res == 0) {
    pr_log_debug(DEBUG3, "FS: ill-formed ACL for '%s' has no entries!", path);
    errno = EACCES;
    return -1;
  }

  acl_pool = make_sub_pool(p);
  acl_groups = make_array(acl_pool, 1, sizeof(acl_entry_t));
  acl_users = make_array(acl_pool, 1, sizeof(acl_entry_t));

  while (res > 0) {
    if (acl_get_tag_type(ae, &ae_type) < 0) {
      pr_log_debug(DEBUG5,
        "FS: error retrieving type of ACL entry for '%s': %s", path,
        strerror(errno));
      res = acl_get_entry(facl, ACL_NEXT_ENTRY, &ae);
      continue;
    }

    if (ae_type & ACL_USER_OBJ) {
      acl_copy_entry(acl_user_entry, ae);

    } else if (ae_type & ACL_USER) {
      acl_entry_t *ae_dup = push_array(acl_users);
      acl_copy_entry(*ae_dup, ae);

    } else if (ae_type & ACL_GROUP_OBJ) {
      acl_copy_entry(acl_group_entry, ae);

    } else if (ae_type & ACL_GROUP) {
      acl_entry_t *ae_dup = push_array(acl_groups);
      acl_copy_entry(*ae_dup, ae);

    } else if (ae_type & ACL_OTHER) {
      acl_copy_entry(acl_other_entry, ae);

    } else if (ae_type & ACL_MASK) {
      acl_copy_entry(acl_mask_entry, ae);
    }

    res = acl_get_entry(facl, ACL_NEXT_ENTRY, &ae);
  }

  /* Select the ACL entry that determines access. */
  res = -1;

  /* 1. If the given user ID matches the file owner, use that entry for
   *    access.
   */
  if (uid == st->st_uid) {
    /* Check the acl_user_entry for access. */
    acl_copy_entry(ae, acl_user_entry);
    ae_type = ACL_USER_OBJ;
    have_access_entry = TRUE;
  }

  /* 2. If not matched above, and f the given user ID matches one of the
   *    named user entries, use that entry for access.
   */
  for (i = 0; !have_access_entry && i < acl_users->nelts; i++) {
    acl_entry_t e = ((acl_entry_t *) acl_users->elts)[i];

    if (uid == *((uid_t *) acl_get_qualifier(e))) {

      /* Check this entry for access. Note that it'll need to
       * be modified by the mask, if any, later.
       */
      acl_copy_entry(ae, e);
      ae_type = ACL_USER;
      have_access_entry = TRUE;
      break;
    }
  }

  /* 3. If not matched above, and if one of the group IDs matches the
   *    group owner entry, and the group owner entry contains the
   *    requested permissions, use that entry for access.
   */
  if (!have_access_entry &&
      gid == st->st_gid) {

    /* Check the acl_group_entry for access. First though, we need to
     * see if the acl_group_entry contains the requested permissions.
     */
    acl_permset_t perms;
    acl_get_permset(acl_group_entry, &perms);

#  if defined(HAVE_BSD_POSIX_ACL)
    if (acl_get_perm_np(perms, mode) == 1) {
#  elif defined(HAVE_LINUX_POSIX_ACL)
    if (acl_get_perm(perms, mode) == 1) {
#  endif
      acl_copy_entry(ae, acl_group_entry);
      ae_type = ACL_GROUP_OBJ;
      have_access_entry = TRUE;
    }
  }

  if (suppl_gids) {
    for (i = 0; !have_access_entry && i < suppl_gids->nelts; i++) {
      gid_t suppl_gid = ((gid_t *) suppl_gids->elts)[i];

      if (suppl_gid == st->st_gid) {
        /* Check the acl_group_entry for access. First though, we need to
         * see if the acl_group_entry contains the requested permissions.
         */
        acl_permset_t perms;
        acl_get_permset(acl_group_entry, &perms);

#  if defined(HAVE_BSD_POSIX_ACL)
        if (acl_get_perm_np(perms, mode) == 1) {
#  elif defined(HAVE_LINUX_POSIX_ACL)
        if (acl_get_perm(perms, mode) == 1) {
#  endif
          acl_copy_entry(ae, acl_group_entry);
          ae_type = ACL_GROUP_OBJ;
          have_access_entry = TRUE;
          break;
        }
      }
    }
  }

  /* 5. If not matched above, and if one of the group IDs matches one
   *    of the named group entries, and that entry contains the requested
   *    permissions, use that entry for access.
   */
  for (i = 0; !have_access_entry && i < acl_groups->nelts; i++) {
    acl_entry_t e = ((acl_entry_t *) acl_groups->elts)[i];

    if (gid == *((gid_t *) acl_get_qualifier(e))) {

      /* Check this entry for access. Note that it'll need to
       * be modified by the mask, if any, later.
       */
      acl_permset_t perms;
      acl_get_permset(e, &perms);

#  if defined(HAVE_BSD_POSIX_ACL)
      if (acl_get_perm_np(perms, mode) == 1) {
#  elif defined(HAVE_LINUX_POSIX_ACL)
      if (acl_get_perm(perms, mode) == 1) {
#  endif
        acl_copy_entry(ae, e);
        ae_type = ACL_GROUP;
        have_access_entry = TRUE;
        break;
      }
    }

    if (suppl_gids) {
      register unsigned int j;

      for (j = 0; !have_access_entry && j < suppl_gids->nelts; j++) {
        gid_t suppl_gid = ((gid_t *) suppl_gids->elts)[j];

        if (suppl_gid == *((gid_t *) acl_get_qualifier(e))) {
          /* Check this entry for access. Note that it'll need to
           * be modified by the mask, if any, later.
           */
          acl_permset_t perms;
          acl_get_permset(e, &perms);

#  if defined(HAVE_BSD_POSIX_ACL)
          if (acl_get_perm_np(perms, mode) == 1) {
#  elif defined(HAVE_LINUX_POSIX_ACL)
          if (acl_get_perm(perms, mode) == 1) {
#  endif
            acl_copy_entry(ae, e);
            ae_type = ACL_GROUP;
            have_access_entry = TRUE;
            break;
          }
        }
      }
    }
  }

  /* 6. If not matched above, and if one of the group IDs matches
   *    the group owner or any of the named group entries, but neither
   *    the group owner entry nor any of the named group entries contains
   *    the requested permissions, access is denied.
   */

  /* 7. If not matched above, the other entry determines access.
   */
  if (!have_access_entry) {
    acl_copy_entry(ae, acl_other_entry);
    ae_type = ACL_OTHER;
    have_access_entry = TRUE;
  }

  /* Access determination:
   *
   *  If either the user owner entry or other entry were used, and the
   *  entry contains the requested permissions, access is permitted.
   *
   *  Otherwise, if the selected entry and the mask entry both contain
   *  the requested permissions, access is permitted.
   *
   *  Otherwise, access is denied.
   */
  switch (ae_type) {
    case ACL_USER_OBJ:
    case ACL_OTHER: {
      acl_permset_t perms;
      acl_get_permset(ae, &perms);

#  if defined(HAVE_BSD_POSIX_ACL)
      if (acl_get_perm_np(perms, mode) == 1) {
#  elif defined(HAVE_LINUX_POSIX_ACL)
      if (acl_get_perm(perms, mode) == 1) {
#  endif
        res = 0;
      }

      break;
    }

    default: {
      acl_permset_t ent_perms, mask_perms;
      acl_get_permset(ae, &ent_perms);
      acl_get_permset(acl_mask_entry, &mask_perms);

#  if defined(HAVE_BSD_POSIX_ACL)
      if (acl_get_perm_np(ent_perms, mode) == 1 &&
          acl_get_perm_np(mask_perms, mode) == 1) {
#  elif defined(HAVE_LINUX_POSIX_ACL)
      if (acl_get_perm(ent_perms, mode) == 1 &&
          acl_get_perm(mask_perms, mode) == 1) {
#  endif
        res = 0;
      }

      break;
    }
  }

  destroy_pool(acl_pool);

  if (res < 0)
    errno = EACCES;
  return res;

# elif defined(HAVE_SOLARIS_POSIX_ACL)
  register unsigned int i;
  int have_access_entry = FALSE, idx, res = -1;
  pool *acl_pool;
  aclent_t *acls = acl;
  aclent_t ae;
  int ae_type = 0;
  aclent_t acl_user_entry;
  aclent_t acl_group_entry;
  aclent_t acl_other_entry;
  aclent_t acl_mask_entry;
  array_header *acl_groups;
  array_header *acl_users;

  /* In the absence of any clear documentation, I'll assume that
   * Solaris ACLs follow the same selection and checking algorithm
   * as do BSD and Linux.
   */

  res = aclcheck(acls, nents, &idx);
  switch (res) {
    case 0:
      break;

    case GRP_ERROR:
      pr_log_debug(DEBUG3, "FS: ill-formed ACL for '%s': %s", path,
        "too many GROUP entries");
      errno = EACCES;
      return -1;

    case USER_ERROR:
      pr_log_debug(DEBUG3, "FS: ill-formed ACL for '%s': %s", path,
        "too many USER entries");
      errno = EACCES;
      return -1;

    case OTHER_ERROR:
      pr_log_debug(DEBUG3, "FS: ill-formed ACL for '%s': %s", path,
        "too many OTHER entries");
      errno = EACCES;
      return -1;

    case CLASS_ERROR:
      pr_log_debug(DEBUG3, "FS: ill-formed ACL for '%s': %s", path,
        "too many CLASS entries");
      errno = EACCES;
      return -1;

    case DUPLICATE_ERROR:
      pr_log_debug(DEBUG3, "FS: ill-formed ACL for '%s': %s", path,
        "duplicate entries");
      errno = EACCES;
      return -1;

    case MISS_ERROR:
      pr_log_debug(DEBUG3, "FS: ill-formed ACL for '%s': %s", path,
        "missing required entry");
      errno = EACCES;
      return -1;

    case MEM_ERROR:
      pr_log_debug(DEBUG3, "FS: ill-formed ACL for '%s': %s", path,
        "Out of memory!");
      errno = EACCES;
      return -1;

    case ENTRY_ERROR:
      pr_log_debug(DEBUG3, "FS: ill-formed ACL for '%s': %s", path,
        "invalid entry type");
      errno = EACCES;
      return -1;
  }

  /* Iterate through all of the ACL entries, sorting them for later
   * checking.
   */

  acl_pool = make_sub_pool(p);
  acl_groups = make_array(acl_pool, 1, sizeof(aclent_t));
  acl_users = make_array(acl_pool, 1, sizeof(aclent_t));

  for (i = 0; i < nents; i++) {
    if (acls[i].a_type & USER_OBJ) {
      memcpy(&acl_user_entry, &(acls[i]), sizeof(aclent_t));

    } else if (acls[i].a_type & USER) {
      aclent_t *ae_dup = push_array(acl_users);
      memcpy(ae_dup, &(acls[i]), sizeof(aclent_t));

    } else if (acls[i].a_type & GROUP_OBJ) {
      memcpy(&acl_group_entry, &(acls[i]), sizeof(aclent_t));

    } else if (acls[i].a_type & GROUP) {
      aclent_t *ae_dup = push_array(acl_groups);
      memcpy(ae_dup, &(acls[i]), sizeof(aclent_t));

    } else if (acls[i].a_type & OTHER_OBJ) {
      memcpy(&acl_other_entry, &(acls[i]), sizeof(aclent_t));

    } else if (acls[i].a_type & CLASS_OBJ) {
      memcpy(&acl_mask_entry, &(acls[i]), sizeof(aclent_t));
    }
  }

  /* Select the ACL entry that determines access. */
  res = -1;

  /* 1. If the given user ID matches the file owner, use that entry for
   *    access.
   */
  if (uid == st->st_uid) {
    /* Check the acl_user_entry for access. */
    memcpy(&ae, &acl_user_entry, sizeof(aclent_t));
    ae_type = USER_OBJ;
    have_access_entry = TRUE;
  }

  /* 2. If not matched above, and f the given user ID matches one of the
   *    named user entries, use that entry for access.
   */
  for (i = 0; !have_access_entry && i < acl_users->nelts; i++) {
    aclent_t e;
    memcpy(&e, &(((aclent_t *) acl_users->elts)[i]), sizeof(aclent_t));

    if (uid == e.a_id) {

      /* Check this entry for access. Note that it'll need to
       * be modified by the mask, if any, later.
       */
      memcpy(&ae, &e, sizeof(aclent_t));
      ae_type = USER;
      have_access_entry = TRUE;
      break;
    }
  }

  /* 3. If not matched above, and if one of the group IDs matches the
   *    group owner entry, and the group owner entry contains the
   *    requested permissions, use that entry for access.
   */
  if (!have_access_entry &&
      gid == st->st_gid) {

    /* Check the acl_group_entry for access. First though, we need to
     * see if the acl_group_entry contains the requested permissions.
     */
    if (acl_group_entry.a_perm & mode) {
      memcpy(&ae, &acl_group_entry, sizeof(aclent_t));
      ae_type = GROUP_OBJ;
      have_access_entry = TRUE;
    }
  }

  if (suppl_gids) {
    for (i = 0; !have_access_entry && i < suppl_gids->nelts; i++) {
      gid_t suppl_gid = ((gid_t *) suppl_gids->elts)[i];

      if (suppl_gid == st->st_gid) {
        /* Check the acl_group_entry for access. First though, we need to
         * see if the acl_group_entry contains the requested permissions.
         */
        if (acl_group_entry.a_perm & mode) {
          memcpy(&ae, &acl_group_entry, sizeof(aclent_t));
          ae_type = GROUP_OBJ;
          have_access_entry = TRUE;
          break;
        }
      }
    }
  }

  /* 5. If not matched above, and if one of the group IDs matches one
   *    of the named group entries, and that entry contains the requested
   *    permissions, use that entry for access.
   */
  for (i = 0; !have_access_entry && i < acl_groups->nelts; i++) {
    aclent_t e;
    memcpy(&e, &(((aclent_t *) acl_groups->elts)[i]), sizeof(aclent_t));

    if (gid == e.a_id) {

      /* Check this entry for access. Note that it'll need to
       * be modified by the mask, if any, later.
       */
      if (e.a_perm & mode) {
        memcpy(&ae, &e, sizeof(aclent_t));
        ae_type = GROUP;
        have_access_entry = TRUE;
        break;
      }
    }

    if (suppl_gids) {
      register unsigned int j;

      for (j = 0; !have_access_entry && j < suppl_gids->nelts; j++) {
        gid_t suppl_gid = ((gid_t *) suppl_gids->elts)[j];

        if (suppl_gid == e.a_id) {
          /* Check this entry for access. Note that it'll need to
           * be modified by the mask, if any, later.
           */
          if (e.a_perm & mode) {
            memcpy(&ae, &e, sizeof(aclent_t));
            ae_type = GROUP;
            have_access_entry = TRUE;
            break;
          }
        }
      }
    }
  }

  /* 6. If not matched above, and if one of the group IDs matches
   *    the group owner or any of the named group entries, but neither
   *    the group owner entry nor any of the named group entries contains
   *    the requested permissions, access is denied.
   */

  /* 7. If not matched above, the other entry determines access.
   */
  if (!have_access_entry) {
    memcpy(&ae, &acl_other_entry, sizeof(aclent_t));
    ae_type = OTHER_OBJ;
    have_access_entry = TRUE;
  }

  /* Access determination:
   *
   *  If either the user owner entry or other entry were used, and the
   *  entry contains the requested permissions, access is permitted.
   *
   *  Otherwise, if the selected entry and the mask entry both contain
   *  the requested permissions, access is permitted.
   *
   *  Otherwise, access is denied.
   */
  switch (ae_type) {
    case USER_OBJ:
    case OTHER_OBJ:
      if (ae.a_perm & mode)
        res = 0;
      break;

    default: 
      if ((ae.a_perm & mode) &&
          (acl_mask_entry.a_perm & mode))
        res = 0;
      break;
  }

  destroy_pool(acl_pool);

  if (res < 0)
    errno = EACCES;
  return res;
# endif /* HAVE_SOLARIS_POSIX_ACL */
}

/* FSIO handlers
 */

static int facl_fsio_access(pr_fs_t *fs, const char *path, int mode,
    uid_t uid, gid_t gid, array_header *suppl_gids) {
  int nents = 0;
  struct stat st;
  void *acls;

  pr_fs_clear_cache();
  if (pr_fsio_stat(path, &st) < 0)
    return -1;

  /* Look up the acl for this path. */
# if defined(HAVE_BSD_POSIX_ACL) || defined(HAVE_LINUX_POSIX_ACL)
  acls = acl_get_file(path, ACL_TYPE_ACCESS);

  if (!acls) {
    pr_log_debug(DEBUG10, "FS: unable to retrieve ACL for '%s': %s",
      path, strerror(errno));
    return -1;
  }

# elif defined(HAVE_SOLARIS_POSIX_ACL)

  nents = acl(path, GETACLCNT, 0, NULL);
  if (nents < 0) {
    pr_log_debug(DEBUG10, "FS: unable to retrieve ACL count for '%s': %s",
      path, strerror(errno));
    return -1;
  }

  acls = pcalloc(fs->fs_pool, nents * sizeof(aclent_t));

  nents = acl(path, GETACL, nents, acls);
  if (nents < 0) {
    pr_log_debug(DEBUG10, "FS: unable to retrieve ACL for '%s': %s",
      path, strerror(errno));
    return -1;
  }
# endif

  return check_facl(fs->fs_pool, path, mode, acls, nents, &st,
    uid, gid, suppl_gids);
}

static int facl_fsio_faccess(pr_fh_t *fh, int mode, uid_t uid, gid_t gid,
    array_header *suppl_gids) {
  int nents = 0;
  struct stat st;
  void *acls;

  pr_fs_clear_cache();
  if (pr_fsio_fstat(fh, &st) < 0)
    return -1;

  /* Look up the acl for this fd. */
# if defined(HAVE_BSD_POSIX_ACL) || defined(HAVE_LINUX_POSIX_ACL)
  acls = acl_get_fd(PR_FH_FD(fh));

  if (!acls) {
    pr_log_debug(DEBUG10, "FS: unable to retrieve ACL for '%s': %s",
      fh->fh_path, strerror(errno));
    return -1;
  }

# elif defined(HAVE_SOLARIS_POSIX_ACL)

  nents = facl(PR_FH_FD(fh), GETACLCNT, 0, NULL);
  if (nents < 0) {
    pr_log_debug(DEBUG10, "FS: unable to retrieve ACL count for '%s': %s",
      fh->fh_path, strerror(errno));
    return -1;
  }

  acls = pcalloc(fh->fh_fs->fs_pool, nents * sizeof(aclent_t));

  nents = facl(PR_FH_FD(fh), GETACL, nents, acls);
  if (nents < 0) {
    pr_log_debug(DEBUG10, "FS: unable to retrieve ACL for '%s': %s",
      fh->fh_path, strerror(errno));
    return -1;
  }
# endif

  return check_facl(fh->fh_fs->fs_pool, fh->fh_path, mode, acls, nents, &st,
    uid, gid, suppl_gids);
}
#endif /* HAVE_POSIX_ACL */

/* Initialization routines
 */

static int facl_init(void) {
#if defined(PR_USE_FACL) && defined(HAVE_POSIX_ACL)
  pr_fs_t *fs = pr_register_fs(permanent_pool, "facl", "/");
  if (!fs) {
    pr_log_pri(PR_LOG_ERR, MOD_FACL_VERSION ": error registering fs: %s",
      strerror(errno));
    return -1;
  }

  /* Ensure that our ACL-checking handlers are used. */
  fs->access = facl_fsio_access;
  fs->faccess = facl_fsio_faccess;
#endif /* PR_USE_FACL and HAVE_POSIX_ACL */

  return 0;
}

/* Module Tables
 */

module facl_module = {
  /* Always NULL */
  NULL, NULL,

  /* Module API version */
  0x20,

  /* Module name */
  "facl",

  /* Module configuration directive handlers */
  NULL,

  /* Module command handlers */
  NULL,

  /* Module authentication handlers */
  NULL,

  /* Module initialization */
  facl_init,

  /* Session initialization */
  NULL,

  /* Module version */
  MOD_FACL_VERSION
};
Example #5
0
static acl_t
_posix1e_acl_strip_np(const acl_t aclp, int recalculate_mask)
{
	acl_t acl_new, acl_old;
	acl_entry_t entry, entry_new;
	acl_permset_t perm;
	acl_tag_t tag;
	int entry_id, have_mask_entry;

	assert(_acl_brand(aclp) == ACL_BRAND_POSIX);

	acl_old = acl_dup(aclp);
	if (acl_old == NULL)
		return (NULL);

	assert(_acl_brand(acl_old) == ACL_BRAND_POSIX);

	have_mask_entry = 0;
	acl_new = acl_init(ACL_MAX_ENTRIES);
	if (acl_new == NULL)
		return (NULL);
	tag = ACL_UNDEFINED_TAG;

	/* only save the default user/group/other entries */
	entry_id = ACL_FIRST_ENTRY;
	while (acl_get_entry(acl_old, entry_id, &entry) == 1) {
		entry_id = ACL_NEXT_ENTRY;

		assert(_entry_brand(entry) == ACL_BRAND_POSIX);

		if (acl_get_tag_type(entry, &tag) == -1)
			return (NULL);

		switch(tag) {
		case ACL_USER_OBJ:
		case ACL_GROUP_OBJ:
		case ACL_OTHER:
			if (acl_get_tag_type(entry, &tag) == -1)
				return (NULL);
			if (acl_get_permset(entry, &perm) == -1)
				return (NULL);
			if (acl_create_entry(&acl_new, &entry_new) == -1)
				return (NULL);
			if (acl_set_tag_type(entry_new, tag) == -1)
				return (NULL);
			if (acl_set_permset(entry_new, perm) == -1)
				return (NULL);
			if (acl_copy_entry(entry_new, entry) == -1)
				return (NULL);
			assert(_entry_brand(entry_new) == ACL_BRAND_POSIX);
			break;
		case ACL_MASK:
			have_mask_entry = 1;
			break;
		default:
			break;
		}
	}

	assert(_acl_brand(acl_new) == ACL_BRAND_POSIX);

	if (have_mask_entry && recalculate_mask) {
		if (acl_calc_mask(&acl_new) == -1)
			return (NULL);
	}

	return (acl_new);
}
Example #6
0
/*
 * remove extended entries
 */
void
remove_ext(acl_t *prev_acl)
{
	acl_t acl_new, acl_old;
	acl_entry_t entry, entry_new;
	acl_permset_t perm;
	acl_tag_t tag;
	int entry_id, have_mask_entry;

	if (acl_type == ACL_TYPE_ACCESS)
		acl_old = acl_dup(prev_acl[ACCESS_ACL]);
	else
		acl_old = acl_dup(prev_acl[DEFAULT_ACL]);

	if (acl_old == NULL)
		err(1, "acl_dup() failed");

	have_mask_entry = 0;
	acl_new = acl_init(ACL_MAX_ENTRIES);
	if (acl_new == NULL)
		err(1, "acl_init() failed");

	tag = ACL_UNDEFINED_TAG;

	/* only save the default user/group/other entries */
	entry_id = ACL_FIRST_ENTRY;
	while (acl_get_entry(acl_old, entry_id, &entry) == 1) {
		entry_id = ACL_NEXT_ENTRY;

		if (acl_get_tag_type(entry, &tag) == -1)
			err(1, "acl_get_tag_type() failed");

		switch(tag) {
		case ACL_USER_OBJ:
		case ACL_GROUP_OBJ:
		case ACL_OTHER:
			if (acl_get_tag_type(entry, &tag) == -1)
				err(1, "acl_get_tag_type() failed");
			if (acl_get_permset(entry, &perm) == -1)
				err(1, "acl_get_permset() failed");
			if (acl_create_entry(&acl_new, &entry_new) == -1)
				err(1, "acl_create_entry() failed");
			if (acl_set_tag_type(entry_new, tag) == -1)
				err(1, "acl_set_tag_type() failed");
			if (acl_set_permset(entry_new, perm) == -1)
				err(1, "acl_get_permset() failed");
			if (acl_copy_entry(entry_new, entry) == -1)
				err(1, "acl_copy_entry() failed");
			break;
		case ACL_MASK:
			have_mask_entry = 1;
			break;
		default:
			break;
		}
	}
	if (have_mask_entry && n_flag == 0) {
		if (acl_calc_mask(&acl_new) == -1)
			err(1, "acl_calc_mask() failed");
	} else {
		have_mask = 1;
	}

	if (acl_type == ACL_TYPE_ACCESS) {
		acl_free(prev_acl[ACCESS_ACL]);
		prev_acl[ACCESS_ACL] = acl_new;
	} else {
		acl_free(prev_acl[DEFAULT_ACL]);
		prev_acl[DEFAULT_ACL] = acl_new;
	}
}
Example #7
0
/*
 * 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);
}
Example #8
0
static unsigned int
call_syscall(struct syscall_desc *scall, char *argv[])
{
	struct stat64 sb;
	long long flags;
	unsigned int i;
	char *endp;
	int name, rval;
	union {
		char *str;
		long long num;
	} args[MAX_ARGS];
#ifdef HAS_FREEBSD_ACL
	int entry_id = ACL_FIRST_ENTRY;
	acl_t acl, newacl;
	acl_entry_t entry, newentry;
#endif

	/*
	 * Verify correctness of the arguments.
	 */
	for (i = 0; i < sizeof(args)/sizeof(args[0]); i++) {
		if (scall->sd_args[i] == TYPE_NONE) {
			if (argv[i] == NULL || strcmp(argv[i], ":") == 0)
				break;
			fprintf(stderr, "too many arguments [%s]\n", argv[i]);
			exit(1);
		} else {
			if (argv[i] == NULL || strcmp(argv[i], ":") == 0) {
				if (scall->sd_args[i] & TYPE_OPTIONAL)
					break;
				fprintf(stderr, "too few arguments\n");
				exit(1);
			}
			if ((scall->sd_args[i] & TYPE_MASK) == TYPE_STRING) {
				if (strcmp(argv[i], "NULL") == 0)
					args[i].str = NULL;
				else if (strcmp(argv[i], "DEADCODE") == 0)
					args[i].str = (void *)0xdeadc0de;
				else
					args[i].str = argv[i];
			} else if ((scall->sd_args[i] & TYPE_MASK) ==
			    TYPE_NUMBER) {
				args[i].num = strtoll(argv[i], &endp, 0);
				if (*endp != '\0' &&
				    !isspace((unsigned char)*endp)) {
					fprintf(stderr,
					    "invalid argument %u, number expected [%s]\n",
					    i, endp);
					exit(1);
				}
			} else if ((scall->sd_args[i] & TYPE_MASK) ==
			    TYPE_DESCRIPTOR) {
				if (strcmp(argv[i], "AT_FDCWD") == 0) {
					args[i].num = AT_FDCWD;
				} else if (strcmp(argv[i], "BADFD") == 0) {
					/* In case AT_FDCWD is -1 on some systems... */
					if (AT_FDCWD == -1)
						args[i].num = -2;
					else
						args[i].num = -1;
				} else {
					int pos;

					pos = strtoll(argv[i], &endp, 0);
					if (*endp != '\0' &&
					    !isspace((unsigned char)*endp)) {
						fprintf(stderr,
						    "invalid argument %u, number expected [%s]\n",
						    i, endp);
						exit(1);
					}
					args[i].num = descriptor_get(pos);
				}
			}
		}
	}
	/*
	 * Call the given syscall.
	 */
#define	NUM(n)	(args[(n)].num)
#define	STR(n)	(args[(n)].str)
	switch (scall->sd_action) {
	case ACTION_OPEN:
		flags = str2flags(open_flags, STR(1));
		if (flags & O_CREAT) {
			if (i == 2) {
				fprintf(stderr, "too few arguments\n");
				exit(1);
			}
			rval = open(STR(0), (int)flags, (mode_t)NUM(2));
		} else {
			if (i == 3) {
				fprintf(stderr, "too many arguments\n");
				exit(1);
			}
			rval = open(STR(0), (int)flags);
		}
		if (rval >= 0)
			descriptor_add(rval);
		break;
	case ACTION_OPENAT:
		flags = str2flags(open_flags, STR(2));
		if (flags & O_CREAT) {
			if (i == 3) {
				fprintf(stderr, "too few arguments\n");
				exit(1);
			}
			rval = openat(NUM(0), STR(1), (int)flags,
			    (mode_t)NUM(3));
		} else {
			if (i == 4) {
				fprintf(stderr, "too many arguments\n");
				exit(1);
			}
			rval = openat(NUM(0), STR(1), (int)flags);
		}
		if (rval >= 0)
			descriptor_add(rval);
		break;
	case ACTION_CREATE:
		rval = open(STR(0), O_CREAT | O_EXCL, (mode_t)NUM(1));
		if (rval >= 0)
			close(rval);
		break;
	case ACTION_UNLINK:
		rval = unlink(STR(0));
		break;
	case ACTION_UNLINKAT:
		rval = unlinkat(NUM(0), STR(1),
		    (int)str2flags(unlinkat_flags, STR(2)));
		break;
	case ACTION_MKDIR:
		rval = mkdir(STR(0), (mode_t)NUM(1));
		break;
	case ACTION_MKDIRAT:
		rval = mkdirat(NUM(0), STR(1), (mode_t)NUM(2));
		break;
	case ACTION_RMDIR:
		rval = rmdir(STR(0));
		break;
	case ACTION_LINK:
		rval = link(STR(0), STR(1));
		break;
	case ACTION_LINKAT:
		rval = linkat(NUM(0), STR(1), NUM(2), STR(3),
		    (int)str2flags(linkat_flags, STR(4)));
		break;
	case ACTION_SYMLINK:
		rval = symlink(STR(0), STR(1));
		break;
	case ACTION_SYMLINKAT:
		rval = symlinkat(STR(0), NUM(1), STR(2));
		break;
	case ACTION_RENAME:
		rval = rename(STR(0), STR(1));
		break;
	case ACTION_RENAMEAT:
		rval = renameat(NUM(0), STR(1), NUM(2), STR(3));
		break;
	case ACTION_MKFIFO:
		rval = mkfifo(STR(0), (mode_t)NUM(1));
		break;
	case ACTION_MKFIFOAT:
		rval = mkfifoat(NUM(0), STR(1), (mode_t)NUM(2));
		break;
	case ACTION_MKNOD:
	case ACTION_MKNODAT:
	    {
		mode_t ntype;
		dev_t dev;
		int fa;

		switch (scall->sd_action) {
		case ACTION_MKNOD:
			fa = 0;
			break;
		case ACTION_MKNODAT:
			fa = 1;
			break;
		default:
			abort();
		}

		dev = makedev(NUM(fa + 3), NUM(fa + 4));
		if (strcmp(STR(fa + 1), "c") == 0)	/* character device */
			ntype = S_IFCHR;
		else if (strcmp(STR(fa + 1), "b") == 0)	/* block device */
			ntype = S_IFBLK;
		else if (strcmp(STR(fa + 1), "f") == 0)	/* fifo special */
			ntype = S_IFIFO;
		else if (strcmp(STR(fa + 1), "d") == 0)	/* directory */
			ntype = S_IFDIR;
		else if (strcmp(STR(fa + 1), "o") == 0)	/* regular file */
			ntype = S_IFREG;
		else {
			fprintf(stderr, "wrong argument 1\n");
			exit(1);
		}
		switch (scall->sd_action) {
		case ACTION_MKNOD:
			rval = mknod(STR(0), ntype | NUM(2), dev);
			break;
		case ACTION_MKNODAT:
			rval = mknodat(NUM(0), STR(1), ntype | NUM(3), dev);
			break;
		default:
			abort();
		}
		break;
	    }
	case ACTION_BIND:
	    {
		struct sockaddr_un sunx;

		sunx.sun_family = AF_UNIX;
		strncpy(sunx.sun_path, STR(0), sizeof(sunx.sun_path) - 1);
		sunx.sun_path[sizeof(sunx.sun_path) - 1] = '\0';
		rval = socket(AF_UNIX, SOCK_STREAM, 0);
		if (rval < 0)
			break;
		rval = bind(rval, (struct sockaddr *)&sunx, sizeof(sunx));
		break;
	    }
#ifdef HAS_BINDAT
	case ACTION_BINDAT:
	    {
		struct sockaddr_un sunx;

		sunx.sun_family = AF_UNIX;
		strncpy(sunx.sun_path, STR(1), sizeof(sunx.sun_path) - 1);
		sunx.sun_path[sizeof(sunx.sun_path) - 1] = '\0';
		rval = socket(AF_UNIX, SOCK_STREAM, 0);
		if (rval < 0)
			break;
		rval = bindat(NUM(0), rval, (struct sockaddr *)&sunx,
		    sizeof(sunx));
		break;
	    }
#endif
	case ACTION_CONNECT:
	    {
		struct sockaddr_un sunx;

		sunx.sun_family = AF_UNIX;
		strncpy(sunx.sun_path, STR(0), sizeof(sunx.sun_path) - 1);
		sunx.sun_path[sizeof(sunx.sun_path) - 1] = '\0';
		rval = socket(AF_UNIX, SOCK_STREAM, 0);
		if (rval < 0)
			break;
		rval = connect(rval, (struct sockaddr *)&sunx, sizeof(sunx));
		break;
	    }
#ifdef HAS_CONNECTAT
	case ACTION_CONNECTAT:
	    {
		struct sockaddr_un sunx;

		sunx.sun_family = AF_UNIX;
		strncpy(sunx.sun_path, STR(1), sizeof(sunx.sun_path) - 1);
		sunx.sun_path[sizeof(sunx.sun_path) - 1] = '\0';
		rval = socket(AF_UNIX, SOCK_STREAM, 0);
		if (rval < 0)
			break;
		rval = connectat(NUM(0), rval, (struct sockaddr *)&sunx,
		    sizeof(sunx));
		break;
	    }
#endif
	case ACTION_CHMOD:
		rval = chmod(STR(0), (mode_t)NUM(1));
		break;
	case ACTION_FCHMOD:
		rval = fchmod(NUM(0), (mode_t)NUM(1));
		break;
#ifdef HAS_LCHMOD
	case ACTION_LCHMOD:
		rval = lchmod(STR(0), (mode_t)NUM(1));
		break;
#endif
	case ACTION_FCHMODAT:
		rval = fchmodat(NUM(0), STR(1), (mode_t)NUM(2),
		    str2flags(fchmodat_flags, STR(3)));
		break;
	case ACTION_CHOWN:
		rval = chown(STR(0), (uid_t)NUM(1), (gid_t)NUM(2));
		break;
	case ACTION_FCHOWN:
		rval = fchown(NUM(0), (uid_t)NUM(1), (gid_t)NUM(2));
		break;
	case ACTION_LCHOWN:
		rval = lchown(STR(0), (uid_t)NUM(1), (gid_t)NUM(2));
		break;
	case ACTION_FCHOWNAT:
		rval = fchownat(NUM(0), STR(1), (uid_t)NUM(2), (gid_t)NUM(3),
		    (int)str2flags(fchownat_flags, STR(4)));
		break;
#ifdef HAS_CHFLAGS
	case ACTION_CHFLAGS:
		rval = chflags(STR(0),
		    (unsigned long)str2flags(chflags_flags, STR(1)));
		break;
#endif
#ifdef HAS_FCHFLAGS
	case ACTION_FCHFLAGS:
		rval = fchflags(NUM(0),
		    (unsigned long)str2flags(chflags_flags, STR(1)));
		break;
#endif
#ifdef HAS_CHFLAGSAT
	case ACTION_CHFLAGSAT:
		rval = chflagsat(NUM(0), STR(1),
		    (unsigned long)str2flags(chflags_flags, STR(2)),
		    (int)str2flags(chflagsat_flags, STR(3)));
		break;
#endif
#ifdef HAS_LCHFLAGS
	case ACTION_LCHFLAGS:
		rval = lchflags(STR(0),
		    (unsigned long)str2flags(chflags_flags, STR(1)));
		break;
#endif
	case ACTION_TRUNCATE:
		rval = truncate64(STR(0), NUM(1));
		break;
	case ACTION_FTRUNCATE:
		rval = ftruncate64(NUM(0), NUM(1));
		break;
	case ACTION_STAT:
		rval = stat64(STR(0), &sb);
		if (rval == 0) {
			show_stats(&sb, STR(1));
			return (i);
		}
		break;
	case ACTION_FSTAT:
		rval = fstat64(NUM(0), &sb);
		if (rval == 0) {
			show_stats(&sb, STR(1));
			return (i);
		}
		break;
	case ACTION_LSTAT:
		rval = lstat64(STR(0), &sb);
		if (rval == 0) {
			show_stats(&sb, STR(1));
			return (i);
		}
		break;
	case ACTION_FSTATAT:
		rval = fstatat(NUM(0), STR(1), &sb,
		    (int)str2flags(fstatat_flags, STR(2)));
		if (rval == 0) {
			show_stats(&sb, STR(3));
			return (i);
		}
		break;
	case ACTION_PATHCONF:
	case ACTION_FPATHCONF:
	case ACTION_LPATHCONF:
	    {
		long lrval;

		name = str2name(pathconf_names, STR(1));
		if (name == -1) {
			fprintf(stderr, "unknown name %s", STR(1));
			exit(1);
		}
		errno = 0;
		switch (scall->sd_action) {
		case ACTION_PATHCONF:
			lrval = pathconf(STR(0), name);
			break;
		case ACTION_FPATHCONF:
			lrval = fpathconf(NUM(0), name);
			break;
		case ACTION_LPATHCONF:
			lrval = lpathconf(STR(0), name);
			break;
		default:
			abort();
		}
		if (lrval == -1 && errno == 0) {
			printf("unlimited\n");
			return (i);
		} else if (lrval >= 0) {
			printf("%ld\n", lrval);
			return (i);
		}
		rval = -1;
		break;
	    }
#ifdef HAS_FREEBSD_ACL
	case ACTION_PREPENDACL:
		rval = -1;

		acl = acl_get_file(STR(0), ACL_TYPE_NFS4);
		if (acl == NULL)
			break;

		newacl = acl_from_text(STR(1));
		if (acl == NULL)
			break;

		while (acl_get_entry(newacl, entry_id, &newentry) == 1) {
			entry_id = ACL_NEXT_ENTRY;

			if (acl_create_entry_np(&acl, &entry, 0))
				break;

			if (acl_copy_entry(entry, newentry))
				break;
		}

		rval = acl_set_file(STR(0), ACL_TYPE_NFS4, acl);
		break;
	case ACTION_READACL:
		acl = acl_get_file(STR(0), ACL_TYPE_NFS4);
		if (acl == NULL)
			rval = -1;
		else
			rval = 0;
		break;
#endif
	case ACTION_WRITE:
		rval = write(NUM(0), STR(1), strlen(STR(1)));
		break;
	default:
		fprintf(stderr, "unsupported syscall\n");
		exit(1);
	}
#undef STR
#undef NUM
	if (rval < 0) {
		const char *serrno;

		serrno = err2str(errno);
		fprintf(stderr, "%s returned %d\n", scall->sd_name, rval);
		printf("%s\n", serrno);
		exit(1);
	}
	printf("0\n");
	return (i);
}
Example #9
0
static int
parse_acl_entry(const char **text_p, acl_t *acl_p)
{
	acl_entry_obj entry_obj;
	acl_entry_t entry_d;
	char *str;
	const char *backup;
	int error, perm_chars;

	new_obj_p_here(acl_entry, &entry_obj);
	init_acl_entry_obj(entry_obj);

	/* parse acl entry type */
	SKIP_WS(*text_p);
	switch (**text_p) {
		case 'u':  /* user */
			if (!skip_tag_name(text_p, "user"))
				goto fail;
			backup = *text_p;
			str = get_token(text_p);
			if (str) {
				entry_obj.etag = ACL_USER;
				error = get_uid(unquote(str),
						&entry_obj.eid.qid);
				free(str);
				if (error) {
					*text_p = backup;
					return -1;
				}
			} else {
				entry_obj.etag = ACL_USER_OBJ;
			}
			break;

		case 'g':  /* group */
			if (!skip_tag_name(text_p, "group"))
				goto fail;
			backup = *text_p;
			str = get_token(text_p);
			if (str) {
				entry_obj.etag = ACL_GROUP;
				error = get_gid(unquote(str),
						&entry_obj.eid.qid);
				free(str);
				if (error) {
					*text_p = backup;
					return -1;
				}
			} else {
				entry_obj.etag = ACL_GROUP_OBJ;
			}
			break;

		case 'm':  /* mask */
			if (!skip_tag_name(text_p, "mask"))
				goto fail;
			/* skip empty entry qualifier field (this field may
			   be missing for compatibility with Solaris.) */
			SKIP_WS(*text_p);
			if (**text_p == ':')
				(*text_p)++;
			entry_obj.etag = ACL_MASK;
			break;

		case 'o':  /* other */
			if (!skip_tag_name(text_p, "other"))
				goto fail;
			/* skip empty entry qualifier field (this field may
			   be missing for compatibility with Solaris.) */
			SKIP_WS(*text_p);
			if (**text_p == ':')
				(*text_p)++;
			entry_obj.etag = ACL_OTHER;
			break;

		default:
			goto fail;
	}

	for (perm_chars=0; perm_chars<3; perm_chars++, (*text_p)++) {
		switch(**text_p) {
			case 'r':
				if (entry_obj.eperm.sperm & ACL_READ)
					goto fail;
				entry_obj.eperm.sperm |= ACL_READ;
				break;

			case 'w':
				if (entry_obj.eperm.sperm  & ACL_WRITE)
					goto fail;
				entry_obj.eperm.sperm  |= ACL_WRITE;
				break;

			case 'x':
				if (entry_obj.eperm.sperm  & ACL_EXECUTE)
					goto fail;
				entry_obj.eperm.sperm  |= ACL_EXECUTE;
				break;

			case '-':
				/* ignore */
				break;

			default:
				if (perm_chars == 0)
					goto fail;
				goto create_entry;
		}
	}

create_entry:
	if (acl_create_entry(acl_p, &entry_d) != 0)
		return -1;
	if (acl_copy_entry(entry_d, int2ext(&entry_obj)) != 0)
		return -1;
	return 0;

fail:
	errno = EINVAL;
	return -1;
}
Example #10
0
static int CheckPosixLinuxACEs(EvalContext *ctx, Rlist *aces, AclMethod method, const char *file_path, acl_type_t acl_type, Attributes a,
                               const Promise *pp, PromiseResult *result)
{
    acl_t acl_existing;
    acl_t acl_new;
    acl_t acl_tmp;
    acl_entry_t ace_parsed;
    acl_entry_t ace_current;
    acl_permset_t perms;
    char *cf_ace;
    int retv;
    int has_mask;
    Rlist *rp;
    char *acl_type_str;

    acl_new = NULL;
    acl_existing = NULL;
    acl_tmp = NULL;
    has_mask = false;

    acl_type_str = acl_type == ACL_TYPE_ACCESS ? "Access" : "Default";

// read existing acl

    if ((acl_existing = acl_get_file(file_path, acl_type)) == NULL)
    {
        Log(LOG_LEVEL_VERBOSE, "No ACL for '%s' could be read. (acl_get_file: %s)", file_path, GetErrorStr());
        return false;
    }

// allocate memory for temp ace (it needs to reside in a temp acl)

    if ((acl_tmp = acl_init(1)) == NULL)
    {
        Log(LOG_LEVEL_ERR, "New ACL could not be allocated (acl_init: %s)", GetErrorStr());
        acl_free((void *) acl_existing);
        return false;
    }

    if (acl_create_entry(&acl_tmp, &ace_parsed) != 0)
    {
        Log(LOG_LEVEL_ERR, "New ACL could not be allocated (acl_create_entry: %s)", GetErrorStr());
        acl_free((void *) acl_existing);
        acl_free((void *) acl_tmp);
        return false;
    }

// copy existing aces if we are appending

    if (method == ACL_METHOD_APPEND)
    {
        if ((acl_new = acl_dup(acl_existing)) == NULL)
        {
            Log(LOG_LEVEL_ERR, "Error copying existing ACL (acl_dup: %s)", GetErrorStr());
            acl_free((void *) acl_existing);
            acl_free((void *) acl_tmp);
            return false;
        }
    }
    else                        // overwrite existing acl
    {
        if ((acl_new = acl_init(5)) == NULL)    // TODO: Always OK with 5 here ?
        {
            Log(LOG_LEVEL_ERR, "New ACL could not be allocated (acl_init: %s)", GetErrorStr());
            acl_free((void *) acl_existing);
            acl_free((void *) acl_tmp);
            return false;
        }
    }

    for (rp = aces; rp != NULL; rp = rp->next)
    {
        cf_ace = RlistScalarValue(rp);

        if (!ParseEntityPosixLinux(&cf_ace, ace_parsed, &has_mask))
        {
            Log(LOG_LEVEL_ERR, "Error parsing entity in 'cf_ace'.");
            acl_free((void *) acl_existing);
            acl_free((void *) acl_tmp);
            acl_free((void *) acl_new);
            return false;
        }

        // check if an ACE with this entity-type and id already exist in the Posix Linux ACL

        ace_current = FindACE(acl_new, ace_parsed);

        // create new entry in ACL if it did not exist

        if (ace_current == NULL)
        {
            if (acl_create_entry(&acl_new, &ace_current) != 0)
            {
                Log(LOG_LEVEL_ERR, "Failed to allocate ace (acl_create_entry: %s)", GetErrorStr());
                acl_free((void *) acl_existing);
                acl_free((void *) acl_tmp);
                acl_free((void *) acl_new);
                return false;
            }

            // copy parsed entity-type and id

            if (acl_copy_entry(ace_current, ace_parsed) != 0)
            {
                Log(LOG_LEVEL_ERR, "Error copying Linux entry in 'cf_ace' (acl_copy_entry: %s)", GetErrorStr());
                acl_free((void *) acl_existing);
                acl_free((void *) acl_tmp);
                acl_free((void *) acl_new);
                return false;
            }

            // clear ace_current's permissions to avoid ace_parsed from last
            // loop iteration to be taken into account when applying mode below
            if ((acl_get_permset(ace_current, &perms) != 0))
            {
                Log(LOG_LEVEL_ERR, "Error obtaining permset for 'ace_current' (acl_get_permset: %s)", GetErrorStr());
                acl_free((void *) acl_existing);
                acl_free((void *) acl_tmp);
                acl_free((void *) acl_new);
                return false;
            }

            if (acl_clear_perms(perms) != 0)
            {
                Log(LOG_LEVEL_ERR, "Error clearing permset for 'ace_current'. (acl_clear_perms: %s)", GetErrorStr());
                acl_free((void *) acl_existing);
                acl_free((void *) acl_tmp);
                acl_free((void *) acl_new);
                return false;
            }
        }

        // mode string should be prefixed with an entry seperator

        if (*cf_ace != ':')
        {
            Log(LOG_LEVEL_ERR, "No separator before mode-string in 'cf_ace'");
            acl_free((void *) acl_existing);
            acl_free((void *) acl_tmp);
            acl_free((void *) acl_new);
            return false;
        }

        cf_ace += 1;

        if (acl_get_permset(ace_current, &perms) != 0)
        {
            Log(LOG_LEVEL_ERR, "Error obtaining permset for 'cf_ace'");
            acl_free((void *) acl_existing);
            acl_free((void *) acl_tmp);
            acl_free((void *) acl_new);
            return false;
        }

        if (!ParseModePosixLinux(cf_ace, perms))
        {
            Log(LOG_LEVEL_ERR, "Error parsing mode-string in 'cf_ace'");
            acl_free((void *) acl_existing);
            acl_free((void *) acl_tmp);
            acl_free((void *) acl_new);
            return false;
        }

        // only allow permissions exist on posix acls, so we do
        // not check what follows next
    }

// if no mask exists, calculate one (or both?): run acl_calc_mask and add one
    if (!has_mask)
    {
        if (acl_calc_mask(&acl_new) != 0)
        {
            Log(LOG_LEVEL_ERR, "Error calculating new acl mask");
            acl_free((void *) acl_existing);
            acl_free((void *) acl_tmp);
            acl_free((void *) acl_new);
            return false;
        }
    }

    if ((retv = ACLEquals(acl_existing, acl_new)) == -1)
    {
        Log(LOG_LEVEL_ERR, "Error while comparing existing and new ACL, unable to repair.");
        acl_free((void *) acl_existing);
        acl_free((void *) acl_tmp);
        acl_free((void *) acl_new);
        return false;
    }

    if (retv == 1)              // existing and new acl differ, update existing
    {

        switch (a.transaction.action)
        {
        case cfa_warn:

            cfPS(ctx, LOG_LEVEL_ERR, PROMISE_RESULT_WARN, pp, a, "%s ACL on file '%s' needs to be updated", acl_type_str, file_path);
            *result = PromiseResultUpdate(*result, PROMISE_RESULT_WARN);
            break;

        case cfa_fix:

            if (!DONTDO)
            {
                if ((retv = acl_set_file(file_path, acl_type, acl_new)) != 0)
                {
                    Log(LOG_LEVEL_ERR, "Error setting new %s ACL on file '%s' (acl_set_file: %s), are required ACEs present ?",
                          acl_type_str, file_path, GetErrorStr());
                    acl_free((void *) acl_existing);
                    acl_free((void *) acl_tmp);
                    acl_free((void *) acl_new);
                    return false;
                }
            }

            cfPS(ctx, LOG_LEVEL_INFO, PROMISE_RESULT_CHANGE, pp, a, "%s ACL on '%s' successfully changed.", acl_type_str, file_path);
            *result = PromiseResultUpdate(*result, PROMISE_RESULT_CHANGE);

            break;

        default:
            ProgrammingError("CFEngine: internal error: illegal file action");
        }

    }
    else
    {
        cfPS(ctx, LOG_LEVEL_INFO, PROMISE_RESULT_NOOP, pp, a, "'%s' ACL on '%s' needs no modification.", acl_type_str, file_path);
    }

    acl_free((void *) acl_existing);
    acl_free((void *) acl_new);
    acl_free((void *) acl_tmp);
    return true;
}
Example #11
0
/* merge 2 acl's together */
static int
merge_acl(acl_t acl, acl_t *prev_acl, const char *path)
{
	acl_t acl_new;
	acl_permset_t permset;
	acl_flagset_t flagset;
	acl_tag_t tag, tag_new;
	acl_entry_t entry, entry_new;
	acl_entry_type_t entry_type, entry_type_new;
	int entry_id, entry_id_new, have_entry, had_entry, entry_number = 0;

	if ((acl_new = acl_dup(*prev_acl)) == NULL)
		err(EX_OSERR, "%s: acl_dup() failed", path);

	entry_id = ACL_FIRST_ENTRY;
	while (acl_get_entry(acl, entry_id, &entry) == 1) {
		entry_id = ACL_NEXT_ENTRY;
		have_entry = had_entry = 0;

		entry_id_new = ACL_FIRST_ENTRY;
		while (acl_get_entry(acl_new, entry_id_new, &entry_new) > 0) {
			entry_id_new = ACL_NEXT_ENTRY;

			if (acl_get_tag_type(entry, &tag) < 0)
				err(EX_OSERR, "%s: acl_get_tag_type() failed", path);
			if (acl_get_tag_type(entry_new, &tag_new) < 0)
				err(EX_OSERR, "%s: acl_get_tag_type() failed", path);
			if (tag != tag_new)
				continue;

			if (acl_get_entry_type_np(entry, &entry_type) < 0)
				err(EX_OSERR, "%s: acl_get_entry_type_np() failed", path);
			if (acl_get_entry_type_np(entry_new, &entry_type_new) < 0)
				err(EX_OSERR, "%s: acl_get_entry_type_np() failed", path);
			if (entry_type != entry_type_new)
				continue;
		
			switch(tag) {
				case ACL_USER:
				case ACL_GROUP:
					have_entry = merge_user_group(&entry, &entry_new);
					if (have_entry == 0)
						break;

				case ACL_USER_OBJ:
				case ACL_GROUP_OBJ:
				case ACL_EVERYONE:
					merge_acl_entries(&entry, &entry_new);
					had_entry = have_entry = 1;
					break;

				default:
					errx(EX_OSERR, "%s: invalid tag type: %i", path, tag);
					break;
			}
		}

		if (had_entry == 0) {
			if (acl_create_entry_np(&acl_new, &entry_new, entry_number) < 0) {
				warn("%s: acl_create_entry_np() failed", path); 
				acl_free(acl_new);
				return (-1);
			}

			entry_number++;
			if (acl_copy_entry(entry_new, entry) < 0)
				err(EX_OSERR, "%s: acl_copy_entry() failed", path);
		}
	}

	acl_free(*prev_acl);
	*prev_acl = acl_new;

	return (0);
}
Example #12
0
int add_base_acls_if_needed(acl_t *acl_p, const char *path) {
        acl_entry_t i;
        int r;
        bool have_user_obj = false, have_group_obj = false, have_other = false;
        struct stat st;
        _cleanup_(acl_freep) acl_t basic = NULL;

        assert(acl_p);

        for (r = acl_get_entry(*acl_p, ACL_FIRST_ENTRY, &i);
             r > 0;
             r = acl_get_entry(*acl_p, ACL_NEXT_ENTRY, &i)) {
                acl_tag_t tag;

                if (acl_get_tag_type(i, &tag) < 0)
                        return -errno;

                if (tag == ACL_USER_OBJ)
                        have_user_obj = true;
                else if (tag == ACL_GROUP_OBJ)
                        have_group_obj = true;
                else if (tag == ACL_OTHER)
                        have_other = true;
                if (have_user_obj && have_group_obj && have_other)
                        return 0;
        }
        if (r < 0)
                return -errno;

        r = stat(path, &st);
        if (r < 0)
                return -errno;

        basic = acl_from_mode(st.st_mode);
        if (!basic)
                return -errno;

        for (r = acl_get_entry(basic, ACL_FIRST_ENTRY, &i);
             r > 0;
             r = acl_get_entry(basic, ACL_NEXT_ENTRY, &i)) {
                acl_tag_t tag;
                acl_entry_t dst;

                if (acl_get_tag_type(i, &tag) < 0)
                        return -errno;

                if ((tag == ACL_USER_OBJ && have_user_obj) ||
                    (tag == ACL_GROUP_OBJ && have_group_obj) ||
                    (tag == ACL_OTHER && have_other))
                        continue;

                r = acl_create_entry(acl_p, &dst);
                if (r < 0)
                        return -errno;

                r = acl_copy_entry(dst, i);
                if (r < 0)
                        return -errno;
        }
        if (r < 0)
                return -errno;
        return 0;
}
Example #13
0
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;
}
Example #14
0
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;
}