1085 lines
34 KiB
C
1085 lines
34 KiB
C
/*
|
|
|
|
Copyright 1996, 1998 The Open Group
|
|
|
|
Permission to use, copy, modify, distribute, and sell this software and its
|
|
documentation for any purpose is hereby granted without fee, provided that
|
|
the above copyright notice appear in all copies and that both that
|
|
copyright notice and this permission notice appear in supporting
|
|
documentation.
|
|
|
|
The above copyright notice and 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
|
|
OPEN GROUP 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.
|
|
|
|
Except as contained in this notice, the name of The Open Group shall not be
|
|
used in advertising or otherwise to promote the sale, use or other dealings
|
|
in this Software without prior written authorization from The Open Group.
|
|
|
|
*/
|
|
|
|
#ifdef HAVE_DIX_CONFIG_H
|
|
#include <dix-config.h>
|
|
#endif
|
|
|
|
#include "scrnintstr.h"
|
|
#include "inputstr.h"
|
|
#include "windowstr.h"
|
|
#include "propertyst.h"
|
|
#include "colormapst.h"
|
|
#include "privates.h"
|
|
#include "registry.h"
|
|
#include "xacestr.h"
|
|
#include "securitysrv.h"
|
|
#include <X11/extensions/securproto.h>
|
|
#include "modinit.h"
|
|
#include "protocol-versions.h"
|
|
|
|
/* Extension stuff */
|
|
static int SecurityErrorBase; /* first Security error number */
|
|
static int SecurityEventBase; /* first Security event number */
|
|
|
|
RESTYPE SecurityAuthorizationResType; /* resource type for authorizations */
|
|
static RESTYPE RTEventClient;
|
|
|
|
static CallbackListPtr SecurityValidateGroupCallback = NULL;
|
|
|
|
/* Private state record */
|
|
static DevPrivateKeyRec stateKeyRec;
|
|
|
|
#define stateKey (&stateKeyRec)
|
|
|
|
/* This is what we store as client security state */
|
|
typedef struct {
|
|
int haveState;
|
|
unsigned int trustLevel;
|
|
XID authId;
|
|
} SecurityStateRec;
|
|
|
|
/* Extensions that untrusted clients shouldn't have access to */
|
|
static const char *SecurityTrustedExtensions[] = {
|
|
"XC-MISC",
|
|
"BIG-REQUESTS",
|
|
"XpExtension",
|
|
NULL
|
|
};
|
|
|
|
/*
|
|
* Access modes that untrusted clients are allowed on trusted objects.
|
|
*/
|
|
static const Mask SecurityResourceMask =
|
|
DixGetAttrAccess | DixReceiveAccess | DixListPropAccess |
|
|
DixGetPropAccess | DixListAccess;
|
|
static const Mask SecurityWindowExtraMask = DixRemoveAccess;
|
|
static const Mask SecurityRootWindowExtraMask =
|
|
DixReceiveAccess | DixSendAccess | DixAddAccess | DixRemoveAccess;
|
|
static const Mask SecurityDeviceMask =
|
|
DixGetAttrAccess | DixReceiveAccess | DixGetFocusAccess |
|
|
DixGrabAccess | DixSetAttrAccess | DixUseAccess;
|
|
static const Mask SecurityServerMask = DixGetAttrAccess | DixGrabAccess;
|
|
static const Mask SecurityClientMask = DixGetAttrAccess;
|
|
|
|
/* SecurityAudit
|
|
*
|
|
* Arguments:
|
|
* format is the formatting string to be used to interpret the
|
|
* remaining arguments.
|
|
*
|
|
* Returns: nothing.
|
|
*
|
|
* Side Effects:
|
|
* Writes the message to the log file if security logging is on.
|
|
*/
|
|
|
|
static void
|
|
_X_ATTRIBUTE_PRINTF(1, 2)
|
|
SecurityAudit(const char *format, ...)
|
|
{
|
|
va_list args;
|
|
|
|
if (auditTrailLevel < SECURITY_AUDIT_LEVEL)
|
|
return;
|
|
va_start(args, format);
|
|
VAuditF(format, args);
|
|
va_end(args);
|
|
} /* SecurityAudit */
|
|
|
|
/*
|
|
* Performs a Security permission check.
|
|
*/
|
|
static int
|
|
SecurityDoCheck(SecurityStateRec * subj, SecurityStateRec * obj,
|
|
Mask requested, Mask allowed)
|
|
{
|
|
if (!subj->haveState || !obj->haveState)
|
|
return Success;
|
|
if (subj->trustLevel == XSecurityClientTrusted)
|
|
return Success;
|
|
if (obj->trustLevel != XSecurityClientTrusted)
|
|
return Success;
|
|
if ((requested | allowed) == allowed)
|
|
return Success;
|
|
|
|
return BadAccess;
|
|
}
|
|
|
|
/*
|
|
* Labels initial server objects.
|
|
*/
|
|
static void
|
|
SecurityLabelInitial(void)
|
|
{
|
|
SecurityStateRec *state;
|
|
|
|
/* Do the serverClient */
|
|
state = dixLookupPrivate(&serverClient->devPrivates, stateKey);
|
|
state->trustLevel = XSecurityClientTrusted;
|
|
state->haveState = TRUE;
|
|
}
|
|
|
|
/*
|
|
* Looks up a request name
|
|
*/
|
|
static _X_INLINE const char *
|
|
SecurityLookupRequestName(ClientPtr client)
|
|
{
|
|
return LookupRequestName(client->majorOp, client->minorOp);
|
|
}
|
|
|
|
/* SecurityDeleteAuthorization
|
|
*
|
|
* Arguments:
|
|
* value is the authorization to delete.
|
|
* id is its resource ID.
|
|
*
|
|
* Returns: Success.
|
|
*
|
|
* Side Effects:
|
|
* Frees everything associated with the authorization.
|
|
*/
|
|
|
|
static int
|
|
SecurityDeleteAuthorization(pointer value, XID id)
|
|
{
|
|
SecurityAuthorizationPtr pAuth = (SecurityAuthorizationPtr) value;
|
|
unsigned short name_len, data_len;
|
|
const char *name;
|
|
char *data;
|
|
int status;
|
|
int i;
|
|
OtherClientsPtr pEventClient;
|
|
|
|
/* Remove the auth using the os layer auth manager */
|
|
|
|
status = AuthorizationFromID(pAuth->id, &name_len, &name, &data_len, &data);
|
|
assert(status);
|
|
status = RemoveAuthorization(name_len, name, data_len, data);
|
|
assert(status);
|
|
(void) status;
|
|
|
|
/* free the auth timer if there is one */
|
|
|
|
if (pAuth->timer)
|
|
TimerFree(pAuth->timer);
|
|
|
|
/* send revoke events */
|
|
|
|
while ((pEventClient = pAuth->eventClients)) {
|
|
/* send revocation event event */
|
|
xSecurityAuthorizationRevokedEvent are;
|
|
|
|
are.type = SecurityEventBase + XSecurityAuthorizationRevoked;
|
|
are.authId = pAuth->id;
|
|
WriteEventsToClient(rClient(pEventClient), 1, (xEvent *) &are);
|
|
FreeResource(pEventClient->resource, RT_NONE);
|
|
}
|
|
|
|
/* kill all clients using this auth */
|
|
|
|
for (i = 1; i < currentMaxClients; i++)
|
|
if (clients[i]) {
|
|
SecurityStateRec *state;
|
|
|
|
state = dixLookupPrivate(&clients[i]->devPrivates, stateKey);
|
|
if (state->haveState && state->authId == pAuth->id)
|
|
CloseDownClient(clients[i]);
|
|
}
|
|
|
|
SecurityAudit("revoked authorization ID %d\n", pAuth->id);
|
|
free(pAuth);
|
|
return Success;
|
|
|
|
} /* SecurityDeleteAuthorization */
|
|
|
|
/* resource delete function for RTEventClient */
|
|
static int
|
|
SecurityDeleteAuthorizationEventClient(pointer value, XID id)
|
|
{
|
|
OtherClientsPtr pEventClient, prev = NULL;
|
|
SecurityAuthorizationPtr pAuth = (SecurityAuthorizationPtr) value;
|
|
|
|
for (pEventClient = pAuth->eventClients;
|
|
pEventClient; pEventClient = pEventClient->next) {
|
|
if (pEventClient->resource == id) {
|
|
if (prev)
|
|
prev->next = pEventClient->next;
|
|
else
|
|
pAuth->eventClients = pEventClient->next;
|
|
free(pEventClient);
|
|
return Success;
|
|
}
|
|
prev = pEventClient;
|
|
}
|
|
/*NOTREACHED*/ return -1; /* make compiler happy */
|
|
} /* SecurityDeleteAuthorizationEventClient */
|
|
|
|
/* SecurityComputeAuthorizationTimeout
|
|
*
|
|
* Arguments:
|
|
* pAuth is the authorization for which we are computing the timeout
|
|
* seconds is the number of seconds we want to wait
|
|
*
|
|
* Returns:
|
|
* the number of milliseconds that the auth timer should be set to
|
|
*
|
|
* Side Effects:
|
|
* Sets pAuth->secondsRemaining to any "overflow" amount of time
|
|
* that didn't fit in 32 bits worth of milliseconds
|
|
*/
|
|
|
|
static CARD32
|
|
SecurityComputeAuthorizationTimeout(SecurityAuthorizationPtr pAuth,
|
|
unsigned int seconds)
|
|
{
|
|
/* maxSecs is the number of full seconds that can be expressed in
|
|
* 32 bits worth of milliseconds
|
|
*/
|
|
CARD32 maxSecs = (CARD32) (~0) / (CARD32) MILLI_PER_SECOND;
|
|
|
|
if (seconds > maxSecs) { /* only come here if we want to wait more than 49 days */
|
|
pAuth->secondsRemaining = seconds - maxSecs;
|
|
return maxSecs * MILLI_PER_SECOND;
|
|
}
|
|
else { /* by far the common case */
|
|
pAuth->secondsRemaining = 0;
|
|
return seconds * MILLI_PER_SECOND;
|
|
}
|
|
} /* SecurityStartAuthorizationTimer */
|
|
|
|
/* SecurityAuthorizationExpired
|
|
*
|
|
* This function is passed as an argument to TimerSet and gets called from
|
|
* the timer manager in the os layer when its time is up.
|
|
*
|
|
* Arguments:
|
|
* timer is the timer for this authorization.
|
|
* time is the current time.
|
|
* pval is the authorization whose time is up.
|
|
*
|
|
* Returns:
|
|
* A new time delay in milliseconds if the timer should wait some
|
|
* more, else zero.
|
|
*
|
|
* Side Effects:
|
|
* Frees the authorization resource if the timeout period is really
|
|
* over, otherwise recomputes pAuth->secondsRemaining.
|
|
*/
|
|
|
|
static CARD32
|
|
SecurityAuthorizationExpired(OsTimerPtr timer, CARD32 time, pointer pval)
|
|
{
|
|
SecurityAuthorizationPtr pAuth = (SecurityAuthorizationPtr) pval;
|
|
|
|
assert(pAuth->timer == timer);
|
|
|
|
if (pAuth->secondsRemaining) {
|
|
return SecurityComputeAuthorizationTimeout(pAuth,
|
|
pAuth->secondsRemaining);
|
|
}
|
|
else {
|
|
FreeResource(pAuth->id, RT_NONE);
|
|
return 0;
|
|
}
|
|
} /* SecurityAuthorizationExpired */
|
|
|
|
/* SecurityStartAuthorizationTimer
|
|
*
|
|
* Arguments:
|
|
* pAuth is the authorization whose timer should be started.
|
|
*
|
|
* Returns: nothing.
|
|
*
|
|
* Side Effects:
|
|
* A timer is started, set to expire after the timeout period for
|
|
* this authorization. When it expires, the function
|
|
* SecurityAuthorizationExpired will be called.
|
|
*/
|
|
|
|
static void
|
|
SecurityStartAuthorizationTimer(SecurityAuthorizationPtr pAuth)
|
|
{
|
|
pAuth->timer = TimerSet(pAuth->timer, 0,
|
|
SecurityComputeAuthorizationTimeout(pAuth,
|
|
pAuth->timeout),
|
|
SecurityAuthorizationExpired, pAuth);
|
|
} /* SecurityStartAuthorizationTimer */
|
|
|
|
/* Proc functions all take a client argument, execute the request in
|
|
* client->requestBuffer, and return a protocol error status.
|
|
*/
|
|
|
|
static int
|
|
ProcSecurityQueryVersion(ClientPtr client)
|
|
{
|
|
/* REQUEST(xSecurityQueryVersionReq); */
|
|
xSecurityQueryVersionReply rep;
|
|
|
|
REQUEST_SIZE_MATCH(xSecurityQueryVersionReq);
|
|
rep.type = X_Reply;
|
|
rep.sequenceNumber = client->sequence;
|
|
rep.length = 0;
|
|
rep.majorVersion = SERVER_SECURITY_MAJOR_VERSION;
|
|
rep.minorVersion = SERVER_SECURITY_MINOR_VERSION;
|
|
if (client->swapped) {
|
|
swaps(&rep.sequenceNumber);
|
|
swaps(&rep.majorVersion);
|
|
swaps(&rep.minorVersion);
|
|
}
|
|
(void) WriteToClient(client, SIZEOF(xSecurityQueryVersionReply),
|
|
(char *) &rep);
|
|
return Success;
|
|
} /* ProcSecurityQueryVersion */
|
|
|
|
static int
|
|
SecurityEventSelectForAuthorization(SecurityAuthorizationPtr pAuth,
|
|
ClientPtr client, Mask mask)
|
|
{
|
|
OtherClients *pEventClient;
|
|
|
|
for (pEventClient = pAuth->eventClients;
|
|
pEventClient; pEventClient = pEventClient->next) {
|
|
if (SameClient(pEventClient, client)) {
|
|
if (mask == 0)
|
|
FreeResource(pEventClient->resource, RT_NONE);
|
|
else
|
|
pEventClient->mask = mask;
|
|
return Success;
|
|
}
|
|
}
|
|
|
|
pEventClient = malloc(sizeof(OtherClients));
|
|
if (!pEventClient)
|
|
return BadAlloc;
|
|
pEventClient->mask = mask;
|
|
pEventClient->resource = FakeClientID(client->index);
|
|
pEventClient->next = pAuth->eventClients;
|
|
if (!AddResource(pEventClient->resource, RTEventClient, (pointer) pAuth)) {
|
|
free(pEventClient);
|
|
return BadAlloc;
|
|
}
|
|
pAuth->eventClients = pEventClient;
|
|
|
|
return Success;
|
|
} /* SecurityEventSelectForAuthorization */
|
|
|
|
static int
|
|
ProcSecurityGenerateAuthorization(ClientPtr client)
|
|
{
|
|
REQUEST(xSecurityGenerateAuthorizationReq);
|
|
int len; /* request length in CARD32s */
|
|
Bool removeAuth = FALSE; /* if bailout, call RemoveAuthorization? */
|
|
SecurityAuthorizationPtr pAuth = NULL; /* auth we are creating */
|
|
int err; /* error to return from this function */
|
|
XID authId; /* authorization ID assigned by os layer */
|
|
xSecurityGenerateAuthorizationReply rep; /* reply struct */
|
|
unsigned int trustLevel; /* trust level of new auth */
|
|
XID group; /* group of new auth */
|
|
CARD32 timeout; /* timeout of new auth */
|
|
CARD32 *values; /* list of supplied attributes */
|
|
char *protoname; /* auth proto name sent in request */
|
|
char *protodata; /* auth proto data sent in request */
|
|
unsigned int authdata_len; /* # bytes of generated auth data */
|
|
char *pAuthdata; /* generated auth data */
|
|
Mask eventMask; /* what events on this auth does client want */
|
|
|
|
/* check request length */
|
|
|
|
REQUEST_AT_LEAST_SIZE(xSecurityGenerateAuthorizationReq);
|
|
len = bytes_to_int32(SIZEOF(xSecurityGenerateAuthorizationReq));
|
|
len += bytes_to_int32(stuff->nbytesAuthProto);
|
|
len += bytes_to_int32(stuff->nbytesAuthData);
|
|
values = ((CARD32 *) stuff) + len;
|
|
len += Ones(stuff->valueMask);
|
|
if (client->req_len != len)
|
|
return BadLength;
|
|
|
|
/* check valuemask */
|
|
if (stuff->valueMask & ~XSecurityAllAuthorizationAttributes) {
|
|
client->errorValue = stuff->valueMask;
|
|
return BadValue;
|
|
}
|
|
|
|
/* check timeout */
|
|
timeout = 60;
|
|
if (stuff->valueMask & XSecurityTimeout) {
|
|
timeout = *values++;
|
|
}
|
|
|
|
/* check trustLevel */
|
|
trustLevel = XSecurityClientUntrusted;
|
|
if (stuff->valueMask & XSecurityTrustLevel) {
|
|
trustLevel = *values++;
|
|
if (trustLevel != XSecurityClientTrusted &&
|
|
trustLevel != XSecurityClientUntrusted) {
|
|
client->errorValue = trustLevel;
|
|
return BadValue;
|
|
}
|
|
}
|
|
|
|
/* check group */
|
|
group = None;
|
|
if (stuff->valueMask & XSecurityGroup) {
|
|
group = *values++;
|
|
if (SecurityValidateGroupCallback) {
|
|
SecurityValidateGroupInfoRec vgi;
|
|
|
|
vgi.group = group;
|
|
vgi.valid = FALSE;
|
|
CallCallbacks(&SecurityValidateGroupCallback, (pointer) &vgi);
|
|
|
|
/* if nobody said they recognized it, it's an error */
|
|
|
|
if (!vgi.valid) {
|
|
client->errorValue = group;
|
|
return BadValue;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* check event mask */
|
|
eventMask = 0;
|
|
if (stuff->valueMask & XSecurityEventMask) {
|
|
eventMask = *values++;
|
|
if (eventMask & ~XSecurityAllEventMasks) {
|
|
client->errorValue = eventMask;
|
|
return BadValue;
|
|
}
|
|
}
|
|
|
|
protoname = (char *) &stuff[1];
|
|
protodata = protoname + bytes_to_int32(stuff->nbytesAuthProto);
|
|
|
|
/* call os layer to generate the authorization */
|
|
|
|
authId = GenerateAuthorization(stuff->nbytesAuthProto, protoname,
|
|
stuff->nbytesAuthData, protodata,
|
|
&authdata_len, &pAuthdata);
|
|
if ((XID) ~0L == authId) {
|
|
err = SecurityErrorBase + XSecurityBadAuthorizationProtocol;
|
|
goto bailout;
|
|
}
|
|
|
|
/* now that we've added the auth, remember to remove it if we have to
|
|
* abort the request for some reason (like allocation failure)
|
|
*/
|
|
removeAuth = TRUE;
|
|
|
|
/* associate additional information with this auth ID */
|
|
|
|
pAuth = malloc(sizeof(SecurityAuthorizationRec));
|
|
if (!pAuth) {
|
|
err = BadAlloc;
|
|
goto bailout;
|
|
}
|
|
|
|
/* fill in the auth fields */
|
|
|
|
pAuth->id = authId;
|
|
pAuth->timeout = timeout;
|
|
pAuth->group = group;
|
|
pAuth->trustLevel = trustLevel;
|
|
pAuth->refcnt = 0; /* the auth was just created; nobody's using it yet */
|
|
pAuth->secondsRemaining = 0;
|
|
pAuth->timer = NULL;
|
|
pAuth->eventClients = NULL;
|
|
|
|
/* handle event selection */
|
|
if (eventMask) {
|
|
err = SecurityEventSelectForAuthorization(pAuth, client, eventMask);
|
|
if (err != Success)
|
|
goto bailout;
|
|
}
|
|
|
|
if (!AddResource(authId, SecurityAuthorizationResType, pAuth)) {
|
|
err = BadAlloc;
|
|
goto bailout;
|
|
}
|
|
|
|
/* start the timer ticking */
|
|
|
|
if (pAuth->timeout != 0)
|
|
SecurityStartAuthorizationTimer(pAuth);
|
|
|
|
/* tell client the auth id and data */
|
|
|
|
rep.type = X_Reply;
|
|
rep.length = bytes_to_int32(authdata_len);
|
|
rep.sequenceNumber = client->sequence;
|
|
rep.authId = authId;
|
|
rep.dataLength = authdata_len;
|
|
|
|
if (client->swapped) {
|
|
swapl(&rep.length);
|
|
swaps(&rep.sequenceNumber);
|
|
swapl(&rep.authId);
|
|
swaps(&rep.dataLength);
|
|
}
|
|
|
|
WriteToClient(client, SIZEOF(xSecurityGenerateAuthorizationReply),
|
|
(char *) &rep);
|
|
WriteToClient(client, authdata_len, pAuthdata);
|
|
|
|
SecurityAudit
|
|
("client %d generated authorization %d trust %d timeout %d group %d events %d\n",
|
|
client->index, pAuth->id, pAuth->trustLevel, pAuth->timeout,
|
|
pAuth->group, eventMask);
|
|
|
|
/* the request succeeded; don't call RemoveAuthorization or free pAuth */
|
|
return Success;
|
|
|
|
bailout:
|
|
if (removeAuth)
|
|
RemoveAuthorization(stuff->nbytesAuthProto, protoname,
|
|
authdata_len, pAuthdata);
|
|
free(pAuth);
|
|
return err;
|
|
|
|
} /* ProcSecurityGenerateAuthorization */
|
|
|
|
static int
|
|
ProcSecurityRevokeAuthorization(ClientPtr client)
|
|
{
|
|
REQUEST(xSecurityRevokeAuthorizationReq);
|
|
SecurityAuthorizationPtr pAuth;
|
|
int rc;
|
|
|
|
REQUEST_SIZE_MATCH(xSecurityRevokeAuthorizationReq);
|
|
|
|
rc = dixLookupResourceByType((pointer *) &pAuth, stuff->authId,
|
|
SecurityAuthorizationResType, client,
|
|
DixDestroyAccess);
|
|
if (rc != Success)
|
|
return rc;
|
|
|
|
FreeResource(stuff->authId, RT_NONE);
|
|
return Success;
|
|
} /* ProcSecurityRevokeAuthorization */
|
|
|
|
static int
|
|
ProcSecurityDispatch(ClientPtr client)
|
|
{
|
|
REQUEST(xReq);
|
|
|
|
switch (stuff->data) {
|
|
case X_SecurityQueryVersion:
|
|
return ProcSecurityQueryVersion(client);
|
|
case X_SecurityGenerateAuthorization:
|
|
return ProcSecurityGenerateAuthorization(client);
|
|
case X_SecurityRevokeAuthorization:
|
|
return ProcSecurityRevokeAuthorization(client);
|
|
default:
|
|
return BadRequest;
|
|
}
|
|
} /* ProcSecurityDispatch */
|
|
|
|
static int
|
|
SProcSecurityQueryVersion(ClientPtr client)
|
|
{
|
|
REQUEST(xSecurityQueryVersionReq);
|
|
|
|
swaps(&stuff->length);
|
|
REQUEST_SIZE_MATCH(xSecurityQueryVersionReq);
|
|
swaps(&stuff->majorVersion);
|
|
swaps(&stuff->minorVersion);
|
|
return ProcSecurityQueryVersion(client);
|
|
} /* SProcSecurityQueryVersion */
|
|
|
|
static int
|
|
SProcSecurityGenerateAuthorization(ClientPtr client)
|
|
{
|
|
REQUEST(xSecurityGenerateAuthorizationReq);
|
|
CARD32 *values;
|
|
unsigned long nvalues;
|
|
int values_offset;
|
|
|
|
swaps(&stuff->length);
|
|
REQUEST_AT_LEAST_SIZE(xSecurityGenerateAuthorizationReq);
|
|
swaps(&stuff->nbytesAuthProto);
|
|
swaps(&stuff->nbytesAuthData);
|
|
swapl(&stuff->valueMask);
|
|
values_offset = bytes_to_int32(stuff->nbytesAuthProto) +
|
|
bytes_to_int32(stuff->nbytesAuthData);
|
|
if (values_offset >
|
|
stuff->length - bytes_to_int32(sz_xSecurityGenerateAuthorizationReq))
|
|
return BadLength;
|
|
values = (CARD32 *) (&stuff[1]) + values_offset;
|
|
nvalues = (((CARD32 *) stuff) + stuff->length) - values;
|
|
SwapLongs(values, nvalues);
|
|
return ProcSecurityGenerateAuthorization(client);
|
|
} /* SProcSecurityGenerateAuthorization */
|
|
|
|
static int
|
|
SProcSecurityRevokeAuthorization(ClientPtr client)
|
|
{
|
|
REQUEST(xSecurityRevokeAuthorizationReq);
|
|
|
|
swaps(&stuff->length);
|
|
REQUEST_SIZE_MATCH(xSecurityRevokeAuthorizationReq);
|
|
swapl(&stuff->authId);
|
|
return ProcSecurityRevokeAuthorization(client);
|
|
} /* SProcSecurityRevokeAuthorization */
|
|
|
|
static int
|
|
SProcSecurityDispatch(ClientPtr client)
|
|
{
|
|
REQUEST(xReq);
|
|
|
|
switch (stuff->data) {
|
|
case X_SecurityQueryVersion:
|
|
return SProcSecurityQueryVersion(client);
|
|
case X_SecurityGenerateAuthorization:
|
|
return SProcSecurityGenerateAuthorization(client);
|
|
case X_SecurityRevokeAuthorization:
|
|
return SProcSecurityRevokeAuthorization(client);
|
|
default:
|
|
return BadRequest;
|
|
}
|
|
} /* SProcSecurityDispatch */
|
|
|
|
static void
|
|
SwapSecurityAuthorizationRevokedEvent(xSecurityAuthorizationRevokedEvent * from,
|
|
xSecurityAuthorizationRevokedEvent * to)
|
|
{
|
|
to->type = from->type;
|
|
to->detail = from->detail;
|
|
cpswaps(from->sequenceNumber, to->sequenceNumber);
|
|
cpswapl(from->authId, to->authId);
|
|
}
|
|
|
|
/* SecurityCheckDeviceAccess
|
|
*
|
|
* Arguments:
|
|
* client is the client attempting to access a device.
|
|
* dev is the device being accessed.
|
|
* fromRequest is TRUE if the device access is a direct result of
|
|
* the client executing some request and FALSE if it is a
|
|
* result of the server trying to send an event (e.g. KeymapNotify)
|
|
* to the client.
|
|
* Returns:
|
|
* TRUE if the device access should be allowed, else FALSE.
|
|
*
|
|
* Side Effects:
|
|
* An audit message is generated if access is denied.
|
|
*/
|
|
|
|
static void
|
|
SecurityDevice(CallbackListPtr *pcbl, pointer unused, pointer calldata)
|
|
{
|
|
XaceDeviceAccessRec *rec = calldata;
|
|
SecurityStateRec *subj, *obj;
|
|
Mask requested = rec->access_mode;
|
|
Mask allowed = SecurityDeviceMask;
|
|
|
|
subj = dixLookupPrivate(&rec->client->devPrivates, stateKey);
|
|
obj = dixLookupPrivate(&serverClient->devPrivates, stateKey);
|
|
|
|
if (rec->dev != inputInfo.keyboard)
|
|
/* this extension only supports the core keyboard */
|
|
allowed = requested;
|
|
|
|
if (SecurityDoCheck(subj, obj, requested, allowed) != Success) {
|
|
SecurityAudit("Security denied client %d keyboard access on request "
|
|
"%s\n", rec->client->index,
|
|
SecurityLookupRequestName(rec->client));
|
|
rec->status = BadAccess;
|
|
}
|
|
}
|
|
|
|
/* SecurityResource
|
|
*
|
|
* This function gets plugged into client->CheckAccess and is called from
|
|
* SecurityLookupIDByType/Class to determine if the client can access the
|
|
* resource.
|
|
*
|
|
* Arguments:
|
|
* client is the client doing the resource access.
|
|
* id is the resource id.
|
|
* rtype is its type or class.
|
|
* access_mode represents the intended use of the resource; see
|
|
* resource.h.
|
|
* res is a pointer to the resource structure for this resource.
|
|
*
|
|
* Returns:
|
|
* If access is granted, the value of rval that was passed in, else FALSE.
|
|
*
|
|
* Side Effects:
|
|
* Disallowed resource accesses are audited.
|
|
*/
|
|
|
|
static void
|
|
SecurityResource(CallbackListPtr *pcbl, pointer unused, pointer calldata)
|
|
{
|
|
XaceResourceAccessRec *rec = calldata;
|
|
SecurityStateRec *subj, *obj;
|
|
int cid = CLIENT_ID(rec->id);
|
|
Mask requested = rec->access_mode;
|
|
Mask allowed = SecurityResourceMask;
|
|
|
|
subj = dixLookupPrivate(&rec->client->devPrivates, stateKey);
|
|
|
|
/* disable background None for untrusted windows */
|
|
if ((requested & DixCreateAccess) && (rec->rtype == RT_WINDOW))
|
|
if (subj->haveState && subj->trustLevel != XSecurityClientTrusted)
|
|
((WindowPtr) rec->res)->forcedBG = TRUE;
|
|
|
|
/* additional permissions for specific resource types */
|
|
if (rec->rtype == RT_WINDOW)
|
|
allowed |= SecurityWindowExtraMask;
|
|
|
|
/* special checks for server-owned resources */
|
|
if (cid == 0) {
|
|
if (rec->rtype & RC_DRAWABLE)
|
|
/* additional operations allowed on root windows */
|
|
allowed |= SecurityRootWindowExtraMask;
|
|
|
|
else if (rec->rtype == RT_COLORMAP)
|
|
/* allow access to default colormaps */
|
|
allowed = requested;
|
|
|
|
else
|
|
/* allow read access to other server-owned resources */
|
|
allowed |= DixReadAccess;
|
|
}
|
|
|
|
if (clients[cid] != NULL) {
|
|
obj = dixLookupPrivate(&clients[cid]->devPrivates, stateKey);
|
|
if (SecurityDoCheck(subj, obj, requested, allowed) == Success)
|
|
return;
|
|
}
|
|
|
|
SecurityAudit("Security: denied client %d access %x to resource 0x%x "
|
|
"of client %d on request %s\n", rec->client->index,
|
|
requested, rec->id, cid,
|
|
SecurityLookupRequestName(rec->client));
|
|
rec->status = BadAccess; /* deny access */
|
|
}
|
|
|
|
static void
|
|
SecurityExtension(CallbackListPtr *pcbl, pointer unused, pointer calldata)
|
|
{
|
|
XaceExtAccessRec *rec = calldata;
|
|
SecurityStateRec *subj;
|
|
int i = 0;
|
|
|
|
subj = dixLookupPrivate(&rec->client->devPrivates, stateKey);
|
|
|
|
if (subj->haveState && subj->trustLevel == XSecurityClientTrusted)
|
|
return;
|
|
|
|
while (SecurityTrustedExtensions[i])
|
|
if (!strcmp(SecurityTrustedExtensions[i++], rec->ext->name))
|
|
return;
|
|
|
|
SecurityAudit("Security: denied client %d access to extension "
|
|
"%s on request %s\n",
|
|
rec->client->index, rec->ext->name,
|
|
SecurityLookupRequestName(rec->client));
|
|
rec->status = BadAccess;
|
|
}
|
|
|
|
static void
|
|
SecurityServer(CallbackListPtr *pcbl, pointer unused, pointer calldata)
|
|
{
|
|
XaceServerAccessRec *rec = calldata;
|
|
SecurityStateRec *subj, *obj;
|
|
Mask requested = rec->access_mode;
|
|
Mask allowed = SecurityServerMask;
|
|
|
|
subj = dixLookupPrivate(&rec->client->devPrivates, stateKey);
|
|
obj = dixLookupPrivate(&serverClient->devPrivates, stateKey);
|
|
|
|
if (SecurityDoCheck(subj, obj, requested, allowed) != Success) {
|
|
SecurityAudit("Security: denied client %d access to server "
|
|
"configuration request %s\n", rec->client->index,
|
|
SecurityLookupRequestName(rec->client));
|
|
rec->status = BadAccess;
|
|
}
|
|
}
|
|
|
|
static void
|
|
SecurityClient(CallbackListPtr *pcbl, pointer unused, pointer calldata)
|
|
{
|
|
XaceClientAccessRec *rec = calldata;
|
|
SecurityStateRec *subj, *obj;
|
|
Mask requested = rec->access_mode;
|
|
Mask allowed = SecurityClientMask;
|
|
|
|
subj = dixLookupPrivate(&rec->client->devPrivates, stateKey);
|
|
obj = dixLookupPrivate(&rec->target->devPrivates, stateKey);
|
|
|
|
if (SecurityDoCheck(subj, obj, requested, allowed) != Success) {
|
|
SecurityAudit("Security: denied client %d access to client %d on "
|
|
"request %s\n", rec->client->index, rec->target->index,
|
|
SecurityLookupRequestName(rec->client));
|
|
rec->status = BadAccess;
|
|
}
|
|
}
|
|
|
|
static void
|
|
SecurityProperty(CallbackListPtr *pcbl, pointer unused, pointer calldata)
|
|
{
|
|
XacePropertyAccessRec *rec = calldata;
|
|
SecurityStateRec *subj, *obj;
|
|
ATOM name = (*rec->ppProp)->propertyName;
|
|
Mask requested = rec->access_mode;
|
|
Mask allowed = SecurityResourceMask | DixReadAccess;
|
|
|
|
subj = dixLookupPrivate(&rec->client->devPrivates, stateKey);
|
|
obj = dixLookupPrivate(&wClient(rec->pWin)->devPrivates, stateKey);
|
|
|
|
if (SecurityDoCheck(subj, obj, requested, allowed) != Success) {
|
|
SecurityAudit("Security: denied client %d access to property %s "
|
|
"(atom 0x%x) window 0x%x of client %d on request %s\n",
|
|
rec->client->index, NameForAtom(name), name,
|
|
rec->pWin->drawable.id, wClient(rec->pWin)->index,
|
|
SecurityLookupRequestName(rec->client));
|
|
rec->status = BadAccess;
|
|
}
|
|
}
|
|
|
|
static void
|
|
SecuritySend(CallbackListPtr *pcbl, pointer unused, pointer calldata)
|
|
{
|
|
XaceSendAccessRec *rec = calldata;
|
|
SecurityStateRec *subj, *obj;
|
|
|
|
if (rec->client) {
|
|
int i;
|
|
|
|
subj = dixLookupPrivate(&rec->client->devPrivates, stateKey);
|
|
obj = dixLookupPrivate(&wClient(rec->pWin)->devPrivates, stateKey);
|
|
|
|
if (SecurityDoCheck(subj, obj, DixSendAccess, 0) == Success)
|
|
return;
|
|
|
|
for (i = 0; i < rec->count; i++)
|
|
if (rec->events[i].u.u.type != UnmapNotify &&
|
|
rec->events[i].u.u.type != ConfigureRequest &&
|
|
rec->events[i].u.u.type != ClientMessage) {
|
|
|
|
SecurityAudit("Security: denied client %d from sending event "
|
|
"of type %s to window 0x%x of client %d\n",
|
|
rec->client->index,
|
|
LookupEventName(rec->events[i].u.u.type),
|
|
rec->pWin->drawable.id,
|
|
wClient(rec->pWin)->index);
|
|
rec->status = BadAccess;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
SecurityReceive(CallbackListPtr *pcbl, pointer unused, pointer calldata)
|
|
{
|
|
XaceReceiveAccessRec *rec = calldata;
|
|
SecurityStateRec *subj, *obj;
|
|
|
|
subj = dixLookupPrivate(&rec->client->devPrivates, stateKey);
|
|
obj = dixLookupPrivate(&wClient(rec->pWin)->devPrivates, stateKey);
|
|
|
|
if (SecurityDoCheck(subj, obj, DixReceiveAccess, 0) == Success)
|
|
return;
|
|
|
|
SecurityAudit("Security: denied client %d from receiving an event "
|
|
"sent to window 0x%x of client %d\n",
|
|
rec->client->index, rec->pWin->drawable.id,
|
|
wClient(rec->pWin)->index);
|
|
rec->status = BadAccess;
|
|
}
|
|
|
|
/* SecurityClientStateCallback
|
|
*
|
|
* Arguments:
|
|
* pcbl is &ClientStateCallback.
|
|
* nullata is NULL.
|
|
* calldata is a pointer to a NewClientInfoRec (include/dixstruct.h)
|
|
* which contains information about client state changes.
|
|
*
|
|
* Returns: nothing.
|
|
*
|
|
* Side Effects:
|
|
*
|
|
* If a new client is connecting, its authorization ID is copied to
|
|
* client->authID. If this is a generated authorization, its reference
|
|
* count is bumped, its timer is cancelled if it was running, and its
|
|
* trustlevel is copied to TRUSTLEVEL(client).
|
|
*
|
|
* If a client is disconnecting and the client was using a generated
|
|
* authorization, the authorization's reference count is decremented, and
|
|
* if it is now zero, the timer for this authorization is started.
|
|
*/
|
|
|
|
static void
|
|
SecurityClientState(CallbackListPtr *pcbl, pointer unused, pointer calldata)
|
|
{
|
|
NewClientInfoRec *pci = calldata;
|
|
SecurityStateRec *state;
|
|
SecurityAuthorizationPtr pAuth;
|
|
int rc;
|
|
|
|
state = dixLookupPrivate(&pci->client->devPrivates, stateKey);
|
|
|
|
switch (pci->client->clientState) {
|
|
case ClientStateInitial:
|
|
state->trustLevel = XSecurityClientTrusted;
|
|
state->authId = None;
|
|
state->haveState = TRUE;
|
|
break;
|
|
|
|
case ClientStateRunning:
|
|
state->authId = AuthorizationIDOfClient(pci->client);
|
|
rc = dixLookupResourceByType((pointer *) &pAuth, state->authId,
|
|
SecurityAuthorizationResType, serverClient,
|
|
DixGetAttrAccess);
|
|
if (rc == Success) {
|
|
/* it is a generated authorization */
|
|
pAuth->refcnt++;
|
|
if (pAuth->refcnt == 1 && pAuth->timer)
|
|
TimerCancel(pAuth->timer);
|
|
|
|
state->trustLevel = pAuth->trustLevel;
|
|
}
|
|
break;
|
|
|
|
case ClientStateGone:
|
|
case ClientStateRetained:
|
|
rc = dixLookupResourceByType((pointer *) &pAuth, state->authId,
|
|
SecurityAuthorizationResType, serverClient,
|
|
DixGetAttrAccess);
|
|
if (rc == Success) {
|
|
/* it is a generated authorization */
|
|
pAuth->refcnt--;
|
|
if (pAuth->refcnt == 0)
|
|
SecurityStartAuthorizationTimer(pAuth);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* SecurityResetProc
|
|
*
|
|
* Arguments:
|
|
* extEntry is the extension information for the security extension.
|
|
*
|
|
* Returns: nothing.
|
|
*
|
|
* Side Effects:
|
|
* Performs any cleanup needed by Security at server shutdown time.
|
|
*/
|
|
|
|
static void
|
|
SecurityResetProc(ExtensionEntry * extEntry)
|
|
{
|
|
/* Unregister callbacks */
|
|
DeleteCallback(&ClientStateCallback, SecurityClientState, NULL);
|
|
|
|
XaceDeleteCallback(XACE_EXT_DISPATCH, SecurityExtension, NULL);
|
|
XaceDeleteCallback(XACE_RESOURCE_ACCESS, SecurityResource, NULL);
|
|
XaceDeleteCallback(XACE_DEVICE_ACCESS, SecurityDevice, NULL);
|
|
XaceDeleteCallback(XACE_PROPERTY_ACCESS, SecurityProperty, NULL);
|
|
XaceDeleteCallback(XACE_SEND_ACCESS, SecuritySend, NULL);
|
|
XaceDeleteCallback(XACE_RECEIVE_ACCESS, SecurityReceive, NULL);
|
|
XaceDeleteCallback(XACE_CLIENT_ACCESS, SecurityClient, NULL);
|
|
XaceDeleteCallback(XACE_EXT_ACCESS, SecurityExtension, NULL);
|
|
XaceDeleteCallback(XACE_SERVER_ACCESS, SecurityServer, NULL);
|
|
}
|
|
|
|
/* SecurityExtensionInit
|
|
*
|
|
* Arguments: none.
|
|
*
|
|
* Returns: nothing.
|
|
*
|
|
* Side Effects:
|
|
* Enables the Security extension if possible.
|
|
*/
|
|
|
|
void
|
|
SecurityExtensionInit(INITARGS)
|
|
{
|
|
ExtensionEntry *extEntry;
|
|
int ret = TRUE;
|
|
|
|
SecurityAuthorizationResType =
|
|
CreateNewResourceType(SecurityDeleteAuthorization,
|
|
"SecurityAuthorization");
|
|
|
|
RTEventClient =
|
|
CreateNewResourceType(SecurityDeleteAuthorizationEventClient,
|
|
"SecurityEventClient");
|
|
|
|
if (!SecurityAuthorizationResType || !RTEventClient)
|
|
return;
|
|
|
|
RTEventClient |= RC_NEVERRETAIN;
|
|
|
|
/* Allocate the private storage */
|
|
if (!dixRegisterPrivateKey
|
|
(stateKey, PRIVATE_CLIENT, sizeof(SecurityStateRec)))
|
|
FatalError("SecurityExtensionSetup: Can't allocate client private.\n");
|
|
|
|
/* Register callbacks */
|
|
ret &= AddCallback(&ClientStateCallback, SecurityClientState, NULL);
|
|
|
|
ret &= XaceRegisterCallback(XACE_EXT_DISPATCH, SecurityExtension, NULL);
|
|
ret &= XaceRegisterCallback(XACE_RESOURCE_ACCESS, SecurityResource, NULL);
|
|
ret &= XaceRegisterCallback(XACE_DEVICE_ACCESS, SecurityDevice, NULL);
|
|
ret &= XaceRegisterCallback(XACE_PROPERTY_ACCESS, SecurityProperty, NULL);
|
|
ret &= XaceRegisterCallback(XACE_SEND_ACCESS, SecuritySend, NULL);
|
|
ret &= XaceRegisterCallback(XACE_RECEIVE_ACCESS, SecurityReceive, NULL);
|
|
ret &= XaceRegisterCallback(XACE_CLIENT_ACCESS, SecurityClient, NULL);
|
|
ret &= XaceRegisterCallback(XACE_EXT_ACCESS, SecurityExtension, NULL);
|
|
ret &= XaceRegisterCallback(XACE_SERVER_ACCESS, SecurityServer, NULL);
|
|
|
|
if (!ret)
|
|
FatalError("SecurityExtensionSetup: Failed to register callbacks\n");
|
|
|
|
/* Add extension to server */
|
|
extEntry = AddExtension(SECURITY_EXTENSION_NAME,
|
|
XSecurityNumberEvents, XSecurityNumberErrors,
|
|
ProcSecurityDispatch, SProcSecurityDispatch,
|
|
SecurityResetProc, StandardMinorOpcode);
|
|
|
|
SecurityErrorBase = extEntry->errorBase;
|
|
SecurityEventBase = extEntry->eventBase;
|
|
|
|
EventSwapVector[SecurityEventBase + XSecurityAuthorizationRevoked] =
|
|
(EventSwapPtr) SwapSecurityAuthorizationRevokedEvent;
|
|
|
|
SetResourceTypeErrorValue(SecurityAuthorizationResType,
|
|
SecurityErrorBase + XSecurityBadAuthorization);
|
|
|
|
/* Label objects that were created before we could register ourself */
|
|
SecurityLabelInitial();
|
|
}
|