--- contrib/openbsm/etc/audit_event.orig +++ contrib/openbsm/etc/audit_event @@ -570,6 +570,7 @@ 43206:AUE_CAP_FCNTLS_GET:cap_fcntls_get(2):fm 43207:AUE_BINDAT:bindat(2):nt 43208:AUE_CONNECTAT:connectat(2):nt +43209:AUE_CHFLAGSAT:chflagsat(2):fm # # Solaris userspace events. # --- lib/libc/sys/Makefile.inc.orig +++ lib/libc/sys/Makefile.inc @@ -278,7 +278,8 @@ MLINKS+=cap_ioctls_limit.2 cap_ioctls_get.2 MLINKS+=cap_rights_limit.2 cap_rights_get.2 MLINKS+=chdir.2 fchdir.2 -MLINKS+=chflags.2 fchflags.2 \ +MLINKS+=chflags.2 chflagsat.2 \ + chflags.2 fchflags.2 \ chflags.2 lchflags.2 MLINKS+=chmod.2 fchmod.2 \ chmod.2 fchmodat.2 \ --- lib/libc/sys/Symbol.map.orig +++ lib/libc/sys/Symbol.map @@ -386,6 +386,7 @@ cap_rights_get; cap_rights_limit; cap_sandboxed; + chflagsat; clock_getcpuclockid2; connectat; ffclock_getcounter; --- lib/libc/sys/cap_rights_limit.2.orig +++ lib/libc/sys/cap_rights_limit.2 @@ -159,7 +159,12 @@ .Xr fchdir 2 . .It Dv CAP_FCHFLAGS Permit -.Xr fchflags 2 . +.Xr fchflags 2 +and +.Xr chflagsat 2 . +.It Dv CAP_CHFLAGSAT +An alias to +.Dv CAP_FCHFLAGS . .It Dv CAP_FCHMOD Permit .Xr fchmod 2 --- lib/libc/sys/chflags.2.orig +++ lib/libc/sys/chflags.2 @@ -47,6 +47,8 @@ .Fn lchflags "const char *path" "int flags" .Ft int .Fn fchflags "int fd" "u_long flags" +.Ft int +.Fn chflagsat "int fd" "const char *path" "int flags" "int atflag" .Sh DESCRIPTION The file whose name is given by @@ -66,6 +68,45 @@ will change the flags of the link itself, rather than the file it points to. .Pp +The +.Fn chflagsat +is equivalent to either +.Fn chflags +or +.Fn lchflags +depending on the +.Fa atflag +except in the case where +.Fa path +specifies a relative path. +In this case the file to be changed is determined relative to the directory +associated with the file descriptor +.Fa fd +instead of the current working directory. +The values for the +.Fa atflag +are constructed by a bitwise-inclusive OR of flags from the following list, +defined in +.In fcntl.h : +.Bl -tag -width indent +.It Dv AT_SYMLINK_NOFOLLOW +If +.Fa path +names a symbolic link, then the flags of the symbolic link are changed. +.El +.Pp +If +.Fn chflagsat +is passed the special value +.Dv AT_FDCWD +in the +.Fa fd +parameter, the current working directory is used. +If also +.Fa atflag +is zero, the behavior is identical to a call to +.Fn chflags . +.Pp The flags specified are formed by .Em or Ns 'ing the following values @@ -236,3 +277,7 @@ .Fn lchflags system call first appeared in .Fx 5.0 . +The +.Fn chflagsat +system call first appeared in +.Fx 10.0 . --- sys/bsm/audit_kevents.h.orig +++ sys/bsm/audit_kevents.h @@ -610,6 +610,7 @@ #define AUE_CAP_FCNTLS_GET 43206 /* TrustedBSD. */ #define AUE_BINDAT 43207 /* TrustedBSD. */ #define AUE_CONNECTAT 43208 /* TrustedBSD. */ +#define AUE_CHFLAGSAT 43209 /* FreeBSD-specific. */ /* * Darwin BSM uses a number of AUE_O_* definitions, which are aliased to the --- sys/compat/freebsd32/syscalls.master.orig +++ sys/compat/freebsd32/syscalls.master @@ -1019,3 +1019,5 @@ int namelen); } 539 AUE_CONNECTAT NOPROTO { int connectat(int fd, int s, caddr_t name, \ int namelen); } +540 AUE_CHFLAGSAT NOPROTO { int chflagsat(int fd, char *path, \ + int flags, int atflag); } --- sys/kern/capabilities.conf.orig +++ sys/kern/capabilities.conf @@ -445,6 +445,7 @@ ## ## Operations relative to directory capabilities. ## +chflagsat faccessat fchmodat fchownat --- sys/kern/syscalls.master.orig +++ sys/kern/syscalls.master @@ -969,5 +969,7 @@ int namelen); } 539 AUE_CONNECTAT STD { int connectat(int fd, int s, caddr_t name, \ int namelen); } +540 AUE_CHFLAGSAT STD { int chflagsat(int fd, char *path, \ + int flags, int atflag); } ; Please copy any additions and changes to the following compatability tables: ; sys/compat/freebsd32/syscalls.master --- sys/kern/vfs_syscalls.c.orig +++ sys/kern/vfs_syscalls.c @@ -101,6 +101,10 @@ static int chroot_refuse_vdir_fds(struct filedesc *fdp); static int getutimes(const struct timeval *, enum uio_seg, struct timespec *); +static int kern_chflags(struct thread *td, const char *path, + enum uio_seg pathseg, int flags); +static int kern_chflagsat(struct thread *td, int fd, const char *path, + enum uio_seg pathseg, int flags, int atflag); static int setfflags(struct thread *td, struct vnode *, int); static int setutimes(struct thread *td, struct vnode *, const struct timespec *, int, int); @@ -2669,17 +2673,38 @@ int flags; } */ *uap; { - int error; - struct nameidata nd; + + return (kern_chflags(td, uap->path, UIO_USERSPACE, uap->flags)); +} + +#ifndef _SYS_SYSPROTO_H_ +struct chflagsat_args { + int fd; + char *path; + int flags; + int atflag; +} +#endif +int +sys_chflagsat(struct thread *td, struct chflagsat_args *uap) +{ + int fd = uap->fd; + char *path = uap->path; + int flags = uap->flags; + int atflag = uap->atflag; + + if (atflag & ~AT_SYMLINK_NOFOLLOW) + return (EINVAL); + + return (kern_chflagsat(td, fd, path, UIO_USERSPACE, flags, atflag)); +} + +static int +kern_chflags(struct thread *td, const char *path, enum uio_seg pathseg, + int flags) +{ - AUDIT_ARG_FFLAGS(uap->flags); - NDINIT(&nd, LOOKUP, FOLLOW | AUDITVNODE1, UIO_USERSPACE, uap->path, td); - if ((error = namei(&nd)) != 0) - return (error); - NDFREE(&nd, NDF_ONLY_PNBUF); - error = setfflags(td, nd.ni_vp, uap->flags); - vrele(nd.ni_vp); - return (error); + return (kern_chflagsat(td, AT_FDCWD, path, pathseg, flags, 0)); } /* @@ -2689,20 +2714,30 @@ sys_lchflags(td, uap) struct thread *td; register struct lchflags_args /* { - char *path; + const char *path; int flags; } */ *uap; { - int error; + + return (kern_chflagsat(td, AT_FDCWD, uap->path, UIO_USERSPACE, + uap->flags, AT_SYMLINK_NOFOLLOW)); +} + +static int +kern_chflagsat(struct thread *td, int fd, const char *path, + enum uio_seg pathseg, int flags, int atflag) +{ struct nameidata nd; + int error, follow; - AUDIT_ARG_FFLAGS(uap->flags); - NDINIT(&nd, LOOKUP, NOFOLLOW | AUDITVNODE1, UIO_USERSPACE, uap->path, - td); + AUDIT_ARG_FFLAGS(flags); + follow = (atflag & AT_SYMLINK_NOFOLLOW) ? NOFOLLOW : FOLLOW; + NDINIT_ATRIGHTS(&nd, LOOKUP, follow | AUDITVNODE1, pathseg, path, fd, + CAP_FCHFLAGS, td); if ((error = namei(&nd)) != 0) return (error); NDFREE(&nd, NDF_ONLY_PNBUF); - error = setfflags(td, nd.ni_vp, uap->flags); + error = setfflags(td, nd.ni_vp, flags); vrele(nd.ni_vp); return (error); } --- sys/sys/capability.h.orig +++ sys/sys/capability.h @@ -102,6 +102,7 @@ /* VFS methods. */ #define CAP_FCHDIR 0x0000000000000200ULL #define CAP_FCHFLAGS 0x0000000000000100ULL +#define CAP_CHFLAGSAT CAP_FCHFLAGS #define CAP_FCHMOD 0x0000000000000400ULL #define CAP_FCHMODAT CAP_FCHMOD #define CAP_FCHOWN 0x0000000000000800ULL --- sys/sys/stat.h.orig +++ sys/sys/stat.h @@ -297,6 +297,7 @@ int chmod(const char *, mode_t); #if __BSD_VISIBLE int fchflags(int, unsigned long); +int chflagsat(int, const char *, int, int); #endif #if __POSIX_VISIBLE >= 200112 int fchmod(int, mode_t); --- tools/regression/pjdfstest/Makefile.orig +++ tools/regression/pjdfstest/Makefile @@ -6,7 +6,7 @@ @OSTYPE=`uname`; \ CFLAGS=-D__OS_$${OSTYPE}__; \ if [ $$OSTYPE = "FreeBSD" ]; then \ - CFLAGS="$$CFLAGS -DHAS_LCHMOD -DHAS_CHFLAGS -DHAS_FCHFLAGS -DHAS_LCHFLAGS -DHAS_FREEBSD_ACL -DHAS_BINDAT -DHAS_CONNECTAT"; \ + CFLAGS="$$CFLAGS -DHAS_LCHMOD -DHAS_CHFLAGS -DHAS_FCHFLAGS -DHAS_CHFLAGSAT -DHAS_LCHFLAGS -DHAS_FREEBSD_ACL -DHAS_BINDAT -DHAS_CONNECTAT"; \ elif [ $$OSTYPE = "SunOS" ]; then \ CFLAGS="$$CFLAGS -DHAS_TRUNCATE64 -DHAS_STAT64"; \ CFLAGS="$$CFLAGS -lsocket"; \ --- tools/regression/pjdfstest/pjdfstest.c.orig +++ tools/regression/pjdfstest/pjdfstest.c @@ -105,6 +105,9 @@ #ifdef HAS_FCHFLAGS ACTION_FCHFLAGS, #endif +#ifdef HAS_CHFLAGSAT + ACTION_CHFLAGSAT, +#endif #ifdef HAS_LCHFLAGS ACTION_LCHFLAGS, #endif @@ -183,6 +186,9 @@ #ifdef HAS_FCHFLAGS { "fchflags", ACTION_FCHFLAGS, { TYPE_DESCRIPTOR, TYPE_STRING, TYPE_NONE } }, #endif +#ifdef HAS_CHFLAGSAT + { "chflagsat", ACTION_CHFLAGSAT, { TYPE_DESCRIPTOR, TYPE_STRING, TYPE_STRING, TYPE_STRING, TYPE_NONE } }, +#endif #ifdef HAS_LCHFLAGS { "lchflags", ACTION_LCHFLAGS, { TYPE_STRING, TYPE_STRING, TYPE_NONE } }, #endif @@ -306,6 +312,11 @@ { 0, NULL } }; +static struct flag chflagsat_flags[] = { + { AT_SYMLINK_NOFOLLOW, "AT_SYMLINK_NOFOLLOW" }, + { 0, NULL } +}; + static struct flag fchmodat_flags[] = { { AT_SYMLINK_NOFOLLOW, "AT_SYMLINK_NOFOLLOW" }, { 0, NULL } @@ -829,6 +840,13 @@ (unsigned long)str2flags(chflags_flags, STR(1))); break; #endif +#ifdef HAS_CHFLAGSAT + case ACTION_CHFLAGSAT: + rval = chflagsat(NUM(0), STR(1), + (int)str2flags(chflags_flags, STR(2)), + (int)str2flags(chflagsat_flags, STR(3))); + break; +#endif #ifdef HAS_LCHFLAGS case ACTION_LCHFLAGS: rval = lchflags(STR(0), (int)str2flags(chflags_flags, STR(1))); --- tools/regression/security/cap_test/cap_test_capabilities.c.orig +++ tools/regression/security/cap_test/cap_test_capabilities.c @@ -293,14 +293,12 @@ CHECK_RESULT(fchflags, CAP_FCHFLAGS, ret == 0 || (is_nfs && errno == EOPNOTSUPP)); -#ifdef TODO /* No such syscalls yet. */ - ret = openat(dirfd, "cap_fchflagsat", O_CREAT, 0600); + ret = openat(dirfd, "cap_chflagsat", O_CREAT, 0600); CHECK(ret >= 0); CHECK(close(ret) == 0); - ret = fchflagsat(dfd_cap, "cap_fchflagsat", UF_NODUMP, 0); - CHECK_RESULT(fchflagsat, CAP_FCHFLAGSAT | CAP_LOOKUP, ret == 0); - CHECK(unlinkat(dirfd, "cap_fchflagsat", 0) == 0); -#endif + ret = chflagsat(dfd_cap, "cap_chflagsat", UF_NODUMP, 0); + CHECK_RESULT(chflagsat, CAP_CHFLAGSAT | CAP_LOOKUP, ret == 0); + CHECK(unlinkat(dirfd, "cap_chflagsat", 0) == 0); ret = fchown(fd_cap, -1, -1); CHECK_RESULT(fchown, CAP_FCHOWN, ret == 0);