GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: lib/libc/net/ruserok.c Lines: 0 170 0.0 %
Date: 2017-11-13 Branches: 0 140 0.0 %

Line Branch Exec Source
1
/*
2
 * Copyright (c) 1995, 1996, 1998 Theo de Raadt.  All rights reserved.
3
 * Copyright (c) 1983, 1993, 1994
4
 *	The Regents of the University of California.  All rights reserved.
5
 *
6
 * Redistribution and use in source and binary forms, with or without
7
 * modification, are permitted provided that the following conditions
8
 * are met:
9
 * 1. Redistributions of source code must retain the above copyright
10
 *    notice, this list of conditions and the following disclaimer.
11
 * 2. Redistributions in binary form must reproduce the above copyright
12
 *    notice, this list of conditions and the following disclaimer in the
13
 *    documentation and/or other materials provided with the distribution.
14
 * 3. Neither the name of the University nor the names of its contributors
15
 *    may be used to endorse or promote products derived from this software
16
 *    without specific prior written permission.
17
 *
18
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
19
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
22
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28
 * SUCH DAMAGE.
29
 */
30
31
#include <sys/socket.h>
32
#include <sys/stat.h>
33
34
#include <netinet/in.h>
35
#include <arpa/inet.h>
36
37
#include <ctype.h>
38
#include <errno.h>
39
#include <fcntl.h>
40
#include <limits.h>
41
#include <netdb.h>
42
#include <netgroup.h>
43
#include <pwd.h>
44
#include <signal.h>
45
#include <stdio.h>
46
#include <stdlib.h>
47
#include <string.h>
48
#include <syslog.h>
49
#include <unistd.h>
50
51
static int __ivaliduser_sa(FILE *, struct sockaddr *, socklen_t,
52
	    const char *, const char *);
53
static int __icheckhost(struct sockaddr *, socklen_t, const char *);
54
static char *__gethostloop(struct sockaddr *, socklen_t);
55
static int iruserok_sa(const void *, int, int, const char *, const char *);
56
57
int
58
ruserok(const char *rhost, int superuser, const char *ruser, const char *luser)
59
{
60
	struct addrinfo hints, *res, *r;
61
	int error;
62
63
	memset(&hints, 0, sizeof(hints));
64
	hints.ai_family = PF_UNSPEC;
65
	hints.ai_socktype = SOCK_DGRAM;	/*dummy*/
66
	error = getaddrinfo(rhost, "0", &hints, &res);
67
	if (error)
68
		return (-1);
69
70
	for (r = res; r; r = r->ai_next) {
71
		if (iruserok_sa(r->ai_addr, r->ai_addrlen, superuser, ruser,
72
		    luser) == 0) {
73
			freeaddrinfo(res);
74
			return (0);
75
		}
76
	}
77
	freeaddrinfo(res);
78
	return (-1);
79
}
80
81
int
82
iruserok_sa(const void *raddr, int rlen, int superuser, const char *ruser,
83
    const char *luser)
84
{
85
	struct sockaddr *sa;
86
	char *cp;
87
	struct stat sbuf;
88
	struct passwd pwstore, *pwd;
89
	FILE *hostf;
90
	uid_t uid;
91
	int first;
92
	char pbuf[PATH_MAX], pwbuf[_PW_BUF_LEN];
93
94
	sa = (struct sockaddr *)raddr;
95
	first = 1;
96
	hostf = superuser ? NULL : fopen(_PATH_HEQUIV, "re");
97
again:
98
	if (hostf) {
99
		if (__ivaliduser_sa(hostf, sa, rlen, luser, ruser) == 0) {
100
			(void)fclose(hostf);
101
			return (0);
102
		}
103
		(void)fclose(hostf);
104
	}
105
	if (first == 1) {
106
		int len;
107
108
		first = 0;
109
		pwd = NULL;
110
		getpwnam_r(luser, &pwstore, pwbuf, sizeof(pwbuf), &pwd);
111
		if (pwd == NULL)
112
			return (-1);
113
		len = snprintf(pbuf, sizeof pbuf, "%s/.rhosts", pwd->pw_dir);
114
		if (len < 0 || len >= sizeof pbuf)
115
			return (-1);
116
117
		/*
118
		 * Change effective uid while opening .rhosts.  If root and
119
		 * reading an NFS mounted file system, can't read files that
120
		 * are protected read/write owner only.
121
		 */
122
		uid = geteuid();
123
		(void)seteuid(pwd->pw_uid);
124
		hostf = fopen(pbuf, "re");
125
		(void)seteuid(uid);
126
127
		if (hostf == NULL)
128
			return (-1);
129
		/*
130
		 * If not a regular file, or is owned by someone other than
131
		 * user or root or if writeable by anyone but the owner, quit.
132
		 */
133
		cp = NULL;
134
		if (lstat(pbuf, &sbuf) < 0)
135
			cp = ".rhosts lstat failed";
136
		else if (!S_ISREG(sbuf.st_mode))
137
			cp = ".rhosts not regular file";
138
		else if (fstat(fileno(hostf), &sbuf) < 0)
139
			cp = ".rhosts fstat failed";
140
		else if (sbuf.st_uid && sbuf.st_uid != pwd->pw_uid)
141
			cp = "bad .rhosts owner";
142
		else if (sbuf.st_mode & (S_IWGRP|S_IWOTH))
143
			cp = ".rhosts writable by other than owner";
144
		/* If there were any problems, quit. */
145
		if (cp) {
146
			(void)fclose(hostf);
147
			return (-1);
148
		}
149
		goto again;
150
	}
151
	return (-1);
152
}
153
154
int
155
__ivaliduser_sa(FILE *hostf, struct sockaddr *raddr, socklen_t salen,
156
    const char *luser, const char *ruser)
