]> git.sesse.net Git - rdpsrv/blobdiff - Xserver/programs/Xserver/Xext/security.c
Import X server from vnc-3.3.7.
[rdpsrv] / Xserver / programs / Xserver / Xext / security.c
diff --git a/Xserver/programs/Xserver/Xext/security.c b/Xserver/programs/Xserver/Xext/security.c
new file mode 100644 (file)
index 0000000..f0153c0
--- /dev/null
@@ -0,0 +1,1992 @@
+/* $XConsortium: security.c /main/13 1996/12/15 21:24:27 rws $ */
+/*
+Copyright (c) 1996 X Consortium, Inc.
+
+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 X CONSORTIUM 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 OF
+OR OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of the X Consortium 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 X Consortium.
+*/
+/* $XFree86: xc/programs/Xserver/Xext/security.c,v 1.2 1997/01/27 06:57:17 dawes Exp $ */
+
+#include "dixstruct.h"
+#include "extnsionst.h"
+#include "windowstr.h"
+#include "inputstr.h"
+#include "gcstruct.h"
+#include "colormapst.h"
+#include "propertyst.h"
+#define _SECURITY_SERVER
+#include "securstr.h"
+#include <assert.h>
+#include <stdarg.h>
+#ifdef LBX
+#define _XLBX_SERVER_
+#include "XLbx.h"
+extern unsigned char LbxReqCode;
+#endif
+#ifdef XAPPGROUP
+#include "Xagsrv.h"
+#endif
+#include <stdio.h>  /* for file reading operations */
+#include "Xatom.h"  /* for XA_STRING */
+
+#ifndef DEFAULTPOLICYFILE
+# define DEFAULTPOLICYFILE NULL
+#endif
+#ifdef WIN32
+#include <X11/Xos.h>
+#undef index
+#endif
+
+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])(
+#if NeedNestedPrototypes
+    ClientPtr /*client*/
+#endif
+);
+int (*SwappedUntrustedProcVector[256])(
+#if NeedNestedPrototypes
+    ClientPtr /*client*/
+#endif
+);
+
+extern int ProcBadRequest();
+
+
+/* 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;
+    AuditPrefix(format);
+    va_start(args, format);
+    VErrorF(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(value, id)
+    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);
+
+    /* 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(value, id)
+    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(pAuth, seconds)
+    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(timer, time, pval)
+    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(pAuth)
+    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(client)
+    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(pAuth, client, mask)
+    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(client)
+    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 */
+    int status;                        /* return value from os functions */
+    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(client)
+    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(client)
+    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(client)
+    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(client)
+    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(client)
+    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(client)
+    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(from, to)
+    xSecurityAuthorizationRevokedEvent *from, *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(dev, ppWin, ppStopWin)
+    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;
+
+    /* 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(client, id)
+    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(client, id, rtype, access_mode, rval)
+    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(pcbl, nulldata, calldata)
+    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_INIT(pScreen, &censorRegion, NullBox, 0);
+
+    /* 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()
+{
+    while (PropertyAccessList)
+    {
+       PropertyAccessPtr freeit = PropertyAccessList;
+       PropertyAccessList = PropertyAccessList->next;
+       xfree(freeit);
+    }
+} /* SecurityFreePropertyAccessList */
+
+#ifndef __EMX__
+#define SecurityIsWhitespace(c) ( (c == ' ') || (c == '\t') || (c == '\n') )
+#else
+#define SecurityIsWhitespace(c) ( (c == ' ') || (c == '\t') || (c == '\n') || (c == '\r') )
+#endif
+
+static char *
+SecuritySkipWhitespace(p)
+    char *p;
+{
+    while (SecurityIsWhitespace(*p))
+       p++;
+    return p;
+} /* SecuritySkipWhitespace */
+
+
+static char *
+SecurityParseString(rest)
+    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(p)
+    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(p)
+    char *p;
+{
+    char *propname;
+    char c;
+    char action = SecurityDefaultAction;
+    char readAction, writeAction, destroyAction;
+    PropertyAccessPtr pacl, prev, cur;
+    ATOM atom;
+    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(p)
+    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;
+
+} /* SecurityParseSitePolicy */
+
+
+char **
+SecurityGetSitePolicyStrings(n)
+    int *n;
+{
+    *n = nSecurityPolicyStrings;
+    return SecurityPolicyStrings;
+} /* SecurityGetSitePolicyStrings */
+
+static void
+SecurityFreeSitePolicyStrings()
+{
+    if (SecurityPolicyStrings)
+    {
+       assert(nSecurityPolicyStrings);
+       while (nSecurityPolicyStrings--)
+       {
+           Xfree(SecurityPolicyStrings[nSecurityPolicyStrings]);
+       }
+       Xfree(SecurityPolicyStrings);
+       SecurityPolicyStrings = NULL;
+       nSecurityPolicyStrings = 0;
+    }
+} /* SecurityFreeSitePolicyStrings */
+
+
+static void
+SecurityLoadPropertyAccessList()
+{
+    FILE *f;
+    int lineNumber = 0;
+
+    SecurityMaxPropertyName = 0;
+
+    if (!SecurityPolicyFile)
+       return;
+
+#ifndef __EMX__
+    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(ws, cs)
+    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(extEntry)
+    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()
+{
+    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] =
+       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 */