[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

setuid logging



Here's a patch to enable setuid logging in -current. I've tested it on
i386/GENERIC which is the only platform that I have. Skipping the find
that /etc/security runs every night really cuts down the amount of time
that it takes to run. To enable it, `sysctl fs.logsetuid=1`. It's also
only been tested on FFS but I don't see why it shouldn't work on other
filesystems (unless there is a problem with the inode numbers?).

The chmod system call will now output lines like:
/bsd: Setuid bit set by uid 1000 on file /tmp/a in filesystem mounted on /

fchmod doesn't have any idea what the filename is, so for now it just
prints out the inode number, like:
/bsd: Setuid bit set by uid 0 on inode 101240 in filesystem mounted
nosuid on /var

So it would be possible to do a `find /var -inum 101240` to locate the
file. It might be possible to try and find the vnode in the namei cache
but I haven't done that yet.

I also put in checks (as you can see above) for filesystems mounted
nosuid and noexec. I didn't add code for setgid files - not sure if it's
a good idea and hoping to get some feedback.

While working on this it occurred to me that perhaps the best thing to
do on a filesystem mounted nosuid would be to have chmod return the
equivalent of EROFS. Stretching that example, should chmod return an
error if you set the execute bit on a file in a filesystem mounted
noexec? I'm not sure how many things that would break. It does seem
strange to allow someone to set permissions on files that are then
overridden by mount options, but that could come back later when the
filesystem is mounted exec/suid.

Comments?

Matt


Index: /usr/src/sys/kern/vfs_syscalls.c
===================================================================
RCS file: /cvs/src/sys/kern/vfs_syscalls.c,v
retrieving revision 1.111
diff -c -r1.111 vfs_syscalls.c
*** /usr/src/sys/kern/vfs_syscalls.c	14 May 2004 04:00:33 -0000	1.111
--- /usr/src/sys/kern/vfs_syscalls.c	1 Jun 2004 23:45:52 -0000
***************
*** 58,64 ****
--- 58,69 ----
  #include <uvm/uvm_extern.h>
  #include <sys/sysctl.h>
  
+ #include <sys/syslog.h>
+ #include <ufs/ufs/quota.h>
+ #include <ufs/ufs/inode.h>
+ 
  extern int suid_clear;
+ extern int log_setuid;
  int	usermount = 0;		/* sysctl: by default, users may not mount */
  
  static int change_dir(struct nameidata *, struct proc *);
***************
*** 1896,1906 ****
  	struct vattr vattr;
  	int error;
  	struct nameidata nd;
  
  	if (SCARG(uap, mode) & ~(S_IFMT | ALLPERMS))
  		return (EINVAL);
  
! 	NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, SCARG(uap, path), p);
  	if ((error = namei(&nd)) != 0)
  		return (error);
  	vp = nd.ni_vp;
--- 1901,1915 ----
  	struct vattr vattr;
  	int error;
  	struct nameidata nd;
+ 	struct statfs *mntbuf;
  
  	if (SCARG(uap, mode) & ~(S_IFMT | ALLPERMS))
  		return (EINVAL);
  
! 	/* path = pool_get(&namei_pool, PR_WAITOK); */
! 
! 	NDINIT(&nd, LOOKUP, FOLLOW | SAVENAME, UIO_USERSPACE, SCARG(uap, path), p);
! 
  	if ((error = namei(&nd)) != 0)
  		return (error);
  	vp = nd.ni_vp;
***************
*** 1914,1919 ****
--- 1923,1961 ----
  		error = VOP_SETATTR(vp, &vattr, p->p_ucred, p);
  	}
  	vput(vp);
