Add "@netgroup" names to host matching.
[rsync.git] / access.c
1 /*
2  * Routines to authenticate access to a daemon (hosts allow/deny).
3  *
4  * Copyright (C) 1998 Andrew Tridgell
5  * Copyright (C) 2004-2020 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 3 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, visit the http://fsf.org website.
19  */
20
21 #include "rsync.h"
22 #include "ifuncs.h"
23
24 static int allow_forward_dns;
25
26 extern const char undetermined_hostname[];
27
28 static int match_hostname(const char **host_ptr, const char *addr, const char *tok)
29 {
30         struct hostent *hp;
31         unsigned int i;
32         const char *host = *host_ptr;
33
34         if (!host || !*host)
35                 return 0;
36
37 #ifdef HAVE_INNETGR
38         if (*tok == '@' && tok[1])
39                 return innetgr(tok + 1, host, NULL, NULL);
40 #endif
41
42         /* First check if the reverse-DNS-determined hostname matches. */
43         if (iwildmatch(tok, host))
44                 return 1;
45
46         if (!allow_forward_dns)
47                 return 0;
48
49         /* Fail quietly if tok is an address or wildcarded entry, not a simple hostname. */
50         if (!tok[strspn(tok, ".0123456789")] || tok[strcspn(tok, ":/*?[")])
51                 return 0;
52
53         /* Now try forward-DNS on the token (config-specified hostname) and see if the IP matches. */
54         if (!(hp = gethostbyname(tok)))
55                 return 0;
56
57         for (i = 0; hp->h_addr_list[i] != NULL; i++) {
58                 if (strcmp(addr, inet_ntoa(*(struct in_addr*)(hp->h_addr_list[i]))) == 0) {
59                         /* If reverse lookups are off, we'll use the conf-specified
60                          * hostname in preference to UNDETERMINED. */
61                         if (host == undetermined_hostname)
62                                 *host_ptr = strdup(tok);
63                         return 1;
64                 }
65         }
66
67         return 0;
68 }
69
70 static int match_binary(const char *b1, const char *b2, const char *mask, int addrlen)
71 {
72         int i;
73
74         for (i = 0; i < addrlen; i++) {
75                 if ((b1[i] ^ b2[i]) & mask[i])
76                         return 0;
77         }
78
79         return 1;
80 }
81
82 static void make_mask(char *mask, int plen, int addrlen)
83 {
84         int w, b;
85
86         w = plen >> 3;
87         b = plen & 0x7;
88
89         if (w)
90                 memset(mask, 0xff, w);
91         if (w < addrlen)
92                 mask[w] = 0xff & (0xff<<(8-b));
93         if (w+1 < addrlen)
94                 memset(mask+w+1, 0, addrlen-w-1);
95
96         return;
97 }
98
99 static int match_address(const char *addr, const char *tok)
100 {
101         char *p;
102         struct addrinfo hints, *resa, *rest;
103         int gai;
104         int ret = 0;
105         int addrlen = 0;
106 #ifdef HAVE_STRTOL
107         long int bits;
108 #else
109         int bits;
110 #endif
111         char mask[16];
112         char *a = NULL, *t = NULL;
113
114         if (!addr || !*addr)
115                 return 0;
116
117         p = strchr(tok,'/');
118         if (p)
119                 *p = '\0';
120
121         /* Fail quietly if tok is a hostname, not an address. */
122         if (tok[strspn(tok, ".0123456789")] && strchr(tok, ':') == NULL) {
123                 if (p)
124                         *p = '/';
125                 return 0;
126         }
127
128         memset(&hints, 0, sizeof(hints));
129         hints.ai_family = PF_UNSPEC;
130         hints.ai_socktype = SOCK_STREAM;
131 #ifdef AI_NUMERICHOST
132         hints.ai_flags = AI_NUMERICHOST;
133 #endif
134
135         if (getaddrinfo(addr, NULL, &hints, &resa) != 0) {
136                 if (p)
137                         *p = '/';
138                 return 0;
139         }
140
141         gai = getaddrinfo(tok, NULL, &hints, &rest);
142         if (p)
143                 *p++ = '/';
144         if (gai != 0) {
145                 rprintf(FLOG, "error matching address %s: %s\n",
146                         tok, gai_strerror(gai));
147                 freeaddrinfo(resa);
148                 return 0;
149         }
150
151         if (rest->ai_family != resa->ai_family) {
152                 ret = 0;
153                 goto out;
154         }
155
156         switch(resa->ai_family) {
157         case PF_INET:
158                 a = (char *)&((struct sockaddr_in *)resa->ai_addr)->sin_addr;
159                 t = (char *)&((struct sockaddr_in *)rest->ai_addr)->sin_addr;
160                 addrlen = 4;
161
162                 break;
163
164 #ifdef INET6
165         case PF_INET6: {
166                 struct sockaddr_in6 *sin6a, *sin6t;
167
168                 sin6a = (struct sockaddr_in6 *)resa->ai_addr;
169                 sin6t = (struct sockaddr_in6 *)rest->ai_addr;
170
171                 a = (char *)&sin6a->sin6_addr;
172                 t = (char *)&sin6t->sin6_addr;
173
174                 addrlen = 16;
175
176 #ifdef HAVE_SOCKADDR_IN6_SCOPE_ID
177                 if (sin6t->sin6_scope_id && sin6a->sin6_scope_id != sin6t->sin6_scope_id) {
178                         ret = 0;
179                         goto out;
180                 }
181 #endif
182
183                 break;
184         }
185 #endif
186         default:
187                 rprintf(FLOG, "unknown family %u\n", rest->ai_family);
188                 ret = 0;
189                 goto out;
190         }
191
192         bits = -1;
193         if (p) {
194                 if (inet_pton(resa->ai_addr->sa_family, p, mask) <= 0) {
195 #ifdef HAVE_STRTOL
196                         char *ep = NULL;
197 #else
198                         unsigned char *pp;
199 #endif
200
201 #ifdef HAVE_STRTOL
202                         bits = strtol(p, &ep, 10);
203                         if (!*p || *ep) {
204                                 rprintf(FLOG, "malformed mask in %s\n", tok);
205                                 ret = 0;
206                                 goto out;
207                         }
208 #else
209                         for (pp = (unsigned char *)p; *pp; pp++) {
210                                 if (!isascii(*pp) || !isdigit(*pp)) {
211                                         rprintf(FLOG, "malformed mask in %s\n", tok);
212                                         ret = 0;
213                                         goto out;
214                                 }
215                         }
216                         bits = atoi(p);
217 #endif
218                         if (bits == 0) {
219                                 ret = 1;
220                                 goto out;
221                         }
222                         if (bits < 0 || bits > (addrlen << 3)) {
223                                 rprintf(FLOG, "malformed mask in %s\n", tok);
224                                 ret = 0;
225                                 goto out;
226                         }
227                 }
228         } else {
229                 bits = 128;
230         }
231
232         if (bits >= 0)
233                 make_mask(mask, bits, addrlen);
234
235         ret = match_binary(a, t, mask, addrlen);
236
237   out:
238         freeaddrinfo(resa);
239         freeaddrinfo(rest);
240         return ret;
241 }
242
243 static int access_match(const char *list, const char *addr, const char **host_ptr)
244 {
245         char *tok;
246         char *list2 = strdup(list);
247
248         strlower(list2);
249
250         for (tok = strtok(list2, " ,\t"); tok; tok = strtok(NULL, " ,\t")) {
251                 if (match_hostname(host_ptr, addr, tok) || match_address(addr, tok)) {
252                         free(list2);
253                         return 1;
254                 }
255         }
256
257         free(list2);
258         return 0;
259 }
260
261 int allow_access(const char *addr, const char **host_ptr, int i)
262 {
263         const char *allow_list = lp_hosts_allow(i);
264         const char *deny_list = lp_hosts_deny(i);
265
266         if (allow_list && !*allow_list)
267                 allow_list = NULL;
268         if (deny_list && !*deny_list)
269                 deny_list = NULL;
270
271         allow_forward_dns = lp_forward_lookup(i);
272
273         /* If we match an allow-list item, we always allow access. */
274         if (allow_list) {
275                 if (access_match(allow_list, addr, host_ptr))
276                         return 1;
277                 /* For an allow-list w/o a deny-list, disallow non-matches. */
278                 if (!deny_list)
279                         return 0;
280         }
281
282         /* If we match a deny-list item (and got past any allow-list
283          * items), we always disallow access. */
284         if (deny_list && access_match(deny_list, addr, host_ptr))
285                 return 0;
286
287         /* Allow all other access. */
288         return 1;
289 }