call gethostbyaddr to make sure that we have canonical hostname
[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 nfs_client *
36 client_lookup(char *hname)
37 {
38         nfs_client      *clp = NULL;
39         int             htype;
40         struct hostent  *hp = NULL;
41
42         htype = client_gettype(hname);
43
44         if (htype == MCL_FQDN) {
45                 struct hostent *hp2;
46                 hp = gethostbyname(hname);
47                 if (hp == NULL || hp->h_addrtype != AF_INET) {
48                         xlog(L_ERROR, "%s has non-inet addr", hname);
49                         return NULL;
50                 }
51                 /* make sure we have canonical name */
52                 hp2 = hostent_dup(hp);
53                 hp = gethostbyaddr(hp2->h_addr, hp2->h_length,
54                                    hp2->h_addrtype);
55                 if (hp) {
56                         free(hp2);
57                         hp = hostent_dup(hp);
58                 } else
59                         hp = hp2;
60
61                 hname = (char *) hp->h_name;
62
63                 for (clp = clientlist[htype]; clp; clp = clp->m_next) {
64                         if (client_check(clp, hp))
65                                 break;
66                 }
67         } else {
68                 for (clp = clientlist[htype]; clp; clp = clp->m_next) {
69                         if (strcmp(hname, clp->m_hostname)==0)
70                                 break;
71                 }
72         }
73
74         if (!clp) {
75                 clp = (nfs_client *) xmalloc(sizeof(*clp));
76                 memset(clp, 0, sizeof(*clp));
77                 clp->m_type = htype;
78                 client_init(clp, hname, NULL);
79                 client_add(clp);
80         }
81
82         if (htype == MCL_FQDN && clp->m_naddr == 0 && hp != NULL) {
83                 char    **ap = hp->h_addr_list;
84                 int     i;
85
86                 for (i = 0; *ap && i < NFSCLNT_ADDRMAX; i++, ap++)
87                         clp->m_addrlist[i] = *(struct in_addr *)*ap;
88                 clp->m_naddr = i;
89         }
90
91         if (hp)
92                 free (hp);
93
94         return clp;
95 }
96
97 nfs_client *
98 client_dup(nfs_client *clp, struct hostent *hp)
99 {
100         nfs_client              *new;
101
102         new = (nfs_client *) xmalloc(sizeof(*new));
103         memcpy(new, clp, sizeof(*new));
104         new->m_type = MCL_FQDN;
105
106         client_init(new, (char *) hp->h_name, hp);
107         client_add(new);
108         return new;
109 }
110
111 static void
112 client_init(nfs_client *clp, const char *hname, struct hostent *hp)
113 {
114         if (hp) {
115                 strncpy(clp->m_hostname, hp->h_name,
116                         sizeof (clp->m_hostname) -  1);
117         } else {
118                 strncpy(clp->m_hostname, hname,
119                         sizeof (clp->m_hostname) - 1);
120         }
121         clp->m_hostname[sizeof (clp->m_hostname) - 1] = '\0';
122
123         clp->m_exported = 0;
124         clp->m_count = 0;
125
126         if (clp->m_type == MCL_SUBNETWORK) {
127                 char    *cp = strchr(clp->m_hostname, '/');
128
129                 *cp = '\0';
130                 clp->m_addrlist[0].s_addr = inet_addr(clp->m_hostname);
131                 if (strchr(cp + 1, '.')) {
132                         clp->m_addrlist[1].s_addr = inet_addr(cp+1);
133                 }
134                 else {
135                         int netmask = atoi(cp + 1);
136                         if (0 < netmask && netmask <= 32) {
137                                 clp->m_addrlist[1].s_addr =
138                                         htonl ((uint32_t) ~0 << (32 - netmask));
139                         }
140                         else {
141                                 xlog(L_FATAL, "invalid netmask `%s' for %s",
142                                      cp + 1, clp->m_hostname);
143                         }
144                 }
145                 *cp = '/';
146                 clp->m_naddr = 0;
147         } else if (!hp) {
148                 clp->m_naddr = 0;
149         } else {
150                 char    **ap = hp->h_addr_list;
151                 int     i;
152
153                 for (i = 0; *ap && i < NFSCLNT_ADDRMAX; i++, ap++) {
154                         clp->m_addrlist[i] = *(struct in_addr *)*ap;
155                 }
156                 clp->m_naddr = i;
157         }
158 }
159
160 void
161 client_add(nfs_client *clp)
162 {
163         nfs_client      **cpp;
164
165         if (clp->m_type < 0 || clp->m_type >= MCL_MAXTYPES)
166                 xlog(L_FATAL, "unknown client type in client_add");
167         cpp = clientlist + clp->m_type;
168         while (*cpp)
169                 cpp = &((*cpp)->m_next);
170         clp->m_next = NULL;
171         *cpp = clp;
172 }
173
174 void
175 client_release(nfs_client *clp)
176 {
177         if (clp->m_count <= 0)
178                 xlog(L_FATAL, "client_free: m_count <= 0!");
179         clp->m_count--;
180 }
181
182 void
183 client_freeall(void)
184 {
185         nfs_client      *clp, **head;
186         int             i;
187
188         for (i = 0; i < MCL_MAXTYPES; i++) {
189                 head = clientlist + i;
190                 while (*head) {
191                         *head = (clp = *head)->m_next;
192                         xfree(clp);
193                 }
194         }
195 }
196
197 nfs_client *
198 client_find(struct hostent *hp)
199 {
200         nfs_client      *clp;
201         int             i;
202
203         for (i = 0; i < MCL_MAXTYPES; i++) {
204                 for (clp = clientlist[i]; clp; clp = clp->m_next) {
205                         if (!client_check(clp, hp))
206                                 continue;
207 #ifdef notdef
208                         if (clp->m_type == MCL_FQDN)
209                                 return clp;
210                         return client_dup(clp, hp);
211 #else
212                         return clp;
213 #endif
214                 }
215         }
216         return NULL;
217 }
218
219 /*
220  * Match a host (given its hostent record) to a client record. This
221  * is usually called from mountd.
222  */
223 int
224 client_check(nfs_client *clp, struct hostent *hp)
225 {
226         char    *hname = (char *) hp->h_name;
227         char    *cname = clp->m_hostname;
228         char    **ap;
229
230         switch (clp->m_type) {
231         case MCL_FQDN:
232         case MCL_SUBNETWORK:
233                 for (ap = hp->h_addr_list; *ap; ap++) {
234                         if (client_checkaddr(clp, *(struct in_addr *) *ap))
235                                 return 1;
236                 }
237                 return 0;
238         case MCL_WILDCARD:
239                 if (wildmat(hname, cname))
240                         return 1;
241                 else {
242                         for (ap = hp->h_aliases; *ap; ap++)
243                                 if (wildmat(*ap, cname))
244                                         return 1;
245                 }
246                 return 0;
247         case MCL_NETGROUP:
248 #ifdef HAVE_INNETGR
249                 {
250                         char    *dot;
251                         int     match;
252                         struct hostent *nhp = NULL;
253                         struct sockaddr_in addr;
254
255                         /* First, try to match the hostname without
256                          * splitting off the domain */
257                         if (innetgr(cname+1, hname, NULL, NULL))
258                                 return 1;
259
260                         /* If hname is ip address convert to FQDN */
261                         if (inet_aton(hname, &addr.sin_addr) &&
262                            (nhp = gethostbyaddr((const char *)&(addr.sin_addr),
263                             sizeof(addr.sin_addr), AF_INET))) {
264                                 hname = (char *)nhp->h_name;
265                                 if (innetgr(cname+1, hname, NULL, NULL))
266                                         return 1;
267                         }
268
269                         /* Okay, strip off the domain (if we have one) */
270                         if ((dot = strchr(hname, '.')) == NULL)
271                                 return 0;
272
273                         *dot = '\0';
274                         match = innetgr(cname+1, hname, NULL, NULL);
275                         *dot = '.';
276
277                         return match;
278                 }
279 #else
280                 return 0;
281 #endif
282         case MCL_ANONYMOUS:
283                 return 1;
284         default:
285                 xlog(L_FATAL, "internal: bad client type %d", clp->m_type);
286         }
287
288         return 0;
289 }
290
291 static int
292 client_checkaddr(nfs_client *clp, struct in_addr addr)
293 {
294         int     i;
295
296         switch (clp->m_type) {
297         case MCL_FQDN:
298                 for (i = 0; i < clp->m_naddr; i++) {
299                         if (clp->m_addrlist[i].s_addr == addr.s_addr)
300                                 return 1;
301                 }
302                 return 0;
303         case MCL_SUBNETWORK:
304                 return !((clp->m_addrlist[0].s_addr ^ addr.s_addr)
305                         & clp->m_addrlist[1].s_addr);
306         }
307         return 0;
308 }
309
310 int
311 client_gettype(char *ident)
312 {
313         char    *sp;
314
315         if (ident[0] == '\0' || strcmp(ident, "*")==0)
316                 return MCL_ANONYMOUS;
317         if (ident[0] == '@') {
318 #ifndef HAVE_INNETGR
319                 xlog(L_WARNING, "netgroup support not compiled in");
320 #endif
321                 return MCL_NETGROUP;
322         }
323         for (sp = ident; *sp; sp++) {
324                 if (*sp == '*' || *sp == '?' || *sp == '[')
325                         return MCL_WILDCARD;
326                 if (*sp == '/')
327                         return MCL_SUBNETWORK;
328                 if (*sp == '\\' && sp[1])
329                         sp++;
330         }
331         return MCL_FQDN;
332 }