コード例 #1
0
ファイル: splat-cred.c プロジェクト: BjoKaSH/spl-osx
/*
 * On most/all systems it can be expected that a task with root
 * permissions also is a member of the root group,  Since the
 * test suite is always run as root we check first that CRED() is
 * a member of the root group, and secondly that it is not a member
 * of our fake group.  This test will break is someone happens to
 * create group number NGROUPS_MAX-1 and then added root to it.
 */
static int
splat_cred_test3(struct file *file, void *arg)
{
	gid_t root_gid, fake_gid;
	int rc;

	root_gid = 0;
	fake_gid = NGROUPS_MAX-1;

	rc = groupmember(root_gid, CRED());
	if (!rc) {
		splat_vprint(file, SPLAT_CRED_TEST3_NAME,
			     "Failed root git %d expected to be member "
			     "of CRED() groups: %d\n", root_gid, rc);
		return -EIDRM;
	}

	rc = groupmember(fake_gid, CRED());
	if (rc) {
		splat_vprint(file, SPLAT_CRED_TEST3_NAME,
			     "Failed fake git %d expected not to be member "
			     "of CRED() groups: %d\n", fake_gid, rc);
		return -EIDRM;
	}

	splat_vprint(file, SPLAT_CRED_TEST3_NAME, "Success root gid "
		     "is a member of the expected groups: %d\n", rc);

	return rc;
} /* splat_cred_test3() */
コード例 #2
0
ファイル: zfs_fuid.c プロジェクト: roddi/maczfs-10a286
/*
 * Check to see if id is a groupmember.  If cred
 * has ksid info then sidlist is checked first
 * and if still not found then POSIX groups are checked
 *
 * Will use a straight FUID compare when possible.
 */
boolean_t
zfs_groupmember(zfsvfs_t *zfsvfs, uint64_t id, cred_t *cr)
{
	ksid_t		*ksid = crgetsid(cr, KSID_GROUP);
	uid_t		gid;

	if (ksid) {
		int 		i;
		ksid_t		*ksid_groups;
		ksidlist_t	*ksidlist = crgetsidlist(cr);
		uint32_t	idx = FUID_INDEX(id);
		uint32_t	rid = FUID_RID(id);

		ASSERT(ksidlist);
		ksid_groups = ksidlist->ksl_sids;

		for (i = 0; i != ksidlist->ksl_nsid; i++) {
			if (idx == 0) {
				if (id != IDMAP_WK_CREATOR_GROUP_GID &&
				    id == ksid_groups[i].ks_id) {
					return (B_TRUE);
				}
			} else {
				char *domain;

				domain = zfs_fuid_find_by_idx(zfsvfs, idx);
				ASSERT(domain != NULL);

				if (strcmp(domain,
				    IDMAP_WK_CREATOR_SID_AUTHORITY) == 0)
					return (B_FALSE);

				if ((strcmp(domain,
				    ksid_groups[i].ks_domain->kd_name) == 0) &&
				    rid == ksid_groups[i].ks_rid)
					return (B_TRUE);
			}
		}
	}

	/*
	 * Not found in ksidlist, check posix groups
	 */
	gid = zfs_fuid_map_id(zfsvfs, id, cr, ZFS_GROUP);
#ifdef __APPLE__
	return (groupmember(gid, (kauth_cred_t)cr));
#else
	return (groupmember(gid, cr));
#endif
}
コード例 #3
0
ファイル: uipc_shm.c プロジェクト: BillTheBest/libuinet
static int
shm_chown(struct file *fp, uid_t uid, gid_t gid, struct ucred *active_cred,
    struct thread *td)
{
	struct shmfd *shmfd;
	int error;

	error = 0;
	shmfd = fp->f_data;
	mtx_lock(&shm_timestamp_lock);
#ifdef MAC
	error = mac_posixshm_check_setowner(active_cred, shmfd, uid, gid);
	if (error != 0)
		goto out;
#endif
	if (uid == (uid_t)-1)
		uid = shmfd->shm_uid;
	if (gid == (gid_t)-1)
                 gid = shmfd->shm_gid;
	if (((uid != shmfd->shm_uid && uid != active_cred->cr_uid) ||
	    (gid != shmfd->shm_gid && !groupmember(gid, active_cred))) &&
	    (error = priv_check_cred(active_cred, PRIV_VFS_CHOWN, 0)))
		goto out;
	shmfd->shm_uid = uid;
	shmfd->shm_gid = gid;
out:
	mtx_unlock(&shm_timestamp_lock);
	return (error);
}
コード例 #4
0
ファイル: mac_seeotheruids.c プロジェクト: 2asoft/freebsd
static int
seeotheruids_check(struct ucred *cr1, struct ucred *cr2)
{

	if (!seeotheruids_enabled)
		return (0);

	if (primarygroup_enabled) {
		if (cr1->cr_rgid == cr2->cr_rgid)
			return (0);
	}

	if (specificgid_enabled) {
		if (cr1->cr_rgid == specificgid ||
		    groupmember(specificgid, cr1))
			return (0);
	}

	if (cr1->cr_ruid == cr2->cr_ruid)
		return (0);

	if (suser_privileged) {
		if (priv_check_cred(cr1, PRIV_SEEOTHERUIDS, 0) == 0)
			return (0);
	}

	return (ESRCH);
}
コード例 #5
0
ファイル: uipc_sem.c プロジェクト: rchander/freebsd
static int
ksem_chown(struct file *fp, uid_t uid, gid_t gid, struct ucred *active_cred,
    struct thread *td)
{
	struct ksem *ks;
	int error;

	error = 0;
	ks = fp->f_data;
	mtx_lock(&sem_lock);
#ifdef MAC
	error = mac_posixsem_check_setowner(active_cred, ks, uid, gid);
	if (error != 0)
		goto out;
#endif
	if (uid == (uid_t)-1)
		uid = ks->ks_uid;
	if (gid == (gid_t)-1)
                 gid = ks->ks_gid;
	if (((uid != ks->ks_uid && uid != active_cred->cr_uid) ||
	    (gid != ks->ks_gid && !groupmember(gid, active_cred))) &&
	    (error = priv_check_cred(active_cred, PRIV_VFS_CHOWN, 0)))
		goto out;
	ks->ks_uid = uid;
	ks->ks_gid = gid;
out:
	mtx_unlock(&sem_lock);
	return (error);
}
コード例 #6
0
/*
 * Change the mode on a file.
 * Inode must be locked before calling.
 */
static int
ext2_chmod(struct vnode *vp, int mode, struct ucred *cred, struct thread *td)
{
	struct inode *ip = VTOI(vp);
	int error;

	/*
	 * To modify the permissions on a file, must possess VADMIN
	 * for that file.
	 */
	if ((error = VOP_ACCESS(vp, VADMIN, cred, td)))
		return (error);
	/*
	 * Privileged processes may set the sticky bit on non-directories,
	 * as well as set the setgid bit on a file with a group that the
	 * process is not a member of.
	 */
	if (vp->v_type != VDIR && (mode & S_ISTXT)) {
		error = priv_check_cred(cred, PRIV_VFS_STICKYFILE, 0);
		if (error)
			return (EFTYPE);
	}
	if (!groupmember(ip->i_gid, cred) && (mode & ISGID)) {
		error = priv_check_cred(cred, PRIV_VFS_SETGID, 0);
		if (error)
			return (error);
	}
	ip->i_mode &= ~ALLPERMS;
	ip->i_mode |= (mode & ALLPERMS);
	ip->i_flag |= IN_CHANGE;
	return (0);
}
コード例 #7
0
ファイル: ext2fs_vnops.c プロジェクト: sofuture/bitrig
/*
 * Perform chown operation on inode ip;
 * inode must be locked prior to call.
 */
