xref: /freebsd/contrib/tcp_wrappers/safe_finger.c (revision 2aef693010b252e8cff0ce46a6ebf15b74c82219)
12aef6930SMark Murray  /*
22aef6930SMark Murray   * safe_finger - finger client wrapper that protects against nasty stuff
32aef6930SMark Murray   * from finger servers. Use this program for automatic reverse finger
42aef6930SMark Murray   * probes, not the raw finger command.
52aef6930SMark Murray   *
62aef6930SMark Murray   * Build with: cc -o safe_finger safe_finger.c
72aef6930SMark Murray   *
82aef6930SMark Murray   * The problem: some programs may react to stuff in the first column. Other
92aef6930SMark Murray   * programs may get upset by thrash anywhere on a line. File systems may
102aef6930SMark Murray   * fill up as the finger server keeps sending data. Text editors may bomb
112aef6930SMark Murray   * out on extremely long lines. The finger server may take forever because
122aef6930SMark Murray   * it is somehow wedged. The code below takes care of all this badness.
132aef6930SMark Murray   *
142aef6930SMark Murray   * Author: Wietse Venema, Eindhoven University of Technology, The Netherlands.
152aef6930SMark Murray   */
162aef6930SMark Murray 
172aef6930SMark Murray #ifndef lint
182aef6930SMark Murray static char sccsid[] = "@(#) safe_finger.c 1.4 94/12/28 17:42:41";
192aef6930SMark Murray #endif
202aef6930SMark Murray 
212aef6930SMark Murray /* System libraries */
222aef6930SMark Murray 
232aef6930SMark Murray #include <sys/types.h>
242aef6930SMark Murray #include <sys/stat.h>
252aef6930SMark Murray #include <signal.h>
262aef6930SMark Murray #include <stdio.h>
272aef6930SMark Murray #include <ctype.h>
282aef6930SMark Murray #include <pwd.h>
292aef6930SMark Murray 
302aef6930SMark Murray extern void exit();
312aef6930SMark Murray 
322aef6930SMark Murray /* Local stuff */
332aef6930SMark Murray 
342aef6930SMark Murray char    path[] = "PATH=/bin:/usr/bin:/usr/ucb:/usr/bsd:/etc:/usr/etc:/usr/sbin";
352aef6930SMark Murray 
362aef6930SMark Murray #define	TIME_LIMIT	60		/* Do not keep listinging forever */
372aef6930SMark Murray #define	INPUT_LENGTH	100000		/* Do not keep listinging forever */
382aef6930SMark Murray #define	LINE_LENGTH	128		/* Editors can choke on long lines */
392aef6930SMark Murray #define	FINGER_PROGRAM	"finger"	/* Most, if not all, UNIX systems */
402aef6930SMark Murray #define	UNPRIV_NAME	"nobody"	/* Preferred privilege level */
412aef6930SMark Murray #define	UNPRIV_UGID	32767		/* Default uid and gid */
422aef6930SMark Murray 
432aef6930SMark Murray int     finger_pid;
442aef6930SMark Murray 
452aef6930SMark Murray void    cleanup(sig)
462aef6930SMark Murray int     sig;
472aef6930SMark Murray {
482aef6930SMark Murray     kill(finger_pid, SIGKILL);
492aef6930SMark Murray     exit(0);
502aef6930SMark Murray }
512aef6930SMark Murray 
522aef6930SMark Murray main(argc, argv)
532aef6930SMark Murray int     argc;
542aef6930SMark Murray char  **argv;
552aef6930SMark Murray {
562aef6930SMark Murray     int     c;
572aef6930SMark Murray     int     line_length = 0;
582aef6930SMark Murray     int     finger_status;
592aef6930SMark Murray     int     wait_pid;
602aef6930SMark Murray     int     input_count = 0;
612aef6930SMark Murray     struct passwd *pwd;
622aef6930SMark Murray 
632aef6930SMark Murray     /*
642aef6930SMark Murray      * First of all, let's don't run with superuser privileges.
652aef6930SMark Murray      */
662aef6930SMark Murray     if (getuid() == 0 || geteuid() == 0) {
672aef6930SMark Murray 	if ((pwd = getpwnam(UNPRIV_NAME)) && pwd->pw_uid > 0) {
682aef6930SMark Murray 	    setgid(pwd->pw_gid);
692aef6930SMark Murray 	    setuid(pwd->pw_uid);
702aef6930SMark Murray 	} else {
712aef6930SMark Murray 	    setgid(UNPRIV_UGID);
722aef6930SMark Murray 	    setuid(UNPRIV_UGID);
732aef6930SMark Murray 	}
742aef6930SMark Murray     }
752aef6930SMark Murray 
762aef6930SMark Murray     /*
772aef6930SMark Murray      * Redirect our standard input through the raw finger command.
782aef6930SMark Murray      */
792aef6930SMark Murray     if (putenv(path)) {
802aef6930SMark Murray 	fprintf(stderr, "%s: putenv: out of memory", argv[0]);
812aef6930SMark Murray 	exit(1);
822aef6930SMark Murray     }
832aef6930SMark Murray     argv[0] = FINGER_PROGRAM;
842aef6930SMark Murray     finger_pid = pipe_stdin(argv);
852aef6930SMark Murray 
862aef6930SMark Murray     /*
872aef6930SMark Murray      * Don't wait forever (Peter Wemm <peter@gecko.DIALix.oz.au>).
882aef6930SMark Murray      */
892aef6930SMark Murray     signal(SIGALRM, cleanup);
902aef6930SMark Murray     (void) alarm(TIME_LIMIT);
912aef6930SMark Murray 
922aef6930SMark Murray     /*
932aef6930SMark Murray      * Main filter loop.
942aef6930SMark Murray      */
952aef6930SMark Murray     while ((c = getchar()) != EOF) {
962aef6930SMark Murray 	if (input_count++ >= INPUT_LENGTH) {	/* don't listen forever */
972aef6930SMark Murray 	    fclose(stdin);
982aef6930SMark Murray 	    printf("\n\n Input truncated to %d bytes...\n", input_count - 1);
992aef6930SMark Murray 	    break;
1002aef6930SMark Murray 	}
1012aef6930SMark Murray 	if (c == '\n') {			/* good: end of line */
1022aef6930SMark Murray 	    putchar(c);
1032aef6930SMark Murray 	    line_length = 0;
1042aef6930SMark Murray 	} else {
1052aef6930SMark Murray 	    if (line_length >= LINE_LENGTH) {	/* force end of line */
1062aef6930SMark Murray 		printf("\\\n");
1072aef6930SMark Murray 		line_length = 0;
1082aef6930SMark Murray 	    }
1092aef6930SMark Murray 	    if (line_length == 0) {		/* protect left margin */
1102aef6930SMark Murray 		putchar(' ');
1112aef6930SMark Murray 		line_length++;
1122aef6930SMark Murray 	    }
1132aef6930SMark Murray 	    if (isascii(c) && (isprint(c) || isspace(c))) {	/* text */
1142aef6930SMark Murray 		if (c == '\\') {
1152aef6930SMark Murray 		    putchar(c);
1162aef6930SMark Murray 		    line_length++;
1172aef6930SMark Murray 		}
1182aef6930SMark Murray 		putchar(c);
1192aef6930SMark Murray 		line_length++;
1202aef6930SMark Murray 	    } else {				/* quote all other thash */
1212aef6930SMark Murray 		printf("\\%03o", c & 0377);
1222aef6930SMark Murray 		line_length += 4;
1232aef6930SMark Murray 	    }
1242aef6930SMark Murray 	}
1252aef6930SMark Murray     }
1262aef6930SMark Murray 
1272aef6930SMark Murray     /*
1282aef6930SMark Murray      * Wait until the finger child process has terminated and account for its
1292aef6930SMark Murray      * exit status. Which will always be zero on most systems.
1302aef6930SMark Murray      */
1312aef6930SMark Murray     while ((wait_pid = wait(&finger_status)) != -1 && wait_pid != finger_pid)
1322aef6930SMark Murray 	 /* void */ ;
1332aef6930SMark Murray     return (wait_pid != finger_pid || finger_status != 0);
1342aef6930SMark Murray }
1352aef6930SMark Murray 
1362aef6930SMark Murray /* perror_exit - report system error text and terminate */
1372aef6930SMark Murray 
1382aef6930SMark Murray void    perror_exit(text)
1392aef6930SMark Murray char   *text;
1402aef6930SMark Murray {
1412aef6930SMark Murray     perror(text);
1422aef6930SMark Murray     exit(1);
1432aef6930SMark Murray }
1442aef6930SMark Murray 
1452aef6930SMark Murray /* pipe_stdin - pipe stdin through program (from my ANSI to OLD C converter) */
1462aef6930SMark Murray 
1472aef6930SMark Murray int     pipe_stdin(argv)
1482aef6930SMark Murray char  **argv;
1492aef6930SMark Murray {
1502aef6930SMark Murray     int     pipefds[2];
1512aef6930SMark Murray     int     pid;
1522aef6930SMark Murray     int     i;
1532aef6930SMark Murray     struct stat st;
1542aef6930SMark Murray 
1552aef6930SMark Murray     /*
1562aef6930SMark Murray      * The code that sets up the pipe requires that file descriptors 0,1,2
1572aef6930SMark Murray      * are already open. All kinds of mysterious things will happen if that
1582aef6930SMark Murray      * is not the case. The following loops makes sure that descriptors 0,1,2
1592aef6930SMark Murray      * are set up properly.
1602aef6930SMark Murray      */
1612aef6930SMark Murray 
1622aef6930SMark Murray     for (i = 0; i < 3; i++) {
1632aef6930SMark Murray 	if (fstat(i, &st) == -1 && open("/dev/null", 2) != i)
1642aef6930SMark Murray 	    perror_exit("open /dev/null");
1652aef6930SMark Murray     }
1662aef6930SMark Murray 
1672aef6930SMark Murray     /*
1682aef6930SMark Murray      * Set up the pipe that interposes the command into our standard input
1692aef6930SMark Murray      * stream.
1702aef6930SMark Murray      */
1712aef6930SMark Murray 
1722aef6930SMark Murray     if (pipe(pipefds))
1732aef6930SMark Murray 	perror_exit("pipe");
1742aef6930SMark Murray 
1752aef6930SMark Murray     switch (pid = fork()) {
1762aef6930SMark Murray     case -1:					/* error */
1772aef6930SMark Murray 	perror_exit("fork");
1782aef6930SMark Murray 	/* NOTREACHED */
1792aef6930SMark Murray     case 0:					/* child */
1802aef6930SMark Murray 	(void) close(pipefds[0]);		/* close reading end */
1812aef6930SMark Murray 	(void) close(1);			/* connect stdout to pipe */
1822aef6930SMark Murray 	if (dup(pipefds[1]) != 1)
1832aef6930SMark Murray 	    perror_exit("dup");
1842aef6930SMark Murray 	(void) close(pipefds[1]);		/* close redundant fd */
1852aef6930SMark Murray 	(void) execvp(argv[0], argv);
1862aef6930SMark Murray 	perror_exit(argv[0]);
1872aef6930SMark Murray 	/* NOTREACHED */
1882aef6930SMark Murray     default:					/* parent */
1892aef6930SMark Murray 	(void) close(pipefds[1]);		/* close writing end */
1902aef6930SMark Murray 	(void) close(0);			/* connect stdin to pipe */
1912aef6930SMark Murray 	if (dup(pipefds[0]) != 0)
1922aef6930SMark Murray 	    perror_exit("dup");
1932aef6930SMark Murray 	(void) close(pipefds[0]);		/* close redundant fd */
1942aef6930SMark Murray 	return (pid);
1952aef6930SMark Murray     }
1962aef6930SMark Murray }
197