1 /* $XConsortium: security.c /main/13 1996/12/15 21:24:27 rws $ */
3 Copyright (c) 1996 X Consortium, Inc.
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.
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.
18 /* $XFree86: xc/programs/Xserver/Xext/security.c,v 1.2 1997/01/27 06:57:17 dawes Exp $ */
20 #include "dixstruct.h"
21 #include "extnsionst.h"
22 #include "windowstr.h"
25 #include "colormapst.h"
26 #include "propertyst.h"
27 #define _SECURITY_SERVER
34 extern unsigned char LbxReqCode;
39 #include <stdio.h> /* for file reading operations */
40 #include "Xatom.h" /* for XA_STRING */
42 #ifndef DEFAULTPOLICYFILE
43 # define DEFAULTPOLICYFILE NULL
50 static int SecurityErrorBase; /* first Security error number */
51 static int SecurityEventBase; /* first Security event number */
53 CallbackListPtr SecurityValidateGroupCallback = NULL; /* see security.h */
55 RESTYPE SecurityAuthorizationResType; /* resource type for authorizations */
57 static RESTYPE RTEventClient;
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.
66 int (*UntrustedProcVector[256])(
67 #if NeedNestedPrototypes
71 int (*SwappedUntrustedProcVector[256])(
72 #if NeedNestedPrototypes
77 extern int ProcBadRequest();
83 * format is the formatting string to be used to interpret the
84 * remaining arguments.
89 * Writes the message to the log file if security logging is on.
93 SecurityAudit(char *format, ...)
97 if (auditTrailLevel < SECURITY_AUDIT_LEVEL)
100 va_start(args, format);
101 VErrorF(format, args);
103 } /* SecurityAudit */
105 #define rClient(obj) (clients[CLIENT_ID((obj)->resource)])
107 /* SecurityDeleteAuthorization
110 * value is the authorization to delete.
111 * id is its resource ID.
116 * Frees everything associated with the authorization.
120 SecurityDeleteAuthorization(value, id)
124 SecurityAuthorizationPtr pAuth = (SecurityAuthorizationPtr)value;
125 unsigned short name_len, data_len;
129 OtherClientsPtr pEventClient;
131 /* Remove the auth using the os layer auth manager */
133 status = AuthorizationFromID(pAuth->id, &name_len, &name,
136 status = RemoveAuthorization(name_len, name, data_len, data);
139 /* free the auth timer if there is one */
141 if (pAuth->timer) TimerFree(pAuth->timer);
143 /* send revoke events */
145 while (pEventClient = pAuth->eventClients)
147 /* send revocation event event */
148 ClientPtr client = rClient(pEventClient);
150 if (!client->clientGone)
152 xSecurityAuthorizationRevokedEvent are;
153 are.type = SecurityEventBase + XSecurityAuthorizationRevoked;
154 are.sequenceNumber = client->sequence;
155 are.authId = pAuth->id;
156 WriteEventsToClient(client, 1, (xEvent *)&are);
158 FreeResource(pEventClient->resource, RT_NONE);
161 /* kill all clients using this auth */
163 for (i = 1; i<currentMaxClients; i++)
165 if (clients[i] && (clients[i]->authId == pAuth->id))
166 CloseDownClient(clients[i]);
169 SecurityAudit("revoked authorization ID %d\n", pAuth->id);
173 } /* SecurityDeleteAuthorization */
176 /* resource delete function for RTEventClient */
178 SecurityDeleteAuthorizationEventClient(value, id)
182 OtherClientsPtr pEventClient, prev = NULL;
183 SecurityAuthorizationPtr pAuth = (SecurityAuthorizationPtr)value;
185 for (pEventClient = pAuth->eventClients;
187 pEventClient = pEventClient->next)
189 if (pEventClient->resource == id)
192 prev->next = pEventClient->next;
194 pAuth->eventClients = pEventClient->next;
201 return -1; /* make compiler happy */
202 } /* SecurityDeleteAuthorizationEventClient */
205 /* SecurityComputeAuthorizationTimeout
208 * pAuth is the authorization for which we are computing the timeout
209 * seconds is the number of seconds we want to wait
212 * the number of milliseconds that the auth timer should be set to
215 * Sets pAuth->secondsRemaining to any "overflow" amount of time
216 * that didn't fit in 32 bits worth of milliseconds
220 SecurityComputeAuthorizationTimeout(pAuth, seconds)
221 SecurityAuthorizationPtr pAuth;
222 unsigned int seconds;
224 /* maxSecs is the number of full seconds that can be expressed in
225 * 32 bits worth of milliseconds
227 CARD32 maxSecs = (CARD32)(~0) / (CARD32)MILLI_PER_SECOND;
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;
235 { /* by far the common case */
236 pAuth->secondsRemaining = 0;
237 return seconds * MILLI_PER_SECOND;
239 } /* SecurityStartAuthorizationTimer */
241 /* SecurityAuthorizationExpired
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.
247 * timer is the timer for this authorization.
248 * time is the current time.
249 * pval is the authorization whose time is up.
252 * A new time delay in milliseconds if the timer should wait some
256 * Frees the authorization resource if the timeout period is really
257 * over, otherwise recomputes pAuth->secondsRemaining.
261 SecurityAuthorizationExpired(timer, time, pval)
266 SecurityAuthorizationPtr pAuth = (SecurityAuthorizationPtr)pval;
268 assert(pAuth->timer == timer);
270 if (pAuth->secondsRemaining)
272 return SecurityComputeAuthorizationTimeout(pAuth,
273 pAuth->secondsRemaining);
277 FreeResource(pAuth->id, RT_NONE);
280 } /* SecurityAuthorizationExpired */
282 /* SecurityStartAuthorizationTimer
285 * pAuth is the authorization whose timer should be started.
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.
296 SecurityStartAuthorizationTimer(pAuth)
297 SecurityAuthorizationPtr pAuth;
299 pAuth->timer = TimerSet(pAuth->timer, 0,
300 SecurityComputeAuthorizationTimeout(pAuth, pAuth->timeout),
301 SecurityAuthorizationExpired, pAuth);
302 } /* SecurityStartAuthorizationTimer */
305 /* Proc functions all take a client argument, execute the request in
306 * client->requestBuffer, and return a protocol error status.
310 ProcSecurityQueryVersion(client)
313 REQUEST(xSecurityQueryVersionReq);
314 xSecurityQueryVersionReply rep;
316 /* paranoia: this "can't happen" because this extension is hidden
317 * from untrusted clients, but just in case...
319 if (client->trustLevel != XSecurityClientTrusted)
322 REQUEST_SIZE_MATCH(xSecurityQueryVersionReq);
324 rep.sequenceNumber = client->sequence;
326 rep.majorVersion = SECURITY_MAJOR_VERSION;
327 rep.minorVersion = SECURITY_MINOR_VERSION;
331 swaps(&rep.sequenceNumber, n);
332 swaps(&rep.majorVersion, n);
333 swaps(&rep.minorVersion, n);
335 (void)WriteToClient(client, SIZEOF(xSecurityQueryVersionReply),
337 return (client->noClientException);
338 } /* ProcSecurityQueryVersion */
342 SecurityEventSelectForAuthorization(pAuth, client, mask)
343 SecurityAuthorizationPtr pAuth;
347 OtherClients *pEventClient;
349 for (pEventClient = pAuth->eventClients;
351 pEventClient = pEventClient->next)
353 if (SameClient(pEventClient, client))
356 FreeResource(pEventClient->resource, RT_NONE);
358 pEventClient->mask = mask;
363 pEventClient = (OtherClients *) xalloc(sizeof(OtherClients));
366 pEventClient->mask = mask;
367 pEventClient->resource = FakeClientID(client->index);
368 pEventClient->next = pAuth->eventClients;
369 if (!AddResource(pEventClient->resource, RTEventClient,
375 pAuth->eventClients = pEventClient;
378 } /* SecurityEventSelectForAuthorization */
382 ProcSecurityGenerateAuthorization(client)
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 */
403 /* paranoia: this "can't happen" because this extension is hidden
404 * from untrusted clients, but just in case...
406 if (client->trustLevel != XSecurityClientTrusted)
409 /* check request length */
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)
420 /* check valuemask */
421 if (stuff->valueMask & ~XSecurityAllAuthorizationAttributes)
423 client->errorValue = stuff->valueMask;
429 if (stuff->valueMask & XSecurityTimeout)
434 /* check trustLevel */
435 trustLevel = XSecurityClientUntrusted;
436 if (stuff->valueMask & XSecurityTrustLevel)
438 trustLevel = *values++;
439 if (trustLevel != XSecurityClientTrusted &&
440 trustLevel != XSecurityClientUntrusted)
442 client->errorValue = trustLevel;
449 if (stuff->valueMask & XSecurityGroup)
452 if (SecurityValidateGroupCallback)
454 SecurityValidateGroupInfoRec vgi;
457 CallCallbacks(&SecurityValidateGroupCallback, (pointer)&vgi);
459 /* if nobody said they recognized it, it's an error */
463 client->errorValue = group;
469 /* check event mask */
471 if (stuff->valueMask & XSecurityEventMask)
473 eventMask = *values++;
474 if (eventMask & ~XSecurityAllEventMasks)
476 client->errorValue = eventMask;
481 protoname = (char *)&stuff[1];
482 protodata = protoname + ((stuff->nbytesAuthProto + (unsigned)3) >> 2);
484 /* call os layer to generate the authorization */
486 authId = GenerateAuthorization(stuff->nbytesAuthProto, protoname,
487 stuff->nbytesAuthData, protodata,
488 &authdata_len, &pAuthdata);
489 if ((XID) ~0L == authId)
491 err = SecurityErrorBase + XSecurityBadAuthorizationProtocol;
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)
500 /* associate additional information with this auth ID */
502 pAuth = (SecurityAuthorizationPtr)xalloc(sizeof(SecurityAuthorizationRec));
509 /* fill in the auth fields */
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;
518 pAuth->eventClients = NULL;
520 /* handle event selection */
523 err = SecurityEventSelectForAuthorization(pAuth, client, eventMask);
528 if (!AddResource(authId, SecurityAuthorizationResType, pAuth))
534 /* start the timer ticking */
536 if (pAuth->timeout != 0)
537 SecurityStartAuthorizationTimer(pAuth);
539 /* tell client the auth id and data */
542 rep.length = (authdata_len + 3) >> 2;
543 rep.sequenceNumber = client->sequence;
545 rep.dataLength = authdata_len;
550 swapl(&rep.length, n);
551 swaps(&rep.sequenceNumber, n);
552 swapl(&rep.authId, n);
553 swaps(&rep.dataLength, n);
556 WriteToClient(client, SIZEOF(xSecurityGenerateAuthorizationReply),
558 WriteToClient(client, authdata_len, pAuthdata);
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);
564 /* the request succeeded; don't call RemoveAuthorization or free pAuth */
568 err = client->noClientException;
572 RemoveAuthorization(stuff->nbytesAuthProto, protoname,
573 authdata_len, pAuthdata);
574 if (pAuth) xfree(pAuth);
577 } /* ProcSecurityGenerateAuthorization */
580 ProcSecurityRevokeAuthorization(client)
583 REQUEST(xSecurityRevokeAuthorizationReq);
584 SecurityAuthorizationPtr pAuth;
586 /* paranoia: this "can't happen" because this extension is hidden
587 * from untrusted clients, but just in case...
589 if (client->trustLevel != XSecurityClientTrusted)
592 REQUEST_SIZE_MATCH(xSecurityRevokeAuthorizationReq);
594 pAuth = (SecurityAuthorizationPtr)SecurityLookupIDByType(client,
595 stuff->authId, SecurityAuthorizationResType, SecurityDestroyAccess);
597 return SecurityErrorBase + XSecurityBadAuthorization;
599 FreeResource(stuff->authId, RT_NONE);
601 } /* ProcSecurityRevokeAuthorization */
605 ProcSecurityDispatch(client)
612 case X_SecurityQueryVersion:
613 return ProcSecurityQueryVersion(client);
614 case X_SecurityGenerateAuthorization:
615 return ProcSecurityGenerateAuthorization(client);
616 case X_SecurityRevokeAuthorization:
617 return ProcSecurityRevokeAuthorization(client);
621 } /* ProcSecurityDispatch */
624 SProcSecurityQueryVersion(client)
627 REQUEST(xSecurityQueryVersionReq);
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 */
639 SProcSecurityGenerateAuthorization(client)
642 REQUEST(xSecurityGenerateAuthorizationReq);
645 unsigned long nvalues;
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 */
662 SProcSecurityRevokeAuthorization(client)
665 REQUEST(xSecurityRevokeAuthorizationReq);
668 swaps(&stuff->length, n);
669 REQUEST_SIZE_MATCH(xSecurityRevokeAuthorizationReq);
670 swapl(&stuff->authId, n);
671 return ProcSecurityRevokeAuthorization(client);
672 } /* SProcSecurityRevokeAuthorization */
676 SProcSecurityDispatch(client)
683 case X_SecurityQueryVersion:
684 return SProcSecurityQueryVersion(client);
685 case X_SecurityGenerateAuthorization:
686 return SProcSecurityGenerateAuthorization(client);
687 case X_SecurityRevokeAuthorization:
688 return SProcSecurityRevokeAuthorization(client);
692 } /* SProcSecurityDispatch */
695 SwapSecurityAuthorizationRevokedEvent(from, to)
696 xSecurityAuthorizationRevokedEvent *from, *to;
698 to->type = from->type;
699 to->detail = from->detail;
700 cpswaps(from->sequenceNumber, to->sequenceNumber);
701 cpswapl(from->authId, to->authId);
704 /* SecurityDetermineEventPropogationLimits
706 * This is a helper function for SecurityCheckDeviceAccess.
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.
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.
720 * Side Effects: none.
724 SecurityDetermineEventPropogationLimits(dev, ppWin, ppStopWin)
727 WindowPtr *ppStopWin;
729 WindowPtr pFocusWin = dev->focus ? dev->focus->win : NoneWin;
731 if (pFocusWin == NoneWin)
732 { /* no focus -- events don't go anywhere */
733 *ppWin = *ppStopWin = NULL;
737 if (pFocusWin == PointerRootWin)
738 { /* focus follows the pointer */
739 *ppWin = GetSpriteWindow();
740 *ppStopWin = NULL; /* propogate all the way to the root */
743 { /* a real window is set for the focus */
744 WindowPtr pSpriteWin = GetSpriteWindow();
745 *ppStopWin = pFocusWin->parent; /* don't go past the focus window */
747 /* if the pointer is in a subwindow of the focus window, start
748 * at that subwindow, else start at the focus window itself
750 if (IsParent(pFocusWin, pSpriteWin))
752 else *ppWin = pFocusWin;
754 } /* SecurityDetermineEventPropogationLimits */
757 /* SecurityCheckDeviceAccess
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)
767 * TRUE if the device access should be allowed, else FALSE.
770 * An audit message is generated if access is denied.
774 SecurityCheckDeviceAccess(client, dev, fromRequest)
779 WindowPtr pWin, pStopWin;
780 Bool untrusted_got_event;
781 Bool found_event_window;
785 /* trusted clients always allowed to do anything */
786 if (client->trustLevel == XSecurityClientTrusted)
789 /* device security other than keyboard is not implemented yet */
790 if (dev != inputInfo.keyboard)
793 /* some untrusted client wants access */
797 reqtype = ((xReq *)client->requestBuffer)->reqType;
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);
812 untrusted_got_event = FALSE;
813 found_event_window = FALSE;
817 untrusted_got_event =
818 ((rClient(dev->grab))->trustLevel != XSecurityClientTrusted);
822 SecurityDetermineEventPropogationLimits(dev, &pWin, &pStopWin);
824 eventmask = KeyPressMask | KeyReleaseMask;
825 while ( (pWin != pStopWin) && !found_event_window)
829 if (pWin->eventMask & eventmask)
831 found_event_window = TRUE;
832 client = wClient(pWin);
833 if (client->trustLevel != XSecurityClientTrusted)
835 untrusted_got_event = TRUE;
838 if (wOtherEventMasks(pWin) & eventmask)
840 found_event_window = TRUE;
841 for (other = wOtherClients(pWin); other; other = other->next)
843 if (other->mask & eventmask)
845 client = rClient(other);
846 if (client->trustLevel != XSecurityClientTrusted)
848 untrusted_got_event = TRUE;
854 if (wDontPropagateMask(pWin) & eventmask)
857 } /* while propogating the event */
860 /* allow access by untrusted clients only if an event would have gone
861 * to an untrusted client
864 if (!untrusted_got_event)
866 char *devname = dev->name;
867 if (!devname) devname = "unnamed";
869 SecurityAudit("client %d attempted request %d device %d (%s)\n",
870 client->index, reqtype, dev->id, devname);
872 SecurityAudit("client %d attempted to access device %d (%s)\n",
873 client->index, dev->id, devname);
875 return untrusted_got_event;
876 } /* SecurityCheckDeviceAccess */
880 /* SecurityAuditResourceIDAccess
883 * client is the client doing the resource access.
884 * id is the resource id.
889 * An audit message is generated with details of the denied
894 SecurityAuditResourceIDAccess(client, id)
898 int cid = CLIENT_ID(id);
899 int reqtype = ((xReq *)client->requestBuffer)->reqType;
902 case X_ChangeProperty:
903 case X_DeleteProperty:
906 xChangePropertyReq *req =
907 (xChangePropertyReq *)client->requestBuffer;
908 int propertyatom = req->property;
909 char *propertyname = NameForAtom(propertyatom);
911 SecurityAudit("client %d attempted request %d with window 0x%x property %s of client %d\n",
912 client->index, reqtype, id, propertyname, cid);
917 SecurityAudit("client %d attempted request %d with resource 0x%x of client %d\n",
918 client->index, reqtype, id, cid);
923 } /* SecurityAuditResourceIDAccess */
926 /* SecurityCheckResourceIDAccess
928 * This function gets plugged into client->CheckAccess and is called from
929 * SecurityLookupIDByType/Class to determine if the client can access the
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
938 * rval is a pointer to the resource structure for this resource.
941 * If access is granted, the value of rval that was passed in, else NULL.
944 * Disallowed resource accesses are audited.
948 SecurityCheckResourceIDAccess(client, id, rtype, access_mode, rval)
955 int cid = CLIENT_ID(id);
956 int reqtype = ((xReq *)client->requestBuffer)->reqType;
958 if (SecurityUnknownAccess == access_mode)
959 return rval; /* for compatibility, we have to allow access */
962 { /* these are always allowed */
964 case X_TranslateCoords:
966 /* property access is controlled in SecurityCheckPropertyAccess */
968 case X_ChangeProperty:
969 case X_DeleteProperty:
970 case X_RotateProperties:
971 case X_ListProperties:
978 { /* not a server-owned resource */
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
992 if (client->trustLevel == clients[cid]->trustLevel
994 || (RT_COLORMAP == rtype &&
995 XagDefaultColormap (client) == (Colormap) id)
1000 return SecurityAuditResourceIDAccess(client, id);
1002 else /* server-owned resource - probably a default colormap or root window */
1004 if (RT_WINDOW == rtype || RC_DRAWABLE == rtype)
1007 { /* the following operations are allowed on root windows */
1008 case X_CreatePixmap:
1010 case X_CreateWindow:
1011 case X_CreateColormap:
1012 case X_ListProperties:
1014 case X_UngrabButton:
1015 case X_QueryBestSize:
1016 case X_GetWindowAttributes:
1019 { /* see if it is an event specified by the ICCCM */
1020 xSendEventReq *req = (xSendEventReq *)
1021 (client->requestBuffer);
1022 if (req->propagate == xTrue
1024 (req->eventMask != ColormapChangeMask &&
1025 req->eventMask != StructureNotifyMask &&
1027 (SubstructureRedirectMask|SubstructureNotifyMask)
1030 (req->event.u.u.type != UnmapNotify &&
1031 req->event.u.u.type != ConfigureRequest &&
1032 req->event.u.u.type != ClientMessage
1035 { /* not an ICCCM event */
1036 return SecurityAuditResourceIDAccess(client, id);
1039 } /* case X_SendEvent on root */
1041 case X_ChangeWindowAttributes:
1042 { /* Allow selection of PropertyNotify and StructureNotify
1043 * events on the root.
1045 xChangeWindowAttributesReq *req =
1046 (xChangeWindowAttributesReq *)(client->requestBuffer);
1047 if (req->valueMask == CWEventMask)
1049 CARD32 value = *((CARD32 *)(req + 1));
1051 ~(PropertyChangeMask|StructureNotifyMask)) == 0)
1054 return SecurityAuditResourceIDAccess(client, id);
1055 } /* case X_ChangeWindowAttributes on root */
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:
1071 /* others not allowed */
1072 return SecurityAuditResourceIDAccess(client, id);
1075 } /* end server-owned window or drawable */
1076 else if (SecurityAuthorizationResType == rtype)
1078 SecurityAuthorizationPtr pAuth = (SecurityAuthorizationPtr)rval;
1079 if (pAuth->trustLevel != client->trustLevel)
1080 return SecurityAuditResourceIDAccess(client, id);
1082 else if (RT_COLORMAP != rtype)
1083 { /* don't allow anything else besides colormaps */
1084 return SecurityAuditResourceIDAccess(client, id);
1088 } /* SecurityCheckResourceIDAccess */
1091 /* SecurityClientStateCallback
1094 * pcbl is &ClientStateCallback.
1096 * calldata is a pointer to a NewClientInfoRec (include/dixstruct.h)
1097 * which contains information about client state changes.
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.
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.
1114 SecurityClientStateCallback(pcbl, nulldata, calldata)
1115 CallbackListPtr *pcbl;
1119 NewClientInfoRec *pci = (NewClientInfoRec *)calldata;
1120 ClientPtr client = pci->client;
1122 switch (client->clientState)
1124 case ClientStateRunning:
1126 XID authId = AuthorizationIDOfClient(client);
1127 SecurityAuthorizationPtr pAuth;
1129 client->authId = authId;
1130 pAuth = (SecurityAuthorizationPtr)LookupIDByType(authId,
1131 SecurityAuthorizationResType);
1133 { /* it is a generated authorization */
1135 if (pAuth->refcnt == 1)
1137 if (pAuth->timer) TimerCancel(pAuth->timer);
1139 client->trustLevel = pAuth->trustLevel;
1140 if (client->trustLevel != XSecurityClientTrusted)
1142 client->CheckAccess = SecurityCheckResourceIDAccess;
1143 client->requestVector = client->swapped ?
1144 SwappedUntrustedProcVector : UntrustedProcVector;
1149 case ClientStateGone:
1150 case ClientStateRetained: /* client disconnected */
1152 XID authId = client->authId;
1153 SecurityAuthorizationPtr pAuth;
1155 pAuth = (SecurityAuthorizationPtr)LookupIDByType(authId,
1156 SecurityAuthorizationResType);
1158 { /* it is a generated authorization */
1160 if (pAuth->refcnt == 0)
1162 SecurityStartAuthorizationTimer(pAuth);
1169 } /* SecurityClientStateCallback */
1173 SecuritySameLevel(client, authId)
1177 SecurityAuthorizationPtr pAuth;
1179 pAuth = (SecurityAuthorizationPtr)LookupIDByType(authId,
1180 SecurityAuthorizationResType);
1182 return client->trustLevel == pAuth->trustLevel;
1183 return client->trustLevel == XSecurityClientTrusted;
1187 /* SecurityCensorImage
1189 * Called after pScreen->GetImage to prevent pieces or trusted windows from
1190 * being returned in image data from an untrusted window.
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.
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.
1208 SecurityCensorImage(client, pVisibleRegion, widthBytesLine, pDraw, x, y, w, h,
1211 RegionPtr pVisibleRegion;
1212 long widthBytesLine;
1215 unsigned int format;
1218 RegionRec imageRegion; /* region representing x,y,w,h */
1219 RegionRec censorRegion; /* region to obliterate */
1225 imageBox.x2 = x + w;
1226 imageBox.y2 = y + h;
1227 REGION_INIT(pScreen, &imageRegion, &imageBox, 1);
1228 REGION_INIT(pScreen, &censorRegion, NullBox, 0);
1230 /* censorRegion = imageRegion - visibleRegion */
1231 REGION_SUBTRACT(pScreen, &censorRegion, &imageRegion, pVisibleRegion);
1232 nRects = REGION_NUM_RECTS(&censorRegion);
1234 { /* we have something to censor */
1235 GCPtr pScratchGC = NULL;
1236 PixmapPtr pPix = NULL;
1237 xRectangle *pRects = NULL;
1238 Bool failed = FALSE;
1240 int bitsPerPixel = 1;
1244 /* convert region to list-of-rectangles for PolyFillRect */
1246 pRects = (xRectangle *)ALLOCATE_LOCAL(nRects * sizeof(xRectangle *));
1252 for (pBox = REGION_RECTS(&censorRegion), i = 0;
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;
1262 /* use pBuf as a fake pixmap */
1264 if (format == ZPixmap)
1266 depth = pDraw->depth;
1267 bitsPerPixel = pDraw->bitsPerPixel;
1270 pPix = GetScratchPixmapHeader(pDraw->pScreen, w, h,
1271 depth, bitsPerPixel,
1272 widthBytesLine, (pointer)pBuf);
1279 pScratchGC = GetScratchGC(depth, pPix->drawable.pScreen);
1286 ValidateGC(&pPix->drawable, pScratchGC);
1287 (* pScratchGC->ops->PolyFillRect)(&pPix->drawable,
1288 pScratchGC, nRects, pRects);
1293 /* Censoring was not completed above. To be safe, wipe out
1294 * all the image data so that nothing trusted gets out.
1296 bzero(pBuf, (int)(widthBytesLine * h));
1298 if (pRects) DEALLOCATE_LOCAL(pRects);
1299 if (pScratchGC) FreeScratchGC(pScratchGC);
1300 if (pPix) FreeScratchPixmapHeader(pPix);
1302 REGION_UNINIT(pScreen, &imageRegion);
1303 REGION_UNINIT(pScreen, &censorRegion);
1304 } /* SecurityCensorImage */
1306 /**********************************************************************/
1308 typedef struct _PropertyAccessRec {
1310 ATOM mustHaveProperty;
1311 char *mustHaveValue;
1312 char windowRestriction;
1313 #define SecurityAnyWindow 0
1314 #define SecurityRootWindow 1
1315 #define SecurityWindowWithProperty 2
1319 struct _PropertyAccessRec *next;
1320 } PropertyAccessRec, *PropertyAccessPtr;
1322 static PropertyAccessPtr PropertyAccessList = NULL;
1323 static char SecurityDefaultAction = SecurityErrorOperation;
1324 static char *SecurityPolicyFile = DEFAULTPOLICYFILE;
1325 static ATOM SecurityMaxPropertyName = 0;
1327 static char *SecurityKeywords[] = {
1328 #define SecurityKeywordComment 0
1330 #define SecurityKeywordProperty 1
1332 #define SecurityKeywordSitePolicy 2
1334 #define SecurityKeywordRoot 3
1336 #define SecurityKeywordAny 4
1340 #define NUMKEYWORDS (sizeof(SecurityKeywords) / sizeof(char *))
1343 /*#define PROPDEBUG 1*/
1346 SecurityFreePropertyAccessList()
1348 while (PropertyAccessList)
1350 PropertyAccessPtr freeit = PropertyAccessList;
1351 PropertyAccessList = PropertyAccessList->next;
1354 } /* SecurityFreePropertyAccessList */
1357 #define SecurityIsWhitespace(c) ( (c == ' ') || (c == '\t') || (c == '\n') )
1359 #define SecurityIsWhitespace(c) ( (c == ' ') || (c == '\t') || (c == '\n') || (c == '\r') )
1363 SecuritySkipWhitespace(p)
1366 while (SecurityIsWhitespace(*p))
1369 } /* SecuritySkipWhitespace */
1373 SecurityParseString(rest)
1376 char *startOfString;
1380 s = SecuritySkipWhitespace(s);
1382 if (*s == '"' || *s == '\'')
1386 while (*s && (*s != endChar))
1392 while (*s && !SecurityIsWhitespace(*s))
1399 return startOfString;
1404 return (endChar) ? NULL : startOfString;
1406 } /* SecurityParseString */
1410 SecurityParseKeyword(p)
1415 s = SecuritySkipWhitespace(s);
1416 for (i = 0; i < NUMKEYWORDS; i++)
1418 int len = strlen(SecurityKeywords[i]);
1419 if (strncmp(s, SecurityKeywords[i], len) == 0)
1427 } /* SecurityParseKeyword */
1431 SecurityParsePropertyAccessRule(p)
1436 char action = SecurityDefaultAction;
1437 char readAction, writeAction, destroyAction;
1438 PropertyAccessPtr pacl, prev, cur;
1440 char *mustHaveProperty = NULL;
1441 char *mustHaveValue = NULL;
1443 char windowRestriction;
1447 /* get property name */
1448 propname = SecurityParseString(&p);
1449 if (!propname || (strlen(propname) == 0))
1452 /* get window on which property must reside for rule to apply */
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 */
1461 mustHaveProperty = SecurityParseString(&p);
1462 if (!mustHaveProperty || (strlen(mustHaveProperty) == 0))
1464 windowRestriction = SecurityWindowWithProperty;
1465 p = SecuritySkipWhitespace(p);
1467 { /* property value is specified too */
1468 p++; /* skip over '=' */
1469 mustHaveValue = SecurityParseString(&p);
1475 /* get operations and actions */
1478 readAction = writeAction = destroyAction = SecurityDefaultAction;
1479 while ( (c = *p++) && !invalid)
1483 case 'i': action = SecurityIgnoreOperation; break;
1484 case 'a': action = SecurityAllowOperation; break;
1485 case 'e': action = SecurityErrorOperation; break;
1487 case 'r': readAction = action; break;
1488 case 'w': writeAction = action; break;
1489 case 'd': destroyAction = action; break;
1492 if (!SecurityIsWhitespace(c))
1500 /* We've successfully collected all the information needed for this
1501 * property access rule. Now record it in a PropertyAccessRec.
1503 size = sizeof(PropertyAccessRec);
1505 /* If there is a property value string, allocate space for it
1506 * right after the PropertyAccessRec.
1509 size += strlen(mustHaveValue) + 1;
1510 pacl = (PropertyAccessPtr)Xalloc(size);
1514 pacl->name = MakeAtom(propname, strlen(propname), TRUE);
1515 if (pacl->name == BAD_RESOURCE)
1520 if (mustHaveProperty)
1522 pacl->mustHaveProperty = MakeAtom(mustHaveProperty,
1523 strlen(mustHaveProperty), TRUE);
1524 if (pacl->mustHaveProperty == BAD_RESOURCE)
1531 pacl->mustHaveProperty = 0;
1535 pacl->mustHaveValue = (char *)(pacl + 1);
1536 strcpy(pacl->mustHaveValue, mustHaveValue);
1539 pacl->mustHaveValue = NULL;
1541 SecurityMaxPropertyName = max(SecurityMaxPropertyName, pacl->name);
1543 pacl->windowRestriction = windowRestriction;
1544 pacl->readAction = readAction;
1545 pacl->writeAction = writeAction;
1546 pacl->destroyAction = destroyAction;
1548 /* link the new rule into the list of rules in order of increasing
1549 * property name (atom) value to make searching easier
1552 for (prev = NULL, cur = PropertyAccessList;
1553 cur && cur->name <= pacl->name;
1554 prev = cur, cur = cur->next)
1559 PropertyAccessList = pacl;
1567 } /* SecurityParsePropertyAccessRule */
1569 static char **SecurityPolicyStrings = NULL;
1570 static int nSecurityPolicyStrings = 0;
1573 SecurityParseSitePolicy(p)
1576 char *policyStr = SecurityParseString(&p);
1577 char *copyPolicyStr;
1583 copyPolicyStr = (char *)Xalloc(strlen(policyStr) + 1);
1586 strcpy(copyPolicyStr, policyStr);
1587 newStrings = (char **)Xrealloc(SecurityPolicyStrings,
1588 sizeof (char *) * (nSecurityPolicyStrings + 1));
1591 Xfree(copyPolicyStr);
1595 SecurityPolicyStrings = newStrings;
1596 SecurityPolicyStrings[nSecurityPolicyStrings++] = copyPolicyStr;
1598 } /* SecurityParseSitePolicy */
1602 SecurityGetSitePolicyStrings(n)
1605 *n = nSecurityPolicyStrings;
1606 return SecurityPolicyStrings;
1607 } /* SecurityGetSitePolicyStrings */
1610 SecurityFreeSitePolicyStrings()
1612 if (SecurityPolicyStrings)
1614 assert(nSecurityPolicyStrings);
1615 while (nSecurityPolicyStrings--)
1617 Xfree(SecurityPolicyStrings[nSecurityPolicyStrings]);
1619 Xfree(SecurityPolicyStrings);
1620 SecurityPolicyStrings = NULL;
1621 nSecurityPolicyStrings = 0;
1623 } /* SecurityFreeSitePolicyStrings */
1627 SecurityLoadPropertyAccessList()
1632 SecurityMaxPropertyName = 0;
1634 if (!SecurityPolicyFile)
1638 f = fopen(SecurityPolicyFile, "r");
1640 f = fopen((char*)__XOS2RedirRoot(SecurityPolicyFile), "r");
1644 ErrorF("error opening security policy file %s\n",
1645 SecurityPolicyFile);
1655 if (!(p = fgets(buf, sizeof(buf), f)))
1659 /* if first line, check version number */
1660 if (lineNumber == 1)
1662 char *v = SecurityParseString(&p);
1663 if (strcmp(v, SECURITY_POLICY_FILE_VERSION) != 0)
1665 ErrorF("%s: invalid security policy file version, ignoring file\n",
1666 SecurityPolicyFile);
1673 switch (SecurityParseKeyword(&p))
1675 case SecurityKeywordComment:
1679 case SecurityKeywordProperty:
1680 validLine = SecurityParsePropertyAccessRule(p);
1683 case SecurityKeywordSitePolicy:
1684 validLine = SecurityParseSitePolicy(p);
1688 validLine = (*p == '\0'); /* blank lines OK, others not */
1694 ErrorF("Line %d of %s invalid, ignoring\n",
1695 lineNumber, SecurityPolicyFile);
1696 } /* end while more input */
1700 PropertyAccessPtr pacl;
1702 for (pacl = PropertyAccessList; pacl; pacl = pacl->next)
1704 ErrorF("property %s ", NameForAtom(pacl->name));
1705 switch (pacl->windowRestriction)
1707 case SecurityAnyWindow: ErrorF("any "); break;
1708 case SecurityRootWindow: ErrorF("root "); break;
1709 case SecurityWindowWithProperty:
1711 ErrorF("%s ", NameForAtom(pacl->mustHaveProperty));
1712 if (pacl->mustHaveValue)
1713 ErrorF(" = \"%s\" ", pacl->mustHaveValue);
1718 ErrorF("%cr %cw %cd\n", op[pacl->readAction],
1719 op[pacl->writeAction], op[pacl->destroyAction]);
1722 #endif /* PROPDEBUG */
1725 } /* SecurityLoadPropertyAccessList */
1729 SecurityMatchString(ws, cs)
1739 while (!(match = SecurityMatchString(ws, cs)) && *cs)
1745 else if (*ws == *cs)
1752 return ( ( (*ws == '\0') || ((*ws == '*') && *(ws+1) == '\0') )
1754 } /* SecurityMatchString */
1757 #include <sys/types.h>
1758 #include <sys/stat.h>
1763 SecurityCheckPropertyAccess(client, pWin, propertyName, access_mode)
1769 PropertyAccessPtr pacl;
1770 char action = SecurityDefaultAction;
1772 /* if client trusted or window untrusted, allow operation */
1774 if ( (client->trustLevel == XSecurityClientTrusted) ||
1775 (wClient(pWin)->trustLevel != XSecurityClientTrusted) )
1776 return SecurityAllowOperation;
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.
1785 static time_t lastmod = 0;
1786 int ret = stat(SecurityPolicyFile , &buf);
1787 if ( (ret == 0) && (buf.st_mtime > lastmod) )
1789 ErrorF("reloading property rules\n");
1790 SecurityFreePropertyAccessList();
1791 SecurityLoadPropertyAccessList();
1792 lastmod = buf.st_mtime;
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.
1800 if (propertyName <= SecurityMaxPropertyName)
1802 /* untrusted client operating on trusted window; see if it's allowed */
1804 for (pacl = PropertyAccessList; pacl; pacl = pacl->next)
1806 if (pacl->name < propertyName)
1808 if (pacl->name > propertyName)
1811 /* pacl->name == propertyName, so see if it applies to this window */
1813 switch (pacl->windowRestriction)
1815 case SecurityAnyWindow: /* always applies */
1818 case SecurityRootWindow:
1820 /* if not a root window, this rule doesn't apply */
1826 case SecurityWindowWithProperty:
1828 PropertyPtr pProp = wUserProps (pWin);
1835 if (pProp->propertyName == pacl->mustHaveProperty)
1837 pProp = pProp->next;
1841 if (!pacl->mustHaveValue)
1843 if (pProp->type != XA_STRING || pProp->format != 8)
1847 pEndData = ((char *)pProp->data) + pProp->size;
1848 while (!match && p < pEndData)
1850 if (SecurityMatchString(pacl->mustHaveValue, p))
1853 { /* skip to the next string */
1854 while (*p++ && p < pEndData)
1861 break; /* end case SecurityWindowWithProperty */
1862 } /* end switch on windowRestriction */
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.
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);
1876 } /* end for each pacl */
1877 } /* end if propertyName <= SecurityMaxPropertyName */
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);
1890 } /* SecurityCheckPropertyAccess */
1893 /* SecurityResetProc
1896 * extEntry is the extension information for the security extension.
1901 * Performs any cleanup needed by Security at server shutdown time.
1905 SecurityResetProc(extEntry)
1906 ExtensionEntry *extEntry;
1908 SecurityFreePropertyAccessList();
1909 SecurityFreeSitePolicyStrings();
1910 } /* SecurityResetProc */
1914 XSecurityOptions(argc, argv, i)
1919 if (strcmp(argv[i], "-sp") == 0)
1922 SecurityPolicyFile = argv[++i];
1926 } /* XSecurityOptions */
1930 /* SecurityExtensionInit
1937 * Enables the Security extension if possible.
1941 SecurityExtensionInit()
1943 ExtensionEntry *extEntry;
1946 SecurityAuthorizationResType =
1947 CreateNewResourceType(SecurityDeleteAuthorization);
1949 RTEventClient = CreateNewResourceType(
1950 SecurityDeleteAuthorizationEventClient);
1952 if (!SecurityAuthorizationResType || !RTEventClient)
1955 RTEventClient |= RC_NEVERRETAIN;
1957 if (!AddCallback(&ClientStateCallback, SecurityClientStateCallback, NULL))
1960 extEntry = AddExtension(SECURITY_EXTENSION_NAME,
1961 XSecurityNumberEvents, XSecurityNumberErrors,
1962 ProcSecurityDispatch, SProcSecurityDispatch,
1963 SecurityResetProc, StandardMinorOpcode);
1965 SecurityErrorBase = extEntry->errorBase;
1966 SecurityEventBase = extEntry->eventBase;
1968 EventSwapVector[SecurityEventBase + XSecurityAuthorizationRevoked] =
1969 SwapSecurityAuthorizationRevokedEvent;
1971 /* initialize untrusted proc vectors */
1973 for (i = 0; i < 128; i++)
1975 UntrustedProcVector[i] = ProcVector[i];
1976 SwappedUntrustedProcVector[i] = SwappedProcVector[i];
1979 /* make sure insecure extensions are not allowed */
1981 for (i = 128; i < 256; i++)
1983 if (!UntrustedProcVector[i])
1985 UntrustedProcVector[i] = ProcBadRequest;
1986 SwappedUntrustedProcVector[i] = ProcBadRequest;
1990 SecurityLoadPropertyAccessList();
1992 } /* SecurityExtensionInit */