swi-prolog: update to 9.2.9
[oi-userland.git] / components / x11 / xorg-server / srcs / os / dtlogin.c
blob0074bb8483c212e9190f255126bce867b5d81ec5
1 /*
2 * Copyright (c) 1996, 2017, Oracle and/or its affiliates. All rights reserved.
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21 * DEALINGS IN THE SOFTWARE.
25 /* Implementation of Display Manager (dtlogin/gdm/xdm/etc.) to X server
26 * communication pipe. The Display Manager process will start
27 * the X window server at system boot time before any user
28 * has logged into the system. The X server is by default
29 * started as the root UID "0".
31 * At login time the Xserver local communication pipe is provided
32 * by the Xserver for user specific configuration data supplied
33 * by the display manager. It notifies the Xserver it needs to change
34 * over to the user's credentials (UID, GID, GID_LIST) and
35 * also switch CWD (current working directory) of to match
36 * the user's CWD home.
38 * When shutting down, the Xserver restores it's original uid/gid as
39 * needed by the cleanup/teardown actions in several drivers.
41 * For the original definition, see Sun ASARC case 1995/390
44 #ifdef HAVE_DIX_CONFIG_H
45 #include <dix-config.h>
46 #endif
48 #include <X11/Xos.h>
49 #include <sys/param.h>
50 #include <sys/socket.h>
51 #include <netinet/in.h>
52 #include <netdb.h>
53 #include <stdio.h>
54 #include <fcntl.h>
55 #include <sys/types.h>
56 #include <sys/stat.h>
57 #include <errno.h>
58 #include <X11/X.h>
59 #include <X11/Xmd.h>
60 #include "misc.h"
61 #include <X11/Xpoll.h>
62 #include "osdep.h"
63 #include "input.h"
64 #include "dixstruct.h"
65 #include "dixfont.h"
66 #include "opaque.h"
67 #include <pwd.h>
68 #include <project.h>
69 #include <sys/task.h>
70 #include <ctype.h>
71 #include "scrnintstr.h"
72 #include <sys/vt.h>
74 #define DTLOGIN_PATH "/var/dt/sdtlogin"
76 #define BUFLEN 1024
78 int xf86ConsoleFd = -1;
80 /* in Xserver/os/auth.c */
81 extern const char *GetAuthFilename(void);
83 /* Data about the user we need to switch to */
84 struct dmuser {
85 uid_t uid;
86 gid_t gid; /* Primary group */
87 char * homedir;
88 projid_t projid;
89 int groupid_cnt;
90 gid_t groupids[NGROUPS_UMAX]; /* Supplementary groups */
93 /* Data passed to block/wakeup handlers */
94 struct dmdata {
95 int pipeFD;
96 char * pipename; /* path to pipe */
97 char * buf; /* contains characters to be processed */
98 size_t bufsize; /* size allocated for buf */
99 struct dmuser user; /* target user, to switch to on login */
103 /* Data stored in screen privates */
104 struct dmScreenPriv {
105 CloseScreenProcPtr CloseScreen;
107 static DevPrivateKeyRec dmScreenKeyRec;
108 #define dmScreenKey (&dmScreenKeyRec)
109 static struct dmdata *dmHandlerData;
110 static struct dmuser originalUser; /* user to switch back to in CloseDown */
112 static Bool DtloginCloseScreen(ScreenPtr pScreen);
113 static int dtlogin_create_pipe(int, struct dmdata *);
114 static void dtlogin_pipe_notify(int, int, void *);
115 static void dtlogin_receive_packet(struct dmdata *);
116 static int dtlogin_parse_packet(struct dmdata *, char *);
117 static void dtlogin_process(struct dmuser *user, int user_logged_in);
118 static void dtlogin_close_pipe(struct dmdata *);
120 #define DtloginError(fmt, ...) \
121 LogMessageVerb(X_ERROR, 1, "dtlogin: " fmt ": %s\n", __VA_ARGS__, \
122 strerror(errno))
124 #define DtloginInfo(fmt, ...) \
125 LogMessageVerb(X_INFO, 5, "dtlogin: " fmt, __VA_ARGS__)
128 * initialize DTLOGIN: create pipe; set handlers.
129 * Called from main loop in os/main.c
132 _X_HIDDEN void
133 DtloginInit(void)
135 int displayNumber = 0;
136 struct dmdata *dmd;
138 if (serverGeneration != 1) return;
140 originalUser.uid = geteuid();
141 originalUser.gid = getegid();
142 getgroups(NGROUPS_UMAX, originalUser.groupids);
143 originalUser.homedir = getcwd(NULL, 0);
144 originalUser.projid = getprojid();
146 if (getuid() != 0) return;
148 dmd = calloc(1, sizeof(struct dmdata));
149 if (dmd == NULL) {
150 DtloginError("Failed to allocate %d bytes for display manager pipe",
151 (int) sizeof(struct dmdata));
152 return;
155 dmd->user.uid = (uid_t) -1;
156 dmd->user.gid = (gid_t) -1;
157 dmd->user.projid = (projid_t) -1;
159 displayNumber = atoi(display); /* Assigned in dix/main.c */
161 dmd->pipeFD = dtlogin_create_pipe(displayNumber, dmd);
163 if (dmd->pipeFD == -1) {
164 free(dmd);
165 return;
168 dmHandlerData = dmd;
170 SetNotifyFd(dmd->pipeFD, dtlogin_pipe_notify, X_NOTIFY_READ, dmd);
174 * cleanup dtlogin pipe at exit if still running, reset privs back to
175 * root as needed for various cleanup tasks.
176 * Called from main loop in os/main.c & CloseScreen wrappers
178 _X_HIDDEN void
179 DtloginCloseDown(void)
181 if (geteuid() != originalUser.uid) {
182 dtlogin_process(&originalUser, 0);
185 if (dmHandlerData != NULL) {
186 dtlogin_close_pipe(dmHandlerData);
190 static Bool
191 DtloginCloseScreen (ScreenPtr pScreen)
193 struct dmScreenPriv *pScreenPriv;
195 DtloginCloseDown();
197 /* Unwrap CloseScreen and call down to further levels */
198 pScreenPriv = (struct dmScreenPriv *)
199 dixGetPrivate(&pScreen->devPrivates, dmScreenKey);
200 dixSetPrivate(&pScreen->devPrivates, dmScreenKey, NULL);
202 pScreen->CloseScreen = pScreenPriv->CloseScreen;
203 free (pScreenPriv);
205 return (*pScreen->CloseScreen) (pScreen);
208 static int
209 dtlogin_create_pipe(int port, struct dmdata *dmd)
211 struct stat statbuf;
212 char pipename[128];
213 int pipeFD = -1;
215 if (stat(DTLOGIN_PATH, &statbuf) == -1) {
216 if (mkdir(DTLOGIN_PATH, 0700) == -1) {
217 DtloginError("Cannot create %s directory for display "
218 "manager pipe", DTLOGIN_PATH);
219 return -1;
221 } else if (!S_ISDIR(statbuf.st_mode)) {
222 DtloginError("Cannot create display manager pipe: "
223 "%s is not a directory", DTLOGIN_PATH);
224 return -1;
227 snprintf(pipename, sizeof(pipename), "%s/%d", DTLOGIN_PATH, port);
229 /* Workaround: cleanup pipe if it has not been removed. */
230 if (stat(pipename, &statbuf) == 0) {
231 DtloginInfo("Cleanup leftover display manager pipe: %s\n", pipename);
232 remove(pipename);
235 if (mkfifo(pipename, S_IRUSR | S_IWUSR) < 0) {
236 DtloginError("Cannot create display manager pipe %s", pipename);
237 return -1;
240 if ((pipeFD = open(pipename, O_RDWR | O_NONBLOCK)) < 0) {
241 DtloginError("Cannot open display manager pipe %s", pipename);
242 remove(pipename);
243 return -1;
246 dmd->pipename = xstrdup(pipename);
248 /* To make sure root has rw permissions. */
249 (void) fchmod(pipeFD, 0600);
251 DtloginInfo("Created display manager pipe: %s\n", dmd->pipename);
252 return pipeFD;
255 static void dtlogin_close_pipe(struct dmdata *dmd)
257 DtloginInfo("Closing display manager pipe: %s\n", dmd->pipename);
258 RemoveNotifyFd(dmd->pipeFD);
259 close(dmd->pipeFD);
260 remove(dmd->pipename);
261 free(dmd->pipename);
262 free(dmd->buf);
263 free(dmd->user.homedir);
264 free(dmd);
266 if (dmHandlerData == dmd) {
267 dmHandlerData = NULL;
271 static void dtlogin_pipe_notify(int fd, int events, void *data)
273 struct dmdata *dmd = (struct dmdata *) data;
275 assert(fd == dmd->pipeFD);
277 if (events & X_NOTIFY_READ) {
278 dtlogin_receive_packet(dmd);
279 /* dmd may have been freed in dtlogin_receive_packet,
280 do not use after this point */
281 } else {
282 if (events & X_NOTIFY_ERROR) {
283 DtloginError("Lost connection on %s (fd %d)",
284 dmd->pipename, dmd->pipeFD);
285 } else {
286 DtloginError("Unexpected notification event 0x%x", events);
288 dtlogin_close_pipe(dmd);
289 /* dmd has been freed in dtlogin_close_pipe,
290 do not use after this point */
294 static void
295 dtlogin_receive_packet(struct dmdata *dmd)
297 size_t bufLen;
298 ssize_t nbRead;
299 char *p, *n;
300 int done = 0;
302 if (dmd->buf == NULL) {
303 dmd->buf = malloc(BUFLEN);
304 if (dmd->buf != NULL) {
305 dmd->buf[0] = '\0';
306 dmd->bufsize = BUFLEN;
307 } else {
308 DtloginError("Failed to allocate display manager pipe buffer of %d",
309 BUFLEN);
310 dtlogin_close_pipe(dmd);
311 return;
315 /* Read data from pipe and split into tokens, buffering the rest */
316 while (!done) {
317 bufLen = strlen(dmd->buf);
320 * Realloc only if buf has filled up and we don't have a record
321 * delimiter yet. Keep track of alloced size.
323 if (bufLen > (dmd->bufsize/2)) {
324 char *newbuf = realloc(dmd->buf, dmd->bufsize + BUFLEN);
325 if (newbuf != NULL) {
326 dmd->buf = newbuf;
327 dmd->bufsize += BUFLEN;
331 nbRead = read(dmd->pipeFD, dmd->buf + bufLen,
332 dmd->bufsize - bufLen - 1);
334 if (nbRead == -1) {
335 if (errno == EINTR) {
336 continue;
337 } else if (errno == EAGAIN) {
338 return; /* return to WaitFor, wait for select() */
339 } else {
340 DtloginError("Unexpected error on display manager pipe %s",
341 dmd->pipename);
342 dtlogin_close_pipe(dmd);
343 return;
347 if (nbRead == 0) { /* End of file */
348 DtloginError("Unexpected EOF on display manager pipe %s",
349 dmd->pipename);
350 dtlogin_close_pipe(dmd);
351 return;
354 bufLen += nbRead;
355 dmd->buf[bufLen] = '\0';
357 DtloginInfo("Data buffer: %s\n", dmd->buf);
359 p = dmd->buf;
360 while ((n = strchr(p, ';')) != NULL) { /* Next complete packet */
361 *n = '\0';
362 DtloginInfo("Next packet: %s\n", p);
363 done = dtlogin_parse_packet(dmd, p);
364 if (done) {
365 break;
367 p = n+1;
370 /* save the rest for the next iteration */
371 if (!done && (p < (dmd->buf + bufLen))) {
372 DtloginInfo("Left over: %s\n", p);
373 strcpy(dmd->buf, p);
374 } else {
375 dmd->buf[0] = '\0';
380 /* Parse data from packet
382 * Example Record:
383 * UID="xxx" GID="yyy";
384 * G_LIST_ID="aaa" G_LIST_ID="bbb" G_LIST_ID="ccc";
385 * HOME="/nn/mmm/ooo" EOF="";
387 static int
388 dtlogin_parse_packet(struct dmdata *dmd, char *s)
390 char *k, *v, *p;
392 while (s < (dmd->buf + dmd->bufsize)) {
393 /* format is key="value" - split into key & value pair */
395 for (k = s ; (*k != '\0') && isspace(*k); k++) {
396 /* Skip over whitespace */
399 if (*k == '\0') {
400 break;
403 p = strchr(k, '=');
405 if (p == NULL) {
406 DtloginInfo("Malformed key \'%s\'\n", k);
407 return 0;
410 *p = '\0'; /* end of key string */
411 p++;
413 if (*p != '\"') {
414 DtloginInfo("Bad delimiter at \'%s\'\n", p);
415 return 0;
418 v = p + 1; /* start of value string */
420 p = strchr(v, '\"');
422 if (p == NULL) {
423 DtloginInfo("Missing quote after value \'%s\'\n", v);
424 break;
427 *p = '\0'; /* end of value string */
429 s = p + 1; /* start of next pair */
431 DtloginInfo("Received key \"%s\" => value \"%s\"\n", k, v);
433 /* Found key & value, now process & put into dmd */
434 if (strcmp(k, "EOF") == 0) {
435 /* End of transmission, process & close */
436 dtlogin_process(&(dmd->user), 1);
437 return 1;
439 else if (strcmp(k, "HOME") == 0) {
440 dmd->user.homedir = xstrdup(v);
442 else if ( (strcmp(k, "UID") == 0) || (strcmp(k, "GID") == 0)
443 || (strcmp(k, "G_LIST_ID") == 0) ) {
444 /* Value is numeric, convert to int */
445 long val;
447 errno = 0;
448 val = strtol(v, NULL, 10);
450 if ((val == 0) && (strcmp(v, "0") != 0)) {
451 /* strtol couldn't parse the results */
452 DtloginInfo("Invalid number \"%s\"\n", v);
453 continue;
456 if ( ((val == LONG_MAX) || (val == LONG_MIN))
457 && (errno == ERANGE) ) {
458 /* Value out of range */
459 DtloginInfo("Out of range number \"%s\"\n", v);
460 continue;
463 if (strcmp(k, "UID") == 0) {
464 dmd->user.uid = (uid_t) val;
466 else if (strcmp(k, "GID") == 0) {
467 dmd->user.gid = (gid_t) val;
469 else if (strcmp(k, "G_LIST_ID") == 0) {
470 if (dmd->user.groupid_cnt < NGROUPS_UMAX) {
471 dmd->user.groupids[dmd->user.groupid_cnt++] = (gid_t) val;
475 else {
476 DtloginInfo("Unrecognized key \"%s\"\n", k);
480 return 0;
484 static void
485 dtlogin_process(struct dmuser *user, int user_logged_in)
487 struct project proj;
488 char proj_buf[PROJECT_BUFSZ];
489 struct passwd *ppasswd;
490 const char *auth_file = NULL;
492 if (geteuid() != 0) { /* reset privs back to root */
493 if (seteuid(0) < 0) {
494 DtloginError("Error in resetting euid to %d", 0);
498 auth_file = GetAuthFilename();
500 if (auth_file) {
501 if (chown(auth_file, user->uid, user->gid) < 0)
502 DtloginError("Error in changing owner of %s to %d",
503 auth_file, user->uid);
506 /* This gid dance is necessary in order to make sure
507 our "saved-set-gid" is 0 so that we can regain gid
508 0 when necessary for priocntl & power management.
509 The first step sets rgid to the user's gid and
510 makes the egid & saved-gid be 0. The second then
511 sets the egid to the users gid, but leaves the
512 saved-gid as 0. */
514 if (user->gid != (gid_t) -1) {
515 DtloginInfo("Setting gid to %d\n", user->gid);
517 if (setregid(user->gid, 0) < 0)
518 DtloginError("Error in setting regid to %d\n", user->gid);
520 if (setegid(user->gid) < 0)
521 DtloginError("Error in setting egid to %d\n", user->gid);
524 if (user->groupid_cnt >= 0) {
525 if (setgroups(user->groupid_cnt, user->groupids) < 0)
526 DtloginError("Error in setting supplemental (%d) groups",
527 user->groupid_cnt);
532 * BUG: 4462531: Set project ID for Xserver
533 * Get user name and default project.
534 * Set before the uid value is set.
536 if (user->projid != (uid_t) -1) {
537 if (settaskid(user->projid, TASK_NORMAL) == (taskid_t) -1) {
538 DtloginError("Error in setting project id to %d", user->projid);
540 } else if (user->uid != (uid_t) -1) {
541 ppasswd = getpwuid(user->uid);
543 if (ppasswd == NULL) {
544 DtloginError("Error in getting user name for %d", user->uid);
545 } else {
546 if (strcmp(ppasswd->pw_name, "gdm") == 0) {
547 /* Avoid trying to screenlock the login screen with a
548 non-existent password for the gdm user */
549 user_logged_in = 0;
551 if (getdefaultproj(ppasswd->pw_name, &proj,
552 (void *)&proj_buf, PROJECT_BUFSZ) == NULL) {
553 DtloginError("Error in getting project id for %s",
554 ppasswd->pw_name);
555 } else {
556 DtloginInfo("Setting project to %s\n", proj.pj_name);
558 if (setproject(proj.pj_name, ppasswd->pw_name,
559 TASK_NORMAL) == -1) {
560 DtloginError("Error in setting project to %s",
561 proj.pj_name);
567 if (user->uid != (uid_t) -1) {
568 DtloginInfo("Setting uid to %d\n", user->uid);
570 if (setreuid(user->uid, -1) < 0)
571 DtloginError("Error in setting ruid to %d", user->uid);
573 if (setreuid(-1, user->uid) < 0)
574 DtloginError("Error in setting euid to %d", user->uid);
576 /* Wrap closeScreen to allow resetting uid on closedown */
577 if ((user->uid != 0) && (user != &originalUser)) {
578 int i;
580 if (dixRegisterPrivateKey(dmScreenKey, PRIVATE_SCREEN, 0)) {
581 for (i = 0; i < screenInfo.numScreens; i++)
583 ScreenPtr pScreen = screenInfo.screens[i];
584 struct dmScreenPriv *pScreenPriv = (struct dmScreenPriv *)
585 dixLookupPrivate(&pScreen->devPrivates, dmScreenKey);
587 if (pScreenPriv == NULL) {
588 pScreenPriv = calloc(1, sizeof(struct dmScreenPriv));
590 if (pScreenPriv != NULL) {
591 pScreenPriv->CloseScreen = pScreen->CloseScreen;
592 pScreen->CloseScreen = DtloginCloseScreen;
594 dixSetPrivate(&pScreen->devPrivates, dmScreenKey,
595 pScreenPriv);
596 } else {
597 DtloginError("Failed to allocate %d bytes"
598 " for screen privates",
599 (int) sizeof(struct dmScreenPriv));
603 } else {
604 DtloginError("Failed to register screen private %s",
605 "for uid reset info");
610 if (user->homedir != NULL) {
611 char *env_str;
613 if (asprintf(&env_str, "HOME=%s", user->homedir) == -1) {
614 DtloginError("Not enough memory to setenv HOME=%s", user->homedir);
615 } else {
616 DtloginInfo("Setting %s\n",env_str);
618 if (putenv(env_str) < 0)
619 DtloginError("Failed to setenv %s", env_str);
622 if (chdir(user->homedir) < 0)
623 DtloginError("Error in changing working directory to %s",
624 user->homedir);
627 /* Inform the kernel whether a user has logged in on this VT device */
628 if (xf86ConsoleFd != -1)
629 ioctl(xf86ConsoleFd, VT_SETDISPLOGIN, user_logged_in);