static int
ext2fs_chown(struct vnode *vp, uid_t uid, gid_t gid, struct ucred *cred, struct proc *p)
{
	struct inode *ip = VTOI(vp);
	uid_t ouid;
	gid_t ogid;
	int error = 0;

	if (uid == (uid_t)VNOVAL)
		uid = ip->i_e2fs_uid;
	if (gid == (gid_t)VNOVAL)
		gid = ip->i_e2fs_gid;
	/*
	 * If we don't own the file, are trying to change the owner
	 * of the file, or are not a member of the target group,
	 * the caller must be superuser or the call fails.
	 */
	if ((cred->cr_uid != ip->i_e2fs_uid || uid != ip->i_e2fs_uid ||
		(gid != ip->i_e2fs_gid && !groupmember((gid_t)gid, cred))) &&
		(error = suser_ucred(cred)))
		return (error);
	ogid = ip->i_e2fs_gid;
	ouid = ip->i_e2fs_uid;

	ip->i_e2fs_gid = gid;
	ip->i_e2fs_uid = uid;
	if (ouid != uid || ogid != gid)
		ip->i_flag |= IN_CHANGE;
	if (ouid != uid && cred->cr_uid != 0)
		ip->i_e2fs_mode &= ~ISUID;
	if (ogid != gid && cred->cr_uid != 0)
		ip->i_e2fs_mode &= ~ISGID;
	return (0);
}
コード例 #8
0
int
secpolicy_vnode_setids_setgids(kauth_cred_t cred, gid_t gid)
{

	if (groupmember(gid, cred))
		return (0);

	return kauth_authorize_generic(cred, KAUTH_GENERIC_ISSUSER, NULL);
}
コード例 #9
0
ファイル: iput.c プロジェクト: BackupTheBerlios/samqfs
int				/* ERRNO if error, 0 if successful. */
sam_access_ino(
	sam_node_t *ip,		/* pointer to inode. */
	int mode,		/* mode of access to be verified */
	boolean_t locked,	/* is ip->inode_rwl held by caller? */
	cred_t *credp)		/* credentials pointer. */
{
	int shift = 0;

	ASSERT(!locked || RW_LOCK_HELD(&ip->inode_rwl));

	/*
	 * If requesting write access, and read only filesystem or WORM file
	 * return error.
	 */
	if (mode & S_IWRITE) {
		if (ip->mp->mt.fi_mflag & MS_RDONLY) {
			return (EROFS);
		}
		if (ip->di.status.b.worm_rdonly && !S_ISDIR(ip->di.mode)) {
			return (EROFS);
		}
	}

	if (!locked) {
		RW_LOCK_OS(&ip->inode_rwl, RW_READER);
	}

	/* Use ACL, if present, to check access. */
	if (ip->di.status.b.acl) {
		int error;

		error = sam_acl_access(ip, mode, credp);
		if (!locked) {
			RW_UNLOCK_OS(&ip->inode_rwl, RW_READER);
		}
		return (error);
	}

	if (!locked) {
		RW_UNLOCK_OS(&ip->inode_rwl, RW_READER);
	}

	if (crgetuid(credp) != ip->di.uid) {
		shift += 3;
		if (!groupmember((uid_t)ip->di.gid, credp)) {
			shift += 3;
		}
	}
	mode &= ~(ip->di.mode << shift);
	if (mode == 0) {
		return (0);
	}
	return (secpolicy_vnode_access(credp, SAM_ITOV(ip), ip->di.uid, mode));
}
コード例 #10
0
ファイル: bootfs_vnops.c プロジェクト: maosi66/illumos-joyent
/*ARGSUSED*/
static int
bootfs_access(vnode_t *vp, int mode, int flags, cred_t *cr,
    caller_context_t *ct)
{
	int shift = 0;
	bootfs_node_t *bpn = (bootfs_node_t *)vp->v_data;

	if (crgetuid(cr) != bpn->bvn_attr.va_uid) {
		shift += 3;
		if (groupmember(bpn->bvn_attr.va_gid, cr) == 0)
			shift += 3;
	}

	return (secpolicy_vnode_access2(cr, vp, bpn->bvn_attr.va_uid,
	    bpn->bvn_attr.va_mode << shift, mode));
}
コード例 #11
0
ファイル: namevno.c プロジェクト: apprisi/illumos-gate
/*
 * Standard access() like check.  Figure out which mode bits apply
 * to the caller then pass the missing mode bits to the secpolicy function.
 */
static int
nm_access_unlocked(void *vnp, int mode, cred_t *crp)
{
	struct namenode *nodep = vnp;
	int shift = 0;

	if (crgetuid(crp) != nodep->nm_vattr.va_uid) {
		shift += 3;
		if (!groupmember(nodep->nm_vattr.va_gid, crp))
			shift += 3;
	}

	return (secpolicy_vnode_access2(crp, NMTOV(nodep),
	    nodep->nm_vattr.va_uid, nodep->nm_vattr.va_mode << shift,
	    mode));
}
コード例 #12
0
ファイル: auto_vnops.c プロジェクト: apprisi/illumos-gate
/* ARGSUSED */
static int
auto_access(
	vnode_t *vp,
	int mode,
	int flags,
	cred_t *cred,
	caller_context_t *ct)
{
	fnnode_t *fnp = vntofn(vp);
	vnode_t *newvp;
	int error;

	AUTOFS_DPRINT((4, "auto_access: vp=%p\n", (void *)vp));

	if (error = auto_trigger_mount(vp, cred, &newvp))
		goto done;

	if (newvp != NULL) {
		/*
		 * Node is mounted on.
		 */
		error = VOP_ACCESS(newvp, mode, 0, cred, ct);
		VN_RELE(newvp);
	} else {
		int shift = 0;

		/*
		 * really interested in the autofs node, check the
		 * access on it
		 */
		ASSERT(error == 0);
		if (crgetuid(cred) != fnp->fn_uid) {
			shift += 3;
			if (groupmember(fnp->fn_gid, cred) == 0)
				shift += 3;
		}
		error = secpolicy_vnode_access2(cred, vp, fnp->fn_uid,
		    fnp->fn_mode << shift, mode);
	}

done:
	AUTOFS_DPRINT((5, "auto_access: error=%d\n", error));
	return (error);
}
コード例 #13
0
/*
 * This helper may be used by VFSs to implement unix chown semantics.
 */
int
vop_helper_chown(struct vnode *vp, uid_t new_uid, gid_t new_gid,
		 struct ucred *cred,
		 uid_t *cur_uidp, gid_t *cur_gidp, mode_t *cur_modep)
{
	gid_t ogid;
	uid_t ouid;
	int error;

	if (new_uid == (uid_t)VNOVAL)
		new_uid = *cur_uidp;
	if (new_gid == (gid_t)VNOVAL)
		new_gid = *cur_gidp;

	/*
	 * If we don't own the file, are trying to change the owner
	 * of the file, or are not a member of the target group,
	 * the caller must be privileged or the call fails.
	 */
	if ((cred->cr_uid != *cur_uidp || new_uid != *cur_uidp ||
	    (new_gid != *cur_gidp && !(cred->cr_gid == new_gid ||
	    groupmember(new_gid, cred)))) &&
	    (error = priv_check_cred(cred, PRIV_VFS_CHOWN, 0))) {
		return (error);
	}
	ogid = *cur_gidp;
	ouid = *cur_uidp;
	/* XXX QUOTA CODE */
	*cur_uidp = new_uid;
	*cur_gidp = new_gid;
	/* XXX QUOTA CODE */

	/*
	 * DragonFly clears both SUID and SGID if either the owner or
	 * group is changed and root isn't doing it.  If root is doing
	 * it we do not clear SUID/SGID.
	 */
	if (cred->cr_uid != 0 && (ouid != new_uid || ogid != new_gid))
		*cur_modep &= ~(S_ISUID | S_ISGID);
	return(0);
}
コード例 #14
0
/*
 * Perform chown operation on inode ip;
 * inode must be locked prior to call.
 */
static int
ext2_chown(struct vnode *vp, uid_t uid, gid_t gid, struct ucred *cred,
    struct thread *td)
{
	struct inode *ip = VTOI(vp);
	uid_t ouid;
	gid_t ogid;
	int error = 0;

	if (uid == (uid_t)VNOVAL)
		uid = ip->i_uid;
	if (gid == (gid_t)VNOVAL)
		gid = ip->i_gid;
	/*
	 * To modify the ownership of a file, must possess VADMIN
	 * for that file.
	 */
	if ((error = VOP_ACCESS(vp, VADMIN, cred, td)))
		return (error);
	/*
	 * To change the owner of a file, or change the group of a file
	 * to a group of which we are not a member, the caller must
	 * have privilege.
	 */
	if (uid != ip->i_uid || (gid != ip->i_gid &&
	    !groupmember(gid, cred))) {
		error = priv_check_cred(cred, PRIV_VFS_CHOWN, 0);
		if (error)
			return (error);
	}
	ogid = ip->i_gid;
	ouid = ip->i_uid;
	ip->i_gid = gid;
	ip->i_uid = uid;
	ip->i_flag |= IN_CHANGE;
	if ((ip->i_mode & (ISUID | ISGID)) && (ouid != uid || ogid != gid)) {
		if (priv_check_cred(cred, PRIV_VFS_RETAINSUGID, 0) != 0)
			ip->i_mode &= ~(ISUID | ISGID);
	}
	return (0);
}
コード例 #15
0
/*
 * This helper may be used by VFSs to implement unix chmod semantics.
 */
int
vop_helper_chmod(struct vnode *vp, mode_t new_mode, struct ucred *cred,
		 uid_t cur_uid, gid_t cur_gid, mode_t *cur_modep)
{
	int error;

	if (cred->cr_uid != cur_uid) {
		error = priv_check_cred(cred, PRIV_VFS_CHMOD, 0);
		if (error)
			return (error);
	}
	if (cred->cr_uid) {
		if (vp->v_type != VDIR && (*cur_modep & S_ISTXT))
			return (EFTYPE);
		if (!groupmember(cur_gid, cred) && (*cur_modep & S_ISGID))
			return (EPERM);
	}
	*cur_modep &= ~ALLPERMS;
	*cur_modep |= new_mode & ALLPERMS;
	return(0);
}
コード例 #16
0
ファイル: ext2fs_vnops.c プロジェクト: sofuture/bitrig
/*
 * Change the mode on a file.
 * Inode must be locked before calling.
 */
