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