2004 lines
		
	
	
		
			50 KiB
		
	
	
	
		
			C
		
	
	
	
			
		
		
	
	
			2004 lines
		
	
	
		
			50 KiB
		
	
	
	
		
			C
		
	
	
	
| /* $XdotOrg: xserver/xorg/Xext/security.c,v 1.5 2005/07/03 07:01:04 daniels Exp $ */
 | |
| /* $Xorg: security.c,v 1.4 2001/02/09 02:04:32 xorgcvs Exp $ */
 | |
| /*
 | |
| 
 | |
| 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.
 | |
| 
 | |
| */
 | |
| /* $XFree86: xc/programs/Xserver/Xext/security.c,v 1.16tsi Exp $ */
 | |
| 
 | |
| #ifdef HAVE_DIX_CONFIG_H
 | |
| #include <dix-config.h>
 | |
| #endif
 | |
| 
 | |
| #include "dixstruct.h"
 | |
| #include "extnsionst.h"
 | |
| #include "windowstr.h"
 | |
| #include "inputstr.h"
 | |
| #include "scrnintstr.h"
 | |
| #include "gcstruct.h"
 | |
| #include "colormapst.h"
 | |
| #include "propertyst.h"
 | |
| #define _SECURITY_SERVER
 | |
| #include <X11/extensions/securstr.h>
 | |
| #include <assert.h>
 | |
| #include <stdarg.h>
 | |
| #ifdef LBX
 | |
| #define _XLBX_SERVER_
 | |
| #include <X11/extensions/XLbx.h>
 | |
| extern unsigned char LbxReqCode;
 | |
| #endif
 | |
| #ifdef XAPPGROUP
 | |
| #include <X11/extensions/Xagsrv.h>
 | |
| #endif
 | |
| #include <stdio.h>  /* for file reading operations */
 | |
| #include <X11/Xatom.h>  /* for XA_STRING */
 | |
| 
 | |
| #ifndef DEFAULTPOLICYFILE
 | |
| # define DEFAULTPOLICYFILE NULL
 | |
| #endif
 | |
| #if defined(WIN32) || defined(__CYGWIN__)
 | |
| #include <X11/Xos.h>
 | |
| #undef index
 | |
| #endif
 | |
| 
 | |
| #include "modinit.h"
 | |
| 
 | |
| static int SecurityErrorBase;  /* first Security error number */
 | |
| static int SecurityEventBase;  /* first Security event number */
 | |
| 
 | |
| CallbackListPtr SecurityValidateGroupCallback = NULL;  /* see security.h */
 | |
| 
 | |
| RESTYPE SecurityAuthorizationResType; /* resource type for authorizations */
 | |
| 
 | |
| static RESTYPE RTEventClient;
 | |
| 
 | |
| /* Proc vectors for untrusted clients, swapped and unswapped versions.
 | |
|  * These are the same as the normal proc vectors except that extensions
 | |
|  * that haven't declared themselves secure will have ProcBadRequest plugged
 | |
|  * in for their major opcode dispatcher.  This prevents untrusted clients
 | |
|  * from guessing extension major opcodes and using the extension even though
 | |
|  * the extension can't be listed or queried.
 | |
|  */
 | |
| int (*UntrustedProcVector[256])(
 | |
|     ClientPtr /*client*/
 | |
| );
 | |
| int (*SwappedUntrustedProcVector[256])(
 | |
|     ClientPtr /*client*/
 | |
| );
 | |
| 
 | |
| /* 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.
 | |
|  */
 | |
| 
 | |
| void
 | |
| SecurityAudit(char *format, ...)
 | |
| {
 | |
|     va_list args;
 | |
| 
 | |
|     if (auditTrailLevel < SECURITY_AUDIT_LEVEL)
 | |
| 	return;
 | |
|     va_start(args, format);
 | |
|     VAuditF(format, args);
 | |
|     va_end(args);
 | |
| } /* SecurityAudit */
 | |
| 
 | |
| #define rClient(obj) (clients[CLIENT_ID((obj)->resource)])
 | |
| 
 | |
| /* 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;
 | |
|     char *name, *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 */
 | |
| 	ClientPtr client = rClient(pEventClient);
 | |
| 
 | |
| 	if (!client->clientGone)
 | |
| 	{
 | |
| 	    xSecurityAuthorizationRevokedEvent are;
 | |
| 	    are.type = SecurityEventBase + XSecurityAuthorizationRevoked;
 | |
| 	    are.sequenceNumber = client->sequence;
 | |
| 	    are.authId = pAuth->id;
 | |
| 	    WriteEventsToClient(client, 1, (xEvent *)&are);
 | |
| 	}
 | |
| 	FreeResource(pEventClient->resource, RT_NONE);
 | |
|     }
 | |
| 
 | |
|     /* kill all clients using this auth */
 | |
| 
 | |
|     for (i = 1; i<currentMaxClients; i++)
 | |
|     {
 | |
| 	if (clients[i] && (clients[i]->authId == pAuth->id))
 | |
| 	    CloseDownClient(clients[i]);
 | |
|     }
 | |
| 
 | |
|     SecurityAudit("revoked authorization ID %d\n", pAuth->id);
 | |
|     xfree(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;
 | |
| 	    xfree(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;
 | |
| 
 | |
|     /* paranoia: this "can't happen" because this extension is hidden
 | |
|      * from untrusted clients, but just in case...
 | |
|      */
 | |
|     if (client->trustLevel != XSecurityClientTrusted)
 | |
| 	return BadRequest;
 | |
| 
 | |
|     REQUEST_SIZE_MATCH(xSecurityQueryVersionReq);
 | |
|     rep.type        	= X_Reply;
 | |
|     rep.sequenceNumber 	= client->sequence;
 | |
|     rep.length         	= 0;
 | |
|     rep.majorVersion  	= SECURITY_MAJOR_VERSION;
 | |
|     rep.minorVersion  	= SECURITY_MINOR_VERSION;
 | |
|     if(client->swapped)
 | |
|     {
 | |
| 	register char n;
 | |
|     	swaps(&rep.sequenceNumber, n);
 | |
| 	swaps(&rep.majorVersion, n);
 | |
| 	swaps(&rep.minorVersion, n);
 | |
|     }
 | |
|     (void)WriteToClient(client, SIZEOF(xSecurityQueryVersionReply),
 | |
| 			(char *)&rep);
 | |
|     return (client->noClientException);
 | |
| } /* 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 = (OtherClients *) xalloc(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))
 | |
|     {
 | |
| 	xfree(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 */
 | |
| 
 | |
|     /* paranoia: this "can't happen" because this extension is hidden
 | |
|      * from untrusted clients, but just in case...
 | |
|      */
 | |
|     if (client->trustLevel != XSecurityClientTrusted)
 | |
| 	return BadRequest;
 | |
| 
 | |
|     /* check request length */
 | |
| 
 | |
|     REQUEST_AT_LEAST_SIZE(xSecurityGenerateAuthorizationReq);
 | |
|     len = SIZEOF(xSecurityGenerateAuthorizationReq) >> 2;
 | |
|     len += (stuff->nbytesAuthProto + (unsigned)3) >> 2;
 | |
|     len += (stuff->nbytesAuthData  + (unsigned)3) >> 2;
 | |
|     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 + ((stuff->nbytesAuthProto + (unsigned)3) >> 2);
 | |
| 
 | |
|     /* 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 = (SecurityAuthorizationPtr)xalloc(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 = (authdata_len + 3) >> 2;
 | |
|     rep.sequenceNumber = client->sequence;
 | |
|     rep.authId = authId;
 | |
|     rep.dataLength = authdata_len;
 | |
| 
 | |
|     if (client->swapped)
 | |
|     {
 | |
| 	register char n;
 | |
|     	swapl(&rep.length, n);
 | |
|     	swaps(&rep.sequenceNumber, n);
 | |
|     	swapl(&rep.authId, n);
 | |
|     	swaps(&rep.dataLength, n);
 | |
|     }
 | |
| 
 | |
|     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 */
 | |
| 
 | |
|     removeAuth = FALSE;
 | |
|     pAuth = NULL;
 | |
|     err = client->noClientException;
 | |
| 
 | |
| bailout:
 | |
|     if (removeAuth)
 | |
| 	RemoveAuthorization(stuff->nbytesAuthProto, protoname,
 | |
| 			    authdata_len, pAuthdata);
 | |
|     if (pAuth) xfree(pAuth);
 | |
|     return err;
 | |
| 
 | |
| } /* ProcSecurityGenerateAuthorization */
 | |
