From kde-core-devel Tue Jan 13 18:37:55 2004 From: Oswald Buddenhagen Date: Tue, 13 Jan 2004 18:37:55 +0000 To: kde-core-devel Subject: [PATCH] kpty weirdness (was: Re: kdelibs/kdecore) Message-Id: <20040113183755.GA21611 () ugly ! local> X-MARC-Message: https://marc.info/?l=kde-core-devel&m=107401918622176 MIME-Version: 1 Content-Type: multipart/mixed; boundary="--0F1p//8PRICkK4MW" --0F1p//8PRICkK4MW Content-Type: text/plain; charset=us-ascii Content-Disposition: inline On Mon, Jan 12, 2004 at 07:38:01PM +0200, Andy Fawcett wrote: > On Monday 12 January 2004 18:23, Oswald Buddenhagen wrote: > > On Mon, Jan 12, 2004 at 06:17:02PM +0200, Andy Fawcett wrote: > > > On Saturday 10 January 2004 20:35, Oswald Buddenhagen wrote: > > > > CVS commit by ossi: > > > > > > > > trying to fix #67464 by not trying to chownpty if everything is > > > > ok already. some cleanup as well. ok'd by waldo. > > > > > > > > > > > > M +69 -75 kpty.cpp 1.15 > > > > > > This change prevents konsole from starting on FreeBSD. > > > > this stuff is weird ... > > Yes. > and here is yet another attempt at fixing this stuff. the patch changes quite a lot of details, so i decided to adhere to the commit policy and post it. :) please review not only the patch, but also the explainations below. there are two types of pseudo ttys: unix98 and bsd. unix98: there is one pty master multiplexer device. every time you open it, you get a new pty pair; a device node for the pty slave appears in the /dev/pts/ virtual file system. when you close the master handle, the slave pty node disappears again. bsd: /dev/ is statically populated by a helluva lot of ptyp* and ttyp* device nodes, every such pair representing a pty. a pty is available, if one can open the master (every master can be opened only once at a time) and the permissions of the slave being rw-rw-rw-, i.e. available to everybody. the latter means, that it is mandatory to reset the permissions when shutting down the pty. now to the patch. #) denotes reverts of (much) earlier patches of mine, so those parts can't be that wrong. :) - #) now i think grantpt is guaranteed to work only with unix98 ptys, so use it only in that branch and use the hand-crafted code for bsd ptys. - #) in the bsd pty branch, remove the geteuid() == 0 before the access() call. i had mistaken this as a permission check - in fact, it is part of the availability check. - entirely remove the openpty() part. it is redundand: it is only a neat encapsulation of all the code below, so one of the two can go away. as for some (to me) absolutely incomprehensible reasons the openpty() based variant causes total failure on tap's freebsd setup, the choice is simple. :) - the bsd pty branch is guaranteed to be unable to chown the pty if running as non-root (hence the check); the unix98 pty branch could fail if the grantpt() call offered by the system does not have an own variant of kgrantpty behind the scenes. therefore after both branches a check is done if the pty slave node ownership and permissions are ok, and if not, we call kgrantpty. - pty slave node group ownership: the preferred gid is "tty". if that fails, try "wheel". if that fails, too, use the user's gid. when shutting down, we check if the pty's gid is the current user's gid - if so, we assume that no appropriate group was found at setup time and therefore reset it to root. otherwise we leave it as-is. this is now consistent for both the cases with and without kgrantpty, and is not dependant on the os. - don't bother to reset pty slave ownership of unix98 ptys, as they vanish after closing the master anyway. - BSD only: revoke() moved to a place where we are sure to own the pty slave, and therefore have permission to revoke() it. therefore it needn't to be in kgrantpty. - in kgrantpty, first try ptsname instead of the weird pty -> tty transformation that works only with bsd ptys. iow, make kgrantpty work with unix98 ptys in the first place (just in case grantpt() fails as non-root on some systems). ok, that's it except for some minor cleanups. maybe somebody can verify whether this fixes #22479. greetings -- Hi! I'm a .signature virus! Copy me into your ~/.signature, please! -- Chaos, panic, and disorder - my work here is done. --0F1p//8PRICkK4MW Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="kpty.diff" Index: kgrantpty.c =================================================================== RCS file: /home/kde/kdelibs/kdecore/kgrantpty.c,v retrieving revision 1.13 diff -U2 -r1.13 kgrantpty.c --- kgrantpty.c 20 Nov 2003 15:53:10 -0000 1.13 +++ kgrantpty.c 13 Jan 2004 17:36:44 -0000 @@ -25,6 +25,7 @@ */ +#include + #include -#include /* for BSD */ #include #include @@ -46,5 +47,4 @@ int main (int argc, char *argv[]) { - char* pty; struct stat st; struct group* p; @@ -72,84 +72,68 @@ } - /* setup parameters for the operation ***********************************/ - - if (!strcmp(argv[1],"--grant")) - { - uid = getuid(); /* current user id */ - mod = S_IRUSR | S_IWUSR | S_IWGRP; - } - else - { - uid = 0; /* root */ - mod = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH; - } - /* Get the group ID of the special `tty' group. */ - p = getgrnam(TTY_GROUP); /* posix */ - gid = p ? p->gr_gid : getgid (); /* posix */ fd = atoi(argv[2]); - /* get slave pty name from master pty file handle in PTY_FILENO *********/ - - /* Check that fd is a valid master pseudo terminal. */ - pty = ttyname(fd); /* posix */ - -#if defined(BSD_PTY_HACK) - if (pty == NULL) + /* get slave pty name from master pty file handle *********/ +#ifdef HAVE_PTSNAME + tty = ptsname(fd); + if (!tty) +#endif { - /* - Hack to make kgrantpty work on some versions of FreeBSD (and possibly - other systems): ttyname(3) does not work with a file descriptor opened - on a /dev/pty?? device. - - Instead, this code looks through all the devices in /dev for a device - which has the same inode as our PTY_FILENO descriptor... if found, we - have the name for our pty. - */ - - struct dirent *dirp; - DIR *dp; - struct stat dsb; - - if (uid == 0) { - p = getgrnam("wheel"); - gid = p ? p->gr_gid : getgid(); - } + /* Check that fd is a valid master pseudo terminal. */ + char *pty = ttyname(fd); - if (fstat(fd, &dsb) != -1) { - if ((dp = opendir(_PATH_DEV)) != NULL) { - while ((dirp = readdir(dp))) { - if (dirp->d_fileno != dsb.st_ino) - continue; - pty = malloc(sizeof(_PATH_DEV) + strlen(dirp->d_name)); - if (pty) { - strcpy(pty, _PATH_DEV); - strcat(pty, dirp->d_name); +#ifdef BSD_PTY_HACK + if (pty == NULL) + { + /* + Hack to make kgrantpty work on some versions of FreeBSD (and possibly + other systems): ttyname(3) does not work with a file descriptor opened + on a /dev/pty?? device. + + Instead, this code looks through all the devices in /dev for a device + which has the same inode as our PTY_FILENO descriptor... if found, we + have the name for our pty. + */ + + struct dirent *dirp; + DIR *dp; + struct stat dsb; + + if (fstat(fd, &dsb) != -1) { + if ((dp = opendir(_PATH_DEV)) != NULL) { + while ((dirp = readdir(dp))) { + if (dirp->d_fileno != dsb.st_ino) + continue; + pty = malloc(sizeof(_PATH_DEV) + strlen(dirp->d_name)); + if (pty) { + strcpy(pty, _PATH_DEV); + strcat(pty, dirp->d_name); + } + break; } - break; - } - (void) closedir(dp); + (void) closedir(dp); + } } } - } #endif - if (pty == NULL) - { - fprintf(stderr,"%s: cannot determine pty name.\n",argv[0]); - return 1; /* FAIL */ - } - close(fd); + if (pty == NULL) + { + fprintf(stderr,"%s: cannot determine pty name.\n",argv[0]); + return 1; /* FAIL */ + } - /* matches /dev/pty?? */ - if (strlen(pty) < 8 || strncmp(pty,"/dev/pty",8)) - { - fprintf(stderr,"%s: determined a strange pty name `%s'.\n",argv[0],pty); - return 1; /* FAIL */ - } + /* matches /dev/pty?? */ + if (memcmp(pty,"/dev/pty",8)) + { + fprintf(stderr,"%s: determined a strange pty name `%s'.\n",argv[0],pty); + return 1; /* FAIL */ + } - tty = malloc(strlen(pty) + 1); - strcpy(tty,"/dev/tty"); - strcat(tty,pty+8); + tty = malloc(strlen(pty) + 1); + strcpy(tty,"/dev/tty"); + strcat(tty,pty+8); + } /* Check that the returned slave pseudo terminal is a character device. */ @@ -160,4 +144,22 @@ } + /* setup parameters for the operation ***********************************/ + + if (!strcmp(argv[1],"--grant")) + { + uid = getuid(); + p = getgrnam(TTY_GROUP); + if (!p) + p = getgrnam("wheel"); + gid = p ? p->gr_gid : getgid (); + mod = S_IRUSR | S_IWUSR | S_IWGRP; + } + else + { + uid = 0; + gid = st.st_gid == getgid () ? 0 : -1; + mod = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH; + } + /* Perform the actual chown/chmod ************************************/ @@ -174,12 +176,4 @@ } -#ifdef BSD - if (revoke(tty) < 0) - { - fprintf(stderr,"%s: cannot revoke %s: %s\n",argv[0],tty,strerror(errno)); - return 1; /* FAIL */ - } -#endif - return 0; /* OK */ } Index: kpty.cpp =================================================================== RCS file: /home/kde/kdelibs/kdecore/kpty.cpp,v retrieving revision 1.18 diff -U2 -r1.18 kpty.cpp --- kpty.cpp 13 Jan 2004 01:02:29 -0000 1.18 +++ kpty.cpp 13 Jan 2004 17:36:44 -0000 @@ -159,5 +159,5 @@ struct KPtyPrivate { KPtyPrivate() : - xonXoff(false), needGrantPty(false), + xonXoff(false), masterFd(-1), slaveFd(-1) { @@ -168,5 +168,4 @@ bool xonXoff : 1; - bool needGrantPty : 1; int masterFd; int slaveFd; @@ -204,13 +203,6 @@ // We try, as we know them, one by one. -#if defined(HAVE_OPENPTY) - if (openpty(&d->masterFd, &d->slaveFd, NULL, NULL, NULL) == 0) - { - d->ttyName = ttyname(d->slaveFd); - goto gotpty2; - } -#endif -#if defined(HAVE_PTSNAME) +#if defined(HAVE_PTSNAME) && defined(HAVE_GRANTPT) #ifdef _AIX d->masterFd = ::open("/dev/ptc",O_RDWR); @@ -222,6 +214,7 @@ char *ptsn = ptsname(d->masterFd); if (ptsn) { + grantpt(d->masterFd); d->ttyName = ptsn; - goto gotpty1; + goto gotpty; } else { ::close(d->masterFd); @@ -254,6 +247,18 @@ } #endif /* sun */ - if (geteuid() == 0 || access(d->ttyName.data(),R_OK|W_OK) == 0) - goto gotpty1; // Success !! + if (!access(d->ttyName.data(),R_OK|W_OK)) // checks availability based on permission bits + { + if (!geteuid()) + { + struct group* p = getgrnam(TTY_GROUP); + if (!p) + p = getgrnam("wheel"); + gid_t gid = p ? p->gr_gid : getgid (); + + chown(d->ttyName.data(), getuid(), gid); + chmod(d->ttyName.data(), S_IRUSR|S_IWUSR|S_IWGRP); + } + goto gotpty; + } ::close(d->masterFd); d->masterFd = -1; @@ -265,28 +270,11 @@ return false; - gotpty1: -#if defined(HAVE_GRANTPT) - grantpt(d->masterFd); -#else - { - /* Get the group ID of the special `tty' group. */ - struct group* p = getgrnam(TTY_GROUP); /* posix */ - gid_t gid = p ? p->gr_gid : getgid (); /* posix */ - - chown(d->ttyName.data(), getuid(), gid); - chmod(d->ttyName.data(), S_IRUSR|S_IWUSR|S_IWGRP); - } -#endif - gotpty2: + gotpty: struct stat st; if (stat(d->ttyName.data(), &st)) return false; // this just cannot happen ... *cough* - d->needGrantPty = st.st_uid != getuid() || (st.st_mode & (S_IRGRP|S_IXGRP|S_IROTH|S_IWOTH|S_IXOTH)); - -#ifdef BSD - revoke(d->ttyName.data()); -#endif - - if (!chownpty(true)) + if (((st.st_uid != getuid()) || + (st.st_mode & (S_IRGRP|S_IXGRP|S_IROTH|S_IWOTH|S_IXOTH))) && + !chownpty(true)) { kdWarning(175) @@ -295,17 +283,19 @@ } +#ifdef BSD + revoke(d->ttyName.data()); +#endif + #ifdef HAVE_UNLOCKPT unlockpt(d->masterFd); #endif - if (d->slaveFd < 0) { // slave pty not yet opened? - d->slaveFd = ::open(d->ttyName, O_RDWR); - if (d->slaveFd < 0) - { - kdWarning(175) << "Can't open slave pseudo teletype" << endl; - ::close(d->masterFd); - d->masterFd = -1; - return false; - } + d->slaveFd = ::open(d->ttyName.data(), O_RDWR); + if (d->slaveFd < 0) + { + kdWarning(175) << "Can't open slave pseudo teletype" << endl; + ::close(d->masterFd); + d->masterFd = -1; + return false; } @@ -348,7 +338,17 @@ if (d->masterFd < 0) return; - chown(d->ttyName.data(), 0, 0); - chmod(d->ttyName.data(), S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH); - chownpty(false); + // don't bother resetting unix98 pty, it will go away after closing master anyway. + if (memcmp(d->ttyName.data(), "/dev/pts/", 9)) { + if (!geteuid()) { + struct stat st; + if (!stat(d->ttyName.data(), &st)) { + chown(d->ttyName.data(), 0, st.st_gid == getgid() ? 0 : -1); + chmod(d->ttyName.data(), S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH); + } + } else { + fcntl(d->masterFd, F_SETFD, 0); + chownpty(false); + } + } ::close(d->slaveFd); ::close(d->masterFd); @@ -492,8 +492,4 @@ bool KPty::chownpty(bool grant) { - if (!d->needGrantPty) - return true; - - fcntl(d->masterFd, F_SETFD, 0); KProcess proc; proc << locate("exe", BASE_CHOWN) << (grant?"--grant":"--revoke") << QString::number(d->masterFd); --0F1p//8PRICkK4MW--