PATCH(es): root coredump fix, O_NOFOLLOW, safer coredump

Chris Wedgwood (chris@cybernet.co.nz)
Wed, 21 Oct 1998 21:04:59 +1300


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

Linus,

Here are three patch sets for you to look at - all against
pre-2.1.126-2. This is the same patch set I sent earlier, broken into
three parts this time, and the error for O_NOFOLLOW has changed from
EACCES to ELOOP.

pre-2.1.126-2-root-coredump-fix.patch

This is Alexander Kjeldaas <astor@guardian.no> core dump as
root fix.


pre-2.1.126-2-no-follow.patch

This adds O_NOFOLLOW to the kernel (and hence libc5). I'm not
sure this is the best way to do this, but it works for me so
far.

The error returned is ELOOP when someone specifies O_NOFOLLOW
on a target that is a symlink - I chose this because this is
what FreeBSD uses.

I'm not sure if the arch. specific stuff is OK, it might be
one of the other ABIs already has O_NOFOLLOW, in which case
it should probably use that.

pre-2.1.126-2-safer-coredump.patch

This requires the previous patch. core dumps are created
O_NOFOLLOW and once opened nlink is check to make sure we
don't have hard link to the core file (eg. ln /etc/passwd
core) that might do bad things.

-cw

--FCuugMFkClbJLl1L
Content-Type: text/plain; charset=us-ascii
Content-Description: Alexander Kjeldaas <astor@guardian.no> core dump as root fix
Content-Disposition: attachment; filename="pre-2.1.126-2-root-coredump-fix.patch"

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/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/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);
}
}

--FCuugMFkClbJLl1L
Content-Type: text/plain; charset=us-ascii
Content-Description: Adds O_NOFOLLOW to vfs
Content-Disposition: attachment; filename="pre-2.1.126-2-no-follow.patch"

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 20:38:02 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 = -ELOOP;
+ goto exit;
+ }
+
acc_mode = ACC_MODE(flag);
if (flag & O_CREAT) {
struct dentry *dir;
diff -ur linux/fs/open.c /usr/src/linux-2.1.x/fs/open.c
--- linux/fs/open.c Wed Oct 21 13:24:09 1998
+++ /usr/src/linux-2.1.x/fs/open.c Wed Oct 21 20:13:38 1998
@@ -665,7 +665,7 @@
if (error)
goto cleanup_all;
}
- f->f_flags &= ~(O_CREAT | O_EXCL | O_NOCTTY | O_TRUNC);
+ f->f_flags &= ~(O_CREAT | O_EXCL | O_NOCTTY | O_TRUNC | O_NOFOLLOW);

fd_install(fd, f);
return 0;
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 */
Only in /usr/src/linux-2.1.x/include/asm-alpha: fcntl.h~
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 */
Only in /usr/src/linux-2.1.x/include/asm-arm: fcntl.h~
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 */
Only in /usr/src/linux-2.1.x/include/asm-i386: fcntl.h.orig
Only in /usr/src/linux-2.1.x/include/asm-i386: fcntl.h~
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 */
Only in /usr/src/linux-2.1.x/include/asm-m68k: fcntl.h~
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

Only in /usr/src/linux-2.1.x/include/asm-mips: fcntl.h~
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 */
Only in /usr/src/linux-2.1.x/include/asm-ppc: fcntl.h~
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 */
Only in /usr/src/linux-2.1.x/include/asm-sparc: fcntl.h~
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 */

--FCuugMFkClbJLl1L
Content-Type: text/plain; charset=us-ascii
Content-Description: Hopefully stops coredumps from clobbering stuff
Content-Disposition: attachment; filename="pre-2.1.126-2-safer-coredump.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)

--FCuugMFkClbJLl1L--

-
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/