| 
 | |
| static int
 | |
| ProcSecurityRevokeAuthorization(
 | |
|     ClientPtr client)
 | |
| {
 | |
|     REQUEST(xSecurityRevokeAuthorizationReq);
 | |
|     SecurityAuthorizationPtr pAuth;
 | |
| 
 | |
|     /* paranoia: this "can't happen" because this extension is hidden
 | |
|      * from untrusted clients, but just in case...
 | |
|      */
 | |
|     if (client->trustLevel != XSecurityClientTrusted)
 | |
| 	return BadRequest;
 | |
| 
 | |
|     REQUEST_SIZE_MATCH(xSecurityRevokeAuthorizationReq);
 | |
| 
 | |
|     pAuth = (SecurityAuthorizationPtr)SecurityLookupIDByType(client,
 | |
| 	stuff->authId, SecurityAuthorizationResType, SecurityDestroyAccess);
 | |
|     if (!pAuth)
 | |
| 	return SecurityErrorBase + XSecurityBadAuthorization;
 | |
| 
 | |
|     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);
 | |
|     register char 	n;
 | |
| 
 | |
|     swaps(&stuff->length, n);
 | |
|     REQUEST_SIZE_MATCH(xSecurityQueryVersionReq);
 | |
|     swaps(&stuff->majorVersion, n);
 | |
|     swaps(&stuff->minorVersion,n);
 | |
|     return ProcSecurityQueryVersion(client);
 | |
| } /* SProcSecurityQueryVersion */
 | |
| 
 | |
| 
 | |
| static int
 | |
| SProcSecurityGenerateAuthorization(
 | |
|     ClientPtr client)
 | |
| {
 | |
|     REQUEST(xSecurityGenerateAuthorizationReq);
 | |
|     register char 	n;
 | |
|     CARD32 *values;
 | |
|     unsigned long nvalues;
 | |
| 
 | |
|     swaps(&stuff->length, n);
 | |
|     REQUEST_AT_LEAST_SIZE(xSecurityGenerateAuthorizationReq);
 | |
|     swaps(&stuff->nbytesAuthProto, n);
 | |
|     swaps(&stuff->nbytesAuthData, n);
 | |
|     swapl(&stuff->valueMask, n);
 | |
|     values = (CARD32 *)(&stuff[1]) +
 | |
| 	((stuff->nbytesAuthProto + (unsigned)3) >> 2) +
 | |
| 	((stuff->nbytesAuthData + (unsigned)3) >> 2);
 | |
|     nvalues = (((CARD32 *)stuff) + stuff->length) - values;
 | |
|     SwapLongs(values, nvalues);
 | |
|     return ProcSecurityGenerateAuthorization(client);
 | |
| } /* SProcSecurityGenerateAuthorization */
 | |
| 
 | |
| 
 | |
| static int
 | |
| SProcSecurityRevokeAuthorization(
 | |
|     ClientPtr client)
 | |
| {
 | |
|     REQUEST(xSecurityRevokeAuthorizationReq);
 | |
|     register char 	n;
 | |
| 
 | |
|     swaps(&stuff->length, n);
 | |
|     REQUEST_SIZE_MATCH(xSecurityRevokeAuthorizationReq);
 | |
|     swapl(&stuff->authId, n);
 | |
|     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);
 | |
| }
 | |
| 
 | |
| /* SecurityDetermineEventPropogationLimits
 | |
|  *
 | |
|  * This is a helper function for SecurityCheckDeviceAccess.
 | |
|  *
 | |
|  * Arguments:
 | |
|  *	dev is the device for which the starting and stopping windows for
 | |
|  *	event propogation should be determined.
 | |
|  *	The values pointed to by ppWin and ppStopWin are not used.
 | |
|  *
 | |
|  * Returns:
 | |
|  *	ppWin is filled in with a pointer to the window at which event
 | |
|  *	propogation for the given device should start given the current
 | |
|  *	state of the server (pointer position, window layout, etc.)
 | |
|  *	ppStopWin is filled in with the window at which event propogation
 | |
|  *	should stop; events should not go to ppStopWin.
 | |
|  *
 | |
|  * Side Effects: none.
 | |
|  */
 | |
| 
 | |
| static void
 | |
| SecurityDetermineEventPropogationLimits(
 | |
|     DeviceIntPtr dev,
 | |
|     WindowPtr *ppWin,
 | |
|     WindowPtr *ppStopWin)
 | |
| {
 | |
|     WindowPtr pFocusWin = dev->focus ? dev->focus->win : NoneWin;
 | |
| 
 | |
|     if (pFocusWin == NoneWin)
 | |
|     { /* no focus -- events don't go anywhere */
 | |
| 	*ppWin = *ppStopWin = NULL;
 | |
| 	return;
 | |
|     }
 | |
| 
 | |
|     if (pFocusWin == PointerRootWin)
 | |
|     { /* focus follows the pointer */
 | |
| 	*ppWin = GetSpriteWindow();
 | |
| 	*ppStopWin = NULL; /* propogate all the way to the root */
 | |
|     }
 | |
|     else
 | |
|     { /* a real window is set for the focus */
 | |
| 	WindowPtr pSpriteWin = GetSpriteWindow();
 | |
| 	*ppStopWin = pFocusWin->parent; /* don't go past the focus window */
 | |
| 
 | |
| 	/* if the pointer is in a subwindow of the focus window, start
 | |
| 	 * at that subwindow, else start at the focus window itself
 | |
| 	 */
 | |
| 	if (IsParent(pFocusWin, pSpriteWin))
 | |
| 	     *ppWin = pSpriteWin;
 | |
| 	else *ppWin = pFocusWin;
 | |
|     }
 | |
| } /* SecurityDetermineEventPropogationLimits */
 | |
| 
 | |
| 
 | |
| /* 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.
 | |
|  */
 | |
| 
 | |
| Bool
 | |
| SecurityCheckDeviceAccess(client, dev, fromRequest)
 | |
|     ClientPtr client;
 | |
|     DeviceIntPtr dev;
 | |
|     Bool fromRequest;
 | |
