Output some info about the size of our structures.
[rsync/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, 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
24 static int match_hostname(char *host, char *tok)
25 {
26         if (!host || !*host)
27                 return 0;
28         return wildmatch(tok, host);
29 }
30
31 static 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
43 static 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
60 static 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
214 static 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
237 int 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 }