157
{
158
	char *user, *p;
159
	char *buf;
160
	const char *auser, *ahost;
161
	int hostok, userok;
162
	char *rhost = (char *)-1;
163
	char domain[HOST_NAME_MAX+1];
164
	size_t buflen;
165
166
	getdomainname(domain, sizeof(domain));
167
168
	while ((buf = fgetln(hostf, &buflen))) {
169
		p = buf;
170
		if (*p == '#')
171
			continue;
172
		while (p < buf + buflen && *p != '\n' && *p != ' ' && *p != '\t') {
173
			if (!isprint((unsigned char)*p))
174
				goto bail;
175
			*p = isupper((unsigned char)*p) ?
176
			    tolower((unsigned char)*p) : *p;
177
			p++;
178
		}
179
		if (p >= buf + buflen)
180
			continue;
181
		if (*p == ' ' || *p == '\t') {
182
			*p++ = '\0';
183
			while (p < buf + buflen && (*p == ' ' || *p == '\t'))
184
				p++;
185
			if (p >= buf + buflen)
186
				continue;
187
			user = p;
188
			while (p < buf + buflen && *p != '\n' && *p != ' ' &&
189
			    *p != '\t') {
190
				if (!isprint((unsigned char)*p))
191
					goto bail;
192
				p++;
193
			}
194
		} else
195
			user = p;
196
		*p = '\0';
197
198
		if (p == buf)
199
			continue;
200
201
		auser = *user ? user : luser;
202
		ahost = buf;
203
204
		if (strlen(ahost) > HOST_NAME_MAX)
205
			continue;
206
207
		/*
208
		 * innetgr() must lookup a hostname (we do not attempt
209
		 * to change the semantics so that netgroups may have
210
		 * #.#.#.# addresses in the list.)
211
		 */
212
		if (ahost[0] == '+')
213
			switch (ahost[1]) {
214
			case '\0':
215
				hostok = 1;
216
				break;
217
			case '@':
218
				if (rhost == (char *)-1)
219
					rhost = __gethostloop(raddr, salen);
220
				hostok = 0;
221
				if (rhost)
222
					hostok = innetgr(&ahost[2], rhost,
223
					    NULL, domain);
224
				break;
225
			default:
226
				hostok = __icheckhost(raddr, salen, &ahost[1]);
227
				break;
228
			}
229
		else if (ahost[0] == '-')
230
			switch (ahost[1]) {
231
			case '\0':
232
				hostok = -1;
233
				break;
234
			case '@':
235
				if (rhost == (char *)-1)
236
					rhost = __gethostloop(raddr, salen);
237
				hostok = 0;
238
				if (rhost)
239
					hostok = -innetgr(&ahost[2], rhost,
240
					    NULL, domain);
241
				break;
242
			default:
243
				hostok = -__icheckhost(raddr, salen, &ahost[1]);
244
				break;
245
			}
246
		else
247
			hostok = __icheckhost(raddr, salen, ahost);
248
249
250
		if (auser[0] == '+')
251
			switch (auser[1]) {
252
			case '\0':
253
				userok = 1;
254
				break;
255
			case '@':
256
				userok = innetgr(&auser[2], NULL, ruser,
257
				    domain);
258
				break;
259
			default:
260
				userok = strcmp(ruser, &auser[1]) ? 0 : 1;
261
				break;
262
			}
263
		else if (auser[0] == '-')
264
			switch (auser[1]) {
265
			case '\0':
266
				userok = -1;
267
				break;
268
			case '@':
269
				userok = -innetgr(&auser[2], NULL, ruser,
270
				    domain);
271
				break;
272
			default:
273
				userok = strcmp(ruser, &auser[1]) ? 0 : -1;
274
				break;
275
			}
276
		else
277
			userok = strcmp(ruser, auser) ? 0 : 1;
278
279
		/* Check if one component did not match */
280
		if (hostok == 0 || userok == 0)
281
			continue;
282
283
		/* Check if we got a forbidden pair */
284
		if (userok <= -1 || hostok <= -1)
285
			return (-1);
286
287
		/* Check if we got a valid pair */
288
		if (hostok >= 1 && userok >= 1)
289
			return (0);
290
	}
291
bail:
292
	return (-1);
293
}
294
295
/*
296
 * Returns "true" if match, 0 if no match.  If we do not find any
297
 * semblance of an A->PTR->A loop, allow a simple #.#.#.# match to work.
298
 */
