1088N/A/*
1351N/A * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved.
0N/A *
0N/A * Permission is hereby granted, free of charge, to any person obtaining a
919N/A * copy of this software and associated documentation files (the "Software"),
919N/A * to deal in the Software without restriction, including without limitation
919N/A * the rights to use, copy, modify, merge, publish, distribute, sublicense,
919N/A * and/or sell copies of the Software, and to permit persons to whom the
919N/A * Software is furnished to do so, subject to the following conditions:
0N/A *
919N/A * The above copyright notice and this permission notice (including the next
919N/A * paragraph) shall be included in all copies or substantial portions of the
919N/A * Software.
0N/A *
919N/A * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
919N/A * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
919N/A * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
919N/A * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
919N/A * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
919N/A * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
919N/A * DEALINGS IN THE SOFTWARE.
0N/A */
0N/A
0N/A
419N/A/* Implementation of Display Manager (dtlogin/gdm/xdm/etc.) to X server
781N/A * communication pipe. The Display Manager process will start
781N/A * the X window server at system boot time before any user
781N/A * has logged into the system. The X server is by default
0N/A * started as the root UID "0".
419N/A *
781N/A * At login time the Xserver local communication pipe is provided
781N/A * by the Xserver for user specific configuration data supplied
781N/A * by the display manager. It notifies the Xserver it needs to change
0N/A * over to the user's credentials (UID, GID, GID_LIST) and
781N/A * also switch CWD (current working directory) of to match
0N/A * the user's CWD home.
781N/A *
781N/A * When shutting down, the Xserver restores it's original uid/gid as
781N/A * needed by the cleanup/teardown actions in several drivers.
781N/A *
781N/A * For the original definition, see Sun ASARC case 1995/390
0N/A */
0N/A
1088N/A#ifdef HAVE_DIX_CONFIG_H
1088N/A#include <dix-config.h>
1088N/A#endif
1088N/A
98N/A#include <X11/Xos.h>
0N/A#include <sys/param.h>
0N/A#include <sys/socket.h>
0N/A#include <netinet/in.h>
0N/A#include <netdb.h>
0N/A#include <stdio.h>
0N/A#include <fcntl.h>
0N/A#include <sys/types.h>
0N/A#include <sys/stat.h>
419N/A#include <errno.h>
98N/A#include <X11/X.h>
98N/A#include <X11/Xmd.h>
0N/A#include "misc.h"
98N/A#include <X11/Xpoll.h>
0N/A#include "osdep.h"
0N/A#include "input.h"
0N/A#include "dixstruct.h"
0N/A#include "dixfont.h"
0N/A#include "opaque.h"
0N/A#include <pwd.h>
0N/A#include <project.h>
0N/A#include <sys/task.h>
0N/A#include <ctype.h>
0N/A#include "scrnintstr.h"
766N/A#include <sys/vt.h>
0N/A
419N/A#define DTLOGIN_PATH "/var/dt/sdtlogin"
0N/A
0N/A#define BUFLEN 1024
0N/A
766N/Aint xf86ConsoleFd = -1;
0N/A
0N/A/* in Xserver/os/auth.c */
0N/Aextern const char *GetAuthFilename(void);
0N/A
781N/A/* Data about the user we need to switch to */
781N/Astruct dmuser {
781N/A uid_t uid;
781N/A gid_t gid; /* Primary group */
781N/A gid_t groupids[NGROUPS_UMAX]; /* Supplementary groups */
781N/A int groupid_cnt;
781N/A char * homedir;
781N/A projid_t projid;
781N/A};
781N/A
419N/A/* Data passed to block/wakeup handlers */
419N/Astruct dmdata {
419N/A int pipeFD;
419N/A char * pipename; /* path to pipe */
419N/A char * buf; /* contains characters to be processed */
419N/A int bufsize; /* size allocated for buf */
781N/A struct dmuser user; /* target user, to switch to on login */
419N/A};
0N/A
781N/A
781N/A/* Data stored in screen privates */
781N/Astruct dmScreenPriv {
781N/A CloseScreenProcPtr CloseScreen;
781N/A};
1088N/Astatic DevPrivateKeyRec dmScreenKeyRec;
1088N/A#define dmScreenKey (&dmScreenKeyRec)
781N/Astatic struct dmdata *dmHandlerData;
781N/Astatic struct dmuser originalUser; /* user to switch back to in CloseDown */
781N/A
1351N/Astatic Bool DtloginCloseScreen(ScreenPtr pScreen);
0N/Astatic void DtloginBlockHandler(pointer, struct timeval **, pointer);
0N/Astatic void DtloginWakeupHandler(pointer, int, pointer);
419N/Astatic int dtlogin_create_pipe(int, struct dmdata *);
419N/Astatic void dtlogin_receive_packet(struct dmdata *);
419N/Astatic int dtlogin_parse_packet(struct dmdata *, char *);
781N/Astatic void dtlogin_process(struct dmuser *user, int user_logged_in);
419N/Astatic void dtlogin_close_pipe(struct dmdata *);
0N/A
781N/A#define DtloginError(fmt, arg) LogMessageVerb(X_ERROR, 1, fmt ": %s\n", \
781N/A arg, strerror(errno))
781N/A#define DtloginInfo(fmt, arg) LogMessageVerb(X_INFO, 5, fmt, arg)
0N/A
0N/A/*
0N/A * initialize DTLOGIN: create pipe; set handlers.
781N/A * Called from main loop in os/main.c
0N/A */
0N/A
419N/A_X_HIDDEN void
0N/ADtloginInit(void)
0N/A{
0N/A int displayNumber = 0;
419N/A struct dmdata *dmd;
0N/A
419N/A if (serverGeneration != 1) return;
781N/A
781N/A originalUser.uid = geteuid();
781N/A originalUser.gid = getegid();
781N/A getgroups(NGROUPS_UMAX, originalUser.groupids);
781N/A originalUser.homedir = getcwd(NULL, 0);
781N/A originalUser.projid = getprojid();
0N/A
419N/A if (getuid() != 0) return;
419N/A
1088N/A dmd = calloc(1, sizeof(struct dmdata));
781N/A if (dmd == NULL) {
781N/A DtloginError("Failed to allocate %d bytes for display manager pipe",
781N/A sizeof(struct dmdata));
419N/A return;
419N/A }
781N/A
781N/A dmd->user.uid = (uid_t) -1;
781N/A dmd->user.gid = (gid_t) -1;
781N/A dmd->user.projid = (projid_t) -1;
781N/A
0N/A displayNumber = atoi(display); /* Assigned in dix/main.c */
0N/A
419N/A dmd->pipeFD = dtlogin_create_pipe(displayNumber, dmd);
0N/A
419N/A if (dmd->pipeFD == -1) {
1088N/A free(dmd);
419N/A return;
419N/A }
419N/A
781N/A dmHandlerData = dmd;
781N/A
419N/A RegisterBlockAndWakeupHandlers (DtloginBlockHandler,
419N/A DtloginWakeupHandler,
419N/A (pointer) dmd);
0N/A}
0N/A
781N/A/*
781N/A * cleanup dtlogin pipe at exit if still running, reset privs back to
781N/A * root as needed for various cleanup tasks.
781N/A * Called from main loop in os/main.c & CloseScreen wrappers
781N/A */
781N/A_X_HIDDEN void
781N/ADtloginCloseDown(void)
781N/A{
781N/A if (geteuid() != 0) { /* reset privs back to root */
781N/A if (seteuid(0) < 0) {
781N/A DtloginError("Error in resetting euid to %d", 0);
781N/A }
781N/A dtlogin_process(&originalUser, 0);
781N/A }
781N/A
781N/A if (dmHandlerData != NULL) {
781N/A dtlogin_close_pipe(dmHandlerData);
781N/A }
781N/A}
781N/A
781N/Astatic Bool
1351N/ADtloginCloseScreen (ScreenPtr pScreen)
781N/A{
781N/A struct dmScreenPriv *pScreenPriv;
781N/A
781N/A DtloginCloseDown();
781N/A
781N/A /* Unwrap CloseScreen and call down to further levels */
781N/A pScreenPriv = (struct dmScreenPriv *)
781N/A dixLookupPrivate(&pScreen->devPrivates, dmScreenKey);
781N/A
781N/A pScreen->CloseScreen = pScreenPriv->CloseScreen;
1088N/A free (pScreenPriv);
781N/A
1351N/A return (*pScreen->CloseScreen) (pScreen);
781N/A}
781N/A
0N/Astatic void
419N/ADtloginBlockHandler(
419N/A pointer data,
419N/A struct timeval **wt, /* unused */
419N/A pointer pReadmask)
0N/A{
419N/A struct dmdata *dmd = (struct dmdata *) data;
0N/A fd_set *LastSelectMask = (fd_set*)pReadmask;
781N/A
419N/A FD_SET(dmd->pipeFD, LastSelectMask);
0N/A}
0N/A
0N/Astatic void
419N/ADtloginWakeupHandler(
419N/A pointer data, /* unused */
419N/A int i,
419N/A pointer pReadmask)
0N/A{
419N/A struct dmdata *dmd = (struct dmdata *) data;
0N/A fd_set* LastSelectMask = (fd_set*)pReadmask;
0N/A
0N/A if (i > 0)
0N/A {
781N/A if (FD_ISSET(dmd->pipeFD, LastSelectMask))
781N/A {
781N/A FD_CLR(dmd->pipeFD, LastSelectMask);
781N/A dtlogin_receive_packet(dmd);
419N/A /* dmd may have been freed in dtlogin_receive_packet, do
419N/A not use after this point */
781N/A }
0N/A }
0N/A}
0N/A
0N/Astatic int
419N/Adtlogin_create_pipe(int port, struct dmdata *dmd)
0N/A{
0N/A struct stat statbuf;
0N/A char pipename[128];
419N/A int pipeFD = -1;
0N/A
419N/A if (stat(DTLOGIN_PATH, &statbuf) == -1) {
419N/A if (mkdir(DTLOGIN_PATH, 0700) == -1) {
781N/A DtloginError("Cannot create %s directory for display "
781N/A "manager pipe", DTLOGIN_PATH);
419N/A return -1;
419N/A }
419N/A } else if (!S_ISDIR(statbuf.st_mode)) {
419N/A DtloginError("Cannot create display manager pipe: "
781N/A "%s is not a directory", DTLOGIN_PATH);
781N/A return -1;
419N/A }
781N/A
419N/A snprintf(pipename, sizeof(pipename), "%s/%d", DTLOGIN_PATH, port);
0N/A
781N/A if (mkfifo(pipename, S_IRUSR | S_IWUSR) < 0)
781N/A return -1;
781N/A
419N/A if ((pipeFD = open(pipename, O_RDONLY | O_NONBLOCK)) < 0) {
419N/A remove(pipename);
781N/A return -1;
419N/A }
0N/A
419N/A dmd->pipename = xstrdup(pipename);
0N/A
0N/A /* To make sure root has rw permissions. */
419N/A (void) fchmod(pipeFD, 0600);
0N/A
419N/A return pipeFD;
0N/A}
0N/A
419N/Astatic void dtlogin_close_pipe(struct dmdata *dmd)
0N/A{
781N/A RemoveBlockAndWakeupHandlers (DtloginBlockHandler,
419N/A DtloginWakeupHandler, dmd);
419N/A
419N/A close(dmd->pipeFD);
419N/A remove(dmd->pipename);
1088N/A free(dmd->pipename);
1088N/A free(dmd->buf);
1088N/A free(dmd->user.homedir);
1088N/A free(dmd);
781N/A
781N/A if (dmHandlerData == dmd) {
781N/A dmHandlerData = NULL;
781N/A }
0N/A}
0N/A
0N/Astatic void
419N/Adtlogin_receive_packet(struct dmdata *dmd)
0N/A{
419N/A int bufLen, nbRead;
419N/A char *p, *n;
419N/A int done = 0;
781N/A
419N/A if (dmd->buf == NULL) {
419N/A dmd->bufsize = BUFLEN;
1088N/A dmd->buf = malloc(dmd->bufsize);
419N/A dmd->buf[0] = '\0';
419N/A }
781N/A
419N/A /* Read data from pipe and split into tokens, buffering the rest */
419N/A while (!done) {
419N/A bufLen = strlen(dmd->buf);
0N/A
419N/A /*
419N/A * Realloc only if buf has filled up and we don't have a record
419N/A * delimiter yet. Keep track of alloced size.
419N/A */
419N/A if (bufLen > (dmd->bufsize/2)) {
419N/A dmd->bufsize += BUFLEN;
1088N/A dmd->buf = realloc(dmd->buf, dmd->bufsize);
419N/A }
0N/A
419N/A nbRead = read(dmd->pipeFD, dmd->buf + bufLen,
419N/A dmd->bufsize - bufLen - 1);
0N/A
419N/A if (nbRead == -1) {
419N/A if (errno == EINTR) {
419N/A continue;
419N/A } else if (errno == EAGAIN) {
419N/A return; /* return to WaitFor, wait for select() */
419N/A } else {
419N/A dtlogin_close_pipe(dmd);
419N/A return;
419N/A }
419N/A }
0N/A
419N/A if (nbRead == 0) { /* End of file */
419N/A dtlogin_close_pipe(dmd);
419N/A return;
419N/A }
781N/A
419N/A bufLen += nbRead;
419N/A dmd->buf[bufLen] = '\0';
0N/A
419N/A DtloginInfo("Data buffer: %s\n", dmd->buf);
0N/A
419N/A p = dmd->buf;
419N/A while ((n = strchr(p, ';')) != NULL) { /* Next complete packet */
419N/A *n = '\0';
419N/A DtloginInfo("Next packet: %s\n", p);
419N/A done = dtlogin_parse_packet(dmd, p);
419N/A if (done) {
419N/A dtlogin_close_pipe(dmd); /* free's dmd */
419N/A return;
419N/A }
419N/A p = n+1;
419N/A }
0N/A
419N/A /* save the rest for the next iteration */
419N/A if (p < (dmd->buf + bufLen)) {
419N/A DtloginInfo("Left over: %s\n", p);
419N/A strcpy(dmd->buf, p);
419N/A }
0N/A }
0N/A}
0N/A
419N/A/* Parse data from packet
781N/A *
419N/A * Example Record:
419N/A * UID="xxx" GID="yyy";
419N/A * G_LIST_ID="aaa" G_LIST_ID="bbb" G_LIST_ID="ccc";
419N/A * HOME="/nn/mmm/ooo" EOF="";
419N/A */
419N/Astatic int
419N/Adtlogin_parse_packet(struct dmdata *dmd, char *s)
419N/A{
419N/A char *k, *v, *p;
419N/A
419N/A while (s < (dmd->buf + dmd->bufsize)) {
419N/A /* format is key="value" - split into key & value pair */
419N/A
419N/A for (k = s ; (*k != '\0') && isspace(*k); k++) {
419N/A /* Skip over whitespace */
419N/A }
419N/A
419N/A if (*k == '\0') {
419N/A break;
419N/A }
419N/A
419N/A p = strchr(k, '=');
419N/A
419N/A if (p == NULL) {
419N/A DtloginInfo("Malformed key \'%s\'\n", k);
419N/A return 0;
419N/A }
419N/A
419N/A *p = '\0'; /* end of key string */
419N/A p++;
419N/A
419N/A if (*p != '\"') {
419N/A DtloginInfo("Bad delimiter at \'%s\'\n", p);
419N/A return 0;
419N/A }
781N/A
419N/A v = p + 1; /* start of value string */
419N/A
419N/A p = strchr(v, '\"');
419N/A
419N/A if (p == NULL) {
419N/A DtloginInfo("Missing quote after value \'%s\'\n", v);
419N/A break;
419N/A }
419N/A
419N/A *p = '\0'; /* end of value string */
419N/A
419N/A s = p + 1; /* start of next pair */
419N/A
419N/A DtloginInfo("Received key \"%s\" =>", k);
419N/A DtloginInfo(" value \"%s\"\n", v);
781N/A
419N/A /* Found key & value, now process & put into dmd */
419N/A if (strcmp(k, "EOF") == 0) {
419N/A /* End of transmission, process & close */
781N/A dtlogin_process(&(dmd->user), 1);
419N/A return 1;
419N/A }
419N/A else if (strcmp(k, "HOME") == 0) {
781N/A dmd->user.homedir = xstrdup(v);
419N/A }
419N/A else if ( (strcmp(k, "UID") == 0) || (strcmp(k, "GID") == 0)
419N/A || (strcmp(k, "G_LIST_ID") == 0) ) {
419N/A /* Value is numeric, convert to int */
1088N/A long val;
419N/A
419N/A errno = 0;
419N/A val = strtol(v, NULL, 10);
419N/A
419N/A if ((val == 0) && (strcmp(v, "0") != 0)) {
419N/A /* strtol couldn't parse the results */
419N/A DtloginInfo("Invalid number \"%s\"\n", v);
419N/A continue;
419N/A }
781N/A
419N/A if ( ((val == LONG_MAX) || (val == LONG_MIN))
419N/A && (errno == ERANGE) ) {
419N/A /* Value out of range */
419N/A DtloginInfo("Out of range number \"%s\"\n", v);
419N/A continue;
419N/A }
781N/A
419N/A if (strcmp(k, "UID") == 0) {
1088N/A dmd->user.uid = (uid_t) val;
781N/A }
419N/A else if (strcmp(k, "GID") == 0) {
1088N/A dmd->user.gid = (gid_t) val;
419N/A }
419N/A else if (strcmp(k, "G_LIST_ID") == 0) {
781N/A if (dmd->user.groupid_cnt < NGROUPS_UMAX) {
1088N/A dmd->user.groupids[dmd->user.groupid_cnt++] = (gid_t) val;
419N/A }
419N/A }
419N/A }
419N/A else {
419N/A DtloginInfo("Unrecognized key \"%s\"\n", k);
419N/A }
419N/A }
419N/A
419N/A return 0;
419N/A}
419N/A
419N/A
419N/Astatic void
781N/Adtlogin_process(struct dmuser *user, int user_logged_in)
419N/A{
419N/A struct project proj;
419N/A char proj_buf[PROJECT_BUFSZ];
419N/A struct passwd *ppasswd;
419N/A const char *auth_file = NULL;
781N/A
419N/A auth_file = GetAuthFilename();
419N/A
419N/A if (auth_file) {
781N/A if (chown(auth_file, user->uid, user->gid) < 0)
781N/A DtloginError("Error in changing owner to %d", user->uid);
419N/A }
419N/A
419N/A /* This gid dance is necessary in order to make sure
419N/A our "saved-set-gid" is 0 so that we can regain gid
419N/A 0 when necessary for priocntl & power management.
781N/A The first step sets rgid to the user's gid and
419N/A makes the egid & saved-gid be 0. The second then
419N/A sets the egid to the users gid, but leaves the
419N/A saved-gid as 0. */
419N/A
781N/A if (user->gid != (gid_t) -1) {
781N/A DtloginInfo("Setting gid to %d\n", user->gid);
781N/A
781N/A if (setregid(user->gid, 0) < 0)
781N/A DtloginError("Error in setting regid to %d\n", user->gid);
419N/A
781N/A if (setegid(user->gid) < 0)
781N/A DtloginError("Error in setting egid to %d\n", user->gid);
419N/A }
781N/A
781N/A if (user->groupid_cnt >= 0) {
781N/A if (setgroups(user->groupid_cnt, user->groupids) < 0)
781N/A DtloginError("Error in setting supplemental (%d) groups",
781N/A user->groupid_cnt);
419N/A }
781N/A
419N/A
419N/A /*
419N/A * BUG: 4462531: Set project ID for Xserver
781N/A * Get user name and default project.
781N/A * Set before the uid value is set.
419N/A */
781N/A if (user->projid != (uid_t) -1) {
781N/A if (settaskid(user->projid, TASK_NORMAL) == (taskid_t) -1) {
781N/A DtloginError("Error in setting project id to %d", user->projid);
781N/A }
781N/A } else if (user->uid != (uid_t) -1) {
781N/A ppasswd = getpwuid(user->uid);
419N/A
781N/A if (ppasswd == NULL) {
781N/A DtloginError("Error in getting user name for %d", user->uid);
781N/A } else {
781N/A if (getdefaultproj(ppasswd->pw_name, &proj,
781N/A (void *)&proj_buf, PROJECT_BUFSZ) == NULL) {
781N/A DtloginError("Error in getting project id for %s",
781N/A ppasswd->pw_name);
781N/A } else {
781N/A DtloginInfo("Setting project to %s\n", proj.pj_name);
419N/A
781N/A if (setproject(proj.pj_name, ppasswd->pw_name,
781N/A TASK_NORMAL) == -1) {
781N/A DtloginError("Error in setting project to %s",
781N/A proj.pj_name);
781N/A }
781N/A }
781N/A }
419N/A }
419N/A
781N/A if (user->uid != (uid_t) -1) {
781N/A DtloginInfo("Setting uid to %d\n", user->uid);
781N/A
781N/A if (setreuid(user->uid, -1) < 0)
781N/A DtloginError("Error in setting ruid to %d", user->uid);
781N/A
781N/A if (setreuid(-1, user->uid) < 0)
781N/A DtloginError("Error in setting euid to %d", user->uid);
781N/A
781N/A /* Wrap closeScreen to allow resetting uid on closedown */
781N/A if ((user->uid != 0) && (user != &originalUser)) {
781N/A int i;
781N/A
1088N/A if (dixRegisterPrivateKey(dmScreenKey, PRIVATE_SCREEN, 0)) {
1088N/A for (i = 0; i < screenInfo.numScreens; i++)
1088N/A {
1088N/A ScreenPtr pScreen = screenInfo.screens[i];
1088N/A struct dmScreenPriv *pScreenPriv
1088N/A = calloc(1, sizeof(struct dmScreenPriv));
1088N/A
1088N/A dixSetPrivate(&pScreen->devPrivates, dmScreenKey,
1088N/A pScreenPriv);
419N/A
1088N/A if (pScreenPriv != NULL) {
1088N/A pScreenPriv->CloseScreen = pScreen->CloseScreen;
1088N/A pScreen->CloseScreen = DtloginCloseScreen;
1088N/A } else {
1088N/A DtloginError("Failed to allocate %d bytes"
1088N/A " for uid reset info",
1088N/A sizeof(struct dmScreenPriv));
1088N/A }
781N/A }
1088N/A } else {
1088N/A DtloginError("Failed to register screen private %s",
1088N/A "for uid reset info");
781N/A }
781N/A }
781N/A }
781N/A
781N/A if (user->homedir != NULL) {
781N/A char *env_str = Xprintf("HOME=%s", user->homedir);
781N/A
781N/A if (env_str == NULL) {
781N/A DtloginError("Not enough memory to setenv HOME=%s", user->homedir);
419N/A } else {
419N/A DtloginInfo("Setting %s\n",env_str);
781N/A
419N/A if (putenv(env_str) < 0)
781N/A DtloginError("Failed to setenv %s", env_str);
419N/A }
419N/A
781N/A if (chdir(user->homedir) < 0)
781N/A DtloginError("Error in changing working directory to %s",
781N/A user->homedir);
419N/A }
766N/A
781N/A /* Inform the kernel whether a user has logged in on this VT device */
766N/A if (xf86ConsoleFd != -1)
781N/A ioctl(xf86ConsoleFd, VT_SETDISPLOGIN, user_logged_in);
419N/A}