1#include <stdio.h>
2#include <string.h>
3#include <stdlib.h>
4#include <errno.h>
5#include <unistd.h>
6#include <fcntl.h>
7#include <sys/stat.h>
8#include <sys/ioctl.h>
9#include <syslog.h>
10#include <termios.h>
11#include <sys/vt.h>
12
13static int console_owner(uid_t, int);
14
15int main(int argc, char **argv)
16{
17  int console;
18  uid_t uid;
19  struct vt_stat origstate;
20  int openvtnum;
21  char openvtname[256];
22  int openvt;
23  gid_t gid;
24  int chowned;
25  FILE *fp;
26  struct termios t;
27  char pass[256], *nl;
28  int outfd, passlen;
29  ssize_t wrote;
30  console=open("/dev/console", O_RDWR);
31
32  uid=getuid();
33  gid=getgid();
34  seteuid(uid);
35
36  openlog(argv[0], LOG_PID, LOG_DAEMON);
37
38  if(argc!=4) {
39    syslog(LOG_WARNING, "Usage error");
40    return 1;
41  }
42
43  if(console<0) {
44    syslog(LOG_ERR, "open(/dev/console): %m");
45    return 1;
46  }
47
48  if(ioctl(console, VT_GETSTATE, &origstate)<0) {
49    syslog(LOG_ERR, "VT_GETSTATE: %m");
50    return 1;
51  }
52
53  if(uid) {
54    if(!console_owner(uid, origstate.v_active)) {
55      int i;
56      for(i=0;i<64;++i) {
57        if(i!=origstate.v_active && console_owner(uid, i))
58          break;
59      }
60      if(i==64) {
61        syslog(LOG_WARNING, "run by uid %lu not at console", (unsigned long)uid);
62        return 1;
63      }
64    }
65  }
66
67  if(ioctl(console, VT_OPENQRY, &openvtnum)<0) {
68    syslog(LOG_ERR, "VT_OPENQRY: %m");
69    return 1;
70  }
71  if(openvtnum==-1) {
72    syslog(LOG_ERR, "No free VTs");
73    return 1;
74  }
75
76  snprintf(openvtname, sizeof openvtname, "/dev/tty%d", openvtnum);
77  seteuid(0);
78  openvt=open(openvtname, O_RDWR);
79  if(openvt<0) {
80    seteuid(uid);
81    syslog(LOG_ERR, "open(%s): %m", openvtname);
82    return 1;
83  }
84
85  chowned=fchown(openvt, uid, gid);
86  if(chowned<0) {
87    seteuid(uid);
88    syslog(LOG_ERR, "fchown(%s): %m", openvtname);
89    return 1;
90  }
91
92  close(console);
93
94  if(ioctl(openvt, VT_ACTIVATE, openvtnum)<0) {
95    seteuid(uid);
96    syslog(LOG_ERR, "VT_ACTIVATE(%d): %m", openvtnum);
97    return 1;
98  }
99
100  while(ioctl(openvt, VT_WAITACTIVE, openvtnum)<0) {
101    if(errno!=EINTR) {
102      ioctl(openvt, VT_ACTIVATE, origstate.v_active);
103      seteuid(uid);
104      syslog(LOG_ERR, "VT_WAITACTIVE(%d): %m", openvtnum);
105      return 1;
106    }
107  }
108
109  seteuid(uid);
110  fp=fdopen(openvt, "r+");
111  if(!fp) {
112    seteuid(0);
113    ioctl(openvt, VT_ACTIVATE, origstate.v_active);
114    seteuid(uid);
115    syslog(LOG_ERR, "fdopen(%s): %m", openvtname);
116    return 1;
117  }
118
119  if(tcgetattr(openvt, &t)<0) {
120    seteuid(0);
121    ioctl(openvt, VT_ACTIVATE, origstate.v_active);
122    seteuid(uid);
123    syslog(LOG_ERR, "tcgetattr(%s): %m", openvtname);
124    return 1;
125  }
126  t.c_lflag &= ~ECHO;
127  if(tcsetattr(openvt, TCSANOW, &t)<0) {
128    seteuid(0);
129    ioctl(openvt, VT_ACTIVATE, origstate.v_active);
130    seteuid(uid);
131    syslog(LOG_ERR, "tcsetattr(%s): %m", openvtname);
132    return 1;
133  }
134
135  if(fprintf(fp, "\033[2J\033[H")<0) {
136    seteuid(0);
137    ioctl(openvt, VT_ACTIVATE, origstate.v_active);
138    seteuid(uid);
139    syslog(LOG_ERR, "write error on %s: %m", openvtname);
140    return 1;
141  }
142  if(argv[1][0] && argv[2][0]) {
143    if(fprintf(fp, "Password for PPP client %s on server %s: ", argv[1], argv[2])<0) {
144      seteuid(0);
145      ioctl(openvt, VT_ACTIVATE, origstate.v_active);
146      seteuid(uid);
147      syslog(LOG_ERR, "write error on %s: %m", openvtname);
148      return 1;
149    }
150  } else if(argv[1][0] && !argv[2][0]) {
151    if(fprintf(fp, "Password for PPP client %s: ", argv[1])<0) {
152      syslog(LOG_ERR, "write error on %s: %m", openvtname);
153      seteuid(0);
154      ioctl(openvt, VT_ACTIVATE, origstate.v_active);
155      seteuid(uid);
156      return 1;
157    }
158  } else if(!argv[1][0] && argv[2][0]) {
159    if(fprintf(fp, "Password for PPP on server %s: ", argv[2])<0) {
160      seteuid(0);
161      ioctl(openvt, VT_ACTIVATE, origstate.v_active);
162      seteuid(uid);
163      syslog(LOG_ERR, "write error on %s: %m", openvtname);
164      return 1;
165    }
166  } else {
167    if(fprintf(fp, "Enter PPP password: ")<0) {
168      seteuid(0);
169      ioctl(openvt, VT_ACTIVATE, origstate.v_active);
170      seteuid(uid);
171      syslog(LOG_ERR, "write error on %s: %m", openvtname);
172      return 1;
173    }
174  }
175
176  if(!fgets(pass, sizeof pass, fp)) {
177    seteuid(0);
178    ioctl(openvt, VT_ACTIVATE, origstate.v_active);
179    seteuid(uid);
180    if(ferror(fp)) {
181      syslog(LOG_ERR, "read error on %s: %m", openvtname);
182    }
183    return 1;
184  }
185  if((nl=strchr(pass, '\n')))
186    *nl=0;
187  passlen=strlen(pass);
188
189  outfd=atoi(argv[3]);
190  if((wrote=write(outfd, pass, passlen))!=passlen) {
191    seteuid(0);
192    ioctl(openvt, VT_ACTIVATE, origstate.v_active);
193    seteuid(uid);
194    if(wrote<0)
195      syslog(LOG_ERR, "write error on outpipe: %m");
196    else
197      syslog(LOG_ERR, "short write on outpipe");
198    return 1;
199  }
200
201  seteuid(0);
202  ioctl(openvt, VT_ACTIVATE, origstate.v_active);
203  seteuid(uid);
204  return 0;
205}
206
207static int console_owner(uid_t uid, int cons)
208{
209  char name[256];
210  struct stat st;
211  snprintf(name, sizeof name, "/dev/tty%d", cons);
212  if(stat(name, &st)<0) {
213    if(errno!=ENOENT)
214      syslog(LOG_ERR, "stat(%s): %m", name);
215    return 0;
216  }
217  return uid==st.st_uid;
218}
219