diff options
Diffstat (limited to 'xorg-server/Xext/xselinux_hooks.c')
-rw-r--r-- | xorg-server/Xext/xselinux_hooks.c | 1870 |
1 files changed, 935 insertions, 935 deletions
diff --git a/xorg-server/Xext/xselinux_hooks.c b/xorg-server/Xext/xselinux_hooks.c index 1c048bd68..f1d8e5d2f 100644 --- a/xorg-server/Xext/xselinux_hooks.c +++ b/xorg-server/Xext/xselinux_hooks.c @@ -1,935 +1,935 @@ -/************************************************************
-
-Author: Eamon Walsh <ewalsh@tycho.nsa.gov>
-
-Permission to use, copy, modify, distribute, and sell this software and its
-documentation for any purpose is hereby granted without fee, provided that
-this permission notice appear in supporting documentation. This permission
-notice shall be included in all copies or substantial portions of the
-Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
-AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
-CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-********************************************************/
-
-/*
- * Portions of this code copyright (c) 2005 by Trusted Computer Solutions, Inc.
- * All rights reserved.
- */
-
-#ifdef HAVE_DIX_CONFIG_H
-#include <dix-config.h>
-#endif
-
-#include <sys/socket.h>
-#include <stdio.h>
-#include <stdarg.h>
-
-#include <libaudit.h>
-
-#include <X11/Xatom.h>
-#include "selection.h"
-#include "inputstr.h"
-#include "scrnintstr.h"
-#include "windowstr.h"
-#include "propertyst.h"
-#include "extnsionst.h"
-#include "xacestr.h"
-#include "client.h"
-#include "../os/osdep.h"
-#define _XSELINUX_NEED_FLASK_MAP
-#include "xselinuxint.h"
-
-
-/* structure passed to auditing callback */
-typedef struct {
- ClientPtr client; /* client */
- DeviceIntPtr dev; /* device */
- char *command; /* client's executable path */
- unsigned id; /* resource id, if any */
- int restype; /* resource type, if any */
- int event; /* event type, if any */
- Atom property; /* property name, if any */
- Atom selection; /* selection name, if any */
- char *extension; /* extension name, if any */
-} SELinuxAuditRec;
-
-/* private state keys */
-DevPrivateKeyRec subjectKeyRec;
-DevPrivateKeyRec objectKeyRec;
-DevPrivateKeyRec dataKeyRec;
-
-/* audit file descriptor */
-static int audit_fd;
-
-/* atoms for window label properties */
-static Atom atom_ctx;
-static Atom atom_client_ctx;
-
-/* The unlabeled SID */
-static security_id_t unlabeled_sid;
-
-/* forward declarations */
-static void SELinuxScreen(CallbackListPtr *, pointer, pointer);
-
-/* "true" pointer value for use as callback data */
-static pointer truep = (pointer)1;
-
-
-/*
- * Performs an SELinux permission check.
- */
-static int
-SELinuxDoCheck(SELinuxSubjectRec *subj, SELinuxObjectRec *obj,
- security_class_t class, Mask mode, SELinuxAuditRec *auditdata)
-{
- /* serverClient requests OK */
- if (subj->privileged)
- return Success;
-
- auditdata->command = subj->command;
- errno = 0;
-
- if (avc_has_perm(subj->sid, obj->sid, class, mode, &subj->aeref,
- auditdata) < 0) {
- if (mode == DixUnknownAccess)
- return Success; /* DixUnknownAccess requests OK ... for now */
- if (errno == EACCES)
- return BadAccess;
- ErrorF("SELinux: avc_has_perm: unexpected error %d\n", errno);
- return BadValue;
- }
-
- return Success;
-}
-
-/*
- * Labels a newly connected client.
- */
-static void
-SELinuxLabelClient(ClientPtr client)
-{
- int fd = XaceGetConnectionNumber(client);
- SELinuxSubjectRec *subj;
- SELinuxObjectRec *obj;
- security_context_t ctx;
-
- subj = dixLookupPrivate(&client->devPrivates, subjectKey);
- obj = dixLookupPrivate(&client->devPrivates, objectKey);
-
- /* Try to get a context from the socket */
- if (fd < 0 || getpeercon_raw(fd, &ctx) < 0) {
- /* Otherwise, fall back to a default context */
- ctx = SELinuxDefaultClientLabel();
- }
-
- /* For local clients, try and determine the executable name */
- if (XaceIsLocal(client)) {
- /* Get cached command name if CLIENTIDS is enabled. */
- const char *cmdname = GetClientCmdName(client);
- Bool cached = (cmdname != NULL);
- /* If CLIENTIDS is disabled, figure out the command name from
- * scratch. */
- if (!cmdname)
- {
- pid_t pid = DetermineClientPid(client);
- if (pid != -1)
- DetermineClientCmd(pid, &cmdname, NULL);
- }
-
- if (!cmdname)
- goto finish;
-
- strncpy(subj->command, cmdname, COMMAND_LEN - 1);
-
- if (!cached)
- free((void *) cmdname); /* const char * */
- }
-
-finish:
- /* Get a SID from the context */
- if (avc_context_to_sid_raw(ctx, &subj->sid) < 0)
- FatalError("SELinux: client %d: context_to_sid_raw(%s) failed\n",
- client->index, ctx);
-
- obj->sid = subj->sid;
- freecon(ctx);
-}
-
-/*
- * Labels initial server objects.
- */
-static void
-SELinuxLabelInitial(void)
-{
- int i;
- XaceScreenAccessRec srec;
- SELinuxSubjectRec *subj;
- SELinuxObjectRec *obj;
- security_context_t ctx;
- pointer unused;
-
- /* Do the serverClient */
- subj = dixLookupPrivate(&serverClient->devPrivates, subjectKey);
- obj = dixLookupPrivate(&serverClient->devPrivates, objectKey);
- subj->privileged = 1;
-
- /* Use the context of the X server process for the serverClient */
- if (getcon_raw(&ctx) < 0)
- FatalError("SELinux: couldn't get context of X server process\n");
-
- /* Get a SID from the context */
- if (avc_context_to_sid_raw(ctx, &subj->sid) < 0)
- FatalError("SELinux: serverClient: context_to_sid(%s) failed\n", ctx);
-
- obj->sid = subj->sid;
- freecon(ctx);
-
- srec.client = serverClient;
- srec.access_mode = DixCreateAccess;
- srec.status = Success;
-
- for (i = 0; i < screenInfo.numScreens; i++) {
- /* Do the screen object */
- srec.screen = screenInfo.screens[i];
- SELinuxScreen(NULL, NULL, &srec);
-
- /* Do the default colormap */
- dixLookupResourceByType(&unused, screenInfo.screens[i]->defColormap,
- RT_COLORMAP, serverClient, DixCreateAccess);
- }
-}
-
-/*
- * Labels new resource objects.
- */
-static int
-SELinuxLabelResource(XaceResourceAccessRec *rec, SELinuxSubjectRec *subj,
- SELinuxObjectRec *obj, security_class_t class)
-{
- int offset;
- security_id_t tsid;
-
- /* Check for a create context */
- if (rec->rtype & RC_DRAWABLE && subj->win_create_sid) {
- obj->sid = subj->win_create_sid;
- return Success;
- }
-
- if (rec->parent)
- offset = dixLookupPrivateOffset(rec->ptype);
-
- if (rec->parent && offset >= 0) {
- /* Use the SID of the parent object in the labeling operation */
- PrivateRec **privatePtr = DEVPRIV_AT(rec->parent, offset);
- SELinuxObjectRec *pobj = dixLookupPrivate(privatePtr, objectKey);
- tsid = pobj->sid;
- } else {
- /* Use the SID of the subject */
- tsid = subj->sid;
- }
-
- /* Perform a transition to obtain the final SID */
- if (avc_compute_create(subj->sid, tsid, class, &obj->sid) < 0) {
- ErrorF("SELinux: a compute_create call failed!\n");
- return BadValue;
- }
-
- return Success;
-}
-
-
-/*
- * Libselinux Callbacks
- */
-
-static int
-SELinuxAudit(void *auditdata,
- security_class_t class,
- char *msgbuf,
- size_t msgbufsize)
-{
- SELinuxAuditRec *audit = auditdata;
- ClientPtr client = audit->client;
- char idNum[16];
- const char *propertyName, *selectionName;
- int major = -1, minor = -1;
-
- if (client) {
- REQUEST(xReq);
- if (stuff) {
- major = stuff->reqType;
- minor = MinorOpcodeOfRequest(client);
- }
- }
- if (audit->id)
- snprintf(idNum, 16, "%x", audit->id);
-
- propertyName = audit->property ? NameForAtom(audit->property) : NULL;
- selectionName = audit->selection ? NameForAtom(audit->selection) : NULL;
-
- return snprintf(msgbuf, msgbufsize,
- "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
- (major >= 0) ? "request=" : "",
- (major >= 0) ? LookupRequestName(major, minor) : "",
- audit->command ? " comm=" : "",
- audit->command ? audit->command : "",
- audit->dev ? " xdevice=\"" : "",
- audit->dev ? audit->dev->name : "",
- audit->dev ? "\"" : "",
- audit->id ? " resid=" : "",
- audit->id ? idNum : "",
- audit->restype ? " restype=" : "",
- audit->restype ? LookupResourceName(audit->restype) : "",
- audit->event ? " event=" : "",
- audit->event ? LookupEventName(audit->event & 127) : "",
- audit->property ? " property=" : "",
- audit->property ? propertyName : "",
- audit->selection ? " selection=" : "",
- audit->selection ? selectionName : "",
- audit->extension ? " extension=" : "",
- audit->extension ? audit->extension : "");
-}
-
-static int
-SELinuxLog(int type, const char *fmt, ...)
-{
- va_list ap;
- char buf[MAX_AUDIT_MESSAGE_LENGTH];
- int rc, aut;
-
- switch (type) {
- case SELINUX_INFO:
- aut = AUDIT_USER_MAC_POLICY_LOAD;
- break;
- case SELINUX_AVC:
- aut = AUDIT_USER_AVC;
- break;
- default:
- aut = AUDIT_USER_SELINUX_ERR;
- break;
- }
-
- va_start(ap, fmt);
- vsnprintf(buf, MAX_AUDIT_MESSAGE_LENGTH, fmt, ap);
- rc = audit_log_user_avc_message(audit_fd, aut, buf, NULL, NULL, NULL, 0);
- va_end(ap);
- LogMessageVerb(X_WARNING, 0, "%s", buf);
- return 0;
-}
-
-/*
- * XACE Callbacks
- */
-
-static void
-SELinuxDevice(CallbackListPtr *pcbl, pointer unused, pointer calldata)
-{
- XaceDeviceAccessRec *rec = calldata;
- SELinuxSubjectRec *subj;
- SELinuxObjectRec *obj;
- SELinuxAuditRec auditdata = { .client = rec->client, .dev = rec->dev };
- security_class_t cls;
- int rc;
-
- subj = dixLookupPrivate(&rec->client->devPrivates, subjectKey);
- obj = dixLookupPrivate(&rec->dev->devPrivates, objectKey);
-
- /* If this is a new object that needs labeling, do it now */
- if (rec->access_mode & DixCreateAccess) {
- SELinuxSubjectRec *dsubj;
- dsubj = dixLookupPrivate(&rec->dev->devPrivates, subjectKey);
-
- if (subj->dev_create_sid) {
- /* Label the device with the create context */
- obj->sid = subj->dev_create_sid;
- dsubj->sid = subj->dev_create_sid;
- } else {
- /* Label the device directly with the process SID */
- obj->sid = subj->sid;
- dsubj->sid = subj->sid;
- }
- }
-
- cls = IsPointerDevice(rec->dev) ? SECCLASS_X_POINTER : SECCLASS_X_KEYBOARD;
- rc = SELinuxDoCheck(subj, obj, cls, rec->access_mode, &auditdata);
- if (rc != Success)
- rec->status = rc;
-}
-
-static void
-SELinuxSend(CallbackListPtr *pcbl, pointer unused, pointer calldata)
-{
- XaceSendAccessRec *rec = calldata;
- SELinuxSubjectRec *subj;
- SELinuxObjectRec *obj, ev_sid;
- SELinuxAuditRec auditdata = { .client = rec->client, .dev = rec->dev };
- security_class_t class;
- int rc, i, type;
-
- if (rec->dev)
- subj = dixLookupPrivate(&rec->dev->devPrivates, subjectKey);
- else
- subj = dixLookupPrivate(&rec->client->devPrivates, subjectKey);
-
- obj = dixLookupPrivate(&rec->pWin->devPrivates, objectKey);
-
- /* Check send permission on window */
- rc = SELinuxDoCheck(subj, obj, SECCLASS_X_DRAWABLE, DixSendAccess,
- &auditdata);
- if (rc != Success)
- goto err;
-
- /* Check send permission on specific event types */
- for (i = 0; i < rec->count; i++) {
- type = rec->events[i].u.u.type;
- class = (type & 128) ? SECCLASS_X_FAKEEVENT : SECCLASS_X_EVENT;
-
- rc = SELinuxEventToSID(type, obj->sid, &ev_sid);
- if (rc != Success)
- goto err;
-
- auditdata.event = type;
- rc = SELinuxDoCheck(subj, &ev_sid, class, DixSendAccess, &auditdata);
- if (rc != Success)
- goto err;
- }
- return;
-err:
- rec->status = rc;
-}
-
-static void
-SELinuxReceive(CallbackListPtr *pcbl, pointer unused, pointer calldata)
-{
- XaceReceiveAccessRec *rec = calldata;
- SELinuxSubjectRec *subj;
- SELinuxObjectRec *obj, ev_sid;
- SELinuxAuditRec auditdata = { .client = NULL };
- security_class_t class;
- int rc, i, type;
-
- subj = dixLookupPrivate(&rec->client->devPrivates, subjectKey);
- obj = dixLookupPrivate(&rec->pWin->devPrivates, objectKey);
-
- /* Check receive permission on window */
- rc = SELinuxDoCheck(subj, obj, SECCLASS_X_DRAWABLE, DixReceiveAccess,
- &auditdata);
- if (rc != Success)
- goto err;
-
- /* Check receive permission on specific event types */
- for (i = 0; i < rec->count; i++) {
- type = rec->events[i].u.u.type;
- class = (type & 128) ? SECCLASS_X_FAKEEVENT : SECCLASS_X_EVENT;
-
- rc = SELinuxEventToSID(type, obj->sid, &ev_sid);
- if (rc != Success)
- goto err;
-
- auditdata.event = type;
- rc = SELinuxDoCheck(subj, &ev_sid, class, DixReceiveAccess, &auditdata);
- if (rc != Success)
- goto err;
- }
- return;
-err:
- rec->status = rc;
-}
-
-static void
-SELinuxExtension(CallbackListPtr *pcbl, pointer unused, pointer calldata)
-{
- XaceExtAccessRec *rec = calldata;
- SELinuxSubjectRec *subj, *serv;
- SELinuxObjectRec *obj;
- SELinuxAuditRec auditdata = { .client = rec->client };
- int rc;
-
- subj = dixLookupPrivate(&rec->client->devPrivates, subjectKey);
- obj = dixLookupPrivate(&rec->ext->devPrivates, objectKey);
-
- /* If this is a new object that needs labeling, do it now */
- /* XXX there should be a separate callback for this */
- if (obj->sid == NULL) {
- security_id_t sid;
-
- serv = dixLookupPrivate(&serverClient->devPrivates, subjectKey);
- rc = SELinuxExtensionToSID(rec->ext->name, &sid);
- if (rc != Success) {
- rec->status = rc;
- return;
- }
-
- /* Perform a transition to obtain the final SID */
- if (avc_compute_create(serv->sid, sid, SECCLASS_X_EXTENSION,
- &obj->sid) < 0) {
- ErrorF("SELinux: a SID transition call failed!\n");
- rec->status = BadValue;
- return;
- }
- }
-
- /* Perform the security check */
- auditdata.extension = rec->ext->name;
- rc = SELinuxDoCheck(subj, obj, SECCLASS_X_EXTENSION, rec->access_mode,
- &auditdata);
- if (rc != Success)
- rec->status = rc;
-}
-
-static void
-SELinuxSelection(CallbackListPtr *pcbl, pointer unused, pointer calldata)
-{
- XaceSelectionAccessRec *rec = calldata;
- SELinuxSubjectRec *subj;
- SELinuxObjectRec *obj, *data;
- Selection *pSel = *rec->ppSel;
- Atom name = pSel->selection;
- Mask access_mode = rec->access_mode;
- SELinuxAuditRec auditdata = { .client = rec->client, .selection = name };
- security_id_t tsid;
- int rc;
-
- subj = dixLookupPrivate(&rec->client->devPrivates, subjectKey);
- obj = dixLookupPrivate(&pSel->devPrivates, objectKey);
-
- /* If this is a new object that needs labeling, do it now */
- if (access_mode & DixCreateAccess) {
- rc = SELinuxSelectionToSID(name, subj, &obj->sid, &obj->poly);
- if (rc != Success)
- obj->sid = unlabeled_sid;
- access_mode = DixSetAttrAccess;
- }
- /* If this is a polyinstantiated object, find the right instance */
- else if (obj->poly) {
- rc = SELinuxSelectionToSID(name, subj, &tsid, NULL);
- if (rc != Success) {
- rec->status = rc;
- return;
- }
- while (pSel->selection != name || obj->sid != tsid) {
- if ((pSel = pSel->next) == NULL)
- break;
- obj = dixLookupPrivate(&pSel->devPrivates, objectKey);
- }
-
- if (pSel)
- *rec->ppSel = pSel;
- else {
- rec->status = BadMatch;
- return;
- }
- }
-
- /* Perform the security check */
- rc = SELinuxDoCheck(subj, obj, SECCLASS_X_SELECTION, access_mode,
- &auditdata);
- if (rc != Success)
- rec->status = rc;
-
- /* Label the content (advisory only) */
- if (access_mode & DixSetAttrAccess) {
- data = dixLookupPrivate(&pSel->devPrivates, dataKey);
- if (subj->sel_create_sid)
- data->sid = subj->sel_create_sid;
- else
- data->sid = obj->sid;
- }
-}
-
-static void
-SELinuxProperty(CallbackListPtr *pcbl, pointer unused, pointer calldata)
-{
- XacePropertyAccessRec *rec = calldata;
- SELinuxSubjectRec *subj;
- SELinuxObjectRec *obj, *data;
- PropertyPtr pProp = *rec->ppProp;
- Atom name = pProp->propertyName;
- SELinuxAuditRec auditdata = { .client = rec->client, .property = name };
- security_id_t tsid;
- int rc;
-
- /* Don't care about the new content check */
- if (rec->access_mode & DixPostAccess)
- return;
-
- subj = dixLookupPrivate(&rec->client->devPrivates, subjectKey);
- obj = dixLookupPrivate(&pProp->devPrivates, objectKey);
-
- /* If this is a new object that needs labeling, do it now */
- if (rec->access_mode & DixCreateAccess) {
- rc = SELinuxPropertyToSID(name, subj, &obj->sid, &obj->poly);
- if (rc != Success) {
- rec->status = rc;
- return;
- }
- }
- /* If this is a polyinstantiated object, find the right instance */
- else if (obj->poly) {
- rc = SELinuxPropertyToSID(name, subj, &tsid, NULL);
- if (rc != Success) {
- rec->status = rc;
- return;
- }
- while (pProp->propertyName != name || obj->sid != tsid) {
- if ((pProp = pProp->next) == NULL)
- break;
- obj = dixLookupPrivate(&pProp->devPrivates, objectKey);
- }
-
- if (pProp)
- *rec->ppProp = pProp;
- else {
- rec->status = BadMatch;
- return;
- }
- }
-
- /* Perform the security check */
- rc = SELinuxDoCheck(subj, obj, SECCLASS_X_PROPERTY, rec->access_mode,
- &auditdata);
- if (rc != Success)
- rec->status = rc;
-
- /* Label the content (advisory only) */
- if (rec->access_mode & DixWriteAccess) {
- data = dixLookupPrivate(&pProp->devPrivates, dataKey);
- if (subj->prp_create_sid)
- data->sid = subj->prp_create_sid;
- else
- data->sid = obj->sid;
- }
-}
-
-static void
-SELinuxResource(CallbackListPtr *pcbl, pointer unused, pointer calldata)
-{
- XaceResourceAccessRec *rec = calldata;
- SELinuxSubjectRec *subj;
- SELinuxObjectRec *obj;
- SELinuxAuditRec auditdata = { .client = rec->client };
- Mask access_mode = rec->access_mode;
- PrivateRec **privatePtr;
- security_class_t class;
- int rc, offset;
-
- subj = dixLookupPrivate(&rec->client->devPrivates, subjectKey);
-
- /* Determine if the resource object has a devPrivates field */
- offset = dixLookupPrivateOffset(rec->rtype);
- if (offset < 0) {
- /* No: use the SID of the owning client */
- class = SECCLASS_X_RESOURCE;
- privatePtr = &clients[CLIENT_ID(rec->id)]->devPrivates;
- obj = dixLookupPrivate(privatePtr, objectKey);
- } else {
- /* Yes: use the SID from the resource object itself */
- class = SELinuxTypeToClass(rec->rtype);
- privatePtr = DEVPRIV_AT(rec->res, offset);
- obj = dixLookupPrivate(privatePtr, objectKey);
- }
-
- /* If this is a new object that needs labeling, do it now */
- if (access_mode & DixCreateAccess && offset >= 0) {
- rc = SELinuxLabelResource(rec, subj, obj, class);
- if (rc != Success) {
- rec->status = rc;
- return;
- }
- }
-
- /* Collapse generic resource permissions down to read/write */
- if (class == SECCLASS_X_RESOURCE) {
- access_mode = !!(rec->access_mode & SELinuxReadMask); /* rd */
- access_mode |= !!(rec->access_mode & ~SELinuxReadMask) << 1; /* wr */
- }
-
- /* Perform the security check */
- auditdata.restype = rec->rtype;
- auditdata.id = rec->id;
- rc = SELinuxDoCheck(subj, obj, class, access_mode, &auditdata);
- if (rc != Success)
- rec->status = rc;
-
- /* Perform the background none check on windows */
- if (access_mode & DixCreateAccess && rec->rtype == RT_WINDOW) {
- rc = SELinuxDoCheck(subj, obj, class, DixBlendAccess, &auditdata);
- if (rc != Success)
- ((WindowPtr)rec->res)->forcedBG = TRUE;
- }
-}
-
-static void
-SELinuxScreen(CallbackListPtr *pcbl, pointer is_saver, pointer calldata)
-{
- XaceScreenAccessRec *rec = calldata;
- SELinuxSubjectRec *subj;
- SELinuxObjectRec *obj;
- SELinuxAuditRec auditdata = { .client = rec->client };
- Mask access_mode = rec->access_mode;
- int rc;
-
- subj = dixLookupPrivate(&rec->client->devPrivates, subjectKey);
- obj = dixLookupPrivate(&rec->screen->devPrivates, objectKey);
-
- /* If this is a new object that needs labeling, do it now */
- if (access_mode & DixCreateAccess) {
- /* Perform a transition to obtain the final SID */
- if (avc_compute_create(subj->sid, subj->sid, SECCLASS_X_SCREEN,
- &obj->sid) < 0) {
- ErrorF("SELinux: a compute_create call failed!\n");
- rec->status = BadValue;
- return;
- }
- }
-
- if (is_saver)
- access_mode <<= 2;
-
- rc = SELinuxDoCheck(subj, obj, SECCLASS_X_SCREEN, access_mode, &auditdata);
- if (rc != Success)
- rec->status = rc;
-}
-
-static void
-SELinuxClient(CallbackListPtr *pcbl, pointer unused, pointer calldata)
-{
- XaceClientAccessRec *rec = calldata;
- SELinuxSubjectRec *subj;
- SELinuxObjectRec *obj;
- SELinuxAuditRec auditdata = { .client = rec->client };
- int rc;
-
- subj = dixLookupPrivate(&rec->client->devPrivates, subjectKey);
- obj = dixLookupPrivate(&rec->target->devPrivates, objectKey);
-
- rc = SELinuxDoCheck(subj, obj, SECCLASS_X_CLIENT, rec->access_mode,
- &auditdata);
- if (rc != Success)
- rec->status = rc;
-}
-
-static void
-SELinuxServer(CallbackListPtr *pcbl, pointer unused, pointer calldata)
-{
- XaceServerAccessRec *rec = calldata;
- SELinuxSubjectRec *subj;
- SELinuxObjectRec *obj;
- SELinuxAuditRec auditdata = { .client = rec->client };
- int rc;
-
- subj = dixLookupPrivate(&rec->client->devPrivates, subjectKey);
- obj = dixLookupPrivate(&serverClient->devPrivates, objectKey);
-
- rc = SELinuxDoCheck(subj, obj, SECCLASS_X_SERVER, rec->access_mode,
- &auditdata);
- if (rc != Success)
- rec->status = rc;
-}
-
-
-/*
- * DIX Callbacks
- */
-
-static void
-SELinuxClientState(CallbackListPtr *pcbl, pointer unused, pointer calldata)
-{
- NewClientInfoRec *pci = calldata;
-
- switch (pci->client->clientState) {
- case ClientStateInitial:
- SELinuxLabelClient(pci->client);
- break;
-
- default:
- break;
- }
-}
-
-static void
-SELinuxResourceState(CallbackListPtr *pcbl, pointer unused, pointer calldata)
-{
- ResourceStateInfoRec *rec = calldata;
- SELinuxSubjectRec *subj;
- SELinuxObjectRec *obj;
- WindowPtr pWin;
-
- if (rec->type != RT_WINDOW)
- return;
- if (rec->state != ResourceStateAdding)
- return;
-
- pWin = (WindowPtr)rec->value;
- subj = dixLookupPrivate(&wClient(pWin)->devPrivates, subjectKey);
-
- if (subj->sid) {
- security_context_t ctx;
- int rc = avc_sid_to_context_raw(subj->sid, &ctx);
- if (rc < 0)
- FatalError("SELinux: Failed to get security context!\n");
- rc = dixChangeWindowProperty(serverClient,
- pWin, atom_client_ctx, XA_STRING, 8,
- PropModeReplace, strlen(ctx), ctx, FALSE);
- if (rc != Success)
- FatalError("SELinux: Failed to set label property on window!\n");
- freecon(ctx);
- } else
- FatalError("SELinux: Unexpected unlabeled client found\n");
-
- obj = dixLookupPrivate(&pWin->devPrivates, objectKey);
-
- if (obj->sid) {
- security_context_t ctx;
- int rc = avc_sid_to_context_raw(obj->sid, &ctx);
- if (rc < 0)
- FatalError("SELinux: Failed to get security context!\n");
- rc = dixChangeWindowProperty(serverClient,
- pWin, atom_ctx, XA_STRING, 8,
- PropModeReplace, strlen(ctx), ctx, FALSE);
- if (rc != Success)
- FatalError("SELinux: Failed to set label property on window!\n");
- freecon(ctx);
- } else
- FatalError("SELinux: Unexpected unlabeled window found\n");
-}
-
-
-static int netlink_fd;
-
-static void
-SELinuxBlockHandler(void *data, struct timeval **tv, void *read_mask)
-{
-}
-
-static void
-SELinuxWakeupHandler(void *data, int err, void *read_mask)
-{
- if (FD_ISSET(netlink_fd, (fd_set *)read_mask))
- avc_netlink_check_nb();
-}
-
-void
-SELinuxFlaskReset(void)
-{
- /* Unregister callbacks */
- DeleteCallback(&ClientStateCallback, SELinuxClientState, NULL);
- DeleteCallback(&ResourceStateCallback, SELinuxResourceState, NULL);
-
- XaceDeleteCallback(XACE_EXT_DISPATCH, SELinuxExtension, NULL);
- XaceDeleteCallback(XACE_RESOURCE_ACCESS, SELinuxResource, NULL);
- XaceDeleteCallback(XACE_DEVICE_ACCESS, SELinuxDevice, NULL);
- XaceDeleteCallback(XACE_PROPERTY_ACCESS, SELinuxProperty, NULL);
- XaceDeleteCallback(XACE_SEND_ACCESS, SELinuxSend, NULL);
- XaceDeleteCallback(XACE_RECEIVE_ACCESS, SELinuxReceive, NULL);
- XaceDeleteCallback(XACE_CLIENT_ACCESS, SELinuxClient, NULL);
- XaceDeleteCallback(XACE_EXT_ACCESS, SELinuxExtension, NULL);
- XaceDeleteCallback(XACE_SERVER_ACCESS, SELinuxServer, NULL);
- XaceDeleteCallback(XACE_SELECTION_ACCESS, SELinuxSelection, NULL);
- XaceDeleteCallback(XACE_SCREEN_ACCESS, SELinuxScreen, NULL);
- XaceDeleteCallback(XACE_SCREENSAVER_ACCESS, SELinuxScreen, truep);
-
- /* Tear down SELinux stuff */
- audit_close(audit_fd);
- avc_netlink_release_fd();
- RemoveBlockAndWakeupHandlers(SELinuxBlockHandler, SELinuxWakeupHandler,
- NULL);
- RemoveGeneralSocket(netlink_fd);
-
- avc_destroy();
-}
-
-void
-SELinuxFlaskInit(void)
-{
- struct selinux_opt avc_option = { AVC_OPT_SETENFORCE, (char *)0 };
- security_context_t ctx;
- int ret = TRUE;
-
- switch(selinuxEnforcingState) {
- case SELINUX_MODE_ENFORCING:
- LogMessage(X_INFO, "SELinux: Configured in enforcing mode\n");
- avc_option.value = (char *)1;
- break;
- case SELINUX_MODE_PERMISSIVE:
- LogMessage(X_INFO, "SELinux: Configured in permissive mode\n");
- avc_option.value = (char *)0;
- break;
- default:
- avc_option.type = AVC_OPT_UNUSED;
- break;
- }
-
- /* Set up SELinux stuff */
- selinux_set_callback(SELINUX_CB_LOG, (union selinux_callback)SELinuxLog);
- selinux_set_callback(SELINUX_CB_AUDIT, (union selinux_callback)SELinuxAudit);
-
- if (selinux_set_mapping(map) < 0) {
- if (errno == EINVAL) {
- ErrorF("SELinux: Invalid object class mapping, disabling SELinux support.\n");
- return;
- }
- FatalError("SELinux: Failed to set up security class mapping\n");
- }
-
- if (avc_open(&avc_option, 1) < 0)
- FatalError("SELinux: Couldn't initialize SELinux userspace AVC\n");
-
- if (security_get_initial_context_raw("unlabeled", &ctx) < 0)
- FatalError("SELinux: Failed to look up unlabeled context\n");
- if (avc_context_to_sid_raw(ctx, &unlabeled_sid) < 0)
- FatalError("SELinux: a context_to_SID call failed!\n");
- freecon(ctx);
-
- /* Prepare for auditing */
- audit_fd = audit_open();
- if (audit_fd < 0)
- FatalError("SELinux: Failed to open the system audit log\n");
-
- /* Allocate private storage */
- if (!dixRegisterPrivateKey(subjectKey, PRIVATE_XSELINUX, sizeof(SELinuxSubjectRec)) ||
- !dixRegisterPrivateKey(objectKey, PRIVATE_XSELINUX, sizeof(SELinuxObjectRec)) ||
- !dixRegisterPrivateKey(dataKey, PRIVATE_XSELINUX, sizeof(SELinuxObjectRec)))
- FatalError("SELinux: Failed to allocate private storage.\n");
-
- /* Create atoms for doing window labeling */
- atom_ctx = MakeAtom("_SELINUX_CONTEXT", 16, TRUE);
- if (atom_ctx == BAD_RESOURCE)
- FatalError("SELinux: Failed to create atom\n");
- atom_client_ctx = MakeAtom("_SELINUX_CLIENT_CONTEXT", 23, TRUE);
- if (atom_client_ctx == BAD_RESOURCE)
- FatalError("SELinux: Failed to create atom\n");
-
- netlink_fd = avc_netlink_acquire_fd();
- AddGeneralSocket(netlink_fd);
- RegisterBlockAndWakeupHandlers(SELinuxBlockHandler, SELinuxWakeupHandler,
- NULL);
-
- /* Register callbacks */
- ret &= AddCallback(&ClientStateCallback, SELinuxClientState, NULL);
- ret &= AddCallback(&ResourceStateCallback, SELinuxResourceState, NULL);
-
- ret &= XaceRegisterCallback(XACE_EXT_DISPATCH, SELinuxExtension, NULL);
- ret &= XaceRegisterCallback(XACE_RESOURCE_ACCESS, SELinuxResource, NULL);
- ret &= XaceRegisterCallback(XACE_DEVICE_ACCESS, SELinuxDevice, NULL);
- ret &= XaceRegisterCallback(XACE_PROPERTY_ACCESS, SELinuxProperty, NULL);
- ret &= XaceRegisterCallback(XACE_SEND_ACCESS, SELinuxSend, NULL);
- ret &= XaceRegisterCallback(XACE_RECEIVE_ACCESS, SELinuxReceive, NULL);
- ret &= XaceRegisterCallback(XACE_CLIENT_ACCESS, SELinuxClient, NULL);
- ret &= XaceRegisterCallback(XACE_EXT_ACCESS, SELinuxExtension, NULL);
- ret &= XaceRegisterCallback(XACE_SERVER_ACCESS, SELinuxServer, NULL);
- ret &= XaceRegisterCallback(XACE_SELECTION_ACCESS, SELinuxSelection, NULL);
- ret &= XaceRegisterCallback(XACE_SCREEN_ACCESS, SELinuxScreen, NULL);
- ret &= XaceRegisterCallback(XACE_SCREENSAVER_ACCESS, SELinuxScreen, truep);
- if (!ret)
- FatalError("SELinux: Failed to register one or more callbacks\n");
-
- /* Label objects that were created before we could register ourself */
- SELinuxLabelInitial();
-}
+/************************************************************ + +Author: Eamon Walsh <ewalsh@tycho.nsa.gov> + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +this permission notice appear in supporting documentation. This permission +notice shall be included in all copies or substantial portions of the +Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +********************************************************/ + +/* + * Portions of this code copyright (c) 2005 by Trusted Computer Solutions, Inc. + * All rights reserved. + */ + +#ifdef HAVE_DIX_CONFIG_H +#include <dix-config.h> +#endif + +#include <sys/socket.h> +#include <stdio.h> +#include <stdarg.h> + +#include <libaudit.h> + +#include <X11/Xatom.h> +#include "selection.h" +#include "inputstr.h" +#include "scrnintstr.h" +#include "windowstr.h" +#include "propertyst.h" +#include "extnsionst.h" +#include "xacestr.h" +#include "client.h" +#include "../os/osdep.h" +#define _XSELINUX_NEED_FLASK_MAP +#include "xselinuxint.h" + + +/* structure passed to auditing callback */ +typedef struct { + ClientPtr client; /* client */ + DeviceIntPtr dev; /* device */ + char *command; /* client's executable path */ + unsigned id; /* resource id, if any */ + int restype; /* resource type, if any */ + int event; /* event type, if any */ + Atom property; /* property name, if any */ + Atom selection; /* selection name, if any */ + char *extension; /* extension name, if any */ +} SELinuxAuditRec; + +/* private state keys */ +DevPrivateKeyRec subjectKeyRec; +DevPrivateKeyRec objectKeyRec; +DevPrivateKeyRec dataKeyRec; + +/* audit file descriptor */ +static int audit_fd; + +/* atoms for window label properties */ +static Atom atom_ctx; +static Atom atom_client_ctx; + +/* The unlabeled SID */ +static security_id_t unlabeled_sid; + +/* forward declarations */ +static void SELinuxScreen(CallbackListPtr *, pointer, pointer); + +/* "true" pointer value for use as callback data */ +static pointer truep = (pointer)1; + + +/* + * Performs an SELinux permission check. + */ +static int +SELinuxDoCheck(SELinuxSubjectRec *subj, SELinuxObjectRec *obj, + security_class_t class, Mask mode, SELinuxAuditRec *auditdata) +{ + /* serverClient requests OK */ + if (subj->privileged) + return Success; + + auditdata->command = subj->command; + errno = 0; + + if (avc_has_perm(subj->sid, obj->sid, class, mode, &subj->aeref, + auditdata) < 0) { + if (mode == DixUnknownAccess) + return Success; /* DixUnknownAccess requests OK ... for now */ + if (errno == EACCES) + return BadAccess; + ErrorF("SELinux: avc_has_perm: unexpected error %d\n", errno); + return BadValue; + } + + return Success; +} + +/* + * Labels a newly connected client. + */ +static void +SELinuxLabelClient(ClientPtr client) +{ + int fd = XaceGetConnectionNumber(client); + SELinuxSubjectRec *subj; + SELinuxObjectRec *obj; + security_context_t ctx; + + subj = dixLookupPrivate(&client->devPrivates, subjectKey); + obj = dixLookupPrivate(&client->devPrivates, objectKey); + + /* Try to get a context from the socket */ + if (fd < 0 || getpeercon_raw(fd, &ctx) < 0) { + /* Otherwise, fall back to a default context */ + ctx = SELinuxDefaultClientLabel(); + } + + /* For local clients, try and determine the executable name */ + if (XaceIsLocal(client)) { + /* Get cached command name if CLIENTIDS is enabled. */ + const char *cmdname = GetClientCmdName(client); + Bool cached = (cmdname != NULL); + /* If CLIENTIDS is disabled, figure out the command name from + * scratch. */ + if (!cmdname) + { + pid_t pid = DetermineClientPid(client); + if (pid != -1) + DetermineClientCmd(pid, &cmdname, NULL); + } + + if (!cmdname) + goto finish; + + strncpy(subj->command, cmdname, COMMAND_LEN - 1); + + if (!cached) + free((void *) cmdname); /* const char * */ + } + +finish: + /* Get a SID from the context */ + if (avc_context_to_sid_raw(ctx, &subj->sid) < 0) + FatalError("SELinux: client %d: context_to_sid_raw(%s) failed\n", + client->index, ctx); + + obj->sid = subj->sid; + freecon(ctx); +} + +/* + * Labels initial server objects. + */ +static void +SELinuxLabelInitial(void) +{ + int i; + XaceScreenAccessRec srec; + SELinuxSubjectRec *subj; + SELinuxObjectRec *obj; + security_context_t ctx; + pointer unused; + + /* Do the serverClient */ + subj = dixLookupPrivate(&serverClient->devPrivates, subjectKey); + obj = dixLookupPrivate(&serverClient->devPrivates, objectKey); + subj->privileged = 1; + + /* Use the context of the X server process for the serverClient */ + if (getcon_raw(&ctx) < 0) + FatalError("SELinux: couldn't get context of X server process\n"); + + /* Get a SID from the context */ + if (avc_context_to_sid_raw(ctx, &subj->sid) < 0) + FatalError("SELinux: serverClient: context_to_sid(%s) failed\n", ctx); + + obj->sid = subj->sid; + freecon(ctx); + + srec.client = serverClient; + srec.access_mode = DixCreateAccess; + srec.status = Success; + + for (i = 0; i < screenInfo.numScreens; i++) { + /* Do the screen object */ + srec.screen = screenInfo.screens[i]; + SELinuxScreen(NULL, NULL, &srec); + + /* Do the default colormap */ + dixLookupResourceByType(&unused, screenInfo.screens[i]->defColormap, + RT_COLORMAP, serverClient, DixCreateAccess); + } +} + +/* + * Labels new resource objects. + */ +static int +SELinuxLabelResource(XaceResourceAccessRec *rec, SELinuxSubjectRec *subj, + SELinuxObjectRec *obj, security_class_t class) +{ + int offset; + security_id_t tsid; + + /* Check for a create context */ + if (rec->rtype & RC_DRAWABLE && subj->win_create_sid) { + obj->sid = subj->win_create_sid; + return Success; + } + + if (rec->parent) + offset = dixLookupPrivateOffset(rec->ptype); + + if (rec->parent && offset >= 0) { + /* Use the SID of the parent object in the labeling operation */ + PrivateRec **privatePtr = DEVPRIV_AT(rec->parent, offset); + SELinuxObjectRec *pobj = dixLookupPrivate(privatePtr, objectKey); + tsid = pobj->sid; + } else { + /* Use the SID of the subject */ + tsid = subj->sid; + } + + /* Perform a transition to obtain the final SID */ + if (avc_compute_create(subj->sid, tsid, class, &obj->sid) < 0) { + ErrorF("SELinux: a compute_create call failed!\n"); + return BadValue; + } + + return Success; +} + + +/* + * Libselinux Callbacks + */ + +static int +SELinuxAudit(void *auditdata, + security_class_t class, + char *msgbuf, + size_t msgbufsize) +{ + SELinuxAuditRec *audit = auditdata; + ClientPtr client = audit->client; + char idNum[16]; + const char *propertyName, *selectionName; + int major = -1, minor = -1; + + if (client) { + REQUEST(xReq); + if (stuff) { + major = stuff->reqType; + minor = MinorOpcodeOfRequest(client); + } + } + if (audit->id) + snprintf(idNum, 16, "%x", audit->id); + + propertyName = audit->property ? NameForAtom(audit->property) : NULL; + selectionName = audit->selection ? NameForAtom(audit->selection) : NULL; + + return snprintf(msgbuf, msgbufsize, + "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s", + (major >= 0) ? "request=" : "", + (major >= 0) ? LookupRequestName(major, minor) : "", + audit->command ? " comm=" : "", + audit->command ? audit->command : "", + audit->dev ? " xdevice=\"" : "", + audit->dev ? audit->dev->name : "", + audit->dev ? "\"" : "", + audit->id ? " resid=" : "", + audit->id ? idNum : "", + audit->restype ? " restype=" : "", + audit->restype ? LookupResourceName(audit->restype) : "", + audit->event ? " event=" : "", + audit->event ? LookupEventName(audit->event & 127) : "", + audit->property ? " property=" : "", + audit->property ? propertyName : "", + audit->selection ? " selection=" : "", + audit->selection ? selectionName : "", + audit->extension ? " extension=" : "", + audit->extension ? audit->extension : ""); +} + +static int +SELinuxLog(int type, const char *fmt, ...) +{ + va_list ap; + char buf[MAX_AUDIT_MESSAGE_LENGTH]; + int rc, aut; + + switch (type) { + case SELINUX_INFO: + aut = AUDIT_USER_MAC_POLICY_LOAD; + break; + case SELINUX_AVC: + aut = AUDIT_USER_AVC; + break; + default: + aut = AUDIT_USER_SELINUX_ERR; + break; + } + + va_start(ap, fmt); + vsnprintf(buf, MAX_AUDIT_MESSAGE_LENGTH, fmt, ap); + rc = audit_log_user_avc_message(audit_fd, aut, buf, NULL, NULL, NULL, 0); + va_end(ap); + LogMessageVerb(X_WARNING, 0, "%s", buf); + return 0; +} + +/* + * XACE Callbacks + */ + +static void +SELinuxDevice(CallbackListPtr *pcbl, pointer unused, pointer calldata) +{ + XaceDeviceAccessRec *rec = calldata; + SELinuxSubjectRec *subj; + SELinuxObjectRec *obj; + SELinuxAuditRec auditdata = { .client = rec->client, .dev = rec->dev }; + security_class_t cls; + int rc; + + subj = dixLookupPrivate(&rec->client->devPrivates, subjectKey); + obj = dixLookupPrivate(&rec->dev->devPrivates, objectKey); + + /* If this is a new object that needs labeling, do it now */ + if (rec->access_mode & DixCreateAccess) { + SELinuxSubjectRec *dsubj; + dsubj = dixLookupPrivate(&rec->dev->devPrivates, subjectKey); + + if (subj->dev_create_sid) { + /* Label the device with the create context */ + obj->sid = subj->dev_create_sid; + dsubj->sid = subj->dev_create_sid; + } else { + /* Label the device directly with the process SID */ + obj->sid = subj->sid; + dsubj->sid = subj->sid; + } + } + + cls = IsPointerDevice(rec->dev) ? SECCLASS_X_POINTER : SECCLASS_X_KEYBOARD; + rc = SELinuxDoCheck(subj, obj, cls, rec->access_mode, &auditdata); + if (rc != Success) + rec->status = rc; +} + +static void +SELinuxSend(CallbackListPtr *pcbl, pointer unused, pointer calldata) +{ + XaceSendAccessRec *rec = calldata; + SELinuxSubjectRec *subj; + SELinuxObjectRec *obj, ev_sid; + SELinuxAuditRec auditdata = { .client = rec->client, .dev = rec->dev }; + security_class_t class; + int rc, i, type; + + if (rec->dev) + subj = dixLookupPrivate(&rec->dev->devPrivates, subjectKey); + else + subj = dixLookupPrivate(&rec->client->devPrivates, subjectKey); + + obj = dixLookupPrivate(&rec->pWin->devPrivates, objectKey); + + /* Check send permission on window */ + rc = SELinuxDoCheck(subj, obj, SECCLASS_X_DRAWABLE, DixSendAccess, + &auditdata); + if (rc != Success) + goto err; + + /* Check send permission on specific event types */ + for (i = 0; i < rec->count; i++) { + type = rec->events[i].u.u.type; + class = (type & 128) ? SECCLASS_X_FAKEEVENT : SECCLASS_X_EVENT; + + rc = SELinuxEventToSID(type, obj->sid, &ev_sid); + if (rc != Success) + goto err; + + auditdata.event = type; + rc = SELinuxDoCheck(subj, &ev_sid, class, DixSendAccess, &auditdata); + if (rc != Success) + goto err; + } + return; +err: + rec->status = rc; +} + +static void +SELinuxReceive(CallbackListPtr *pcbl, pointer unused, pointer calldata) +{ + XaceReceiveAccessRec *rec = calldata; + SELinuxSubjectRec *subj; + SELinuxObjectRec *obj, ev_sid; + SELinuxAuditRec auditdata = { .client = NULL }; + security_class_t class; + int rc, i, type; + + subj = dixLookupPrivate(&rec->client->devPrivates, subjectKey); + obj = dixLookupPrivate(&rec->pWin->devPrivates, objectKey); + + /* Check receive permission on window */ + rc = SELinuxDoCheck(subj, obj, SECCLASS_X_DRAWABLE, DixReceiveAccess, + &auditdata); + if (rc != Success) + goto err; + + /* Check receive permission on specific event types */ + for (i = 0; i < rec->count; i++) { + type = rec->events[i].u.u.type; + class = (type & 128) ? SECCLASS_X_FAKEEVENT : SECCLASS_X_EVENT; + + rc = SELinuxEventToSID(type, obj->sid, &ev_sid); + if (rc != Success) + goto err; + + auditdata.event = type; + rc = SELinuxDoCheck(subj, &ev_sid, class, DixReceiveAccess, &auditdata); + if (rc != Success) + goto err; + } + return; +err: + rec->status = rc; +} + +static void +SELinuxExtension(CallbackListPtr *pcbl, pointer unused, pointer calldata) +{ + XaceExtAccessRec *rec = calldata; + SELinuxSubjectRec *subj, *serv; + SELinuxObjectRec *obj; + SELinuxAuditRec auditdata = { .client = rec->client }; + int rc; + + subj = dixLookupPrivate(&rec->client->devPrivates, subjectKey); + obj = dixLookupPrivate(&rec->ext->devPrivates, objectKey); + + /* If this is a new object that needs labeling, do it now */ + /* XXX there should be a separate callback for this */ + if (obj->sid == NULL) { + security_id_t sid; + + serv = dixLookupPrivate(&serverClient->devPrivates, subjectKey); + rc = SELinuxExtensionToSID(rec->ext->name, &sid); + if (rc != Success) { + rec->status = rc; + return; + } + + /* Perform a transition to obtain the final SID */ + if (avc_compute_create(serv->sid, sid, SECCLASS_X_EXTENSION, + &obj->sid) < 0) { + ErrorF("SELinux: a SID transition call failed!\n"); + rec->status = BadValue; + return; + } + } + + /* Perform the security check */ + auditdata.extension = rec->ext->name; + rc = SELinuxDoCheck(subj, obj, SECCLASS_X_EXTENSION, rec->access_mode, + &auditdata); + if (rc != Success) + rec->status = rc; +} + +static void +SELinuxSelection(CallbackListPtr *pcbl, pointer unused, pointer calldata) +{ + XaceSelectionAccessRec *rec = calldata; + SELinuxSubjectRec *subj; + SELinuxObjectRec *obj, *data; + Selection *pSel = *rec->ppSel; + Atom name = pSel->selection; + Mask access_mode = rec->access_mode; + SELinuxAuditRec auditdata = { .client = rec->client, .selection = name }; + security_id_t tsid; + int rc; + + subj = dixLookupPrivate(&rec->client->devPrivates, subjectKey); + obj = dixLookupPrivate(&pSel->devPrivates, objectKey); + + /* If this is a new object that needs labeling, do it now */ + if (access_mode & DixCreateAccess) { + rc = SELinuxSelectionToSID(name, subj, &obj->sid, &obj->poly); + if (rc != Success) + obj->sid = unlabeled_sid; + access_mode = DixSetAttrAccess; + } + /* If this is a polyinstantiated object, find the right instance */ + else if (obj->poly) { + rc = SELinuxSelectionToSID(name, subj, &tsid, NULL); + if (rc != Success) { + rec->status = rc; + return; + } + while (pSel->selection != name || obj->sid != tsid) { + if ((pSel = pSel->next) == NULL) + break; + obj = dixLookupPrivate(&pSel->devPrivates, objectKey); + } + + if (pSel) + *rec->ppSel = pSel; + else { + rec->status = BadMatch; + return; + } + } + + /* Perform the security check */ + rc = SELinuxDoCheck(subj, obj, SECCLASS_X_SELECTION, access_mode, + &auditdata); + if (rc != Success) + rec->status = rc; + + /* Label the content (advisory only) */ + if (access_mode & DixSetAttrAccess) { + data = dixLookupPrivate(&pSel->devPrivates, dataKey); + if (subj->sel_create_sid) + data->sid = subj->sel_create_sid; + else + data->sid = obj->sid; + } +} + +static void +SELinuxProperty(CallbackListPtr *pcbl, pointer unused, pointer calldata) +{ + XacePropertyAccessRec *rec = calldata; + SELinuxSubjectRec *subj; + SELinuxObjectRec *obj, *data; + PropertyPtr pProp = *rec->ppProp; + Atom name = pProp->propertyName; + SELinuxAuditRec auditdata = { .client = rec->client, .property = name }; + security_id_t tsid; + int rc; + + /* Don't care about the new content check */ + if (rec->access_mode & DixPostAccess) + return; + + subj = dixLookupPrivate(&rec->client->devPrivates, subjectKey); + obj = dixLookupPrivate(&pProp->devPrivates, objectKey); + + /* If this is a new object that needs labeling, do it now */ + if (rec->access_mode & DixCreateAccess) { + rc = SELinuxPropertyToSID(name, subj, &obj->sid, &obj->poly); + if (rc != Success) { + rec->status = rc; + return; + } + } + /* If this is a polyinstantiated object, find the right instance */ + else if (obj->poly) { + rc = SELinuxPropertyToSID(name, subj, &tsid, NULL); + if (rc != Success) { + rec->status = rc; + return; + } + while (pProp->propertyName != name || obj->sid != tsid) { + if ((pProp = pProp->next) == NULL) + break; + obj = dixLookupPrivate(&pProp->devPrivates, objectKey); + } + + if (pProp) + *rec->ppProp = pProp; + else { + rec->status = BadMatch; + return; + } + } + + /* Perform the security check */ + rc = SELinuxDoCheck(subj, obj, SECCLASS_X_PROPERTY, rec->access_mode, + &auditdata); + if (rc != Success) + rec->status = rc; + + /* Label the content (advisory only) */ + if (rec->access_mode & DixWriteAccess) { + data = dixLookupPrivate(&pProp->devPrivates, dataKey); + if (subj->prp_create_sid) + data->sid = subj->prp_create_sid; + else + data->sid = obj->sid; + } +} + +static void +SELinuxResource(CallbackListPtr *pcbl, pointer unused, pointer calldata) +{ + XaceResourceAccessRec *rec = calldata; + SELinuxSubjectRec *subj; + SELinuxObjectRec *obj; + SELinuxAuditRec auditdata = { .client = rec->client }; + Mask access_mode = rec->access_mode; + PrivateRec **privatePtr; + security_class_t class; + int rc, offset; + + subj = dixLookupPrivate(&rec->client->devPrivates, subjectKey); + + /* Determine if the resource object has a devPrivates field */ + offset = dixLookupPrivateOffset(rec->rtype); + if (offset < 0) { + /* No: use the SID of the owning client */ + class = SECCLASS_X_RESOURCE; + privatePtr = &clients[CLIENT_ID(rec->id)]->devPrivates; + obj = dixLookupPrivate(privatePtr, objectKey); + } else { + /* Yes: use the SID from the resource object itself */ + class = SELinuxTypeToClass(rec->rtype); + privatePtr = DEVPRIV_AT(rec->res, offset); + obj = dixLookupPrivate(privatePtr, objectKey); + } + + /* If this is a new object that needs labeling, do it now */ + if (access_mode & DixCreateAccess && offset >= 0) { + rc = SELinuxLabelResource(rec, subj, obj, class); + if (rc != Success) { + rec->status = rc; + return; + } + } + + /* Collapse generic resource permissions down to read/write */ + if (class == SECCLASS_X_RESOURCE) { + access_mode = !!(rec->access_mode & SELinuxReadMask); /* rd */ + access_mode |= !!(rec->access_mode & ~SELinuxReadMask) << 1; /* wr */ + } + + /* Perform the security check */ + auditdata.restype = rec->rtype; + auditdata.id = rec->id; + rc = SELinuxDoCheck(subj, obj, class, access_mode, &auditdata); + if (rc != Success) + rec->status = rc; + + /* Perform the background none check on windows */ + if (access_mode & DixCreateAccess && rec->rtype == RT_WINDOW) { + rc = SELinuxDoCheck(subj, obj, class, DixBlendAccess, &auditdata); + if (rc != Success) + ((WindowPtr)rec->res)->forcedBG = TRUE; + } +} + +static void +SELinuxScreen(CallbackListPtr *pcbl, pointer is_saver, pointer calldata) +{ + XaceScreenAccessRec *rec = calldata; + SELinuxSubjectRec *subj; + SELinuxObjectRec *obj; + SELinuxAuditRec auditdata = { .client = rec->client }; + Mask access_mode = rec->access_mode; + int rc; + + subj = dixLookupPrivate(&rec->client->devPrivates, subjectKey); + obj = dixLookupPrivate(&rec->screen->devPrivates, objectKey); + + /* If this is a new object that needs labeling, do it now */ + if (access_mode & DixCreateAccess) { + /* Perform a transition to obtain the final SID */ + if (avc_compute_create(subj->sid, subj->sid, SECCLASS_X_SCREEN, + &obj->sid) < 0) { + ErrorF("SELinux: a compute_create call failed!\n"); + rec->status = BadValue; + return; + } + } + + if (is_saver) + access_mode <<= 2; + + rc = SELinuxDoCheck(subj, obj, SECCLASS_X_SCREEN, access_mode, &auditdata); + if (rc != Success) + rec->status = rc; +} + +static void +SELinuxClient(CallbackListPtr *pcbl, pointer unused, pointer calldata) +{ + XaceClientAccessRec *rec = calldata; + SELinuxSubjectRec *subj; + SELinuxObjectRec *obj; + SELinuxAuditRec auditdata = { .client = rec->client }; + int rc; + + subj = dixLookupPrivate(&rec->client->devPrivates, subjectKey); + obj = dixLookupPrivate(&rec->target->devPrivates, objectKey); + + rc = SELinuxDoCheck(subj, obj, SECCLASS_X_CLIENT, rec->access_mode, + &auditdata); + if (rc != Success) + rec->status = rc; +} + +static void +SELinuxServer(CallbackListPtr *pcbl, pointer unused, pointer calldata) +{ + XaceServerAccessRec *rec = calldata; + SELinuxSubjectRec *subj; + SELinuxObjectRec *obj; + SELinuxAuditRec auditdata = { .client = rec->client }; + int rc; + + subj = dixLookupPrivate(&rec->client->devPrivates, subjectKey); + obj = dixLookupPrivate(&serverClient->devPrivates, objectKey); + + rc = SELinuxDoCheck(subj, obj, SECCLASS_X_SERVER, rec->access_mode, + &auditdata); + if (rc != Success) + rec->status = rc; +} + + +/* + * DIX Callbacks + */ + +static void +SELinuxClientState(CallbackListPtr *pcbl, pointer unused, pointer calldata) +{ + NewClientInfoRec *pci = calldata; + + switch (pci->client->clientState) { + case ClientStateInitial: + SELinuxLabelClient(pci->client); + break; + + default: + break; + } +} + +static void +SELinuxResourceState(CallbackListPtr *pcbl, pointer unused, pointer calldata) +{ + ResourceStateInfoRec *rec = calldata; + SELinuxSubjectRec *subj; + SELinuxObjectRec *obj; + WindowPtr pWin; + + if (rec->type != RT_WINDOW) + return; + if (rec->state != ResourceStateAdding) + return; + + pWin = (WindowPtr)rec->value; + subj = dixLookupPrivate(&wClient(pWin)->devPrivates, subjectKey); + + if (subj->sid) { + security_context_t ctx; + int rc = avc_sid_to_context_raw(subj->sid, &ctx); + if (rc < 0) + FatalError("SELinux: Failed to get security context!\n"); + rc = dixChangeWindowProperty(serverClient, + pWin, atom_client_ctx, XA_STRING, 8, + PropModeReplace, strlen(ctx), ctx, FALSE); + if (rc != Success) + FatalError("SELinux: Failed to set label property on window!\n"); + freecon(ctx); + } else + FatalError("SELinux: Unexpected unlabeled client found\n"); + + obj = dixLookupPrivate(&pWin->devPrivates, objectKey); + + if (obj->sid) { + security_context_t ctx; + int rc = avc_sid_to_context_raw(obj->sid, &ctx); + if (rc < 0) + FatalError("SELinux: Failed to get security context!\n"); + rc = dixChangeWindowProperty(serverClient, + pWin, atom_ctx, XA_STRING, 8, + PropModeReplace, strlen(ctx), ctx, FALSE); + if (rc != Success) + FatalError("SELinux: Failed to set label property on window!\n"); + freecon(ctx); + } else + FatalError("SELinux: Unexpected unlabeled window found\n"); +} + + +static int netlink_fd; + +static void +SELinuxBlockHandler(void *data, struct timeval **tv, void *read_mask) +{ +} + +static void +SELinuxWakeupHandler(void *data, int err, void *read_mask) +{ + if (FD_ISSET(netlink_fd, (fd_set *)read_mask)) + avc_netlink_check_nb(); +} + +void +SELinuxFlaskReset(void) +{ + /* Unregister callbacks */ + DeleteCallback(&ClientStateCallback, SELinuxClientState, NULL); + DeleteCallback(&ResourceStateCallback, SELinuxResourceState, NULL); + + XaceDeleteCallback(XACE_EXT_DISPATCH, SELinuxExtension, NULL); + XaceDeleteCallback(XACE_RESOURCE_ACCESS, SELinuxResource, NULL); + XaceDeleteCallback(XACE_DEVICE_ACCESS, SELinuxDevice, NULL); + XaceDeleteCallback(XACE_PROPERTY_ACCESS, SELinuxProperty, NULL); + XaceDeleteCallback(XACE_SEND_ACCESS, SELinuxSend, NULL); + XaceDeleteCallback(XACE_RECEIVE_ACCESS, SELinuxReceive, NULL); + XaceDeleteCallback(XACE_CLIENT_ACCESS, SELinuxClient, NULL); + XaceDeleteCallback(XACE_EXT_ACCESS, SELinuxExtension, NULL); + XaceDeleteCallback(XACE_SERVER_ACCESS, SELinuxServer, NULL); + XaceDeleteCallback(XACE_SELECTION_ACCESS, SELinuxSelection, NULL); + XaceDeleteCallback(XACE_SCREEN_ACCESS, SELinuxScreen, NULL); + XaceDeleteCallback(XACE_SCREENSAVER_ACCESS, SELinuxScreen, truep); + + /* Tear down SELinux stuff */ + audit_close(audit_fd); + avc_netlink_release_fd(); + RemoveBlockAndWakeupHandlers(SELinuxBlockHandler, SELinuxWakeupHandler, + NULL); + RemoveGeneralSocket(netlink_fd); + + avc_destroy(); +} + +void +SELinuxFlaskInit(void) +{ + struct selinux_opt avc_option = { AVC_OPT_SETENFORCE, (char *)0 }; + security_context_t ctx; + int ret = TRUE; + + switch(selinuxEnforcingState) { + case SELINUX_MODE_ENFORCING: + LogMessage(X_INFO, "SELinux: Configured in enforcing mode\n"); + avc_option.value = (char *)1; + break; + case SELINUX_MODE_PERMISSIVE: + LogMessage(X_INFO, "SELinux: Configured in permissive mode\n"); + avc_option.value = (char *)0; + break; + default: + avc_option.type = AVC_OPT_UNUSED; + break; + } + + /* Set up SELinux stuff */ + selinux_set_callback(SELINUX_CB_LOG, (union selinux_callback)SELinuxLog); + selinux_set_callback(SELINUX_CB_AUDIT, (union selinux_callback)SELinuxAudit); + + if (selinux_set_mapping(map) < 0) { + if (errno == EINVAL) { + ErrorF("SELinux: Invalid object class mapping, disabling SELinux support.\n"); + return; + } + FatalError("SELinux: Failed to set up security class mapping\n"); + } + + if (avc_open(&avc_option, 1) < 0) + FatalError("SELinux: Couldn't initialize SELinux userspace AVC\n"); + + if (security_get_initial_context_raw("unlabeled", &ctx) < 0) + FatalError("SELinux: Failed to look up unlabeled context\n"); + if (avc_context_to_sid_raw(ctx, &unlabeled_sid) < 0) + FatalError("SELinux: a context_to_SID call failed!\n"); + freecon(ctx); + + /* Prepare for auditing */ + audit_fd = audit_open(); + if (audit_fd < 0) + FatalError("SELinux: Failed to open the system audit log\n"); + + /* Allocate private storage */ + if (!dixRegisterPrivateKey(subjectKey, PRIVATE_XSELINUX, sizeof(SELinuxSubjectRec)) || + !dixRegisterPrivateKey(objectKey, PRIVATE_XSELINUX, sizeof(SELinuxObjectRec)) || + !dixRegisterPrivateKey(dataKey, PRIVATE_XSELINUX, sizeof(SELinuxObjectRec))) + FatalError("SELinux: Failed to allocate private storage.\n"); + + /* Create atoms for doing window labeling */ + atom_ctx = MakeAtom("_SELINUX_CONTEXT", 16, TRUE); + if (atom_ctx == BAD_RESOURCE) + FatalError("SELinux: Failed to create atom\n"); + atom_client_ctx = MakeAtom("_SELINUX_CLIENT_CONTEXT", 23, TRUE); + if (atom_client_ctx == BAD_RESOURCE) + FatalError("SELinux: Failed to create atom\n"); + + netlink_fd = avc_netlink_acquire_fd(); + AddGeneralSocket(netlink_fd); + RegisterBlockAndWakeupHandlers(SELinuxBlockHandler, SELinuxWakeupHandler, + NULL); + + /* Register callbacks */ + ret &= AddCallback(&ClientStateCallback, SELinuxClientState, NULL); + ret &= AddCallback(&ResourceStateCallback, SELinuxResourceState, NULL); + + ret &= XaceRegisterCallback(XACE_EXT_DISPATCH, SELinuxExtension, NULL); + ret &= XaceRegisterCallback(XACE_RESOURCE_ACCESS, SELinuxResource, NULL); + ret &= XaceRegisterCallback(XACE_DEVICE_ACCESS, SELinuxDevice, NULL); + ret &= XaceRegisterCallback(XACE_PROPERTY_ACCESS, SELinuxProperty, NULL); + ret &= XaceRegisterCallback(XACE_SEND_ACCESS, SELinuxSend, NULL); + ret &= XaceRegisterCallback(XACE_RECEIVE_ACCESS, SELinuxReceive, NULL); + ret &= XaceRegisterCallback(XACE_CLIENT_ACCESS, SELinuxClient, NULL); + ret &= XaceRegisterCallback(XACE_EXT_ACCESS, SELinuxExtension, NULL); + ret &= XaceRegisterCallback(XACE_SERVER_ACCESS, SELinuxServer, NULL); + ret &= XaceRegisterCallback(XACE_SELECTION_ACCESS, SELinuxSelection, NULL); + ret &= XaceRegisterCallback(XACE_SCREEN_ACCESS, SELinuxScreen, NULL); + ret &= XaceRegisterCallback(XACE_SCREENSAVER_ACCESS, SELinuxScreen, truep); + if (!ret) + FatalError("SELinux: Failed to register one or more callbacks\n"); + + /* Label objects that were created before we could register ourself */ + SELinuxLabelInitial(); +} |