Subject: NetBSD Security Advisory 2001-002
To: None <netbsd-announce@netbsd.org>
From: None <security-officer@netbsd.org>
List: tech-security
Date: 02/16/2001 17:33:55
auscert@auscert.org.au
Subject: NetBSD Security Advisory 2001-002
Organisation: The NetBSD Foundation, Inc.
Reply-to: security-officer@netbsd.org


-----BEGIN PGP SIGNED MESSAGE-----


                 NetBSD Security Advisory 2001-002
                 =================================

Topic:		Vulnerability in x86 USER_LDT validation.
Version:	All versions of NetBSD, on the i386 platform ONLY.
Severity:	Local users may execute code with system priveleges
Fixed:		NetBSD-current:    January 16, 2001
		NetBSD-1.5 branch: January 17, 2001
		NetBSD-1.4 branch: January 17, 2001

Abstract
========

A subtle bug in validation of user-supplied arguments to a syscall
can allow allow user applications on the i386 platform to transfer
control to arbitrary addresses in kernel memory, bypassing normal
system protections.

This problem is only present on the i386 platform, and only when the
USER_LDT kernel configuration option is enabled at compile
time. Unfortunately, this option is present in the GENERIC and
GENERIC_LAPTOP kernel configurations shipped with NetBSD releases, as
well as several examples, so many users will be affected.

The problem stems from the complexity of the x86 processor
architecture memory protection system, and other systems are known to
be affected. This includes systems with code derived from NetBSD as
well as other independently-written systems, indicating that the same
mistake may have been repeated elsewhere as well.


Technical Details
=================

The Intel i386 architecture supports a complex and confusing
protection and segmentation model relying on rings, segment selectors,
segment descriptors, gates, descriptor tables and the like.

The Local Descriptor Table (LDT) is usually a per-process segment;
most unix OS's provide system calls allowing application processes to
add new segments to the LDT.  In NetBSD, this option is enabled with
"options USER_LDT" in the kernel configuration file, and is generally
enabled for the WINE Windows emulator.

One segment type is a "call gate", which allows code in outer rings to
make calls to code in inner, more priviledged rings.  Call gates are
one of the three (or more?) different ways to implement system calls
on the x86.  The segment descriptor in the LDT for a call gate
contains a segment selector and offset; when a program running in an
outer ring executes a "ljmp" or "lcall" instruction which specifies
the segment selector of a call gate, control is transferred to the
specified offset of the specified code segment.  If the gate target is
an inner-ring code segment, a ring transition occurs; therefore it is
vital for code which creates call gate descriptors to validate that
the target segment and offset of the gate is appropriate.

While reviewing code in NetBSD, it was discovered that the NetBSD 
"i386_set_ldt" syscall did not do appropriate validation of call gate
targets; in short, it's possible to create a call gate in the LDT
which transfers control to an arbitrary address in the kernel.

The problem affects other Operating Systems also:

 * OpenBSD has the same bug, in code inherited directly from NetBSD.
 * FreeBSD also once had the bug, but it was eliminated a couple of
   years ago as part of a change to disable setting call gates.
 * Sun Solaris/x86 has the same bug, in a different implementation of
   a similar mechanism.
 * The problem is not necessarily limited to unix; any OS for the x86
   which allows an unprivileged program to create gate descriptors in its
   LDT could have this bug.  

A common misunderstanding of how gate descriptors work may result in
the programmer believing they've defended against this attack (by
checking the gate's DPL) without having done so (you need to check the
DPL of the code segment that the gate targets).

Note that this behaviour is not restricted to Intel processors; the bug
applies to implementations of the x86 architecture by other
manufacturers as well.


Solutions and Workarounds
=========================

Systems running NetBSD/i386 with the USER_LDT kernel option enabled
are vulnerable.  This includes users running the GENERIC kernel
configurations distributed with releases, or users who have built
their own kernel configurations based on GENERIC and not removed the
USER_LDT option.

The most effective and simplest workaround, for users not requiring
this option for Wine or similar emulators, is to build a kernel
without "options USER_LDT".

For users who need this option enabled, kernel sources must be updated
and a new kernel built and installed.

Systems running releases older than NetBSD 1.4 should be upgraded to
NetBSD 1.4.3 before applying the fixes described here.

Systems running NetBSD-current dated from before 2001-01-17 should be
upgraded to NetBSD-current dated 2001-01-17 or later.

Systems running NetBSD-release-1-4 or NetBSD-release-1-5 dated from
before 2001-01-18 should be upgraded to 2001-01-18 later.

The following patch to /sys/arch/i386/i386/sys_machdep.c should be
applied before rebuilding a new kernel with USER_LDT enabled.  This
patch can be applied (with offset differences) to NetBSD-1.4.x,
NetBSD-1.5 or NetBSD-current kernel sources.


Index: sys_machdep.c
===================================================================
RCS file: /cvsroot/syssrc/sys/arch/i386/i386/sys_machdep.c,v
retrieving revision 1.54
retrieving revision 1.55
diff -c -r1.54 -r1.55
*** sys_machdep.c	2001/01/16 01:50:36	1.54
- --- sys_machdep.c	2001/01/16 23:32:21	1.55
***************
*** 222,227 ****
- --- 222,238 ----
  			break;
  		case SDT_SYS286CGT:
  		case SDT_SYS386CGT:
+ 			/*
+ 			 * Only allow call gates targeting a segment
+ 			 * in the LDT or a user segment in the fixed
+ 			 * part of the gdt.  Segments in the LDT are
+ 			 * constrained (below) to be user segments.
+ 			 */
+ 			if (desc.gd.gd_p != 0 && !ISLDT(desc.gd.gd_selector) &&
+ 			    ((IDXSEL(desc.gd.gd_selector) >= NGDT) ||
+ 			     (gdt[IDXSEL(desc.gd.gd_selector)].sd.sd_dpl !=
+ 				 SEL_UPL)))
+ 				return (EACCES);
  			/* Can't replace in use descriptor with gate. */
  			if (n == fsslot || n == gsslot)
  				return (EBUSY);


For all NetBSD versions:

  * either apply the above patch to your kernel source tree using the
    patch(1) command, or

  * edit your kernel configuration file to remove or comment out
    "options USER_LDT", save, and rerun config(8)

Then rebuild, install the new kernel, and reboot. For more information
on how to do this, see:

    http://www.netbsd.org/Documentation/kernel/#building_a_kernel 


Thanks To
=========

Thanks to Bill Sommerfeld for discovery, analysis and fixing the
problem, and to Charles Hannum for additional discussion.


Revision History
================

	2000-02-16	Initial Release


More Information
================

Information about NetBSD and NetBSD security can be found at
http://www.NetBSD.ORG/ and http://www.NetBSD.ORG/Security/.


Copyright 2001, The NetBSD Foundation, Inc.  All Rights Reserved.

$NetBSD: NetBSD-SA2001-002.txt,v 1.4 2001/02/16 04:43:40 dan Exp $

-----BEGIN PGP SIGNATURE-----
Version: 2.6.3ia
Charset: noconv

iQCVAwUBOoy2Yj5Ru2/4N2IFAQF9fgQAoWYyT1U+BoduaNM24a0Z258erw2unXfs
vclZqO+pBjwoz4nciAr+oisB6Wh7r67l+pLWdba1tQBVMe8kWT+ui7cutUjeycI8
6hEEiENDRgC1KUM6F7cuQ/4TL7ML03Y8TR3CPjfpRuOYzQJM+vXx8IQfzOTDg2B5
G1WQC3ZDdWg=
=clr4
-----END PGP SIGNATURE-----