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