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