I administer a Gentoo Hardened box that uses file capabilities to eliminate most of the need for setuid-root binaries (e.g. /bin/ping
has CAP_NET_RAW, etc).
Infact, the only binary I have left is this one:
abraxas ~ # find / -xdev -type f -perm -u=s
/usr/lib64/misc/glibc/pt_chown
abraxas ~ #
If I remove the setuid bit, or remount my root filesystem nosuid
, sshd and GNU Screen stop working, because they call grantpt(3)
on their master pesudoterminals and glibc apparently executes this program to chown and chmod the slave pseudoterminal under /dev/pts/
, and GNU Screen cares about when this function fails.
The problem is, the manpage for grantpt(3)
explicitly states that under Linux, with the devpts
filesystem mounted, no such helper binary is required; the kernel will automatically set the UID & GID of the slave to the real UID & GID of the process that opened /dev/ptmx
(by calling getpt(3)
).
I have written a small example program to demonstrate this:
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
int main(void)
{
int master;
char slave[16];
struct stat slavestat;
if ((master = getpt()) < 0) {
fprintf(stderr, "getpt: %m\n");
return 1;
}
printf("Opened a UNIX98 master terminal, fd = %d\n", master);
/* I am not going to call grantpt() because I am trying to
* demonstrate that it is not necessary with devpts mounted,
* the owners and mode will be set automatically by the kernel.
*/
if (unlockpt(master) < 0) {
fprintf(stderr, "unlockpt: %m\n");
return 2;
}
memset(slave, 0, sizeof(slave));
if (ptsname_r(master, slave, sizeof(slave)) < 0) {
fprintf(stderr, "ptsname: %m\n");
return 2;
}
printf("Device name of slave pseudoterminal: %s\n", slave);
if (stat(slave, &slavestat) < 0) {
fprintf(stderr, "stat: %m\n");
return 3;
}
printf("Information for device %s:\n", slave);
printf(" Owner UID: %d\n", slavestat.st_uid);
printf(" Owner GID: %d\n", slavestat.st_gid);
printf(" Octal mode: %04o\n", slavestat.st_mode & 00007777);
return 0;
}
Observe it in action with the setuid bit on the aforementioned program removed:
aaron@abraxas ~ $ id
uid=1000(aaron) gid=100(users) groups=100(users)
aaron@abraxas ~ $ ./ptytest
Opened a UNIX98 master terminal, fd = 3
Device name of slave pseudoterminal: /dev/pts/17
Information for device /dev/pts/17:
Owner UID: 1000
Owner GID: 100
Octal mode: 0620
I have only a few ideas as to how to work around this problem:
1) Replace the program with a skeleton that simply returns 0.
2) Patch grantpt() in my libc to do nothing.
I can automate both of these, but does anyone have a recommendation for one over the other, or recommendations for how else to solve this?
Once this is solved, I can finally mount -o remount,nosuid /
.
pty
(as they should) but for the program? – vonbrand Mar 16 '13 at 22:03line 270
ofpty.c
in version4.0.3
of GNU Screen, it will abort ifgrantpt()
returns a non-zero result. This is exactly what will happen if an unprivileged user attempts to use GNU Screen with anosuid
root filesystem, becausegrantpt()
calls the helper binary regardless of whetherdevpts
is mounted, and the helper binary will return a non-zero result if its EUID is not 0. – Aaron Jones Mar 16 '13 at 22:50