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