| {
 | |
|     WindowPtr pWin, pStopWin;
 | |
|     Bool untrusted_got_event;
 | |
|     Bool found_event_window;
 | |
|     Mask eventmask;
 | |
|     int reqtype = 0;
 | |
| 
 | |
|     /* trusted clients always allowed to do anything */
 | |
|     if (client->trustLevel == XSecurityClientTrusted)
 | |
| 	return TRUE;
 | |
| 
 | |
|     /* device security other than keyboard is not implemented yet */
 | |
|     if (dev != inputInfo.keyboard)
 | |
| 	return TRUE;
 | |
| 
 | |
|     /* some untrusted client wants access */
 | |
| 
 | |
|     if (fromRequest)
 | |
|     {
 | |
| 	reqtype = ((xReq *)client->requestBuffer)->reqType;
 | |
| 	switch (reqtype)
 | |
| 	{
 | |
| 	    /* never allow these */
 | |
| 	    case X_ChangeKeyboardMapping:
 | |
| 	    case X_ChangeKeyboardControl:
 | |
| 	    case X_SetModifierMapping:
 | |
| 		SecurityAudit("client %d attempted request %d\n",
 | |
| 			      client->index, reqtype);
 | |
| 		return FALSE;
 | |
| 	    default:
 | |
| 		break;
 | |
| 	}
 | |
|     }
 | |
| 
 | |
|     untrusted_got_event = FALSE;
 | |
|     found_event_window = FALSE;
 | |
| 
 | |
|     if (dev->grab)
 | |
|     {
 | |
| 	untrusted_got_event =
 | |
| 	    ((rClient(dev->grab))->trustLevel != XSecurityClientTrusted);
 | |
|     }
 | |
|     else
 | |
|     {
 | |
| 	SecurityDetermineEventPropogationLimits(dev, &pWin, &pStopWin);
 | |
| 
 | |
| 	eventmask = KeyPressMask | KeyReleaseMask;
 | |
| 	while ( (pWin != pStopWin) && !found_event_window)
 | |
| 	{
 | |
| 	    OtherClients *other;
 | |
| 
 | |
| 	    if (pWin->eventMask & eventmask)
 | |
| 	    {
 | |
| 		found_event_window = TRUE;
 | |
| 		client = wClient(pWin);
 | |
| 		if (client->trustLevel != XSecurityClientTrusted)
 | |
| 		{
 | |
| 		    untrusted_got_event = TRUE;
 | |
| 		}
 | |
| 	    }
 | |
| 	    if (wOtherEventMasks(pWin) & eventmask)
 | |
| 	    {
 | |
| 		found_event_window = TRUE;
 | |
| 		for (other = wOtherClients(pWin); other; other = other->next)
 | |
| 		{
 | |
| 		    if (other->mask & eventmask)
 | |
| 		    {
 | |
| 			client = rClient(other);
 | |
| 			if (client->trustLevel != XSecurityClientTrusted)
 | |
| 			{
 | |
| 			    untrusted_got_event = TRUE;
 | |
| 			    break;
 | |
| 			}
 | |
| 		    }
 | |
| 		}
 | |
| 	    }
 | |
| 	    if (wDontPropagateMask(pWin) & eventmask)
 | |
| 		break;
 | |
| 	    pWin = pWin->parent;
 | |
| 	} /* while propogating the event */
 | |
|     }
 | |
| 
 | |
|     /* allow access by untrusted clients only if an event would have gone 
 | |
|      * to an untrusted client
 | |
|      */
 | |
|     
 | |
|     if (!untrusted_got_event)
 | |
|     {
 | |
| 	char *devname = dev->name;
 | |
| 	if (!devname) devname = "unnamed";
 | |
| 	if (fromRequest)
 | |
| 	    SecurityAudit("client %d attempted request %d device %d (%s)\n",
 | |
| 			  client->index, reqtype, dev->id, devname);
 | |
| 	else
 | |
| 	    SecurityAudit("client %d attempted to access device %d (%s)\n",
 | |
| 			  client->index, dev->id, devname);
 | |
|     }
 | |
|     return untrusted_got_event;
 | |
| } /* SecurityCheckDeviceAccess */
 | |
| 
 | |
| 
 | |
| 
 | |
| /* SecurityAuditResourceIDAccess
 | |
|  *
 | |
|  * Arguments:
 | |
|  *	client is the client doing the resource access.
 | |
|  *	id is the resource id.
 | |
|  *
 | |
|  * Returns: NULL
 | |
|  *
 | |
|  * Side Effects:
 | |
|  *	An audit message is generated with details of the denied
 | |
|  *	resource access.
 | |
|  */
 | |
| 
 | |
| static pointer
 | |
| SecurityAuditResourceIDAccess(
 | |
|     ClientPtr client,
 | |
|     XID id)
 | |
| {
 | |
|     int cid = CLIENT_ID(id);
 | |
|     int reqtype = ((xReq *)client->requestBuffer)->reqType;
 | |
|     switch (reqtype)
 | |
|     {
 | |
| 	case X_ChangeProperty:
 | |
| 	case X_DeleteProperty:
 | |
| 	case X_GetProperty:
 | |
| 	{
 | |
| 	    xChangePropertyReq *req =
 | |
| 		(xChangePropertyReq *)client->requestBuffer;
 | |
| 	    int propertyatom = req->property;
 | |
| 	    char *propertyname = NameForAtom(propertyatom);
 | |
| 
 | |
| 	    SecurityAudit("client %d attempted request %d with window 0x%x property %s of client %d\n",
 | |
| 		   client->index, reqtype, id, propertyname, cid);
 | |
| 	    break;
 | |
| 	}
 | |
| 	default:
 | |
| 	{
 | |
| 	    SecurityAudit("client %d attempted request %d with resource 0x%x of client %d\n",
 | |
| 		   client->index, reqtype, id, cid);
 | |
| 	    break;
 | |
| 	}   
 | |
|     }
 | |
|     return NULL;
 | |
| } /* SecurityAuditResourceIDAccess */
 | |
| 
 | |
| 
 | |
| /* SecurityCheckResourceIDAccess
 | |
|  *
 | |
|  * 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.
 | |
|  *	rval is a pointer to the resource structure for this resource.
 | |
|  *
 | |
|  * Returns:
 | |
|  *	If access is granted, the value of rval that was passed in, else NULL.
 | |
|  *
 | |
|  * Side Effects:
 | |
|  *	Disallowed resource accesses are audited.
 | |
|  */
 | |
| 
 | |
| static pointer
 | |
| SecurityCheckResourceIDAccess(
 | |
|     ClientPtr client,
 | |
|     XID id,
 | |
|     RESTYPE rtype,
 | |
|     Mask access_mode,
 | |
|     pointer rval)
 | |
