144743Smarkm /* 244743Smarkm * safe_finger - finger client wrapper that protects against nasty stuff 344743Smarkm * from finger servers. Use this program for automatic reverse finger 444743Smarkm * probes, not the raw finger command. 544743Smarkm * 644743Smarkm * Build with: cc -o safe_finger safe_finger.c 744743Smarkm * 844743Smarkm * The problem: some programs may react to stuff in the first column. Other 944743Smarkm * programs may get upset by thrash anywhere on a line. File systems may 1044743Smarkm * fill up as the finger server keeps sending data. Text editors may bomb 1144743Smarkm * out on extremely long lines. The finger server may take forever because 1244743Smarkm * it is somehow wedged. The code below takes care of all this badness. 1344743Smarkm * 1444743Smarkm * Author: Wietse Venema, Eindhoven University of Technology, The Netherlands. 1544743Smarkm */ 1644743Smarkm 1744743Smarkm#ifndef lint 1844743Smarkmstatic char sccsid[] = "@(#) safe_finger.c 1.4 94/12/28 17:42:41"; 1944743Smarkm#endif 2044743Smarkm 2144743Smarkm/* System libraries */ 2244743Smarkm 2344743Smarkm#include <sys/types.h> 2444743Smarkm#include <sys/stat.h> 2544743Smarkm#include <signal.h> 2644743Smarkm#include <stdio.h> 2744743Smarkm#include <ctype.h> 2844743Smarkm#include <pwd.h> 2944743Smarkm 3044743Smarkmextern void exit(); 3144743Smarkm 3244743Smarkm/* Local stuff */ 3344743Smarkm 3444743Smarkmchar path[] = "PATH=/bin:/usr/bin:/usr/ucb:/usr/bsd:/etc:/usr/etc:/usr/sbin"; 3544743Smarkm 3644743Smarkm#define TIME_LIMIT 60 /* Do not keep listinging forever */ 3744743Smarkm#define INPUT_LENGTH 100000 /* Do not keep listinging forever */ 3844743Smarkm#define LINE_LENGTH 128 /* Editors can choke on long lines */ 3944743Smarkm#define FINGER_PROGRAM "finger" /* Most, if not all, UNIX systems */ 4044743Smarkm#define UNPRIV_NAME "nobody" /* Preferred privilege level */ 4144743Smarkm#define UNPRIV_UGID 32767 /* Default uid and gid */ 4244743Smarkm 4344743Smarkmint finger_pid; 4444743Smarkm 4544743Smarkmvoid cleanup(sig) 4644743Smarkmint sig; 4744743Smarkm{ 4844743Smarkm kill(finger_pid, SIGKILL); 4944743Smarkm exit(0); 5044743Smarkm} 5144743Smarkm 5244743Smarkmmain(argc, argv) 5344743Smarkmint argc; 5444743Smarkmchar **argv; 5544743Smarkm{ 5644743Smarkm int c; 5744743Smarkm int line_length = 0; 5844743Smarkm int finger_status; 5944743Smarkm int wait_pid; 6044743Smarkm int input_count = 0; 6144743Smarkm struct passwd *pwd; 6244743Smarkm 6344743Smarkm /* 6444743Smarkm * First of all, let's don't run with superuser privileges. 6544743Smarkm */ 6644743Smarkm if (getuid() == 0 || geteuid() == 0) { 6744743Smarkm if ((pwd = getpwnam(UNPRIV_NAME)) && pwd->pw_uid > 0) { 6844743Smarkm setgid(pwd->pw_gid); 6944743Smarkm setuid(pwd->pw_uid); 7044743Smarkm } else { 7144743Smarkm setgid(UNPRIV_UGID); 7244743Smarkm setuid(UNPRIV_UGID); 7344743Smarkm } 7444743Smarkm } 7544743Smarkm 7644743Smarkm /* 7744743Smarkm * Redirect our standard input through the raw finger command. 7844743Smarkm */ 7944743Smarkm if (putenv(path)) { 8044743Smarkm fprintf(stderr, "%s: putenv: out of memory", argv[0]); 8144743Smarkm exit(1); 8244743Smarkm } 8344743Smarkm argv[0] = FINGER_PROGRAM; 8444743Smarkm finger_pid = pipe_stdin(argv); 8544743Smarkm 8644743Smarkm /* 8744743Smarkm * Don't wait forever (Peter Wemm <peter@gecko.DIALix.oz.au>). 8844743Smarkm */ 8944743Smarkm signal(SIGALRM, cleanup); 9044743Smarkm (void) alarm(TIME_LIMIT); 9144743Smarkm 9244743Smarkm /* 9344743Smarkm * Main filter loop. 9444743Smarkm */ 9544743Smarkm while ((c = getchar()) != EOF) { 9644743Smarkm if (input_count++ >= INPUT_LENGTH) { /* don't listen forever */ 9744743Smarkm fclose(stdin); 9844743Smarkm printf("\n\n Input truncated to %d bytes...\n", input_count - 1); 9944743Smarkm break; 10044743Smarkm } 10144743Smarkm if (c == '\n') { /* good: end of line */ 10244743Smarkm putchar(c); 10344743Smarkm line_length = 0; 10444743Smarkm } else { 10544743Smarkm if (line_length >= LINE_LENGTH) { /* force end of line */ 10644743Smarkm printf("\\\n"); 10744743Smarkm line_length = 0; 10844743Smarkm } 10944743Smarkm if (line_length == 0) { /* protect left margin */ 11044743Smarkm putchar(' '); 11144743Smarkm line_length++; 11244743Smarkm } 11344743Smarkm if (isascii(c) && (isprint(c) || isspace(c))) { /* text */ 11444743Smarkm if (c == '\\') { 11544743Smarkm putchar(c); 11644743Smarkm line_length++; 11744743Smarkm } 11844743Smarkm putchar(c); 11944743Smarkm line_length++; 12044743Smarkm } else { /* quote all other thash */ 12144743Smarkm printf("\\%03o", c & 0377); 12244743Smarkm line_length += 4; 12344743Smarkm } 12444743Smarkm } 12544743Smarkm } 12644743Smarkm 12744743Smarkm /* 12844743Smarkm * Wait until the finger child process has terminated and account for its 12944743Smarkm * exit status. Which will always be zero on most systems. 13044743Smarkm */ 13144743Smarkm while ((wait_pid = wait(&finger_status)) != -1 && wait_pid != finger_pid) 13244743Smarkm /* void */ ; 13344743Smarkm return (wait_pid != finger_pid || finger_status != 0); 13444743Smarkm} 13544743Smarkm 13644743Smarkm/* perror_exit - report system error text and terminate */ 13744743Smarkm 13844743Smarkmvoid perror_exit(text) 13944743Smarkmchar *text; 14044743Smarkm{ 14144743Smarkm perror(text); 14244743Smarkm exit(1); 14344743Smarkm} 14444743Smarkm 14544743Smarkm/* pipe_stdin - pipe stdin through program (from my ANSI to OLD C converter) */ 14644743Smarkm 14744743Smarkmint pipe_stdin(argv) 14844743Smarkmchar **argv; 14944743Smarkm{ 15044743Smarkm int pipefds[2]; 15144743Smarkm int pid; 15244743Smarkm int i; 15344743Smarkm struct stat st; 15444743Smarkm 15544743Smarkm /* 15644743Smarkm * The code that sets up the pipe requires that file descriptors 0,1,2 15744743Smarkm * are already open. All kinds of mysterious things will happen if that 15844743Smarkm * is not the case. The following loops makes sure that descriptors 0,1,2 15944743Smarkm * are set up properly. 16044743Smarkm */ 16144743Smarkm 16244743Smarkm for (i = 0; i < 3; i++) { 16344743Smarkm if (fstat(i, &st) == -1 && open("/dev/null", 2) != i) 16444743Smarkm perror_exit("open /dev/null"); 16544743Smarkm } 16644743Smarkm 16744743Smarkm /* 16844743Smarkm * Set up the pipe that interposes the command into our standard input 16944743Smarkm * stream. 17044743Smarkm */ 17144743Smarkm 17244743Smarkm if (pipe(pipefds)) 17344743Smarkm perror_exit("pipe"); 17444743Smarkm 17544743Smarkm switch (pid = fork()) { 17644743Smarkm case -1: /* error */ 17744743Smarkm perror_exit("fork"); 17844743Smarkm /* NOTREACHED */ 17944743Smarkm case 0: /* child */ 18044743Smarkm (void) close(pipefds[0]); /* close reading end */ 18144743Smarkm (void) close(1); /* connect stdout to pipe */ 18244743Smarkm if (dup(pipefds[1]) != 1) 18344743Smarkm perror_exit("dup"); 18444743Smarkm (void) close(pipefds[1]); /* close redundant fd */ 18544743Smarkm (void) execvp(argv[0], argv); 18644743Smarkm perror_exit(argv[0]); 18744743Smarkm /* NOTREACHED */ 18844743Smarkm default: /* parent */ 18944743Smarkm (void) close(pipefds[1]); /* close writing end */ 19044743Smarkm (void) close(0); /* connect stdin to pipe */ 19144743Smarkm if (dup(pipefds[0]) != 0) 19244743Smarkm perror_exit("dup"); 19344743Smarkm (void) close(pipefds[0]); /* close redundant fd */ 19444743Smarkm return (pid); 19544743Smarkm } 19644743Smarkm} 197