static int
ext2fs_chmod(struct vnode *vp, mode_t mode, struct ucred *cred, struct proc *p)
{
	struct inode *ip = VTOI(vp);
	int error;

	if (cred->cr_uid != ip->i_e2fs_uid && (error = suser_ucred(cred)))
		return (error);
	if (cred->cr_uid) {
		if (vp->v_type != VDIR && (mode & S_ISTXT))
			return (EFTYPE);
		if (!groupmember(ip->i_e2fs_gid, cred) && (mode & ISGID))
			return (EPERM);
	}
	ip->i_e2fs_mode &= ~ALLPERMS;
	ip->i_e2fs_mode |= (mode & ALLPERMS);
	ip->i_flag |= IN_CHANGE;
	if ((vp->v_flag & VTEXT) && (ip->i_e2fs_mode & S_ISTXT) == 0)
		(void) uvm_vnp_uncache(vp);
	return (0);
}
コード例 #17
0
ファイル: ufs_vnops.c プロジェクト: sofuture/bitrig
/*
 * Change the mode on a file.
 * Inode must be locked before calling.
 */
int
ufs_chmod(struct vnode *vp, int mode, struct ucred *cred, struct proc *p)
{
	struct inode *ip = VTOI(vp);
	int error;

	if (cred->cr_uid != DIP(ip, uid) &&
	    (error = suser_ucred(cred)))
		return (error);
	if (cred->cr_uid) {
		if (vp->v_type != VDIR && (mode & S_ISTXT))
			return (EFTYPE);
		if (!groupmember(DIP(ip, gid), cred) && (mode & ISGID))
			return (EPERM);
	}
	DIP_AND(ip, mode, ~ALLPERMS);
	DIP_OR(ip, mode, mode & ALLPERMS);
	ip->i_flag |= IN_CHANGE;
	if ((vp->v_flag & VTEXT) && (DIP(ip, mode) & S_ISTXT) == 0)
		(void) uvm_vnp_uncache(vp);
	return (0);
}
コード例 #18
0
ファイル: devfs_vnops.c プロジェクト: andreiw/polaris
/*
 * This function is used for secpolicy_setattr().  It must call an
 * access() like function while it is already holding the
 * dv_contents lock.  We only care about this when dv_attr != NULL;
 * so the unlocked access call only concerns itself with that
 * particular branch of devfs_access().
 */
static int
devfs_unlocked_access(void *vdv, int mode, struct cred *cr)
{
	struct dv_node *dv = vdv;
	int shift = 0;
	uid_t owner = dv->dv_attr->va_uid;

	/* Check access based on owner, group and public permissions. */
	if (crgetuid(cr) != owner) {
		shift += 3;
		if (groupmember(dv->dv_attr->va_gid, cr) == 0)
			shift += 3;
	}

	/* compute missing mode bits */
	mode &= ~(dv->dv_attr->va_mode << shift);

	if (mode == 0)
		return (0);

	return (secpolicy_vnode_access(cr, DVTOV(dv), owner, mode));
}
コード例 #19
0
int
tmp_taccess(void *vtp, int mode, struct cred *cred)
{
	struct tmpnode *tp = vtp;
	int shift = 0;
	/*
	 * Check access based on owner, group and
	 * public permissions in tmpnode.
	 */
	if (crgetuid(cred) != tp->tn_uid) {
		shift += MODESHIFT;
		if (groupmember(tp->tn_gid, cred) == 0)
			shift += MODESHIFT;
	}

	/* compute missing mode bits */
	mode &= ~(tp->tn_mode << shift);

	if (mode == 0)
		return (0);

	return (secpolicy_vnode_access(cred, TNTOV(tp), tp->tn_uid, mode));
}
コード例 #20
0
/*
 * uid and gid in sffs determine owner and group for all files.
 */
static int
sfnode_access(sfnode_t *node, mode_t mode, cred_t *cr)
{
	sffs_data_t *sffs = node->sf_sffs;
	mode_t m;
	int shift = 0;
	int error;
	vnode_t *vp;

	ASSERT(MUTEX_HELD(&sffs_lock));

	/* get the mode from the cache or provider */
	if (sfnode_stat_cached(node))
		error = 0;
	else
		error = sfnode_update_stat_cache(node);
	m = (error == 0) ? node->sf_stat.sf_mode : 0;

	/*
	 * mask off the permissions based on uid/gid
	 */
 	if (crgetuid(cr) != sffs->sf_uid) {
		shift += 3;
		if (groupmember(sffs->sf_gid, cr) == 0)
			shift += 3;
	}
	mode &= ~(m << shift);

	if (mode == 0) {
		error = 0;
	} else {
		vp = sfnode_get_vnode(node);
		error = secpolicy_vnode_access(cr, vp, sffs->sf_uid, mode);
		VN_RELE(vp);
	}
	return (error);
}
コード例 #21
0
ファイル: ufs_quota.c プロジェクト: jaredmcneill/freebsd
/*
 * Q_GETQUOTA - return current values in a dqblk structure.
 */
static int
_getquota(struct thread *td, struct mount *mp, u_long id, int type,
          struct dqblk64 *dqb)
{
    struct dquot *dq;
    int error;

    switch (type) {
    case USRQUOTA:
        if ((td->td_ucred->cr_uid != id) && !unprivileged_get_quota) {
            error = priv_check(td, PRIV_VFS_GETQUOTA);
            if (error)
                return (error);
        }
        break;

    case GRPQUOTA:
        if (!groupmember(id, td->td_ucred) &&
                !unprivileged_get_quota) {
            error = priv_check(td, PRIV_VFS_GETQUOTA);
            if (error)
                return (error);
        }
        break;

    default:
        return (EINVAL);
    }