| {
 | |
|     int cid = CLIENT_ID(id);
 | |
|     int reqtype = ((xReq *)client->requestBuffer)->reqType;
 | |
| 
 | |
|     if (SecurityUnknownAccess == access_mode)
 | |
| 	return rval;  /* for compatibility, we have to allow access */
 | |
| 
 | |
|     switch (reqtype)
 | |
|     { /* these are always allowed */
 | |
| 	case X_QueryTree:
 | |
|         case X_TranslateCoords:
 | |
|         case X_GetGeometry:
 | |
| 	/* property access is controlled in SecurityCheckPropertyAccess */
 | |
| 	case X_GetProperty:
 | |
| 	case X_ChangeProperty:
 | |
| 	case X_DeleteProperty:
 | |
| 	case X_RotateProperties:
 | |
|         case X_ListProperties:
 | |
| 	    return rval;
 | |
| 	default:
 | |
| 	    break;
 | |
|     }
 | |
| 
 | |
|     if (cid != 0)
 | |
|     { /* not a server-owned resource */
 | |
|      /*
 | |
|       * The following 'if' restricts clients to only access resources at
 | |
|       * the same trustLevel.  Since there are currently only two trust levels,
 | |
|       * and trusted clients never call this function, this degenerates into
 | |
|       * saying that untrusted clients can only access resources of other
 | |
|       * untrusted clients.  One way to add the notion of groups would be to
 | |
|       * allow values other than Trusted (0) and Untrusted (1) for this field.
 | |
|       * Clients at the same trust level would be able to use each other's
 | |
|       * resources, but not those of clients at other trust levels.  I haven't
 | |
|       * tried it, but this probably mostly works already.  The obvious
 | |
|       * competing alternative for grouping clients for security purposes is to
 | |
|       * use app groups.  dpw
 | |
|       */
 | |
| 	if (client->trustLevel == clients[cid]->trustLevel
 | |
| #ifdef XAPPGROUP
 | |
| 	    || (RT_COLORMAP == rtype && 
 | |
| 		XagDefaultColormap (client) == (Colormap) id)
 | |
| #endif
 | |
| 	)
 | |
| 	    return rval;
 | |
| 	else
 | |
| 	    return SecurityAuditResourceIDAccess(client, id);
 | |
|     }
 | |
|     else /* server-owned resource - probably a default colormap or root window */
 | |
|     {
 | |
| 	if (RT_WINDOW == rtype || RC_DRAWABLE == rtype)
 | |
| 	{
 | |
| 	    switch (reqtype)
 | |
| 	    {   /* the following operations are allowed on root windows */
 | |
| 	        case X_CreatePixmap:
 | |
| 	        case X_CreateGC:
 | |
| 	        case X_CreateWindow:
 | |
| 	        case X_CreateColormap:
 | |
| 		case X_ListProperties:
 | |
| 		case X_GrabPointer:
 | |
| 	        case X_UngrabButton:
 | |
| 		case X_QueryBestSize:
 | |
| 		case X_GetWindowAttributes:
 | |
| 		    break;
 | |
| 		case X_SendEvent:
 | |
| 		{ /* see if it is an event specified by the ICCCM */
 | |
| 		    xSendEventReq *req = (xSendEventReq *)
 | |
| 						(client->requestBuffer);
 | |
| 		    if (req->propagate == xTrue
 | |
| 			||
 | |
| 			  (req->eventMask != ColormapChangeMask &&
 | |
| 			   req->eventMask != StructureNotifyMask &&
 | |
| 			   req->eventMask !=
 | |
| 			      (SubstructureRedirectMask|SubstructureNotifyMask)
 | |
| 			  )
 | |
| 			||
 | |
| 			  (req->event.u.u.type != UnmapNotify &&
 | |
| 			   req->event.u.u.type != ConfigureRequest &&
 | |
| 			   req->event.u.u.type != ClientMessage
 | |
| 			  )
 | |
| 		       )
 | |
| 		    { /* not an ICCCM event */
 | |
| 			return SecurityAuditResourceIDAccess(client, id);
 | |
| 		    }
 | |
| 		    break;
 | |
| 		} /* case X_SendEvent on root */
 | |
| 
 | |
| 		case X_ChangeWindowAttributes:
 | |
| 		{ /* Allow selection of PropertyNotify and StructureNotify
 | |
| 		   * events on the root.
 | |
| 		   */
 | |
| 		    xChangeWindowAttributesReq *req =
 | |
| 			(xChangeWindowAttributesReq *)(client->requestBuffer);
 | |
| 		    if (req->valueMask == CWEventMask)
 | |
| 		    {
 | |
| 			CARD32 value = *((CARD32 *)(req + 1));
 | |
| 			if ( (value &
 | |
| 			      ~(PropertyChangeMask|StructureNotifyMask)) == 0)
 | |
| 			    break;
 | |
| 		    }
 | |
| 		    return SecurityAuditResourceIDAccess(client, id);
 | |
| 		} /* case X_ChangeWindowAttributes on root */
 | |
| 
 | |
| 		default:
 | |
| 		{
 | |
| #ifdef LBX
 | |
| 		    /* XXX really need per extension dispatching */
 | |
| 		    if (reqtype == LbxReqCode) {
 | |
| 			switch (((xReq *)client->requestBuffer)->data) {
 | |
| 			case X_LbxGetProperty:
 | |
| 			case X_LbxChangeProperty:
 | |
| 			    return rval;
 | |
| 			default:
 | |
| 			    break;
 | |
| 			}
 | |
| 		    }
 | |
| #endif
 | |
| 		    /* others not allowed */
 | |
| 		    return SecurityAuditResourceIDAccess(client, id);
 | |
| 		}
 | |
| 	    }
 | |
| 	} /* end server-owned window or drawable */
 | |
| 	else if (SecurityAuthorizationResType == rtype)
 | |
| 	{
 | |
| 	    SecurityAuthorizationPtr pAuth = (SecurityAuthorizationPtr)rval;
 | |
| 	    if (pAuth->trustLevel != client->trustLevel)
 | |
| 		return SecurityAuditResourceIDAccess(client, id);
 | |
| 	}
 | |
| 	else if (RT_COLORMAP != rtype)
 | |
| 	{ /* don't allow anything else besides colormaps */
 | |
| 	    return SecurityAuditResourceIDAccess(client, id);
 | |
| 	}
 | |
|     }
 | |
|     return rval;
 | |
| } /* SecurityCheckResourceIDAccess */
 | |
| 
 | |
| 
 | |
| /* 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 client->trustLevel.
 | |
|  * 
 | |
|  * 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
 | |
| SecurityClientStateCallback(
 | |
|     CallbackListPtr *pcbl,
 | |
|     pointer nulldata,
 | |
|     pointer calldata)
 | |
| {
 | |
|     NewClientInfoRec *pci = (NewClientInfoRec *)calldata;
 | |
|     ClientPtr client = pci->client;
 | |
| 
 | |
|     switch (client->clientState)
 | |
|     {
 | |
| 	case ClientStateRunning:
 | |
| 	{ 
 | |
| 	    XID authId = AuthorizationIDOfClient(client);
 | |
| 	    SecurityAuthorizationPtr pAuth;
 | |
| 
 | |
| 	    client->authId = authId;
 | |
| 	    pAuth = (SecurityAuthorizationPtr)LookupIDByType(authId,
 | |
| 						SecurityAuthorizationResType);
 | |
| 	    if (pAuth)
 | |
| 	    { /* it is a generated authorization */
 | |
| 		pAuth->refcnt++;
 | |
| 		if (pAuth->refcnt == 1)
 | |
| 		{
 | |
| 		    if (pAuth->timer) TimerCancel(pAuth->timer);
 | |
| 		}
 | |
| 		client->trustLevel = pAuth->trustLevel;
 | |
| 		if (client->trustLevel != XSecurityClientTrusted)
 | |
| 		{
 | |
| 		    client->CheckAccess = SecurityCheckResourceIDAccess;
 | |
| 		    client->requestVector = client->swapped ?
 | |
| 			SwappedUntrustedProcVector : UntrustedProcVector;
 | |
| 		}
 | |
| 	    }
 | |
| 	    break;
 | |
| 	}
 | |
| 	case ClientStateGone:
 | |
| 	case ClientStateRetained: /* client disconnected */
 | |
| 	{
 | |
| 	    XID authId = client->authId;
 | |
| 	    SecurityAuthorizationPtr pAuth;
 | |
| 
 | |
| 	    pAuth = (SecurityAuthorizationPtr)LookupIDByType(authId,
 | |
| 						SecurityAuthorizationResType);
 | |
| 	    if (pAuth)
 | |
| 	    { /* it is a generated authorization */
 | |
| 		pAuth->refcnt--;
 | |
| 		if (pAuth->refcnt == 0)
 | |
| 		{
 | |
| 		    SecurityStartAuthorizationTimer(pAuth);
 | |
| 		}
 | |
| 	    }	    
 | |
| 	    break;
 | |
| 	}
 | |
| 	default: break; 
 | |
|     }
 | |
| } /* SecurityClientStateCallback */
 | |
| 
 | |
| #ifdef LBX
 | |
| Bool
 | |
| SecuritySameLevel(client, authId)
 | |
|     ClientPtr client;
 | |
|     XID authId;
 | |
| {
 | |
|     SecurityAuthorizationPtr pAuth;
 | |
| 
 | |
|     pAuth = (SecurityAuthorizationPtr)LookupIDByType(authId,
 | |
| 						SecurityAuthorizationResType);
 | |
|     if (pAuth)
 | |
| 	return client->trustLevel == pAuth->trustLevel;
 | |
|     return client->trustLevel == XSecurityClientTrusted;
 | |
| }
 | |
| #endif
 | |
| 
 | |
| /* SecurityCensorImage
 | |
|  *
 | |
|  * Called after pScreen->GetImage to prevent pieces or trusted windows from
 | |
|  * being returned in image data from an untrusted window.
 | |
|  *
 | |
|  * Arguments:
 | |
|  *	client is the client doing the GetImage.
 | |
|  *      pVisibleRegion is the visible region of the window.
 | |
|  *	widthBytesLine is the width in bytes of one horizontal line in pBuf.
 | |
|  *	pDraw is the source window.
 | |
|  *	x, y, w, h is the rectangle of image data from pDraw in pBuf.
 | |
|  *	format is the format of the image data in pBuf: ZPixmap or XYPixmap.
 | |
|  *	pBuf is the image data.
 | |
|  *
 | |
|  * Returns: nothing.
 | |
|  *
 | |
|  * Side Effects:
 | |
|  *	Any part of the rectangle (x, y, w, h) that is outside the visible
 | |
|  *	region of the window will be destroyed (overwritten) in pBuf.
 | |
|  */
 | |
| void
 | |
| SecurityCensorImage(client, pVisibleRegion, widthBytesLine, pDraw, x, y, w, h,
 | |
| 		    format, pBuf)
 | |
|     ClientPtr client;
 | |
|     RegionPtr pVisibleRegion;
 | |
|     long widthBytesLine;
 | |
|     DrawablePtr pDraw;
 | |
|     int x, y, w, h;
 | |
