5c5a1eaf68
Big boss says UniSA isn't unique enough. Who am I to argue?
316 lines
8.6 KiB
C
316 lines
8.6 KiB
C
/*
|
|
* Copyright 2007-2008 Peter Hutterer
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
|
* copy of this software and associated documentation files (the "Software"),
|
|
* to deal in the Software without restriction, including without limitation
|
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
* and/or sell copies of the Software, and to permit persons to whom the
|
|
* Software is furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice (including the next
|
|
* paragraph) 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 AUTHORS OR COPYRIGHT HOLDERS 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.
|
|
*
|
|
* Author: Peter Hutterer, University of South Australia, NICTA
|
|
*/
|
|
|
|
/* This file controls the access control lists for each window.
|
|
* Each device can be explicitely allowed or denied access to a given window.
|
|
*/
|
|
|
|
#ifdef HAVE_DIX_CONFIG_H
|
|
#include <dix-config.h>
|
|
#endif
|
|
|
|
#include <X11/Xlib.h>
|
|
#include <X11/extensions/XI.h>
|
|
#include "exglobals.h"
|
|
|
|
#include "input.h"
|
|
#include "inputstr.h"
|
|
#include "windowstr.h"
|
|
|
|
|
|
/* Only one single client can be responsible for window access control. */
|
|
static ClientPtr ACClient = NULL;
|
|
|
|
|
|
/* Forward declarations */
|
|
static void acReplaceList(DeviceIntPtr** list,
|
|
int* count,
|
|
DeviceIntPtr* devices,
|
|
int ndevices);
|
|
|
|
/* Register global window access control client
|
|
* Return True on success or False otherwise.
|
|
*/
|
|
|
|
Bool
|
|
ACRegisterClient(ClientPtr client)
|
|
{
|
|
if (ACClient && ACClient != client)
|
|
return False;
|
|
|
|
ACClient = client;
|
|
return True;
|
|
}
|
|
|
|
|
|
/* Unregister global client. If client is not the registered client, nothing
|
|
* happens and False is returned. If no client is registered, return True.
|
|
* Returns True if client was registred and is now unregistered.
|
|
*/
|
|
|
|
Bool
|
|
ACUnregisterClient(ClientPtr client)
|
|
{
|
|
if (ACClient && ACClient != client)
|
|
return False;
|
|
|
|
ACClient = NULL;
|
|
return True;
|
|
}
|
|
|
|
/* Clears all access control for the window and remove the default rule,
|
|
* depending on what is set. */
|
|
int ACClearWindowAccess(ClientPtr client,
|
|
WindowPtr win,
|
|
int what)
|
|
{
|
|
if (client != ACClient && client != wClient(win))
|
|
return BadAccess;
|
|
|
|
if (!win->optional)
|
|
{
|
|
/* we shouldn't get here if programmers know what they're doing.
|
|
* A client should not request to clear a window's access controls
|
|
* if they've never been set before anyway. If they do, do nothing and
|
|
* let the client figure out what to do next.
|
|
*/
|
|
return Success;
|
|
}
|
|
|
|
if (what & WindowAccessClearPerm)
|
|
{
|
|
xfree(win->optional->access.perm);
|
|
win->optional->access.perm = NULL;
|
|
win->optional->access.nperm = 0;
|
|
}
|
|
|
|
if (what & WindowAccessClearDeny)
|
|
{
|
|
xfree(win->optional->access.deny);
|
|
win->optional->access.deny = NULL;
|
|
win->optional->access.ndeny = 0;
|
|
}
|
|
|
|
if (what & WindowAccessClearRule)
|
|
win->optional->access.defaultRule = WindowAccessNoRule;
|
|
|
|
return Success;
|
|
}
|
|
|
|
/*
|
|
* Changes window access control.
|
|
*
|
|
* Returns Success or BadAccess if the client is not allowed to change
|
|
* anything.
|
|
*/
|
|
|
|
int
|
|
ACChangeWindowAccess(ClientPtr client,
|
|
WindowPtr win,
|
|
int defaultRule,
|
|
DeviceIntPtr* perm_devices,
|
|
int nperm,
|
|
DeviceIntPtr* deny_devices,
|
|
int ndeny)
|
|
{
|
|
if (client != ACClient && client != wClient(win))
|
|
return BadAccess;
|
|
|
|
if (!win->optional && !MakeWindowOptional(win))
|
|
{
|
|
ErrorF("[dix] ACChangeWindowAcccess: Failed to make window optional.\n");
|
|
return BadImplementation;
|
|
}
|
|
|
|
if (defaultRule != WindowAccessKeepRule)
|
|
win->optional->access.defaultRule = defaultRule;
|
|
|
|
if (nperm)
|
|
{
|
|
acReplaceList(&win->optional->access.perm,
|
|
&win->optional->access.nperm,
|
|
perm_devices, nperm);
|
|
}
|
|
|
|
if (ndeny)
|
|
{
|
|
acReplaceList(&win->optional->access.deny,
|
|
&win->optional->access.ndeny,
|
|
deny_devices, ndeny);
|
|
}
|
|
|
|
return Success;
|
|
}
|
|
|
|
static void
|
|
acReplaceList(DeviceIntPtr** list,
|
|
int* count,
|
|
DeviceIntPtr* devices,
|
|
int ndevices)
|
|
{
|
|
xfree(*list);
|
|
*list = NULL;
|
|
*count = 0;
|
|
|
|
if (ndevices)
|
|
{
|
|
*list =
|
|
xalloc(ndevices * sizeof(DeviceIntPtr*));
|
|
if (!*list)
|
|
{
|
|
ErrorF("[dix] ACChangeWindowAccess: out of memory\n");
|
|
return;
|
|
}
|
|
memcpy(*list,
|
|
devices,
|
|
ndevices * sizeof(DeviceIntPtr));
|
|
*count = ndevices;
|
|
}
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Query the given window for the devices allowed to access a window.
|
|
* The caller is responsible for freeing perm and deny.
|
|
*/
|
|
void
|
|
ACQueryWindowAccess(WindowPtr win,
|
|
int* defaultRule,
|
|
DeviceIntPtr** perm,
|
|
int* nperm,
|
|
DeviceIntPtr** deny,
|
|
int* ndeny)
|
|
{
|
|
*defaultRule = WindowAccessNoRule;
|
|
*perm = NULL;
|
|
*nperm = 0;
|
|
*deny = NULL;
|
|
*ndeny = 0;
|
|
|
|
if (!win->optional)
|
|
return;
|
|
|
|
*defaultRule = win->optional->access.defaultRule;
|
|
|
|
if (win->optional->access.nperm)
|
|
{
|
|
*nperm = win->optional->access.nperm;
|
|
*perm = (DeviceIntPtr*)xalloc(*nperm * sizeof(DeviceIntPtr));
|
|
if (!*perm)
|
|
{
|
|
ErrorF("[dix] ACQuerywinAccess: xalloc failure\n");
|
|
return;
|
|
}
|
|
memcpy(*perm,
|
|
win->optional->access.perm,
|
|
*nperm * sizeof(DeviceIntPtr));
|
|
}
|
|
|
|
if (win->optional->access.ndeny)
|
|
{
|
|
*ndeny = win->optional->access.ndeny;
|
|
*deny = (DeviceIntPtr*)xalloc(*ndeny * sizeof(DeviceIntPtr));
|
|
if (!*deny)
|
|
{
|
|
ErrorF("[dix] ACQuerywinAccess: xalloc failure\n");
|
|
return;
|
|
}
|
|
memcpy(*deny,
|
|
win->optional->access.deny,
|
|
*ndeny * sizeof(DeviceIntPtr));
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Check if the given device is allowed to send events to the window. Returns
|
|
* true if device is allowed or false otherwise.
|
|
*
|
|
* Checks are done in the following order until a result is found:
|
|
* If the device is explicitely permitted, allow.
|
|
* If the window has a default of DenyAll, do not allow.
|
|
* If the device is explicitely denied, do not allow.
|
|
* If the window has a default of AllowAll, allow.
|
|
* Check parent window. Rinse, wash, repeat.
|
|
* If no rule could be found, allow.
|
|
*/
|
|
Bool
|
|
ACDeviceAllowed(WindowPtr win, DeviceIntPtr dev, xEvent* xE)
|
|
{
|
|
int i;
|
|
|
|
if (!win) /* happens for parent of RootWindow */
|
|
return True;
|
|
|
|
/* there's a number of events we don't care about */
|
|
switch (xE->u.u.type)
|
|
{
|
|
case ButtonPress:
|
|
case ButtonRelease:
|
|
case MotionNotify:
|
|
case EnterNotify:
|
|
case LeaveNotify:
|
|
case KeyPress:
|
|
case KeyRelease:
|
|
break;
|
|
default:
|
|
if (xE->u.u.type == DeviceMotionNotify ||
|
|
xE->u.u.type == DeviceButtonPress ||
|
|
xE->u.u.type == DeviceButtonRelease ||
|
|
xE->u.u.type == DeviceKeyPress ||
|
|
xE->u.u.type == DeviceKeyRelease ||
|
|
xE->u.u.type == DeviceEnterNotify ||
|
|
xE->u.u.type == DeviceLeaveNotify)
|
|
{
|
|
break;
|
|
}
|
|
return True;
|
|
}
|
|
|
|
|
|
if (!win->optional) /* no list, check parent */
|
|
return ACDeviceAllowed(win->parent, dev, xE);
|
|
|
|
for (i = 0; i < win->optional->access.nperm; i++)
|
|
{
|
|
if (win->optional->access.perm[i]->id == dev->id)
|
|
return True;
|
|
}
|
|
|
|
if (win->optional->access.defaultRule == WindowAccessDenyAll)
|
|
return False;
|
|
|
|
for (i = 0; i < win->optional->access.ndeny; i++)
|
|
{
|
|
if (win->optional->access.deny[i]->id == dev->id)
|
|
return False;
|
|
}
|
|
|
|
if (win->optional->access.defaultRule == WindowAccessAllowAll)
|
|
return True;
|
|
|
|
return ACDeviceAllowed(win->parent, dev, xE);
|
|
}
|
|
|