mountd: Replace "struct hostent" with "struct addrinfo"
[nfs-utils.git] / utils / mountd / auth.c
1 /*
2  * utils/mountd/auth.c
3  *
4  * Authentication procedures for mountd.
5  *
6  * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
7  */
8
9 #ifdef HAVE_CONFIG_H
10 #include <config.h>
11 #endif
12
13 #include <sys/stat.h>
14 #include <netinet/in.h>
15 #include <arpa/inet.h>
16 #include <errno.h>
17 #include <unistd.h>
18 #include "misc.h"
19 #include "nfslib.h"
20 #include "exportfs.h"
21 #include "mountd.h"
22 #include "xmalloc.h"
23 #include "v4root.h"
24
25 enum auth_error
26 {
27   bad_path,
28   unknown_host,
29   no_entry,
30   not_exported,
31   illegal_port,
32   success
33 };
34
35 static void             auth_fixpath(char *path);
36 static char     *export_file = NULL;
37 static nfs_export my_exp;
38 static nfs_client my_client;
39
40 extern int new_cache;
41 extern int use_ipaddr;
42
43 void
44 auth_init(char *exports)
45 {
46
47         export_file = exports;
48         auth_reload();
49         xtab_mount_write();
50 }
51
52 /*
53  * A client can match many different netgroups and it's tough to know
54  * beforehand whether it will. If the concatenated string of netgroup
55  * m_hostnames is >512 bytes, then enable the "use_ipaddr" mode. This
56  * makes mountd change how it matches a client ip address when a mount
57  * request comes in. It's more efficient at handling netgroups at the
58  * expense of larger kernel caches.
59  */
60 static void
61 check_useipaddr(void)
62 {
63         nfs_client *clp;
64         int old_use_ipaddr = use_ipaddr;
65         unsigned int len = 0;
66
67         /* add length of m_hostname + 1 for the comma */
68         for (clp = clientlist[MCL_NETGROUP]; clp; clp = clp->m_next)
69                 len += (strlen(clp->m_hostname) + 1);
70
71         if (len > (NFSCLNT_IDMAX / 2))
72                 use_ipaddr = 1;
73         else
74                 use_ipaddr = 0;
75
76         if (use_ipaddr != old_use_ipaddr)
77                 cache_flush(1);
78 }
79
80 unsigned int
81 auth_reload()
82 {
83         struct stat             stb;
84         static ino_t            last_inode;
85         static int              last_fd;
86         static unsigned int     counter;
87         int                     fd;
88
89         if ((fd = open(_PATH_ETAB, O_RDONLY)) < 0) {
90                 xlog(L_FATAL, "couldn't open %s", _PATH_ETAB);
91         } else if (fstat(fd, &stb) < 0) {
92                 xlog(L_FATAL, "couldn't stat %s", _PATH_ETAB);
93         } else if (stb.st_ino == last_inode) {
94                 close(fd);
95                 return counter;
96         } else {
97                 close(last_fd);
98                 last_fd = fd;
99                 last_inode = stb.st_ino;
100         }
101
102         export_freeall();
103         memset(&my_client, 0, sizeof(my_client));
104         xtab_export_read();
105         check_useipaddr();
106         v4root_set();
107
108         ++counter;
109
110         return counter;
111 }
112
113 static char *
114 get_client_hostname(struct sockaddr_in *caller, struct addrinfo *ai,
115                 enum auth_error *error)
116 {
117         char *n;
118
119         if (use_ipaddr)
120                 return strdup(inet_ntoa(caller->sin_addr));
121         n = client_compose(ai);
122         *error = unknown_host;
123         if (!n)
124                 return NULL;
125         if (*n)
126                 return n;
127         free(n);
128         return strdup("DEFAULT");
129 }
130
131 /* return static nfs_export with details filled in */
132 static nfs_export *
133 auth_authenticate_newcache(struct sockaddr_in *caller,
134                            char *path, struct addrinfo *ai,
135                            enum auth_error *error)
136 {
137         nfs_export *exp;
138         int i;
139
140         free(my_client.m_hostname);
141
142         my_client.m_hostname = get_client_hostname(caller, ai, error);
143         if (my_client.m_hostname == NULL)
144                 return NULL;
145
146         my_client.m_naddr = 1;
147         set_addrlist_in(&my_client, 0, caller);
148         my_exp.m_client = &my_client;
149
150         exp = NULL;
151         for (i = 0; !exp && i < MCL_MAXTYPES; i++)
152                 for (exp = exportlist[i].p_head; exp; exp = exp->m_next) {
153                         if (strcmp(path, exp->m_export.e_path))
154                                 continue;
155                         if (!use_ipaddr && !client_member(my_client.m_hostname, exp->m_client->m_hostname))
156                                 continue;
157                         if (use_ipaddr && !client_check(exp->m_client, ai))
158                                 continue;
159                         break;
160                 }
161         *error = not_exported;
162         if (!exp)
163                 return NULL;
164
165         my_exp.m_export = exp->m_export;
166         exp = &my_exp;
167         return exp;
168 }
169
170 static nfs_export *
171 auth_authenticate_internal(struct sockaddr_in *caller,
172                            char *path, struct addrinfo *ai,
173                            enum auth_error *error)
174 {
175         nfs_export *exp;
176
177         if (new_cache) {
178                 exp = auth_authenticate_newcache(caller, path, ai, error);
179                 if (!exp)
180                         return NULL;
181         } else {
182                 exp = export_find(ai, path);
183                 if (exp == NULL) {
184                         *error = no_entry;
185                         return NULL;
186                 }
187         }
188         if (exp->m_export.e_flags & NFSEXP_V4ROOT) {
189                 *error = no_entry;
190                 return NULL;
191         }
192         if (!(exp->m_export.e_flags & NFSEXP_INSECURE_PORT) &&
193                      ntohs(caller->sin_port) >= IPPORT_RESERVED) {
194                 *error = illegal_port;
195                 return NULL;
196         }
197         *error = success;
198
199         return exp;
200 }
201
202 nfs_export *
203 auth_authenticate(char *what, struct sockaddr_in *caller, char *path)
204 {
205         nfs_export      *exp = NULL;
206         char            epath[MAXPATHLEN+1];
207         char            *p = NULL;
208         struct addrinfo *ai = NULL;
209         struct in_addr  addr = caller->sin_addr;
210         enum auth_error error = bad_path;
211
212         if (path [0] != '/') {
213                 xlog(L_WARNING, "bad path in %s request from %s: \"%s\"",
214                      what, inet_ntoa(addr), path);
215                 return exp;
216         }
217
218         strncpy(epath, path, sizeof (epath) - 1);
219         epath[sizeof (epath) - 1] = '\0';
220         auth_fixpath(epath); /* strip duplicate '/' etc */
221
222         ai = client_resolve((struct sockaddr *)caller);
223         if (ai == NULL)
224                 return exp;
225
226         /* Try the longest matching exported pathname. */
227         while (1) {
228                 exp = auth_authenticate_internal(caller, epath,
229                                                  ai, &error);
230                 if (exp || (error != not_exported && error != no_entry))
231                         break;
232                 /* We have to treat the root, "/", specially. */
233                 if (p == &epath[1]) break;
234                 p = strrchr(epath, '/');
235                 if (p == epath) p++;
236                 *p = '\0';
237         }
238
239         switch (error) {
240         case bad_path:
241                 xlog(L_WARNING, "bad path in %s request from %s: \"%s\"",
242                      what, inet_ntoa(addr), path);
243                 break;
244
245         case unknown_host:
246                 xlog(L_WARNING, "refused %s request from %s for %s (%s): unmatched host",
247                      what, inet_ntoa(addr), path, epath);
248                 break;
249
250         case no_entry:
251                 xlog(L_WARNING, "refused %s request from %s for %s (%s): no export entry",
252                      what, ai->ai_canonname, path, epath);
253                 break;
254
255         case not_exported:
256                 xlog(L_WARNING, "refused %s request from %s for %s (%s): not exported",
257                      what, ai->ai_canonname, path, epath);
258                 break;
259
260         case illegal_port:
261                 xlog(L_WARNING, "refused %s request from %s for %s (%s): illegal port %d",
262                      what, ai->ai_canonname, path, epath, ntohs(caller->sin_port));
263                 break;
264
265         case success:
266                 xlog(L_NOTICE, "authenticated %s request from %s:%d for %s (%s)",
267                      what, ai->ai_canonname, ntohs(caller->sin_port), path, epath);
268                 break;
269         default:
270                 xlog(L_NOTICE, "%s request from %s:%d for %s (%s) gave %d",
271                      what, ai->ai_canonname, ntohs(caller->sin_port),
272                         path, epath, error);
273         }
274
275         freeaddrinfo(ai);
276         return exp;
277 }
278
279 static void
280 auth_fixpath(char *path)
281 {
282         char    *sp, *cp;
283
284         for (sp = cp = path; *sp; sp++) {
285                 if (*sp != '/' || sp[1] != '/')
286                         *cp++ = *sp;
287         }
288         while (cp > path+1 && cp[-1] == '/')
289                 cp--;
290         *cp = '\0';
291 }