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