summaryrefslogtreecommitdiff
path: root/Xext
diff options
context:
space:
mode:
authorEamon Walsh <ewalsh@tycho.nsa.gov>2007-10-23 20:58:48 -0400
committerEamon Walsh <ewalsh@moss-charon.epoch.ncsc.mil>2007-10-23 20:58:48 -0400
commit46521f529841e032e198e5df87974088548a68de (patch)
tree11ed5c24906ef6d890d4247b07c24fa265580302 /Xext
parent825f09dffd94cfcd0562a01c5181998503851461 (diff)
xselinux: Add basic support for selection access control and redirection.
Probably not fully baked yet. It's difficult to test since so few apps actually follow the ICCCM with respect to cut & paste.
Diffstat (limited to 'Xext')
-rw-r--r--Xext/xselinux.c371
-rw-r--r--Xext/xselinux.h37
2 files changed, 348 insertions, 60 deletions
diff --git a/Xext/xselinux.c b/Xext/xselinux.c
index ef5be571f..f11bc1aaa 100644
--- a/Xext/xselinux.c
+++ b/Xext/xselinux.c
@@ -65,6 +65,15 @@ typedef struct {
char *command;
} SELinuxStateRec;
+/* selection manager */
+typedef struct {
+ Atom selection;
+ security_id_t sid;
+} SELinuxSelectionRec;
+
+static ClientPtr selectionManager;
+static Window selectionWindow;
+
/* audit file descriptor */
static int audit_fd;
@@ -99,6 +108,10 @@ static unsigned numKnownTypes;
static security_id_t *knownEvents;
static unsigned numKnownEvents;
+/* Array of selection SID structures */
+static SELinuxSelectionRec *knownSelections;
+static unsigned numKnownSelections;
+
/* dynamically allocated security classes and permissions */
static struct security_class_mapping map[] = {
{ "x_drawable", { "read", "write", "destroy", "create", "getattr", "setattr", "list_property", "get_property", "set_property", "", "", "list_child", "add_child", "remove_child", "hide", "show", "blend", "override", "", "", "", "", "send", "receive", "", "manage", NULL }},
@@ -130,6 +143,52 @@ static pointer truep = (pointer)1;
*/
/*
+ * Looks up the SID corresponding to the given selection atom
+ */
+static int
+SELinuxSelectionToSID(Atom selection, SELinuxStateRec *sid_return)
+{
+ const char *name;
+ unsigned i, size;
+
+ for (i = 0; i < numKnownSelections; i++)
+ if (knownSelections[i].selection == selection) {
+ sid_return->sid = knownSelections[i].sid;
+ return Success;
+ }
+
+ /* Need to increase size of array */
+ i = numKnownSelections;
+ size = (i + 1) * sizeof(SELinuxSelectionRec);
+ knownSelections = xrealloc(knownSelections, size);
+ if (!knownSelections)
+ return BadAlloc;
+ knownSelections[i].selection = selection;
+
+ /* Look in the mappings of selection names to contexts */
+ name = NameForAtom(selection);
+ if (name) {
+ security_context_t con;
+ security_id_t sid;
+
+ if (selabel_lookup(label_hnd, &con, name, SELABEL_X_SELN) < 0) {
+ ErrorF("XSELinux: a selection label lookup failed!\n");
+ return BadValue;
+ }
+ /* Get a SID for context */
+ if (avc_context_to_sid(con, &sid) < 0) {
+ ErrorF("XSELinux: a context_to_SID call failed!\n");
+ return BadAlloc;
+ }
+ freecon(con);
+ knownSelections[i].sid = sid_return->sid = sid;
+ } else
+ knownSelections[i].sid = sid_return->sid = unlabeled_sid;
+
+ return Success;
+}
+
+/*
* Looks up the SID corresponding to the given event type
*/
static int
@@ -240,10 +299,71 @@ SELinuxDoCheck(int clientIndex, SELinuxStateRec *subj, SELinuxStateRec *obj,
}
/*
+ * Labels a newly connected client.
+ */
+static void
+SELinuxLabelClient(ClientPtr client)
+{
+ XtransConnInfo ci = ((OsCommPtr)client->osPrivate)->trans_conn;
+ SELinuxStateRec *state;
+ security_context_t ctx;
+
+ state = dixLookupPrivate(&client->devPrivates, stateKey);
+ sidput(state->sid);
+
+ if (_XSERVTransIsLocal(ci)) {
+ int fd = _XSERVTransGetConnectionNumber(ci);
+ struct ucred creds;
+ socklen_t len = sizeof(creds);
+ char path[PATH_MAX + 1];
+ size_t bytes;
+
+ /* For local clients, can get context from the socket */
+ if (getpeercon(fd, &ctx) < 0)
+ FatalError("Client %d: couldn't get context from socket\n",
+ client->index);
+
+ /* Try and determine the client's executable name */
+ memset(&creds, 0, sizeof(creds));
+ if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &creds, &len) < 0)
+ goto finish;
+
+ snprintf(path, PATH_MAX + 1, "/proc/%d/cmdline", creds.pid);
+ fd = open(path, O_RDONLY);
+ if (fd < 0)
+ goto finish;
+
+ bytes = read(fd, path, PATH_MAX + 1);
+ close(fd);
+ if (bytes <= 0)
+ goto finish;
+
+ state->command = xalloc(bytes);
+ if (!state->command)
+ goto finish;
+
+ memcpy(state->command, path, bytes);
+ state->command[bytes - 1] = 0;
+ } else
+ /* For remote clients, need to use a default context */
+ if (selabel_lookup(label_hnd, &ctx, NULL, SELABEL_X_CLIENT) < 0)
+ FatalError("Client %d: couldn't get default remote context\n",
+ client->index);
+
+finish:
+ /* Get a SID from the context */
+ if (avc_context_to_sid(ctx, &state->sid) < 0)
+ FatalError("Client %d: context_to_sid(%s) failed\n",
+ client->index, ctx);
+
+ freecon(ctx);
+}
+
+/*
* Labels initial server objects.
*/
static void
-SELinuxFixupLabels(void)
+SELinuxLabelInitial(void)
{
int i;
XaceScreenAccessRec srec;
@@ -674,6 +794,28 @@ SELinuxServer(CallbackListPtr *pcbl, pointer unused, pointer calldata)
rec->status = rc;
}
+static void
+SELinuxSelection(CallbackListPtr *pcbl, pointer unused, pointer calldata)
+{
+ XaceSelectionAccessRec *rec = (XaceSelectionAccessRec *)calldata;
+ SELinuxStateRec *subj, sel_sid;
+ SELinuxAuditRec auditdata = { rec->client, NULL, 0, 0, 0, NULL };
+ int rc;
+
+ subj = dixLookupPrivate(&rec->client->devPrivates, stateKey);
+
+ rc = SELinuxSelectionToSID(rec->name, &sel_sid);
+ if (rc != Success) {
+ rec->status = rc;
+ return;
+ }
+
+ rc = SELinuxDoCheck(rec->client->index, subj, &sel_sid,
+ SECCLASS_X_SELECTION, rec->access_mode, &auditdata);
+ if (rc != Success)
+ rec->status = rc;
+}
+
/*
* DIX Callbacks
@@ -683,63 +825,23 @@ static void
SELinuxClientState(CallbackListPtr *pcbl, pointer unused, pointer calldata)
{
NewClientInfoRec *pci = calldata;
- ClientPtr client = pci->client;
- XtransConnInfo ci = ((OsCommPtr)client->osPrivate)->trans_conn;
- SELinuxStateRec *state;
- security_context_t ctx;
-
- if (client->clientState != ClientStateInitial)
- return;
-
- state = dixLookupPrivate(&client->devPrivates, stateKey);
- sidput(state->sid);
-
- if (_XSERVTransIsLocal(ci)) {
- int fd = _XSERVTransGetConnectionNumber(ci);
- struct ucred creds;
- socklen_t len = sizeof(creds);
- char path[PATH_MAX + 1];
- size_t bytes;
-
- /* For local clients, can get context from the socket */
- if (getpeercon(fd, &ctx) < 0)
- FatalError("Client %d: couldn't get context from socket\n",
- client->index);
-
- /* Try and determine the client's executable name */
- memset(&creds, 0, sizeof(creds));
- if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &creds, &len) < 0)
- goto finish;
-
- snprintf(path, PATH_MAX + 1, "/proc/%d/cmdline", creds.pid);
- fd = open(path, O_RDONLY);
- if (fd < 0)
- goto finish;
-
- bytes = read(fd, path, PATH_MAX + 1);
- close(fd);
- if (bytes <= 0)
- goto finish;
- state->command = xalloc(bytes);
- if (!state->command)
- goto finish;
-
- memcpy(state->command, path, bytes);
- state->command[bytes - 1] = 0;
- } else
- /* For remote clients, need to use a default context */
- if (selabel_lookup(label_hnd, &ctx, NULL, SELABEL_X_CLIENT) < 0)
- FatalError("Client %d: couldn't get default remote context\n",
- client->index);
+ switch (pci->client->clientState) {
+ case ClientStateInitial:
+ SELinuxLabelClient(pci->client);
+ break;
-finish:
- /* Get a SID from the context */
- if (avc_context_to_sid(ctx, &state->sid) < 0)
- FatalError("Client %d: context_to_sid(%s) failed\n",
- client->index, ctx);
+ case ClientStateRetained:
+ case ClientStateGone:
+ if (pci->client == selectionManager) {
+ selectionManager = NULL;
+ selectionWindow = 0;
+ }
+ break;
- freecon(ctx);
+ default:
+ break;
+ }
}
static void
@@ -788,6 +890,50 @@ SELinuxResourceState(CallbackListPtr *pcbl, pointer unused, pointer calldata)
FatalError("XSELinux: Unexpected unlabeled window found\n");
}
+static void
+SELinuxSelectionState(CallbackListPtr *pcbl, pointer unused, pointer calldata)
+{
+ SelectionInfoRec *rec = calldata;
+ SELinuxStateRec *subj, *obj;
+
+ switch (rec->kind) {
+ case SelectionSetOwner:
+ /* save off the "real" owner of the selection */
+ rec->selection->alt_client = rec->selection->client;
+ rec->selection->alt_window = rec->selection->window;
+
+ /* figure out the new label for the content */
+ subj = dixLookupPrivate(&rec->client->devPrivates, stateKey);
+ obj = dixLookupPrivate(&rec->selection->devPrivates, stateKey);
+ sidput(obj->sid);
+
+ if (avc_compute_create(subj->sid, subj->sid, SECCLASS_X_SELECTION,
+ &obj->sid) < 0) {
+ ErrorF("XSELinux: a compute_create call failed!\n");
+ obj->sid = unlabeled_sid;
+ }
+ break;
+
+ case SelectionGetOwner:
+ /* restore the real owner */
+ rec->selection->window = rec->selection->alt_window;
+ break;
+
+ case SelectionConvertSelection:
+ /* redirect the convert request if necessary */
+ if (selectionManager && selectionManager != rec->client) {
+ rec->selection->client = selectionManager;
+ rec->selection->window = selectionWindow;
+ } else {
+ rec->selection->client = rec->selection->alt_client;
+ rec->selection->window = rec->selection->alt_window;
+ }
+ break;
+ default:
+ break;
+ }
+}
+
/*
* DevPrivates Callbacks
@@ -823,9 +969,108 @@ SELinuxStateFree(CallbackListPtr *pcbl, pointer unused, pointer calldata)
*/
static int
+ProcSELinuxQueryVersion(ClientPtr client)
+{
+ SELinuxQueryVersionReply rep;
+ /*
+ REQUEST(SELinuxQueryVersionReq);
+ REQUEST_SIZE_MATCH (SELinuxQueryVersionReq);
+ */
+
+ rep.type = X_Reply;
+ rep.length = 0;
+ rep.sequenceNumber = client->sequence;
+ rep.server_major = XSELINUX_MAJOR_VERSION;
+ rep.server_minor = XSELINUX_MINOR_VERSION;
+ if (client->swapped) {
+ int n;
+ swaps(&rep.sequenceNumber, n);
+ swapl(&rep.length, n);
+ swaps(&rep.server_major, n);
+ swaps(&rep.server_minor, n);
+ }
+ WriteToClient(client, sizeof(rep), (char *)&rep);
+ return (client->noClientException);
+}
+
+static int
+ProcSELinuxSetSelectionManager(ClientPtr client)
+{
+ REQUEST(SELinuxSetSelectionManagerReq);
+ WindowPtr pWin;
+ int rc;
+
+ REQUEST_SIZE_MATCH(SELinuxSetSelectionManagerReq);
+
+ if (stuff->window == None) {
+ selectionManager = NULL;
+ selectionWindow = None;
+ } else {
+ rc = dixLookupResource((pointer *)&pWin, stuff->window, RT_WINDOW,
+ client, DixGetAttrAccess);
+ if (rc != Success)
+ return rc;
+
+ selectionManager = client;
+ selectionWindow = stuff->window;
+ }
+
+ return Success;
+}
+
+static int
ProcSELinuxDispatch(ClientPtr client)
{
- return BadRequest;
+ REQUEST(xReq);
+ switch (stuff->data) {
+ case X_SELinuxQueryVersion:
+ return ProcSELinuxQueryVersion(client);
+ case X_SELinuxSetSelectionManager:
+ return ProcSELinuxSetSelectionManager(client);
+ default:
+ return BadRequest;
+ }
+}
+
+static int
+SProcSELinuxQueryVersion(ClientPtr client)
+{
+ REQUEST(SELinuxQueryVersionReq);
+ int n;
+
+ REQUEST_SIZE_MATCH (SELinuxQueryVersionReq);
+ swaps(&stuff->client_major,n);
+ swaps(&stuff->client_minor,n);
+ return ProcSELinuxQueryVersion(client);
+}
+
+static int
+SProcSELinuxSetSelectionManager(ClientPtr client)
+{
+ REQUEST(SELinuxSetSelectionManagerReq);
+ int n;
+
+ REQUEST_SIZE_MATCH (SELinuxSetSelectionManagerReq);
+ swapl(&stuff->window,n);
+ return ProcSELinuxSetSelectionManager(client);
+}
+
+static int
+SProcSELinuxDispatch(ClientPtr client)
+{
+ REQUEST(xReq);
+ int n;
+
+ swaps(&stuff->length, n);
+
+ switch (stuff->data) {
+ case X_SELinuxQueryVersion:
+ return SProcSELinuxQueryVersion(client);
+ case X_SELinuxSetSelectionManager:
+ return SProcSELinuxSetSelectionManager(client);
+ default:
+ return BadRequest;
+ }
}
@@ -839,6 +1084,7 @@ SELinuxResetProc(ExtensionEntry *extEntry)
/* Unregister callbacks */
DeleteCallback(&ClientStateCallback, SELinuxClientState, NULL);
DeleteCallback(&ResourceStateCallback, SELinuxResourceState, NULL);
+ DeleteCallback(&SelectionCallback, SELinuxSelectionState, NULL);
XaceDeleteCallback(XACE_EXT_DISPATCH, SELinuxExtension, NULL);
XaceDeleteCallback(XACE_RESOURCE_ACCESS, SELinuxResource, NULL);
@@ -849,7 +1095,7 @@ SELinuxResetProc(ExtensionEntry *extEntry)
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_SELECTION_ACCESS, SELinuxSelection, NULL);
XaceDeleteCallback(XACE_SCREEN_ACCESS, SELinuxScreen, NULL);
XaceDeleteCallback(XACE_SCREENSAVER_ACCESS, SELinuxScreen, truep);
@@ -863,6 +1109,10 @@ SELinuxResetProc(ExtensionEntry *extEntry)
avc_active = 0;
/* Free local state */
+ xfree(knownSelections);
+ knownSelections = NULL;
+ numKnownSelections = 0;
+
xfree(knownEvents);
knownEvents = NULL;
numKnownEvents = 0;
@@ -929,6 +1179,7 @@ XSELinuxExtensionInit(INITARGS)
ret &= AddCallback(&ClientStateCallback, SELinuxClientState, NULL);
ret &= AddCallback(&ResourceStateCallback, SELinuxResourceState, NULL);
+ ret &= AddCallback(&SelectionCallback, SELinuxSelectionState, NULL);
ret &= XaceRegisterCallback(XACE_EXT_DISPATCH, SELinuxExtension, NULL);
ret &= XaceRegisterCallback(XACE_RESOURCE_ACCESS, SELinuxResource, NULL);
@@ -939,7 +1190,7 @@ XSELinuxExtensionInit(INITARGS)
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_SELECTION_ACCESS, SELinuxSelection, NULL);
ret &= XaceRegisterCallback(XACE_SCREEN_ACCESS, SELinuxScreen, NULL);
ret &= XaceRegisterCallback(XACE_SCREENSAVER_ACCESS, SELinuxScreen, truep);
if (!ret)
@@ -948,9 +1199,9 @@ XSELinuxExtensionInit(INITARGS)
/* Add extension to server */
extEntry = AddExtension(XSELINUX_EXTENSION_NAME,
XSELinuxNumberEvents, XSELinuxNumberErrors,
- ProcSELinuxDispatch, ProcSELinuxDispatch,
+ ProcSELinuxDispatch, SProcSELinuxDispatch,
SELinuxResetProc, StandardMinorOpcode);
/* Label objects that were created before we could register ourself */
- SELinuxFixupLabels();
+ SELinuxLabelInitial();
}
diff --git a/Xext/xselinux.h b/Xext/xselinux.h
index 407b81f93..691154d1d 100644
--- a/Xext/xselinux.h
+++ b/Xext/xselinux.h
@@ -29,6 +29,43 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#define XSELinuxNumberEvents 0
#define XSELinuxNumberErrors 0
+/* Extension protocol */
+#define X_SELinuxQueryVersion 0
+#define X_SELinuxSetSelectionManager 1
+
+typedef struct _SELinuxQueryVersion {
+ CARD8 reqType;
+ CARD8 SELinuxReqType;
+ CARD16 length;
+ CARD8 client_major;
+ CARD8 client_minor;
+ CARD16 unused;
+} SELinuxQueryVersionReq;
+#define sz_SELinuxQueryVersionReq 8
+
+typedef struct {
+ CARD8 type;
+ CARD8 pad1;
+ CARD16 sequenceNumber;
+ CARD32 length;
+ CARD16 server_major;
+ CARD16 server_minor;
+ CARD32 pad2;
+ CARD32 pad3;
+ CARD32 pad4;
+ CARD32 pad5;
+ CARD32 pad6;
+} SELinuxQueryVersionReply;
+#define sz_SELinuxQueryVersionReply 32
+
+typedef struct _SELinuxSetSelectionManager {
+ CARD8 reqType;
+ CARD8 SELinuxReqType;
+ CARD16 length;
+ CARD32 window;
+} SELinuxSetSelectionManagerReq;
+#define sz_SELinuxSetSelectionManagerReq 8
+
/* Private Flask definitions */
#define SECCLASS_X_DRAWABLE 1
#define SECCLASS_X_SCREEN 2