|     unsigned int format;
 | |
|     char * pBuf;
 | |
| {
 | |
|     RegionRec imageRegion;  /* region representing x,y,w,h */
 | |
|     RegionRec censorRegion; /* region to obliterate */
 | |
|     BoxRec imageBox;
 | |
|     int nRects;
 | |
| 
 | |
|     imageBox.x1 = x;
 | |
|     imageBox.y1 = y;
 | |
|     imageBox.x2 = x + w;
 | |
|     imageBox.y2 = y + h;
 | |
|     REGION_INIT(pScreen, &imageRegion, &imageBox, 1);
 | |
|     REGION_NULL(pScreen, &censorRegion);
 | |
| 
 | |
|     /* censorRegion = imageRegion - visibleRegion */
 | |
|     REGION_SUBTRACT(pScreen, &censorRegion, &imageRegion, pVisibleRegion);
 | |
|     nRects = REGION_NUM_RECTS(&censorRegion);
 | |
|     if (nRects > 0)
 | |
|     { /* we have something to censor */
 | |
| 	GCPtr pScratchGC = NULL;
 | |
| 	PixmapPtr pPix = NULL;
 | |
| 	xRectangle *pRects = NULL;
 | |
| 	Bool failed = FALSE;
 | |
| 	int depth = 1;
 | |
| 	int bitsPerPixel = 1;
 | |
| 	int i;
 | |
| 	BoxPtr pBox;
 | |
| 
 | |
| 	/* convert region to list-of-rectangles for PolyFillRect */
 | |
| 
 | |
| 	pRects = (xRectangle *)ALLOCATE_LOCAL(nRects * sizeof(xRectangle *));
 | |
| 	if (!pRects)
 | |
| 	{
 | |
| 	    failed = TRUE;
 | |
| 	    goto failSafe;
 | |
| 	}
 | |
| 	for (pBox = REGION_RECTS(&censorRegion), i = 0;
 | |
| 	     i < nRects;
 | |
| 	     i++, pBox++)
 | |
| 	{
 | |
| 	    pRects[i].x = pBox->x1;
 | |
| 	    pRects[i].y = pBox->y1 - imageBox.y1;
 | |
| 	    pRects[i].width  = pBox->x2 - pBox->x1;
 | |
| 	    pRects[i].height = pBox->y2 - pBox->y1;
 | |
| 	}
 | |
| 
 | |
| 	/* use pBuf as a fake pixmap */
 | |
| 
 | |
| 	if (format == ZPixmap)
 | |
| 	{
 | |
| 	    depth = pDraw->depth;
 | |
| 	    bitsPerPixel = pDraw->bitsPerPixel;
 | |
| 	}
 | |
| 
 | |
| 	pPix = GetScratchPixmapHeader(pDraw->pScreen, w, h,
 | |
| 		    depth, bitsPerPixel,
 | |
| 		    widthBytesLine, (pointer)pBuf);
 | |
| 	if (!pPix)
 | |
| 	{
 | |
| 	    failed = TRUE;
 | |
| 	    goto failSafe;
 | |
| 	}
 | |
| 
 | |
| 	pScratchGC = GetScratchGC(depth, pPix->drawable.pScreen);
 | |
| 	if (!pScratchGC)
 | |
| 	{
 | |
| 	    failed = TRUE;
 | |
| 	    goto failSafe;
 | |
| 	}
 | |
| 
 | |
| 	ValidateGC(&pPix->drawable, pScratchGC);
 | |
| 	(* pScratchGC->ops->PolyFillRect)(&pPix->drawable,
 | |
| 			    pScratchGC, nRects, pRects);
 | |
| 
 | |
|     failSafe:
 | |
| 	if (failed)
 | |
| 	{
 | |
| 	    /* Censoring was not completed above.  To be safe, wipe out
 | |
| 	     * all the image data so that nothing trusted gets out.
 | |
| 	     */
 | |
| 	    bzero(pBuf, (int)(widthBytesLine * h));
 | |
| 	}
 | |
| 	if (pRects)     DEALLOCATE_LOCAL(pRects);
 | |
| 	if (pScratchGC) FreeScratchGC(pScratchGC);
 | |
| 	if (pPix)       FreeScratchPixmapHeader(pPix);
 | |
|     }
 | |
|     REGION_UNINIT(pScreen, &imageRegion);
 | |
|     REGION_UNINIT(pScreen, &censorRegion);
 | |
| } /* SecurityCensorImage */
 | |
| 
 | |
| /**********************************************************************/
 | |
| 
 | |
| typedef struct _PropertyAccessRec {
 | |
|     ATOM name;
 | |
|     ATOM mustHaveProperty;
 | |
|     char *mustHaveValue;
 | |
|     char windowRestriction;
 | |
| #define SecurityAnyWindow          0
 | |
| #define SecurityRootWindow         1
 | |
| #define SecurityWindowWithProperty 2
 | |
|     char readAction;
 | |
|     char writeAction;
 | |
|     char destroyAction;
 | |
|     struct _PropertyAccessRec *next;
 | |
| } PropertyAccessRec, *PropertyAccessPtr;
 | |
| 
 | |
| static PropertyAccessPtr PropertyAccessList = NULL;
 | |
| static char SecurityDefaultAction = SecurityErrorOperation;
 | |
| static char *SecurityPolicyFile = DEFAULTPOLICYFILE;
 | |
| static ATOM SecurityMaxPropertyName = 0;
 | |
| 
 | |
| static char *SecurityKeywords[] = {
 | |
| #define SecurityKeywordComment 0
 | |
|     "#",
 | |
| #define SecurityKeywordProperty 1
 | |
|     "property",
 | |
| #define SecurityKeywordSitePolicy 2
 | |
|     "sitepolicy",
 | |
| #define SecurityKeywordRoot 3
 | |
|     "root",
 | |
| #define SecurityKeywordAny 4
 | |
|     "any"
 | |
| };
 | |
| 
 | |
| #define NUMKEYWORDS (sizeof(SecurityKeywords) / sizeof(char *))
 | |
| 
 | |
| #undef PROPDEBUG
 | |
| /*#define PROPDEBUG  1*/
 | |
| 
 | |
| static void
 | |
| SecurityFreePropertyAccessList(void)
 | |
| {
 | |
|     while (PropertyAccessList)
 | |
|     {
 | |
| 	PropertyAccessPtr freeit = PropertyAccessList;
 | |
| 	PropertyAccessList = PropertyAccessList->next;
 | |
| 	xfree(freeit);
 | |
|     }
 | |
| } /* SecurityFreePropertyAccessList */
 | |
| 
 | |
| #ifndef __UNIXOS2__
 | |
| #define SecurityIsWhitespace(c) ( (c == ' ') || (c == '\t') || (c == '\n') )
 | |
| #else
 | |
| #define SecurityIsWhitespace(c) ( (c == ' ') || (c == '\t') || (c == '\n') || (c == '\r') )
 | |
| #endif
 | |
| 
 | |
| static char *
 | |
| SecuritySkipWhitespace(
 | |
|     char *p)
 | |
| {
 | |
|     while (SecurityIsWhitespace(*p))
 | |
| 	p++;
 | |
|     return p;
 | |
| } /* SecuritySkipWhitespace */
 | |
| 
 | |
| 
 | |
| static char *
 | |
| SecurityParseString(
 | |
|     char **rest)
 | |
| {
 | |
|     char *startOfString;
 | |
|     char *s = *rest;
 | |
|     char endChar = 0;
 | |
| 
 | |
|     s = SecuritySkipWhitespace(s);
 | |
| 
 | |
|     if (*s == '"' || *s == '\'')
 | |
|     {
 | |
| 	endChar = *s++;
 | |
| 	startOfString = s;
 | |
| 	while (*s && (*s != endChar))
 | |
| 	    s++;
 | |
|     }
 | |
|     else
 | |
|     {
 | |
| 	startOfString = s;
 | |
| 	while (*s && !SecurityIsWhitespace(*s))
 | |
| 	    s++;
 | |
|     }
 | |
|     if (*s)
 | |
|     {
 | |
| 	*s = '\0';
 | |
| 	*rest = s + 1;
 | |
| 	return startOfString;
 | |
|     }
 | |
|     else
 | |
|     {
 | |
| 	*rest = s;
 | |
| 	return (endChar) ? NULL : startOfString;
 | |
|     }
 | |
| } /* SecurityParseString */
 | |
