static bool solaris_acl_get_fd(int fd, SOLARIS_ACL_T *solaris_acl, int *count)
{
	bool ret = False;

	DEBUG(10, ("entering solaris_acl_get_fd\n"));

	/* 
	 * see solaris_acl_get_file for comment about omission 
	 * of INITIAL_ACL_SIZE... 
	 */
	*count = facl(fd, GETACLCNT, 0, NULL);
	if (*count < 0) {
		DEBUG(10, ("facl GETACLCNT failed: %s\n", strerror(errno)));
		goto done;
	}
	*solaris_acl = solaris_acl_init(*count);
	if (*solaris_acl == NULL) {
		DEBUG(10, ("error allocating memory for solaris acl...\n"));
		goto done;
	}
	*count = facl(fd, GETACL, *count, *solaris_acl);
	if (*count < 0) {
		DEBUG(10, ("facl GETACL failed: %s\n", strerror(errno)));
		goto done;
	}
	ret = True;

 done:
	DEBUG(10, ("solaris_acl_get_fd %s\n",
		   ((ret == True) ? "succeeded" : "failed")));
	return ret;
}
/*
 * set the access ACL on the file referred to by a fd 
 */
int solarisacl_sys_acl_set_fd(vfs_handle_struct *handle,
			      files_struct *fsp,
			      SMB_ACL_T theacl)
{
	SOLARIS_ACL_T solaris_acl = NULL;
	SOLARIS_ACL_T default_acl = NULL;
	int count, default_count;
	int ret = -1;

	DEBUG(10, ("entering solarisacl_sys_acl_set_fd\n"));

	/* 
	 * the posix acl_set_fd call sets the access acl of the
	 * file referred to by fd. the solaris facl-SETACL call
	 * sets the access and default acl as provided, so we
	 * have to retrieve the default acl of the file and 
	 * concatenate it with the access acl provided.
	 */
	if (!smb_acl_to_solaris_acl(theacl, &solaris_acl, &count, 
				    SMB_ACL_TYPE_ACCESS))
	{
		DEBUG(10, ("conversion smb_acl -> solaris_acl failed (%s).\n",
			   strerror(errno)));
		goto done;
	}
	if (!solaris_acl_get_fd(fsp->fh->fd, &default_acl, &default_count)) {
		DEBUG(10, ("error getting (default) acl from fd\n"));
		goto done;
	}
	if (!solaris_add_to_acl(&solaris_acl, &count,
				default_acl, default_count,
				SMB_ACL_TYPE_DEFAULT))
	{
		DEBUG(10, ("error adding default acl to solaris acl\n"));
		goto done;
	}
	if (!solaris_acl_sort(solaris_acl, count)) {
		DEBUG(10, ("resulting acl is not valid!\n"));
		goto done;
	}

	ret = facl(fsp->fh->fd, SETACL, count, solaris_acl);
	if (ret != 0) {
		DEBUG(10, ("call of facl failed (%s).\n", strerror(errno)));
	}

 done:
	DEBUG(10, ("solarisacl_sys_acl_set_fd %s.\n",
		   ((ret == 0) ? "succeeded" : "failed" )));
	SAFE_FREE(solaris_acl);
	SAFE_FREE(default_acl);
	return ret;
}
Beispiel #3
0
/*
 * Set an ACL, translates acl to ace_t when appropriate.
 */
