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