+ 
+ 	/* 
+ 	 * log if setuid bit is set
+ 	 */
+ 	if ((SCARG(uap, mode) & S_ISUID)
+ 	    && !error
+ 	    && log_setuid) {
+ 		/* get the filesystem that the file is in*/
+ 		mntbuf = &vp->v_mount->mnt_stat;
+ 
+ 		/* check if the fs is mounted 'nosuid' */
+ 		if (vp->v_mount->mnt_flag & MNT_NOSUID) {
+ 			/* send notice to syslog */
+ 			log(LOG_NOTICE, "Setuid bit set by uid %u on file %s in filesystem mounted nosuid on %s\n", 
+ 		    	    p->p_cred->p_ruid, nd.ni_cnd.cn_pnbuf, mntbuf->f_mntonname);
+ 		/* check if the fs is mounted 'noexec' */
+ 		} else if (vp->v_mount->mnt_flag & MNT_NOEXEC) {
+ 			/* send notice to syslog */
+ 			log(LOG_NOTICE, "Setuid bit set by uid %u on file %s in filesystem mounted noexec on %s\n", 
+ 		    	    p->p_cred->p_ruid, nd.ni_cnd.cn_pnbuf, mntbuf->f_mntonname);
+ 		/* check for both ? */
+ 		/* else fs is mounted to allow setuid execution */
+ 		} else {
+ 			/* send notice to syslog */
+ 			log(LOG_NOTICE, "Setuid bit set by uid %u on file %s in filesystem mounted on %s\n", 
+ 		    	    p->p_cred->p_ruid, nd.ni_cnd.cn_pnbuf, mntbuf->f_mntonname);
+ 		}
+ 	} /* setuid */
+ 
+ 	/* we have to free the buffer since we specified SAVENAME in NDINIT */
+ 	/* FREE(nd.ni_cnd.cn_pnbuf, M_NAMEI); */
+ 	pool_put(&namei_pool, nd.ni_cnd.cn_pnbuf);
+ 
  	return (error);
  }
  
***************
*** 1933,1940 ****
--- 1975,1984 ----
  	} */ *uap = v;
  	struct vattr vattr;
  	struct vnode *vp;
+ 	struct inode *ip;
  	struct file *fp;
  	int error;
+ 	struct statfs *mntbuf;
  
  	if (SCARG(uap, mode) & ~(S_IFMT | ALLPERMS))
  		return (EINVAL);
***************
*** 1953,1958 ****
--- 1997,2032 ----
  	}
  	VOP_UNLOCK(vp, 0, p);
  	FRELE(fp);
+ 
+ 	/* 
+ 	 * log if setuid bit is set
+ 	 */
+ 	if ((SCARG(uap, mode) & S_ISUID)
+ 	    && !error
+ 	    && log_setuid) {
+ 		/* get the filesystem that the file is in */
+ 		mntbuf = &vp->v_mount->mnt_stat;
+ 		
+ 		/* get the file's inode */
+ 		/* since we only have an fd, we can't get the filename so just log the inode number */
+ 		ip = VTOI(vp);
+ 
+ 		/* check if the fs is mounted 'nosuid' */
+ 		if (vp->v_mount->mnt_flag & MNT_NOSUID) {
+ 			/* send notice to syslog */
+ 			log(LOG_NOTICE, "Setuid bit set by uid %u on inode %u in filesystem mounted nosuid on %s\n", 
+ 			    p->p_cred->p_ruid, ip->i_number, mntbuf->f_mntonname);
+ 		} else if (vp->v_mount->mnt_flag & MNT_NOEXEC) {
+ 			/* send notice to syslog */
+ 			log(LOG_NOTICE, "Setuid bit set by uid %u on inode %u in filesystem mounted noexec on %s\n", 
+ 	    	    	    p->p_cred->p_ruid, ip->i_number, mntbuf->f_mntonname);
+ 		} else {
+ 			/* send notice to syslog */
+ 			log(LOG_NOTICE, "Setuid bit set by uid %u on inode %u in filesystem mounted on %s\n", 
+ 	    	    	    p->p_cred->p_ruid, ip->i_number, mntbuf->f_mntonname);
+ 		}
+ 	} /* setuid */
+ 
  	return (error);
  }
  

Index: /usr/src/sys/kern/vfs_subr.c
===================================================================
RCS file: /cvs/src/sys/kern/vfs_subr.c,v
retrieving revision 1.100
diff -c -r1.100 vfs_subr.c
*** /usr/src/sys/kern/vfs_subr.c	27 May 2004 20:48:46 -0000	1.100
--- /usr/src/sys/kern/vfs_subr.c	1 Jun 2004 19:08:09 -0000
***************
*** 77,82 ****
--- 77,83 ----
  int doforce = 1;		/* 1 => permit forcible unmounting */
  int prtactive = 0;		/* 1 => print out reclaim of active vnodes */
  int suid_clear = 1;		/* 1 => clear SUID / SGID on owner change */
