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