1/*
2 * Routines to authenticate access to a daemon (hosts allow/deny).
3 *
4 * Copyright (C) 1998 Andrew Tridgell
5 * Copyright (C) 2004, 2005 Wayne Davison
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License along
18 * with this program; if not, write to the Free Software Foundation, Inc.,
19 * 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
20 */
21
22#include "rsync.h"
23
24static int match_hostname(char *host, char *tok)
25{
26	if (!host || !*host)
27		return 0;
28	return wildmatch(tok, host);
29}
30
31static int match_binary(char *b1, char *b2, char *mask, int addrlen)
32{
33	int i;
34
35	for (i = 0; i < addrlen; i++) {
36		if ((b1[i] ^ b2[i]) & mask[i])
37			return 0;
38	}
39
40	return 1;
41}
42
43static void make_mask(char *mask, int plen, int addrlen)
44{
45	int w, b;
46
47	w = plen >> 3;
48	b = plen & 0x7;
49
50	if (w)
51		memset(mask, 0xff, w);
52	if (w < addrlen)
53		mask[w] = 0xff & (0xff<<(8-b));
54	if (w+1 < addrlen)
55		memset(mask+w+1, 0, addrlen-w-1);
56
57	return;
58}
59
60static int match_address(char *addr, char *tok)
61{
62	char *p;
63	struct addrinfo hints, *resa, *rest;
64	int gai;
65	int ret = 0;
66	int addrlen = 0;
67#ifdef HAVE_STRTOL
68	long int bits;
69#else
70	int bits;
71#endif
72	char mask[16];
73	char *a = NULL, *t = NULL;
74	unsigned int len;
75
76	if (!addr || !*addr)
77		return 0;
78
79	p = strchr(tok,'/');
80	if (p) {
81		*p = '\0';
82		len = p - tok;
83	} else
84		len = strlen(tok);
85
86	/* Fail quietly if tok is a hostname (not an address) */
87	if (strspn(tok, ".0123456789") != len
88#ifdef INET6
89	    && strchr(tok, ':') == NULL
90#endif
91	) {
92		if (p)
93			*p = '/';
94		return 0;
95	}
96
97	memset(&hints, 0, sizeof(hints));
98	hints.ai_family = PF_UNSPEC;
99	hints.ai_socktype = SOCK_STREAM;
100#ifdef AI_NUMERICHOST
101	hints.ai_flags = AI_NUMERICHOST;
102#endif
103
104	if (getaddrinfo(addr, NULL, &hints, &resa) != 0) {
105		if (p)
106			*p = '/';
107		return 0;
108	}
109
110	gai = getaddrinfo(tok, NULL, &hints, &rest);
111	if (p)
112		*p++ = '/';
113	if (gai != 0) {
114		rprintf(FLOG, "error matching address %s: %s\n",
115			tok, gai_strerror(gai));
116		freeaddrinfo(resa);
117		return 0;
118	}
119
120	if (rest->ai_family != resa->ai_family) {
121		ret = 0;
122		goto out;
123	}
124
125	switch(resa->ai_family) {
126	case PF_INET:
127		a = (char *)&((struct sockaddr_in *)resa->ai_addr)->sin_addr;
128		t = (char *)&((struct sockaddr_in *)rest->ai_addr)->sin_addr;
129		addrlen = 4;
130
131		break;
132
133#ifdef INET6
134	case PF_INET6:
135	    {
136		struct sockaddr_in6 *sin6a, *sin6t;
137
138		sin6a = (struct sockaddr_in6 *)resa->ai_addr;
139		sin6t = (struct sockaddr_in6 *)rest->ai_addr;
140
141		a = (char *)&sin6a->sin6_addr;
142		t = (char *)&sin6t->sin6_addr;
143
144		addrlen = 16;
145
146#ifdef HAVE_SOCKADDR_IN6_SCOPE_ID
147		if (sin6t->sin6_scope_id &&
148		    sin6a->sin6_scope_id != sin6t->sin6_scope_id) {
149			ret = 0;
150			goto out;
151		}
152#endif
153
154		break;
155	    }
156#endif
157	default:
158	    rprintf(FLOG, "unknown family %u\n", rest->ai_family);
159	    ret = 0;
160	    goto out;
161	}
162
163	bits = -1;
164	if (p) {
165		if (inet_pton(resa->ai_addr->sa_family, p, mask) <= 0) {
166#ifdef HAVE_STRTOL
167			char *ep = NULL;
168#else
169			unsigned char *pp;
170#endif
171
172#ifdef HAVE_STRTOL
173			bits = strtol(p, &ep, 10);
174			if (!*p || *ep) {
175				rprintf(FLOG, "malformed mask in %s\n", tok);
176				ret = 0;
177				goto out;
178			}
179#else
180			for (pp = (unsigned char *)p; *pp; pp++) {
181				if (!isascii(*pp) || !isdigit(*pp)) {
182					rprintf(FLOG, "malformed mask in %s\n", tok);
183					ret = 0;
184					goto out;
185				}
186			}
187			bits = atoi(p);
188#endif
189			if (bits == 0) {
190				ret = 1;
191				goto out;
192			}
193			if (bits < 0 || bits > (addrlen << 3)) {
194				rprintf(FLOG, "malformed mask in %s\n", tok);
195				ret = 0;
196				goto out;
197			}
198		}
199	} else {
200		bits = 128;
201	}
202
203	if (bits >= 0)
204		make_mask(mask, bits, addrlen);
205
206	ret = match_binary(a, t, mask, addrlen);
207
208  out:
209	freeaddrinfo(resa);
210	freeaddrinfo(rest);
211	return ret;
212}
213
214static int access_match(char *list, char *addr, char *host)
215{
216	char *tok;
217	char *list2 = strdup(list);
218
219	if (!list2)
220		out_of_memory("access_match");
221
222	strlower(list2);
223	if (host)
224		strlower(host);
225
226	for (tok = strtok(list2, " ,\t"); tok; tok = strtok(NULL, " ,\t")) {
227		if (match_hostname(host, tok) || match_address(addr, tok)) {
228			free(list2);
229			return 1;
230		}
231	}
232
233	free(list2);
234	return 0;
235}
236
237int allow_access(char *addr, char *host, char *allow_list, char *deny_list)
238{
239	if (allow_list && !*allow_list)
240		allow_list = NULL;
241	if (deny_list && !*deny_list)
242		deny_list = NULL;
243
244	/* If we match an allow-list item, we always allow access. */
245	if (allow_list) {
246		if (access_match(allow_list, addr, host))
247			return 1;
248		/* For an allow-list w/o a deny-list, disallow non-matches. */
249		if (!deny_list)
250			return 0;
251	}
252
253	/* If we match a deny-list item (and got past any allow-list
254	 * items), we always disallow access. */
255	if (deny_list && access_match(deny_list, addr, host))
256		return 0;
257
258	/* Allow all other access. */
259	return 1;
260}
261