+ int log_setuid = 0;		/* 1 => log file setuid changes */
  
  /*
   * Insq/Remq for the vnode usage lists.
***************
*** 1910,1919 ****
--- 1911,1932 ----
  {
  	sysctlfn *fn;
  
+ 	int error, ctlval;
+ 
  	switch (name[0]) {
  	case FS_POSIX:
  		fn = fs_posix_sysctl;
  		break;
+ 	case FS_LOGSETUID:
+ 		ctlval = log_setuid;
+ 		if ((error = sysctl_int(oldp, oldlenp, newp, newlen, &ctlval))
+ 		    || newp == NULL)
+ 			return (error);
+ 		if (ctlval != 1 && ctlval != 0)
+ 			return (EINVAL);
+ 		/* return(sysctl_int(oldp, oldlenp, newp, newlen, &log_setuid));*/
+ 		log_setuid = ctlval;
+ 		return (0);
  	default:
  		return (EOPNOTSUPP);
  	}

Index: /usr/src/sys/sys/sysctl.h
===================================================================
RCS file: /cvs/src/sys/sys/sysctl.h,v
retrieving revision 1.77
diff -c -r1.77 sysctl.h
*** /usr/src/sys/sys/sysctl.h	19 Apr 2004 22:52:33 -0000	1.77
--- /usr/src/sys/sys/sysctl.h	13 May 2004 01:58:04 -0000
***************
*** 482,492 ****
   * CTL_FS identifiers
   */
  #define	FS_POSIX	1		/* POSIX flags */
! #define	FS_MAXID	2
  
  #define	CTL_FS_NAMES { \
  	{ 0, 0 }, \
  	{ "posix", CTLTYPE_NODE }, \
  }
  
  /*
--- 482,494 ----
   * CTL_FS identifiers
   */
  #define	FS_POSIX	1		/* POSIX flags */
! #define FS_LOGSETUID	2		/* log setuid changes */	
! #define	FS_MAXID	3
  
  #define	CTL_FS_NAMES { \
  	{ 0, 0 }, \
  	{ "posix", CTLTYPE_NODE }, \
