#include #include #include #include #include #include #include #include #include #include #include static int console_owner(uid_t, int); int main(int argc, char **argv) { int console; uid_t uid; struct vt_stat origstate; int openvtnum; char openvtname[256]; int openvt; gid_t gid; int chowned; FILE *fp; struct termios t; char pass[256], *nl; int outfd, passlen; ssize_t wrote; console=open("/dev/console", O_RDWR); uid=getuid(); gid=getgid(); seteuid(uid); openlog(argv[0], LOG_PID, LOG_DAEMON); if(argc!=4) { syslog(LOG_WARNING, "Usage error"); return 1; } if(console<0) { syslog(LOG_ERR, "open(/dev/console): %m"); return 1; } if(ioctl(console, VT_GETSTATE, &origstate)<0) { syslog(LOG_ERR, "VT_GETSTATE: %m"); return 1; } if(uid) { if(!console_owner(uid, origstate.v_active)) { int i; for(i=0;i<64;++i) { if(i!=origstate.v_active && console_owner(uid, i)) break; } if(i==64) { syslog(LOG_WARNING, "run by uid %lu not at console", (unsigned long)uid); return 1; } } } if(ioctl(console, VT_OPENQRY, &openvtnum)<0) { syslog(LOG_ERR, "VT_OPENQRY: %m"); return 1; } if(openvtnum==-1) { syslog(LOG_ERR, "No free VTs"); return 1; } snprintf(openvtname, sizeof openvtname, "/dev/tty%d", openvtnum); seteuid(0); openvt=open(openvtname, O_RDWR); if(openvt<0) { seteuid(uid); syslog(LOG_ERR, "open(%s): %m", openvtname); return 1; } chowned=fchown(openvt, uid, gid); if(chowned<0) { seteuid(uid); syslog(LOG_ERR, "fchown(%s): %m", openvtname); return 1; } close(console); if(ioctl(openvt, VT_ACTIVATE, openvtnum)<0) { seteuid(uid); syslog(LOG_ERR, "VT_ACTIVATE(%d): %m", openvtnum); return 1; } while(ioctl(openvt, VT_WAITACTIVE, openvtnum)<0) { if(errno!=EINTR) { ioctl(openvt, VT_ACTIVATE, origstate.v_active); seteuid(uid); syslog(LOG_ERR, "VT_WAITACTIVE(%d): %m", openvtnum); return 1; } } seteuid(uid); fp=fdopen(openvt, "r+"); if(!fp) { seteuid(0); ioctl(openvt, VT_ACTIVATE, origstate.v_active); seteuid(uid); syslog(LOG_ERR, "fdopen(%s): %m", openvtname); return 1; } if(tcgetattr(openvt, &t)<0) { seteuid(0); ioctl(openvt, VT_ACTIVATE, origstate.v_active); seteuid(uid); syslog(LOG_ERR, "tcgetattr(%s): %m", openvtname); return 1; } t.c_lflag &= ~ECHO; if(tcsetattr(openvt, TCSANOW, &t)<0) { seteuid(0); ioctl(openvt, VT_ACTIVATE, origstate.v_active); seteuid(uid); syslog(LOG_ERR, "tcsetattr(%s): %m", openvtname); return 1; } if(fprintf(fp, "\033[2J\033[H")<0) { seteuid(0); ioctl(openvt, VT_ACTIVATE, origstate.v_active); seteuid(uid); syslog(LOG_ERR, "write error on %s: %m", openvtname); return 1; } if(argv[1][0] && argv[2][0]) { if(fprintf(fp, "Password for PPP client %s on server %s: ", argv[1], argv[2])<0) { seteuid(0); ioctl(openvt, VT_ACTIVATE, origstate.v_active); seteuid(uid); syslog(LOG_ERR, "write error on %s: %m", openvtname); return 1; } } else if(argv[1][0] && !argv[2][0]) { if(fprintf(fp, "Password for PPP client %s: ", argv[1])<0) { syslog(LOG_ERR, "write error on %s: %m", openvtname); seteuid(0); ioctl(openvt, VT_ACTIVATE, origstate.v_active); seteuid(uid); return 1; } } else if(!argv[1][0] && argv[2][0]) { if(fprintf(fp, "Password for PPP on server %s: ", argv[2])<0) { seteuid(0); ioctl(openvt, VT_ACTIVATE, origstate.v_active); seteuid(uid); syslog(LOG_ERR, "write error on %s: %m", openvtname); return 1; } } else { if(fprintf(fp, "Enter PPP password: ")<0) { seteuid(0); ioctl(openvt, VT_ACTIVATE, origstate.v_active); seteuid(uid); syslog(LOG_ERR, "write error on %s: %m", openvtname); return 1; } } if(!fgets(pass, sizeof pass, fp)) { seteuid(0); ioctl(openvt, VT_ACTIVATE, origstate.v_active); seteuid(uid); if(ferror(fp)) { syslog(LOG_ERR, "read error on %s: %m", openvtname); } return 1; } if((nl=strchr(pass, '\n'))) *nl=0; passlen=strlen(pass); outfd=atoi(argv[3]); if((wrote=write(outfd, pass, passlen))!=passlen) { seteuid(0); ioctl(openvt, VT_ACTIVATE, origstate.v_active); seteuid(uid); if(wrote<0) syslog(LOG_ERR, "write error on outpipe: %m"); else syslog(LOG_ERR, "short write on outpipe"); return 1; } seteuid(0); ioctl(openvt, VT_ACTIVATE, origstate.v_active); seteuid(uid); return 0; } static int console_owner(uid_t uid, int cons) { char name[256]; struct stat st; snprintf(name, sizeof name, "/dev/tty%d", cons); if(stat(name, &st)<0) { if(errno!=ENOENT) syslog(LOG_ERR, "stat(%s): %m", name); return 0; } return uid==st.st_uid; }