| 
 | |
| 
 | |
| static int
 | |
| SecurityParseKeyword(
 | |
|     char **p)
 | |
| {
 | |
|     int i;
 | |
|     char *s = *p;
 | |
|     s = SecuritySkipWhitespace(s);
 | |
|     for (i = 0; i < NUMKEYWORDS; i++)
 | |
|     {
 | |
| 	int len = strlen(SecurityKeywords[i]);
 | |
| 	if (strncmp(s, SecurityKeywords[i], len) == 0)
 | |
| 	{
 | |
| 	    *p = s + len;
 | |
| 	    return (i);
 | |
| 	}
 | |
|     }
 | |
|     *p = s;
 | |
|     return -1;
 | |
| } /* SecurityParseKeyword */
 | |
| 
 | |
| 
 | |
| static Bool
 | |
| SecurityParsePropertyAccessRule(
 | |
|     char *p)
 | |
| {
 | |
|     char *propname;
 | |
|     char c;
 | |
|     char action = SecurityDefaultAction;
 | |
|     char readAction, writeAction, destroyAction;
 | |
|     PropertyAccessPtr pacl, prev, cur;
 | |
|     char *mustHaveProperty = NULL;
 | |
|     char *mustHaveValue = NULL;
 | |
|     Bool invalid;
 | |
|     char windowRestriction;
 | |
|     int size;
 | |
|     int keyword;
 | |
| 
 | |
|     /* get property name */
 | |
|     propname = SecurityParseString(&p);
 | |
|     if (!propname || (strlen(propname) == 0))
 | |
| 	return FALSE;
 | |
| 
 | |
|     /* get window on which property must reside for rule to apply */
 | |
| 
 | |
|     keyword = SecurityParseKeyword(&p);
 | |
|     if (keyword == SecurityKeywordRoot)
 | |
| 	windowRestriction = SecurityRootWindow;
 | |
|     else if (keyword == SecurityKeywordAny) 
 | |
| 	windowRestriction = SecurityAnyWindow;
 | |
|     else /* not root or any, must be a property name */
 | |
|     {
 | |
| 	mustHaveProperty = SecurityParseString(&p);
 | |
| 	if (!mustHaveProperty || (strlen(mustHaveProperty) == 0))
 | |
| 	    return FALSE;
 | |
| 	windowRestriction = SecurityWindowWithProperty;
 | |
| 	p = SecuritySkipWhitespace(p);
 | |
| 	if (*p == '=')
 | |
| 	{ /* property value is specified too */
 | |
| 	    p++; /* skip over '=' */
 | |
| 	    mustHaveValue = SecurityParseString(&p);
 | |
| 	    if (!mustHaveValue)
 | |
| 		return FALSE;
 | |
| 	}
 | |
|     }
 | |
| 
 | |
|     /* get operations and actions */
 | |
| 
 | |
|     invalid = FALSE;
 | |
|     readAction = writeAction = destroyAction = SecurityDefaultAction;
 | |
|     while ( (c = *p++) && !invalid)
 | |
|     {
 | |
| 	switch (c)
 | |
| 	{
 | |
| 	    case 'i': action = SecurityIgnoreOperation; break;
 | |
| 	    case 'a': action = SecurityAllowOperation;  break;
 | |
| 	    case 'e': action = SecurityErrorOperation;  break;
 | |
| 
 | |
| 	    case 'r': readAction    = action; break;
 | |
| 	    case 'w': writeAction   = action; break;
 | |
| 	    case 'd': destroyAction = action; break;
 | |
| 
 | |
| 	    default :
 | |
| 		if (!SecurityIsWhitespace(c))
 | |
| 		    invalid = TRUE;
 | |
| 	    break;
 | |
| 	}
 | |
|     }
 | |
|     if (invalid)
 | |
| 	return FALSE;
 | |
| 
 | |
|     /* We've successfully collected all the information needed for this
 | |
|      * property access rule.  Now record it in a PropertyAccessRec.
 | |
|      */
 | |
|     size = sizeof(PropertyAccessRec);
 | |
| 
 | |
|     /* If there is a property value string, allocate space for it 
 | |
|      * right after the PropertyAccessRec.
 | |
|      */
 | |
|     if (mustHaveValue)
 | |
| 	size += strlen(mustHaveValue) + 1;
 | |
|     pacl = (PropertyAccessPtr)Xalloc(size);
 | |
|     if (!pacl)
 | |
| 	return FALSE;
 | |
| 
 | |
|     pacl->name = MakeAtom(propname, strlen(propname), TRUE);
 | |
|     if (pacl->name == BAD_RESOURCE)
 | |
|     {
 | |
| 	Xfree(pacl);
 | |
| 	return FALSE;
 | |
|     }
 | |
|     if (mustHaveProperty)
 | |
|     {
 | |
| 	pacl->mustHaveProperty = MakeAtom(mustHaveProperty,
 | |
| 					  strlen(mustHaveProperty), TRUE);
 | |
| 	if (pacl->mustHaveProperty == BAD_RESOURCE)
 | |
| 	{
 | |
| 	    Xfree(pacl);
 | |
| 	    return FALSE;
 | |
| 	}
 | |
|     }
 | |
|     else
 | |
| 	pacl->mustHaveProperty = 0;
 | |
| 
 | |
|     if (mustHaveValue)
 | |
|     {
 | |
| 	pacl->mustHaveValue = (char *)(pacl + 1);
 | |
| 	strcpy(pacl->mustHaveValue, mustHaveValue);
 | |
|     }
 | |
|     else
 | |
| 	pacl->mustHaveValue = NULL;
 | |
| 
 | |
|     SecurityMaxPropertyName = max(SecurityMaxPropertyName, pacl->name);
 | |
| 
 | |
|     pacl->windowRestriction = windowRestriction;
 | |
|     pacl->readAction  = readAction;
 | |
|     pacl->writeAction = writeAction;
 | |
|     pacl->destroyAction = destroyAction;
 | |
| 
 | |
|     /* link the new rule into the list of rules in order of increasing
 | |
|      * property name (atom) value to make searching easier
 | |
|      */
 | |
| 
 | |
|     for (prev = NULL,  cur = PropertyAccessList;
 | |
| 	 cur && cur->name <= pacl->name;
 | |
| 	 prev = cur, cur = cur->next)
 | |
| 	;
 | |
|     if (!prev)
 | |
|     {
 | |
| 	pacl->next = cur;
 | |
| 	PropertyAccessList = pacl;
 | |
|     }
 | |
|     else
 | |
|     {
 | |
| 	prev->next = pacl;
 | |
| 	pacl->next = cur;
 | |
|     }
 | |
|     return TRUE;
 | |
| } /* SecurityParsePropertyAccessRule */
 | |
| 
 | |
| static char **SecurityPolicyStrings = NULL;
 | |
| static int nSecurityPolicyStrings = 0;
 | |
| 
 | |
| static Bool
 | |
| SecurityParseSitePolicy(
 | |
|     char *p)
 | |
| {
 | |
|     char *policyStr = SecurityParseString(&p);
 | |
|     char *copyPolicyStr;
 | |
|     char **newStrings;
 | |
| 
 | |
|     if (!policyStr)
 | |
| 	return FALSE;
 | |
| 
 | |
|     copyPolicyStr = (char *)Xalloc(strlen(policyStr) + 1);
 | |
|     if (!copyPolicyStr)
 | |
| 	return TRUE;
 | |
|     strcpy(copyPolicyStr, policyStr);
 | |
|     newStrings = (char **)Xrealloc(SecurityPolicyStrings,
 | |
| 			  sizeof (char *) * (nSecurityPolicyStrings + 1));
 | |
|     if (!newStrings)
 | |
|     {
 | |
| 	Xfree(copyPolicyStr);
 | |
| 	return TRUE;
 | |
|     }
 | |
| 
 | |
|     SecurityPolicyStrings = newStrings;
 | |
|     SecurityPolicyStrings[nSecurityPolicyStrings++] = copyPolicyStr;
 | |
| 
 | |
|     return TRUE;
 | |
| 
 | |
| } /* SecurityParseSitePolicy */
 | |