    dq = NODQUOT;
    error = dqget(NULLVP, id, VFSTOUFS(mp), type, &dq);
    if (error)
        return (error);
    *dqb = dq->dq_dqb;
    dqrele(NULLVP, dq);
    return (error);
}
コード例 #22
0
ファイル: inode.c プロジェクト: BackupTheBerlios/samqfs
int				/* ERRNO if error, 0 if successful. */
sam_setattr_ino(
	sam_node_t *ip,		/* pointer to inode. */
	vattr_t *vap,		/* vattr pointer. */
	int flags,		/* flags. */
	cred_t *credp)		/* credentials pointer. */
{
	uint_t mask;
	int error = 0;
	vnode_t *vp;
	sam_mode_t oldmode, mode;
	timespec_t  system_time;
	vattr_t oldva;

	oldva.va_mode = ip->di.mode;
	oldva.va_uid = ip->di.uid;
	oldva.va_gid = ip->di.gid;

	vp = SAM_ITOV(ip);
	if (vap->va_mask & AT_NOSET) {
		return (EINVAL);
	}
	mode = vap->va_mode & ~S_IFMT;
	SAM_HRESTIME(&system_time);

	/*
	 * Enforce the "read only" portion of WORM files.
	 */
	if (ip->di.status.b.worm_rdonly && !S_ISDIR(ip->di.mode)) {
		error = sam_chk_worm(mode, vap->va_mask, ip);
		if (error) {
			return (error);
		}
	}

	/*
	 * Generic setattr security policy check.
	 */
	if (error = secpolicy_vnode_setattr(credp, vp, vap,
	    &oldva, flags, sam_access_ino_ul, ip)) {
		return (error);
	}

	mask = vap->va_mask;

	if (mask & AT_SIZE) {		/* -----Change size */
		if (error == 0) {
			/* Can only truncate a regular file */
			if (S_ISREQ(ip->di.mode)) {
				error = EINVAL;
				goto out;
			} else if (SAM_PRIVILEGE_INO(ip->di.version,
			    ip->di.id.ino)) {
				error = EPERM;	/* Can't trunc priv'ed inodes */
				goto out;
			}
			if (S_ISSEGI(&ip->di) && (vap->va_size != 0)) {
				/*
				 * If file is segment access and not truncating
				 * to zero--fix.
				 */
				error = EINVAL;
				goto out;
			}
			/*
			 * Might need to do TRANS_ITRUNC here for LQFS....
			 */
			if ((error = sam_clear_ino(ip, (offset_t)vap->va_size,
			    STALE_ARCHIVE, credp))) {
				goto out;
			}
		}
	}

	if (mask & AT_MODE) {				/* -----Change mode */
		/* Cannot change .inodes file */
		if (ip->di.id.ino == SAM_INO_INO) {
			error = EPERM;
			goto out;
		}
		oldmode = ip->di.mode;
		ip->di.mode &= S_IFMT;
		ip->di.mode |= vap->va_mode & ~S_IFMT;
		if (ip->di.status.b.worm_rdonly) {
			if (!S_ISDIR(ip->di.mode)) {
				ip->di.mode &= ~WMASK;
			}
			if (oldmode & S_ISUID) {
				ip->di.mode |= S_ISUID;
			}
		}

		/*
		 * In 4.6 there are two modes of WORM trigger operation.
		 * One is compatible with the 53xx SUN NAS series.  This
		 * mode uses the SUID bit by itself.  The second mode is
		 * called compatibility mode.  This mode uses the transition
		 * from a writeable mode as the trigger. Note, copying a
		 * read-only file to a WORM capable volume does *NOT*
		 * initiate the WORM trigger in this mode.
		 */
		if (samgt.license.license.lic_u.b.WORM_fs &&
		    (ip->di.version >= SAM_INODE_VERS_2) &&
		    (((vap->va_mode == S_ISUID) &&
		    (ip->mp->mt.fi_config & MT_ALLWORM)) ||
		    ((ip->mp->mt.fi_config & MT_ALLEMUL) &&
		    (((oldmode & RWXALLMASK) == RWXALLMASK) ||
		    ((vap->va_mode != S_ISUID) &&
		    (oldmode & WMASK) && !(ip->di.mode & WMASK)))))) {
			error = sam_worm_trigger(ip, oldmode, system_time);
			if (error) {
				ip->di.mode = oldmode;
				goto out;
			} else if ((ip->mp->mt.fi_config & MT_ALLEMUL) &&
			    ((oldmode & RWXALLMASK) == RWXALLMASK)) {
				ip->di.mode = oldmode;
			} else if ((vap->va_mode == S_ISUID) &&
			    (!S_ISDIR(ip->di.mode))) {
				if (ip->mp->mt.fi_config & MT_ALLEMUL) {
					ip->di.mode = oldmode &
					    (S_IFMT | RMASK);
				} else {
					ip->di.mode = S_ISUID |
					    (oldmode & (S_IFMT | RMASK));
				}
			}
		}
		TRANS_INODE(ip->mp, ip);
		if (S_ISATTRDIR(oldmode)) {
			ip->di.mode |= S_IFATTRDIR;
		}
		sam_mark_ino(ip, SAM_CHANGED);
	}

	if (mask & (AT_UID | AT_GID)) {		/* -----Change uid/gid */
		int ouid, ogid;

		if (vap->va_mask & AT_MODE) {
			ip->di.mode = (ip->di.mode & S_IFMT) |
			    (vap->va_mode & ~S_IFMT);
		}
		/*
		 * To change file ownership, a process must have
		 * privilege if:
		 *
		 * If it is not the owner of the file, or
		 * if doing restricted chown semantics and
		 * either changing the ownership to someone else or
		 * changing the group to a group that we are not
		 * currently in.
		 */
		if (crgetuid(credp) != ip->di.uid ||
		    (rstchown &&
		    (((mask & AT_UID) && vap->va_uid != ip->di.uid) ||
		    ((mask & AT_GID) && !groupmember(vap->va_gid, credp))))) {
			error = secpolicy_vnode_owner(credp, vap->va_uid);
			if (error) {
				goto out;
			}
		}

		ouid = ip->di.uid;
		ogid = ip->di.gid;
		if (error = sam_quota_chown(ip->mp, ip,
		    (mask&AT_UID) ? vap->va_uid : ouid,
		    (mask&AT_GID) ? vap->va_gid : ogid, credp)) {
			goto out;
		}
		if (mask & AT_UID)  ip->di.uid = vap->va_uid;
		if (mask & AT_GID)  ip->di.gid = vap->va_gid;
		ip->di.status.b.archdone = 0;
		TRANS_INODE(ip->mp, ip);
		sam_mark_ino(ip, SAM_CHANGED);
		/*
		 * Notify arfind and event daemon of setattr.
		 */
		sam_send_to_arfind(ip, AE_change, 0);
		if (ip->mp->ms.m_fsev_buf) {
			sam_send_event(ip->mp, &ip->di, ev_change, 0, 0,
			    ip->di.change_time.tv_sec);
		}
	}

	if (mask & (AT_ATIME | AT_MTIME)) {	/* -----Modify times */
		/*
		 * Synchronously flush pages so dates do not get changed after
		 * utime.  If staging, finish stage and then flush pages.
		 */
		if (ip->flags.b.staging) {
			/*
			 * Might need to do TRANS_ITRUNC or similar here
			 * for LQFS
			 */
			if ((error = sam_clear_file(ip, ip->di.rm.size,
			    MAKE_ONLINE, credp))) {
				goto out;
			}
		}
		sam_flush_pages(ip, 0);
		if (mask & AT_ATIME) {
			/*
			 * The access time field is used by WORM operations to
			 * store the retention timestamp.  This is intercepted
			 * here and stored in the inode's retention period
			 * time fields if either the field hasn't been set or
			 * the provided value exceeds what is currently there
			 * (i.e. we're extending the period).
			 */
			error = sam_check_worm_capable(ip, TRUE);
			if (!error) {
				boolean_t   lite_mode =
				    ((ip->mp->mt.fi_config &
				    MT_LITE_WORM) != 0);
				boolean_t   is_priv =
				    (secpolicy_fs_config(credp,
				    ip->mp->mi.m_vfsp) == 0);

				if (S_ISREG(ip->di.mode) && !WORM(ip)) {
					/*
					 * Regular file in WORM capable
					 * directory.  Set access time per the
					 * request.
					 */
					ip->di.access_time.tv_sec =
					    vap->va_atime.tv_sec;
					ip->di.access_time.tv_nsec =
					    vap->va_atime.tv_nsec;
					ip->flags.b.accessed = 1;
				} else if (WORM(ip) &&
				    (ip->di.version >= SAM_INODE_VERS_2)) {
					boolean_t	extend_period;
					/*
					 * Extend the retention period if so
					 * requested. If lite mode and a
					 * privileged user or a directory allow
					 * the retention period to be shortened.
					 */

					if (vap->va_atime.tv_sec >
					    ip->di2.rperiod_start_time +
					    ip->di2.rperiod_duration * 60) {
						extend_period = 1;
					} else {
						extend_period = 0;
					}

					if (S_ISREG(ip->di.mode) &&
					    extend_period) {
						error = sam_set_rperiod(ip,
						    vap, is_priv &&
						    lite_mode);
					} else if (S_ISDIR(ip->di.mode) ||
					    (S_ISREG(ip->di.mode) &&
					    is_priv && lite_mode)) {
						/*
						 * If the requested time would
						 * result in a non- negative
						 * retention period, set the
						 * period to the difference of
						 * the request and current time.
						 * A negative retention period
						 * is not allowed.
						 */
						if (vap->va_atime.tv_sec >
						    system_time.tv_sec) {


			if (vap->va_atime.tv_sec == INT_MAX) {
				ip->di2.rperiod_duration = 0;
			} else {
				ip->di2.rperiod_duration = 1 +
				    (vap->va_atime.tv_sec -
				    system_time.tv_sec)/60;
			}


						} else {
							error = EINVAL;
						}
					}
					if (error) {
						goto out;
					}
				} else {
					/*
					 * Shouldn't get here, invalid request.
					 */
					error = EINVAL;
					goto out;
				}
				TRANS_INODE(ip->mp, ip);
				sam_mark_ino(ip, SAM_CHANGED);
			} else {
				error = 0;
				ip->di.access_time.tv_sec =
				    vap->va_atime.tv_sec;
				ip->di.access_time.tv_nsec =
				    vap->va_atime.tv_nsec;
				ip->flags.b.accessed = 1;
				TRANS_INODE(ip->mp, ip);
			}
		}
		if (mask & AT_MTIME) {
			if (!ip->di.status.b.worm_rdonly) {
				ip->di.modify_time.tv_sec =
				    vap->va_mtime.tv_sec;
				ip->di.modify_time.tv_nsec =
				    vap->va_mtime.tv_nsec;
				ip->di.change_time.tv_sec = system_time.tv_sec;
				ip->di.change_time.tv_nsec =
				    system_time.tv_nsec;
				ip->flags.b.updated = 1;
				/* Modify time has been set */
				ip->flags.b.dirty = 1;
			}
			TRANS_INODE(ip->mp, ip);
		}
	}

	/*
	 * Check for and apply ACL info, if present.
	 */
	if (ip->di.status.b.acl && !ip->di.status.b.worm_rdonly) {
		if (SAM_IS_SHARED_FS(ip->mp) && SAM_IS_SHARED_SERVER(ip->mp)) {
			RW_UNLOCK_OS(&ip->inode_rwl, RW_WRITER);
			sam_callout_acl(ip, ip->mp->ms.m_client_ord);
			RW_LOCK_OS(&ip->inode_rwl, RW_WRITER);
		}
		if (error = sam_acl_setattr(ip, vap)) {
			goto out;
		}
	}
	if (ip->mp->mt.fi_config & MT_SHARED_WRITER) {
		if ((error == 0) &&
		    (ip->flags.bits & (SAM_ACCESSED|SAM_UPDATED|SAM_CHANGED))) {
			(void) sam_update_inode(ip, SAM_SYNC_ONE, FALSE);
		}
	}
out:

	return (error);
}
コード例 #23
0
int
secpolicy_vnode_setattr(kauth_cred_t cred, struct vnode *vp, struct vattr *vap,
    const struct vattr *ovap, int flags,
    int unlocked_access(void *, int, kauth_cred_t), void *node)
{
	int mask = vap->va_mask;
	int error;

	if (mask & AT_SIZE) {
		if (vp->v_type == VDIR)
			return (EISDIR);
		error = unlocked_access(node, VWRITE, cred);
		if (error)
			return (error);
	}
	if (mask & AT_MODE) {
		/*
		 * If not the owner of the file then check privilege
		 * for two things: the privilege to set the mode at all
		 * and, if we're setting setuid, we also need permissions
		 * to add the set-uid bit, if we're not the owner.
		 * In the specific case of creating a set-uid root
		 * file, we need even more permissions.
		 */
		error = secpolicy_vnode_setdac(cred, ovap->va_uid);
		if (error)
			return (error);
		error = secpolicy_setid_setsticky_clear(vp, vap, ovap, cred);
		if (error)
			return (error);
	} else {
		vap->va_mode = ovap->va_mode;
	}
	if (mask & (AT_UID | AT_GID)) {
		error = secpolicy_vnode_setdac(cred, ovap->va_uid);
		if (error)
			return (error);

		/*
		 * To change the owner of a file, or change the group of a file to a
		 * group of which we are not a member, the caller must have
		 * privilege.
		 */
		if (((mask & AT_UID) && vap->va_uid != ovap->va_uid) ||
		    ((mask & AT_GID) && vap->va_gid != ovap->va_gid &&
		     !groupmember(vap->va_gid, cred))) {
			error = priv_check_cred(cred, PRIV_VFS_CHOWN, 0);
			if (error)
				return (error);
		}

		if (((mask & AT_UID) && vap->va_uid != ovap->va_uid) ||
		    ((mask & AT_GID) && vap->va_gid != ovap->va_gid)) {
			secpolicy_setid_clear(vap, cred);
		}
	}
	if (mask & (AT_ATIME | AT_MTIME)) {
		/*
		 * From utimes(2):
		 * If times is NULL, ... The caller must be the owner of
		 * the file, have permission to write the file, or be the
		 * super-user.
		 * If times is non-NULL, ... The caller must be the owner of
		 * the file or be the super-user.
		 */
		error = secpolicy_vnode_setdac(cred, ovap->va_uid);
		if (error && (vap->va_vaflags & VA_UTIMES_NULL))
			error = unlocked_access(node, VWRITE, cred);
		if (error)
			return (error);
	}
	return (0);
}
コード例 #24
0
/*
 * XXX Copied from illumos.  Should not be here; should be under
 * external/cddl/osnet/dist.  Not sure why it is even in illumos's
 * policy.c rather than somewhere in vnode.c or something.
 */
