PATCH: pre-2.1.126-2 core dump as root and O_NOFOLLOW (was "Linux 2.1.125 doesn't dump core on SIGSE

Chris Wedgwood (chris@cybernet.co.nz)
Wed, 21 Oct 1998 13:41:51 +1300


--+HP7ph2BbKc20aGI
Content-Type: text/plain; charset=us-ascii

On Wed, Oct 21, 1998 at 01:26:32AM +0100, David Woodhouse wrote:

> It would appear so - the only operations we're affecting are those with
> O_NOFOLLOW set, and those currently total zero, as we've only just defined it.
>
> Second version (i386 only for now)...
>
> --- linux/fs/namei.c.nofollow Wed Oct 21 00:30:39 1998
> +++ linux/fs/namei.c Wed Oct 21 01:15:13 1998
> @@ -564,7 +564,8 @@
> * is the same scheme used by, for example, Solaris 2.5.1. --KAB
> */
> dentry = lookup_dentry(pathname, NULL,
> - (flag & (O_CREAT|O_EXCL)) != (O_CREAT|O_EXCL));
> + (flag & (O_CREAT|O_EXCL)) != (O_CREAT|O_EXCL)
> + && !(flag & O_NOFOLLOW));

[...]

What are thes patches against? pre-2.1.126-2 looks a little different.

Here are my patches (against pre-2.1.126-2), which should work for
all architectures (untested).

Summary of changes

- Alexander Kjeldaas <astor@guardian.no> `core dump as root' fix

- Added O_NOFOLLOW semantics to VFS, disallow open on O_FOLLOW
and symlink (bad idea?)

- core dump no uses O_NOFOLLOW

- don't dump on multiple linked files

In truth, mose of this is untested properly, but `works for me'.

-cw

--+HP7ph2BbKc20aGI
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename="pre-2.1.126-2-O_NOFOLLOW.patch"

diff -ur linux/fs/binfmt_elf.c /usr/src/linux-2.1.x/fs/binfmt_elf.c
--- linux/fs/binfmt_elf.c Wed Oct 21 13:24:56 1998
+++ /usr/src/linux-2.1.x/fs/binfmt_elf.c Wed Oct 21 13:16:29 1998
@@ -1118,12 +1118,16 @@
#else
corefile[4] = '\0';
#endif
- dentry = open_namei(corefile, O_CREAT | 2 | O_TRUNC, 0600);
+ dentry = open_namei(corefile, O_CREAT | 2 | O_TRUNC | O_NOFOLLOW, 0600);
if (IS_ERR(dentry)) {
dentry = NULL;
goto end_coredump;
}
inode = dentry->d_inode;
+
+ if(inode->i_nlink > 1)
+ goto end_coredump; /* multiple links - don't dump */
+
if (!S_ISREG(inode->i_mode))
goto end_coredump;
if (!inode->i_op || !inode->i_op->default_file_ops)
diff -ur linux/fs/exec.c /usr/src/linux-2.1.x/fs/exec.c
--- linux/fs/exec.c Wed Oct 21 13:24:22 1998
+++ /usr/src/linux-2.1.x/fs/exec.c Tue Oct 20 13:13:43 1998
@@ -702,17 +702,17 @@

void compute_creds(struct linux_binprm *bprm)
{
+ int new_permitted = cap_t(bprm->cap_permitted) |
+ (cap_t(bprm->cap_inheritable) &
+ cap_t(current->cap_inheritable));
+
/* For init, we want to retain the capabilities set
* in the init_task struct. Thus we skip the usual
* capability rules */
if (current->pid != 1) {
- int new_permitted = bprm->cap_permitted.cap |
- (bprm->cap_inheritable.cap &
- current->cap_inheritable.cap);
-
- current->cap_permitted.cap = new_permitted;
- current->cap_effective.cap = new_permitted &
- bprm->cap_effective.cap;
+ cap_t(current->cap_permitted) = new_permitted;
+ cap_t(current->cap_effective) = new_permitted &
+ cap_t(bprm->cap_effective);
}

/* AUD: Audit candidate if current->cap_effective is set */
@@ -720,7 +720,7 @@
current->suid = current->euid = current->fsuid = bprm->e_uid;
current->sgid = current->egid = current->fsgid = bprm->e_gid;
if (current->euid != current->uid || current->egid != current->gid ||
- !cap_isclear(current->cap_permitted))
+ !cap_issubset(new_permitted, current->cap_permitted))
current->dumpable = 0;
}

