]> git.sesse.net Git - rdpsrv/blob - Xserver/programs/Xserver/Xext/security.c
Support RDP5 logon packets.
[rdpsrv] / Xserver / programs / Xserver / Xext / security.c
1 /* $XConsortium: security.c /main/13 1996/12/15 21:24:27 rws $ */
2 /*
3 Copyright (c) 1996 X Consortium, Inc.
4
5 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
6 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
7 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
8 IN NO EVENT SHALL THE X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR
9 OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
10 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OF
11 OR OTHER DEALINGS IN THE SOFTWARE.
12
13 Except as contained in this notice, the name of the X Consortium shall
14 not be used in advertising or otherwise to promote the sale, use or
15 other dealings in this Software without prior written authorization
16 from the X Consortium.
17 */
18 /* $XFree86: xc/programs/Xserver/Xext/security.c,v 1.2 1997/01/27 06:57:17 dawes Exp $ */
19
20 #include "dixstruct.h"
21 #include "extnsionst.h"
22 #include "windowstr.h"
23 #include "inputstr.h"
24 #include "gcstruct.h"
25 #include "colormapst.h"
26 #include "propertyst.h"
27 #define _SECURITY_SERVER
28 #include "securstr.h"
29 #include <assert.h>
30 #include <stdarg.h>
31 #ifdef LBX
32 #define _XLBX_SERVER_
33 #include "XLbx.h"
34 extern unsigned char LbxReqCode;
35 #endif
36 #ifdef XAPPGROUP
37 #include "Xagsrv.h"
38 #endif
39 #include <stdio.h>  /* for file reading operations */
40 #include "Xatom.h"  /* for XA_STRING */
41
42 #ifndef DEFAULTPOLICYFILE
43 # define DEFAULTPOLICYFILE NULL
44 #endif
45 #ifdef WIN32
46 #include <X11/Xos.h>
47 #undef index
48 #endif
49
50 static int SecurityErrorBase;  /* first Security error number */
51 static int SecurityEventBase;  /* first Security event number */
52
53 CallbackListPtr SecurityValidateGroupCallback = NULL;  /* see security.h */
54
55 RESTYPE SecurityAuthorizationResType; /* resource type for authorizations */
56
57 static RESTYPE RTEventClient;
58
59 /* Proc vectors for untrusted clients, swapped and unswapped versions.
60  * These are the same as the normal proc vectors except that extensions
61  * that haven't declared themselves secure will have ProcBadRequest plugged
62  * in for their major opcode dispatcher.  This prevents untrusted clients
63  * from guessing extension major opcodes and using the extension even though
64  * the extension can't be listed or queried.
65  */
66 int (*UntrustedProcVector[256])(
67 #if NeedNestedPrototypes
68     ClientPtr /*client*/
69 #endif
70 );
71 int (*SwappedUntrustedProcVector[256])(
72 #if NeedNestedPrototypes
73     ClientPtr /*client*/
74 #endif
75 );
76
77 extern int ProcBadRequest();
78
79
80 /* SecurityAudit
81  *
82  * Arguments:
83  *      format is the formatting string to be used to interpret the
84  *        remaining arguments.
85  *
86  * Returns: nothing.
87  *
88  * Side Effects:
89  *      Writes the message to the log file if security logging is on.
90  */
91
92 void
93 SecurityAudit(char *format, ...)
94 {
95     va_list args;
96
97     if (auditTrailLevel < SECURITY_AUDIT_LEVEL)
98         return;
99     AuditPrefix(format);
100     va_start(args, format);
101     VErrorF(format, args);
102     va_end(args);
103 } /* SecurityAudit */
104
105 #define rClient(obj) (clients[CLIENT_ID((obj)->resource)])
106
107 /* SecurityDeleteAuthorization
108  *
109  * Arguments:
110  *      value is the authorization to delete.
111  *      id is its resource ID.
112  *
113  * Returns: Success.
114  *
115  * Side Effects:
116  *      Frees everything associated with the authorization.
117  */
118
119 static int
120 SecurityDeleteAuthorization(value, id)
121     pointer value;
122     XID id;
123 {
124     SecurityAuthorizationPtr pAuth = (SecurityAuthorizationPtr)value;
125     unsigned short name_len, data_len;
126     char *name, *data;
127     int status;
128     int i;
129     OtherClientsPtr pEventClient;
130
131     /* Remove the auth using the os layer auth manager */
132
133     status = AuthorizationFromID(pAuth->id, &name_len, &name,
134                                  &data_len, &data);
135     assert(status);
136     status = RemoveAuthorization(name_len, name, data_len, data);
137     assert(status);
138
139     /* free the auth timer if there is one */
140
141     if (pAuth->timer) TimerFree(pAuth->timer);
142
143     /* send revoke events */
144
145     while (pEventClient = pAuth->eventClients)
146     {
147         /* send revocation event event */
148         ClientPtr client = rClient(pEventClient);
149
150         if (!client->clientGone)
151         {
152             xSecurityAuthorizationRevokedEvent are;
153             are.type = SecurityEventBase + XSecurityAuthorizationRevoked;
154             are.sequenceNumber = client->sequence;
155             are.authId = pAuth->id;
156             WriteEventsToClient(client, 1, (xEvent *)&are);
157         }
158         FreeResource(pEventClient->resource, RT_NONE);
159     }
160
161     /* kill all clients using this auth */
162
163     for (i = 1; i<currentMaxClients; i++)
164     {
165         if (clients[i] && (clients[i]->authId == pAuth->id))
166             CloseDownClient(clients[i]);
167     }
168
169     SecurityAudit("revoked authorization ID %d\n", pAuth->id);
170     xfree(pAuth);
171     return Success;
172
173 } /* SecurityDeleteAuthorization */
174
175
176 /* resource delete function for RTEventClient */
177 static int
178 SecurityDeleteAuthorizationEventClient(value, id)
179     pointer value;
180     XID id;
181 {
182     OtherClientsPtr pEventClient, prev = NULL;
183     SecurityAuthorizationPtr pAuth = (SecurityAuthorizationPtr)value;
184
185     for (pEventClient = pAuth->eventClients;
186          pEventClient;
187          pEventClient = pEventClient->next)
188     {
189         if (pEventClient->resource == id)
190         {
191             if (prev)
192                 prev->next = pEventClient->next;
193             else
194                 pAuth->eventClients = pEventClient->next;
195             xfree(pEventClient);
196             return(Success);
197         }
198         prev = pEventClient;
199     }
200     /*NOTREACHED*/
201     return -1; /* make compiler happy */
202 } /* SecurityDeleteAuthorizationEventClient */
203
204
205 /* SecurityComputeAuthorizationTimeout
206  *
207  * Arguments:
208  *      pAuth is the authorization for which we are computing the timeout
209  *      seconds is the number of seconds we want to wait
210  *
211  * Returns:
212  *      the number of milliseconds that the auth timer should be set to
213  *
214  * Side Effects:
215  *      Sets pAuth->secondsRemaining to any "overflow" amount of time
216  *      that didn't fit in 32 bits worth of milliseconds
217  */
218
219 static CARD32
220 SecurityComputeAuthorizationTimeout(pAuth, seconds)
221     SecurityAuthorizationPtr pAuth;
222     unsigned int seconds;
223 {
224     /* maxSecs is the number of full seconds that can be expressed in
225      * 32 bits worth of milliseconds
226      */
227     CARD32 maxSecs = (CARD32)(~0) / (CARD32)MILLI_PER_SECOND;
228
229     if (seconds > maxSecs)
230     { /* only come here if we want to wait more than 49 days */
231         pAuth->secondsRemaining = seconds - maxSecs;
232         return maxSecs * MILLI_PER_SECOND;
233     }
234     else
235     { /* by far the common case */
236         pAuth->secondsRemaining = 0;
237         return seconds * MILLI_PER_SECOND;
238     }
239 } /* SecurityStartAuthorizationTimer */
240
241 /* SecurityAuthorizationExpired
242  *
243  * This function is passed as an argument to TimerSet and gets called from
244  * the timer manager in the os layer when its time is up.
245  *
246  * Arguments:
247  *      timer is the timer for this authorization.
248  *      time is the current time.
249  *      pval is the authorization whose time is up.
250  *
251  * Returns:
252  *      A new time delay in milliseconds if the timer should wait some
253  *      more, else zero.
254  *
255  * Side Effects:
256  *      Frees the authorization resource if the timeout period is really
257  *      over, otherwise recomputes pAuth->secondsRemaining.
258  */
259
260 static CARD32
261 SecurityAuthorizationExpired(timer, time, pval)
262     OsTimerPtr timer;
263     CARD32 time;
264     pointer pval;
265 {
266     SecurityAuthorizationPtr pAuth = (SecurityAuthorizationPtr)pval;
267
268     assert(pAuth->timer == timer);
269
270     if (pAuth->secondsRemaining)
271     {
272         return SecurityComputeAuthorizationTimeout(pAuth,
273                                                    pAuth->secondsRemaining);
274     }
275     else
276     {
277         FreeResource(pAuth->id, RT_NONE);
278         return 0;
279     }
280 } /* SecurityAuthorizationExpired */
281
282 /* SecurityStartAuthorizationTimer
283  *
284  * Arguments:
285  *      pAuth is the authorization whose timer should be started.
286  *
287  * Returns: nothing.
288  *
289  * Side Effects:
290  *      A timer is started, set to expire after the timeout period for
291  *      this authorization.  When it expires, the function
292  *      SecurityAuthorizationExpired will be called.
293  */
294
295 static void
296 SecurityStartAuthorizationTimer(pAuth)
297     SecurityAuthorizationPtr pAuth;
298 {
299     pAuth->timer = TimerSet(pAuth->timer, 0,
300         SecurityComputeAuthorizationTimeout(pAuth, pAuth->timeout),
301                             SecurityAuthorizationExpired, pAuth);
302 } /* SecurityStartAuthorizationTimer */
303
304
305 /* Proc functions all take a client argument, execute the request in
306  * client->requestBuffer, and return a protocol error status.
307  */
308
309 static int
310 ProcSecurityQueryVersion(client)
311     ClientPtr client;
312 {
313     REQUEST(xSecurityQueryVersionReq);
314     xSecurityQueryVersionReply  rep;
315
316     /* paranoia: this "can't happen" because this extension is hidden
317      * from untrusted clients, but just in case...
318      */
319     if (client->trustLevel != XSecurityClientTrusted)
320         return BadRequest;
321
322     REQUEST_SIZE_MATCH(xSecurityQueryVersionReq);
323     rep.type            = X_Reply;
324     rep.sequenceNumber  = client->sequence;
325     rep.length          = 0;
326     rep.majorVersion    = SECURITY_MAJOR_VERSION;
327     rep.minorVersion    = SECURITY_MINOR_VERSION;
328     if(client->swapped)
329     {
330         register char n;
331         swaps(&rep.sequenceNumber, n);
332         swaps(&rep.majorVersion, n);
333         swaps(&rep.minorVersion, n);
334     }
335     (void)WriteToClient(client, SIZEOF(xSecurityQueryVersionReply),
336                         (char *)&rep);
337     return (client->noClientException);
338 } /* ProcSecurityQueryVersion */
339
340
341 static int
342 SecurityEventSelectForAuthorization(pAuth, client, mask)
343     SecurityAuthorizationPtr pAuth;
344     ClientPtr client;
345     Mask mask;
346 {
347     OtherClients *pEventClient;
348
349     for (pEventClient = pAuth->eventClients;
350          pEventClient;
351          pEventClient = pEventClient->next)
352     {
353         if (SameClient(pEventClient, client))
354         {
355             if (mask == 0)
356                 FreeResource(pEventClient->resource, RT_NONE);
357             else
358                 pEventClient->mask = mask;
359             return Success;
360         }
361     }
362     
363     pEventClient = (OtherClients *) xalloc(sizeof(OtherClients));
364     if (!pEventClient)
365         return BadAlloc;
366     pEventClient->mask = mask;
367     pEventClient->resource = FakeClientID(client->index);
368     pEventClient->next = pAuth->eventClients;
369     if (!AddResource(pEventClient->resource, RTEventClient,
370                      (pointer)pAuth))
371     {
372         xfree(pEventClient);
373         return BadAlloc;
374     }
375     pAuth->eventClients = pEventClient;
376
377     return Success;
378 } /* SecurityEventSelectForAuthorization */
379
380
381 static int
382 ProcSecurityGenerateAuthorization(client)
383     ClientPtr client;
384 {
385     REQUEST(xSecurityGenerateAuthorizationReq);
386     int len;                    /* request length in CARD32s*/
387     Bool removeAuth = FALSE;    /* if bailout, call RemoveAuthorization? */
388     SecurityAuthorizationPtr pAuth = NULL;  /* auth we are creating */
389     int err;                    /* error to return from this function */
390     int status;                 /* return value from os functions */
391     XID authId;                 /* authorization ID assigned by os layer */
392     xSecurityGenerateAuthorizationReply rep; /* reply struct */
393     unsigned int trustLevel;    /* trust level of new auth */
394     XID group;                  /* group of new auth */
395     CARD32 timeout;             /* timeout of new auth */
396     CARD32 *values;             /* list of supplied attributes */
397     char *protoname;            /* auth proto name sent in request */
398     char *protodata;            /* auth proto data sent in request */
399     unsigned int authdata_len;  /* # bytes of generated auth data */
400     char *pAuthdata;            /* generated auth data */
401     Mask eventMask;             /* what events on this auth does client want */
402
403     /* paranoia: this "can't happen" because this extension is hidden
404      * from untrusted clients, but just in case...
405      */
406     if (client->trustLevel != XSecurityClientTrusted)
407         return BadRequest;
408
409     /* check request length */
410
411     REQUEST_AT_LEAST_SIZE(xSecurityGenerateAuthorizationReq);
412     len = SIZEOF(xSecurityGenerateAuthorizationReq) >> 2;
413     len += (stuff->nbytesAuthProto + (unsigned)3) >> 2;
414     len += (stuff->nbytesAuthData  + (unsigned)3) >> 2;
415     values = ((CARD32 *)stuff) + len;
416     len += Ones(stuff->valueMask);
417     if (client->req_len != len)
418         return BadLength;
419
420     /* check valuemask */
421     if (stuff->valueMask & ~XSecurityAllAuthorizationAttributes)
422     {
423         client->errorValue = stuff->valueMask;
424         return BadValue;
425     }
426
427     /* check timeout */
428     timeout = 60;
429     if (stuff->valueMask & XSecurityTimeout)
430     {
431         timeout = *values++;
432     }
433
434     /* check trustLevel */
435     trustLevel = XSecurityClientUntrusted;
436     if (stuff->valueMask & XSecurityTrustLevel)
437     {
438         trustLevel = *values++;
439         if (trustLevel != XSecurityClientTrusted &&
440             trustLevel != XSecurityClientUntrusted)
441         {
442             client->errorValue = trustLevel;
443             return BadValue;
444         }
445     }
446
447     /* check group */
448     group = None;
449     if (stuff->valueMask & XSecurityGroup)
450     {
451         group = *values++;
452         if (SecurityValidateGroupCallback)
453         {
454             SecurityValidateGroupInfoRec vgi;
455             vgi.group = group;
456             vgi.valid = FALSE;
457             CallCallbacks(&SecurityValidateGroupCallback, (pointer)&vgi);
458
459             /* if nobody said they recognized it, it's an error */
460
461             if (!vgi.valid)
462             {
463                 client->errorValue = group;
464                 return BadValue;
465             }
466         }
467     }
468
469     /* check event mask */
470     eventMask = 0;
471     if (stuff->valueMask & XSecurityEventMask)
472     {
473         eventMask = *values++;
474         if (eventMask & ~XSecurityAllEventMasks)
475         {
476             client->errorValue = eventMask;
477             return BadValue;
478         }
479     }
480
481     protoname = (char *)&stuff[1];
482     protodata = protoname + ((stuff->nbytesAuthProto + (unsigned)3) >> 2);
483
484     /* call os layer to generate the authorization */
485
486     authId = GenerateAuthorization(stuff->nbytesAuthProto, protoname,
487                                    stuff->nbytesAuthData,  protodata,
488                                    &authdata_len, &pAuthdata);
489     if ((XID) ~0L == authId)
490     {
491         err = SecurityErrorBase + XSecurityBadAuthorizationProtocol;
492         goto bailout;
493     }
494
495     /* now that we've added the auth, remember to remove it if we have to
496      * abort the request for some reason (like allocation failure)
497      */
498     removeAuth = TRUE;
499
500     /* associate additional information with this auth ID */
501
502     pAuth = (SecurityAuthorizationPtr)xalloc(sizeof(SecurityAuthorizationRec));
503     if (!pAuth)
504     {
505         err = BadAlloc;
506         goto bailout;
507     }
508
509     /* fill in the auth fields */
510
511     pAuth->id = authId;
512     pAuth->timeout = timeout;
513     pAuth->group = group;
514     pAuth->trustLevel = trustLevel;
515     pAuth->refcnt = 0;  /* the auth was just created; nobody's using it yet */
516     pAuth->secondsRemaining = 0;
517     pAuth->timer = NULL;
518     pAuth->eventClients = NULL;
519
520     /* handle event selection */
521     if (eventMask)
522     {
523         err = SecurityEventSelectForAuthorization(pAuth, client, eventMask);
524         if (err != Success)
525             goto bailout;
526     }
527
528     if (!AddResource(authId, SecurityAuthorizationResType, pAuth))
529     {
530         err = BadAlloc;
531         goto bailout;
532     }
533
534     /* start the timer ticking */
535
536     if (pAuth->timeout != 0)
537         SecurityStartAuthorizationTimer(pAuth);
538
539     /* tell client the auth id and data */
540
541     rep.type = X_Reply;
542     rep.length = (authdata_len + 3) >> 2;
543     rep.sequenceNumber = client->sequence;
544     rep.authId = authId;
545     rep.dataLength = authdata_len;
546
547     if (client->swapped)
548     {
549         register char n;
550         swapl(&rep.length, n);
551         swaps(&rep.sequenceNumber, n);
552         swapl(&rep.authId, n);
553         swaps(&rep.dataLength, n);
554     }
555
556     WriteToClient(client, SIZEOF(xSecurityGenerateAuthorizationReply),
557                   (char *)&rep);
558     WriteToClient(client, authdata_len, pAuthdata);
559
560     SecurityAudit("client %d generated authorization %d trust %d timeout %d group %d events %d\n",
561                   client->index, pAuth->id, pAuth->trustLevel, pAuth->timeout,
562                   pAuth->group, eventMask);
563
564     /* the request succeeded; don't call RemoveAuthorization or free pAuth */
565
566     removeAuth = FALSE;
567     pAuth = NULL;
568     err = client->noClientException;
569
570 bailout:
571     if (removeAuth)
572         RemoveAuthorization(stuff->nbytesAuthProto, protoname,
573                             authdata_len, pAuthdata);
574     if (pAuth) xfree(pAuth);
575     return err;
576
577 } /* ProcSecurityGenerateAuthorization */
578
579 static int
580 ProcSecurityRevokeAuthorization(client)
581     ClientPtr client;
582 {
583     REQUEST(xSecurityRevokeAuthorizationReq);
584     SecurityAuthorizationPtr pAuth;
585
586     /* paranoia: this "can't happen" because this extension is hidden
587      * from untrusted clients, but just in case...
588      */
589     if (client->trustLevel != XSecurityClientTrusted)
590         return BadRequest;
591
592     REQUEST_SIZE_MATCH(xSecurityRevokeAuthorizationReq);
593
594     pAuth = (SecurityAuthorizationPtr)SecurityLookupIDByType(client,
595         stuff->authId, SecurityAuthorizationResType, SecurityDestroyAccess);
596     if (!pAuth)
597         return SecurityErrorBase + XSecurityBadAuthorization;
598
599     FreeResource(stuff->authId, RT_NONE);
600     return Success;
601 } /* ProcSecurityRevokeAuthorization */
602
603
604 static int
605 ProcSecurityDispatch(client)
606     ClientPtr client;
607 {
608     REQUEST(xReq);
609
610     switch (stuff->data)
611     {
612         case X_SecurityQueryVersion:
613             return ProcSecurityQueryVersion(client);
614         case X_SecurityGenerateAuthorization:
615             return ProcSecurityGenerateAuthorization(client);
616         case X_SecurityRevokeAuthorization:
617             return ProcSecurityRevokeAuthorization(client);
618         default:
619             return BadRequest;
620     }
621 } /* ProcSecurityDispatch */
622
623 static int
624 SProcSecurityQueryVersion(client)
625     ClientPtr client;
626 {
627     REQUEST(xSecurityQueryVersionReq);
628     register char       n;
629
630     swaps(&stuff->length, n);
631     REQUEST_SIZE_MATCH(xSecurityQueryVersionReq);
632     swaps(&stuff->majorVersion, n);
633     swaps(&stuff->minorVersion,n);
634     return ProcSecurityQueryVersion(client);
635 } /* SProcSecurityQueryVersion */
636
637
638 static int
639 SProcSecurityGenerateAuthorization(client)
640     ClientPtr client;
641 {
642     REQUEST(xSecurityGenerateAuthorizationReq);
643     register char       n;
644     CARD32 *values;
645     unsigned long nvalues;
646
647     swaps(&stuff->length, n);
648     REQUEST_AT_LEAST_SIZE(xSecurityGenerateAuthorizationReq);
649     swaps(&stuff->nbytesAuthProto, n);
650     swaps(&stuff->nbytesAuthData, n);
651     swapl(&stuff->valueMask, n);
652     values = (CARD32 *)(&stuff[1]) +
653         ((stuff->nbytesAuthProto + (unsigned)3) >> 2) +
654         ((stuff->nbytesAuthData + (unsigned)3) >> 2);
655     nvalues = (((CARD32 *)stuff) + stuff->length) - values;
656     SwapLongs(values, nvalues);
657     return ProcSecurityGenerateAuthorization(client);
658 } /* SProcSecurityGenerateAuthorization */
659
660
661 static int
662 SProcSecurityRevokeAuthorization(client)
663     ClientPtr client;
664 {
665     REQUEST(xSecurityRevokeAuthorizationReq);
666     register char       n;
667
668     swaps(&stuff->length, n);
669     REQUEST_SIZE_MATCH(xSecurityRevokeAuthorizationReq);
670     swapl(&stuff->authId, n);
671     return ProcSecurityRevokeAuthorization(client);
672 } /* SProcSecurityRevokeAuthorization */
673
674
675 static int
676 SProcSecurityDispatch(client)
677     ClientPtr client;
678 {
679     REQUEST(xReq);
680
681     switch (stuff->data)
682     {
683         case X_SecurityQueryVersion:
684             return SProcSecurityQueryVersion(client);
685         case X_SecurityGenerateAuthorization:
686             return SProcSecurityGenerateAuthorization(client);
687         case X_SecurityRevokeAuthorization:
688             return SProcSecurityRevokeAuthorization(client);
689         default:
690             return BadRequest;
691     }
692 } /* SProcSecurityDispatch */
693
694 static void 
695 SwapSecurityAuthorizationRevokedEvent(from, to)
696     xSecurityAuthorizationRevokedEvent *from, *to;
697 {
698     to->type = from->type;
699     to->detail = from->detail;
700     cpswaps(from->sequenceNumber, to->sequenceNumber);
701     cpswapl(from->authId, to->authId);
702 }
703
704 /* SecurityDetermineEventPropogationLimits
705  *
706  * This is a helper function for SecurityCheckDeviceAccess.
707  *
708  * Arguments:
709  *      dev is the device for which the starting and stopping windows for
710  *      event propogation should be determined.
711  *      The values pointed to by ppWin and ppStopWin are not used.
712  *
713  * Returns:
714  *      ppWin is filled in with a pointer to the window at which event
715  *      propogation for the given device should start given the current
716  *      state of the server (pointer position, window layout, etc.)
717  *      ppStopWin is filled in with the window at which event propogation
718  *      should stop; events should not go to ppStopWin.
719  *
720  * Side Effects: none.
721  */
722
723 static void
724 SecurityDetermineEventPropogationLimits(dev, ppWin, ppStopWin)
725     DeviceIntPtr dev;
726     WindowPtr *ppWin;
727     WindowPtr *ppStopWin;
728 {
729     WindowPtr pFocusWin = dev->focus ? dev->focus->win : NoneWin;
730
731     if (pFocusWin == NoneWin)
732     { /* no focus -- events don't go anywhere */
733         *ppWin = *ppStopWin = NULL;
734         return;
735     }
736
737     if (pFocusWin == PointerRootWin)
738     { /* focus follows the pointer */
739         *ppWin = GetSpriteWindow();
740         *ppStopWin = NULL; /* propogate all the way to the root */
741     }
742     else
743     { /* a real window is set for the focus */
744         WindowPtr pSpriteWin = GetSpriteWindow();
745         *ppStopWin = pFocusWin->parent; /* don't go past the focus window */
746
747         /* if the pointer is in a subwindow of the focus window, start
748          * at that subwindow, else start at the focus window itself
749          */
750         if (IsParent(pFocusWin, pSpriteWin))
751              *ppWin = pSpriteWin;
752         else *ppWin = pFocusWin;
753     }
754 } /* SecurityDetermineEventPropogationLimits */
755
756
757 /* SecurityCheckDeviceAccess
758  *
759  * Arguments:
760  *      client is the client attempting to access a device.
761  *      dev is the device being accessed.
762  *      fromRequest is TRUE if the device access is a direct result of
763  *        the client executing some request and FALSE if it is a
764  *        result of the server trying to send an event (e.g. KeymapNotify)
765  *        to the client.
766  * Returns:
767  *      TRUE if the device access should be allowed, else FALSE.
768  *
769  * Side Effects:
770  *      An audit message is generated if access is denied.
771  */
772
773 Bool
774 SecurityCheckDeviceAccess(client, dev, fromRequest)
775     ClientPtr client;
776     DeviceIntPtr dev;
777     Bool fromRequest;
778 {
779     WindowPtr pWin, pStopWin;
780     Bool untrusted_got_event;
781     Bool found_event_window;
782     Mask eventmask;
783     int reqtype;
784
785     /* trusted clients always allowed to do anything */
786     if (client->trustLevel == XSecurityClientTrusted)
787         return TRUE;
788
789     /* device security other than keyboard is not implemented yet */
790     if (dev != inputInfo.keyboard)
791         return TRUE;
792
793     /* some untrusted client wants access */
794
795     if (fromRequest)
796     {
797         reqtype = ((xReq *)client->requestBuffer)->reqType;
798         switch (reqtype)
799         {
800             /* never allow these */
801             case X_ChangeKeyboardMapping:
802             case X_ChangeKeyboardControl:
803             case X_SetModifierMapping:
804                 SecurityAudit("client %d attempted request %d\n",
805                               client->index, reqtype);
806                 return FALSE;
807             default:
808                 break;
809         }
810     }
811
812     untrusted_got_event = FALSE;
813     found_event_window = FALSE;
814
815     if (dev->grab)
816     {
817         untrusted_got_event =
818             ((rClient(dev->grab))->trustLevel != XSecurityClientTrusted);
819     }
820     else
821     {
822         SecurityDetermineEventPropogationLimits(dev, &pWin, &pStopWin);
823
824         eventmask = KeyPressMask | KeyReleaseMask;
825         while ( (pWin != pStopWin) && !found_event_window)
826         {
827             OtherClients *other;
828
829             if (pWin->eventMask & eventmask)
830             {
831                 found_event_window = TRUE;
832                 client = wClient(pWin);
833                 if (client->trustLevel != XSecurityClientTrusted)
834                 {
835                     untrusted_got_event = TRUE;
836                 }
837             }
838             if (wOtherEventMasks(pWin) & eventmask)
839             {
840                 found_event_window = TRUE;
841                 for (other = wOtherClients(pWin); other; other = other->next)
842                 {
843                     if (other->mask & eventmask)
844                     {
845                         client = rClient(other);
846                         if (client->trustLevel != XSecurityClientTrusted)
847                         {
848                             untrusted_got_event = TRUE;
849                             break;
850                         }
851                     }
852                 }
853             }
854             if (wDontPropagateMask(pWin) & eventmask)
855                 break;
856             pWin = pWin->parent;
857         } /* while propogating the event */
858     }
859
860     /* allow access by untrusted clients only if an event would have gone 
861      * to an untrusted client
862      */
863     
864     if (!untrusted_got_event)
865     {
866         char *devname = dev->name;
867         if (!devname) devname = "unnamed";
868         if (fromRequest)
869             SecurityAudit("client %d attempted request %d device %d (%s)\n",
870                           client->index, reqtype, dev->id, devname);
871         else
872             SecurityAudit("client %d attempted to access device %d (%s)\n",
873                           client->index, dev->id, devname);
874     }
875     return untrusted_got_event;
876 } /* SecurityCheckDeviceAccess */
877
878
879
880 /* SecurityAuditResourceIDAccess
881  *
882  * Arguments:
883  *      client is the client doing the resource access.
884  *      id is the resource id.
885  *
886  * Returns: NULL
887  *
888  * Side Effects:
889  *      An audit message is generated with details of the denied
890  *      resource access.
891  */
892
893 static pointer
894 SecurityAuditResourceIDAccess(client, id)
895     ClientPtr client;
896     XID id;
897 {
898     int cid = CLIENT_ID(id);
899     int reqtype = ((xReq *)client->requestBuffer)->reqType;
900     switch (reqtype)
901     {
902         case X_ChangeProperty:
903         case X_DeleteProperty:
904         case X_GetProperty:
905         {
906             xChangePropertyReq *req =
907                 (xChangePropertyReq *)client->requestBuffer;
908             int propertyatom = req->property;
909             char *propertyname = NameForAtom(propertyatom);
910
911             SecurityAudit("client %d attempted request %d with window 0x%x property %s of client %d\n",
912                    client->index, reqtype, id, propertyname, cid);
913             break;
914         }
915         default:
916         {
917             SecurityAudit("client %d attempted request %d with resource 0x%x of client %d\n",
918                    client->index, reqtype, id, cid);
919             break;
920         }   
921     }
922     return NULL;
923 } /* SecurityAuditResourceIDAccess */
924
925
926 /* SecurityCheckResourceIDAccess
927  *
928  * This function gets plugged into client->CheckAccess and is called from
929  * SecurityLookupIDByType/Class to determine if the client can access the
930  * resource.
931  *
932  * Arguments:
933  *      client is the client doing the resource access.
934  *      id is the resource id.
935  *      rtype is its type or class.
936  *      access_mode represents the intended use of the resource; see
937  *        resource.h.
938  *      rval is a pointer to the resource structure for this resource.
939  *
940  * Returns:
941  *      If access is granted, the value of rval that was passed in, else NULL.
942  *
943  * Side Effects:
944  *      Disallowed resource accesses are audited.
945  */
946
947 static pointer
948 SecurityCheckResourceIDAccess(client, id, rtype, access_mode, rval)
949     ClientPtr client;
950     XID id;
951     RESTYPE rtype;
952     Mask access_mode;
953     pointer rval;
954 {
955     int cid = CLIENT_ID(id);
956     int reqtype = ((xReq *)client->requestBuffer)->reqType;
957
958     if (SecurityUnknownAccess == access_mode)
959         return rval;  /* for compatibility, we have to allow access */
960
961     switch (reqtype)
962     { /* these are always allowed */
963         case X_QueryTree:
964         case X_TranslateCoords:
965         case X_GetGeometry:
966         /* property access is controlled in SecurityCheckPropertyAccess */
967         case X_GetProperty:
968         case X_ChangeProperty:
969         case X_DeleteProperty:
970         case X_RotateProperties:
971         case X_ListProperties:
972             return rval;
973         default:
974             break;
975     }
976
977     if (cid != 0)
978     { /* not a server-owned resource */
979      /*
980       * The following 'if' restricts clients to only access resources at
981       * the same trustLevel.  Since there are currently only two trust levels,
982       * and trusted clients never call this function, this degenerates into
983       * saying that untrusted clients can only access resources of other
984       * untrusted clients.  One way to add the notion of groups would be to
985       * allow values other than Trusted (0) and Untrusted (1) for this field.
986       * Clients at the same trust level would be able to use each other's
987       * resources, but not those of clients at other trust levels.  I haven't
988       * tried it, but this probably mostly works already.  The obvious
989       * competing alternative for grouping clients for security purposes is to
990       * use app groups.  dpw
991       */
992         if (client->trustLevel == clients[cid]->trustLevel
993 #ifdef XAPPGROUP
994             || (RT_COLORMAP == rtype && 
995                 XagDefaultColormap (client) == (Colormap) id)
996 #endif
997         )
998             return rval;
999         else
1000             return SecurityAuditResourceIDAccess(client, id);
1001     }
1002     else /* server-owned resource - probably a default colormap or root window */
1003     {
1004         if (RT_WINDOW == rtype || RC_DRAWABLE == rtype)
1005         {
1006             switch (reqtype)
1007             {   /* the following operations are allowed on root windows */
1008                 case X_CreatePixmap:
1009                 case X_CreateGC:
1010                 case X_CreateWindow:
1011                 case X_CreateColormap:
1012                 case X_ListProperties:
1013                 case X_GrabPointer:
1014                 case X_UngrabButton:
1015                 case X_QueryBestSize:
1016                 case X_GetWindowAttributes:
1017                     break;
1018                 case X_SendEvent:
1019                 { /* see if it is an event specified by the ICCCM */
1020                     xSendEventReq *req = (xSendEventReq *)
1021                                                 (client->requestBuffer);
1022                     if (req->propagate == xTrue
1023                         ||
1024                           (req->eventMask != ColormapChangeMask &&
1025                            req->eventMask != StructureNotifyMask &&
1026                            req->eventMask !=
1027                               (SubstructureRedirectMask|SubstructureNotifyMask)
1028                           )
1029                         ||
1030                           (req->event.u.u.type != UnmapNotify &&
1031                            req->event.u.u.type != ConfigureRequest &&
1032                            req->event.u.u.type != ClientMessage
1033                           )
1034                        )
1035                     { /* not an ICCCM event */
1036                         return SecurityAuditResourceIDAccess(client, id);
1037                     }
1038                     break;
1039                 } /* case X_SendEvent on root */
1040
1041                 case X_ChangeWindowAttributes:
1042                 { /* Allow selection of PropertyNotify and StructureNotify
1043                    * events on the root.
1044                    */
1045                     xChangeWindowAttributesReq *req =
1046                         (xChangeWindowAttributesReq *)(client->requestBuffer);
1047                     if (req->valueMask == CWEventMask)
1048                     {
1049                         CARD32 value = *((CARD32 *)(req + 1));
1050                         if ( (value &
1051                               ~(PropertyChangeMask|StructureNotifyMask)) == 0)
1052                             break;
1053                     }
1054                     return SecurityAuditResourceIDAccess(client, id);
1055                 } /* case X_ChangeWindowAttributes on root */
1056
1057                 default:
1058                 {
1059 #ifdef LBX
1060                     /* XXX really need per extension dispatching */
1061                     if (reqtype == LbxReqCode) {
1062                         switch (((xReq *)client->requestBuffer)->data) {
1063                         case X_LbxGetProperty:
1064                         case X_LbxChangeProperty:
1065                             return rval;
1066                         default:
1067                             break;
1068                         }
1069                     }
1070 #endif
1071                     /* others not allowed */
1072                     return SecurityAuditResourceIDAccess(client, id);
1073                 }
1074             }
1075         } /* end server-owned window or drawable */
1076         else if (SecurityAuthorizationResType == rtype)
1077         {
1078             SecurityAuthorizationPtr pAuth = (SecurityAuthorizationPtr)rval;
1079             if (pAuth->trustLevel != client->trustLevel)
1080                 return SecurityAuditResourceIDAccess(client, id);
1081         }
1082         else if (RT_COLORMAP != rtype)
1083         { /* don't allow anything else besides colormaps */
1084             return SecurityAuditResourceIDAccess(client, id);
1085         }
1086     }
1087     return rval;
1088 } /* SecurityCheckResourceIDAccess */
1089
1090
1091 /* SecurityClientStateCallback
1092  *
1093  * Arguments:
1094  *      pcbl is &ClientStateCallback.
1095  *      nullata is NULL.
1096  *      calldata is a pointer to a NewClientInfoRec (include/dixstruct.h)
1097  *      which contains information about client state changes.
1098  *
1099  * Returns: nothing.
1100  *
1101  * Side Effects:
1102  * 
1103  * If a new client is connecting, its authorization ID is copied to
1104  * client->authID.  If this is a generated authorization, its reference
1105  * count is bumped, its timer is cancelled if it was running, and its
1106  * trustlevel is copied to client->trustLevel.
1107  * 
1108  * If a client is disconnecting and the client was using a generated
1109  * authorization, the authorization's reference count is decremented, and
1110  * if it is now zero, the timer for this authorization is started.
1111  */
1112
1113 static void
1114 SecurityClientStateCallback(pcbl, nulldata, calldata)
1115     CallbackListPtr *pcbl;
1116     pointer nulldata;
1117     pointer calldata;
1118 {
1119     NewClientInfoRec *pci = (NewClientInfoRec *)calldata;
1120     ClientPtr client = pci->client;
1121
1122     switch (client->clientState)
1123     {
1124         case ClientStateRunning:
1125         { 
1126             XID authId = AuthorizationIDOfClient(client);
1127             SecurityAuthorizationPtr pAuth;
1128
1129             client->authId = authId;
1130             pAuth = (SecurityAuthorizationPtr)LookupIDByType(authId,
1131                                                 SecurityAuthorizationResType);
1132             if (pAuth)
1133             { /* it is a generated authorization */
1134                 pAuth->refcnt++;
1135                 if (pAuth->refcnt == 1)
1136                 {
1137                     if (pAuth->timer) TimerCancel(pAuth->timer);
1138                 }
1139                 client->trustLevel = pAuth->trustLevel;
1140                 if (client->trustLevel != XSecurityClientTrusted)
1141                 {
1142                     client->CheckAccess = SecurityCheckResourceIDAccess;
1143                     client->requestVector = client->swapped ?
1144                         SwappedUntrustedProcVector : UntrustedProcVector;
1145                 }
1146             }
1147             break;
1148         }
1149         case ClientStateGone:
1150         case ClientStateRetained: /* client disconnected */
1151         {
1152             XID authId = client->authId;
1153             SecurityAuthorizationPtr pAuth;
1154
1155             pAuth = (SecurityAuthorizationPtr)LookupIDByType(authId,
1156                                                 SecurityAuthorizationResType);
1157             if (pAuth)
1158             { /* it is a generated authorization */
1159                 pAuth->refcnt--;
1160                 if (pAuth->refcnt == 0)
1161                 {
1162                     SecurityStartAuthorizationTimer(pAuth);
1163                 }
1164             }       
1165             break;
1166         }
1167         default: break; 
1168     }
1169 } /* SecurityClientStateCallback */
1170
1171 #ifdef LBX
1172 Bool
1173 SecuritySameLevel(client, authId)
1174     ClientPtr client;
1175     XID authId;
1176 {
1177     SecurityAuthorizationPtr pAuth;
1178
1179     pAuth = (SecurityAuthorizationPtr)LookupIDByType(authId,
1180                                                 SecurityAuthorizationResType);
1181     if (pAuth)
1182         return client->trustLevel == pAuth->trustLevel;
1183     return client->trustLevel == XSecurityClientTrusted;
1184 }
1185 #endif
1186
1187 /* SecurityCensorImage
1188  *
1189  * Called after pScreen->GetImage to prevent pieces or trusted windows from
1190  * being returned in image data from an untrusted window.
1191  *
1192  * Arguments:
1193  *      client is the client doing the GetImage.
1194  *      pVisibleRegion is the visible region of the window.
1195  *      widthBytesLine is the width in bytes of one horizontal line in pBuf.
1196  *      pDraw is the source window.
1197  *      x, y, w, h is the rectangle of image data from pDraw in pBuf.
1198  *      format is the format of the image data in pBuf: ZPixmap or XYPixmap.
1199  *      pBuf is the image data.
1200  *
1201  * Returns: nothing.
1202  *
1203  * Side Effects:
1204  *      Any part of the rectangle (x, y, w, h) that is outside the visible
1205  *      region of the window will be destroyed (overwritten) in pBuf.
1206  */
1207 void
1208 SecurityCensorImage(client, pVisibleRegion, widthBytesLine, pDraw, x, y, w, h,
1209                     format, pBuf)
1210     ClientPtr client;
1211     RegionPtr pVisibleRegion;
1212     long widthBytesLine;
1213     DrawablePtr pDraw;
1214     int x, y, w, h;
1215     unsigned int format;
1216     char * pBuf;
1217 {
1218     RegionRec imageRegion;  /* region representing x,y,w,h */
1219     RegionRec censorRegion; /* region to obliterate */
1220     BoxRec imageBox;
1221     int nRects;
1222
1223     imageBox.x1 = x;
1224     imageBox.y1 = y;
1225     imageBox.x2 = x + w;
1226     imageBox.y2 = y + h;
1227     REGION_INIT(pScreen, &imageRegion, &imageBox, 1);
1228     REGION_INIT(pScreen, &censorRegion, NullBox, 0);
1229
1230     /* censorRegion = imageRegion - visibleRegion */
1231     REGION_SUBTRACT(pScreen, &censorRegion, &imageRegion, pVisibleRegion);
1232     nRects = REGION_NUM_RECTS(&censorRegion);
1233     if (nRects > 0)
1234     { /* we have something to censor */
1235         GCPtr pScratchGC = NULL;
1236         PixmapPtr pPix = NULL;
1237         xRectangle *pRects = NULL;
1238         Bool failed = FALSE;
1239         int depth = 1;
1240         int bitsPerPixel = 1;
1241         int i;
1242         BoxPtr pBox;
1243
1244         /* convert region to list-of-rectangles for PolyFillRect */
1245
1246         pRects = (xRectangle *)ALLOCATE_LOCAL(nRects * sizeof(xRectangle *));
1247         if (!pRects)
1248         {
1249             failed = TRUE;
1250             goto failSafe;
1251         }
1252         for (pBox = REGION_RECTS(&censorRegion), i = 0;
1253              i < nRects;
1254              i++, pBox++)
1255         {
1256             pRects[i].x = pBox->x1;
1257             pRects[i].y = pBox->y1 - imageBox.y1;
1258             pRects[i].width  = pBox->x2 - pBox->x1;
1259             pRects[i].height = pBox->y2 - pBox->y1;
1260         }
1261
1262         /* use pBuf as a fake pixmap */
1263
1264         if (format == ZPixmap)
1265         {
1266             depth = pDraw->depth;
1267             bitsPerPixel = pDraw->bitsPerPixel;
1268         }
1269
1270         pPix = GetScratchPixmapHeader(pDraw->pScreen, w, h,
1271                     depth, bitsPerPixel,
1272                     widthBytesLine, (pointer)pBuf);
1273         if (!pPix)
1274         {
1275             failed = TRUE;
1276             goto failSafe;
1277         }
1278
1279         pScratchGC = GetScratchGC(depth, pPix->drawable.pScreen);
1280         if (!pScratchGC)
1281         {
1282             failed = TRUE;
1283             goto failSafe;
1284         }
1285
1286         ValidateGC(&pPix->drawable, pScratchGC);
1287         (* pScratchGC->ops->PolyFillRect)(&pPix->drawable,
1288                             pScratchGC, nRects, pRects);
1289
1290     failSafe:
1291         if (failed)
1292         {
1293             /* Censoring was not completed above.  To be safe, wipe out
1294              * all the image data so that nothing trusted gets out.
1295              */
1296             bzero(pBuf, (int)(widthBytesLine * h));
1297         }
1298         if (pRects)     DEALLOCATE_LOCAL(pRects);
1299         if (pScratchGC) FreeScratchGC(pScratchGC);
1300         if (pPix)       FreeScratchPixmapHeader(pPix);
1301     }
1302     REGION_UNINIT(pScreen, &imageRegion);
1303     REGION_UNINIT(pScreen, &censorRegion);
1304 } /* SecurityCensorImage */
1305
1306 /**********************************************************************/
1307
1308 typedef struct _PropertyAccessRec {
1309     ATOM name;
1310     ATOM mustHaveProperty;
1311     char *mustHaveValue;
1312     char windowRestriction;
1313 #define SecurityAnyWindow          0
1314 #define SecurityRootWindow         1
1315 #define SecurityWindowWithProperty 2
1316     char readAction;
1317     char writeAction;
1318     char destroyAction;
1319     struct _PropertyAccessRec *next;
1320 } PropertyAccessRec, *PropertyAccessPtr;
1321
1322 static PropertyAccessPtr PropertyAccessList = NULL;
1323 static char SecurityDefaultAction = SecurityErrorOperation;
1324 static char *SecurityPolicyFile = DEFAULTPOLICYFILE;
1325 static ATOM SecurityMaxPropertyName = 0;
1326
1327 static char *SecurityKeywords[] = {
1328 #define SecurityKeywordComment 0
1329     "#",
1330 #define SecurityKeywordProperty 1
1331     "property",
1332 #define SecurityKeywordSitePolicy 2
1333     "sitepolicy",
1334 #define SecurityKeywordRoot 3
1335     "root",
1336 #define SecurityKeywordAny 4
1337     "any"
1338 };
1339
1340 #define NUMKEYWORDS (sizeof(SecurityKeywords) / sizeof(char *))
1341
1342 #undef PROPDEBUG
1343 /*#define PROPDEBUG  1*/
1344
1345 static void
1346 SecurityFreePropertyAccessList()
1347 {
1348     while (PropertyAccessList)
1349     {
1350         PropertyAccessPtr freeit = PropertyAccessList;
1351         PropertyAccessList = PropertyAccessList->next;
1352         xfree(freeit);
1353     }
1354 } /* SecurityFreePropertyAccessList */
1355
1356 #ifndef __EMX__
1357 #define SecurityIsWhitespace(c) ( (c == ' ') || (c == '\t') || (c == '\n') )
1358 #else
1359 #define SecurityIsWhitespace(c) ( (c == ' ') || (c == '\t') || (c == '\n') || (c == '\r') )
1360 #endif
1361
1362 static char *
1363 SecuritySkipWhitespace(p)
1364     char *p;
1365 {
1366     while (SecurityIsWhitespace(*p))
1367         p++;
1368     return p;
1369 } /* SecuritySkipWhitespace */
1370
1371
1372 static char *
1373 SecurityParseString(rest)
1374     char **rest;
1375 {
1376     char *startOfString;
1377     char *s = *rest;
1378     char endChar = 0;
1379
1380     s = SecuritySkipWhitespace(s);
1381
1382     if (*s == '"' || *s == '\'')
1383     {
1384         endChar = *s++;
1385         startOfString = s;
1386         while (*s && (*s != endChar))
1387             s++;
1388     }
1389     else
1390     {
1391         startOfString = s;
1392         while (*s && !SecurityIsWhitespace(*s))
1393             s++;
1394     }
1395     if (*s)
1396     {
1397         *s = '\0';
1398         *rest = s + 1;
1399         return startOfString;
1400     }
1401     else
1402     {
1403         *rest = s;
1404         return (endChar) ? NULL : startOfString;
1405     }
1406 } /* SecurityParseString */
1407
1408
1409 static int
1410 SecurityParseKeyword(p)
1411     char **p;
1412 {
1413     int i;
1414     char *s = *p;
1415     s = SecuritySkipWhitespace(s);
1416     for (i = 0; i < NUMKEYWORDS; i++)
1417     {
1418         int len = strlen(SecurityKeywords[i]);
1419         if (strncmp(s, SecurityKeywords[i], len) == 0)
1420         {
1421             *p = s + len;
1422             return (i);
1423         }
1424     }
1425     *p = s;
1426     return -1;
1427 } /* SecurityParseKeyword */
1428
1429
1430 static Bool
1431 SecurityParsePropertyAccessRule(p)
1432     char *p;
1433 {
1434     char *propname;
1435     char c;
1436     char action = SecurityDefaultAction;
1437     char readAction, writeAction, destroyAction;
1438     PropertyAccessPtr pacl, prev, cur;
1439     ATOM atom;
1440     char *mustHaveProperty = NULL;
1441     char *mustHaveValue = NULL;
1442     Bool invalid;
1443     char windowRestriction;
1444     int size;
1445     int keyword;
1446
1447     /* get property name */
1448     propname = SecurityParseString(&p);
1449     if (!propname || (strlen(propname) == 0))
1450         return FALSE;
1451
1452     /* get window on which property must reside for rule to apply */
1453
1454     keyword = SecurityParseKeyword(&p);
1455     if (keyword == SecurityKeywordRoot)
1456         windowRestriction = SecurityRootWindow;
1457     else if (keyword == SecurityKeywordAny) 
1458         windowRestriction = SecurityAnyWindow;
1459     else /* not root or any, must be a property name */
1460     {
1461         mustHaveProperty = SecurityParseString(&p);
1462         if (!mustHaveProperty || (strlen(mustHaveProperty) == 0))
1463             return FALSE;
1464         windowRestriction = SecurityWindowWithProperty;
1465         p = SecuritySkipWhitespace(p);
1466         if (*p == '=')
1467         { /* property value is specified too */
1468             p++; /* skip over '=' */
1469             mustHaveValue = SecurityParseString(&p);
1470             if (!mustHaveValue)
1471                 return FALSE;
1472         }
1473     }
1474
1475     /* get operations and actions */
1476
1477     invalid = FALSE;
1478     readAction = writeAction = destroyAction = SecurityDefaultAction;
1479     while ( (c = *p++) && !invalid)
1480     {
1481         switch (c)
1482         {
1483             case 'i': action = SecurityIgnoreOperation; break;
1484             case 'a': action = SecurityAllowOperation;  break;
1485             case 'e': action = SecurityErrorOperation;  break;
1486
1487             case 'r': readAction    = action; break;
1488             case 'w': writeAction   = action; break;
1489             case 'd': destroyAction = action; break;
1490
1491             default :
1492                 if (!SecurityIsWhitespace(c))
1493                     invalid = TRUE;
1494             break;
1495         }
1496     }
1497     if (invalid)
1498         return FALSE;
1499
1500     /* We've successfully collected all the information needed for this
1501      * property access rule.  Now record it in a PropertyAccessRec.
1502      */
1503     size = sizeof(PropertyAccessRec);
1504
1505     /* If there is a property value string, allocate space for it 
1506      * right after the PropertyAccessRec.
1507      */
1508     if (mustHaveValue)
1509         size += strlen(mustHaveValue) + 1;
1510     pacl = (PropertyAccessPtr)Xalloc(size);
1511     if (!pacl)
1512         return FALSE;
1513
1514     pacl->name = MakeAtom(propname, strlen(propname), TRUE);
1515     if (pacl->name == BAD_RESOURCE)
1516     {
1517         Xfree(pacl);
1518         return FALSE;
1519     }
1520     if (mustHaveProperty)
1521     {
1522         pacl->mustHaveProperty = MakeAtom(mustHaveProperty,
1523                                           strlen(mustHaveProperty), TRUE);
1524         if (pacl->mustHaveProperty == BAD_RESOURCE)
1525         {
1526             Xfree(pacl);
1527             return FALSE;
1528         }
1529     }
1530     else
1531         pacl->mustHaveProperty = 0;
1532
1533     if (mustHaveValue)
1534     {
1535         pacl->mustHaveValue = (char *)(pacl + 1);
1536         strcpy(pacl->mustHaveValue, mustHaveValue);
1537     }
1538     else
1539         pacl->mustHaveValue = NULL;
1540
1541     SecurityMaxPropertyName = max(SecurityMaxPropertyName, pacl->name);
1542
1543     pacl->windowRestriction = windowRestriction;
1544     pacl->readAction  = readAction;
1545     pacl->writeAction = writeAction;
1546     pacl->destroyAction = destroyAction;
1547
1548     /* link the new rule into the list of rules in order of increasing
1549      * property name (atom) value to make searching easier
1550      */
1551
1552     for (prev = NULL,  cur = PropertyAccessList;
1553          cur && cur->name <= pacl->name;
1554          prev = cur, cur = cur->next)
1555         ;
1556     if (!prev)
1557     {
1558         pacl->next = cur;
1559         PropertyAccessList = pacl;
1560     }
1561     else
1562     {
1563         prev->next = pacl;
1564         pacl->next = cur;
1565     }
1566     return TRUE;
1567 } /* SecurityParsePropertyAccessRule */
1568
1569 static char **SecurityPolicyStrings = NULL;
1570 static int nSecurityPolicyStrings = 0;
1571
1572 static Bool
1573 SecurityParseSitePolicy(p)
1574     char *p;
1575 {
1576     char *policyStr = SecurityParseString(&p);
1577     char *copyPolicyStr;
1578     char **newStrings;
1579
1580     if (!policyStr)
1581         return FALSE;
1582
1583     copyPolicyStr = (char *)Xalloc(strlen(policyStr) + 1);
1584     if (!copyPolicyStr)
1585         return TRUE;
1586     strcpy(copyPolicyStr, policyStr);
1587     newStrings = (char **)Xrealloc(SecurityPolicyStrings,
1588                           sizeof (char *) * (nSecurityPolicyStrings + 1));
1589     if (!newStrings)
1590     {
1591         Xfree(copyPolicyStr);
1592         return TRUE;
1593     }
1594
1595     SecurityPolicyStrings = newStrings;
1596     SecurityPolicyStrings[nSecurityPolicyStrings++] = copyPolicyStr;
1597
1598 } /* SecurityParseSitePolicy */
1599
1600
1601 char **
1602 SecurityGetSitePolicyStrings(n)
1603     int *n;
1604 {
1605     *n = nSecurityPolicyStrings;
1606     return SecurityPolicyStrings;
1607 } /* SecurityGetSitePolicyStrings */
1608
1609 static void
1610 SecurityFreeSitePolicyStrings()
1611 {
1612     if (SecurityPolicyStrings)
1613     {
1614         assert(nSecurityPolicyStrings);
1615         while (nSecurityPolicyStrings--)
1616         {
1617             Xfree(SecurityPolicyStrings[nSecurityPolicyStrings]);
1618         }
1619         Xfree(SecurityPolicyStrings);
1620         SecurityPolicyStrings = NULL;
1621         nSecurityPolicyStrings = 0;
1622     }
1623 } /* SecurityFreeSitePolicyStrings */
1624
1625
1626 static void
1627 SecurityLoadPropertyAccessList()
1628 {
1629     FILE *f;
1630     int lineNumber = 0;
1631
1632     SecurityMaxPropertyName = 0;
1633
1634     if (!SecurityPolicyFile)
1635         return;
1636
1637 #ifndef __EMX__
1638     f = fopen(SecurityPolicyFile, "r");
1639 #else
1640     f = fopen((char*)__XOS2RedirRoot(SecurityPolicyFile), "r");
1641 #endif    
1642     if (!f)
1643     {
1644         ErrorF("error opening security policy file %s\n",
1645                SecurityPolicyFile);
1646         return;
1647     }
1648
1649     while (!feof(f))
1650     {
1651         char buf[200];
1652         Bool validLine;
1653         char *p;
1654
1655         if (!(p = fgets(buf, sizeof(buf), f)))
1656             break;
1657         lineNumber++;
1658
1659         /* if first line, check version number */
1660         if (lineNumber == 1)
1661         {
1662             char *v = SecurityParseString(&p);
1663             if (strcmp(v, SECURITY_POLICY_FILE_VERSION) != 0)
1664             {
1665                 ErrorF("%s: invalid security policy file version, ignoring file\n",
1666                        SecurityPolicyFile);
1667                 break;
1668             }
1669             validLine = TRUE;
1670         }
1671         else
1672         {
1673             switch (SecurityParseKeyword(&p))
1674             {
1675                 case SecurityKeywordComment:
1676                     validLine = TRUE;
1677                 break;
1678
1679                 case SecurityKeywordProperty:
1680                     validLine = SecurityParsePropertyAccessRule(p);
1681                 break;
1682
1683                 case SecurityKeywordSitePolicy:
1684                     validLine = SecurityParseSitePolicy(p);
1685                 break;
1686
1687                 default:
1688                     validLine = (*p == '\0'); /* blank lines OK, others not */
1689                 break;
1690             }
1691         }
1692
1693         if (!validLine)
1694             ErrorF("Line %d of %s invalid, ignoring\n",
1695                    lineNumber, SecurityPolicyFile);
1696     } /* end while more input */
1697
1698 #ifdef PROPDEBUG
1699     {
1700         PropertyAccessPtr pacl;
1701         char *op = "aie";
1702         for (pacl = PropertyAccessList; pacl; pacl = pacl->next)
1703         {
1704             ErrorF("property %s ", NameForAtom(pacl->name));
1705             switch (pacl->windowRestriction)
1706             {
1707                 case SecurityAnyWindow: ErrorF("any "); break;
1708                 case SecurityRootWindow: ErrorF("root "); break;
1709                 case SecurityWindowWithProperty:
1710                 {
1711                     ErrorF("%s ", NameForAtom(pacl->mustHaveProperty));
1712                     if (pacl->mustHaveValue)
1713                         ErrorF(" = \"%s\" ", pacl->mustHaveValue);
1714
1715                 }
1716                 break;
1717             }
1718             ErrorF("%cr %cw %cd\n", op[pacl->readAction],
1719                    op[pacl->writeAction], op[pacl->destroyAction]);
1720         }
1721     }
1722 #endif /* PROPDEBUG */
1723
1724     fclose(f);
1725 } /* SecurityLoadPropertyAccessList */
1726
1727
1728 static Bool
1729 SecurityMatchString(ws, cs)
1730     char *ws;
1731     char *cs;
1732 {
1733     while (*ws && *cs)
1734     {
1735         if (*ws == '*')
1736         {
1737             Bool match = FALSE;
1738             ws++;
1739             while (!(match = SecurityMatchString(ws, cs)) && *cs)
1740             {
1741                 cs++;
1742             }
1743             return match;
1744         }
1745         else if (*ws == *cs)
1746         {
1747             ws++;
1748             cs++;
1749         }
1750         else break;
1751     }
1752     return ( ( (*ws == '\0') || ((*ws == '*') && *(ws+1) == '\0') )
1753              && (*cs == '\0') );
1754 } /* SecurityMatchString */
1755
1756 #ifdef PROPDEBUG
1757 #include <sys/types.h>
1758 #include <sys/stat.h>
1759 #endif
1760
1761
1762 char
1763 SecurityCheckPropertyAccess(client, pWin, propertyName, access_mode)
1764     ClientPtr client;
1765     WindowPtr pWin;
1766     ATOM propertyName;
1767     Mask access_mode;
1768 {
1769     PropertyAccessPtr pacl;
1770     char action = SecurityDefaultAction;
1771
1772     /* if client trusted or window untrusted, allow operation */
1773
1774     if ( (client->trustLevel == XSecurityClientTrusted) ||
1775          (wClient(pWin)->trustLevel != XSecurityClientTrusted) )
1776         return SecurityAllowOperation;
1777
1778 #ifdef PROPDEBUG
1779     /* For testing, it's more convenient if the property rules file gets
1780      * reloaded whenever it changes, so we can rapidly try things without
1781      * having to reset the server.
1782      */
1783     {
1784         struct stat buf;
1785         static time_t lastmod = 0;
1786         int ret = stat(SecurityPolicyFile , &buf);
1787         if ( (ret == 0) && (buf.st_mtime > lastmod) )
1788         {
1789             ErrorF("reloading property rules\n");
1790             SecurityFreePropertyAccessList();
1791             SecurityLoadPropertyAccessList();
1792             lastmod = buf.st_mtime;
1793         }
1794     }
1795 #endif
1796
1797     /* If the property atom is bigger than any atoms on the list, 
1798      * we know we won't find it, so don't even bother looking.
1799      */
1800     if (propertyName <= SecurityMaxPropertyName)
1801     {
1802         /* untrusted client operating on trusted window; see if it's allowed */
1803
1804         for (pacl = PropertyAccessList; pacl; pacl = pacl->next)
1805         {
1806             if (pacl->name < propertyName)
1807                 continue;
1808             if (pacl->name > propertyName)
1809                 break;
1810
1811             /* pacl->name == propertyName, so see if it applies to this window */
1812
1813             switch (pacl->windowRestriction)
1814             {
1815                 case SecurityAnyWindow: /* always applies */
1816                     break;
1817
1818                 case SecurityRootWindow:
1819                 {
1820                     /* if not a root window, this rule doesn't apply */
1821                     if (pWin->parent)
1822                         continue;
1823                 }
1824                 break;
1825
1826                 case SecurityWindowWithProperty:
1827                 {
1828                     PropertyPtr pProp = wUserProps (pWin);
1829                     Bool match = FALSE;
1830                     char *p;
1831                     char *pEndData;
1832
1833                     while (pProp)
1834                     {
1835                         if (pProp->propertyName == pacl->mustHaveProperty)
1836                             break;
1837                         pProp = pProp->next;
1838                     }
1839                     if (!pProp)
1840                         continue;
1841                     if (!pacl->mustHaveValue)
1842                         break;
1843                     if (pProp->type != XA_STRING || pProp->format != 8)
1844                         continue;
1845
1846                     p = pProp->data;
1847                     pEndData = ((char *)pProp->data) + pProp->size;
1848                     while (!match && p < pEndData)
1849                     {
1850                          if (SecurityMatchString(pacl->mustHaveValue, p))
1851                              match = TRUE;
1852                          else
1853                          { /* skip to the next string */
1854                              while (*p++ && p < pEndData)
1855                                  ;
1856                          }
1857                     }
1858                     if (!match)
1859                         continue;
1860                 }
1861                 break; /* end case SecurityWindowWithProperty */
1862             } /* end switch on windowRestriction */
1863
1864             /* If we get here, the property access rule pacl applies.
1865              * If pacl doesn't apply, something above should have
1866              * executed a continue, which will skip the follwing code.
1867              */
1868             action = SecurityAllowOperation;
1869             if (access_mode & SecurityReadAccess)
1870                 action = max(action, pacl->readAction);
1871             if (access_mode & SecurityWriteAccess)
1872                 action = max(action, pacl->writeAction);
1873             if (access_mode & SecurityDestroyAccess)
1874                 action = max(action, pacl->destroyAction);
1875             break;
1876         } /* end for each pacl */
1877     } /* end if propertyName <= SecurityMaxPropertyName */
1878
1879     if (SecurityAllowOperation != action)
1880     { /* audit the access violation */
1881         int cid = CLIENT_ID(pWin->drawable.id);
1882         int reqtype = ((xReq *)client->requestBuffer)->reqType;
1883         char *actionstr = (SecurityIgnoreOperation == action) ?
1884                                                         "ignored" : "error";
1885         SecurityAudit("client %d attempted request %d with window 0x%x property %s (atom 0x%x) of client %d, %s\n",
1886                 client->index, reqtype, pWin->drawable.id,
1887                       NameForAtom(propertyName), propertyName, cid, actionstr);
1888     }
1889     return action;
1890 } /* SecurityCheckPropertyAccess */
1891
1892
1893 /* SecurityResetProc
1894  *
1895  * Arguments:
1896  *      extEntry is the extension information for the security extension.
1897  *
1898  * Returns: nothing.
1899  *
1900  * Side Effects:
1901  *      Performs any cleanup needed by Security at server shutdown time.
1902  */
1903
1904 static void
1905 SecurityResetProc(extEntry)
1906     ExtensionEntry      *extEntry;
1907 {
1908     SecurityFreePropertyAccessList();
1909     SecurityFreeSitePolicyStrings();
1910 } /* SecurityResetProc */
1911
1912
1913 int
1914 XSecurityOptions(argc, argv, i)
1915     int argc;
1916     char **argv;
1917     int i;
1918 {
1919     if (strcmp(argv[i], "-sp") == 0)
1920     {
1921         if (i < argc)
1922             SecurityPolicyFile = argv[++i];
1923         return (i + 1);
1924     }
1925     return (i);
1926 } /* XSecurityOptions */
1927
1928
1929
1930 /* SecurityExtensionInit
1931  *
1932  * Arguments: none.
1933  *
1934  * Returns: nothing.
1935  *
1936  * Side Effects:
1937  *      Enables the Security extension if possible.
1938  */
1939
1940 void
1941 SecurityExtensionInit()
1942 {
1943     ExtensionEntry      *extEntry;
1944     int i;
1945
1946     SecurityAuthorizationResType =
1947         CreateNewResourceType(SecurityDeleteAuthorization);
1948
1949     RTEventClient = CreateNewResourceType(
1950                                 SecurityDeleteAuthorizationEventClient);
1951
1952     if (!SecurityAuthorizationResType || !RTEventClient)
1953         return;
1954
1955     RTEventClient |= RC_NEVERRETAIN;
1956
1957     if (!AddCallback(&ClientStateCallback, SecurityClientStateCallback, NULL))
1958         return;
1959
1960     extEntry = AddExtension(SECURITY_EXTENSION_NAME,
1961                             XSecurityNumberEvents, XSecurityNumberErrors,
1962                             ProcSecurityDispatch, SProcSecurityDispatch,
1963                             SecurityResetProc, StandardMinorOpcode);
1964
1965     SecurityErrorBase = extEntry->errorBase;
1966     SecurityEventBase = extEntry->eventBase;
1967
1968     EventSwapVector[SecurityEventBase + XSecurityAuthorizationRevoked] =
1969         SwapSecurityAuthorizationRevokedEvent;
1970
1971     /* initialize untrusted proc vectors */
1972
1973     for (i = 0; i < 128; i++)
1974     {
1975         UntrustedProcVector[i] = ProcVector[i];
1976         SwappedUntrustedProcVector[i] = SwappedProcVector[i];
1977     }
1978
1979     /* make sure insecure extensions are not allowed */
1980
1981     for (i = 128; i < 256; i++)
1982     {
1983         if (!UntrustedProcVector[i])
1984         {
1985             UntrustedProcVector[i] = ProcBadRequest;
1986             SwappedUntrustedProcVector[i] = ProcBadRequest;
1987         }
1988     }
1989
1990     SecurityLoadPropertyAccessList();
1991
1992 } /* SecurityExtensionInit */