treat N.N.N.N as a special case of MCL_SUBNETWORK instead of
[nfs-utils.git] / support / export / client.c
1 /*
2  * support/export/client.c
3  *
4  * Maintain list of nfsd clients.
5  *
6  * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
7  */
8
9 #include "config.h"
10
11 #include <sys/types.h>
12 #include <netinet/in.h>
13 #include <arpa/inet.h>
14 #include <stdlib.h>
15 #include <string.h>
16 #include <netdb.h>
17 #include "xmalloc.h"
18 #include "misc.h"
19 #include "nfslib.h"
20 #include "exportfs.h"
21
22 /* netgroup stuff never seems to be defined in any header file. Linux is
23  * not alone in this.
24  */
25 #if !defined(__GLIBC__) || __GLIBC__ < 2
26 extern int      innetgr(char *netgr, char *host, char *, char *);
27 #endif
28 static void     client_init(nfs_client *clp, const char *hname,
29                                         struct hostent *hp);
30 static int      client_checkaddr(nfs_client *clp, struct in_addr addr);
31
32 nfs_client      *clientlist[MCL_MAXTYPES] = { NULL, };
33
34
35 /* if canonical is set, then we *know* this is already a canonical name
36  * so hostname lookup is avoided.
37  * This is used when reading /proc/fs/nfs/exports
38  */
39 nfs_client *
40 client_lookup(char *hname, int canonical)
41 {
42         nfs_client      *clp = NULL;
43         int             htype;
44         struct hostent  *hp = NULL;
45
46         htype = client_gettype(hname);
47
48         if (htype == MCL_FQDN && !canonical) {
49                 struct hostent *hp2;
50                 hp = gethostbyname(hname);
51                 if (hp == NULL || hp->h_addrtype != AF_INET) {
52                         xlog(L_ERROR, "%s has non-inet addr", hname);
53                         return NULL;
54                 }
55                 /* make sure we have canonical name */
56                 hp2 = hostent_dup(hp);
57                 hp = gethostbyaddr(hp2->h_addr, hp2->h_length,
58                                    hp2->h_addrtype);
59                 if (hp) {
60                         hp = hostent_dup(hp);
61                         /* but now we might not have all addresses... */
62                         if (hp2->h_addr_list[1]) {
63                                 struct hostent *hp3 =
64                                         gethostbyname(hp->h_name);
65                                 if (hp3) {
66                                         free(hp);
67                                         hp = hostent_dup(hp3);
68                                 }
69                         }
70                         free(hp2);
71                 } else
72                         hp = hp2;
73
74                 hname = (char *) hp->h_name;
75
76                 for (clp = clientlist[htype]; clp; clp = clp->m_next) {
77                         if (client_check(clp, hp))
78                                 break;
79                 }
80         } else {
81                 for (clp = clientlist[htype]; clp; clp = clp->m_next) {
82                         if (strcasecmp(hname, clp->m_hostname)==0)
83                                 break;
84                 }
85         }
86
87         if (!clp) {
88                 clp = (nfs_client *) xmalloc(sizeof(*clp));
89                 memset(clp, 0, sizeof(*clp));
90                 clp->m_type = htype;
91                 client_init(clp, hname, NULL);
92                 client_add(clp);
93         }
94
95         if (htype == MCL_FQDN && clp->m_naddr == 0 && hp != NULL) {
96                 char    **ap = hp->h_addr_list;
97                 int     i;
98
99                 for (i = 0; *ap && i < NFSCLNT_ADDRMAX; i++, ap++)
100                         clp->m_addrlist[i] = *(struct in_addr *)*ap;
101                 clp->m_naddr = i;
102         }
103
104         if (hp)
105                 free (hp);
106
107         return clp;
108 }
109
110 nfs_client *
111 client_dup(nfs_client *clp, struct hostent *hp)
112 {
113         nfs_client              *new;
114
115         new = (nfs_client *) xmalloc(sizeof(*new));
116         memcpy(new, clp, sizeof(*new));
117         new->m_type = MCL_FQDN;
118
119         client_init(new, (char *) hp->h_name, hp);
120         client_add(new);
121         return new;
122 }
123
124 static void
125 client_init(nfs_client *clp, const char *hname, struct hostent *hp)
126 {
127         if (hp) {
128                 strncpy(clp->m_hostname, hp->h_name,
129                         sizeof (clp->m_hostname) -  1);
130         } else {
131                 strncpy(clp->m_hostname, hname,
132                         sizeof (clp->m_hostname) - 1);
133         }
134         clp->m_hostname[sizeof (clp->m_hostname) - 1] = '\0';
135
136         clp->m_exported = 0;
137         clp->m_count = 0;
138
139         if (clp->m_type == MCL_SUBNETWORK) {
140                 char    *cp = strchr(clp->m_hostname, '/');
141                 static char slash32[] = "/32";
142
143                 if(!cp) cp = slash32;
144                 *cp = '\0';
145                 clp->m_addrlist[0].s_addr = inet_addr(clp->m_hostname);
146                 if (strchr(cp + 1, '.')) {
147                         clp->m_addrlist[1].s_addr = inet_addr(cp+1);
148                 }
149                 else {
150                         int netmask = atoi(cp + 1);
151                         if (0 < netmask && netmask <= 32) {
152                                 clp->m_addrlist[1].s_addr =
153                                         htonl ((uint32_t) ~0 << (32 - netmask));
154                         }
155                         else {
156                                 xlog(L_FATAL, "invalid netmask `%s' for %s",
157                                      cp + 1, clp->m_hostname);
158                         }
159                 }
160                 *cp = '/';
161                 clp->m_naddr = 0;
162         } else if (!hp) {
163                 clp->m_naddr = 0;
164         } else {
165                 char    **ap = hp->h_addr_list;
166                 int     i;
167
168                 for (i = 0; *ap && i < NFSCLNT_ADDRMAX; i++, ap++) {
169                         clp->m_addrlist[i] = *(struct in_addr *)*ap;
170                 }
171                 clp->m_naddr = i;
172         }
173 }
174
175 void
176 client_add(nfs_client *clp)
177 {
178         nfs_client      **cpp;
179
180         if (clp->m_type < 0 || clp->m_type >= MCL_MAXTYPES)
181                 xlog(L_FATAL, "unknown client type in client_add");
182         cpp = clientlist + clp->m_type;
183         while (*cpp)
184                 cpp = &((*cpp)->m_next);
185         clp->m_next = NULL;
186         *cpp = clp;
187 }
188
189 void
190 client_release(nfs_client *clp)
191 {
192         if (clp->m_count <= 0)
193                 xlog(L_FATAL, "client_free: m_count <= 0!");
194         clp->m_count--;
195 }
196
197 void
198 client_freeall(void)
199 {
200         nfs_client      *clp, **head;
201         int             i;
202
203         for (i = 0; i < MCL_MAXTYPES; i++) {
204                 head = clientlist + i;
205                 while (*head) {
206                         *head = (clp = *head)->m_next;
207                         xfree(clp);
208                 }
209         }
210 }
211
212 nfs_client *
213 client_find(struct hostent *hp)
214 {
215         nfs_client      *clp;
216         int             i;
217
218         for (i = 0; i < MCL_MAXTYPES; i++) {
219                 for (clp = clientlist[i]; clp; clp = clp->m_next) {
220                         if (!client_check(clp, hp))
221                                 continue;
222 #ifdef notdef
223                         if (clp->m_type == MCL_FQDN)
224                                 return clp;
225                         return client_dup(clp, hp);
226 #else
227                         return clp;
228 #endif
229                 }
230         }
231         return NULL;
232 }
233
234 /*
235  * Find client name given an IP address
236  * This is found by gathering all known names that match that IP address,
237  * sorting them and joining them with '+'
238  *
239  */
240 static char *add_name(char *old, char *add);
241
242 char *
243 client_compose(struct in_addr addr)
244 {
245         struct hostent *he = NULL;
246         char *name = NULL;
247         int i;
248
249         if (clientlist[MCL_WILDCARD] || clientlist[MCL_NETGROUP])
250                 he = get_reliable_hostbyaddr((const char*)&addr, sizeof(addr), AF_INET);
251         if (he == NULL)
252                 he = get_hostent((const char*)&addr, sizeof(addr), AF_INET);
253
254         for (i = 0 ; i < MCL_MAXTYPES; i++) {
255                 nfs_client      *clp;
256                 for (clp = clientlist[i]; clp ; clp = clp->m_next) {
257                         if (!client_check(clp, he))
258                                 continue;
259                         name = add_name(name, clp->m_hostname);
260                 }
261         }
262         return name;
263 }
264
265 int 
266 client_member(char *client, char *name)
267 {
268         /* check if "client" (a ',' separated list of names)
269          * contains 'name' as a member 
270          */
271         int l = strlen(name);
272         while (*client) {
273                 if (strncmp(client, name, l) == 0 &&
274                     (client[l] == ',' || client[l] == '\0'))
275                         return 1;
276                 client = strchr(client, ',');
277                 if (client == NULL)
278                         return 0;
279                 client++;
280         }
281         return 0;
282 }
283
284
285 int 
286 name_cmp(char *a, char *b)
287 {
288         /* compare strings a and b, but only upto ',' in a */
289         while (*a && *b && *a != ',' && *a == *b)
290                 a++, b++;
291         if (!*b && (!*a || !a == ',') )
292                 return 0;
293         if (!*b) return 1;
294         if (!*a || *a == ',') return -1;
295         return *a - *b;
296 }
297
298 static char *
299 add_name(char *old, char *add)
300 {
301         int len = strlen(add)+2;
302         char *new;
303         char *cp;
304         if (old) len += strlen(old);
305         
306         new = malloc(len);
307         if (!new) {
308                 free(old);
309                 return NULL;
310         }
311         cp = old;
312         while (cp && *cp && name_cmp(cp, add) < 0) {
313                 /* step cp forward over a name */
314                 char *e = strchr(cp, ',');
315                 if (e)
316                         cp = e+1;
317                 else
318                         cp = cp + strlen(cp);
319         }
320         strncpy(new, old, cp-old);
321         new[cp-old] = 0;
322         if (cp != old && !*cp)
323                 strcat(new, ",");
324         strcat(new, add);
325         if (cp && *cp) {
326                 strcat(new, ",");
327                 strcat(new, cp);
328         }
329         return new;
330 }
331
332 /*
333  * Match a host (given its hostent record) to a client record. This
334  * is usually called from mountd.
335  */
336 int
337 client_check(nfs_client *clp, struct hostent *hp)
338 {
339         char    *hname = (char *) hp->h_name;
340         char    *cname = clp->m_hostname;
341         char    **ap;
342
343         switch (clp->m_type) {
344         case MCL_FQDN:
345         case MCL_SUBNETWORK:
346                 for (ap = hp->h_addr_list; *ap; ap++) {
347                         if (client_checkaddr(clp, *(struct in_addr *) *ap))
348                                 return 1;
349                 }
350                 return 0;
351         case MCL_WILDCARD:
352                 if (wildmat(hname, cname))
353                         return 1;
354                 else {
355                         for (ap = hp->h_aliases; *ap; ap++)
356                                 if (wildmat(*ap, cname))
357                                         return 1;
358                 }
359                 return 0;
360         case MCL_NETGROUP:
361 #ifdef HAVE_INNETGR
362                 {
363                         char    *dot;
364                         int     match;
365                         struct hostent *nhp = NULL;
366                         struct sockaddr_in addr;
367
368                         /* First, try to match the hostname without
369                          * splitting off the domain */
370                         if (innetgr(cname+1, hname, NULL, NULL))
371                                 return 1;
372
373                         /* If hname is ip address convert to FQDN */
374                         if (inet_aton(hname, &addr.sin_addr) &&
375                            (nhp = gethostbyaddr((const char *)&(addr.sin_addr),
376                             sizeof(addr.sin_addr), AF_INET))) {
377                                 hname = (char *)nhp->h_name;
378                                 if (innetgr(cname+1, hname, NULL, NULL))
379                                         return 1;
380                         }
381
382                         /* Okay, strip off the domain (if we have one) */
383                         if ((dot = strchr(hname, '.')) == NULL)
384                                 return 0;
385
386                         *dot = '\0';
387                         match = innetgr(cname+1, hname, NULL, NULL);
388                         *dot = '.';
389
390                         return match;
391                 }
392 #else
393                 return 0;
394 #endif
395         case MCL_ANONYMOUS:
396                 return 1;
397         case MCL_GSS:
398                 return 0;
399         default:
400                 xlog(L_FATAL, "internal: bad client type %d", clp->m_type);
401         }
402
403         return 0;
404 }
405
406 static int
407 client_checkaddr(nfs_client *clp, struct in_addr addr)
408 {
409         int     i;
410
411         switch (clp->m_type) {
412         case MCL_FQDN:
413                 for (i = 0; i < clp->m_naddr; i++) {
414                         if (clp->m_addrlist[i].s_addr == addr.s_addr)
415                                 return 1;
416                 }
417                 return 0;
418         case MCL_SUBNETWORK:
419                 return !((clp->m_addrlist[0].s_addr ^ addr.s_addr)
420                         & clp->m_addrlist[1].s_addr);
421         }
422         return 0;
423 }
424
425 int
426 client_gettype(char *ident)
427 {
428         char    *sp;
429
430         if (ident[0] == '\0' || strcmp(ident, "*")==0)
431                 return MCL_ANONYMOUS;
432         if (strncmp(ident, "gss/", 4) == 0)
433                 return MCL_GSS;
434         if (ident[0] == '@') {
435 #ifndef HAVE_INNETGR
436                 xlog(L_WARNING, "netgroup support not compiled in");
437 #endif
438                 return MCL_NETGROUP;
439         }
440         for (sp = ident; *sp; sp++) {
441                 if (*sp == '*' || *sp == '?' || *sp == '[')
442                         return MCL_WILDCARD;
443                 if (*sp == '/')
444                         return MCL_SUBNETWORK;
445                 if (*sp == '\\' && sp[1])
446                         sp++;
447         }
448         /* check for N.N.N.N */
449         sp = ident;
450         if(!isdigit(*sp) || strtoul(sp, &sp, 10) > 255 || *sp != '.') return MCL_FQDN;
451         sp++; if(!isdigit(*sp) || strtoul(sp, &sp, 10) > 255 || *sp != '.') return MCL_FQDN;
452         sp++; if(!isdigit(*sp) || strtoul(sp, &sp, 10) > 255 || *sp != '.') return MCL_FQDN;
453         sp++; if(!isdigit(*sp) || strtoul(sp, &sp, 10) > 255 || *sp != '\0') return MCL_FQDN;
454         /* we lie here a bit. but technically N.N.N.N == N.N.N.N/32 :) */
455         return MCL_SUBNETWORK;
456 }