| 
 | |
| 
 | |
| char **
 | |
| SecurityGetSitePolicyStrings(n)
 | |
|     int *n;
 | |
| {
 | |
|     *n = nSecurityPolicyStrings;
 | |
|     return SecurityPolicyStrings;
 | |
| } /* SecurityGetSitePolicyStrings */
 | |
| 
 | |
| static void
 | |
| SecurityFreeSitePolicyStrings(void)
 | |
| {
 | |
|     if (SecurityPolicyStrings)
 | |
|     {
 | |
| 	assert(nSecurityPolicyStrings);
 | |
| 	while (nSecurityPolicyStrings--)
 | |
| 	{
 | |
| 	    Xfree(SecurityPolicyStrings[nSecurityPolicyStrings]);
 | |
| 	}
 | |
| 	Xfree(SecurityPolicyStrings);
 | |
| 	SecurityPolicyStrings = NULL;
 | |
| 	nSecurityPolicyStrings = 0;
 | |
|     }
 | |
| } /* SecurityFreeSitePolicyStrings */
 | |
| 
 | |
| 
 | |
| static void
 | |
| SecurityLoadPropertyAccessList(void)
 | |
| {
 | |
|     FILE *f;
 | |
|     int lineNumber = 0;
 | |
| 
 | |
|     SecurityMaxPropertyName = 0;
 | |
| 
 | |
|     if (!SecurityPolicyFile)
 | |
| 	return;
 | |
| 
 | |
| #ifndef __UNIXOS2__
 | |
|     f = fopen(SecurityPolicyFile, "r");
 | |
| #else
 | |
|     f = fopen((char*)__XOS2RedirRoot(SecurityPolicyFile), "r");
 | |
| #endif    
 | |
|     if (!f)
 | |
|     {
 | |
| 	ErrorF("error opening security policy file %s\n",
 | |
| 	       SecurityPolicyFile);
 | |
| 	return;
 | |
|     }
 | |
| 
 | |
|     while (!feof(f))
 | |
|     {
 | |
| 	char buf[200];
 | |
| 	Bool validLine;
 | |
| 	char *p;
 | |
| 
 | |
| 	if (!(p = fgets(buf, sizeof(buf), f)))
 | |
| 	    break;
 | |
| 	lineNumber++;
 | |
| 
 | |
| 	/* if first line, check version number */
 | |
| 	if (lineNumber == 1)
 | |
| 	{
 | |
| 	    char *v = SecurityParseString(&p);
 | |
| 	    if (strcmp(v, SECURITY_POLICY_FILE_VERSION) != 0)
 | |
| 	    {
 | |
| 		ErrorF("%s: invalid security policy file version, ignoring file\n",
 | |
| 		       SecurityPolicyFile);
 | |
| 		break;
 | |
| 	    }
 | |
| 	    validLine = TRUE;
 | |
| 	}
 | |
| 	else
 | |
| 	{
 | |
| 	    switch (SecurityParseKeyword(&p))
 | |
| 	    {
 | |
| 		case SecurityKeywordComment:
 | |
| 		    validLine = TRUE;
 | |
| 		break;
 | |
| 
 | |
| 		case SecurityKeywordProperty:
 | |
| 		    validLine = SecurityParsePropertyAccessRule(p);
 | |
| 		break;
 | |
| 
 | |
| 		case SecurityKeywordSitePolicy:
 | |
| 		    validLine = SecurityParseSitePolicy(p);
 | |
| 		break;
 | |
| 
 | |
| 		default:
 | |
| 		    validLine = (*p == '\0'); /* blank lines OK, others not */
 | |
| 		break;
 | |
| 	    }
 | |
| 	}
 | |
| 
 | |
| 	if (!validLine)
 | |
| 	    ErrorF("Line %d of %s invalid, ignoring\n",
 | |
| 		   lineNumber, SecurityPolicyFile);
 | |
|     } /* end while more input */
 | |
| 
 | |
| #ifdef PROPDEBUG
 | |
|     {
 | |
| 	PropertyAccessPtr pacl;
 | |
| 	char *op = "aie";
 | |
| 	for (pacl = PropertyAccessList; pacl; pacl = pacl->next)
 | |
| 	{
 | |
| 	    ErrorF("property %s ", NameForAtom(pacl->name));
 | |
| 	    switch (pacl->windowRestriction)
 | |
| 	    {
 | |
| 		case SecurityAnyWindow: ErrorF("any "); break;
 | |
| 		case SecurityRootWindow: ErrorF("root "); break;
 | |
| 		case SecurityWindowWithProperty:
 | |
| 		{
 | |
| 		    ErrorF("%s ", NameForAtom(pacl->mustHaveProperty));
 | |
| 		    if (pacl->mustHaveValue)
 | |
| 			ErrorF(" = \"%s\" ", pacl->mustHaveValue);
 | |
| 
 | |
| 		}
 | |
| 		break;
 | |
| 	    }
 | |
| 	    ErrorF("%cr %cw %cd\n", op[pacl->readAction],
 | |
| 		   op[pacl->writeAction], op[pacl->destroyAction]);
 | |
| 	}
 | |
|     }
 | |
| #endif /* PROPDEBUG */
 | |
| 
 | |
|     fclose(f);
 | |
| } /* SecurityLoadPropertyAccessList */
 | |
| 
 | |
| 
 | |
| static Bool
 | |
| SecurityMatchString(
 | |
|     char *ws,
 | |
|     char *cs)
 | |
| {
 | |
|     while (*ws && *cs)
 | |
|     {
 | |
| 	if (*ws == '*')
 | |
| 	{
 | |
| 	    Bool match = FALSE;
 | |
| 	    ws++;
 | |
| 	    while (!(match = SecurityMatchString(ws, cs)) && *cs)
 | |
| 	    {
 | |
| 		cs++;
 | |
| 	    }
 | |
| 	    return match;
 | |
| 	}
 | |
| 	else if (*ws == *cs)
 | |
| 	{
 | |
| 	    ws++;
 | |
| 	    cs++;
 | |
| 	}
 | |
| 	else break;
 | |
|     }
 | |
|     return ( ( (*ws == '\0') || ((*ws == '*') && *(ws+1) == '\0') )
 | |
| 	     && (*cs == '\0') );
 | |
| } /* SecurityMatchString */
 | |
| 
 | |
| #ifdef PROPDEBUG
 | |
| #include <sys/types.h>
 | |
| #include <sys/stat.h>
 | |
| #endif
 | |
| 
 | |
| 
 | |
| char
 | |
| SecurityCheckPropertyAccess(client, pWin, propertyName, access_mode)
 | |
|     ClientPtr client;
 | |
|     WindowPtr pWin;
 | |
|     ATOM propertyName;
 | |
|     Mask access_mode;
 | |