diff -ur linux/fs/namei.c /usr/src/linux-2.1.x/fs/namei.c
--- linux/fs/namei.c Wed Oct 21 13:26:56 1998
+++ /usr/src/linux-2.1.x/fs/namei.c Wed Oct 21 13:00:14 1998
@@ -544,8 +544,23 @@
*/
#define no_follow(f) (((f) & (O_CREAT|O_EXCL)) == (O_CREAT|O_EXCL))
#define opendir(f) ((f) & O_DIRECTORY)
-#define lookup_flags(f) \
- (no_follow(f) ? 0 : opendir(f) ? (LOOKUP_FOLLOW | LOOKUP_DIRECTORY) : LOOKUP_FOLLOW)
+
+
+/* my brain didn't grok the macro initially... -cw */
+
+static inline int mk_lkupflags(int f)
+{
+ if((f) & O_NOFOLLOW)
+ return 0;
+
+ if(no_follow(f))
+ return 0;
+
+ if(opendir(f))
+ return LOOKUP_FOLLOW | LOOKUP_DIRECTORY;
+
+ return LOOKUP_FOLLOW;
+}

/*
* open_namei()
@@ -569,10 +584,15 @@
mode &= S_IALLUGO & ~current->fs->umask;
mode |= S_IFREG;

- dentry = lookup_dentry(pathname, NULL, lookup_flags(flag));
+ dentry = lookup_dentry(pathname,NULL,mk_lkupflags(flag));
if (IS_ERR(dentry))
return dentry;

+ if(dentry->d_inode && S_ISLNK(dentry->d_inode->i_mode)){
+ error = -EACCES;
+ goto exit;
+ }
+
acc_mode = ACC_MODE(flag);
if (flag & O_CREAT) {
struct dentry *dir;
diff -ur linux/fs/proc/array.c /usr/src/linux-2.1.x/fs/proc/array.c
--- linux/fs/proc/array.c Wed Oct 21 13:25:11 1998
+++ /usr/src/linux-2.1.x/fs/proc/array.c Tue Oct 20 13:13:44 1998
@@ -799,9 +799,9 @@
return buffer + sprintf(buffer, "CapInh:\t%016x\n"
"CapPrm:\t%016x\n"
"CapEff:\t%016x\n",
- p->cap_inheritable.cap,
- p->cap_permitted.cap,
- p->cap_effective.cap);
+ cap_t(p->cap_inheritable),
+ cap_t(p->cap_permitted),
+ cap_t(p->cap_effective));
}


diff -ur linux/include/asm-alpha/fcntl.h /usr/src/linux-2.1.x/include/asm-alpha/fcntl.h
--- linux/include/asm-alpha/fcntl.h Wed Oct 21 13:26:56 1998
+++ /usr/src/linux-2.1.x/include/asm-alpha/fcntl.h Wed Oct 21 13:30:55 1998
@@ -19,6 +19,7 @@
#define FASYNC 020000 /* fcntl, for BSD compatibility */
#define O_DIRECT 040000 /* direct disk access - should check with OSF/1 */
#define O_DIRECTORY 0100000 /* must be a directory */
+#define O_NOFOLLOW 0200000 /* don't follow links */

#define F_DUPFD 0 /* dup */
#define F_GETFD 1 /* get f_flags */
diff -ur linux/include/asm-arm/fcntl.h /usr/src/linux-2.1.x/include/asm-arm/fcntl.h
--- linux/include/asm-arm/fcntl.h Wed Oct 21 13:26:56 1998
+++ /usr/src/linux-2.1.x/include/asm-arm/fcntl.h Wed Oct 21 13:31:07 1998
@@ -17,6 +17,7 @@
#define O_SYNC 010000
#define FASYNC 020000 /* fcntl, for BSD compatibility */
#define O_DIRECTORY 040000 /* must be a directory */
+#define O_NOFOLLOW 080000 /* don't follow links */

#define F_DUPFD 0 /* dup */
#define F_GETFD 1 /* get f_flags */
diff -ur linux/include/asm-i386/fcntl.h /usr/src/linux-2.1.x/include/asm-i386/fcntl.h
--- linux/include/asm-i386/fcntl.h Wed Oct 21 13:26:56 1998
+++ /usr/src/linux-2.1.x/include/asm-i386/fcntl.h Wed Oct 21 12:16:39 1998
@@ -19,6 +19,7 @@
#define O_DIRECT 040000 /* direct disk access hint - currently ignored */
#define O_LARGEFILE 0100000
#define O_DIRECTORY 0200000 /* must be a directory */
+#define O_NOFOLLOW 0400000 /* don't follow links */

#define F_DUPFD 0 /* dup */
#define F_GETFD 1 /* get f_flags */
diff -ur linux/include/asm-m68k/fcntl.h /usr/src/linux-2.1.x/include/asm-m68k/fcntl.h
--- linux/include/asm-m68k/fcntl.h Wed Oct 21 13:26:56 1998
+++ /usr/src/linux-2.1.x/include/asm-m68k/fcntl.h Wed Oct 21 13:31:17 1998
@@ -17,6 +17,7 @@
#define O_SYNC 010000
#define FASYNC 020000 /* fcntl, for BSD compatibility */
#define O_DIRECTORY 040000 /* must be a directory */
+#define O_NOFOLLOW 080000 /* don't follow links */

