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