static int
cacl_set(acl_inp *acl_inp, acl_t *aclp, int type)
{
	int error = 0;
	int acl_flavor_target;
	struct stat64 statbuf;
	int stat_error;
	int isdir;


	if (type == ACL_PATH) {
		stat_error = stat64(acl_inp->file, &statbuf);
		if (stat_error)
			return (-1);
		acl_flavor_target = pathconf(acl_inp->file, _PC_ACL_ENABLED);
	} else {
		stat_error = fstat64(acl_inp->fd, &statbuf);
		if (stat_error)
			return (-1);
		acl_flavor_target = fpathconf(acl_inp->fd, _PC_ACL_ENABLED);
	}

	/*
	 * If target returns an error or 0 from pathconf call then
	 * fall back to UFS/POSIX Draft interface.
	 * In the case of 0 we will then fail in either acl(2) or
	 * acl_translate().  We could erroneously get 0 back from
	 * a file system that is using fs_pathconf() and not answering
	 * the _PC_ACL_ENABLED question itself.
	 */
	if (acl_flavor_target == 0 || acl_flavor_target == -1)
		acl_flavor_target = _ACL_ACLENT_ENABLED;

	isdir = S_ISDIR(statbuf.st_mode);

	if ((error = acl_translate(aclp, acl_flavor_target, isdir,
	    statbuf.st_uid, statbuf.st_gid)) != 0) {
		return (error);
	}

	if (type == ACL_PATH) {
		error = acl(acl_inp->file,
		    (aclp->acl_type == ACE_T) ? ACE_SETACL : SETACL,
		    aclp->acl_cnt, aclp->acl_aclp);
	} else {
		error = facl(acl_inp->fd,
		    (aclp->acl_type == ACE_T) ? ACE_SETACL : SETACL,
		    aclp->acl_cnt, aclp->acl_aclp);
	}

	return (error);
}
int
qset_acl (char const *name, int desc, mode_t mode)
{
#if USE_ACL
# if HAVE_ACL_GET_FILE
  /* POSIX 1003.1e draft 17 (abandoned) specific version.  */
  /* Linux, FreeBSD, MacOS X, IRIX, Tru64 */
#  if !HAVE_ACL_TYPE_EXTENDED
  /* Linux, FreeBSD, IRIX, Tru64 */

  /* We must also have acl_from_text and acl_delete_def_file.
     (acl_delete_def_file could be emulated with acl_init followed
      by acl_set_file, but acl_set_file with an empty acl is
      unspecified.)  */

#   ifndef HAVE_ACL_FROM_TEXT
#    error Must have acl_from_text (see POSIX 1003.1e draft 17).
#   endif
#   ifndef HAVE_ACL_DELETE_DEF_FILE
#    error Must have acl_delete_def_file (see POSIX 1003.1e draft 17).
#   endif

  acl_t acl;
  int ret;

  if (HAVE_ACL_FROM_MODE) /* Linux */
    {
      acl = acl_from_mode (mode);
      if (!acl)
        return -1;
    }
  else /* FreeBSD, IRIX, Tru64 */
    {
      /* If we were to create the ACL using the functions acl_init(),
         acl_create_entry(), acl_set_tag_type(), acl_set_qualifier(),
         acl_get_permset(), acl_clear_perm[s](), acl_add_perm(), we
         would need to create a qualifier.  I don't know how to do this.
         So create it using acl_from_text().  */

#   if HAVE_ACL_FREE_TEXT /* Tru64 */
      char acl_text[] = "u::---,g::---,o::---,";
#   else /* FreeBSD, IRIX */
      char acl_text[] = "u::---,g::---,o::---";
#   endif

      if (mode & S_IRUSR) acl_text[ 3] = 'r';
      if (mode & S_IWUSR) acl_text[ 4] = 'w';
      if (mode & S_IXUSR) acl_text[ 5] = 'x';
      if (mode & S_IRGRP) acl_text[10] = 'r';
      if (mode & S_IWGRP) acl_text[11] = 'w';
      if (mode & S_IXGRP) acl_text[12] = 'x';
      if (mode & S_IROTH) acl_text[17] = 'r';
      if (mode & S_IWOTH) acl_text[18] = 'w';
      if (mode & S_IXOTH) acl_text[19] = 'x';

      acl = acl_from_text (acl_text);
      if (!acl)
        return -1;
    }
  if (HAVE_ACL_SET_FD && desc != -1)
    ret = acl_set_fd (desc, acl);
  else
    ret = acl_set_file (name, ACL_TYPE_ACCESS, acl);
  if (ret != 0)
    {
      int saved_errno = errno;
      acl_free (acl);

      if (ACL_NOT_WELL_SUPPORTED (errno))
        return chmod_or_fchmod (name, desc, mode);
      else
        {
          errno = saved_errno;
          return -1;
        }
    }
  else
    acl_free (acl);

  if (S_ISDIR (mode) && acl_delete_def_file (name))
    return -1;

  if (!MODE_INSIDE_ACL || (mode & (S_ISUID | S_ISGID | S_ISVTX)))
    {
      /* We did not call chmod so far, and either the mode and the ACL are
         separate or special bits are to be set which don't fit into ACLs.  */
      return chmod_or_fchmod (name, desc, mode);
    }
  return 0;

#  else /* HAVE_ACL_TYPE_EXTENDED */
  /* MacOS X */

  /* On MacOS X,  acl_get_file (name, ACL_TYPE_ACCESS)
     and          acl_get_file (name, ACL_TYPE_DEFAULT)
     always return NULL / EINVAL.  You have to use
                  acl_get_file (name, ACL_TYPE_EXTENDED)
     or           acl_get_fd (open (name, ...))
     to retrieve an ACL.
     On the other hand,
                  acl_set_file (name, ACL_TYPE_ACCESS, acl)
     and          acl_set_file (name, ACL_TYPE_DEFAULT, acl)
     have the same effect as
                  acl_set_file (name, ACL_TYPE_EXTENDED, acl):
     Each of these calls sets the file's ACL.  */

  acl_t acl;
  int ret;

  /* Remove the ACL if the file has ACLs.  */
  if (HAVE_ACL_GET_FD && desc != -1)
    acl = acl_get_fd (desc);
  else
    acl = acl_get_file (name, ACL_TYPE_EXTENDED);
  if (acl)
    {
      acl_free (acl);

      acl = acl_init (0);
      if (acl)
        {
          if (HAVE_ACL_SET_FD && desc != -1)
            ret = acl_set_fd (desc, acl);
          else
            ret = acl_set_file (name, ACL_TYPE_EXTENDED, acl);
          if (ret != 0)
            {
              int saved_errno = errno;

              acl_free (acl);

              if (ACL_NOT_WELL_SUPPORTED (saved_errno))
                return chmod_or_fchmod (name, desc, mode);
              else
                {
                  errno = saved_errno;
                  return -1;
                }
            }
          acl_free (acl);
        }
    }

  /* Since !MODE_INSIDE_ACL, we have to call chmod explicitly.  */
  return chmod_or_fchmod (name, desc, mode);
#  endif

# elif HAVE_FACL && defined GETACLCNT /* Solaris, Cygwin, not HP-UX */

  int done_setacl = 0;

#  ifdef ACE_GETACL
  /* Solaris also has a different variant of ACLs, used in ZFS and NFSv4
     file systems (whereas the other ones are used in UFS file systems).  */

  /* The flags in the ace_t structure changed in a binary incompatible way
     when ACL_NO_TRIVIAL etc. were introduced in <sys/acl.h> version 1.15.
     How to distinguish the two conventions at runtime?
     We fetch the existing ACL.  In the old convention, usually three ACEs have
     a_flags = ACE_OWNER / ACE_GROUP / ACE_OTHER, in the range 0x0100..0x0400.
     In the new convention, these values are not used.  */
  int convention;

  {
    int count;
    ace_t *entries;

    for (;;)
      {
        if (desc != -1)
          count = facl (desc, ACE_GETACLCNT, 0, NULL);
        else
          count = acl (name, ACE_GETACLCNT, 0, NULL);
        if (count <= 0)
          {
            convention = -1;
            break;
          }
        entries = (ace_t *) malloc (count * sizeof (ace_t));
        if (entries == NULL)
          {
            errno = ENOMEM;
            return -1;
          }
        if ((desc != -1
             ? facl (desc, ACE_GETACL, count, entries)
             : acl (name, ACE_GETACL, count, entries))
            == count)
          {
            int i;

            convention = 0;
            for (i = 0; i < count; i++)
              if (entries[i].a_flags & (OLD_ACE_OWNER | OLD_ACE_GROUP | OLD_ACE_OTHER))
                {
                  convention = 1;
                  break;
                }
            free (entries);
            break;
          }
        /* Huh? The number of ACL entries changed since the last call.
           Repeat.  */
        free (entries);
      }
  }

  if (convention >= 0)
    {
      ace_t entries[6];
      int count;
      int ret;

      if (convention)
        {
          /* Running on Solaris 10.  */
          entries[0].a_type = OLD_ALLOW;
          entries[0].a_flags = OLD_ACE_OWNER;
          entries[0].a_who = 0; /* irrelevant */
          entries[0].a_access_mask = (mode >> 6) & 7;
          entries[1].a_type = OLD_ALLOW;
          entries[1].a_flags = OLD_ACE_GROUP;
          entries[1].a_who = 0; /* irrelevant */
          entries[1].a_access_mask = (mode >> 3) & 7;
          entries[2].a_type = OLD_ALLOW;
          entries[2].a_flags = OLD_ACE_OTHER;
          entries[2].a_who = 0;
          entries[2].a_access_mask = mode & 7;
          count = 3;
        }
      else
        {
Beispiel #5
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
};
Beispiel #6
0
int
qcopy_acl (const char *src_name, int source_desc, const char *dst_name,
           int dest_desc, mode_t mode)
{
#if USE_ACL && HAVE_ACL_GET_FILE
  /* POSIX 1003.1e (draft 17 -- abandoned) specific version.  */
  /* Linux, FreeBSD, Mac OS X, IRIX, Tru64 */
# if !HAVE_ACL_TYPE_EXTENDED
  /* Linux, FreeBSD, IRIX, Tru64 */

  acl_t acl;
  int ret;

  if (HAVE_ACL_GET_FD && source_desc != -1)
    acl = acl_get_fd (source_desc);
  else
    acl = acl_get_file (src_name, ACL_TYPE_ACCESS);
  if (acl == NULL)
    {
      if (! acl_errno_valid (errno))
        return qset_acl (dst_name, dest_desc, mode);
      else
        return -2;
    }

  if (HAVE_ACL_SET_FD && dest_desc != -1)
    ret = acl_set_fd (dest_desc, acl);
  else
    ret = acl_set_file (dst_name, ACL_TYPE_ACCESS, acl);
  if (ret != 0)
    {
      int saved_errno = errno;

      if (! acl_errno_valid (errno) && !acl_access_nontrivial (acl))
        {
          acl_free (acl);
          return chmod_or_fchmod (dst_name, dest_desc, mode);
        }
      else
        {
          acl_free (acl);
          chmod_or_fchmod (dst_name, dest_desc, mode);
          errno = saved_errno;
          return -1;
        }
    }
  else
    acl_free (acl);

  if (!MODE_INSIDE_ACL || (mode & (S_ISUID | S_ISGID | S_ISVTX)))
    {
      /* We did not call chmod so far, and either the mode and the ACL are
         separate or special bits are to be set which don't fit into ACLs.  */

      if (chmod_or_fchmod (dst_name, dest_desc, mode) != 0)
        return -1;
    }

  if (S_ISDIR (mode))
    {
      acl = acl_get_file (src_name, ACL_TYPE_DEFAULT);
      if (acl == NULL)
        return -2;

      if (acl_set_file (dst_name, ACL_TYPE_DEFAULT, acl))
        {
          int saved_errno = errno;

          acl_free (acl);
          errno = saved_errno;
          return -1;
        }
      else
        acl_free (acl);
    }
  return 0;

# else /* HAVE_ACL_TYPE_EXTENDED */
  /* Mac OS X */

  /* On Mac OS X,  acl_get_file (name, ACL_TYPE_ACCESS)
     and           acl_get_file (name, ACL_TYPE_DEFAULT)
     always return NULL / EINVAL.  You have to use
                   acl_get_file (name, ACL_TYPE_EXTENDED)
     or            acl_get_fd (open (name, ...))
     to retrieve an ACL.
     On the other hand,
                   acl_set_file (name, ACL_TYPE_ACCESS, acl)
     and           acl_set_file (name, ACL_TYPE_DEFAULT, acl)
     have the same effect as
                   acl_set_file (name, ACL_TYPE_EXTENDED, acl):
     Each of these calls sets the file's ACL.  */

  acl_t acl;
  int ret;

  if (HAVE_ACL_GET_FD && source_desc != -1)
    acl = acl_get_fd (source_desc);
  else
    acl = acl_get_file (src_name, ACL_TYPE_EXTENDED);
  if (acl == NULL)
    {
      if (!acl_errno_valid (errno))
        return qset_acl (dst_name, dest_desc, mode);
      else
        return -2;
    }

  if (HAVE_ACL_SET_FD && dest_desc != -1)
    ret = acl_set_fd (dest_desc, acl);
  else
    ret = acl_set_file (dst_name, ACL_TYPE_EXTENDED, acl);
  if (ret != 0)
    {
      int saved_errno = errno;

      if (!acl_errno_valid (saved_errno) && !acl_extended_nontrivial (acl))
        {
          acl_free (acl);
          return chmod_or_fchmod (dst_name, dest_desc, mode);
        }
      else
        {
          acl_free (acl);
          chmod_or_fchmod (dst_name, dest_desc, mode);
          errno = saved_errno;
          return -1;
        }
    }
  else
    acl_free (acl);

  /* Since !MODE_INSIDE_ACL, we have to call chmod explicitly.  */
  return chmod_or_fchmod (dst_name, dest_desc, mode);

# endif

#elif USE_ACL && defined GETACL /* Solaris, Cygwin, not HP-UX */

  /* Solaris 2.5 through Solaris 10, Cygwin, and contemporaneous versions
     of Unixware.  The acl() call returns the access and default ACL both
     at once.  */
# ifdef ACE_GETACL
  int ace_count;
  ace_t *ace_entries;
# endif
  int count;
  aclent_t *entries;
  int did_chmod;
  int saved_errno;
  int ret;

# ifdef ACE_GETACL
  /* Solaris also has a different variant of ACLs, used in ZFS and NFSv4
     file systems (whereas the other ones are used in UFS file systems).
     There is an API
       pathconf (name, _PC_ACL_ENABLED)
       fpathconf (desc, _PC_ACL_ENABLED)
     that allows to determine which of the two kinds of ACLs is supported
     for the given file.  But some file systems may implement this call
     incorrectly, so better not use it.
     When fetching the source ACL, we simply fetch both ACL types.
     When setting the destination ACL, we try either ACL types, assuming
     that the kernel will translate the ACL from one form to the other.
     (See in <http://docs.sun.com/app/docs/doc/819-2241/6n4huc7ia?l=en&a=view>
     the description of ENOTSUP.)  */
  for (;;)
    {
      ace_count = (source_desc != -1
                   ? facl (source_desc, ACE_GETACLCNT, 0, NULL)
                   : acl (src_name, ACE_GETACLCNT, 0, NULL));

      if (ace_count < 0)
        {
          if (errno == ENOSYS || errno == EINVAL)
            {
              ace_count = 0;
              ace_entries = NULL;
              break;
            }
          else
            return -2;
        }

      if (ace_count == 0)
        {
          ace_entries = NULL;
          break;
        }

      ace_entries = (ace_t *) malloc (ace_count * sizeof (ace_t));
      if (ace_entries == NULL)
        {
          errno = ENOMEM;
          return -2;
        }

      ret = (source_desc != -1
             ? facl (source_desc, ACE_GETACL, ace_count, ace_entries)
             : acl (src_name, ACE_GETACL, ace_count, ace_entries));
      if (ret < 0)
        {
          free (ace_entries);
          if (errno == ENOSYS || errno == EINVAL)
            {
              ace_count = 0;
              ace_entries = NULL;
              break;
            }
          else
            return -2;
        }
      if (ret == ace_count)
        break;
      /* Huh? The number of ACL entries changed since the last call.
         Repeat.  */
    }
# endif

  for (;;)
    {
      count = (source_desc != -1
               ? facl (source_desc, GETACLCNT, 0, NULL)
               : acl (src_name, GETACLCNT, 0, NULL));

      if (count < 0)
        {
          if (errno == ENOSYS || errno == ENOTSUP || errno == EOPNOTSUPP)
            {
              count = 0;
              entries = NULL;
              break;
            }
          else
            return -2;
        }

      if (count == 0)
        {
          entries = NULL;
          break;
        }

      entries = (aclent_t *) malloc (count * sizeof (aclent_t));
      if (entries == NULL)
        {
          errno = ENOMEM;
          return -2;
        }

      if ((source_desc != -1
           ? facl (source_desc, GETACL, count, entries)
           : acl (src_name, GETACL, count, entries))
          == count)
        break;
      /* Huh? The number of ACL entries changed since the last call.
         Repeat.  */
    }

  /* Is there an ACL of either kind?  */
# ifdef ACE_GETACL
  if (ace_count == 0)
# endif
    if (count == 0)
      return qset_acl (dst_name, dest_desc, mode);

  did_chmod = 0; /* set to 1 once the mode bits in 0777 have been set */
  saved_errno = 0; /* the first non-ignorable error code */

  if (!MODE_INSIDE_ACL)
    {
      /* On Cygwin, it is necessary to call chmod before acl, because
         chmod can change the contents of the ACL (in ways that don't
         change the allowed accesses, but still visible).  */
      if (chmod_or_fchmod (dst_name, dest_desc, mode) != 0)
        saved_errno = errno;
      did_chmod = 1;
    }

  /* If both ace_entries and entries are available, try SETACL before
     ACE_SETACL, because SETACL cannot fail with ENOTSUP whereas ACE_SETACL
     can.  */

  if (count > 0)
    {
      ret = (dest_desc != -1
             ? facl (dest_desc, SETACL, count, entries)
             : acl (dst_name, SETACL, count, entries));
      if (ret < 0 && saved_errno == 0)
        {
          saved_errno = errno;
          if ((errno == ENOSYS || errno == EOPNOTSUPP || errno == EINVAL)
              && !acl_nontrivial (count, entries))
            saved_errno = 0;
        }
      else
        did_chmod = 1;
    }
  free (entries);

# ifdef ACE_GETACL
  if (ace_count > 0)
    {
      ret = (dest_desc != -1
             ? facl (dest_desc, ACE_SETACL, ace_count, ace_entries)
             : acl (dst_name, ACE_SETACL, ace_count, ace_entries));
      if (ret < 0 && saved_errno == 0)
        {
          saved_errno = errno;
          if ((errno == ENOSYS || errno == EINVAL || errno == ENOTSUP)
              && !acl_ace_nontrivial (ace_count, ace_entries))
            saved_errno = 0;
        }
    }
  free (ace_entries);
# endif

  if (MODE_INSIDE_ACL
      && did_chmod <= ((mode & (S_ISUID | S_ISGID | S_ISVTX)) ? 1 : 0))
    {
      /* We did not call chmod so far, and either the mode and the ACL are
         separate or special bits are to be set which don't fit into ACLs.  */

      if (chmod_or_fchmod (dst_name, dest_desc, mode) != 0)
        {
          if (saved_errno == 0)
            saved_errno = errno;
        }
    }

  if (saved_errno)
    {
      errno = saved_errno;
      return -1;
    }
  return 0;

#elif USE_ACL && HAVE_GETACL /* HP-UX */

  struct acl_entry entries[NACLENTRIES];
  int count;
# if HAVE_ACLV_H
  struct acl aclv_entries[NACLVENTRIES];
  int aclv_count;
# endif
  int did_chmod;
  int saved_errno;
  int ret;

  count = (source_desc != -1
           ? fgetacl (source_desc, NACLENTRIES, entries)
           : getacl (src_name, NACLENTRIES, entries));

  if (count < 0)
    {
      if (errno == ENOSYS || errno == EOPNOTSUPP || errno == ENOTSUP)
        count = 0;
      else
        return -2;
    }
  else if (count > 0)
    {
      if (count > NACLENTRIES)
        /* If NACLENTRIES cannot be trusted, use dynamic memory allocation.  */
        abort ();
    }

# if HAVE_ACLV_H
  aclv_count = acl ((char *) src_name, ACL_GET, NACLVENTRIES, aclv_entries);

  if (aclv_count < 0)
    {
      if (errno == ENOSYS || errno == EOPNOTSUPP || errno == EINVAL)
        count = 0;
      else
        return -2;
    }
  else if (aclv_count > 0)
    {
      if (aclv_count > NACLVENTRIES)
        /* If NACLVENTRIES cannot be trusted, use dynamic memory allocation.  */
        abort ();
    }
# endif

  if (count == 0)
# if HAVE_ACLV_H
    if (aclv_count == 0)
# endif
      return qset_acl (dst_name, dest_desc, mode);

  did_chmod = 0; /* set to 1 once the mode bits in 0777 have been set */
  saved_errno = 0; /* the first non-ignorable error code */

  if (count > 0)
    {
      ret = (dest_desc != -1
             ? fsetacl (dest_desc, count, entries)
             : setacl (dst_name, count, entries));
      if (ret < 0 && saved_errno == 0)
        {
          saved_errno = errno;
          if (errno == ENOSYS || errno == EOPNOTSUPP || errno == ENOTSUP)
            {
              struct stat source_statbuf;

              if ((source_desc != -1
                   ? fstat (source_desc, &source_statbuf)
                   : stat (src_name, &source_statbuf)) == 0)
                {
                  if (!acl_nontrivial (count, entries, &source_statbuf))
                    saved_errno = 0;
                }
              else
                saved_errno = errno;
            }
        }
      else
        did_chmod = 1;
    }

# if HAVE_ACLV_H
  if (aclv_count > 0)
    {
      ret = acl ((char *) dst_name, ACL_SET, aclv_count, aclv_entries);
      if (ret < 0 && saved_errno == 0)
        {
          saved_errno = errno;
          if (errno == ENOSYS || errno == EOPNOTSUPP || errno == EINVAL)
            {
              if (!aclv_nontrivial (aclv_count, aclv_entries))
                saved_errno = 0;
            }
        }
      else
        did_chmod = 1;
    }
# endif

  if (did_chmod <= ((mode & (S_ISUID | S_ISGID | S_ISVTX)) ? 1 : 0))
    {
      /* We did not call chmod so far, and special bits are to be set which
         don't fit into ACLs.  */

      if (chmod_or_fchmod (dst_name, dest_desc, mode) != 0)
        {
          if (saved_errno == 0)
            saved_errno = errno;
        }
    }

  if (saved_errno)
    {
      errno = saved_errno;
      return -1;
    }
  return 0;

#elif USE_ACL && HAVE_ACLX_GET && 0 /* AIX */

  /* TODO */

#elif USE_ACL && HAVE_STATACL /* older AIX */

  union { struct acl a; char room[4096]; } u;
  int ret;

  if ((source_desc != -1
       ? fstatacl (source_desc, STX_NORMAL, &u.a, sizeof (u))
       : statacl (src_name, STX_NORMAL, &u.a, sizeof (u)))
      < 0)
    return -2;

  ret = (dest_desc != -1
         ? fchacl (dest_desc, &u.a, u.a.acl_len)
         : chacl (dst_name, &u.a, u.a.acl_len));
  if (ret < 0)
    {
      int saved_errno = errno;

      chmod_or_fchmod (dst_name, dest_desc, mode);
      errno = saved_errno;
      return -1;
    }

  /* No need to call chmod_or_fchmod at this point, since the mode bits
     S_ISUID, S_ISGID, S_ISVTX are also stored in the ACL.  */

  return 0;

#elif USE_ACL && HAVE_ACLSORT /* NonStop Kernel */

  struct acl entries[NACLENTRIES];
  int count;
  int ret;

  count = acl ((char *) src_name, ACL_GET, NACLENTRIES, entries);

  if (count < 0)
    {
      if (0)
        count = 0;
      else
        return -2;
    }
  else if (count > 0)
    {
      if (count > NACLENTRIES)
        /* If NACLENTRIES cannot be trusted, use dynamic memory allocation.  */
        abort ();
    }

  if (count == 0)
    return qset_acl (dst_name, dest_desc, mode);

  ret = acl ((char *) dst_name, ACL_SET, count, entries);
  if (ret < 0)
    {
      int saved_errno = errno;

      if (0)
        {
          if (!acl_nontrivial (count, entries))
            return chmod_or_fchmod (dst_name, dest_desc, mode);
        }

      chmod_or_fchmod (dst_name, dest_desc, mode);
      errno = saved_errno;
      return -1;
    }

  if (mode & (S_ISUID | S_ISGID | S_ISVTX))
    {
      /* We did not call chmod so far, and either the mode and the ACL are
         separate or special bits are to be set which don't fit into ACLs.  */

      return chmod_or_fchmod (dst_name, dest_desc, mode);
    }
  return 0;

#else

  return qset_acl (dst_name, dest_desc, mode);

#endif
}
Beispiel #7
0
/* Set permissions of a file.  -*- coding: utf-8 -*-

   Copyright (C) 2002-2003, 2005-2017 Free Software Foundation, Inc.

   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 3 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program.  If not, see <https://www.gnu.org/licenses/>.

   Written by Paul Eggert, Andreas Grünbacher, and Bruno Haible.  */

#include <config.h>

#include "acl.h"

#include "acl-internal.h"

#if USE_ACL
# if ! defined HAVE_ACL_FROM_MODE && defined HAVE_ACL_FROM_TEXT /* FreeBSD, IRIX, Tru64 */
#  if HAVE_ACL_GET_FILE && !HAVE_ACL_TYPE_EXTENDED

static acl_t
acl_from_mode (mode_t mode)
{
#  if HAVE_ACL_FREE_TEXT /* Tru64 */
  char acl_text[] = "u::---,g::---,o::---,";
#  else /* FreeBSD, IRIX */
  char acl_text[] = "u::---,g::---,o::---";
#  endif

  if (mode & S_IRUSR) acl_text[ 3] = 'r';
  if (mode & S_IWUSR) acl_text[ 4] = 'w';
  if (mode & S_IXUSR) acl_text[ 5] = 'x';
  if (mode & S_IRGRP) acl_text[10] = 'r';
  if (mode & S_IWGRP) acl_text[11] = 'w';
  if (mode & S_IXGRP) acl_text[12] = 'x';
  if (mode & S_IROTH) acl_text[17] = 'r';
  if (mode & S_IWOTH) acl_text[18] = 'w';
  if (mode & S_IXOTH) acl_text[19] = 'x';

  return acl_from_text (acl_text);
}
#  endif
# endif

# if HAVE_FACL && defined GETACL /* Solaris, Cygwin, not HP-UX */
static int
set_acls_from_mode (const char *name, int desc, mode_t mode, bool *must_chmod)
{
#  ifdef ACE_GETACL
  /* Solaris also has a different variant of ACLs, used in ZFS and NFSv4
     file systems (whereas the other ones are used in UFS file systems).  */

  /* The flags in the ace_t structure changed in a binary incompatible way
     when ACL_NO_TRIVIAL etc. were introduced in <sys/acl.h> version 1.15.
     How to distinguish the two conventions at runtime?
     We fetch the existing ACL.  In the old convention, usually three ACEs have
     a_flags = ACE_OWNER / ACE_GROUP / ACE_OTHER, in the range 0x0100..0x0400.
     In the new convention, these values are not used.  */
  int convention;

  {
    /* Initially, try to read the entries into a stack-allocated buffer.
       Use malloc if it does not fit.  */
    enum
      {
        alloc_init = 4000 / sizeof (ace_t), /* >= 3 */
        alloc_max = MIN (INT_MAX, SIZE_MAX / sizeof (ace_t))
      };
    ace_t buf[alloc_init];
    size_t alloc = alloc_init;
    ace_t *entries = buf;
    ace_t *malloced = NULL;
    int count;

    for (;;)
      {
        count = (desc != -1
                 ? facl (desc, ACE_GETACL, alloc, entries)
                 : acl (name, ACE_GETACL, alloc, entries));
        if (count < 0 && errno == ENOSPC)
          {
            /* Increase the size of the buffer.  */
            free (malloced);
            if (alloc > alloc_max / 2)
              {
                errno = ENOMEM;
                return -1;
              }
            alloc = 2 * alloc; /* <= alloc_max */
            entries = malloced = (ace_t *) malloc (alloc * sizeof (ace_t));
            if (entries == NULL)
              {
                errno = ENOMEM;
                return -1;
              }
            continue;
          }
        break;
      }

    if (count <= 0)
      convention = -1;
    else
      {
        int i;

        convention = 0;
        for (i = 0; i < count; i++)
          if (entries[i].a_flags & (OLD_ACE_OWNER | OLD_ACE_GROUP | OLD_ACE_OTHER))
            {
              convention = 1;
              break;
            }
      }
    free (malloced);
  }

  if (convention >= 0)
    {
      ace_t entries[6];
      int count;
      int ret;

      if (convention)
        {
          /* Running on Solaris 10.  */
          entries[0].a_type = OLD_ALLOW;
          entries[0].a_flags = OLD_ACE_OWNER;
          entries[0].a_who = 0; /* irrelevant */
          entries[0].a_access_mask = (mode >> 6) & 7;
          entries[1].a_type = OLD_ALLOW;
          entries[1].a_flags = OLD_ACE_GROUP;
          entries[1].a_who = 0; /* irrelevant */
          entries[1].a_access_mask = (mode >> 3) & 7;
          entries[2].a_type = OLD_ALLOW;
          entries[2].a_flags = OLD_ACE_OTHER;
          entries[2].a_who = 0;
          entries[2].a_access_mask = mode & 7;
          count = 3;
        }
      else
        {
#include <errno.h>
#include <sys/acl.h>

#include "sun_nio_fs_SolarisNativeDispatcher.h"

static void throwUnixException(JNIEnv* env, int errnum) {
    jobject x = JNU_NewObjectByName(env, "sun/nio/fs/UnixException",
        "(I)V", errnum);
    if (x != NULL) {
        (*env)->Throw(env, x);
    }
}

JNIEXPORT void JNICALL
Java_sun_nio_fs_SolarisNativeDispatcher_init(JNIEnv *env, jclass clazz) {
}

JNIEXPORT jint JNICALL
Java_sun_nio_fs_SolarisNativeDispatcher_facl(JNIEnv* env, jclass this, jint fd,
    jint cmd, jint nentries, jlong address)
{
    void* aclbufp = jlong_to_ptr(address);
    int n = -1;

    n = facl((int)fd, (int)cmd, (int)nentries, aclbufp);
    if (n == -1) {
        throwUnixException(env, errno);
    }
    return (jint)n;
}
Beispiel #9
0
static int
cacl_get(acl_inp inp, int get_flag, int type, acl_t **aclp)
{
	const char *fname;
	int fd;
	int ace_acl = 0;
	int error;
	int getcmd, cntcmd;
	acl_t *acl_info;
	int	save_errno;
	int	stat_error;
	struct stat64 statbuf;

	*aclp = NULL;
	if (type == ACL_PATH) {
		fname = inp.file;
		ace_acl = pathconf(fname, _PC_ACL_ENABLED);
	} else {
		fd = inp.fd;
		ace_acl = fpathconf(fd, _PC_ACL_ENABLED);
	}

	/*
	 * if acl's aren't supported then
	 * send it through the old GETACL interface
	 */
	if (ace_acl == 0 || ace_acl == -1) {
		ace_acl = _ACL_ACLENT_ENABLED;
	}

	if (ace_acl & _ACL_ACE_ENABLED) {
		cntcmd = ACE_GETACLCNT;
		getcmd = ACE_GETACL;
		acl_info = acl_alloc(ACE_T);
	} else {
		cntcmd = GETACLCNT;
		getcmd = GETACL;
		acl_info = acl_alloc(ACLENT_T);
	}

	if (acl_info == NULL)
		return (-1);

	if (type == ACL_PATH) {
		acl_info->acl_cnt = acl(fname, cntcmd, 0, NULL);
	} else {
		acl_info->acl_cnt = facl(fd, cntcmd, 0, NULL);
	}

	save_errno = errno;
	if (acl_info->acl_cnt < 0) {
		acl_free(acl_info);
		errno = save_errno;
		return (-1);
	}

	if (acl_info->acl_cnt == 0) {
		acl_free(acl_info);
		errno = save_errno;
		return (0);
	}

	acl_info->acl_aclp =
	    malloc(acl_info->acl_cnt * acl_info->acl_entry_size);
	save_errno = errno;

	if (acl_info->acl_aclp == NULL) {
		acl_free(acl_info);
		errno = save_errno;
		return (-1);
	}

	if (type == ACL_PATH) {
		stat_error = stat64(fname, &statbuf);
		error = acl(fname, getcmd, acl_info->acl_cnt,
		    acl_info->acl_aclp);
	} else {
		stat_error = fstat64(fd, &statbuf);
		error = facl(fd, getcmd, acl_info->acl_cnt,
		    acl_info->acl_aclp);
	}

	save_errno = errno;
	if (error == -1) {
		acl_free(acl_info);
		errno = save_errno;
		return (-1);
	}


	if (stat_error == 0) {
		acl_info->acl_flags =
		    (S_ISDIR(statbuf.st_mode) ? ACL_IS_DIR : 0);
	} else
		acl_info->acl_flags = 0;

	switch (acl_info->acl_type) {
	case ACLENT_T:
		if (acl_info->acl_cnt <= MIN_ACL_ENTRIES)
			acl_info->acl_flags |= ACL_IS_TRIVIAL;
		break;
	case ACE_T:
		if (ace_trivial(acl_info->acl_aclp, acl_info->acl_cnt) == 0)
			acl_info->acl_flags |= ACL_IS_TRIVIAL;
		break;
	default:
		errno = EINVAL;
		acl_free(acl_info);
		return (-1);
	}

	if ((acl_info->acl_flags & ACL_IS_TRIVIAL) &&
	    (get_flag & ACL_NO_TRIVIAL)) {
		acl_free(acl_info);
		errno = 0;
		return (0);
	}

	*aclp = acl_info;
	return (0);
}