Reviewed by: hackers@freebsd.org in general

Obtained from: Whistle Communications tree

Add an option to the way UFS works dependent on the SUID bit of directories
This changes makes things a whole lot simpler on systems running as
fileservers for PCs and MACS. to enable the new code you must
1/ enable option SUIDDIR on the kernel.
2/ mount the filesystem with option suiddir.
hopefully this makes it difficult enough for people to
do this accidentally.
see the new chmod(2) man page for detailed info.
This commit is contained in:
Julian Elischer 1997-11-13 00:28:51 +00:00
parent a272e47d50
commit 52bf64c787
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=31144
14 changed files with 216 additions and 35 deletions

View File

@ -33,7 +33,7 @@
.\" SUCH DAMAGE.
.\"
.\" @(#)chmod.1 8.4 (Berkeley) 3/31/94
.\" $Id$
.\" $Id: chmod.1,v 1.7 1997/02/22 14:01:29 peter Exp $
.\"
.Dd March 31, 1994
.Dt CHMOD 1
@ -110,10 +110,14 @@ the following values:
.Bl -tag -width 6n -compact -offset indent
.It Li 4000
set-user-ID-on-execution
( see
.Xr chmod 2
for directories )
.It Li 2000
set-group-ID-on-execution
.It Li 1000
sticky bit, see chmod(2)
sticky bit, see
.Xr chmod 2
.It Li 0400
read by owner
.It Li 0200

View File

@ -30,7 +30,7 @@
.\" SUCH DAMAGE.
.\"
.\" @(#)chmod.2 8.1 (Berkeley) 6/4/93
.\" $Id: chmod.2,v 1.9 1997/03/21 20:57:18 mpp Exp $
.\" $Id: chmod.2,v 1.10 1997/08/17 00:19:28 steve Exp $
.\"
.Dd June 4, 1993
.Dt CHMOD 2
@ -112,6 +112,25 @@ permissions.
For more details of the properties of the sticky bit, see
.Xr sticky 8 .
.Pp
If mode ISUID (set UID) is set on a directory,
and the MNT_SUIDDIR option was used in the mount of the filesystem,
then the owner of any new files and sub-directories
created within this directory are set
to be the same as the owner of that directory.
If this function is enabled, new directories will inherit
the bit from their parents. Execute bits are removed from
the file, and it will not be given to root. This behaviour does not change the
requirements for the user to be allowed to write the file, but only the eventual
owner after it has been created. Group inheritance is not effected.
.Pp
This feature is designed for use on fileservers serving PC users via
ftp, SAMBA, or netatalk. It provides security holes for shell users and as
such should not be used on shell machines, especially on home directories.
This option requires the SUIDDIR
option in the kernel to work. Only UFS filesystems support this option.
For more details of the suiddir mount option, see
.Xr mount 8 .
.Pp
Writing or changing the owner of a file
turns off the set-user-id and set-group-id bits
unless the user is the super-user.

View File

@ -89,6 +89,8 @@ Do not honor setuid or setgid bits on files when executing them.
Disable update of file access times.
.It Dv MNT_NODEV
Do not interpret special files on the filesystem.
.It Dv MNT_SUIDDIR
Directories with the SUID bit set chown new files to their own owner.
.It Dv MNT_SYNCHRONOUS
All I/O to the filesystem should be done synchronously.
.It Dv MNT_ASYNC
@ -153,6 +155,16 @@ even if files are still active.
Active special devices continue to work,
but any further accesses to any other active files result in errors
even if the filesystem is later remounted.
.Pp
The
.Dv MNT_SUIDDIR
option requires the SUIDDIR option to have been compiled into the kernel
to have any effect.
See the
.Xr mount 8
and
.Xr chmod 2
pages for more information.
.Sh RETURN VALUES
The
.Fn mount

View File

