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