| {
 | |
|     PropertyAccessPtr pacl;
 | |
|     char action = SecurityDefaultAction;
 | |
| 
 | |
|     /* if client trusted or window untrusted, allow operation */
 | |
| 
 | |
|     if ( (client->trustLevel == XSecurityClientTrusted) ||
 | |
| 	 (wClient(pWin)->trustLevel != XSecurityClientTrusted) )
 | |
| 	return SecurityAllowOperation;
 | |
| 
 | |
| #ifdef PROPDEBUG
 | |
|     /* For testing, it's more convenient if the property rules file gets
 | |
|      * reloaded whenever it changes, so we can rapidly try things without
 | |
|      * having to reset the server.
 | |
|      */
 | |
|     {
 | |
| 	struct stat buf;
 | |
| 	static time_t lastmod = 0;
 | |
| 	int ret = stat(SecurityPolicyFile , &buf);
 | |
| 	if ( (ret == 0) && (buf.st_mtime > lastmod) )
 | |
| 	{
 | |
| 	    ErrorF("reloading property rules\n");
 | |
| 	    SecurityFreePropertyAccessList();
 | |
| 	    SecurityLoadPropertyAccessList();
 | |
| 	    lastmod = buf.st_mtime;
 | |
| 	}
 | |
|     }
 | |
| #endif
 | |
| 
 | |
|     /* If the property atom is bigger than any atoms on the list, 
 | |
|      * we know we won't find it, so don't even bother looking.
 | |
|      */
 | |
|     if (propertyName <= SecurityMaxPropertyName)
 | |
|     {
 | |
| 	/* untrusted client operating on trusted window; see if it's allowed */
 | |
| 
 | |
| 	for (pacl = PropertyAccessList; pacl; pacl = pacl->next)
 | |
| 	{
 | |
| 	    if (pacl->name < propertyName)
 | |
| 		continue;
 | |
| 	    if (pacl->name > propertyName)
 | |
| 		break;
 | |
| 
 | |
| 	    /* pacl->name == propertyName, so see if it applies to this window */
 | |
| 
 | |
| 	    switch (pacl->windowRestriction)
 | |
| 	    {
 | |
| 		case SecurityAnyWindow: /* always applies */
 | |
| 		    break;
 | |
| 
 | |
| 		case SecurityRootWindow:
 | |
| 		{
 | |
| 		    /* if not a root window, this rule doesn't apply */
 | |
| 		    if (pWin->parent)
 | |
| 			continue;
 | |
| 		}
 | |
| 		break;
 | |
| 
 | |
| 		case SecurityWindowWithProperty:
 | |
| 		{
 | |
| 		    PropertyPtr pProp = wUserProps (pWin);
 | |
| 		    Bool match = FALSE;
 | |
| 		    char *p;
 | |
| 		    char *pEndData;
 | |
| 
 | |
| 		    while (pProp)
 | |
| 		    {
 | |
| 			if (pProp->propertyName == pacl->mustHaveProperty)
 | |
| 			    break;
 | |
| 			pProp = pProp->next;
 | |
| 		    }
 | |
| 		    if (!pProp)
 | |
| 			continue;
 | |
| 		    if (!pacl->mustHaveValue)
 | |
| 			break;
 | |
| 		    if (pProp->type != XA_STRING || pProp->format != 8)
 | |
| 			continue;
 | |
| 
 | |
| 		    p = pProp->data;
 | |
| 		    pEndData = ((char *)pProp->data) + pProp->size;
 | |
| 		    while (!match && p < pEndData)
 | |
| 		    {
 | |
| 			 if (SecurityMatchString(pacl->mustHaveValue, p))
 | |
| 			     match = TRUE;
 | |
| 			 else
 | |
| 			 { /* skip to the next string */
 | |
| 			     while (*p++ && p < pEndData)
 | |
| 				 ;
 | |
| 			 }
 | |
| 		    }
 | |
| 		    if (!match)
 | |
| 			continue;
 | |
| 		}
 | |
| 		break; /* end case SecurityWindowWithProperty */
 | |
| 	    } /* end switch on windowRestriction */
 | |
| 
 | |
| 	    /* If we get here, the property access rule pacl applies.
 | |
| 	     * If pacl doesn't apply, something above should have
 | |
| 	     * executed a continue, which will skip the follwing code.
 | |
| 	     */
 | |
| 	    action = SecurityAllowOperation;
 | |
| 	    if (access_mode & SecurityReadAccess)
 | |
| 		action = max(action, pacl->readAction);
 | |
| 	    if (access_mode & SecurityWriteAccess)
 | |
| 		action = max(action, pacl->writeAction);
 | |
| 	    if (access_mode & SecurityDestroyAccess)
 | |
| 		action = max(action, pacl->destroyAction);
 | |
| 	    break;
 | |
| 	} /* end for each pacl */
 | |
|     } /* end if propertyName <= SecurityMaxPropertyName */
 | |
| 
 | |
|     if (SecurityAllowOperation != action)
 | |
|     { /* audit the access violation */
 | |
| 	int cid = CLIENT_ID(pWin->drawable.id);
 | |
| 	int reqtype = ((xReq *)client->requestBuffer)->reqType;
 | |
| 	char *actionstr = (SecurityIgnoreOperation == action) ?
 | |
| 							"ignored" : "error";
 | |
| 	SecurityAudit("client %d attempted request %d with window 0x%x property %s (atom 0x%x) of client %d, %s\n",
 | |
| 		client->index, reqtype, pWin->drawable.id,
 | |
| 		      NameForAtom(propertyName), propertyName, cid, actionstr);
 | |
|     }
 | |
|     return action;
 | |
| } /* SecurityCheckPropertyAccess */
 | |
| 
 | |
| 
 | |
| /* 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)
 | |
| {
 | |
|     SecurityFreePropertyAccessList();
 | |
|     SecurityFreeSitePolicyStrings();
 | |
| } /* SecurityResetProc */
 | |
| 
 | |
| 
 | |
| int
 | |
| XSecurityOptions(argc, argv, i)
 | |
|     int argc;
 | |
|     char **argv;
 | |
|     int i;
 | |
| {
 | |
|     if (strcmp(argv[i], "-sp") == 0)
 | |
|     {
 | |
| 	if (i < argc)
 | |
| 	    SecurityPolicyFile = argv[++i];
 | |
| 	return (i + 1);
 | |
|     }
 | |
|     return (i);
 | |
| } /* XSecurityOptions */
 | |
| 
 | |
| 
 | |
| 
 | |
| /* SecurityExtensionInit
 | |
|  *
 | |
|  * Arguments: none.
 | |
|  *
 | |
|  * Returns: nothing.
 | |
|  *
 | |
|  * Side Effects:
 | |
|  *	Enables the Security extension if possible.
 | |
|  */
 | |
| 
 | |
| void
 | |
| SecurityExtensionInit(INITARGS)
 | |
| {
 | |
|     ExtensionEntry	*extEntry;
 | |
|     int i;
 | |
| 
 | |
|     SecurityAuthorizationResType =
 | |
| 	CreateNewResourceType(SecurityDeleteAuthorization);
 | |
| 
 | |
|     RTEventClient = CreateNewResourceType(
 | |
| 				SecurityDeleteAuthorizationEventClient);
 | |
| 
 | |
|     if (!SecurityAuthorizationResType || !RTEventClient)
 | |
| 	return;
 | |
| 
 | |
|     RTEventClient |= RC_NEVERRETAIN;
 | |
| 
 | |
|     if (!AddCallback(&ClientStateCallback, SecurityClientStateCallback, NULL))
 | |
| 	return;
 | |
| 
 | |
|     extEntry = AddExtension(SECURITY_EXTENSION_NAME,
 | |
| 			    XSecurityNumberEvents, XSecurityNumberErrors,
 | |
| 			    ProcSecurityDispatch, SProcSecurityDispatch,
 | |
|                             SecurityResetProc, StandardMinorOpcode);
 | |
| 
 | |
|     SecurityErrorBase = extEntry->errorBase;
 | |
|     SecurityEventBase = extEntry->eventBase;
 | |
| 
 | |
|     EventSwapVector[SecurityEventBase + XSecurityAuthorizationRevoked] =
 | |
| 	(EventSwapPtr)SwapSecurityAuthorizationRevokedEvent;
 | |
| 
 | |
|     /* initialize untrusted proc vectors */
 | |
| 
 | |
|     for (i = 0; i < 128; i++)
 | |
|     {
 | |
| 	UntrustedProcVector[i] = ProcVector[i];
 | |
| 	SwappedUntrustedProcVector[i] = SwappedProcVector[i];
 | |
|     }
 | |
| 
 | |
|     /* make sure insecure extensions are not allowed */
 | |
| 
 | |
|     for (i = 128; i < 256; i++)
 | |
|     {
 | |
| 	if (!UntrustedProcVector[i])
 | |
| 	{
 | |
| 	    UntrustedProcVector[i] = ProcBadRequest;
 | |
| 	    SwappedUntrustedProcVector[i] = ProcBadRequest;
 | |
| 	}
 | |
|     }
 | |
| 
 | |
|     SecurityLoadPropertyAccessList();
 | |
| 
 | |
| } /* SecurityExtensionInit */
 |