kernel - Reduce lwp_signotify() latency
[dragonfly.git] / usr.sbin / sliplogin / sliplogin.c
1 /*-
2  * Copyright (c) 1990, 1993
3  *      The Regents of the University of California.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. Neither the name of the University nor the names of its contributors
14  *    may be used to endorse or promote products derived from this software
15  *    without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  *
29  * @(#) Copyright (c) 1990, 1993 The Regents of the University of California.  All rights reserved.
30  * @(#)sliplogin.c        8.2 (Berkeley) 2/1/94
31  * $FreeBSD: src/usr.sbin/sliplogin/sliplogin.c,v 1.9.6.2 2001/07/19 05:21:28 kris Exp $
32  * $DragonFly: src/usr.sbin/sliplogin/sliplogin.c,v 1.5 2008/09/14 22:04:36 swildner Exp $
33  */
34
35 /*
36  * sliplogin.c
37  * [MUST BE RUN SUID, SLOPEN DOES A SUSER()!]
38  *
39  * This program initializes its own tty port to be an async TCP/IP interface.
40  * It sets the line discipline to slip, invokes a shell script to initialize
41  * the network interface, then pauses forever waiting for hangup.
42  *
43  * It is a remote descendant of several similar programs with incestuous ties:
44  * - Kirk Smith's slipconf, modified by Richard Johnsson @ DEC WRL.
45  * - slattach, probably by Rick Adams but touched by countless hordes.
46  * - the original sliplogin for 4.2bsd, Doug Kingston the mover behind it.
47  *
48  * There are two forms of usage:
49  *
50  * "sliplogin"
51  * Invoked simply as "sliplogin", the program looks up the username
52  * in the file /etc/slip.hosts.
53  * If an entry is found, the line on fd0 is configured for SLIP operation
54  * as specified in the file.
55  *
56  * "sliplogin IPhostlogin </dev/ttyb"
57  * Invoked by root with a username, the name is looked up in the
58  * /etc/slip.hosts file and if found fd0 is configured as in case 1.
59  */
60
61 #include <sys/param.h>
62 #include <sys/socket.h>
63 #include <sys/file.h>
64 #include <sys/stat.h>
65 #include <syslog.h>
66 #include <netdb.h>
67
68 #include <termios.h>
69 #include <sys/ioctl.h>
70 #include <net/slip.h>
71 #include <net/if.h>
72
73 #include <stdio.h>
74 #include <errno.h>
75 #include <ctype.h>
76 #include <paths.h>
77 #include <string.h>
78 #include <unistd.h>
79 #include <stdlib.h>
80 #include <signal.h>
81 #include "pathnames.h"
82
83 extern char **environ;
84
85 static char *restricted_environ[] = {
86         "PATH=" _PATH_STDPATH,
87         NULL
88 };
89
90 int     unit;
91 int     slip_mode;
92 speed_t speed;
93 int     uid;
94 int     keepal;
95 int     outfill;
96 int     slunit;
97 char    loginargs[BUFSIZ];
98 char    loginfile[MAXPATHLEN];
99 char    loginname[BUFSIZ];
100 static char raddr[32];                  /* remote address */
101 char ifname[IFNAMSIZ];                  /* interface name */
102 static  char pidfilename[MAXPATHLEN];   /* name of pid file */
103 static  char iffilename[MAXPATHLEN];    /* name of if file */
104 static  pid_t   pid;                    /* our pid */
105
106 char *
107 make_ipaddr(void)
108 {
109 static char address[20] ="";
110 struct hostent *he;
111 unsigned long ipaddr;
112
113 address[0] = '\0';
114 if ((he = gethostbyname(raddr)) != NULL) {
115         ipaddr = ntohl(*(long *)he->h_addr_list[0]);
116         sprintf(address, "%lu.%lu.%lu.%lu",
117                 ipaddr >> 24,
118                 (ipaddr & 0x00ff0000) >> 16,
119                 (ipaddr & 0x0000ff00) >> 8,
120                 (ipaddr & 0x000000ff));
121         }
122
123 return address;
124 }
125
126 struct slip_modes {
127         char    *sm_name;
128         int     sm_or_flag;
129         int     sm_and_flag;
130 }        modes[] = {
131         "normal",       0        , 0        ,
132         "compress",     IFF_LINK0, IFF_LINK2,
133         "noicmp",       IFF_LINK1, 0        ,
134         "autocomp",     IFF_LINK2, IFF_LINK0,
135 };
136
137 void
138 findid(char *name)
139 {
140         FILE *fp;
141         static char slopt[5][16];
142         static char laddr[16];
143         static char mask[16];
144         char   slparmsfile[MAXPATHLEN];
145         char user[16];
146         char buf[128];
147         int i, j, n;
148
149         environ = restricted_environ; /* minimal protection for system() */
150
151         strncpy(loginname, name, sizeof(loginname)-1);
152         loginname[sizeof(loginname)-1] = '\0';
153
154         if ((fp = fopen(_PATH_ACCESS, "r")) == NULL) {
155         accfile_err:
156                 syslog(LOG_ERR, "%s: %m\n", _PATH_ACCESS);
157                 exit(1);
158         }
159         while (fgets(loginargs, sizeof(loginargs) - 1, fp)) {
160                 if (ferror(fp))
161                         goto accfile_err;
162                 if (loginargs[0] == '#' || isspace(loginargs[0]))
163                         continue;
164                 n = sscanf(loginargs, "%15s%*[ \t]%15s%*[ \t]%15s%*[ \t]%15s%*[ \t]%15s%*[ \t]%15s%*[ \t]%15s%*[ \t]%15s%*[ \t]%15s\n",
165                         user, laddr, raddr, mask, slopt[0], slopt[1],
166                         slopt[2], slopt[3], slopt[4]);
167                 if (n < 4) {
168                         syslog(LOG_ERR, "%s: wrong format\n", _PATH_ACCESS);
169                         exit(1);
170                 }
171                 if (strcmp(user, name) != 0)
172                         continue;
173
174                 fclose(fp);
175
176                 slip_mode = 0;
177                 for (i = 0; i < n - 4; i++) {
178                         for (j = 0; j < sizeof(modes)/sizeof(struct slip_modes);
179                                 j++) {
180                                 if (strcmp(modes[j].sm_name, slopt[i]) == 0) {
181                                         slip_mode |= (modes[j].sm_or_flag);
182                                         slip_mode &= ~(modes[j].sm_and_flag);
183                                         break;
184                                 }
185                         }
186                 }
187
188                 /*
189                  * we've found the guy we're looking for -- see if
190                  * there's a login file we can use.  First check for
191                  * one specific to this host.  If none found, try for
192                  * a generic one.
193                  */
194                 snprintf(loginfile, sizeof(loginfile), "%s.%s", _PATH_SLOGIN, name);
195                 if (access(loginfile, R_OK|X_OK) != 0) {
196                         strncpy(loginfile, _PATH_SLOGIN, sizeof(loginfile)-1);
197                         loginfile[sizeof(loginfile)-1] = '\0';
198                         if (access(loginfile, R_OK|X_OK)) {
199                                 syslog(LOG_ERR,
200                                        "access denied for %s - no %s\n",
201                                        name, _PATH_SLOGIN);
202                                 exit(5);
203                         }
204                 }
205                 snprintf(slparmsfile, sizeof(slparmsfile), "%s.%s", _PATH_SLPARMS, name);
206                 if (access(slparmsfile, R_OK|X_OK) != 0) {
207                         strncpy(slparmsfile, _PATH_SLPARMS, sizeof(slparmsfile)-1);
208                         slparmsfile[sizeof(slparmsfile)-1] = '\0';
209                         if (access(slparmsfile, R_OK|X_OK))
210                                 *slparmsfile = '\0';
211                 }
212                 keepal = outfill = 0;
213                 slunit = -1;
214                 if (*slparmsfile) {
215                         if ((fp = fopen(slparmsfile, "r")) == NULL) {
216                         slfile_err:
217                                 syslog(LOG_ERR, "%s: %m\n", slparmsfile);
218                                 exit(1);
219                         }
220                         n = 0;
221                         while (fgets(buf, sizeof(buf) - 1, fp) != NULL) {
222                                 if (ferror(fp))
223                                         goto slfile_err;
224                                 if (buf[0] == '#' || isspace(buf[0]))
225                                         continue;
226                                 n = sscanf(buf, "%d %d %d", &keepal, &outfill, &slunit);
227                                 if (n < 1) {
228                                 slwrong_fmt:
229                                         syslog(LOG_ERR, "%s: wrong format\n", slparmsfile);
230                                         exit(1);
231                                 }
232                                 fclose(fp);
233                                 break;
234                         }
235                         if (n == 0)
236                                 goto slwrong_fmt;
237                 }
238
239                 return;
240         }
241         syslog(LOG_ERR, "SLIP access denied for %s\n", name);
242         exit(4);
243         /* NOTREACHED */
244 }
245
246 char *
247 sigstr(int s)
248 {
249         static char buf[32];
250
251         switch (s) {
252         case SIGHUP:    return("HUP");
253         case SIGINT:    return("INT");
254         case SIGQUIT:   return("QUIT");
255         case SIGILL:    return("ILL");
256         case SIGTRAP:   return("TRAP");
257         case SIGIOT:    return("IOT");
258         case SIGEMT:    return("EMT");
259         case SIGFPE:    return("FPE");
260         case SIGKILL:   return("KILL");
261         case SIGBUS:    return("BUS");
262         case SIGSEGV:   return("SEGV");
263         case SIGSYS:    return("SYS");
264         case SIGPIPE:   return("PIPE");
265         case SIGALRM:   return("ALRM");
266         case SIGTERM:   return("TERM");
267         case SIGURG:    return("URG");
268         case SIGSTOP:   return("STOP");
269         case SIGTSTP:   return("TSTP");
270         case SIGCONT:   return("CONT");
271         case SIGCHLD:   return("CHLD");
272         case SIGTTIN:   return("TTIN");
273         case SIGTTOU:   return("TTOU");
274         case SIGIO:     return("IO");
275         case SIGXCPU:   return("XCPU");
276         case SIGXFSZ:   return("XFSZ");
277         case SIGVTALRM: return("VTALRM");
278         case SIGPROF:   return("PROF");
279         case SIGWINCH:  return("WINCH");
280 #ifdef SIGLOST
281         case SIGLOST:   return("LOST");
282 #endif
283         case SIGUSR1:   return("USR1");
284         case SIGUSR2:   return("USR2");
285         }
286         snprintf(buf, sizeof(buf), "sig %d", s);
287         return(buf);
288 }
289
290 void
291 hup_handler(int s)
292 {
293         char logoutfile[MAXPATHLEN];
294
295         close(0);
296         seteuid(0);
297         snprintf(logoutfile, sizeof(logoutfile), "%s.%s", _PATH_SLOGOUT, loginname);
298         if (access(logoutfile, R_OK|X_OK) != 0) {
299                 strncpy(logoutfile, _PATH_SLOGOUT, sizeof(logoutfile)-1);
300                 logoutfile[sizeof(logoutfile)-1] = '\0';
301         }
302         if (access(logoutfile, R_OK|X_OK) == 0) {
303                 char logincmd[2*MAXPATHLEN+32];
304
305                 snprintf(logincmd, sizeof(logincmd), "%s %d %ld %s", logoutfile, unit, speed, loginargs);
306                 system(logincmd);
307         }
308         syslog(LOG_INFO, "closed %s slip unit %d (%s)\n", loginname, unit,
309                sigstr(s));
310         if (unlink(pidfilename) < 0 && errno != ENOENT)
311                 syslog(LOG_WARNING, "unable to delete pid file: %m");
312         if (unlink(iffilename) < 0 && errno != ENOENT)
313                 syslog(LOG_WARNING, "unable to delete if file: %m");
314         exit(1);
315         /* NOTREACHED */
316 }
317
318
319 /* Modify the slip line mode and add any compression or no-icmp flags. */
320 void
321 line_flags(int unit)
322 {
323         struct ifreq ifr;
324         int s;
325
326         /* open a socket as the handle to the interface */
327         s = socket(AF_INET, SOCK_DGRAM, 0);
328         if (s < 0) {
329                 syslog(LOG_ERR, "socket: %m");
330                 exit(1);
331         }
332         sprintf(ifr.ifr_name, "sl%d", unit);
333
334         /* get the flags for the interface */
335         if (ioctl(s, SIOCGIFFLAGS, (caddr_t)&ifr) < 0) {
336                 syslog(LOG_ERR, "ioctl (SIOCGIFFLAGS): %m");
337                 exit(1);
338         }
339
340         /* Assert any compression or no-icmp flags. */
341 #define SLMASK (~(IFF_LINK0 | IFF_LINK1 | IFF_LINK2))
342         ifr.ifr_flags &= SLMASK;
343         ifr.ifr_flags |= slip_mode;
344         if (ioctl(s, SIOCSIFFLAGS, (caddr_t)&ifr) < 0) {
345                 syslog(LOG_ERR, "ioctl (SIOCSIFFLAGS): %m");
346                 exit(1);
347         }
348         close(s);
349 }
350
351 int
352 main(int argc, char *argv[])
353 {
354         int fd, s, ldisc;
355         char *name;
356         struct termios tios, otios;
357         char logincmd[2*BUFSIZ+32];
358         extern uid_t getuid();
359
360         FILE *pidfile;                          /* pid file */
361         FILE *iffile;                           /* interfaces file */
362         char *p;
363         int n;
364         char devnam[MAXPATHLEN] = _PATH_TTY;   /* Device name */
365
366         if ((name = strrchr(argv[0], '/')) == NULL)
367                 name = argv[0];
368         s = getdtablesize();
369         for (fd = 3 ; fd < s ; fd++)
370                 close(fd);
371         openlog(name, LOG_PID|LOG_PERROR, LOG_DAEMON);
372         uid = getuid();
373         if (argc > 1) {
374                 findid(argv[1]);
375
376                 /*
377                  * Disassociate from current controlling terminal, if any,
378                  * and ensure that the slip line is our controlling terminal.
379                  */
380                 if (daemon(1, 1)) {
381                         syslog(LOG_ERR, "daemon(1, 1): %m");
382                         exit(1);
383                 }
384                 if (argc > 2) {
385                         if ((fd = open(argv[2], O_RDWR)) == -1) {
386                                 syslog(LOG_ERR, "open %s: %m", argv[2]);
387                                 exit(2);
388                         }
389                         dup2(fd, 0);
390                         if (fd > 2)
391                                 close(fd);
392                 }
393                 if (ioctl(0, TIOCSCTTY, 0) == -1) {
394                         syslog(LOG_ERR, "ioctl (TIOCSCTTY): %m");
395                         exit(1);
396                 }
397                 if (tcsetpgrp(0, getpid()) < 0) {
398                         syslog(LOG_ERR, "tcsetpgrp failed: %m");
399                         exit(1);
400                 }
401         } else {
402                 if ((name = getlogin()) == NULL) {
403                         syslog(LOG_ERR, "access denied - login name not found\n");
404                         exit(1);
405                 }
406                 findid(name);
407         }
408         fchmod(0, 0600);
409         fprintf(stderr, "starting slip login for %s\n", loginname);
410         fprintf(stderr, "your address is %s\n\n", make_ipaddr());
411
412         fflush(stderr);
413         sleep(1);
414
415         /* set up the line parameters */
416         if (tcgetattr(0, &tios) < 0) {
417                 syslog(LOG_ERR, "tcgetattr: %m");
418                 exit(1);
419         }
420         otios = tios;
421         cfmakeraw(&tios);
422         if (tcsetattr(0, TCSAFLUSH, &tios) < 0) {
423                 syslog(LOG_ERR, "tcsetattr: %m");
424                 exit(1);
425         }
426         speed = cfgetispeed(&tios);
427
428         ldisc = SLIPDISC;
429         if (ioctl(0, TIOCSETD, &ldisc) < 0) {
430                 syslog(LOG_ERR, "ioctl(TIOCSETD): %m");
431                 exit(1);
432         }
433         if (slunit >= 0 && ioctl(0, SLIOCSUNIT, &slunit) < 0) {
434                 syslog(LOG_ERR, "ioctl (SLIOCSUNIT): %m");
435                 exit(1);
436         }
437         /* find out what unit number we were assigned */
438         if (ioctl(0, SLIOCGUNIT, &unit) < 0) {
439                 syslog(LOG_ERR, "ioctl (SLIOCGUNIT): %m");
440                 exit(1);
441         }
442         signal(SIGHUP, hup_handler);
443         signal(SIGTERM, hup_handler);
444
445         if (keepal > 0) {
446                 signal(SIGURG, hup_handler);
447                 if (ioctl(0, SLIOCSKEEPAL, &keepal) < 0) {
448                         syslog(LOG_ERR, "ioctl(SLIOCSKEEPAL): %m");
449                         exit(1);
450                 }
451         }
452         if (outfill > 0 && ioctl(0, SLIOCSOUTFILL, &outfill) < 0) {
453                 syslog(LOG_ERR, "ioctl(SLIOCSOUTFILL): %m");
454                 exit(1);
455         }
456
457         /* write pid to file */
458         pid = getpid();
459         sprintf(ifname, "sl%d", unit);
460         sprintf(pidfilename, "%s%s.pid", _PATH_VARRUN, ifname);
461         if ((pidfile = fopen(pidfilename, "w")) != NULL) {
462                 fprintf(pidfile, "%d\n", pid);
463                 fclose(pidfile);
464         } else {
465                 syslog(LOG_ERR, "Failed to create pid file %s: %m",
466                                 pidfilename);
467                 pidfilename[0] = 0;
468         }
469
470         /* write interface unit number to file */
471         p = ttyname(0);
472         if (p)
473                 strcpy(devnam, p);
474         for (n = strlen(devnam); n > 0; n--) 
475                 if (devnam[n] == '/') {
476                         n++;
477                         break;
478                 }
479         sprintf(iffilename, "%s%s.if", _PATH_VARRUN, &devnam[n]);
480         if ((iffile = fopen(iffilename, "w")) != NULL) {
481                 fprintf(iffile, "sl%d\n", unit); 
482                 fclose(iffile);
483         } else {
484                 syslog(LOG_ERR, "Failed to create if file %s: %m", iffilename);
485                 iffilename[0] = 0;  
486         }
487
488
489         syslog(LOG_INFO, "attaching slip unit %d for %s\n", unit, loginname);
490         snprintf(logincmd, sizeof(logincmd), "%s %d %ld %s", loginfile, unit, speed,
491                  loginargs);
492         /*
493          * aim stdout and errout at /dev/null so logincmd output won't
494          * babble into the slip tty line.
495          */
496         close(1);
497         if ((fd = open(_PATH_DEVNULL, O_WRONLY)) != 1) {
498                 if (fd < 0) {
499                         syslog(LOG_ERR, "open %s: %m", _PATH_DEVNULL);
500                         exit(1);
501                 }
502                 dup2(fd, 1);
503                 close(fd);
504         }
505         dup2(1, 2);
506
507         /*
508          * Run login and logout scripts as root (real and effective);
509          * current route(8) is setuid root, and checks the real uid
510          * to see whether changes are allowed (or just "route get").
511          */
512         setuid(0);
513         if (s = system(logincmd)) {
514                 syslog(LOG_ERR, "%s login failed: exit status %d from %s",
515                        loginname, s, loginfile);
516                 exit(6);
517         }
518
519         /* Handle any compression or no-icmp flags. */
520         line_flags(unit);
521
522         /* reset uid to users' to allow the user to give a signal. */
523         seteuid(uid);
524         /* twiddle thumbs until we get a signal */
525         while (1)
526                 sigpause(0);
527
528         /* NOTREACHED */
529 }