+ 	{ "logsetuid", CTLTYPE_INT }, \
  }
  
  /*

Index: /usr/src/sbin/sysctl/sysctl.8
===================================================================
RCS file: /cvs/src/sbin/sysctl/sysctl.8,v
retrieving revision 1.122
diff -c -r1.122 sysctl.8
*** /usr/src/sbin/sysctl/sysctl.8	7 May 2004 21:58:14 -0000	1.122
--- /usr/src/sbin/sysctl/sysctl.8	2 Jun 2004 00:28:06 -0000
***************
*** 202,207 ****
--- 202,208 ----
  .It vm.maxslp	integer	no
  .It vm.uspace	integer	no
  .It fs.posix.setuid	integer	yes
+ .It fs.logsetuid	integer	yes
  .It net.inet.ip.forwarding	integer	yes
  .It net.inet.ip.redirect	integer	yes
  .It net.inet.ip.ttl	integer	yes

Index: /usr/src/sbin/sysctl/sysctl.c
===================================================================
RCS file: /cvs/src/sbin/sysctl/sysctl.c,v
retrieving revision 1.113
diff -c -r1.113 sysctl.c
*** /usr/src/sbin/sysctl/sysctl.c	15 Apr 2004 00:23:17 -0000	1.113
--- /usr/src/sbin/sysctl/sysctl.c	14 May 2004 18:59:06 -0000
***************
*** 605,614 ****
  		break;
  
  	case CTL_FS:
! 		len = sysctl_fs(string, &bufp, mib, flags, &type);
! 		if (len >= 0)
  			break;
! 		return;
  
  	case CTL_VFS:
  		if (mib[1])
--- 605,618 ----
  		break;
  
  	case CTL_FS:
! 		if (mib[1] == FS_POSIX) {
! 			len = sysctl_fs(string, &bufp, mib, flags, &type);
! 			if (len >= 0)
! 				break;
! 			return;
! 		} else {
  			break;
! 		}
  
  	case CTL_VFS:
  		if (mib[1])

Index: /usr/src/etc/security
===================================================================
RCS file: /cvs/src/etc/security,v
retrieving revision 1.66
diff -c -r1.66 security
*** /usr/src/etc/security	28 Dec 2003 19:51:31 -0000	1.66
--- /usr/src/etc/security	28 May 2004 23:35:37 -0000
***************
*** 457,530 ****
  
  # Display any changes in setuid/setgid files and devices.
  pending="\nChecking setuid/setgid files and devices:\n"
- (find / \( ! -fstype local -o -fstype fdesc -o -fstype kernfs \
- 	-o -fstype procfs \) -a -prune -o \
- 	-type f -a \( -perm -u+s -o -perm -g+s \) -print0 -o \
- 	! -type d -a ! -type f -a ! -type l -a ! -type s -a ! -type p \
- 	-print0 | xargs -0 ls -ldgT | sort +9 > $LIST) 2> $OUTPUT
- 
- # Display any errors that occurred during system file walk.
- if [ -s $OUTPUT ] ; then
- 	echo "${pending}Setuid/device find errors:"
- 	pending=
- 	cat $OUTPUT
- 	echo ""
- fi
  
! # Display any changes in the setuid/setgid file list.
! FIELDS1=1.1,1.2,1.3,1.4,1.5,1.6,1.7,1.8,1.9,0
! FIELDS2=2.1,2.2,2.3,2.4,2.5,2.6,2.7,2.8,2.9,0
! egrep -av '^[bc]' $LIST | join -o $FIELDS2 -110 -210 -v2 /dev/null - > $TMP1
! if [ -s $TMP1 ] ; then
! 	# Check to make sure uudecode isn't setuid.
! 	if grep -aw uudecode $TMP1 > /dev/null ; then
! 		echo "${pending}\nUudecode is setuid."
  		pending=
  	fi
  
! 	CUR=/var/backups/setuid.current
! 	BACK=/var/backups/setuid.backup
! 
! 	if [ -s $CUR ] ; then
! 		if cmp -s $CUR $TMP1 ; then
! 			:
! 		else
! 			> $TMP2
! 			join -o $FIELDS2 -110 -210 -v2 $CUR $TMP1 > $OUTPUT
! 			if [ -s $OUTPUT ] ; then
! 				echo "${pending}Setuid additions:"
! 				pending=
! 				tee -a $TMP2 < $OUTPUT | column -t
! 				echo ""
! 			fi
! 
! 			join -o $FIELDS1 -110 -210 -v1 $CUR $TMP1 > $OUTPUT
! 			if [ -s $OUTPUT ] ; then
! 				echo "${pending}Setuid deletions:"
! 				pending=
! 				tee -a $TMP2 < $OUTPUT | column -t
! 				echo ""
! 			fi
  
! 			sort +9 $TMP2 $CUR $TMP1 | \
! 			    sed -e 's/[	 ][	 ]*/ /g' | uniq -u > $OUTPUT
! 			if [ -s $OUTPUT ] ; then
! 				echo "${pending}Setuid changes:"
! 				pending=
! 				column -t $OUTPUT
! 				echo ""
  			fi
! 
! 			cp $CUR $BACK
  			cp $TMP1 $CUR
  		fi
- 	else
- 		echo "${pending}Setuid additions:"
- 		pending=
- 		column -t $TMP1
- 		echo ""
- 		cp $TMP1 $CUR
  	fi
  fi
  
  # Check for block and character disk devices that are readable or writeable
--- 457,535 ----
  
  # Display any changes in setuid/setgid files and devices.
  pending="\nChecking setuid/setgid files and devices:\n"
  
! # First check to see if we are logging changes and can avoid a find
! if [ `/usr/sbin/sysctl fs.logsetuid` = "fs.logsetuid=1" ] ; then
! 	echo "\nSetuid/setgid logging enabled, skipping file check\n"
! else
! 	(find /\( ! -fstype local -o -fstype fdesc -o -fstype kernfs \
! 		-o -fstype procfs \) -a -prune -o \
! 		-type f -a \( -perm -u+s -o -perm -g+s \) -print0 -o \
! 		! -type d -a ! -type f -a ! -type l -a ! -type s -a ! -type p \
! 		-print0 | xargs -0 ls -ldgT | sort +9 > $LIST) 2> $OUTPUT
! 
! 	# Display any errors that occurred during system file walk.
! 	if [ -s $OUTPUT ] ; then
! 		echo "${pending}Setuid/device find errors:"
  		pending=
