Commit | Line | Data |
---|---|---|
a09e091a JB |
1 | /* |
2 | ||
3 | Copyright 1996, 1998 The Open Group | |
4 | ||
5 | Permission to use, copy, modify, distribute, and sell this software and its | |
6 | documentation for any purpose is hereby granted without fee, provided that | |
7 | the above copyright notice appear in all copies and that both that | |
8 | copyright notice and this permission notice appear in supporting | |
9 | documentation. | |
10 | ||
11 | The above copyright notice and this permission notice shall be included in | |
12 | all copies or substantial portions of the Software. | |
13 | ||
14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
17 | OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN | |
18 | AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | |
19 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |
20 | ||
21 | Except as contained in this notice, the name of The Open Group shall not be | |
22 | used in advertising or otherwise to promote the sale, use or other dealings | |
23 | in 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 "scrnintstr.h" | |
32 | #include "inputstr.h" | |
33 | #include "windowstr.h" | |
34 | #include "propertyst.h" | |
35 | #include "colormapst.h" | |
36 | #include "privates.h" | |
37 | #include "registry.h" | |
38 | #include "xacestr.h" | |
39 | #include "securitysrv.h" | |
40 | #include <X11/extensions/securproto.h> | |
41 | #include "extinit.h" | |
42 | #include "protocol-versions.h" | |
43 | ||
44 | /* Extension stuff */ | |
45 | static int SecurityErrorBase; /* first Security error number */ | |
46 | static int SecurityEventBase; /* first Security event number */ | |
47 | ||
48 | RESTYPE SecurityAuthorizationResType; /* resource type for authorizations */ | |
49 | static RESTYPE RTEventClient; | |
50 | ||
51 | static CallbackListPtr SecurityValidateGroupCallback = NULL; | |
52 | ||
53 | /* Private state record */ | |
54 | static DevPrivateKeyRec stateKeyRec; | |
55 | ||
56 | #define stateKey (&stateKeyRec) | |
57 | ||
58 | /* This is what we store as client security state */ | |
59 | typedef struct { | |
60 | unsigned int haveState :1; | |
61 | unsigned int live :1; | |
62 | unsigned int trustLevel :2; | |
63 | XID authId; | |
64 | } SecurityStateRec; | |
65 | ||
66 | /* Extensions that untrusted clients shouldn't have access to */ | |
67 | static const char *SecurityTrustedExtensions[] = { | |
68 | "XC-MISC", | |
69 | "BIG-REQUESTS", | |
70 | "XpExtension", | |
71 | NULL | |
72 | }; | |
73 | ||
74 | /* | |
75 | * Access modes that untrusted clients are allowed on trusted objects. | |
76 | */ | |
77 | static const Mask SecurityResourceMask = | |
78 | DixGetAttrAccess | DixReceiveAccess | DixListPropAccess | | |
79 | DixGetPropAccess | DixListAccess; | |
80 | static const Mask SecurityWindowExtraMask = DixRemoveAccess; | |
81 | static const Mask SecurityRootWindowExtraMask = | |
82 | DixReceiveAccess | DixSendAccess | DixAddAccess | DixRemoveAccess; | |
83 | static const Mask SecurityDeviceMask = | |
84 | DixGetAttrAccess | DixReceiveAccess | DixGetFocusAccess | | |
85 | DixGrabAccess | DixSetAttrAccess | DixUseAccess; | |
86 | static const Mask SecurityServerMask = DixGetAttrAccess | DixGrabAccess; | |
87 | static const Mask SecurityClientMask = DixGetAttrAccess; | |
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 | ||
101 | static void | |
102 | _X_ATTRIBUTE_PRINTF(1, 2) | |
103 | SecurityAudit(const char *format, ...) | |
104 | { | |
105 | va_list args; | |
106 | ||
107 | if (auditTrailLevel < SECURITY_AUDIT_LEVEL) | |
108 | return; | |
109 | va_start(args, format); | |
110 | VAuditF(format, args); | |
111 | va_end(args); | |
112 | } /* SecurityAudit */ | |
113 | ||
114 | /* | |
115 | * Performs a Security permission check. | |
116 | */ | |
117 | static int | |
118 | SecurityDoCheck(SecurityStateRec * subj, SecurityStateRec * obj, | |
119 | Mask requested, Mask allowed) | |
120 | { | |
121 | if (!subj->haveState || !obj->haveState) | |
122 | return Success; | |
123 | if (subj->trustLevel == XSecurityClientTrusted) | |
124 | return Success; | |
125 | if (obj->trustLevel != XSecurityClientTrusted) | |
126 | return Success; | |
127 | if ((requested | allowed) == allowed) | |
128 | return Success; | |
129 | ||
130 | return BadAccess; | |
131 | } | |
132 | ||
133 | /* | |
134 | * Labels initial server objects. | |
135 | */ | |
136 | static void | |
137 | SecurityLabelInitial(void) | |
138 | { | |
139 | SecurityStateRec *state; | |
140 | ||
141 | /* Do the serverClient */ | |
142 | state = dixLookupPrivate(&serverClient->devPrivates, stateKey); | |
143 | state->trustLevel = XSecurityClientTrusted; | |
144 | state->haveState = TRUE; | |
145 | state->live = FALSE; | |
146 | } | |
147 | ||
148 | /* | |
149 | * Looks up a request name | |
150 | */ | |
151 | static _X_INLINE const char * | |
152 | SecurityLookupRequestName(ClientPtr client) | |
153 | { | |
154 | return LookupRequestName(client->majorOp, client->minorOp); | |
155 | } | |
156 | ||
157 | /* SecurityDeleteAuthorization | |
158 | * | |
159 | * Arguments: | |
160 | * value is the authorization to delete. | |
161 | * id is its resource ID. | |
162 | * | |
163 | * Returns: Success. | |
164 | * | |
165 | * Side Effects: | |
166 | * Frees everything associated with the authorization. | |
167 | */ | |
168 | ||
169 | static int | |
170 | SecurityDeleteAuthorization(pointer value, XID id) | |
171 | { | |
172 | SecurityAuthorizationPtr pAuth = (SecurityAuthorizationPtr) value; | |
173 | unsigned short name_len, data_len; | |
174 | const char *name; | |
175 | char *data; | |
176 | int status; | |
177 | int i; | |
178 | OtherClientsPtr pEventClient; | |
179 | ||
180 | /* Remove the auth using the os layer auth manager */ | |
181 | ||
182 | status = AuthorizationFromID(pAuth->id, &name_len, &name, &data_len, &data); | |
183 | assert(status); | |
184 | status = RemoveAuthorization(name_len, name, data_len, data); | |
185 | assert(status); | |
186 | (void) status; | |
187 | ||
188 | /* free the auth timer if there is one */ | |
189 | ||
190 | if (pAuth->timer) | |
191 | TimerFree(pAuth->timer); | |
192 | ||
193 | /* send revoke events */ | |
194 | ||
195 | while ((pEventClient = pAuth->eventClients)) { | |
196 | /* send revocation event event */ | |
197 | xSecurityAuthorizationRevokedEvent are = { | |
198 | .type = SecurityEventBase + XSecurityAuthorizationRevoked, | |
199 | .authId = pAuth->id | |
200 | }; | |
201 | WriteEventsToClient(rClient(pEventClient), 1, (xEvent *) &are); | |
202 | FreeResource(pEventClient->resource, RT_NONE); | |
203 | } | |
204 | ||
205 | /* kill all clients using this auth */ | |
206 | ||
207 | for (i = 1; i < currentMaxClients; i++) | |
208 | if (clients[i]) { | |
209 | SecurityStateRec *state; | |
210 | ||
211 | state = dixLookupPrivate(&clients[i]->devPrivates, stateKey); | |
212 | if (state->haveState && state->authId == pAuth->id) | |
213 | CloseDownClient(clients[i]); | |
214 | } | |
215 | ||
216 | SecurityAudit("revoked authorization ID %d\n", pAuth->id); | |
217 | free(pAuth); | |
218 | return Success; | |
219 | ||
220 | } /* SecurityDeleteAuthorization */ | |
221 | ||
222 | /* resource delete function for RTEventClient */ | |
223 | static int | |
224 | SecurityDeleteAuthorizationEventClient(pointer value, XID id) | |
225 | { | |
226 | OtherClientsPtr pEventClient, prev = NULL; | |
227 | SecurityAuthorizationPtr pAuth = (SecurityAuthorizationPtr) value; | |
228 | ||
229 | for (pEventClient = pAuth->eventClients; | |
230 | pEventClient; pEventClient = pEventClient->next) { | |
231 | if (pEventClient->resource == id) { | |
232 | if (prev) | |
233 | prev->next = pEventClient->next; | |
234 | else | |
235 | pAuth->eventClients = pEventClient->next; | |
236 | free(pEventClient); | |
237 | return Success; | |
238 | } | |
239 | prev = pEventClient; | |
240 | } | |
241 | /*NOTREACHED*/ return -1; /* make compiler happy */ | |
242 | } /* SecurityDeleteAuthorizationEventClient */ | |
243 | ||
244 | /* SecurityComputeAuthorizationTimeout | |
245 | * | |
246 | * Arguments: | |
247 | * pAuth is the authorization for which we are computing the timeout | |
248 | * seconds is the number of seconds we want to wait | |
249 | * | |
250 | * Returns: | |
251 | * the number of milliseconds that the auth timer should be set to | |
252 | * | |
253 | * Side Effects: | |
254 | * Sets pAuth->secondsRemaining to any "overflow" amount of time | |
255 | * that didn't fit in 32 bits worth of milliseconds | |
256 | */ | |
257 | ||
258 | static CARD32 | |
259 | SecurityComputeAuthorizationTimeout(SecurityAuthorizationPtr pAuth, | |
260 | unsigned int seconds) | |
261 | { | |
262 | /* maxSecs is the number of full seconds that can be expressed in | |
263 | * 32 bits worth of milliseconds | |
264 | */ | |
265 | CARD32 maxSecs = (CARD32) (~0) / (CARD32) MILLI_PER_SECOND; | |
266 | ||
267 | if (seconds > maxSecs) { /* only come here if we want to wait more than 49 days */ | |
268 | pAuth->secondsRemaining = seconds - maxSecs; | |
269 | return maxSecs * MILLI_PER_SECOND; | |
270 | } | |
271 | else { /* by far the common case */ | |
272 | pAuth->secondsRemaining = 0; | |
273 | return seconds * MILLI_PER_SECOND; | |
274 | } | |
275 | } /* SecurityStartAuthorizationTimer */ | |
276 | ||
277 | /* SecurityAuthorizationExpired | |
278 | * | |
279 | * This function is passed as an argument to TimerSet and gets called from | |
280 | * the timer manager in the os layer when its time is up. | |
281 | * | |
282 | * Arguments: | |
283 | * timer is the timer for this authorization. | |
284 | * time is the current time. | |
285 | * pval is the authorization whose time is up. | |
286 | * | |
287 | * Returns: | |
288 | * A new time delay in milliseconds if the timer should wait some | |
289 | * more, else zero. | |
290 | * | |
291 | * Side Effects: | |
292 | * Frees the authorization resource if the timeout period is really | |
293 | * over, otherwise recomputes pAuth->secondsRemaining. | |
294 | */ | |
295 | ||
296 | static CARD32 | |
297 | SecurityAuthorizationExpired(OsTimerPtr timer, CARD32 time, pointer pval) | |
298 | { | |
299 | SecurityAuthorizationPtr pAuth = (SecurityAuthorizationPtr) pval; | |
300 | ||
301 | assert(pAuth->timer == timer); | |
302 | ||
303 | if (pAuth->secondsRemaining) { | |
304 | return SecurityComputeAuthorizationTimeout(pAuth, | |
305 | pAuth->secondsRemaining); | |
306 | } | |
307 | else { | |
308 | FreeResource(pAuth->id, RT_NONE); | |
309 | return 0; | |
310 | } | |
311 | } /* SecurityAuthorizationExpired */ | |
312 | ||
313 | /* SecurityStartAuthorizationTimer | |
314 | * | |
315 | * Arguments: | |
316 | * pAuth is the authorization whose timer should be started. | |
317 | * | |
318 | * Returns: nothing. | |
319 | * | |
320 | * Side Effects: | |
321 | * A timer is started, set to expire after the timeout period for | |
322 | * this authorization. When it expires, the function | |
323 | * SecurityAuthorizationExpired will be called. | |
324 | */ | |
325 | ||
326 | static void | |
327 | SecurityStartAuthorizationTimer(SecurityAuthorizationPtr pAuth) | |
328 | { | |
329 | pAuth->timer = TimerSet(pAuth->timer, 0, | |
330 | SecurityComputeAuthorizationTimeout(pAuth, | |
331 | pAuth->timeout), | |
332 | SecurityAuthorizationExpired, pAuth); | |
333 | } /* SecurityStartAuthorizationTimer */ | |
334 | ||
335 | /* Proc functions all take a client argument, execute the request in | |
336 | * client->requestBuffer, and return a protocol error status. | |
337 | */ | |
338 | ||
339 | static int | |
340 | ProcSecurityQueryVersion(ClientPtr client) | |
341 | { | |
342 | /* REQUEST(xSecurityQueryVersionReq); */ | |
343 | xSecurityQueryVersionReply rep = { | |
344 | .type = X_Reply, | |
345 | .sequenceNumber = client->sequence, | |
346 | .length = 0, | |
347 | .majorVersion = SERVER_SECURITY_MAJOR_VERSION, | |
348 | .minorVersion = SERVER_SECURITY_MINOR_VERSION | |
349 | }; | |
350 | ||
351 | REQUEST_SIZE_MATCH(xSecurityQueryVersionReq); | |
352 | ||
353 | if (client->swapped) { | |
354 | swaps(&rep.sequenceNumber); | |
355 | swaps(&rep.majorVersion); | |
356 | swaps(&rep.minorVersion); | |
357 | } | |
358 | WriteToClient(client, SIZEOF(xSecurityQueryVersionReply), &rep); | |
359 | return Success; | |
360 | } /* ProcSecurityQueryVersion */ | |
361 | ||
362 | static int | |
363 | SecurityEventSelectForAuthorization(SecurityAuthorizationPtr pAuth, | |
364 | ClientPtr client, Mask mask) | |
365 | { | |
366 | OtherClients *pEventClient; | |
367 | ||
368 | for (pEventClient = pAuth->eventClients; | |
369 | pEventClient; pEventClient = pEventClient->next) { | |
370 | if (SameClient(pEventClient, client)) { | |
371 | if (mask == 0) | |
372 | FreeResource(pEventClient->resource, RT_NONE); | |
373 | else | |
374 | pEventClient->mask = mask; | |
375 | return Success; | |
376 | } | |
377 | } | |
378 | ||
379 | pEventClient = malloc(sizeof(OtherClients)); | |
380 | if (!pEventClient) | |
381 | return BadAlloc; | |
382 | pEventClient->mask = mask; | |
383 | pEventClient->resource = FakeClientID(client->index); | |
384 | pEventClient->next = pAuth->eventClients; | |
385 | if (!AddResource(pEventClient->resource, RTEventClient, (pointer) pAuth)) { | |
386 | free(pEventClient); | |
387 | return BadAlloc; | |
388 | } | |
389 | pAuth->eventClients = pEventClient; | |
390 | ||
391 | return Success; | |
392 | } /* SecurityEventSelectForAuthorization */ | |
393 | ||
394 | static int | |
395 | ProcSecurityGenerateAuthorization(ClientPtr client) | |
396 | { | |
397 | REQUEST(xSecurityGenerateAuthorizationReq); | |
398 | int len; /* request length in CARD32s */ | |
399 | Bool removeAuth = FALSE; /* if bailout, call RemoveAuthorization? */ | |
400 | SecurityAuthorizationPtr pAuth = NULL; /* auth we are creating */ | |
401 | int err; /* error to return from this function */ | |
402 | XID authId; /* authorization ID assigned by os layer */ | |
403 | xSecurityGenerateAuthorizationReply rep; /* reply struct */ | |
404 | unsigned int trustLevel; /* trust level of new auth */ | |
405 | XID group; /* group of new auth */ | |
406 | CARD32 timeout; /* timeout of new auth */ | |
407 | CARD32 *values; /* list of supplied attributes */ | |
408 | char *protoname; /* auth proto name sent in request */ | |
409 | char *protodata; /* auth proto data sent in request */ | |
410 | unsigned int authdata_len; /* # bytes of generated auth data */ | |
411 | char *pAuthdata; /* generated auth data */ | |
412 | Mask eventMask; /* what events on this auth does client want */ | |
413 | ||
414 | /* check request length */ | |
415 | ||
416 | REQUEST_AT_LEAST_SIZE(xSecurityGenerateAuthorizationReq); | |
417 | len = bytes_to_int32(SIZEOF(xSecurityGenerateAuthorizationReq)); | |
418 | len += bytes_to_int32(stuff->nbytesAuthProto); | |
419 | len += bytes_to_int32(stuff->nbytesAuthData); | |
420 | values = ((CARD32 *) stuff) + len; | |
421 | len += Ones(stuff->valueMask); | |
422 | if (client->req_len != len) | |
423 | return BadLength; | |
424 | ||
425 | /* check valuemask */ | |
426 | if (stuff->valueMask & ~XSecurityAllAuthorizationAttributes) { | |
427 | client->errorValue = stuff->valueMask; | |
428 | return BadValue; | |
429 | } | |
430 | ||
431 | /* check timeout */ | |
432 | timeout = 60; | |
433 | if (stuff->valueMask & XSecurityTimeout) { | |
434 | timeout = *values++; | |
435 | } | |
436 | ||
437 | /* check trustLevel */ | |
438 | trustLevel = XSecurityClientUntrusted; | |
439 | if (stuff->valueMask & XSecurityTrustLevel) { | |
440 | trustLevel = *values++; | |
441 | if (trustLevel != XSecurityClientTrusted && | |
442 | trustLevel != XSecurityClientUntrusted) { | |
443 | client->errorValue = trustLevel; | |
444 | return BadValue; | |
445 | } | |
446 | } | |
447 | ||
448 | /* check group */ | |
449 | group = None; | |
450 | if (stuff->valueMask & XSecurityGroup) { | |
451 | group = *values++; | |
452 | if (SecurityValidateGroupCallback) { | |
453 | SecurityValidateGroupInfoRec vgi; | |
454 | ||
455 | vgi.group = group; | |
456 | vgi.valid = FALSE; | |
457 | CallCallbacks(&SecurityValidateGroupCallback, (pointer) &vgi); | |
458 | ||
459 | /* if nobody said they recognized it, it's an error */ | |
460 | ||
461 | if (!vgi.valid) { | |
462 | client->errorValue = group; | |
463 | return BadValue; | |
464 | } | |
465 | } | |
466 | } | |
467 | ||
468 | /* check event mask */ | |
469 | eventMask = 0; | |
470 | if (stuff->valueMask & XSecurityEventMask) { | |
471 | eventMask = *values++; | |
472 | if (eventMask & ~XSecurityAllEventMasks) { | |
473 | client->errorValue = eventMask; | |
474 | return BadValue; | |
475 | } | |
476 | } | |
477 | ||
478 | protoname = (char *) &stuff[1]; | |
479 | protodata = protoname + bytes_to_int32(stuff->nbytesAuthProto); | |
480 | ||
481 | /* call os layer to generate the authorization */ | |
482 | ||
483 | authId = GenerateAuthorization(stuff->nbytesAuthProto, protoname, | |
484 | stuff->nbytesAuthData, protodata, | |
485 | &authdata_len, &pAuthdata); | |
486 | if ((XID) ~0L == authId) { | |
487 | err = SecurityErrorBase + XSecurityBadAuthorizationProtocol; | |
488 | goto bailout; | |
489 | } | |
490 | ||
491 | /* now that we've added the auth, remember to remove it if we have to | |
492 | * abort the request for some reason (like allocation failure) | |
493 | */ | |
494 | removeAuth = TRUE; | |
495 | ||
496 | /* associate additional information with this auth ID */ | |
497 | ||
498 | pAuth = malloc(sizeof(SecurityAuthorizationRec)); | |
499 | if (!pAuth) { | |
500 | err = BadAlloc; | |
501 | goto bailout; | |
502 | } | |
503 | ||
504 | /* fill in the auth fields */ | |
505 | ||
506 | pAuth->id = authId; | |
507 | pAuth->timeout = timeout; | |
508 | pAuth->group = group; | |
509 | pAuth->trustLevel = trustLevel; | |
510 | pAuth->refcnt = 0; /* the auth was just created; nobody's using it yet */ | |
511 | pAuth->secondsRemaining = 0; | |
512 | pAuth->timer = NULL; | |
513 | pAuth->eventClients = NULL; | |
514 | ||
515 | /* handle event selection */ | |
516 | if (eventMask) { | |
517 | err = SecurityEventSelectForAuthorization(pAuth, client, eventMask); | |
518 | if (err != Success) | |
519 | goto bailout; | |
520 | } | |
521 | ||
522 | if (!AddResource(authId, SecurityAuthorizationResType, pAuth)) { | |
523 | err = BadAlloc; | |
524 | goto bailout; | |
525 | } | |
526 | ||
527 | /* start the timer ticking */ | |
528 | ||
529 | if (pAuth->timeout != 0) | |
530 | SecurityStartAuthorizationTimer(pAuth); | |
531 | ||
532 | /* tell client the auth id and data */ | |
533 | ||
534 | rep = (xSecurityGenerateAuthorizationReply) { | |
535 | .type = X_Reply, | |
536 | .sequenceNumber = client->sequence, | |
537 | .length = bytes_to_int32(authdata_len), | |
538 | .authId = authId, | |
539 | .dataLength = authdata_len | |
540 | }; | |
541 | ||
542 | if (client->swapped) { | |
543 | swapl(&rep.length); | |
544 | swaps(&rep.sequenceNumber); | |
545 | swapl(&rep.authId); | |
546 | swaps(&rep.dataLength); | |
547 | } | |
548 | ||
549 | WriteToClient(client, SIZEOF(xSecurityGenerateAuthorizationReply), &rep); | |
550 | WriteToClient(client, authdata_len, pAuthdata); | |
551 | ||
552 | SecurityAudit | |
553 | ("client %d generated authorization %d trust %d timeout %d group %d events %d\n", | |
554 | client->index, pAuth->id, pAuth->trustLevel, pAuth->timeout, | |
555 | pAuth->group, eventMask); | |
556 | ||
557 | /* the request succeeded; don't call RemoveAuthorization or free pAuth */ | |
558 | return Success; | |
559 | ||
560 | bailout: | |
561 | if (removeAuth) | |
562 | RemoveAuthorization(stuff->nbytesAuthProto, protoname, | |
563 | authdata_len, pAuthdata); | |
564 | free(pAuth); | |
565 | return err; | |
566 | ||
567 | } /* ProcSecurityGenerateAuthorization */ | |
568 | ||
569 | static int | |
570 | ProcSecurityRevokeAuthorization(ClientPtr client) | |
571 | { | |
572 | REQUEST(xSecurityRevokeAuthorizationReq); | |
573 | SecurityAuthorizationPtr pAuth; | |
574 | int rc; | |
575 | ||
576 | REQUEST_SIZE_MATCH(xSecurityRevokeAuthorizationReq); | |
577 | ||
578 | rc = dixLookupResourceByType((pointer *) &pAuth, stuff->authId, | |
579 | SecurityAuthorizationResType, client, | |
580 | DixDestroyAccess); | |
581 | if (rc != Success) | |
582 | return rc; | |
583 | ||
584 | FreeResource(stuff->authId, RT_NONE); | |
585 | return Success; | |
586 | } /* ProcSecurityRevokeAuthorization */ | |
587 | ||
588 | static int | |
589 | ProcSecurityDispatch(ClientPtr client) | |
590 | { | |
591 | REQUEST(xReq); | |
592 | ||
593 | switch (stuff->data) { | |
594 | case X_SecurityQueryVersion: | |
595 | return ProcSecurityQueryVersion(client); | |
596 | case X_SecurityGenerateAuthorization: | |
597 | return ProcSecurityGenerateAuthorization(client); | |
598 | case X_SecurityRevokeAuthorization: | |
599 | return ProcSecurityRevokeAuthorization(client); | |
600 | default: | |
601 | return BadRequest; | |
602 | } | |
603 | } /* ProcSecurityDispatch */ | |
604 | ||
605 | static int | |
606 | SProcSecurityQueryVersion(ClientPtr client) | |
607 | { | |
608 | REQUEST(xSecurityQueryVersionReq); | |
609 | ||
610 | swaps(&stuff->length); | |
611 | REQUEST_SIZE_MATCH(xSecurityQueryVersionReq); | |
612 | swaps(&stuff->majorVersion); | |
613 | swaps(&stuff->minorVersion); | |
614 | return ProcSecurityQueryVersion(client); | |
615 | } /* SProcSecurityQueryVersion */ | |
616 | ||
617 | static int | |
618 | SProcSecurityGenerateAuthorization(ClientPtr client) | |
619 | { | |
620 | REQUEST(xSecurityGenerateAuthorizationReq); | |
621 | CARD32 *values; | |
622 | unsigned long nvalues; | |
623 | int values_offset; | |
624 | ||
625 | swaps(&stuff->length); | |
626 | REQUEST_AT_LEAST_SIZE(xSecurityGenerateAuthorizationReq); | |
627 | swaps(&stuff->nbytesAuthProto); | |
628 | swaps(&stuff->nbytesAuthData); | |
629 | swapl(&stuff->valueMask); | |
630 | values_offset = bytes_to_int32(stuff->nbytesAuthProto) + | |
631 | bytes_to_int32(stuff->nbytesAuthData); | |
632 | if (values_offset > | |
633 | stuff->length - bytes_to_int32(sz_xSecurityGenerateAuthorizationReq)) | |
634 | return BadLength; | |
635 | values = (CARD32 *) (&stuff[1]) + values_offset; | |
636 | nvalues = (((CARD32 *) stuff) + stuff->length) - values; | |
637 | SwapLongs(values, nvalues); | |
638 | return ProcSecurityGenerateAuthorization(client); | |
639 | } /* SProcSecurityGenerateAuthorization */ | |
640 | ||
641 | static int | |
642 | SProcSecurityRevokeAuthorization(ClientPtr client) | |
643 | { | |
644 | REQUEST(xSecurityRevokeAuthorizationReq); | |
645 | ||
646 | swaps(&stuff->length); | |
647 | REQUEST_SIZE_MATCH(xSecurityRevokeAuthorizationReq); | |
648 | swapl(&stuff->authId); | |
649 | return ProcSecurityRevokeAuthorization(client); | |
650 | } /* SProcSecurityRevokeAuthorization */ | |
651 | ||
652 | static int | |
653 | SProcSecurityDispatch(ClientPtr client) | |
654 | { | |
655 | REQUEST(xReq); | |
656 | ||
657 | switch (stuff->data) { | |
658 | case X_SecurityQueryVersion: | |
659 | return SProcSecurityQueryVersion(client); | |
660 | case X_SecurityGenerateAuthorization: | |
661 | return SProcSecurityGenerateAuthorization(client); | |
662 | case X_SecurityRevokeAuthorization: | |
663 | return SProcSecurityRevokeAuthorization(client); | |
664 | default: | |
665 | return BadRequest; | |
666 | } | |
667 | } /* SProcSecurityDispatch */ | |
668 | ||
669 | static void | |
670 | SwapSecurityAuthorizationRevokedEvent(xSecurityAuthorizationRevokedEvent * from, | |
671 | xSecurityAuthorizationRevokedEvent * to) | |
672 | { | |
673 | to->type = from->type; | |
674 | to->detail = from->detail; | |
675 | cpswaps(from->sequenceNumber, to->sequenceNumber); | |
676 | cpswapl(from->authId, to->authId); | |
677 | } | |
678 | ||
679 | /* SecurityCheckDeviceAccess | |
680 | * | |
681 | * Arguments: | |
682 | * client is the client attempting to access a device. | |
683 | * dev is the device being accessed. | |
684 | * fromRequest is TRUE if the device access is a direct result of | |
685 | * the client executing some request and FALSE if it is a | |
686 | * result of the server trying to send an event (e.g. KeymapNotify) | |
687 | * to the client. | |
688 | * Returns: | |
689 | * TRUE if the device access should be allowed, else FALSE. | |
690 | * | |
691 | * Side Effects: | |
692 | * An audit message is generated if access is denied. | |
693 | */ | |
694 | ||
695 | static void | |
696 | SecurityDevice(CallbackListPtr *pcbl, pointer unused, pointer calldata) | |
697 | { | |
698 | XaceDeviceAccessRec *rec = calldata; | |
699 | SecurityStateRec *subj, *obj; | |
700 | Mask requested = rec->access_mode; | |
701 | Mask allowed = SecurityDeviceMask; | |
702 | ||
703 | subj = dixLookupPrivate(&rec->client->devPrivates, stateKey); | |
704 | obj = dixLookupPrivate(&serverClient->devPrivates, stateKey); | |
705 | ||
706 | if (rec->dev != inputInfo.keyboard) | |
707 | /* this extension only supports the core keyboard */ | |
708 | allowed = requested; | |
709 | ||
710 | if (SecurityDoCheck(subj, obj, requested, allowed) != Success) { | |
711 | SecurityAudit("Security denied client %d keyboard access on request " | |
712 | "%s\n", rec->client->index, | |
713 | SecurityLookupRequestName(rec->client)); | |
714 | rec->status = BadAccess; | |
715 | } | |
716 | } | |
717 | ||
718 | /* SecurityResource | |
719 | * | |
720 | * This function gets plugged into client->CheckAccess and is called from | |
721 | * SecurityLookupIDByType/Class to determine if the client can access the | |
722 | * resource. | |
723 | * | |
724 | * Arguments: | |
725 | * client is the client doing the resource access. | |
726 | * id is the resource id. | |
727 | * rtype is its type or class. | |
728 | * access_mode represents the intended use of the resource; see | |
729 | * resource.h. | |
730 | * res is a pointer to the resource structure for this resource. | |
731 | * | |
732 | * Returns: | |
733 | * If access is granted, the value of rval that was passed in, else FALSE. | |
734 | * | |
735 | * Side Effects: | |
736 | * Disallowed resource accesses are audited. | |
737 | */ | |
738 | ||
739 | static void | |
740 | SecurityResource(CallbackListPtr *pcbl, pointer unused, pointer calldata) | |
741 | { | |
742 | XaceResourceAccessRec *rec = calldata; | |
743 | SecurityStateRec *subj, *obj; | |
744 | int cid = CLIENT_ID(rec->id); | |
745 | Mask requested = rec->access_mode; | |
746 | Mask allowed = SecurityResourceMask; | |
747 | ||
748 | subj = dixLookupPrivate(&rec->client->devPrivates, stateKey); | |
749 | ||
750 | /* disable background None for untrusted windows */ | |
751 | if ((requested & DixCreateAccess) && (rec->rtype == RT_WINDOW)) | |
752 | if (subj->haveState && subj->trustLevel != XSecurityClientTrusted) | |
753 | ((WindowPtr) rec->res)->forcedBG = TRUE; | |
754 | ||
755 | /* additional permissions for specific resource types */ | |
756 | if (rec->rtype == RT_WINDOW) | |
757 | allowed |= SecurityWindowExtraMask; | |
758 | ||
759 | /* special checks for server-owned resources */ | |
760 | if (cid == 0) { | |
761 | if (rec->rtype & RC_DRAWABLE) | |
762 | /* additional operations allowed on root windows */ | |
763 | allowed |= SecurityRootWindowExtraMask; | |
764 | ||
765 | else if (rec->rtype == RT_COLORMAP) | |
766 | /* allow access to default colormaps */ | |
767 | allowed = requested; | |
768 | ||
769 | else | |
770 | /* allow read access to other server-owned resources */ | |
771 | allowed |= DixReadAccess; | |
772 | } | |
773 | ||
774 | if (clients[cid] != NULL) { | |
775 | obj = dixLookupPrivate(&clients[cid]->devPrivates, stateKey); | |
776 | if (SecurityDoCheck(subj, obj, requested, allowed) == Success) | |
777 | return; | |
778 | } | |
779 | ||
780 | SecurityAudit("Security: denied client %d access %x to resource 0x%x " | |
781 | "of client %d on request %s\n", rec->client->index, | |
782 | requested, rec->id, cid, | |
783 | SecurityLookupRequestName(rec->client)); | |
784 | rec->status = BadAccess; /* deny access */ | |
785 | } | |
786 | ||
787 | static void | |
788 | SecurityExtension(CallbackListPtr *pcbl, pointer unused, pointer calldata) | |
789 | { | |
790 | XaceExtAccessRec *rec = calldata; | |
791 | SecurityStateRec *subj; | |
792 | int i = 0; | |
793 | ||
794 | subj = dixLookupPrivate(&rec->client->devPrivates, stateKey); | |
795 | ||
796 | if (subj->haveState && subj->trustLevel == XSecurityClientTrusted) | |
797 | return; | |
798 | ||
799 | while (SecurityTrustedExtensions[i]) | |
800 | if (!strcmp(SecurityTrustedExtensions[i++], rec->ext->name)) | |
801 | return; | |
802 | ||
803 | SecurityAudit("Security: denied client %d access to extension " | |
804 | "%s on request %s\n", | |
805 | rec->client->index, rec->ext->name, | |
806 | SecurityLookupRequestName(rec->client)); | |
807 | rec->status = BadAccess; | |
808 | } | |
809 | ||
810 | static void | |
811 | SecurityServer(CallbackListPtr *pcbl, pointer unused, pointer calldata) | |
812 | { | |
813 | XaceServerAccessRec *rec = calldata; | |
814 | SecurityStateRec *subj, *obj; | |
815 | Mask requested = rec->access_mode; | |
816 | Mask allowed = SecurityServerMask; | |
817 | ||
818 | subj = dixLookupPrivate(&rec->client->devPrivates, stateKey); | |
819 | obj = dixLookupPrivate(&serverClient->devPrivates, stateKey); | |
820 | ||
821 | if (SecurityDoCheck(subj, obj, requested, allowed) != Success) { | |
822 | SecurityAudit("Security: denied client %d access to server " | |
823 | "configuration request %s\n", rec->client->index, | |
824 | SecurityLookupRequestName(rec->client)); | |
825 | rec->status = BadAccess; | |
826 | } | |
827 | } | |
828 | ||
829 | static void | |
830 | SecurityClient(CallbackListPtr *pcbl, pointer unused, pointer calldata) | |
831 | { | |
832 | XaceClientAccessRec *rec = calldata; | |
833 | SecurityStateRec *subj, *obj; | |
834 | Mask requested = rec->access_mode; | |
835 | Mask allowed = SecurityClientMask; | |
836 | ||
837 | subj = dixLookupPrivate(&rec->client->devPrivates, stateKey); | |
838 | obj = dixLookupPrivate(&rec->target->devPrivates, stateKey); | |
839 | ||
840 | if (SecurityDoCheck(subj, obj, requested, allowed) != Success) { | |
841 | SecurityAudit("Security: denied client %d access to client %d on " | |
842 | "request %s\n", rec->client->index, rec->target->index, | |
843 | SecurityLookupRequestName(rec->client)); | |
844 | rec->status = BadAccess; | |
845 | } | |
846 | } | |
847 | ||
848 | static void | |
849 | SecurityProperty(CallbackListPtr *pcbl, pointer unused, pointer calldata) | |
850 | { | |
851 | XacePropertyAccessRec *rec = calldata; | |
852 | SecurityStateRec *subj, *obj; | |
853 | ATOM name = (*rec->ppProp)->propertyName; | |
854 | Mask requested = rec->access_mode; | |
855 | Mask allowed = SecurityResourceMask | DixReadAccess; | |
856 | ||
857 | subj = dixLookupPrivate(&rec->client->devPrivates, stateKey); | |
858 | obj = dixLookupPrivate(&wClient(rec->pWin)->devPrivates, stateKey); | |
859 | ||
860 | if (SecurityDoCheck(subj, obj, requested, allowed) != Success) { | |
861 | SecurityAudit("Security: denied client %d access to property %s " | |
862 | "(atom 0x%x) window 0x%x of client %d on request %s\n", | |
863 | rec->client->index, NameForAtom(name), name, | |
864 | rec->pWin->drawable.id, wClient(rec->pWin)->index, | |
865 | SecurityLookupRequestName(rec->client)); | |
866 | rec->status = BadAccess; | |
867 | } | |
868 | } | |
869 | ||
870 | static void | |
871 | SecuritySend(CallbackListPtr *pcbl, pointer unused, pointer calldata) | |
872 | { | |
873 | XaceSendAccessRec *rec = calldata; | |
874 | SecurityStateRec *subj, *obj; | |
875 | ||
876 | if (rec->client) { | |
877 | int i; | |
878 | ||
879 | subj = dixLookupPrivate(&rec->client->devPrivates, stateKey); | |
880 | obj = dixLookupPrivate(&wClient(rec->pWin)->devPrivates, stateKey); | |
881 | ||
882 | if (SecurityDoCheck(subj, obj, DixSendAccess, 0) == Success) | |
883 | return; | |
884 | ||
885 | for (i = 0; i < rec->count; i++) | |
886 | if (rec->events[i].u.u.type != UnmapNotify && | |
887 | rec->events[i].u.u.type != ConfigureRequest && | |
888 | rec->events[i].u.u.type != ClientMessage) { | |
889 | ||
890 | SecurityAudit("Security: denied client %d from sending event " | |
891 | "of type %s to window 0x%x of client %d\n", | |
892 | rec->client->index, | |
893 | LookupEventName(rec->events[i].u.u.type), | |
894 | rec->pWin->drawable.id, | |
895 | wClient(rec->pWin)->index); | |
896 | rec->status = BadAccess; | |
897 | return; | |
898 | } | |
899 | } | |
900 | } | |
901 | ||
902 | static void | |
903 | SecurityReceive(CallbackListPtr *pcbl, pointer unused, pointer calldata) | |
904 | { | |
905 | XaceReceiveAccessRec *rec = calldata; | |
906 | SecurityStateRec *subj, *obj; | |
907 | ||
908 | subj = dixLookupPrivate(&rec->client->devPrivates, stateKey); | |
909 | obj = dixLookupPrivate(&wClient(rec->pWin)->devPrivates, stateKey); | |
910 | ||
911 | if (SecurityDoCheck(subj, obj, DixReceiveAccess, 0) == Success) | |
912 | return; | |
913 | ||
914 | SecurityAudit("Security: denied client %d from receiving an event " | |
915 | "sent to window 0x%x of client %d\n", | |
916 | rec->client->index, rec->pWin->drawable.id, | |
917 | wClient(rec->pWin)->index); | |
918 | rec->status = BadAccess; | |
919 | } | |
920 | ||
921 | /* SecurityClientStateCallback | |
922 | * | |
923 | * Arguments: | |
924 | * pcbl is &ClientStateCallback. | |
925 | * nullata is NULL. | |
926 | * calldata is a pointer to a NewClientInfoRec (include/dixstruct.h) | |
927 | * which contains information about client state changes. | |
928 | * | |
929 | * Returns: nothing. | |
930 | * | |
931 | * Side Effects: | |
932 | * | |
933 | * If a new client is connecting, its authorization ID is copied to | |
934 | * client->authID. If this is a generated authorization, its reference | |
935 | * count is bumped, its timer is cancelled if it was running, and its | |
936 | * trustlevel is copied to TRUSTLEVEL(client). | |
937 | * | |
938 | * If a client is disconnecting and the client was using a generated | |
939 | * authorization, the authorization's reference count is decremented, and | |
940 | * if it is now zero, the timer for this authorization is started. | |
941 | */ | |
942 | ||
943 | static void | |
944 | SecurityClientState(CallbackListPtr *pcbl, pointer unused, pointer calldata) | |
945 | { | |
946 | NewClientInfoRec *pci = calldata; | |
947 | SecurityStateRec *state; | |
948 | SecurityAuthorizationPtr pAuth; | |
949 | int rc; | |
950 | ||
951 | state = dixLookupPrivate(&pci->client->devPrivates, stateKey); | |
952 | ||
953 | switch (pci->client->clientState) { | |
954 | case ClientStateInitial: | |
955 | state->trustLevel = XSecurityClientTrusted; | |
956 | state->authId = None; | |
957 | state->haveState = TRUE; | |
958 | state->live = FALSE; | |
959 | break; | |
960 | ||
961 | case ClientStateRunning: | |
962 | state->authId = AuthorizationIDOfClient(pci->client); | |
963 | rc = dixLookupResourceByType((pointer *) &pAuth, state->authId, | |
964 | SecurityAuthorizationResType, serverClient, | |
965 | DixGetAttrAccess); | |
966 | if (rc == Success) { | |
967 | /* it is a generated authorization */ | |
968 | pAuth->refcnt++; | |
969 | state->live = TRUE; | |
970 | if (pAuth->refcnt == 1 && pAuth->timer) | |
971 | TimerCancel(pAuth->timer); | |
972 | ||
973 | state->trustLevel = pAuth->trustLevel; | |
974 | } | |
975 | break; | |
976 | ||
977 | case ClientStateGone: | |
978 | case ClientStateRetained: | |
979 | rc = dixLookupResourceByType((pointer *) &pAuth, state->authId, | |
980 | SecurityAuthorizationResType, serverClient, | |
981 | DixGetAttrAccess); | |
982 | if (rc == Success && state->live) { | |
983 | /* it is a generated authorization */ | |
984 | pAuth->refcnt--; | |
985 | state->live = FALSE; | |
986 | if (pAuth->refcnt == 0) | |
987 | SecurityStartAuthorizationTimer(pAuth); | |
988 | } | |
989 | break; | |
990 | ||
991 | default: | |
992 | break; | |
993 | } | |
994 | } | |
995 | ||
996 | /* SecurityResetProc | |
997 | * | |
998 | * Arguments: | |
999 | * extEntry is the extension information for the security extension. | |
1000 | * | |
1001 | * Returns: nothing. | |
1002 | * | |
1003 | * Side Effects: | |
1004 | * Performs any cleanup needed by Security at server shutdown time. | |
1005 | */ | |
1006 | ||
1007 | static void | |
1008 | SecurityResetProc(ExtensionEntry * extEntry) | |
1009 | { | |
1010 | /* Unregister callbacks */ | |
1011 | DeleteCallback(&ClientStateCallback, SecurityClientState, NULL); | |
1012 | ||
1013 | XaceDeleteCallback(XACE_EXT_DISPATCH, SecurityExtension, NULL); | |
1014 | XaceDeleteCallback(XACE_RESOURCE_ACCESS, SecurityResource, NULL); | |
1015 | XaceDeleteCallback(XACE_DEVICE_ACCESS, SecurityDevice, NULL); | |
1016 | XaceDeleteCallback(XACE_PROPERTY_ACCESS, SecurityProperty, NULL); | |
1017 | XaceDeleteCallback(XACE_SEND_ACCESS, SecuritySend, NULL); | |
1018 | XaceDeleteCallback(XACE_RECEIVE_ACCESS, SecurityReceive, NULL); | |
1019 | XaceDeleteCallback(XACE_CLIENT_ACCESS, SecurityClient, NULL); | |
1020 | XaceDeleteCallback(XACE_EXT_ACCESS, SecurityExtension, NULL); | |
1021 | XaceDeleteCallback(XACE_SERVER_ACCESS, SecurityServer, NULL); | |
1022 | } | |
1023 | ||
1024 | /* SecurityExtensionInit | |
1025 | * | |
1026 | * Arguments: none. | |
1027 | * | |
1028 | * Returns: nothing. | |
1029 | * | |
1030 | * Side Effects: | |
1031 | * Enables the Security extension if possible. | |
1032 | */ | |
1033 | ||
1034 | void | |
1035 | SecurityExtensionInit(void) | |
1036 | { | |
1037 | ExtensionEntry *extEntry; | |
1038 | int ret = TRUE; | |
1039 | ||
1040 | SecurityAuthorizationResType = | |
1041 | CreateNewResourceType(SecurityDeleteAuthorization, | |
1042 | "SecurityAuthorization"); | |
1043 | ||
1044 | RTEventClient = | |
1045 | CreateNewResourceType(SecurityDeleteAuthorizationEventClient, | |
1046 | "SecurityEventClient"); | |
1047 | ||
1048 | if (!SecurityAuthorizationResType || !RTEventClient) | |
1049 | return; | |
1050 | ||
1051 | RTEventClient |= RC_NEVERRETAIN; | |
1052 | ||
1053 | /* Allocate the private storage */ | |
1054 | if (!dixRegisterPrivateKey | |
1055 | (stateKey, PRIVATE_CLIENT, sizeof(SecurityStateRec))) | |
1056 | FatalError("SecurityExtensionSetup: Can't allocate client private.\n"); | |
1057 | ||
1058 | /* Register callbacks */ | |
1059 | ret &= AddCallback(&ClientStateCallback, SecurityClientState, NULL); | |
1060 | ||
1061 | ret &= XaceRegisterCallback(XACE_EXT_DISPATCH, SecurityExtension, NULL); | |
1062 | ret &= XaceRegisterCallback(XACE_RESOURCE_ACCESS, SecurityResource, NULL); | |
1063 | ret &= XaceRegisterCallback(XACE_DEVICE_ACCESS, SecurityDevice, NULL); | |
1064 | ret &= XaceRegisterCallback(XACE_PROPERTY_ACCESS, SecurityProperty, NULL); | |
1065 | ret &= XaceRegisterCallback(XACE_SEND_ACCESS, SecuritySend, NULL); | |
1066 | ret &= XaceRegisterCallback(XACE_RECEIVE_ACCESS, SecurityReceive, NULL); | |
1067 | ret &= XaceRegisterCallback(XACE_CLIENT_ACCESS, SecurityClient, NULL); | |
1068 | ret &= XaceRegisterCallback(XACE_EXT_ACCESS, SecurityExtension, NULL); | |
1069 | ret &= XaceRegisterCallback(XACE_SERVER_ACCESS, SecurityServer, NULL); | |
1070 | ||
1071 | if (!ret) | |
1072 | FatalError("SecurityExtensionSetup: Failed to register callbacks\n"); | |
1073 | ||
1074 | /* Add extension to server */ | |
1075 | extEntry = AddExtension(SECURITY_EXTENSION_NAME, | |
1076 | XSecurityNumberEvents, XSecurityNumberErrors, | |
1077 | ProcSecurityDispatch, SProcSecurityDispatch, | |
1078 | SecurityResetProc, StandardMinorOpcode); | |
1079 | ||
1080 | SecurityErrorBase = extEntry->errorBase; | |
1081 | SecurityEventBase = extEntry->eventBase; | |
1082 | ||
1083 | EventSwapVector[SecurityEventBase + XSecurityAuthorizationRevoked] = | |
1084 | (EventSwapPtr) SwapSecurityAuthorizationRevokedEvent; | |
1085 | ||
1086 | SetResourceTypeErrorValue(SecurityAuthorizationResType, | |
1087 | SecurityErrorBase + XSecurityBadAuthorization); | |
1088 | ||
1089 | /* Label objects that were created before we could register ourself */ | |
1090 | SecurityLabelInitial(); | |
1091 | } |