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