int
secpolicy_vnode_setattr(kauth_cred_t cred, struct vnode *vp, struct vattr *vap,
    const struct vattr *ovap, int flags,
    int unlocked_access(void *, int, kauth_cred_t), void *node)
{
	int mask = vap->va_mask;
	int error = 0;
	boolean_t skipaclchk = (flags & ATTR_NOACLCHECK) ? B_TRUE : B_FALSE;

	if (mask & AT_SIZE) {
		if (vp->v_type == VDIR) {
			error = EISDIR;
			goto out;
		}

		/*
		 * If ATTR_NOACLCHECK is set in the flags, then we don't
		 * perform the secondary unlocked_access() call since the
		 * ACL (if any) is being checked there.
		 */
		if (skipaclchk == B_FALSE) {
			error = unlocked_access(node, VWRITE, cred);
			if (error)
				goto out;
		}
	}
	if (mask & AT_MODE) {
		/*
		 * If not the owner of the file then check privilege
		 * for two things: the privilege to set the mode at all
		 * and, if we're setting setuid, we also need permissions
		 * to add the set-uid bit, if we're not the owner.
		 * In the specific case of creating a set-uid root
		 * file, we need even more permissions.
		 */
		if ((error = secpolicy_vnode_setdac(cred, ovap->va_uid)) != 0)
			goto out;

		if ((error = secpolicy_setid_setsticky_clear(vp, vap,
		    ovap, cred)) != 0)
			goto out;
	} else
		vap->va_mode = ovap->va_mode;

	if (mask & (AT_UID|AT_GID)) {
		boolean_t checkpriv = B_FALSE;

		/*
		 * Chowning files.
		 *
		 * If you are the file owner:
		 *	chown to other uid		FILE_CHOWN_SELF
		 *	chown to gid (non-member) 	FILE_CHOWN_SELF
		 *	chown to gid (member) 		<none>
		 *
		 * Instead of PRIV_FILE_CHOWN_SELF, FILE_CHOWN is also
		 * acceptable but the first one is reported when debugging.
		 *
		 * If you are not the file owner:
		 *	chown from root			PRIV_FILE_CHOWN + zone
		 *	chown from other to any		PRIV_FILE_CHOWN
		 *
		 */
		if (kauth_cred_getuid(cred) != ovap->va_uid) {
			checkpriv = B_TRUE;
		} else {
			if (((mask & AT_UID) && vap->va_uid != ovap->va_uid) ||
			    ((mask & AT_GID) && vap->va_gid != ovap->va_gid &&
			    !groupmember(vap->va_gid, cred))) {
				checkpriv = B_TRUE;
			}
		}
		/*
		 * If necessary, check privilege to see if update can be done.
		 */
		if (checkpriv &&
		    (error = secpolicy_vnode_chown(cred, ovap->va_uid)) != 0) {
			goto out;
		}

		/*
		 * If the file has either the set UID or set GID bits
		 * set and the caller can set the bits, then leave them.
		 */
		secpolicy_setid_clear(vap, cred);
	}
	if (mask & (AT_ATIME|AT_MTIME)) {
		/*
		 * If not the file owner and not otherwise privileged,
		 * always return an error when setting the
		 * time other than the current (ATTR_UTIME flag set).
		 * If setting the current time (ATTR_UTIME not set) then
		 * unlocked_access will check permissions according to policy.
		 */
		if (kauth_cred_getuid(cred) != ovap->va_uid) {
			if (flags & ATTR_UTIME)
				error = secpolicy_vnode_utime_modify(cred);
			else if (skipaclchk == B_FALSE) {
				error = unlocked_access(node, VWRITE, cred);
				if (error == EACCES &&
				    secpolicy_vnode_utime_modify(cred) == 0)
					error = 0;
			}
			if (error)
				goto out;
		}
	}

	/*
	 * Check for optional attributes here by checking the following:
	 */
	if (mask & AT_XVATTR)
		error = secpolicy_xvattr((xvattr_t *)vap, ovap->va_uid, cred,
		    vp->v_type);
out:
	return (error);
}
コード例 #25
0
ファイル: ext2fs_vnops.c プロジェクト: sofuture/bitrig
/*
 * Allocate a new inode.
 */
int
ext2fs_makeinode(int mode, struct vnode *dvp, struct vnode **vpp,
    struct componentname *cnp)
{
	struct inode *ip, *pdir;
	struct vnode *tvp;
	int error;

	pdir = VTOI(dvp);
#ifdef DIAGNOSTIC
	if ((cnp->cn_flags & HASBUF) == 0)
		panic("ext2fs_makeinode: no name");
#endif
	*vpp = NULL;
	if ((mode & IFMT) == 0)
		mode |= IFREG;

	if ((error = ext2fs_inode_alloc(pdir, mode, cnp->cn_cred, &tvp)) 
	    != 0) {
		pool_put(&namei_pool, cnp->cn_pnbuf);
		vput(dvp);
		return (error);
	}
	ip = VTOI(tvp);
	ip->i_e2fs_gid = pdir->i_e2fs_gid;
	ip->i_e2fs_uid = cnp->cn_cred->cr_uid;
	ip->i_flag |= IN_ACCESS | IN_CHANGE | IN_UPDATE;
	ip->i_e2fs_mode = mode;
	tvp->v_type = IFTOVT(mode);	/* Rest init'd in getnewvnode(). */
	ip->i_e2fs_nlink = 1;
	if ((ip->i_e2fs_mode & ISGID) &&
		!groupmember(ip->i_e2fs_gid, cnp->cn_cred) &&
	    suser_ucred(cnp->cn_cred))
		ip->i_e2fs_mode &= ~ISGID;

	/*
	 * Make sure inode goes to disk before directory entry.
	 */
	if ((error = ext2fs_update(ip, NULL, NULL, 1)) != 0)
		goto bad;
	error = ext2fs_direnter(ip, dvp, cnp);
	if (error != 0)
		goto bad;
	if ((cnp->cn_flags & SAVESTART) == 0)
		pool_put(&namei_pool, cnp->cn_pnbuf);
	vput(dvp);
	*vpp = tvp;
	return (0);

bad:
	/*
	 * Write error occurred trying to update the inode
	 * or the directory so must deallocate the inode.
	 */
	pool_put(&namei_pool, cnp->cn_pnbuf);
	vput(dvp);
	ip->i_e2fs_nlink = 0;
	ip->i_flag |= IN_CHANGE;
	tvp->v_type = VNON;
	vput(tvp);
	return (error);
}
コード例 #26
0
ファイル: ufs_vnops.c プロジェクト: sofuture/bitrig
/*
 * Allocate a new inode.
 */
int
ufs_makeinode(int mode, struct vnode *dvp, struct vnode **vpp,
    struct componentname *cnp)
{
	struct inode *ip, *pdir;
	struct direct newdir;
	struct vnode *tvp;
	int error;

	pdir = VTOI(dvp);
#ifdef DIAGNOSTIC
	if ((cnp->cn_flags & HASBUF) == 0)
		panic("ufs_makeinode: no name");
#endif
	*vpp = NULL;
	if ((mode & IFMT) == 0)
		mode |= IFREG;

