Fix the val reading for MSG_ERROR_EXIT. Use 0-length MSG_DATA when
[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-2009 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
23 static int match_hostname(const char *host, const char *tok)
24 {
25         if (!host || !*host)
26                 return 0;
27         return iwildmatch(tok, host);
28 }
29
30 static int match_binary(const char *b1, const char *b2, const char *mask, int addrlen)
31 {
32         int i;
33
34         for (i = 0; i < addrlen; i++) {
35                 if ((b1[i] ^ b2[i]) & mask[i])
36                         return 0;
37         }
38
39         return 1;
40 }
41
42 static void make_mask(char *mask, int plen, int addrlen)
43 {
44         int w, b;
45
46         w = plen >> 3;
47         b = plen & 0x7;
48
49         if (w)
50                 memset(mask, 0xff, w);
51         if (w < addrlen)
52                 mask[w] = 0xff & (0xff<<(8-b));
53         if (w+1 < addrlen)
54                 memset(mask+w+1, 0, addrlen-w-1);
55
56         return;
57 }
58
59 static int match_address(const char *addr, const char *tok)
60 {
61         char *p;
62         struct addrinfo hints, *resa, *rest;
63         int gai;
64         int ret = 0;
65         int addrlen = 0;
66 #ifdef HAVE_STRTOL
67         long int bits;
68 #else
69         int bits;
70 #endif
71         char mask[16];
72         char *a = NULL, *t = NULL;
73         unsigned int len;
74
75         if (!addr || !*addr)
76                 return 0;
77
78         p = strchr(tok,'/');
79         if (p) {
80                 *p = '\0';
81                 len = p - tok;
82         } else
83                 len = strlen(tok);
84
85         /* Fail quietly if tok is a hostname (not an address) */
86         if (strspn(tok, ".0123456789") != len
87 #ifdef INET6
88             && strchr(tok, ':') == NULL
89 #endif
90         ) {
91                 if (p)
92                         *p = '/';
93                 return 0;
94         }
95
96         memset(&hints, 0, sizeof(hints));
97         hints.ai_family = PF_UNSPEC;
98         hints.ai_socktype = SOCK_STREAM;
99 #ifdef AI_NUMERICHOST
100         hints.ai_flags = AI_NUMERICHOST;
101 #endif
102
103         if (getaddrinfo(addr, NULL, &hints, &resa) != 0) {
104                 if (p)
105                         *p = '/';
106                 return 0;
107         }
108
109         gai = getaddrinfo(tok, NULL, &hints, &rest);
110         if (p)
111                 *p++ = '/';
112         if (gai != 0) {
113                 rprintf(FLOG, "error matching address %s: %s\n",
114                         tok, gai_strerror(gai));
115                 freeaddrinfo(resa);
116                 return 0;
117         }
118
119         if (rest->ai_family != resa->ai_family) {
120                 ret = 0;
121                 goto out;
122         }
123
124         switch(resa->ai_family) {
125         case PF_INET:
126                 a = (char *)&((struct sockaddr_in *)resa->ai_addr)->sin_addr;
127                 t = (char *)&((struct sockaddr_in *)rest->ai_addr)->sin_addr;
128                 addrlen = 4;
129
130                 break;
131
132 #ifdef INET6
133         case PF_INET6:
134             {
135                 struct sockaddr_in6 *sin6a, *sin6t;
136
137                 sin6a = (struct sockaddr_in6 *)resa->ai_addr;
138                 sin6t = (struct sockaddr_in6 *)rest->ai_addr;
139
140                 a = (char *)&sin6a->sin6_addr;
141                 t = (char *)&sin6t->sin6_addr;
142
143                 addrlen = 16;
144
145 #ifdef HAVE_SOCKADDR_IN6_SCOPE_ID
146                 if (sin6t->sin6_scope_id &&
147                     sin6a->sin6_scope_id != sin6t->sin6_scope_id) {
148                         ret = 0;
149                         goto out;
150                 }
151 #endif
152
153                 break;
154             }
155 #endif
156         default:
157             rprintf(FLOG, "unknown family %u\n", rest->ai_family);
158             ret = 0;
159             goto out;
160         }
161
162         bits = -1;
163         if (p) {
164                 if (inet_pton(resa->ai_addr->sa_family, p, mask) <= 0) {
165 #ifdef HAVE_STRTOL
166                         char *ep = NULL;
167 #else
168                         unsigned char *pp;
169 #endif
170
171 #ifdef HAVE_STRTOL
172                         bits = strtol(p, &ep, 10);
173                         if (!*p || *ep) {
174                                 rprintf(FLOG, "malformed mask in %s\n", tok);
175                                 ret = 0;
176                                 goto out;
177                         }
178 #else
179                         for (pp = (unsigned char *)p; *pp; pp++) {
180                                 if (!isascii(*pp) || !isdigit(*pp)) {
181                                         rprintf(FLOG, "malformed mask in %s\n", tok);
182                                         ret = 0;
183                                         goto out;
184                                 }
185                         }
186                         bits = atoi(p);
187 #endif
188                         if (bits == 0) {
189                                 ret = 1;
190                                 goto out;
191                         }
192                         if (bits < 0 || bits > (addrlen << 3)) {
193                                 rprintf(FLOG, "malformed mask in %s\n", tok);
194                                 ret = 0;
195                                 goto out;
196                         }
197                 }
198         } else {
199                 bits = 128;
200         }
201
202         if (bits >= 0)
203                 make_mask(mask, bits, addrlen);
204
205         ret = match_binary(a, t, mask, addrlen);
206
207   out:
208         freeaddrinfo(resa);
209         freeaddrinfo(rest);
210         return ret;
211 }
212
213 static int access_match(const char *list, const char *addr, const char *host)
214 {
215         char *tok;
216         char *list2 = strdup(list);
217
218         if (!list2)
219                 out_of_memory("access_match");
220
221         strlower(list2);
222
223         for (tok = strtok(list2, " ,\t"); tok; tok = strtok(NULL, " ,\t")) {
224                 if (match_hostname(host, tok) || match_address(addr, tok)) {
225                         free(list2);
226                         return 1;
227                 }
228         }
229
230         free(list2);
231         return 0;
232 }
233
234 int allow_access(const char *addr, const char *host,
235                  const char *allow_list, const 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 }