summaryrefslogtreecommitdiffstats
path: root/contrib/tcp_wrappers/safe_finger.c
blob: 7b8f3cd638a89a24743af292c4b7423f6ebb0743 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
 /*
  * safe_finger - finger client wrapper that protects against nasty stuff
  * from finger servers. Use this program for automatic reverse finger
  * probes, not the raw finger command.
  * 
  * Build with: cc -o safe_finger safe_finger.c
  * 
  * The problem: some programs may react to stuff in the first column. Other
  * programs may get upset by thrash anywhere on a line. File systems may
  * fill up as the finger server keeps sending data. Text editors may bomb
  * out on extremely long lines. The finger server may take forever because
  * it is somehow wedged. The code below takes care of all this badness.
  * 
  * Author: Wietse Venema, Eindhoven University of Technology, The Netherlands.
  */

#ifndef lint
static char sccsid[] = "@(#) safe_finger.c 1.4 94/12/28 17:42:41";
#endif

/* System libraries */

#include <sys/types.h>
#include <sys/stat.h>
#include <signal.h>
#include <stdio.h>
#include <ctype.h>
#include <pwd.h>

extern void exit();

/* Local stuff */

char    path[] = "PATH=/bin:/usr/bin:/usr/ucb:/usr/bsd:/etc:/usr/etc:/usr/sbin";

#define	TIME_LIMIT	60		/* Do not keep listinging forever */
#define	INPUT_LENGTH	100000		/* Do not keep listinging forever */
#define	LINE_LENGTH	128		/* Editors can choke on long lines */
#define	FINGER_PROGRAM	"finger"	/* Most, if not all, UNIX systems */
#define	UNPRIV_NAME	"nobody"	/* Preferred privilege level */
#define	UNPRIV_UGID	32767		/* Default uid and gid */

int     finger_pid;

void    cleanup(sig)
int     sig;
{
    kill(finger_pid, SIGKILL);
    exit(0);
}

main(argc, argv)
int     argc;
char  **argv;
{
    int     c;
    int     line_length = 0;
    int     finger_status;
    int     wait_pid;
    int     input_count = 0;
    struct passwd *pwd;

    /*
     * First of all, let's don't run with superuser privileges.
     */
    if (getuid() == 0 || geteuid() == 0) {
	if ((pwd = getpwnam(UNPRIV_NAME)) && pwd->pw_uid > 0) {
	    setgid(pwd->pw_gid);
	    setuid(pwd->pw_uid);
	} else {
	    setgid(UNPRIV_UGID);
	    setuid(UNPRIV_UGID);
	}
    }

    /*
     * Redirect our standard input through the raw finger command.
     */
    if (putenv(path)) {
	fprintf(stderr, "%s: putenv: out of memory", argv[0]);
	exit(1);
    }
    argv[0] = FINGER_PROGRAM;
    finger_pid = pipe_stdin(argv);

    /*
     * Don't wait forever (Peter Wemm <peter@gecko.DIALix.oz.au>).
     */
    signal(SIGALRM, cleanup);
    (void) alarm(TIME_LIMIT);

    /*
     * Main filter loop.
     */
    while ((c = getchar()) != EOF) {
	if (input_count++ >= INPUT_LENGTH) {	/* don't listen forever */
	    fclose(stdin);
	    printf("\n\n Input truncated to %d bytes...\n", input_count - 1);
	    break;
	}
	if (c == '\n') {			/* good: end of line */
	    putchar(c);
	    line_length = 0;
	} else {
	    if (line_length >= LINE_LENGTH) {	/* force end of line */
		printf("\\\n");
		line_length = 0;
	    }
	    if (line_length == 0) {		/* protect left margin */
		putchar(' ');
		line_length++;
	    }
	    if (isascii(c) && (isprint(c) || isspace(c))) {	/* text */
		if (c == '\\') {
		    putchar(c);
		    line_length++;
		}
		putchar(c);
		line_length++;
	    } else {				/* quote all other thash */
		printf("\\%03o", c & 0377);
		line_length += 4;
	    }
	}
    }

    /*
     * Wait until the finger child process has terminated and account for its
     * exit status. Which will always be zero on most systems.
     */
    while ((wait_pid = wait(&finger_status)) != -1 && wait_pid != finger_pid)
	 /* void */ ;
    return (wait_pid != finger_pid || finger_status != 0);
}

/* perror_exit - report system error text and terminate */

void    perror_exit(text)
char   *text;
{
    perror(text);
    exit(1);
}

/* pipe_stdin - pipe stdin through program (from my ANSI to OLD C converter) */

int     pipe_stdin(argv)
char  **argv;
{
    int     pipefds[2];
    int     pid;
    int     i;
    struct stat st;

    /*
     * The code that sets up the pipe requires that file descriptors 0,1,2
     * are already open. All kinds of mysterious things will happen if that
     * is not the case. The following loops makes sure that descriptors 0,1,2
     * are set up properly.
     */

    for (i = 0; i < 3; i++) {
	if (fstat(i, &st) == -1 && open("/dev/null", 2) != i)
	    perror_exit("open /dev/null");
    }

    /*
     * Set up the pipe that interposes the command into our standard input
     * stream.
     */

    if (pipe(pipefds))
	perror_exit("pipe");

    switch (pid = fork()) {
    case -1:					/* error */
	perror_exit("fork");
	/* NOTREACHED */
    case 0:					/* child */
	(void) close(pipefds[0]);		/* close reading end */
	(void) close(1);			/* connect stdout to pipe */
	if (dup(pipefds[1]) != 1)
	    perror_exit("dup");
	(void) close(pipefds[1]);		/* close redundant fd */
	(void) execvp(argv[0], argv);
	perror_exit(argv[0]);
	/* NOTREACHED */
    default:					/* parent */
	(void) close(pipefds[1]);		/* close writing end */
	(void) close(0);			/* connect stdin to pipe */
	if (dup(pipefds[0]) != 0)
	    perror_exit("dup");
	(void) close(pipefds[0]);		/* close redundant fd */
	return (pid);
    }
}
OpenPOWER on IntegriCloud