1*86d7f5d3SJohn Marino /*
2*86d7f5d3SJohn Marino * safe_finger - finger client wrapper that protects against nasty stuff
3*86d7f5d3SJohn Marino * from finger servers. Use this program for automatic reverse finger
4*86d7f5d3SJohn Marino * probes, not the raw finger command.
5*86d7f5d3SJohn Marino *
6*86d7f5d3SJohn Marino * Build with: cc -o safe_finger safe_finger.c
7*86d7f5d3SJohn Marino *
8*86d7f5d3SJohn Marino * The problem: some programs may react to stuff in the first column. Other
9*86d7f5d3SJohn Marino * programs may get upset by thrash anywhere on a line. File systems may
10*86d7f5d3SJohn Marino * fill up as the finger server keeps sending data. Text editors may bomb
11*86d7f5d3SJohn Marino * out on extremely long lines. The finger server may take forever because
12*86d7f5d3SJohn Marino * it is somehow wedged. The code below takes care of all this badness.
13*86d7f5d3SJohn Marino *
14*86d7f5d3SJohn Marino * Author: Wietse Venema, Eindhoven University of Technology, The Netherlands.
15*86d7f5d3SJohn Marino */
16*86d7f5d3SJohn Marino
17*86d7f5d3SJohn Marino #ifndef lint
18*86d7f5d3SJohn Marino static char sccsid[] = "@(#) safe_finger.c 1.4 94/12/28 17:42:41";
19*86d7f5d3SJohn Marino #endif
20*86d7f5d3SJohn Marino
21*86d7f5d3SJohn Marino /* System libraries */
22*86d7f5d3SJohn Marino
23*86d7f5d3SJohn Marino #include <sys/types.h>
24*86d7f5d3SJohn Marino #include <sys/stat.h>
25*86d7f5d3SJohn Marino #include <signal.h>
26*86d7f5d3SJohn Marino #include <stdio.h>
27*86d7f5d3SJohn Marino #include <ctype.h>
28*86d7f5d3SJohn Marino #include <pwd.h>
29*86d7f5d3SJohn Marino
30*86d7f5d3SJohn Marino extern void exit();
31*86d7f5d3SJohn Marino
32*86d7f5d3SJohn Marino /* Local stuff */
33*86d7f5d3SJohn Marino
34*86d7f5d3SJohn Marino char path[] = "PATH=/bin:/usr/bin:/usr/ucb:/usr/bsd:/etc:/usr/etc:/usr/sbin";
35*86d7f5d3SJohn Marino
36*86d7f5d3SJohn Marino #define TIME_LIMIT 60 /* Do not keep listinging forever */
37*86d7f5d3SJohn Marino #define INPUT_LENGTH 100000 /* Do not keep listinging forever */
38*86d7f5d3SJohn Marino #define LINE_LENGTH 128 /* Editors can choke on long lines */
39*86d7f5d3SJohn Marino #define FINGER_PROGRAM "finger" /* Most, if not all, UNIX systems */
40*86d7f5d3SJohn Marino #define UNPRIV_NAME "nobody" /* Preferred privilege level */
41*86d7f5d3SJohn Marino #define UNPRIV_UGID 32767 /* Default uid and gid */
42*86d7f5d3SJohn Marino
43*86d7f5d3SJohn Marino int finger_pid;
44*86d7f5d3SJohn Marino
cleanup(sig)45*86d7f5d3SJohn Marino void cleanup(sig)
46*86d7f5d3SJohn Marino int sig;
47*86d7f5d3SJohn Marino {
48*86d7f5d3SJohn Marino kill(finger_pid, SIGKILL);
49*86d7f5d3SJohn Marino exit(0);
50*86d7f5d3SJohn Marino }
51*86d7f5d3SJohn Marino
main(argc,argv)52*86d7f5d3SJohn Marino main(argc, argv)
53*86d7f5d3SJohn Marino int argc;
54*86d7f5d3SJohn Marino char **argv;
55*86d7f5d3SJohn Marino {
56*86d7f5d3SJohn Marino int c;
57*86d7f5d3SJohn Marino int line_length = 0;
58*86d7f5d3SJohn Marino int finger_status;
59*86d7f5d3SJohn Marino int wait_pid;
60*86d7f5d3SJohn Marino int input_count = 0;
61*86d7f5d3SJohn Marino struct passwd *pwd;
62*86d7f5d3SJohn Marino
63*86d7f5d3SJohn Marino /*
64*86d7f5d3SJohn Marino * First of all, let's don't run with superuser privileges.
65*86d7f5d3SJohn Marino */
66*86d7f5d3SJohn Marino if (getuid() == 0 || geteuid() == 0) {
67*86d7f5d3SJohn Marino if ((pwd = getpwnam(UNPRIV_NAME)) && pwd->pw_uid > 0) {
68*86d7f5d3SJohn Marino setgid(pwd->pw_gid);
69*86d7f5d3SJohn Marino setuid(pwd->pw_uid);
70*86d7f5d3SJohn Marino } else {
71*86d7f5d3SJohn Marino setgid(UNPRIV_UGID);
72*86d7f5d3SJohn Marino setuid(UNPRIV_UGID);
73*86d7f5d3SJohn Marino }
74*86d7f5d3SJohn Marino }
75*86d7f5d3SJohn Marino
76*86d7f5d3SJohn Marino /*
77*86d7f5d3SJohn Marino * Redirect our standard input through the raw finger command.
78*86d7f5d3SJohn Marino */
79*86d7f5d3SJohn Marino if (putenv(path)) {
80*86d7f5d3SJohn Marino fprintf(stderr, "%s: putenv: out of memory", argv[0]);
81*86d7f5d3SJohn Marino exit(1);
82*86d7f5d3SJohn Marino }
83*86d7f5d3SJohn Marino argv[0] = FINGER_PROGRAM;
84*86d7f5d3SJohn Marino finger_pid = pipe_stdin(argv);
85*86d7f5d3SJohn Marino
86*86d7f5d3SJohn Marino /*
87*86d7f5d3SJohn Marino * Don't wait forever (Peter Wemm <peter@gecko.DIALix.oz.au>).
88*86d7f5d3SJohn Marino */
89*86d7f5d3SJohn Marino signal(SIGALRM, cleanup);
90*86d7f5d3SJohn Marino (void) alarm(TIME_LIMIT);
91*86d7f5d3SJohn Marino
92*86d7f5d3SJohn Marino /*
93*86d7f5d3SJohn Marino * Main filter loop.
94*86d7f5d3SJohn Marino */
95*86d7f5d3SJohn Marino while ((c = getchar()) != EOF) {
96*86d7f5d3SJohn Marino if (input_count++ >= INPUT_LENGTH) { /* don't listen forever */
97*86d7f5d3SJohn Marino fclose(stdin);
98*86d7f5d3SJohn Marino printf("\n\n Input truncated to %d bytes...\n", input_count - 1);
99*86d7f5d3SJohn Marino break;
100*86d7f5d3SJohn Marino }
101*86d7f5d3SJohn Marino if (c == '\n') { /* good: end of line */
102*86d7f5d3SJohn Marino putchar(c);
103*86d7f5d3SJohn Marino line_length = 0;
104*86d7f5d3SJohn Marino } else {
105*86d7f5d3SJohn Marino if (line_length >= LINE_LENGTH) { /* force end of line */
106*86d7f5d3SJohn Marino printf("\\\n");
107*86d7f5d3SJohn Marino line_length = 0;
108*86d7f5d3SJohn Marino }
109*86d7f5d3SJohn Marino if (line_length == 0) { /* protect left margin */
110*86d7f5d3SJohn Marino putchar(' ');
111*86d7f5d3SJohn Marino line_length++;
112*86d7f5d3SJohn Marino }
113*86d7f5d3SJohn Marino if (isascii(c) && (isprint(c) || isspace(c))) { /* text */
114*86d7f5d3SJohn Marino if (c == '\\') {
115*86d7f5d3SJohn Marino putchar(c);
116*86d7f5d3SJohn Marino line_length++;
117*86d7f5d3SJohn Marino }
118*86d7f5d3SJohn Marino putchar(c);
119*86d7f5d3SJohn Marino line_length++;
120*86d7f5d3SJohn Marino } else { /* quote all other thash */
121*86d7f5d3SJohn Marino printf("\\%03o", c & 0377);
122*86d7f5d3SJohn Marino line_length += 4;
123*86d7f5d3SJohn Marino }
124*86d7f5d3SJohn Marino }
125*86d7f5d3SJohn Marino }
126*86d7f5d3SJohn Marino
127*86d7f5d3SJohn Marino /*
128*86d7f5d3SJohn Marino * Wait until the finger child process has terminated and account for its
129*86d7f5d3SJohn Marino * exit status. Which will always be zero on most systems.
130*86d7f5d3SJohn Marino */
131*86d7f5d3SJohn Marino while ((wait_pid = wait(&finger_status)) != -1 && wait_pid != finger_pid)
132*86d7f5d3SJohn Marino /* void */ ;
133*86d7f5d3SJohn Marino return (wait_pid != finger_pid || finger_status != 0);
134*86d7f5d3SJohn Marino }
135*86d7f5d3SJohn Marino
136*86d7f5d3SJohn Marino /* perror_exit - report system error text and terminate */
137*86d7f5d3SJohn Marino
perror_exit(text)138*86d7f5d3SJohn Marino void perror_exit(text)
139*86d7f5d3SJohn Marino char *text;
140*86d7f5d3SJohn Marino {
141*86d7f5d3SJohn Marino perror(text);
142*86d7f5d3SJohn Marino exit(1);
143*86d7f5d3SJohn Marino }
144*86d7f5d3SJohn Marino
145*86d7f5d3SJohn Marino /* pipe_stdin - pipe stdin through program (from my ANSI to OLD C converter) */
146*86d7f5d3SJohn Marino
pipe_stdin(argv)147*86d7f5d3SJohn Marino int pipe_stdin(argv)
148*86d7f5d3SJohn Marino char **argv;
149*86d7f5d3SJohn Marino {
150*86d7f5d3SJohn Marino int pipefds[2];
151*86d7f5d3SJohn Marino int pid;
152*86d7f5d3SJohn Marino int i;
153*86d7f5d3SJohn Marino struct stat st;
154*86d7f5d3SJohn Marino
155*86d7f5d3SJohn Marino /*
156*86d7f5d3SJohn Marino * The code that sets up the pipe requires that file descriptors 0,1,2
157*86d7f5d3SJohn Marino * are already open. All kinds of mysterious things will happen if that
158*86d7f5d3SJohn Marino * is not the case. The following loops makes sure that descriptors 0,1,2
159*86d7f5d3SJohn Marino * are set up properly.
160*86d7f5d3SJohn Marino */
161*86d7f5d3SJohn Marino
162*86d7f5d3SJohn Marino for (i = 0; i < 3; i++) {
163*86d7f5d3SJohn Marino if (fstat(i, &st) == -1 && open("/dev/null", 2) != i)
164*86d7f5d3SJohn Marino perror_exit("open /dev/null");
165*86d7f5d3SJohn Marino }
166*86d7f5d3SJohn Marino
167*86d7f5d3SJohn Marino /*
168*86d7f5d3SJohn Marino * Set up the pipe that interposes the command into our standard input
169*86d7f5d3SJohn Marino * stream.
170*86d7f5d3SJohn Marino */
171*86d7f5d3SJohn Marino
172*86d7f5d3SJohn Marino if (pipe(pipefds))
173*86d7f5d3SJohn Marino perror_exit("pipe");
174*86d7f5d3SJohn Marino
175*86d7f5d3SJohn Marino switch (pid = fork()) {
176*86d7f5d3SJohn Marino case -1: /* error */
177*86d7f5d3SJohn Marino perror_exit("fork");
178*86d7f5d3SJohn Marino /* NOTREACHED */
179*86d7f5d3SJohn Marino case 0: /* child */
180*86d7f5d3SJohn Marino (void) close(pipefds[0]); /* close reading end */
181*86d7f5d3SJohn Marino (void) close(1); /* connect stdout to pipe */
182*86d7f5d3SJohn Marino if (dup(pipefds[1]) != 1)
183*86d7f5d3SJohn Marino perror_exit("dup");
184*86d7f5d3SJohn Marino (void) close(pipefds[1]); /* close redundant fd */
185*86d7f5d3SJohn Marino (void) execvp(argv[0], argv);
186*86d7f5d3SJohn Marino perror_exit(argv[0]);
187*86d7f5d3SJohn Marino /* NOTREACHED */
188*86d7f5d3SJohn Marino default: /* parent */
189*86d7f5d3SJohn Marino (void) close(pipefds[1]); /* close writing end */
190*86d7f5d3SJohn Marino (void) close(0); /* connect stdin to pipe */
191*86d7f5d3SJohn Marino if (dup(pipefds[0]) != 0)
192*86d7f5d3SJohn Marino perror_exit("dup");
193*86d7f5d3SJohn Marino (void) close(pipefds[0]); /* close redundant fd */
194*86d7f5d3SJohn Marino return (pid);
195*86d7f5d3SJohn Marino }
196*86d7f5d3SJohn Marino }
197