	if ((error = UFS_INODE_ALLOC(pdir, mode, cnp->cn_cred, &tvp)) != 0) {
		pool_put(&namei_pool, cnp->cn_pnbuf);
		vput(dvp);
		return (error);
	}

	ip = VTOI(tvp);

	DIP_ASSIGN(ip, gid, DIP(pdir, gid));
	DIP_ASSIGN(ip, uid, cnp->cn_cred->cr_uid);

	if ((error = getinoquota(ip)) ||
	    (error = ufs_quota_alloc_inode(ip, cnp->cn_cred))) {
		pool_put(&namei_pool, cnp->cn_pnbuf);
		UFS_INODE_FREE(ip, ip->i_number, mode);
		vput(tvp);
		vput(dvp);
		return (error);
	}

	ip->i_flag |= IN_ACCESS | IN_CHANGE | IN_UPDATE;
	DIP_ASSIGN(ip, mode, mode);
	tvp->v_type = IFTOVT(mode);	/* Rest init'd in getnewvnode(). */
	ip->i_effnlink = 1;
	DIP_ASSIGN(ip, nlink, 1);
	if (DOINGSOFTDEP(tvp))
		softdep_change_linkcnt(ip, 0);
	if ((DIP(ip, mode) & ISGID) &&
		!groupmember(DIP(ip, gid), cnp->cn_cred) &&
	    suser_ucred(cnp->cn_cred))
		DIP_AND(ip, mode, ~ISGID);

	/*
	 * Make sure inode goes to disk before directory entry.
	 */
	if ((error = UFS_UPDATE(ip, !DOINGSOFTDEP(tvp))) != 0)
		goto bad;

	ufs_makedirentry(ip, cnp, &newdir);
	if ((error = ufs_direnter(dvp, tvp, &newdir, cnp, NULL)) != 0)
		goto bad;

	if ((cnp->cn_flags & SAVESTART) == 0)
		pool_put(&namei_pool, cnp->cn_pnbuf);
	vput(dvp);
	*vpp = tvp;
	return (0);

bad:
	/*
	 * Write error occurred trying to update the inode
	 * or the directory so must deallocate the inode.
	 */
	pool_put(&namei_pool, cnp->cn_pnbuf);
	vput(dvp);
	ip->i_effnlink = 0;
	DIP_ASSIGN(ip, nlink, 0);
	ip->i_flag |= IN_CHANGE;
	if (DOINGSOFTDEP(tvp))
		softdep_change_linkcnt(ip, 0);
	tvp->v_type = VNON;
	vput(tvp);

	return (error);
}
コード例 #27
0
/*
 * Allocate a new inode.
 */
static int
ext2_makeinode(int mode, struct vnode *dvp, struct vnode **vpp,
    struct componentname *cnp)
{
	struct inode *ip, *pdir;
	struct vnode *tvp;
	int error;

	pdir = VTOI(dvp);
#ifdef DIAGNOSTIC
	if ((cnp->cn_flags & HASBUF) == 0)
		panic("ext2_makeinode: no name");
#endif
	*vpp = NULL;
	if ((mode & IFMT) == 0)
		mode |= IFREG;

	error = ext2_valloc(dvp, mode, cnp->cn_cred, &tvp);
	if (error) {
		return (error);
	}
	ip = VTOI(tvp);
	ip->i_gid = pdir->i_gid;
#ifdef SUIDDIR
	{
		/*
		 * if we are
		 * not the owner of the directory,
		 * and we are hacking owners here, (only do this where told to)
		 * and we are not giving it TOO root, (would subvert quotas)
		 * then go ahead and give it to the other user.
		 * Note that this drops off the execute bits for security.
		 */
		if ( (dvp->v_mount->mnt_flag & MNT_SUIDDIR) &&
		     (pdir->i_mode & ISUID) &&
		     (pdir->i_uid != cnp->cn_cred->cr_uid) && pdir->i_uid) {
			ip->i_uid = pdir->i_uid;
			mode &= ~07111;
		} else {
			ip->i_uid = cnp->cn_cred->cr_uid;
		}
	}
#else
	ip->i_uid = cnp->cn_cred->cr_uid;
#endif
	ip->i_flag |= IN_ACCESS | IN_CHANGE | IN_UPDATE;
	ip->i_mode = mode;
	tvp->v_type = IFTOVT(mode);	/* Rest init'd in getnewvnode(). */
	ip->i_nlink = 1;
	if ((ip->i_mode & ISGID) && !groupmember(ip->i_gid, cnp->cn_cred)) {
		if (priv_check_cred(cnp->cn_cred, PRIV_VFS_RETAINSUGID, 0))
			ip->i_mode &= ~ISGID;
	}

	if (cnp->cn_flags & ISWHITEOUT)
		ip->i_flags |= UF_OPAQUE;

	/*
	 * Make sure inode goes to disk before directory entry.
	 */
	error = ext2_update(tvp, !DOINGASYNC(tvp));
	if (error)
		goto bad;
	error = ext2_direnter(ip, dvp, cnp);
	if (error)
		goto bad;

	*vpp = tvp;
	return (0);

bad:
	/*
	 * Write error occurred trying to update the inode
	 * or the directory so must deallocate the inode.
	 */
	ip->i_nlink = 0;
	ip->i_flag |= IN_CHANGE;
	vput(tvp);
	return (error);
}
コード例 #28
0
static int
ugidfw_rulecheck(struct mac_bsdextended_rule *rule,
    struct ucred *cred, struct vnode *vp, struct vattr *vap, int acc_mode)
{
	int mac_granted, match, priv_granted;
	int i;

	/*
	 * Is there a subject match?
	 */
	mtx_assert(&ugidfw_mtx, MA_OWNED);
	if (rule->mbr_subject.mbs_flags & MBS_UID_DEFINED) {
		match =  ((cred->cr_uid <= rule->mbr_subject.mbs_uid_max &&
		    cred->cr_uid >= rule->mbr_subject.mbs_uid_min) ||
		    (cred->cr_ruid <= rule->mbr_subject.mbs_uid_max &&
		    cred->cr_ruid >= rule->mbr_subject.mbs_uid_min) ||
		    (cred->cr_svuid <= rule->mbr_subject.mbs_uid_max &&
		    cred->cr_svuid >= rule->mbr_subject.mbs_uid_min));
		if (rule->mbr_subject.mbs_neg & MBS_UID_DEFINED)
			match = !match;
		if (!match)
			return (0);
	}

	if (rule->mbr_subject.mbs_flags & MBS_GID_DEFINED) {
		match = ((cred->cr_rgid <= rule->mbr_subject.mbs_gid_max &&
		    cred->cr_rgid >= rule->mbr_subject.mbs_gid_min) ||
		    (cred->cr_svgid <= rule->mbr_subject.mbs_gid_max &&
		    cred->cr_svgid >= rule->mbr_subject.mbs_gid_min));
		if (!match) {
			for (i = 0; i < cred->cr_ngroups; i++) {
				if (cred->cr_groups[i]
				    <= rule->mbr_subject.mbs_gid_max &&
				    cred->cr_groups[i]
				    >= rule->mbr_subject.mbs_gid_min) {
					match = 1;
					break;
				}
			}
		}
		if (rule->mbr_subject.mbs_neg & MBS_GID_DEFINED)
			match = !match;
		if (!match)
			return (0);
	}

	if (rule->mbr_subject.mbs_flags & MBS_PRISON_DEFINED) {
		match =
		    (cred->cr_prison->pr_id == rule->mbr_subject.mbs_prison);
		if (rule->mbr_subject.mbs_neg & MBS_PRISON_DEFINED)
			match = !match;
		if (!match)
			return (0);
	}

	/*
	 * Is there an object match?
	 */
	if (rule->mbr_object.mbo_flags & MBO_UID_DEFINED) {
		match = (vap->va_uid <= rule->mbr_object.mbo_uid_max &&
		    vap->va_uid >= rule->mbr_object.mbo_uid_min);
		if (rule->mbr_object.mbo_neg & MBO_UID_DEFINED)
			match = !match;
		if (!match)
			return (0);
	}

	if (rule->mbr_object.mbo_flags & MBO_GID_DEFINED) {
		match = (vap->va_gid <= rule->mbr_object.mbo_gid_max &&
		    vap->va_gid >= rule->mbr_object.mbo_gid_min);
		if (rule->mbr_object.mbo_neg & MBO_GID_DEFINED)
			match = !match;
		if (!match)
			return (0);
	}

	if (rule->mbr_object.mbo_flags & MBO_FSID_DEFINED) {
		match = (bcmp(&(vp->v_mount->mnt_stat.f_fsid),
		    &(rule->mbr_object.mbo_fsid),
		    sizeof(rule->mbr_object.mbo_fsid)) == 0);
		if (rule->mbr_object.mbo_neg & MBO_FSID_DEFINED)
			match = !match;
		if (!match)
			return (0);
	}

	if (rule->mbr_object.mbo_flags & MBO_SUID) {
		match = (vap->va_mode & S_ISUID);
		if (rule->mbr_object.mbo_neg & MBO_SUID)
			match = !match;
		if (!match)
			return (0);
	}

