936 lines
27 KiB
C
936 lines
27 KiB
C
/************************************************************
|
|
|
|
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 = client->majorOp;
|
|
minor = client->minorOp;
|
|
}
|
|
}
|
|
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();
|
|
}
|