+ 		cat $OUTPUT
+ 		echo ""
  	fi
  
! 	# Display any changes in the setuid/setgid file list.
! 	FIELDS1=1.1,1.2,1.3,1.4,1.5,1.6,1.7,1.8,1.9,0
! 	FIELDS2=2.1,2.2,2.3,2.4,2.5,2.6,2.7,2.8,2.9,0
! 	egrep -av '^[bc]' $LIST | join -o $FIELDS2 -110 -210 -v2 /dev/null - > $TMP1
! 	if [ -s $TMP1 ] ; then
! 		CUR=/var/backups/setuid.current
! 		BACK=/var/backups/setuid.backup
! 
! 		if [ -s $CUR ] ; then
! 			if cmp -s $CUR $TMP1 ; then
! 				:
! 			else
! 				> $TMP2
! 				join -o $FIELDS2 -110 -210 -v2 $CUR $TMP1 > $OUTPUT
! 				if [ -s $OUTPUT ] ; then
! 					echo "${pending}Setuid additions:"
! 					pending=
! 					tee -a $TMP2 < $OUTPUT | column -t
! 					echo ""
! 				fi
! 
! 				join -o $FIELDS1 -110 -210 -v1 $CUR $TMP1 > $OUTPUT
! 				if [ -s $OUTPUT ] ; then
! 					echo "${pending}Setuid deletions:"
! 					pending=
! 					tee -a $TMP2 < $OUTPUT | column -t
! 					echo ""
! 				fi
! 
! 				sort +9 $TMP2 $CUR $TMP1 | \
! 				    sed -e 's/[	 ][	 ]*/ /g' | uniq -u > $OUTPUT
! 				if [ -s $OUTPUT ] ; then
! 					echo "${pending}Setuid changes:"
! 					pending=
! 					column -t $OUTPUT
! 					echo ""
! 				fi
  
! 				cp $CUR $BACK
! 				cp $TMP1 $CUR
  			fi
! 		else
! 			echo "${pending}Setuid additions:"
! 			pending=
! 			column -t $TMP1
! 			echo ""
  			cp $TMP1 $CUR
  		fi
  	fi
+ fi
+ # Check to make sure uudecode isn't setuid.
+ if grep -aw uudecode $TMP1 > /dev/null ; then
+ 	echo "${pending}\nUudecode is setuid."
+ 	pending=
  fi
  
  # Check for block and character disk devices that are readable or writeable

Index: /usr/src/lib/libc/gen/sysctl.3
===================================================================
RCS file: /cvs/src/lib/libc/gen/sysctl.3,v
retrieving revision 1.138
diff -c -r1.138 sysctl.3
*** /usr/src/lib/libc/gen/sysctl.3	20 Apr 2004 06:41:47 -0000	1.138
--- /usr/src/lib/libc/gen/sysctl.3	2 Jun 2004 04:02:08 -0000
***************
*** 248,253 ****
--- 248,254 ----
  .Bl -column "Second level nameXXXXXX" integerXXX -offset indent
  .It Sy Second level name	Type	Changeable
  .It Dv FS_POSIX_SETUID No "	integer	yes"
+ .It Dv FS_LOGSETUID No "	integer	yes"
  .El
  .Bl -tag -width "123456"
  .It Dv FS_POSIX_SETUID
***************
*** 260,265 ****
--- 261,274 ----
  As detailed in
  .Xr securelevel 7 ,
  this variable may not be changed if the securelevel is \*(Gt 0.
+ .It Dv FS_LOGSETUID
+ When this variable is set, adding the 
+ .Va S_ISUID
+ bit to a file's permissions will be logged to syslog with the
+ .Va LOG_KERN
+ facility and
+ .Va LOG_INFO
+ level.
  .El
  .Ss CTL_HW
  The string and integer information available for the



Visit your host, monkey.org