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