#define F_DUPFD 0 /* dup */
#define F_GETFD 1 /* get f_flags */
diff -ur linux/include/asm-mips/fcntl.h /usr/src/linux-2.1.x/include/asm-mips/fcntl.h
--- linux/include/asm-mips/fcntl.h Wed Oct 21 13:26:56 1998
+++ /usr/src/linux-2.1.x/include/asm-mips/fcntl.h Wed Oct 21 13:31:26 1998
@@ -16,6 +16,7 @@
#define O_NOCTTY 0x0800 /* not fcntl */
#define FASYNC 0x1000 /* fcntl, for BSD compatibility */
#define O_DIRECTORY 0x2000 /* must be a directory */
+#define O_NOFOLLOW 0x4000 /* don't follow links */

#define O_NDELAY O_NONBLOCK

diff -ur linux/include/asm-ppc/fcntl.h /usr/src/linux-2.1.x/include/asm-ppc/fcntl.h
--- linux/include/asm-ppc/fcntl.h Wed Oct 21 13:26:56 1998
+++ /usr/src/linux-2.1.x/include/asm-ppc/fcntl.h Wed Oct 21 13:32:06 1998
@@ -17,6 +17,7 @@
#define O_SYNC 010000
#define FASYNC 020000 /* fcntl, for BSD compatibility */
#define O_DIRECTORY 040000 /* must be a directory */
+#define O_NOFOLLOW 080000 /* don't follow links */

#define F_DUPFD 0 /* dup */
#define F_GETFD 1 /* get f_flags */
diff -ur linux/include/asm-sparc/fcntl.h /usr/src/linux-2.1.x/include/asm-sparc/fcntl.h
--- linux/include/asm-sparc/fcntl.h Wed Oct 21 13:26:56 1998
+++ /usr/src/linux-2.1.x/include/asm-sparc/fcntl.h Wed Oct 21 13:32:14 1998
@@ -18,6 +18,7 @@
#define O_NDELAY (0x0004 | O_NONBLOCK)
#define O_NOCTTY 0x8000 /* not fcntl */
#define O_DIRECTORY 0x10000 /* must be a directory */
+#define O_NOFOLLOW 0x20000 /* don't follow links */

#define F_DUPFD 0 /* dup */
#define F_GETFD 1 /* get f_flags */
diff -ur linux/include/asm-sparc64/fcntl.h /usr/src/linux-2.1.x/include/asm-sparc64/fcntl.h
--- linux/include/asm-sparc64/fcntl.h Wed Oct 21 13:26:56 1998
+++ /usr/src/linux-2.1.x/include/asm-sparc64/fcntl.h Wed Oct 21 13:32:31 1998
@@ -18,6 +18,7 @@
#define O_NONBLOCK 0x4000
#define O_NOCTTY 0x8000 /* not fcntl */
#define O_DIRECTORY 0x10000 /* must be a directory */
+#define O_NOFOLLOW 0x20000 /* don't follow links */

#define F_DUPFD 0 /* dup */
#define F_GETFD 1 /* get f_flags */
diff -ur linux/include/linux/capability.h /usr/src/linux-2.1.x/include/linux/capability.h
--- linux/include/linux/capability.h Wed Oct 21 13:22:09 1998
+++ /usr/src/linux-2.1.x/include/linux/capability.h Wed Oct 21 12:16:44 1998
@@ -38,9 +38,19 @@

#ifdef __KERNEL__

+/* #define STRICT_CAP_T_TYPECHECKS */
+
+#ifdef STRICT_CAP_T_TYPECHECKS
+
typedef struct kernel_cap_struct {
__u32 cap;
} kernel_cap_t;
+
+#else
+
+typedef __u32 kernel_cap_t;
+
+#endif

#define _USER_CAP_HEADER_SIZE (2*sizeof(__u32))
#define _KERNEL_CAP_T_SIZE (sizeof(kernel_cap_t))
@@ -259,51 +269,63 @@
/*
* Internal kernel functions only
*/
+
+#ifdef STRICT_CAP_T_TYPECHECKS
+
+#define to_cap_t(x) { x }
+#define cap_t(x) (x).cap
+
+#else
+
+#define to_cap_t(x) (x)
+#define cap_t(x) (x)
+
+#endif

