Many of the checks that will be required have been added to the XFree X server by the X11 Security Extension. While the Security Extension does not fully label both subjects and objects, it does do a certain amount of labeling, and defines an API that is widely used in the X server for the checks.
Unfortunately, the Security Extension does not appear to be fully implemented at the time of this report. Only four types of access (actions) are recognized, one of which is Unknown_Access, and Unknown_Access is always permitted. The degree of effort required to expand the extension to support a richer policy is unknown.
All communication between an X client and the X server happens over the protocol. Performing mediation at the X protocol interface is sufficient to ensure that clients can not communicate via the server without authorization. Extensions like the MIT-SHM (the MIT Shared Memory Extension) merely allow clients to share image data with the server, and do not present additional channels of communication.
One place to put the hook for all requests is ProcessWorkQueue (dix.c) in the XFree86 source tree, the central dispatch loop for the X server. However, it is likely that each request will need to be modified to check permissions, as the central dispatch loop does not know the data contained in the request.
For events, there are several control points. Some events are sent via WriteToClient, a low level output method. Some events are sent via DeliverEvents, some by MaybeDeliverEventsToClient, and most by WriteEventsToClient.
Since there is no single interface for intercepting the events being sent to a client, the X server will need to be modified to be more consistent about sending events to a client. The X server currently treats events like any other bit of opaque data that needs to be sent to the client: it queues the event onto an output buffer. While one could certainly place a hook in the low level WriteToClient method, the hook will need to implement a significant amount of logic to determine the type of the opaque data being queued.
Another feature that needs access control is the use of extensions. The X protocol is flexible, and allows arbitrary extensions to be registered. It is not practical for an ongoing security project to know the security properties of a new extension, so part of the policy will need to limit extensions. While extensions could be aware of the security policy, they do constitute code running inside a trusted enforcement module. Limiting extensions should be possible from the kernel-level policy for the X server, as the extensions are implemented as shared libraries that the X server links in at run time.
The SELinux framework currently trusts only the kernel to make security decisions, and trusts only the kernel to enforce security decisions. However, it is not practical to refactor the X server to allow enforcement to take place in the kernel. The X server will need to enforce the policy.
Policies for X objects will likely be incorporated with the general system policy. If two processes should not communicate by more traditional system means like Unix domain sockets, then they should also be prevented from communicating through the X server. If one application's output should not be allowed as input to a different application, then that second application should not be allowed to copy a window belonging to the first application.
The X server should use the Access Vector Cache (AVC) component from the Flask architecture. The AVC component is not currently included in libSecure, and will need to be. The AVC will need a security server to make policy decisions. The security server could be included in libSecure, or the AVC could use the security server in the kernel. Both approaches are valid, but we recommend initially using the kernel's security server. Using the security server already in SELinux will consolidate the security server code and simplify the process of changing the current policy. If the performance of this approach is unacceptable, the AVC architecture would make moving the security server into the X server straight-forward.
The diagram in Figure 2 represents the classical layer diagram of the proposed design. The X server will use the AVC implementation in libSecure for authorization, which in turn will use a security server. The X server will rely on the SELinux controls for protection against other processes. The clients will communicate with the server through TCP/IP connections or Unix Domain Sockets via the normal X protocol, where the server will enforce the systems policy upon the client requests.
The core X protocol defines 17 errors; some are generic (e.g. Access). Other errors are specific to a request; BadPixmap for example. Most of these errors are returned to a client when the client provides bad arguments to a request.
When a client attempts to perform an operation that is not permitted by the security policy, the X server must decide how best to respond. Depending on the severity of the error, there are at least three options. The X server can ignore the request, send a protocol error to the client, or terminate the client. The SELinux security server typically returns errors that only have meaning within the Linux kernel; these error values must be translated into something that is more appropriate for X. Some probable translations are listed in Table 21. The actual error, if any, returned when a request is denied will be dependent on the request, and the exact reason why a request was denied. For example, if a client invokes the QueryTree request, and some child window is not visible to the client, the child window should just be eliminated from the output and no error sent. If the client attempts to set a property on a window that should not be visible (by guessing its Resource ID), then a BadWindow error should be sent.
Table 21. Translation table for SELinux error codes
|SELinux error code||X Error|
|EAGAIN||no direct translation|
If the error reflects an intermittent failure within the security server (ENOMEM), it may be preferable to report the error to the application and allow the user to potentially retry the operation. In this case, ENOMEM would translate to a BadAlloc error condition. The X protocol already defines a BadAccess error that may be returned for some operations. However, a BadAccess error is not an expected result of many operations and there may be unexpected consequences for the client application.
The EAGAIN error indicates that the system has a temporary issue, and that the client should try the request again. In most cases, this return code indicates a transient issue with the security server, and the modified X server will rerun the query instead of passing the error back to the client.
It will often be the case that most operations can simply be stopped. This may cause problems with some clients, since few X protocol methods are expected to fail. Even considering the impact upon client applications, it is preferable to err on the side of stronger security. Even though clients may freeze as a result of access denials, the X server may not need to terminate the application. [WigginsProtocol96]