	if (rule->mbr_object.mbo_flags & MBO_SGID) {
		match = (vap->va_mode & S_ISGID);
		if (rule->mbr_object.mbo_neg & MBO_SGID)
			match = !match;
		if (!match)
			return (0);
	}

	if (rule->mbr_object.mbo_flags & MBO_UID_SUBJECT) {
		match = (vap->va_uid == cred->cr_uid ||
		    vap->va_uid == cred->cr_ruid ||
		    vap->va_uid == cred->cr_svuid);
		if (rule->mbr_object.mbo_neg & MBO_UID_SUBJECT)
			match = !match;
		if (!match)
			return (0);
	}

	if (rule->mbr_object.mbo_flags & MBO_GID_SUBJECT) {
		match = (groupmember(vap->va_gid, cred) ||
		    vap->va_gid == cred->cr_rgid ||
		    vap->va_gid == cred->cr_svgid);
		if (rule->mbr_object.mbo_neg & MBO_GID_SUBJECT)
			match = !match;
		if (!match)
			return (0);
	}

	if (rule->mbr_object.mbo_flags & MBO_TYPE_DEFINED) {
		switch (vap->va_type) {
		case VREG:
			match = (rule->mbr_object.mbo_type & MBO_TYPE_REG);
			break;
		case VDIR:
			match = (rule->mbr_object.mbo_type & MBO_TYPE_DIR);
			break;
		case VBLK:
			match = (rule->mbr_object.mbo_type & MBO_TYPE_BLK);
			break;
		case VCHR:
			match = (rule->mbr_object.mbo_type & MBO_TYPE_CHR);
			break;
		case VLNK:
			match = (rule->mbr_object.mbo_type & MBO_TYPE_LNK);
			break;
		case VSOCK:
			match = (rule->mbr_object.mbo_type & MBO_TYPE_SOCK);
			break;
		case VFIFO:
			match = (rule->mbr_object.mbo_type & MBO_TYPE_FIFO);
			break;
		default:
			match = 0;
		}
		if (rule->mbr_object.mbo_neg & MBO_TYPE_DEFINED)
			match = !match;
		if (!match)
			return (0);
	}

	/*
	 * MBI_APPEND should not be here as it should get converted to
	 * MBI_WRITE.
	 */
	priv_granted = 0;
	mac_granted = rule->mbr_mode;
	if ((acc_mode & MBI_ADMIN) && (mac_granted & MBI_ADMIN) == 0 &&
	    priv_check_cred(cred, PRIV_VFS_ADMIN, 0) == 0)
		priv_granted |= MBI_ADMIN;
	if ((acc_mode & MBI_EXEC) && (mac_granted & MBI_EXEC) == 0 &&
	    priv_check_cred(cred, (vap->va_type == VDIR) ? PRIV_VFS_LOOKUP :
	    PRIV_VFS_EXEC, 0) == 0)
		priv_granted |= MBI_EXEC;
	if ((acc_mode & MBI_READ) && (mac_granted & MBI_READ) == 0 &&
	    priv_check_cred(cred, PRIV_VFS_READ, 0) == 0)
		priv_granted |= MBI_READ;
	if ((acc_mode & MBI_STAT) && (mac_granted & MBI_STAT) == 0 &&
	    priv_check_cred(cred, PRIV_VFS_STAT, 0) == 0)
		priv_granted |= MBI_STAT;
	if ((acc_mode & MBI_WRITE) && (mac_granted & MBI_WRITE) == 0 &&
	    priv_check_cred(cred, PRIV_VFS_WRITE, 0) == 0)
		priv_granted |= MBI_WRITE;
	/*
	 * Is the access permitted?
	 */
	if (((mac_granted | priv_granted) & acc_mode) != acc_mode) {
		if (ugidfw_logging)
			log(LOG_AUTHPRIV, "mac_bsdextended: %d:%d request %d"
			    " on %d:%d failed. \n", cred->cr_ruid,
			    cred->cr_rgid, acc_mode, vap->va_uid,
			    vap->va_gid);
		return (EACCES);
	}

	/*
	 * If the rule matched, permits access, and first match is enabled,
	 * return success.
	 */
	if (ugidfw_firstmatch_enabled)
		return (EJUSTRETURN);
	else
		return (0);
}
コード例 #29
0
ファイル: subr_acl_posix1e.c プロジェクト: hmatyschok/MeshBSD
/*
 * Implement a version of vaccess() that understands POSIX.1e ACL semantics;
 * the access ACL has already been prepared for evaluation by the file system
 * and is passed via 'uid', 'gid', and 'acl'.  Return 0 on success, else an
 * errno value.
 */
int
vaccess_acl_posix1e(enum vtype type, uid_t file_uid, gid_t file_gid,
                    struct acl *acl, accmode_t accmode, struct ucred *cred, int *privused)
{
    struct acl_entry *acl_other, *acl_mask;
    accmode_t dac_granted;
    accmode_t priv_granted;
    accmode_t acl_mask_granted;
    int group_matched, i;

    KASSERT((accmode & ~(VEXEC | VWRITE | VREAD | VADMIN | VAPPEND)) == 0,
            ("invalid bit in accmode"));
    KASSERT((accmode & VAPPEND) == 0 || (accmode & VWRITE),
            ("VAPPEND without VWRITE"));

    /*
     * Look for a normal, non-privileged way to access the file/directory
     * as requested.  If it exists, go with that.  Otherwise, attempt to
     * use privileges granted via priv_granted.  In some cases, which
     * privileges to use may be ambiguous due to "best match", in which
     * case fall back on first match for the time being.
     */
    if (privused != NULL)
        *privused = 0;

    /*
     * Determine privileges now, but don't apply until we've found a DAC
     * entry that matches but has failed to allow access.
     *
     * XXXRW: Ideally, we'd determine the privileges required before
     * asking for them.
     */
    priv_granted = 0;

    if (type == VDIR) {
        if ((accmode & VEXEC) && !priv_check_cred(cred,
                PRIV_VFS_LOOKUP, 0))
            priv_granted |= VEXEC;
    } else {
        /*
         * Ensure that at least one execute bit is on. Otherwise,
         * a privileged user will always succeed, and we don't want
         * this to happen unless the file really is executable.
         */
        if ((accmode & VEXEC) && (acl_posix1e_acl_to_mode(acl) &
                                  (S_IXUSR | S_IXGRP | S_IXOTH)) != 0 &&
                !priv_check_cred(cred, PRIV_VFS_EXEC, 0))
            priv_granted |= VEXEC;
    }

    if ((accmode & VREAD) && !priv_check_cred(cred, PRIV_VFS_READ, 0))
        priv_granted |= VREAD;

    if (((accmode & VWRITE) || (accmode & VAPPEND)) &&
            !priv_check_cred(cred, PRIV_VFS_WRITE, 0))
        priv_granted |= (VWRITE | VAPPEND);

    if ((accmode & VADMIN) && !priv_check_cred(cred, PRIV_VFS_ADMIN, 0))
        priv_granted |= VADMIN;

    /*
     * The owner matches if the effective uid associated with the
     * credential matches that of the ACL_USER_OBJ entry.  While we're
     * doing the first scan, also cache the location of the ACL_MASK and
     * ACL_OTHER entries, preventing some future iterations.
     */
    acl_mask = acl_other = NULL;
    for (i = 0; i < acl->acl_cnt; i++) {
        switch (acl->acl_entry[i].ae_tag) {
        case ACL_USER_OBJ:
            if (file_uid != cred->cr_uid)
                break;
            dac_granted = 0;
            dac_granted |= VADMIN;
            if (acl->acl_entry[i].ae_perm & ACL_EXECUTE)
                dac_granted |= VEXEC;
            if (acl->acl_entry[i].ae_perm & ACL_READ)
                dac_granted |= VREAD;
            if (acl->acl_entry[i].ae_perm & ACL_WRITE)
                dac_granted |= (VWRITE | VAPPEND);
            if ((accmode & dac_granted) == accmode)
                return (0);

            /*
             * XXXRW: Do privilege lookup here.
             */
            if ((accmode & (dac_granted | priv_granted)) ==
                    accmode) {
                if (privused != NULL)
                    *privused = 1;
                return (0);
            }
            goto error;

        case ACL_MASK:
            acl_mask = &acl->acl_entry[i];
            break;

        case ACL_OTHER:
            acl_other = &acl->acl_entry[i];
            break;

        default:
            break;
        }
    }

    /*
     * An ACL_OTHER entry should always exist in a valid access ACL.  If
     * it doesn't, then generate a serious failure.  For now, this means
     * a debugging message and EPERM, but in the future should probably
     * be a panic.
     */
    if (acl_other == NULL) {
        /*
         * XXX This should never happen
         */
        printf("vaccess_acl_posix1e: ACL_OTHER missing\n");
        return (EPERM);
    }