-#define CAP_EMPTY_SET { 0 }
-#define CAP_FULL_SET { ~0 }
-#define CAP_INIT_EFF_SET { ~0 & ~CAP_TO_MASK(CAP_SETPCAP) }
-#define CAP_INIT_INH_SET { ~0 & ~CAP_TO_MASK(CAP_SETPCAP) }
+#define CAP_EMPTY_SET to_cap_t(0)
+#define CAP_FULL_SET to_cap_t(~0)
+#define CAP_INIT_EFF_SET to_cap_t(~0 & ~CAP_TO_MASK(CAP_SETPCAP))
+#define CAP_INIT_INH_SET to_cap_t(~0 & ~CAP_TO_MASK(CAP_SETPCAP))

#define CAP_TO_MASK(x) (1 << (x))
-#define cap_raise(c, flag) ((c).cap |= CAP_TO_MASK(flag))
-#define cap_lower(c, flag) ((c).cap &= ~CAP_TO_MASK(flag))
-#define cap_raised(c, flag) ((c).cap & CAP_TO_MASK(flag))
+#define cap_raise(c, flag) (cap_t(c) |= CAP_TO_MASK(flag))
+#define cap_lower(c, flag) (cap_t(c) &= ~CAP_TO_MASK(flag))
+#define cap_raised(c, flag) (cap_t(c) & CAP_TO_MASK(flag))

static inline kernel_cap_t cap_combine(kernel_cap_t a, kernel_cap_t b)
{
kernel_cap_t dest;
- dest.cap = a.cap | b.cap;
+ cap_t(dest) = cap_t(a) | cap_t(b);
return dest;
}

static inline kernel_cap_t cap_intersect(kernel_cap_t a, kernel_cap_t b)
{
kernel_cap_t dest;
- dest.cap = a.cap & b.cap;
+ cap_t(dest) = cap_t(a) & cap_t(b);
return dest;
}

static inline kernel_cap_t cap_drop(kernel_cap_t a, kernel_cap_t drop)
{
kernel_cap_t dest;
- dest.cap = a.cap & ~drop.cap;
+ cap_t(dest) = cap_t(a) & ~cap_t(drop);
return dest;
}

static inline kernel_cap_t cap_invert(kernel_cap_t c)
{
kernel_cap_t dest;
- dest.cap = ~c.cap;
+ cap_t(dest) = ~cap_t(c);
return dest;
}

-#define cap_isclear(c) (!(c).cap)
-#define cap_issubset(a,set) (!((a).cap & ~(set).cap))
+#define cap_isclear(c) (!cap_t(c))
+#define cap_issubset(a,set) (!(cap_t(a) & ~cap_t(set)))

-#define cap_clear(c) do { (c).cap = 0; } while(0)
-#define cap_set_full(c) do { (c).cap = ~0; } while(0)
-#define cap_mask(c,mask) do { (c).cap &= (mask).cap; } while(0)
+#define cap_clear(c) do { cap_t(c) = 0; } while(0)
+#define cap_set_full(c) do { cap_t(c) = ~0; } while(0)
+#define cap_mask(c,mask) do { cap_t(c) &= cap_t(mask); } while(0)

#define cap_is_fs_cap(c) (CAP_TO_MASK(c) & CAP_FS_MASK)

diff -ur linux/kernel/capability.c /usr/src/linux-2.1.x/kernel/capability.c
--- linux/kernel/capability.c Wed Oct 21 13:24:10 1998
+++ /usr/src/linux-2.1.x/kernel/capability.c Tue Oct 20 13:13:44 1998
@@ -61,9 +61,9 @@
}

if (!error) {
- data.permitted = target->cap_permitted.cap;
- data.inheritable = target->cap_inheritable.cap;
- data.effective = target->cap_effective.cap;
+ data.permitted = cap_t(target->cap_permitted);
+ data.inheritable = cap_t(target->cap_inheritable);
+ data.effective = cap_t(target->cap_effective);
}

if (target != current)
diff -ur linux/kernel/sys.c /usr/src/linux-2.1.x/kernel/sys.c
--- linux/kernel/sys.c Wed Oct 21 13:23:57 1998
+++ /usr/src/linux-2.1.x/kernel/sys.c Tue Oct 20 13:13:44 1998
@@ -586,11 +586,11 @@

if (!issecure(SECURE_NO_SETUID_FIXUP)) {
if (old_fsuid == 0 && current->fsuid != 0) {
- current->cap_effective.cap &= ~CAP_FS_MASK;
+ cap_t(current->cap_effective) &= ~CAP_FS_MASK;
}
if (old_fsuid != 0 && current->fsuid == 0) {
- current->cap_effective.cap |=
- (current->cap_permitted.cap & CAP_FS_MASK);
+ cap_t(current->cap_effective) |=
+ (cap_t(current->cap_permitted) & CAP_FS_MASK);
}
}

--+HP7ph2BbKc20aGI--

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.rutgers.edu
Please read the FAQ at http://www.tux.org/lkml/