Debian bug report logs - #988 `script' is insecure, and general tty insecurity Package: bsdutils ; Reported by: iwj10@cus.cam.ac.uk (Ian Jackson); 139 days old . ----------------------------------------------------------------------- Message received at debian-bugs: From pixar.com!bruce Wed Jun 14 12:44:09 1995 Return-Path: Received: from pixar.com by mongo.pixar.com with smtp (Smail3.1.28.1 #15) id m0sLyMD-0007hOC; Wed, 14 Jun 95 12:44 PDT Received: from mongo.pixar.com by pixar.com with SMTP id AA16863 (5.67b/IDA-1.5 for debian-bugs-pipe@mongo.pixar.com); Wed, 14 Jun 1995 12:42:42 -0700 Received: by mongo.pixar.com (Smail3.1.28.1 #15) id m0sLyKr-00051OC; Wed, 14 Jun 95 12:42 PDT Message-Id: Date: Wed, 14 Jun 95 12:42 PDT From: bruce@pixar.com (Bruce Perens) To: debian-bugs@pixar.com, iwj10@cus.cam.ac.uk (Ian Jackson) Subject: Re: Bug#988: `script' is insecure, and general tty insecurity Here is a get_pseudo_tty() function that attempts to jettision pernicious listeners on the slave side. You can easily hack this to change the slave to be owned by the real UID. I've also included an execute() function that redirects input and output to the pseudo-tty. You can see how these are used and also find a driver for doing asynchronous I/O using the select() system call if you download the source for ax25-util. The calling sequence for get_pseudo_tty() and execute() is: int main(int argc, char * * argv, char * * environment) { int masterFD; int slaveFD; static const char * argumentVector = { "/bin/sh", 0 }; masterFD = get_pseudo_tty(&slaveFD); if ( masterFD < 0 ) complain_and_die(); /* * Start the client program with input and output directed * to the slave FD. Do I/O to that from the master FD. */ if ( !execute("/bin/sh", argumentVector, environment, slaveFD) ) complain_and_die(); ... } - Bruce BEGIN pseudo_tty.c /* AX.25 Utilities: Attach an interface. * Bruce Perens, November 1994 * * Copyright 1994 Bruce Perens. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #include #include #include #include #include #include #include #define NAME_SIZE 64 static const char Prototype[] = "/dev/pty"; #define PROTOTYPE_BASE 5 /* index to "pty" in prototype. */ static int open_pseudo_tty(const char * name, int * slave) { char slaveName[NAME_SIZE]; int master = open(name, O_RDWR, 0); struct termios t; if ( master < 0 ) return -1; strcpy(slaveName, name); slaveName[PROTOTYPE_BASE] = 't'; /* Close master again to jettison any pernicious listeners on slave * side. I'd like to be able to lock opens on the slave side while * this is going on. This won't work if you're not root. */ chown(slaveName, 0, 0); chmod(slaveName, 0600); close(master); /* * Closing the master hung up on any listeners on the slave side. They * can't open it again unless they are root. */ if ( (master = open(name, O_RDWR, 0)) < 0 ) return -1; if ( (*slave = open(slaveName, O_RDWR, 0)) < 0 ) { close(master); return -1; } if ( tcgetattr(*slave, &t) == 0 ) { /* * Attempt to provide a consistent environment upon open. * Of course if you are running a script you can override * this by running stty. */ t.c_cc[VINTR] = 'c' & 0x1f; t.c_cc[VQUIT] = '\\' & 0x1f; t.c_cc[VERASE] = 'h' & 0x1f; t.c_cc[VKILL] = 'u' & 0x1f; t.c_cc[VEOF] = 'd' & 0x1f; t.c_cc[VEOL] = '\n'; t.c_cc[VSTOP] = 's' & 0x1f; t.c_cc[VSTART] = 'q' & 0x1f; t.c_cc[VSUSP] = 'z' & 0x1f; t.c_cc[VLNEXT] = 'v' & 0x1f; t.c_cc[VWERASE] = 'w' & 0x1f; t.c_cc[VREPRINT]= 'r' & 0x1f; t.c_cc[VDISCARD]= 'o' & 0x1f; t.c_iflag = BRKINT|ICRNL; t.c_oflag = OPOST; t.c_cflag = B9600|CS8|CREAD|HUPCL; t.c_lflag = ISIG|ICANON|ECHO|ECHOE; t.c_line = 0; tcsetattr(*slave, TCSANOW, &t); } return master; } int get_pseudo_tty(int * slave) { char name[NAME_SIZE]; char * const ones = &name[sizeof(Prototype)]; char * const tens = &name[sizeof(Prototype) - 1]; strcpy(name, Prototype); name[sizeof(Prototype) + 1] = '\0'; for ( *tens = 'p'; *tens <= 's'; ++*tens ) { int n; for ( n = 0; n < 16; n++ ) { static const char Hexits[16] = "0123456789abcdef"; int master; *ones = Hexits[n]; master = open_pseudo_tty(name, slave); if ( master >= 0 ) return master; } } return -1; } BEGIN execute.c /* AX.25 Utilities: Run a program, with input and output directed * to a file descriptor. * * * Bruce Perens, November 1994 * * Copyright 1994 Bruce Perens. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #include #include #include int execute( const char * file ,char * const * argv ,char * const * envp ,int ioFile) { int pid; signal(SIGCLD, SIG_IGN); /* No zombies */ pid = fork(); if ( pid < 0 ) return -1; else if ( pid == 0 ) { /* In the child process */ int max = (int)sysconf(_SC_OPEN_MAX); int fd; int n; for ( fd = 0; fd < max; fd++ ) { if ( fd != ioFile ) close(fd); } /* * If the IO file isn't 0, 1, or 2, I'm starting a new * session on some other device. */ if ( ioFile > 2 ) setsid(); for ( fd = 0; fd <= 2; fd++ ) { if ( fd != ioFile && dup2(ioFile, fd) != fd ) _exit(-1); } if ( ioFile > 2 ) close(ioFile); /* * Try to give the process as pristine an environment * as possible. */ for ( n = 0; n < NSIG; n++ ) signal(n, SIG_DFL); /* * If I started a new session above, set the tty process * group to match it. */ if ( ioFile > 2 ) tcsetpgrp(0, getpgrp()); execve(file, argv, envp); _exit(-1); } return pid; } -- -- Attention Ham Radio Operators: For information on "Linux for Hams", read -- the World Wide Web page http://www.hams.com/perens/LinuxForHams, or send -- an e-mail message containing the word "help" to info@hams.com . ----------------------------------------------------------------------- Acknowledgement sent to bruce@pixar.com (Bruce Perens) : Extra info received and forwarded. Full text available. ----------------------------------------------------------------------- Information forwarded to debian-devel@pixar.com : Bug#988 ; Package bsdutils . Full text available. ----------------------------------------------------------------------- Message received at debian-bugs: From cus.cam.ac.uk!iwj10 Wed Jun 14 05:54:23 1995 Return-Path: Received: from pixar.com by mongo.pixar.com with smtp (Smail3.1.28.1 #15) id m0sLrxe-0007mTC; Wed, 14 Jun 95 05:54 PDT Received: from bootes.cus.cam.ac.uk by pixar.com with SMTP id AA25300 (5.67b/IDA-1.5 for debian-bugs-pipe@mongo.pixar.com); Wed, 14 Jun 1995 05:52:52 -0700 Received: by bootes.cus.cam.ac.uk (Smail-3.1.29.0 #36) id m0sLrxB-000C01C; Wed, 14 Jun 95 13:53 BST Received: by chiark id (Debian /\oo/\ Smail3.1.29.1 #29.32); Wed, 14 Jun 95 13:37 BST Message-Id: Date: Wed, 14 Jun 95 13:37 BST From: iwj10@cus.cam.ac.uk (Ian Jackson) To: Debian bugs submission address Subject: `script' is insecure, and general tty insecurity Package: bsdutils Version: 1.2-1 chiark:~> tty /dev/ttyp3 chiark:~> script Script started, output file is typescript chiark:~> tty /dev/ttyp7 chiark:~> ls -al /dev/ttyp3 /dev/ttyp7 crw--w--w- 1 ian ian 4, 195 Jun 14 13:31 /dev/ttyp3 crw-rw-rw- 1 root root 4, 199 Jun 14 13:31 /dev/ttyp7 chiark:~> exit exit Script done, output file is typescript chiark:~> ls -al /dev/ttyp3 /dev/ttyp7 crw--w--w- 1 ian ian 4, 195 Jun 14 13:31 /dev/ttyp3 crw-rw-rw- 1 root root 4, 199 Jun 14 13:31 /dev/ttyp7 chiark:~> Clearly /dev/ttyp7 should, while script is running: * not be readable by everyone * be owned by the user (so that they can use mesg and biff) * have mesg off by default Fixing this will require the intervention of a setuid root program (either script will have to be setuid or another program will have to be made). There may be other security problems, notably races in the pty allocation. In general this is a very messy area, and the solutions to the problems here are likely to involve nontrivial amounts of thought, coding and/or introduction of additional software. This problem with programs like `script' is common on many unices, but we should arrange to find solutions at least for programs we supply. There are other problems related to having globally-writeable tty's. IMO tty's should be made group-writeable only by a special group (conventially called `tty'), to which all programs like `write' and `talk' will have to be setgid. This is probably a major undertaking, though, requiring changes to login, telnet, &c &c Ian. ----------------------------------------------------------------------- Acknowledgement sent to iwj10@cus.cam.ac.uk (Ian Jackson) : New bug report received and forwarded. Full text available. ----------------------------------------------------------------------- Report forwarded to debian-devel@pixar.com : Bug#988 ; Package bsdutils . Full text available. ----------------------------------------------------------------------- Ian Jackson / iwj10@thor.cam.ac.uk , with the debian-bugs tracking mechanism This page last modified 07:43:01 GMT Wed 01 Nov