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
cleanup(sig)43 void cleanup(sig)
44 int sig;
45 {
46 kill(finger_pid, SIGKILL);
47 exit(0);
48 }
49
50 int
main(argc,argv)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
perror_exit(char * text)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
pipe_stdin(argv)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