299
static int
300
__icheckhost(struct sockaddr *raddr, socklen_t salen, const char *lhost)
301
{
302
	struct addrinfo hints, *res, *r;
303
	char h1[NI_MAXHOST], h2[NI_MAXHOST];
304
	int error;
305
	const int niflags = NI_NUMERICHOST;
306
307
	h1[0] = '\0';
308
	if (getnameinfo(raddr, salen, h1, sizeof(h1), NULL, 0,
309
	    niflags) != 0)
310
		return (0);
311
312
	/* Resolve laddr into sockaddr */
313
	memset(&hints, 0, sizeof(hints));
314
	hints.ai_family = raddr->sa_family;
315
	hints.ai_socktype = SOCK_DGRAM;	/*dummy*/
316
	res = NULL;
317
	error = getaddrinfo(lhost, "0", &hints, &res);
318
	if (error)
319
		return (0);
320
321
	/*
322
	 * Try string comparisons between raddr and laddr.
323
	 */
324
	for (r = res; r; r = r->ai_next) {
325
		h2[0] = '\0';
326
		if (getnameinfo(r->ai_addr, r->ai_addrlen, h2, sizeof(h2),
327
		    NULL, 0, niflags) != 0)
328
			continue;
329
		if (strcmp(h1, h2) == 0) {
330
			freeaddrinfo(res);
331
			return (1);
332
		}
333
	}
334
335
	/* No match. */
336
	freeaddrinfo(res);
337
	return (0);
338
}
339
340
/*
341
 * Return the hostname associated with the supplied address.
342
 * Do a reverse lookup as well for security. If a loop cannot
343
 * be found, pack the result of inet_ntoa() into the string.
344
 */
345
static char *
346
__gethostloop(struct sockaddr *raddr, socklen_t salen)
347
{
348
	static char remotehost[NI_MAXHOST];
349
	char h1[NI_MAXHOST], h2[NI_MAXHOST];
350
	struct addrinfo hints, *res, *r;
351
	int error;
352
	const int niflags = NI_NUMERICHOST;
353
354
	h1[0] = remotehost[0] = '\0';
355
	if (getnameinfo(raddr, salen, remotehost, sizeof(remotehost),
356
	    NULL, 0, NI_NAMEREQD) != 0)
357
		return (NULL);
358
	if (getnameinfo(raddr, salen, h1, sizeof(h1), NULL, 0,
359
	    niflags) != 0)
360
		return (NULL);
361
362
	/*
363
	 * Look up the name and check that the supplied
364
	 * address is in the list
365
	 */
366
	memset(&hints, 0, sizeof(hints));
367
	hints.ai_family = raddr->sa_family;
368
	hints.ai_socktype = SOCK_DGRAM;	/*dummy*/
369
	hints.ai_flags = AI_CANONNAME;
370
	res = NULL;
371
	error = getaddrinfo(remotehost, "0", &hints, &res);
372
	if (error)
373
		return (NULL);
374
375
	for (r = res; r; r = r->ai_next) {
376
		h2[0] = '\0';
377
		if (getnameinfo(r->ai_addr, r->ai_addrlen, h2, sizeof(h2),
378
		    NULL, 0, niflags) != 0)
379
			continue;
380
		if (strcmp(h1, h2) == 0) {
381
			freeaddrinfo(res);
382
			return (remotehost);
383
		}
384
	}
385
386
	/*
387
	 * either the DNS adminstrator has made a configuration
388
	 * mistake, or someone has attempted to spoof us
389
	 */
390
	freeaddrinfo(res);
391
	return (NULL);
392
}