summaryrefslogtreecommitdiff
path: root/libexec/tcpd/tcpdchk/inetcf.c
blob: ba13ab4ef16ad299f532a0ca2f725cd6774747da (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
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
/*	$OpenBSD: inetcf.c,v 1.4 2003/06/26 07:53:27 deraadt Exp $	*/

 /*
  * Routines to parse an inetd.conf or tlid.conf file. This would be a great
  * job for a PERL script.
  * 
  * Author: Wietse Venema, Eindhoven University of Technology, The Netherlands.
  */

#ifndef lint
#if 0
static char sccsid[] = "@(#) inetcf.c 1.7 97/02/12 02:13:23";
#else
static char rcsid[] = "$OpenBSD: inetcf.c,v 1.4 2003/06/26 07:53:27 deraadt Exp $";
#endif
#endif

#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>

#include <tcpd.h>

#include "inetcf.h"
#include "scaffold.h"

 /*
  * Network configuration files may live in unusual places. Here are some
  * guesses. Shorter names follow longer ones.
  */
char   *inet_files[] = {
    "/private/etc/inetd.conf",		/* NEXT */
    "/etc/inet/inetd.conf",		/* SYSV4 */
    "/usr/etc/inetd.conf",		/* IRIX?? */
    "/etc/inetd.conf",			/* BSD */
    "/etc/net/tlid.conf",		/* SYSV4?? */
    "/etc/saf/tlid.conf",		/* SYSV4?? */
    "/etc/tlid.conf",			/* SYSV4?? */
    0,
};

static void inet_chk(char *, char *, char *, char *);
static char *base_name(char *);

 /*
  * Structure with everything we know about a service.
  */
struct inet_ent {
    struct inet_ent *next;
    int     type;
    char    name[1];
};

static struct inet_ent *inet_list = 0;

static char whitespace[] = " \t\r\n";

/* inet_conf - read in and examine inetd.conf (or tlid.conf) entries */

char   *inet_cfg(conf)
char   *conf;
{
    char    buf[BUFSIZ];
    FILE   *fp = (FILE *)NULL;
    char   *service;
    char   *protocol;
    char   *user;
    char   *path;
    char   *arg0;
    char   *arg1;
    struct tcpd_context saved_context;
    int     i;
    struct stat st;

    saved_context = tcpd_context;

    /*
     * The inetd.conf (or tlid.conf) information is so useful that we insist
     * on its availability. When no file is given run a series of educated
     * guesses.
     */
    if (conf != 0) {
	if ((fp = fopen(conf, "r")) == (FILE *)NULL) {
	    fprintf(stderr, percent_m(buf, "open %s: %m\n"), conf);
	    exit(1);
	}
    } else {
	for (i = 0; inet_files[i] && (fp = fopen(inet_files[i], "r")) == 0; i++)
	     /* void */ ;
	if (fp == (FILE *)NULL) {
	    fprintf(stderr, "Cannot find your inetd.conf or tlid.conf file.\n");
	    fprintf(stderr, "Please specify its location.\n");
	    exit(1);
	}
	conf = inet_files[i];
	check_path(conf, &st);
    }

    /*
     * Process the file. After the 7.0 wrapper release it became clear that
     * there are many more inetd.conf formats than the 8 systems that I had
     * studied. EP/IX uses a two-line specification for rpc services; HP-UX
     * permits long lines to be broken with backslash-newline.
     */
    tcpd_context.file = conf;
    tcpd_context.line = 0;
    while (xgets(buf, sizeof(buf), fp)) {
	service = strtok(buf, whitespace);	/* service */
	if (service == 0 || *service == '#')
	    continue;
	if (STR_NE(service, "stream") && STR_NE(service, "dgram"))
	    strtok((char *) 0, whitespace);	/* endpoint */
	protocol = strtok((char *) 0, whitespace);
	(void) strtok((char *) 0, whitespace);	/* wait */
	if ((user = strtok((char *) 0, whitespace)) == 0)
	    continue;
	if (user[0] == '/') {			/* user */
	    path = user;
	} else {				/* path */
	    if ((path = strtok((char *) 0, whitespace)) == 0)
		continue;
	}
	if (path[0] == '?')			/* IRIX optional service */
	    path++;
	if (STR_EQ(path, "internal"))
	    continue;
	if (path[strspn(path, "-0123456789")] == 0) {

	    /*
	     * ConvexOS puts RPC version numbers before path names. Jukka
	     * Ukkonen <ukkonen@csc.fi>.
	     */
	    if ((path = strtok((char *) 0, whitespace)) == 0)
		continue;
	}
	if ((arg0 = strtok((char *) 0, whitespace)) == 0) {
	    tcpd_warn("incomplete line");
	    continue;
	}
	if (arg0[strspn(arg0, "0123456789")] == 0) {

	    /*
	     * We're reading a tlid.conf file, the format is:
	     * 
	     * ...stuff... path arg_count arguments mod_count modules
	     */
	    if ((arg0 = strtok((char *) 0, whitespace)) == 0) {
		tcpd_warn("incomplete line");
		continue;
	    }
	}
	if ((arg1 = strtok((char *) 0, whitespace)) == 0)
	    arg1 = "";

	inet_chk(protocol, path, arg0, arg1);
    }
    fclose(fp);
    tcpd_context = saved_context;
    return (conf);
}

/* inet_chk - examine one inetd.conf (tlid.conf?) entry */

static void inet_chk(protocol, path, arg0, arg1)
char   *protocol;
char   *path;
char   *arg0;
char   *arg1;
{
    char    daemon[BUFSIZ];
    struct stat st;
    int     wrap_status = WR_MAYBE;
    char   *base_name_path = base_name(path);
    char   *tcpd_proc_name = (arg0[0] == '/' ? base_name(arg0) : arg0);

    /*
     * Always warn when the executable does not exist or when it is not
     * executable.
     */
    if (check_path(path, &st) < 0) {
	tcpd_warn("%s: not found: %m", path);
    } else if ((st.st_mode & 0100) == 0) {
	tcpd_warn("%s: not executable", path);
    }

    /*
     * Cheat on the miscd tests, nobody uses it anymore.
     */
    if (STR_EQ(base_name_path, "miscd")) {
	inet_set(arg0, WR_YES);
	return;
    }

    /*
     * While we are here...
     */
    if (STR_EQ(tcpd_proc_name, "rexd") || STR_EQ(tcpd_proc_name, "rpc.rexd"))
	tcpd_warn("%s may be an insecure service", tcpd_proc_name);

    /*
     * The tcpd program gets most of the attention.
     */
    if (STR_EQ(base_name_path, "tcpd")) {

	if (STR_EQ(tcpd_proc_name, "tcpd"))
	    tcpd_warn("%s is recursively calling itself", tcpd_proc_name);

	wrap_status = WR_YES;

	/*
	 * Check: some sites install the wrapper set-uid.
	 */
	if ((st.st_mode & 06000) != 0)
	    tcpd_warn("%s: file is set-uid or set-gid", path);

	/*
	 * Check: some sites insert tcpd in inetd.conf, instead of replacing
	 * the daemon pathname.
	 */
	if (arg0[0] == '/' && STR_EQ(tcpd_proc_name, base_name(arg1)))
	    tcpd_warn("%s inserted before %s", path, arg0);

	/*
	 * Check: make sure files exist and are executable. On some systems
	 * the network daemons are set-uid so we cannot complain. Note that
	 * tcpd takes the basename only in case of absolute pathnames.
	 */
	if (arg0[0] == '/') {			/* absolute path */
	    if (check_path(arg0, &st) < 0) {
		tcpd_warn("%s: not found: %m", arg0);
	    } else if ((st.st_mode & 0100) == 0) {
		tcpd_warn("%s: not executable", arg0);
	    }
	} else {				/* look in REAL_DAEMON_DIR */
	    snprintf(daemon, sizeof daemon, "%s/%s", REAL_DAEMON_DIR, arg0);
	    if (check_path(daemon, &st) < 0) {
		tcpd_warn("%s: not found in %s: %m",
			  arg0, REAL_DAEMON_DIR);
	    } else if ((st.st_mode & 0100) == 0) {
		tcpd_warn("%s: not executable", daemon);
	    }
	}

    } else {

	/*
	 * No tcpd program found. Perhaps they used the "simple installation"
	 * recipe. Look for a file with the same basename in REAL_DAEMON_DIR.
	 * Draw some conservative conclusions when a distinct file is found.
	 */
	snprintf(daemon, sizeof daemon, "%s/%s", REAL_DAEMON_DIR, arg0);
	if (STR_EQ(path, daemon)) {
	    wrap_status = WR_NOT;
	} else if (check_path(daemon, &st) >= 0) {
	    wrap_status = WR_MAYBE;
	} else if (errno == ENOENT) {
	    wrap_status = WR_NOT;
	} else {
	    tcpd_warn("%s: file lookup: %m", daemon);
	    wrap_status = WR_MAYBE;
	}
    }

    /*
     * Alas, we cannot wrap rpc/tcp services.
     */
    if (wrap_status == WR_YES && STR_EQ(protocol, "rpc/tcp"))
	tcpd_warn("%s: cannot wrap rpc/tcp services", tcpd_proc_name);

    inet_set(tcpd_proc_name, wrap_status);
}

/* inet_set - remember service status */

void    inet_set(name, type)
char   *name;
int     type;
{
    int len = strlen(name);	/* NUL is inside the struct */
    struct inet_ent *ip =
    (struct inet_ent *) malloc(sizeof(struct inet_ent) + len);

    if (ip == 0) {
	fprintf(stderr, "out of memory\n");
	exit(1);
    }
    ip->next = inet_list;
    strlcpy(ip->name, name, len);
    ip->type = type;
    inet_list = ip;
}

/* inet_get - look up service status */

int     inet_get(name)
char   *name;
{
    struct inet_ent *ip;

    if (inet_list == 0)
	return (WR_MAYBE);

    for (ip = inet_list; ip; ip = ip->next)
	if (STR_EQ(ip->name, name))
	    return (ip->type);

    return (-1);
}

/* base_name - compute last pathname component */

static char *base_name(path)
char   *path;
{
    char   *cp;

    if ((cp = strrchr(path, '/')) != 0)
	path = cp + 1;
    return (path);
}