@ -31,7 +31,7 @@
* SUCH DAMAGE.
*
* @(#)mntopts.h 8.7 (Berkeley) 3/29/95
* $Id: mntopts.h,v 1.10 1997/08/25 21:02:21 bde Exp $
* $Id: mntopts.h,v 1.11 1997/09/27 13:44:08 kato Exp $
*/
struct mntopt {
@ -54,6 +54,7 @@ struct mntopt {
#define MOPT_GROUPQUOTA { "groupquota", 0, 0, 0 }
#define MOPT_NOCLUSTERR { "clusterr", 1, MNT_NOCLUSTERR, 0 }
#define MOPT_NOCLUSTERW { "clusterw", 1, MNT_NOCLUSTERW, 0 }
#define MOPT_SUIDDIR { "suiddir", 0, MNT_SUIDDIR, 0 }
/* Control flags. */
#define MOPT_FORCE { "force", 0, MNT_FORCE, 0 }
@ -77,6 +78,7 @@ struct mntopt {
MOPT_NOATIME, \
MOPT_NODEV, \
MOPT_NOEXEC, \
MOPT_SUIDDIR, /* must be before MOPT_NOSUID */ \
MOPT_NOSUID, \
MOPT_RDONLY, \
MOPT_UNION, \

View File

@ -30,7 +30,7 @@
.\" SUCH DAMAGE.
.\"
.\" @(#)mount.8 8.8 (Berkeley) 6/16/94
.\" $Id: mount.8,v 1.18 1997/08/24 21:02:48 steve Exp $
.\" $Id: mount.8,v 1.19 1997/09/27 13:44:12 kato Exp $
.\"
.Dd June 16, 1994
.Dt MOUNT 8
@ -154,6 +154,22 @@ mount the file system read-only (even the super-user may not write it).
All
.Tn I/O
to the file system should be done synchronously.
.It suiddir
A directory on the mounted filesystem will respond to the SUID bit
being set, by setting the owner of any new files to be the same
as the owner of the directory.
New directories will inherit the bit from their parents.
Execute bits are removed from
the file, and it will not be given to root.
.Pp
This feature is designed for use on fileservers serving PC users via
ftp, SAMBA, or netatalk. It provides security holes for shell users and as
such should not be used on shell machines, especially on home directories.
This option requires the SUIDDIR
option in the kernel to work. Only UFS filesystems support this option.
See
.Xr chmod 2
for more information.
.It update
The same as
.Fl u ;

View File

@ -42,7 +42,7 @@ static const char copyright[] =
static char sccsid[] = "@(#)mount.c 8.25 (Berkeley) 5/8/95";
#else
static const char rcsid[] =
"$Id: mount.c,v 1.19 1997/08/24 21:02:49 steve Exp $";
"$Id: mount.c,v 1.20 1997/09/27 13:44:17 kato Exp $";
#endif
#endif /* not lint */
@ -97,6 +97,7 @@ static struct opt {
{ MNT_UNION, "union" },
{ MNT_NOCLUSTERR, "noclusterr" },
{ MNT_NOCLUSTERW, "noclusterw" },
{ MNT_SUIDDIR, "suiddir" },
{ NULL }
};

View File

@ -31,7 +31,7 @@
* SUCH DAMAGE.
*
* @(#)mntopts.h 8.7 (Berkeley) 3/29/95
* $Id: mntopts.h,v 1.10 1997/08/25 21:02:21 bde Exp $
* $Id: mntopts.h,v 1.11 1997/09/27 13:44:08 kato Exp $
*/
struct mntopt {
@ -54,6 +54,7 @@ struct mntopt {
#define MOPT_GROUPQUOTA { "groupquota", 0, 0, 0 }
#define MOPT_NOCLUSTERR { "clusterr", 1, MNT_NOCLUSTERR, 0 }
#define MOPT_NOCLUSTERW { "clusterw", 1, MNT_NOCLUSTERW, 0 }
#define MOPT_SUIDDIR { "suiddir", 0, MNT_SUIDDIR, 0 }
/* Control flags. */
#define MOPT_FORCE { "force", 0, MNT_FORCE, 0 }
@ -77,6 +78,7 @@ struct mntopt {
MOPT_NOATIME, \
MOPT_NODEV, \
MOPT_NOEXEC, \
MOPT_SUIDDIR, /* must be before MOPT_NOSUID */ \
MOPT_NOSUID, \
MOPT_RDONLY, \
MOPT_UNION, \

View File

@ -30,7 +30,7 @@
.\" SUCH DAMAGE.
.\"
.\" @(#)mount.8 8.8 (Berkeley) 6/16/94
.\" $Id: mount.8,v 1.18 1997/08/24 21:02:48 steve Exp $
.\" $Id: mount.8,v 1.19 1997/09/27 13:44:12 kato Exp $
.\"
.Dd June 16, 1994
.Dt MOUNT 8
@ -154,6 +154,22 @@ mount the file system read-only (even the super-user may not write it).
All
.Tn I/O
to the file system should be done synchronously.
.It suiddir
A directory on the mounted filesystem will respond to the SUID bit
being set, by setting the owner of any new files to be the same
as the owner of the directory.
New directories will inherit the bit from their parents.
Execute bits are removed from
the file, and it will not be given to root.
.Pp
This feature is designed for use on fileservers serving PC users via
ftp, SAMBA, or netatalk. It provides security holes for shell users and as
such should not be used on shell machines, especially on home directories.
This option requires the SUIDDIR
option in the kernel to work. Only UFS filesystems support this option.
See
.Xr chmod 2
for more information.
.It update
The same as
.Fl u ;

View File

@ -42,7 +42,7 @@ static const char copyright[] =
static char sccsid[] = "@(#)mount.c 8.25 (Berkeley) 5/8/95";
#else
static const char rcsid[] =
"$Id: mount.c,v 1.19 1997/08/24 21:02:49 steve Exp $";
"$Id: mount.c,v 1.20 1997/09/27 13:44:17 kato Exp $";
#endif
#endif /* not lint */
@ -97,6 +97,7 @@ static struct opt {
{ MNT_UNION, "union" },
{ MNT_NOCLUSTERR, "noclusterr" },
{ MNT_NOCLUSTERW, "noclusterw" },
{ MNT_SUIDDIR, "suiddir" },
{ NULL }
};

View File

@ -1,4 +1,4 @@
# $Id: options,v 1.36 1997/10/18 01:15:32 peter Exp $
# $Id: options,v 1.37 1997/11/05 20:17:06 joerg Exp $
# Format:
# Option name filename
@ -9,6 +9,7 @@ DDB
DDB_UNATTENDED opt_ddb.h
GDB_REMOTE_CHAT opt_ddb.h
DEVFS_ROOT opt_devfs.h
SUIDDIR
KTRACE
QUOTA
SYSVMSG opt_sysvipc.h

View File

@ -36,7 +36,7 @@
* SUCH DAMAGE.
*
* @(#)vfs_syscalls.c 8.13 (Berkeley) 4/15/94
* $Id: vfs_syscalls.c,v 1.80 1997/11/06 19:29:30 phk Exp $
* $Id: vfs_syscalls.c,v 1.81 1997/11/12 05:42:17 julian Exp $
*/
/*
@ -261,10 +261,10 @@ mount(p, uap)
mp->mnt_kern_flag |= MNTK_WANTRDWR;
mp->mnt_flag &=~ (MNT_NOSUID | MNT_NOEXEC | MNT_NODEV |
MNT_SYNCHRONOUS | MNT_UNION | MNT_ASYNC | MNT_NOATIME |
MNT_NOCLUSTERR | MNT_NOCLUSTERW);
MNT_NOCLUSTERR | MNT_NOCLUSTERW | MNT_SUIDDIR);
mp->mnt_flag |= SCARG(uap, flags) & (MNT_NOSUID | MNT_NOEXEC |
MNT_NODEV | MNT_SYNCHRONOUS | MNT_UNION | MNT_ASYNC | MNT_FORCE |
MNT_NOATIME | MNT_NOCLUSTERR | MNT_NOCLUSTERW);
MNT_NOATIME | MNT_NOCLUSTERR | MNT_NOCLUSTERW | MNT_SUIDDIR);
/*
* Mount the filesystem.
*/

View File

@ -36,7 +36,7 @@
* SUCH DAMAGE.
*
* @(#)vfs_syscalls.c 8.13 (Berkeley) 4/15/94
* $Id: vfs_syscalls.c,v 1.80 1997/11/06 19:29:30 phk Exp $
* $Id: vfs_syscalls.c,v 1.81 1997/11/12 05:42:17 julian Exp $
*/
/*
@ -261,10 +261,10 @@ mount(p, uap)
mp->mnt_kern_flag |= MNTK_WANTRDWR;
mp->mnt_flag &=~ (MNT_NOSUID | MNT_NOEXEC | MNT_NODEV |
MNT_SYNCHRONOUS | MNT_UNION | MNT_ASYNC | MNT_NOATIME |
MNT_NOCLUSTERR | MNT_NOCLUSTERW);
MNT_NOCLUSTERR | MNT_NOCLUSTERW | MNT_SUIDDIR);
mp->mnt_flag |= SCARG(uap, flags) & (MNT_NOSUID | MNT_NOEXEC |
MNT_NODEV | MNT_SYNCHRONOUS | MNT_UNION | MNT_ASYNC | MNT_FORCE |
MNT_NOATIME | MNT_NOCLUSTERR | MNT_NOCLUSTERW);
MNT_NOATIME | MNT_NOCLUSTERR | MNT_NOCLUSTERW | MNT_SUIDDIR);
/*
* Mount the filesystem.
*/

View File

@ -31,7 +31,7 @@
* SUCH DAMAGE.
*
* @(#)mount.h 8.21 (Berkeley) 5/20/95
* $Id: mount.h,v 1.48 1997/10/12 20:26:02 phk Exp $
* $Id: mount.h,v 1.49 1997/11/12 05:42:23 julian Exp $
*/
#ifndef _SYS_MOUNT_H_
@ -146,7 +146,7 @@ struct mount {
struct vnode *mnt_vnodecovered; /* vnode we mounted on */
struct vnodelst mnt_vnodelist; /* list of vnodes this mount */
struct lock mnt_lock; /* mount structure lock */
int mnt_flag; /* exported flags */
int mnt_flag; /* flags shared with user */
int mnt_kern_flag; /* kernel only flags */
int mnt_maxsymlinklen; /* max size of short symlink */
struct statfs mnt_stat; /* cache of filesystem stats */
@ -155,9 +155,7 @@ struct mount {
};
/*
* Mount flags.
*
* Unmount uses MNT_FORCE flag.
* User specifiable flags.
*/
#define MNT_RDONLY 0x00000001 /* read only filesystem */
#define MNT_SYNCHRONOUS 0x00000002 /* file system written synchronously */
@ -166,12 +164,13 @@ struct mount {
#define MNT_NODEV 0x00000010 /* don't interpret special files */
#define MNT_UNION 0x00000020 /* union with underlying filesystem */
#define MNT_ASYNC 0x00000040 /* file system written asynchronously */
#define MNT_SUIDDIR 0x00100000 /* Disable cluster read */
#define MNT_NOATIME 0x10000000 /* Disable update of file access time */
#define MNT_NOCLUSTERR 0x40000000 /* Disable cluster read */
#define MNT_NOCLUSTERW 0x80000000 /* Disable cluster read */
/*
* exported mount flags.
* NFS export related mount flags.
*/
#define MNT_EXRDONLY 0x00000080 /* exported read only */
#define MNT_EXPORTED 0x00000100 /* file system is exported */
@ -181,7 +180,9 @@ struct mount {
#define MNT_EXPUBLIC 0x20000000 /* public export (WebNFS) */
/*
* Flags set by internal operations.
* Flags set by internal operations,
* but visible to the user.
* XXX some of these are not quite right.. (I've never seen the root flag set)
*/
#define MNT_LOCAL 0x00001000 /* filesystem is stored locally */
#define MNT_QUOTA 0x00002000 /* quotas are enabled on filesystem */
@ -190,25 +191,31 @@ struct mount {
/*
* Mask of flags that are visible to statfs()
* XXX I think that this could now become (~(MNT_CMDFLAGS))
* but the 'mount' program may need changing to handle this.
* XXX MNT_EXPUBLIC is presently left out. I don't know why.
*/
#define MNT_VISFLAGMASK (MNT_RDONLY|MNT_SYNCHRONOUS|MNT_NOEXEC|MNT_NOSUID| \
MNT_NODEV|MNT_UNION|MNT_ASYNC|MNT_EXRDONLY| \
MNT_EXPORTED|MNT_DEFEXPORTED|MNT_EXPORTANON| \
MNT_EXKERB|MNT_LOCAL|MNT_USER|MNT_QUOTA|MNT_ROOTFS| \
MNT_NOATIME|MNT_NOCLUSTERR|MNT_NOCLUSTERW \
/* | MNT_EXPUBLIC */)
#define MNT_VISFLAGMASK (MNT_RDONLY | MNT_SYNCHRONOUS | MNT_NOEXEC | \
MNT_NOSUID | MNT_NODEV | MNT_UNION | \
MNT_ASYNC | MNT_EXRDONLY | MNT_EXPORTED | \
MNT_DEFEXPORTED | MNT_EXPORTANON| MNT_EXKERB | \
MNT_LOCAL | MNT_USER | MNT_QUOTA | \
MNT_ROOTFS | MNT_NOATIME | MNT_NOCLUSTERR| \
MNT_NOCLUSTERW | MNT_SUIDDIR/* | MNT_EXPUBLIC */)
/*
* External filesystem control flags.
* External filesystem command modifier flags.
* Unmount can use the MNT_FORCE flag.
* XXX These are not STATES and really should be somewhere else.
*/
#define MNT_UPDATE 0x00010000 /* not a real mount, just an update */
#define MNT_DELEXPORT 0x00020000 /* delete export host lists */
#define MNT_RELOAD 0x00040000 /* reload filesystem data */
#define MNT_FORCE 0x00080000 /* force unmount or readonly change */
#define MNT_CMDFLAGS (MNT_UPDATE|MNT_DELEXPORT|MNT_RELOAD|MNT_FORCE)
/*
* Internal filesystem control flags.
* Internal filesystem control flags stored in mnt_kern_flag.
*
* MNT_UNMOUNT locks the mount entry so that name lookup cannot proceed
* MNTK_UNMOUNT locks the mount entry so that name lookup cannot proceed
* past the mount point. This keeps the subtree stable during mounts
* and unmounts.
*/

View File

@ -36,7 +36,7 @@
* SUCH DAMAGE.
*
* @(#)ufs_vnops.c 8.27 (Berkeley) 5/27/95
* $Id: ufs_vnops.c,v 1.64 1997/10/26 20:55:39 phk Exp $
* $Id: ufs_vnops.c,v 1.65 1997/10/27 13:33:47 bde Exp $
*/
#include "opt_quota.h"
@ -1329,8 +1329,57 @@ ufs_mkdir(ap)
if (error)
goto out;
ip = VTOI(tvp);
ip->i_uid = cnp->cn_cred->cr_uid;
ip->i_gid = dp->i_gid;
#ifdef SUIDDIR
{
#ifdef QUOTA
struct ucred ucred, *ucp;
ucp = cnp->cn_cred
#endif I
/*
* if 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.
* The new directory also inherits the SUID bit.
* If user's UID an ddir UID are the same,
* 'give it away' so that the SUID is still forced on.
*/
if ( (dvp->v_mount->mnt_flag & MNT_SUIDDIR) &&
(dp->i_mode & ISUID) && dp->i_uid) {
dmode |= ISUID;
ip->i_uid = dp->i_uid;
#ifdef QUOTA
if (pdir->i_uid != cnp->cn_cred->cr_uid) {
/*
* make sure the correct user gets charged
* for the space.
* Make a dummy credential for the victim.
* XXX This seems to never be accessed out of
* our context so a stack variable is ok.
*/
ucred.cr_ref = 1
ucred.cr_uid = ip->i_uid;
ucred.cr_ngroups = 1
ucred.cr_groups[0] = dp->i_gid;
ucp = *ucred;
}
#endif I
} else {
ip->i_uid = cnp->cn_cred->cr_uid;
}
#ifdef QUOTA
if ((error = getinoquota(ip)) ||
(error = chkiq(ip, 1, ucp, 0))) {
free(cnp->cn_pnbuf, M_NAMEI);
VOP_VFREE(tvp, ip->i_number, dmode);
vput(tvp);
vput(dvp);
return (error);
}
#endif
}
#else
ip->i_uid = cnp->cn_cred->cr_uid;
#ifdef QUOTA
if ((error = getinoquota(ip)) ||
(error = chkiq(ip, 1, cnp->cn_cred, 0))) {
@ -1340,6 +1389,7 @@ ufs_mkdir(ap)
vput(dvp);
return (error);
}
#endif
#endif
ip->i_flag |= IN_ACCESS | IN_CHANGE | IN_UPDATE;
ip->i_mode = dmode;
@ -2007,6 +2057,55 @@ ufs_makeinode(mode, dvp, vpp, cnp)
}
ip = VTOI(tvp);
ip->i_gid = pdir->i_gid;
#ifdef SUIDDIR
{
#ifdef QUOTA
struct ucred ucred, *ucp;
ucp = cnp->cn_cred
#endif I
/*
* 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;
#ifdef QUOTA
/*
* make sure the correct user gets charged
* for the space.
* Quickly knock up a dummy credential for the victim.
* XXX This seems to never be accessed out of our
* context so a stack variable is ok.
*/
ucred.cr_ref = 1
ucred.cr_uid = ip->i_uid;
ucred.cr_ngroups = 1
ucred.cr_groups[0] = pdir->i_gid;
ucp = *ucred;
#endif I
} else {
ip->i_uid = cnp->cn_cred->cr_uid;
}
#ifdef QUOTA
if ((error = getinoquota(ip)) ||
(error = chkiq(ip, 1, ucp, 0))) {
free(cnp->cn_pnbuf, M_NAMEI);
VOP_VFREE(tvp, ip->i_number, mode);
vput(tvp);
vput(dvp);
return (error);
}
#endif
}
#else
ip->i_uid = cnp->cn_cred->cr_uid;
#ifdef QUOTA
if ((error = getinoquota(ip)) ||
@ -2017,6 +2116,7 @@ ufs_makeinode(mode, dvp, vpp, cnp)
vput(dvp);
return (error);
}
#endif
#endif
ip->i_flag |= IN_ACCESS | IN_CHANGE | IN_UPDATE;
ip->i_mode = mode;