/* Copyright 1996, 1998 The Open Group Permission to use, copy, modify, distribute, and sell this software and its documentation for any purpose is hereby granted without fee, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation. The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of The Open Group shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from The Open Group. */ #ifdef HAVE_DIX_CONFIG_H #include #endif #include #include #include #include "dix/dix_priv.h" #include "dix/registry_priv.h" #include "include/extinit_priv.h" #include "os/audit.h" #include "os/auth.h" #include "scrnintstr.h" #include "inputstr.h" #include "windowstr.h" #include "propertyst.h" #include "colormapst.h" #include "privates.h" #include "xacestr.h" #include "securitysrv.h" #include "extinit.h" #include "protocol-versions.h" /* Extension stuff */ static int SecurityErrorBase; /* first Security error number */ static int SecurityEventBase; /* first Security event number */ RESTYPE SecurityAuthorizationResType; /* resource type for authorizations */ static RESTYPE RTEventClient; static CallbackListPtr SecurityValidateGroupCallback = NULL; /* Private state record */ static DevPrivateKeyRec stateKeyRec; #define stateKey (&stateKeyRec) /* This is what we store as client security state */ typedef struct { unsigned int haveState :1; unsigned int live :1; unsigned int trustLevel :2; XID authId; } SecurityStateRec; /* The only extensions that untrusted clients have access to */ static const char *SecurityTrustedExtensions[] = { "XC-MISC", "BIG-REQUESTS", NULL }; /* * Access modes that untrusted clients are allowed on trusted objects. */ static const Mask SecurityResourceMask = DixGetAttrAccess | DixReceiveAccess | DixListPropAccess | DixGetPropAccess | DixListAccess; static const Mask SecurityWindowExtraMask = DixRemoveAccess; static const Mask SecurityRootWindowExtraMask = DixReceiveAccess | DixSendAccess | DixAddAccess | DixRemoveAccess; static const Mask SecurityDeviceMask = DixGetAttrAccess | DixReceiveAccess | DixGetFocusAccess | DixGrabAccess | DixSetAttrAccess | DixUseAccess; static const Mask SecurityServerMask = DixGetAttrAccess | DixGrabAccess; static const Mask SecurityClientMask = DixGetAttrAccess; /* SecurityAudit * * Arguments: * format is the formatting string to be used to interpret the * remaining arguments. * * Returns: nothing. * * Side Effects: * Writes the message to the log file if security logging is on. */ static void _X_ATTRIBUTE_PRINTF(1, 2) SecurityAudit(const char *format, ...) { va_list args; if (auditTrailLevel < SECURITY_AUDIT_LEVEL) return; va_start(args, format); VAuditF(format, args); va_end(args); } /* SecurityAudit */ /* * Performs a Security permission check. */ static int SecurityDoCheck(SecurityStateRec * subj, SecurityStateRec * obj, Mask requested, Mask allowed) { if (!subj->haveState || !obj->haveState) return Success; if (subj->trustLevel == XSecurityClientTrusted) return Success; if (obj->trustLevel != XSecurityClientTrusted) return Success; if ((requested | allowed) == allowed) return Success; return BadAccess; } /* * Labels initial server objects. */ static void SecurityLabelInitial(void) { SecurityStateRec *state; /* Do the serverClient */ state = dixLookupPrivate(&serverClient->devPrivates, stateKey); state->trustLevel = XSecurityClientTrusted; state->haveState = TRUE; state->live = FALSE; } /* * Looks up a request name */ static inline const char * SecurityLookupRequestName(ClientPtr client) { return LookupRequestName(client->majorOp, client->minorOp); } /* SecurityDeleteAuthorization * * Arguments: * value is the authorization to delete. * id is its resource ID. * * Returns: Success. * * Side Effects: * Frees everything associated with the authorization. */ static int SecurityDeleteAuthorization(void *value, XID id) { SecurityAuthorizationPtr pAuth = (SecurityAuthorizationPtr) value; unsigned short name_len, data_len; const char *name; char *data; int status; int i; OtherClientsPtr pEventClient; /* Remove the auth using the os layer auth manager */ status = AuthorizationFromID(pAuth->id, &name_len, &name, &data_len, &data); assert(status); status = RemoveAuthorization(name_len, name, data_len, data); assert(status); (void) status; /* free the auth timer if there is one */ if (pAuth->timer) TimerFree(pAuth->timer); /* send revoke events */ while ((pEventClient = pAuth->eventClients)) { /* send revocation event event */ xSecurityAuthorizationRevokedEvent are = { .type = SecurityEventBase + XSecurityAuthorizationRevoked, .authId = pAuth->id }; WriteEventsToClient(rClient(pEventClient), 1, (xEvent *) &are); FreeResource(pEventClient->resource, X11_RESTYPE_NONE); } /* kill all clients using this auth */ for (i = 1; i < currentMaxClients; i++) if (clients[i]) { SecurityStateRec *state; state = dixLookupPrivate(&clients[i]->devPrivates, stateKey); if (state->haveState && state->authId == pAuth->id) CloseDownClient(clients[i]); } SecurityAudit("revoked authorization ID %lu\n", (unsigned long)pAuth->id); free(pAuth); return Success; } /* SecurityDeleteAuthorization */ /* resource delete function for RTEventClient */ static int SecurityDeleteAuthorizationEventClient(void *value, XID id) { OtherClientsPtr pEventClient, prev = NULL; SecurityAuthorizationPtr pAuth = (SecurityAuthorizationPtr) value; for (pEventClient = pAuth->eventClients; pEventClient; pEventClient = pEventClient->next) { if (pEventClient->resource == id) { if (prev) prev->next = pEventClient->next; else pAuth->eventClients = pEventClient->next; free(pEventClient); return Success; } prev = pEventClient; } /*NOTREACHED*/ return -1; /* make compiler happy */ } /* SecurityDeleteAuthorizationEventClient */ /* SecurityComputeAuthorizationTimeout * * Arguments: * pAuth is the authorization for which we are computing the timeout * seconds is the number of seconds we want to wait * * Returns: * the number of milliseconds that the auth timer should be set to * * Side Effects: * Sets pAuth->secondsRemaining to any "overflow" amount of time * that didn't fit in 32 bits worth of milliseconds */ static CARD32 SecurityComputeAuthorizationTimeout(SecurityAuthorizationPtr pAuth, unsigned int seconds) { /* maxSecs is the number of full seconds that can be expressed in * 32 bits worth of milliseconds */ CARD32 maxSecs = (CARD32) (~0) / (CARD32) MILLI_PER_SECOND; if (seconds > maxSecs) { /* only come here if we want to wait more than 49 days */ pAuth->secondsRemaining = seconds - maxSecs; return maxSecs * MILLI_PER_SECOND; } else { /* by far the common case */ pAuth->secondsRemaining = 0; return seconds * MILLI_PER_SECOND; } } /* SecurityStartAuthorizationTimer */ /* SecurityAuthorizationExpired * * This function is passed as an argument to TimerSet and gets called from * the timer manager in the os layer when its time is up. * * Arguments: * timer is the timer for this authorization. * time is the current time. * pval is the authorization whose time is up. * * Returns: * A new time delay in milliseconds if the timer should wait some * more, else zero. * * Side Effects: * Frees the authorization resource if the timeout period is really * over, otherwise recomputes pAuth->secondsRemaining. */ static CARD32 SecurityAuthorizationExpired(OsTimerPtr timer, CARD32 time, void *pval) { SecurityAuthorizationPtr pAuth = (SecurityAuthorizationPtr) pval; assert(pAuth->timer == timer); if (pAuth->secondsRemaining) { return SecurityComputeAuthorizationTimeout(pAuth, pAuth->secondsRemaining); } else { FreeResource(pAuth->id, X11_RESTYPE_NONE); return 0; } } /* SecurityAuthorizationExpired */ /* SecurityStartAuthorizationTimer * * Arguments: * pAuth is the authorization whose timer should be started. * * Returns: nothing. * * Side Effects: * A timer is started, set to expire after the timeout period for * this authorization. When it expires, the function * SecurityAuthorizationExpired will be called. */ static void SecurityStartAuthorizationTimer(SecurityAuthorizationPtr pAuth) { pAuth->timer = TimerSet(pAuth->timer, 0, SecurityComputeAuthorizationTimeout(pAuth, pAuth->timeout), SecurityAuthorizationExpired, pAuth); } /* SecurityStartAuthorizationTimer */ /* Proc functions all take a client argument, execute the request in * client->requestBuffer, and return a protocol error status. */ static int ProcSecurityQueryVersion(ClientPtr client) { /* REQUEST(xSecurityQueryVersionReq); */ xSecurityQueryVersionReply rep = { .type = X_Reply, .sequenceNumber = client->sequence, .length = 0, .majorVersion = SERVER_SECURITY_MAJOR_VERSION, .minorVersion = SERVER_SECURITY_MINOR_VERSION }; REQUEST_SIZE_MATCH(xSecurityQueryVersionReq); if (client->swapped) { swaps(&rep.sequenceNumber); swaps(&rep.majorVersion); swaps(&rep.minorVersion); } WriteToClient(client, SIZEOF(xSecurityQueryVersionReply), &rep); return Success; } /* ProcSecurityQueryVersion */ static int SecurityEventSelectForAuthorization(SecurityAuthorizationPtr pAuth, ClientPtr client, Mask mask) { OtherClients *pEventClient; for (pEventClient = pAuth->eventClients; pEventClient; pEventClient = pEventClient->next) { if (SameClient(pEventClient, client)) { if (mask == 0) FreeResource(pEventClient->resource, X11_RESTYPE_NONE); else pEventClient->mask = mask; return Success; } } pEventClient = malloc(sizeof(OtherClients)); if (!pEventClient) return BadAlloc; pEventClient->mask = mask; pEventClient->resource = FakeClientID(client->index); pEventClient->next = pAuth->eventClients; if (!AddResource(pEventClient->resource, RTEventClient, (void *) pAuth)) { free(pEventClient); return BadAlloc; } pAuth->eventClients = pEventClient; return Success; } /* SecurityEventSelectForAuthorization */ static int ProcSecurityGenerateAuthorization(ClientPtr client) { REQUEST(xSecurityGenerateAuthorizationReq); int len; /* request length in CARD32s */ Bool removeAuth = FALSE; /* if bailout, call RemoveAuthorization? */ SecurityAuthorizationPtr pAuth = NULL; /* auth we are creating */ int err; /* error to return from this function */ XID authId; /* authorization ID assigned by os layer */ xSecurityGenerateAuthorizationReply rep; /* reply struct */ unsigned int trustLevel; /* trust level of new auth */ XID group; /* group of new auth */ CARD32 timeout; /* timeout of new auth */ CARD32 *values; /* list of supplied attributes */ char *protoname; /* auth proto name sent in request */ char *protodata; /* auth proto data sent in request */ unsigned int authdata_len; /* # bytes of generated auth data */ char *pAuthdata; /* generated auth data */ Mask eventMask; /* what events on this auth does client want */ /* check request length */ REQUEST_AT_LEAST_SIZE(xSecurityGenerateAuthorizationReq); len = bytes_to_int32(SIZEOF(xSecurityGenerateAuthorizationReq)); len += bytes_to_int32(stuff->nbytesAuthProto); len += bytes_to_int32(stuff->nbytesAuthData); values = ((CARD32 *) stuff) + len; len += Ones(stuff->valueMask); if (client->req_len != len) return BadLength; /* check valuemask */ if (stuff->valueMask & ~XSecurityAllAuthorizationAttributes) { client->errorValue = stuff->valueMask; return BadValue; } /* check timeout */ timeout = 60; if (stuff->valueMask & XSecurityTimeout) { timeout = *values++; } /* check trustLevel */ trustLevel = XSecurityClientUntrusted; if (stuff->valueMask & XSecurityTrustLevel) { trustLevel = *values++; if (trustLevel != XSecurityClientTrusted && trustLevel != XSecurityClientUntrusted) { client->errorValue = trustLevel; return BadValue; } } /* check group */ group = None; if (stuff->valueMask & XSecurityGroup) { group = *values++; if (SecurityValidateGroupCallback) { SecurityValidateGroupInfoRec vgi; vgi.group = group; vgi.valid = FALSE; CallCallbacks(&SecurityValidateGroupCallback, (void *) &vgi); /* if nobody said they recognized it, it's an error */ if (!vgi.valid) { client->errorValue = group; return BadValue; } } } /* check event mask */ eventMask = 0; if (stuff->valueMask & XSecurityEventMask) { eventMask = *values++; if (eventMask & ~XSecurityAllEventMasks) { client->errorValue = eventMask; return BadValue; } } protoname = (char *) &stuff[1]; protodata = protoname + bytes_to_int32(stuff->nbytesAuthProto); /* call os layer to generate the authorization */ authId = GenerateAuthorization(stuff->nbytesAuthProto, protoname, stuff->nbytesAuthData, protodata, &authdata_len, &pAuthdata); if ((XID) ~0L == authId) { err = SecurityErrorBase + XSecurityBadAuthorizationProtocol; goto bailout; } /* now that we've added the auth, remember to remove it if we have to * abort the request for some reason (like allocation failure) */ removeAuth = TRUE; /* associate additional information with this auth ID */ pAuth = malloc(sizeof(SecurityAuthorizationRec)); if (!pAuth) { err = BadAlloc; goto bailout; } /* fill in the auth fields */ pAuth->id = authId; pAuth->timeout = timeout; pAuth->group = group; pAuth->trustLevel = trustLevel; pAuth->refcnt = 0; /* the auth was just created; nobody's using it yet */ pAuth->secondsRemaining = 0; pAuth->timer = NULL; pAuth->eventClients = NULL; /* handle event selection */ if (eventMask) { err = SecurityEventSelectForAuthorization(pAuth, client, eventMask); if (err != Success) goto bailout; } if (!AddResource(authId, SecurityAuthorizationResType, pAuth)) { err = BadAlloc; goto bailout; } /* start the timer ticking */ if (pAuth->timeout != 0) SecurityStartAuthorizationTimer(pAuth); /* tell client the auth id and data */ rep = (xSecurityGenerateAuthorizationReply) { .type = X_Reply, .sequenceNumber = client->sequence, .length = bytes_to_int32(authdata_len), .authId = authId, .dataLength = authdata_len }; if (client->swapped) { swapl(&rep.length); swaps(&rep.sequenceNumber); swapl(&rep.authId); swaps(&rep.dataLength); } WriteToClient(client, SIZEOF(xSecurityGenerateAuthorizationReply), &rep); WriteToClient(client, authdata_len, pAuthdata); SecurityAudit ("client %d generated authorization %lu trust %d timeout %lu group %lu events %lu\n", client->index, (unsigned long)pAuth->id, pAuth->trustLevel, (unsigned long)pAuth->timeout, (unsigned long)pAuth->group, (unsigned long)eventMask); /* the request succeeded; don't call RemoveAuthorization or free pAuth */ return Success; bailout: if (removeAuth) RemoveAuthorization(stuff->nbytesAuthProto, protoname, authdata_len, pAuthdata); free(pAuth); return err; } /* ProcSecurityGenerateAuthorization */ static int ProcSecurityRevokeAuthorization(ClientPtr client) { REQUEST(xSecurityRevokeAuthorizationReq); SecurityAuthorizationPtr pAuth; int rc; REQUEST_SIZE_MATCH(xSecurityRevokeAuthorizationReq); rc = dixLookupResourceByType((void **) &pAuth, stuff->authId, SecurityAuthorizationResType, client, DixDestroyAccess); if (rc != Success) return rc; FreeResource(stuff->authId, X11_RESTYPE_NONE); return Success; } /* ProcSecurityRevokeAuthorization */ static int ProcSecurityDispatch(ClientPtr client) { REQUEST(xReq); switch (stuff->data) { case X_SecurityQueryVersion: return ProcSecurityQueryVersion(client); case X_SecurityGenerateAuthorization: return ProcSecurityGenerateAuthorization(client); case X_SecurityRevokeAuthorization: return ProcSecurityRevokeAuthorization(client); default: return BadRequest; } } /* ProcSecurityDispatch */ static int _X_COLD SProcSecurityQueryVersion(ClientPtr client) { REQUEST(xSecurityQueryVersionReq); swaps(&stuff->length); REQUEST_SIZE_MATCH(xSecurityQueryVersionReq); swaps(&stuff->majorVersion); swaps(&stuff->minorVersion); return ProcSecurityQueryVersion(client); } /* SProcSecurityQueryVersion */ static int _X_COLD SProcSecurityGenerateAuthorization(ClientPtr client) { REQUEST(xSecurityGenerateAuthorizationReq); CARD32 *values; unsigned long nvalues; int values_offset; swaps(&stuff->length); REQUEST_AT_LEAST_SIZE(xSecurityGenerateAuthorizationReq); swaps(&stuff->nbytesAuthProto); swaps(&stuff->nbytesAuthData); swapl(&stuff->valueMask); values_offset = bytes_to_int32(stuff->nbytesAuthProto) + bytes_to_int32(stuff->nbytesAuthData); if (values_offset > stuff->length - bytes_to_int32(sz_xSecurityGenerateAuthorizationReq)) return BadLength; values = (CARD32 *) (&stuff[1]) + values_offset; nvalues = (((CARD32 *) stuff) + stuff->length) - values; SwapLongs(values, nvalues); return ProcSecurityGenerateAuthorization(client); } /* SProcSecurityGenerateAuthorization */ static int _X_COLD SProcSecurityRevokeAuthorization(ClientPtr client) { REQUEST(xSecurityRevokeAuthorizationReq); swaps(&stuff->length); REQUEST_SIZE_MATCH(xSecurityRevokeAuthorizationReq); swapl(&stuff->authId); return ProcSecurityRevokeAuthorization(client); } /* SProcSecurityRevokeAuthorization */ static int _X_COLD SProcSecurityDispatch(ClientPtr client) { REQUEST(xReq); switch (stuff->data) { case X_SecurityQueryVersion: return SProcSecurityQueryVersion(client); case X_SecurityGenerateAuthorization: return SProcSecurityGenerateAuthorization(client); case X_SecurityRevokeAuthorization: return SProcSecurityRevokeAuthorization(client); default: return BadRequest; } } /* SProcSecurityDispatch */ static void _X_COLD SwapSecurityAuthorizationRevokedEvent(xSecurityAuthorizationRevokedEvent * from, xSecurityAuthorizationRevokedEvent * to) { to->type = from->type; to->detail = from->detail; cpswaps(from->sequenceNumber, to->sequenceNumber); cpswapl(from->authId, to->authId); } /* SecurityCheckDeviceAccess * * Arguments: * client is the client attempting to access a device. * dev is the device being accessed. * fromRequest is TRUE if the device access is a direct result of * the client executing some request and FALSE if it is a * result of the server trying to send an event (e.g. KeymapNotify) * to the client. * Returns: * TRUE if the device access should be allowed, else FALSE. * * Side Effects: * An audit message is generated if access is denied. */ static void SecurityDevice(CallbackListPtr *pcbl, void *unused, void *calldata) { XaceDeviceAccessRec *rec = calldata; SecurityStateRec *subj, *obj; Mask requested = rec->access_mode; Mask allowed = SecurityDeviceMask; subj = dixLookupPrivate(&rec->client->devPrivates, stateKey); obj = dixLookupPrivate(&serverClient->devPrivates, stateKey); if (rec->dev != inputInfo.keyboard) /* this extension only supports the core keyboard */ allowed = requested; if (SecurityDoCheck(subj, obj, requested, allowed) != Success) { SecurityAudit("Security denied client %d keyboard access on request " "%s\n", rec->client->index, SecurityLookupRequestName(rec->client)); rec->status = BadAccess; } } /* SecurityResource * * This function gets plugged into client->CheckAccess and is called from * SecurityLookupIDByType/Class to determine if the client can access the * resource. * * Arguments: * client is the client doing the resource access. * id is the resource id. * rtype is its type or class. * access_mode represents the intended use of the resource; see * resource.h. * res is a pointer to the resource structure for this resource. * * Returns: * If access is granted, the value of rval that was passed in, else FALSE. * * Side Effects: * Disallowed resource accesses are audited. */ static void SecurityResource(CallbackListPtr *pcbl, void *unused, void *calldata) { XaceResourceAccessRec *rec = calldata; SecurityStateRec *subj, *obj; int cid = CLIENT_ID(rec->id); Mask requested = rec->access_mode; Mask allowed = SecurityResourceMask; subj = dixLookupPrivate(&rec->client->devPrivates, stateKey); /* disable background None for untrusted windows */ if ((requested & DixCreateAccess) && (rec->rtype == X11_RESTYPE_WINDOW)) if (subj->haveState && subj->trustLevel != XSecurityClientTrusted) ((WindowPtr) rec->res)->forcedBG = TRUE; /* additional permissions for specific resource types */ if (rec->rtype == X11_RESTYPE_WINDOW) allowed |= SecurityWindowExtraMask; /* special checks for server-owned resources */ if (cid == 0) { if (rec->rtype & RC_DRAWABLE) /* additional operations allowed on root windows */ allowed |= SecurityRootWindowExtraMask; else if (rec->rtype == X11_RESTYPE_COLORMAP) /* allow access to default colormaps */ allowed = requested; else /* allow read access to other server-owned resources */ allowed |= DixReadAccess; } if (clients[cid] != NULL) { obj = dixLookupPrivate(&clients[cid]->devPrivates, stateKey); if (SecurityDoCheck(subj, obj, requested, allowed) == Success) return; } SecurityAudit("Security: denied client %d access %lx to resource 0x%lx " "of client %d on request %s\n", rec->client->index, (unsigned long)requested, (unsigned long)rec->id, cid, SecurityLookupRequestName(rec->client)); rec->status = BadAccess; /* deny access */ } static void SecurityExtension(CallbackListPtr *pcbl, void *unused, void *calldata) { XaceExtAccessRec *rec = calldata; SecurityStateRec *subj; int i = 0; subj = dixLookupPrivate(&rec->client->devPrivates, stateKey); if (subj->haveState && subj->trustLevel == XSecurityClientTrusted) return; while (SecurityTrustedExtensions[i]) if (!strcmp(SecurityTrustedExtensions[i++], rec->ext->name)) return; SecurityAudit("Security: denied client %d access to extension " "%s on request %s\n", rec->client->index, rec->ext->name, SecurityLookupRequestName(rec->client)); rec->status = BadAccess; } static void SecurityServer(CallbackListPtr *pcbl, void *unused, void *calldata) { XaceServerAccessRec *rec = calldata; SecurityStateRec *subj, *obj; Mask requested = rec->access_mode; Mask allowed = SecurityServerMask; subj = dixLookupPrivate(&rec->client->devPrivates, stateKey); obj = dixLookupPrivate(&serverClient->devPrivates, stateKey); if (SecurityDoCheck(subj, obj, requested, allowed) != Success) { SecurityAudit("Security: denied client %d access to server " "configuration request %s\n", rec->client->index, SecurityLookupRequestName(rec->client)); rec->status = BadAccess; } } static void SecurityClient(CallbackListPtr *pcbl, void *unused, void *calldata) { XaceClientAccessRec *rec = calldata; SecurityStateRec *subj, *obj; Mask requested = rec->access_mode; Mask allowed = SecurityClientMask; subj = dixLookupPrivate(&rec->client->devPrivates, stateKey); obj = dixLookupPrivate(&rec->target->devPrivates, stateKey); if (SecurityDoCheck(subj, obj, requested, allowed) != Success) { SecurityAudit("Security: denied client %d access to client %d on " "request %s\n", rec->client->index, rec->target->index, SecurityLookupRequestName(rec->client)); rec->status = BadAccess; } } static void SecurityProperty(CallbackListPtr *pcbl, void *unused, void *calldata) { XacePropertyAccessRec *rec = calldata; SecurityStateRec *subj, *obj; ATOM name = (*rec->ppProp)->propertyName; Mask requested = rec->access_mode; Mask allowed = SecurityResourceMask | DixReadAccess; subj = dixLookupPrivate(&rec->client->devPrivates, stateKey); obj = dixLookupPrivate(&wClient(rec->pWin)->devPrivates, stateKey); if (SecurityDoCheck(subj, obj, requested, allowed) != Success) { SecurityAudit("Security: denied client %d access to property %s " "(atom 0x%x) window 0x%lx of client %d on request %s\n", rec->client->index, NameForAtom(name), name, (unsigned long)rec->pWin->drawable.id, wClient(rec->pWin)->index, SecurityLookupRequestName(rec->client)); rec->status = BadAccess; } } static void SecuritySend(CallbackListPtr *pcbl, void *unused, void *calldata) { XaceSendAccessRec *rec = calldata; SecurityStateRec *subj, *obj; if (rec->client) { int i; subj = dixLookupPrivate(&rec->client->devPrivates, stateKey); obj = dixLookupPrivate(&wClient(rec->pWin)->devPrivates, stateKey); if (SecurityDoCheck(subj, obj, DixSendAccess, 0) == Success) return; for (i = 0; i < rec->count; i++) if (rec->events[i].u.u.type != UnmapNotify && rec->events[i].u.u.type != ConfigureRequest && rec->events[i].u.u.type != ClientMessage) { SecurityAudit("Security: denied client %d from sending event " "of type %s to window 0x%lx of client %d\n", rec->client->index, LookupEventName(rec->events[i].u.u.type), (unsigned long)rec->pWin->drawable.id, wClient(rec->pWin)->index); rec->status = BadAccess; return; } } } static void SecurityReceive(CallbackListPtr *pcbl, void *unused, void *calldata) { XaceReceiveAccessRec *rec = calldata; SecurityStateRec *subj, *obj; subj = dixLookupPrivate(&rec->client->devPrivates, stateKey); obj = dixLookupPrivate(&wClient(rec->pWin)->devPrivates, stateKey); if (SecurityDoCheck(subj, obj, DixReceiveAccess, 0) == Success) return; SecurityAudit("Security: denied client %d from receiving an event " "sent to window 0x%lx of client %d\n", rec->client->index, (unsigned long)rec->pWin->drawable.id, wClient(rec->pWin)->index); rec->status = BadAccess; } /* SecurityClientStateCallback * * Arguments: * pcbl is &ClientStateCallback. * nullata is NULL. * calldata is a pointer to a NewClientInfoRec (include/dixstruct.h) * which contains information about client state changes. * * Returns: nothing. * * Side Effects: * * If a new client is connecting, its authorization ID is copied to * client->authID. If this is a generated authorization, its reference * count is bumped, its timer is cancelled if it was running, and its * trustlevel is copied to TRUSTLEVEL(client). * * If a client is disconnecting and the client was using a generated * authorization, the authorization's reference count is decremented, and * if it is now zero, the timer for this authorization is started. */ static void SecurityClientState(CallbackListPtr *pcbl, void *unused, void *calldata) { NewClientInfoRec *pci = calldata; SecurityStateRec *state; SecurityAuthorizationPtr pAuth; int rc; state = dixLookupPrivate(&pci->client->devPrivates, stateKey); switch (pci->client->clientState) { case ClientStateInitial: state->trustLevel = XSecurityClientTrusted; state->authId = None; state->haveState = TRUE; state->live = FALSE; break; case ClientStateRunning: state->authId = AuthorizationIDOfClient(pci->client); rc = dixLookupResourceByType((void **) &pAuth, state->authId, SecurityAuthorizationResType, serverClient, DixGetAttrAccess); if (rc == Success) { /* it is a generated authorization */ pAuth->refcnt++; state->live = TRUE; if (pAuth->refcnt == 1 && pAuth->timer) TimerCancel(pAuth->timer); state->trustLevel = pAuth->trustLevel; } break; case ClientStateGone: case ClientStateRetained: rc = dixLookupResourceByType((void **) &pAuth, state->authId, SecurityAuthorizationResType, serverClient, DixGetAttrAccess); if (rc == Success && state->live) { /* it is a generated authorization */ pAuth->refcnt--; state->live = FALSE; if (pAuth->refcnt == 0) SecurityStartAuthorizationTimer(pAuth); } break; default: break; } } /* SecurityResetProc * * Arguments: * extEntry is the extension information for the security extension. * * Returns: nothing. * * Side Effects: * Performs any cleanup needed by Security at server shutdown time. */ static void SecurityResetProc(ExtensionEntry * extEntry) { /* Unregister callbacks */ DeleteCallback(&ClientStateCallback, SecurityClientState, NULL); XaceDeleteCallback(XACE_EXT_DISPATCH, SecurityExtension, NULL); XaceDeleteCallback(XACE_RESOURCE_ACCESS, SecurityResource, NULL); XaceDeleteCallback(XACE_DEVICE_ACCESS, SecurityDevice, NULL); XaceDeleteCallback(XACE_PROPERTY_ACCESS, SecurityProperty, NULL); XaceDeleteCallback(XACE_SEND_ACCESS, SecuritySend, NULL); XaceDeleteCallback(XACE_RECEIVE_ACCESS, SecurityReceive, NULL); XaceDeleteCallback(XACE_CLIENT_ACCESS, SecurityClient, NULL); XaceDeleteCallback(XACE_EXT_ACCESS, SecurityExtension, NULL); XaceDeleteCallback(XACE_SERVER_ACCESS, SecurityServer, NULL); } /* SecurityExtensionInit * * Arguments: none. * * Returns: nothing. * * Side Effects: * Enables the Security extension if possible. */ void SecurityExtensionInit(void) { ExtensionEntry *extEntry; int ret = TRUE; SecurityAuthorizationResType = CreateNewResourceType(SecurityDeleteAuthorization, "SecurityAuthorization"); RTEventClient = CreateNewResourceType(SecurityDeleteAuthorizationEventClient, "SecurityEventClient"); if (!SecurityAuthorizationResType || !RTEventClient) return; RTEventClient |= RC_NEVERRETAIN; /* Allocate the private storage */ if (!dixRegisterPrivateKey (stateKey, PRIVATE_CLIENT, sizeof(SecurityStateRec))) FatalError("SecurityExtensionSetup: Can't allocate client private.\n"); /* Register callbacks */ ret &= AddCallback(&ClientStateCallback, SecurityClientState, NULL); ret &= XaceRegisterCallback(XACE_EXT_DISPATCH, SecurityExtension, NULL); ret &= XaceRegisterCallback(XACE_RESOURCE_ACCESS, SecurityResource, NULL); ret &= XaceRegisterCallback(XACE_DEVICE_ACCESS, SecurityDevice, NULL); ret &= XaceRegisterCallback(XACE_PROPERTY_ACCESS, SecurityProperty, NULL); ret &= XaceRegisterCallback(XACE_SEND_ACCESS, SecuritySend, NULL); ret &= XaceRegisterCallback(XACE_RECEIVE_ACCESS, SecurityReceive, NULL); ret &= XaceRegisterCallback(XACE_CLIENT_ACCESS, SecurityClient, NULL); ret &= XaceRegisterCallback(XACE_EXT_ACCESS, SecurityExtension, NULL); ret &= XaceRegisterCallback(XACE_SERVER_ACCESS, SecurityServer, NULL); if (!ret) FatalError("SecurityExtensionSetup: Failed to register callbacks\n"); /* Add extension to server */ extEntry = AddExtension(SECURITY_EXTENSION_NAME, XSecurityNumberEvents, XSecurityNumberErrors, ProcSecurityDispatch, SProcSecurityDispatch, SecurityResetProc, StandardMinorOpcode); SecurityErrorBase = extEntry->errorBase; SecurityEventBase = extEntry->eventBase; EventSwapVector[SecurityEventBase + XSecurityAuthorizationRevoked] = (EventSwapPtr) SwapSecurityAuthorizationRevokedEvent; SetResourceTypeErrorValue(SecurityAuthorizationResType, SecurityErrorBase + XSecurityBadAuthorization); /* Label objects that were created before we could register ourself */ SecurityLabelInitial(); }