    /*
     * Checks against ACL_USER, ACL_GROUP_OBJ, and ACL_GROUP fields are
     * masked by an ACL_MASK entry, if any.  As such, first identify the
     * ACL_MASK field, then iterate through identifying potential user
     * matches, then group matches.  If there is no ACL_MASK, assume that
     * the mask allows all requests to succeed.
     */
    if (acl_mask != NULL) {
        acl_mask_granted = 0;
        if (acl_mask->ae_perm & ACL_EXECUTE)
            acl_mask_granted |= VEXEC;
        if (acl_mask->ae_perm & ACL_READ)
            acl_mask_granted |= VREAD;
        if (acl_mask->ae_perm & ACL_WRITE)
            acl_mask_granted |= (VWRITE | VAPPEND);
    } else
        acl_mask_granted = VEXEC | VREAD | VWRITE | VAPPEND;

    /*
     * Check ACL_USER ACL entries.  There will either be one or no
     * matches; if there is one, we accept or rejected based on the
     * match; otherwise, we continue on to groups.
     */
    for (i = 0; i < acl->acl_cnt; i++) {
        switch (acl->acl_entry[i].ae_tag) {
        case ACL_USER:
            if (acl->acl_entry[i].ae_id != cred->cr_uid)
                break;
            dac_granted = 0;
            if (acl->acl_entry[i].ae_perm & ACL_EXECUTE)
                dac_granted |= VEXEC;
            if (acl->acl_entry[i].ae_perm & ACL_READ)
                dac_granted |= VREAD;
            if (acl->acl_entry[i].ae_perm & ACL_WRITE)
                dac_granted |= (VWRITE | VAPPEND);
            dac_granted &= acl_mask_granted;
            if ((accmode & dac_granted) == accmode)
                return (0);
            /*
             * XXXRW: Do privilege lookup here.
             */
            if ((accmode & (dac_granted | priv_granted)) !=
                    accmode)
                goto error;

            if (privused != NULL)
                *privused = 1;
            return (0);
        }
    }

    /*
     * Group match is best-match, not first-match, so find a "best"
     * match.  Iterate across, testing each potential group match.  Make
     * sure we keep track of whether we found a match or not, so that we
     * know if we should try again with any available privilege, or if we
     * should move on to ACL_OTHER.
     */
    group_matched = 0;
    for (i = 0; i < acl->acl_cnt; i++) {
        switch (acl->acl_entry[i].ae_tag) {
        case ACL_GROUP_OBJ:
            if (!groupmember(file_gid, cred))
                break;
            dac_granted = 0;
            if (acl->acl_entry[i].ae_perm & ACL_EXECUTE)
                dac_granted |= VEXEC;
            if (acl->acl_entry[i].ae_perm & ACL_READ)
                dac_granted |= VREAD;
            if (acl->acl_entry[i].ae_perm & ACL_WRITE)
                dac_granted |= (VWRITE | VAPPEND);
            dac_granted  &= acl_mask_granted;

            if ((accmode & dac_granted) == accmode)
                return (0);

            group_matched = 1;
            break;

        case ACL_GROUP:
            if (!groupmember(acl->acl_entry[i].ae_id, cred))
                break;
            dac_granted = 0;
            if (acl->acl_entry[i].ae_perm & ACL_EXECUTE)
                dac_granted |= VEXEC;
            if (acl->acl_entry[i].ae_perm & ACL_READ)
                dac_granted |= VREAD;
            if (acl->acl_entry[i].ae_perm & ACL_WRITE)
                dac_granted |= (VWRITE | VAPPEND);
            dac_granted  &= acl_mask_granted;

            if ((accmode & dac_granted) == accmode)
                return (0);

            group_matched = 1;
            break;

        default:
            break;
        }
    }

    if (group_matched == 1) {
        /*
         * There was a match, but it did not grant rights via pure
         * DAC.  Try again, this time with privilege.
         */
        for (i = 0; i < acl->acl_cnt; i++) {
            switch (acl->acl_entry[i].ae_tag) {
            case ACL_GROUP_OBJ:
                if (!groupmember(file_gid, cred))
                    break;
                dac_granted = 0;
                if (acl->acl_entry[i].ae_perm & ACL_EXECUTE)
                    dac_granted |= VEXEC;
                if (acl->acl_entry[i].ae_perm & ACL_READ)
                    dac_granted |= VREAD;
                if (acl->acl_entry[i].ae_perm & ACL_WRITE)
                    dac_granted |= (VWRITE | VAPPEND);
                dac_granted &= acl_mask_granted;

                /*
                 * XXXRW: Do privilege lookup here.
                 */
                if ((accmode & (dac_granted | priv_granted))
                        != accmode)
                    break;

                if (privused != NULL)
                    *privused = 1;
                return (0);

            case ACL_GROUP:
                if (!groupmember(acl->acl_entry[i].ae_id,
                                 cred))
                    break;
                dac_granted = 0;
                if (acl->acl_entry[i].ae_perm & ACL_EXECUTE)
                    dac_granted |= VEXEC;
                if (acl->acl_entry[i].ae_perm & ACL_READ)
                    dac_granted |= VREAD;
                if (acl->acl_entry[i].ae_perm & ACL_WRITE)
                    dac_granted |= (VWRITE | VAPPEND);
                dac_granted &= acl_mask_granted;

                /*
                 * XXXRW: Do privilege lookup here.
                 */
                if ((accmode & (dac_granted | priv_granted))
                        != accmode)
                    break;

                if (privused != NULL)
                    *privused = 1;
                return (0);

            default:
                break;
            }
        }
        /*
         * Even with privilege, group membership was not sufficient.
         * Return failure.
         */
        goto error;
    }

    /*
     * Fall back on ACL_OTHER.  ACL_MASK is not applied to ACL_OTHER.
     */
    dac_granted = 0;
    if (acl_other->ae_perm & ACL_EXECUTE)
        dac_granted |= VEXEC;
    if (acl_other->ae_perm & ACL_READ)
        dac_granted |= VREAD;
    if (acl_other->ae_perm & ACL_WRITE)
        dac_granted |= (VWRITE | VAPPEND);

    if ((accmode & dac_granted) == accmode)
        return (0);
    /*
     * XXXRW: Do privilege lookup here.
     */
    if ((accmode & (dac_granted | priv_granted)) == accmode) {
        if (privused != NULL)
            *privused = 1;
        return (0);
    }

error:
    return ((accmode & VADMIN) ? EPERM : EACCES);
}
コード例 #30
0
ファイル: ufs_vnops.c プロジェクト: sofuture/bitrig
/*
 * Perform chown operation on inode ip;
 * inode must be locked prior to call.
 */
int
ufs_chown(struct vnode *vp, uid_t uid, gid_t gid, struct ucred *cred,
    struct proc *p)
{
	struct inode *ip = VTOI(vp);
	uid_t ouid;
	gid_t ogid;
	int error = 0;
	daddr64_t change;
	enum ufs_quota_flags quota_flags = 0;

	if (uid == (uid_t)VNOVAL)
		uid = DIP(ip, uid);
	if (gid == (gid_t)VNOVAL)
		gid = DIP(ip, gid);
	/*
	 * If we don't own the file, are trying to change the owner
	 * of the file, or are not a member of the target group,
	 * the caller must be superuser or the call fails.
	 */
	if ((cred->cr_uid != DIP(ip, uid) || uid != DIP(ip, uid) ||
	    (gid != DIP(ip, gid) && !groupmember((gid_t)gid, cred))) &&
	    (error = suser_ucred(cred)))
		return (error);
	ogid = DIP(ip, gid);
	ouid = DIP(ip, uid);
	change = DIP(ip, blocks);

	if (ouid == uid)
		quota_flags |= UFS_QUOTA_NOUID;
	
	if (ogid == gid)
		quota_flags |= UFS_QUOTA_NOGID;

	if ((error = getinoquota(ip)) != 0)
		return (error);
	(void) ufs_quota_free_blocks2(ip, change, cred, quota_flags);
	(void) ufs_quota_free_inode2(ip, cred, quota_flags);
	(void) ufs_quota_delete(ip);

	DIP_ASSIGN(ip, gid, gid);
	DIP_ASSIGN(ip, uid, uid);

	if ((error = getinoquota(ip)) != 0)
		goto error;

	if ((error = ufs_quota_alloc_blocks2(ip, change, cred, 
		 quota_flags)) != 0) 
		goto error;

	if ((error = ufs_quota_alloc_inode2(ip, cred ,
		 quota_flags)) != 0) {
		(void)ufs_quota_free_blocks2(ip, change, cred, 
		    quota_flags);		
		goto error;
	}

	if (getinoquota(ip))
		panic("chown: lost quota");

	if (ouid != uid || ogid != gid)
		ip->i_flag |= IN_CHANGE;
	if (ouid != uid && cred->cr_uid != 0)
		DIP_AND(ip, mode, ~ISUID);
	if (ogid != gid && cred->cr_uid != 0)
		DIP_AND(ip, mode, ~ISGID);
	return (0);

error:
	(void) ufs_quota_delete(ip);

	DIP_ASSIGN(ip, gid, ogid);
	DIP_ASSIGN(ip, uid, ouid);

	if (getinoquota(ip) == 0) {
		(void) ufs_quota_alloc_blocks2(ip, change, cred, 
		    quota_flags | UFS_QUOTA_FORCE);
		(void) ufs_quota_alloc_inode2(ip, cred,
		    quota_flags | UFS_QUOTA_FORCE);
		(void) getinoquota(ip);
	}
	return (error);

}