dfe61ea7fcc7c8d49faa697541f31ebf0e8d9eee
[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]; 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/2 ||
173                      ntohs(caller->sin_port) >= IPPORT_RESERVED)) {
174                 *error = illegal_port;
175                 return NULL;
176         }
177         *error = success;
178
179         return exp;
180 }
181
182 nfs_export *
183 auth_authenticate(char *what, struct sockaddr_in *caller, char *path)
184 {
185         nfs_export      *exp = NULL;
186         char            epath[MAXPATHLEN+1];
187         char            *p = NULL;
188         struct hostent  *hp = NULL;
189         struct in_addr  addr = caller->sin_addr;
190         enum auth_error error = bad_path;
191
192         if (path [0] != '/') {
193                 xlog(L_WARNING, "bad path in %s request from %s: \"%s\"",
194                      what, inet_ntoa(addr), path);
195                 return exp;
196         }
197
198         strncpy(epath, path, sizeof (epath) - 1);
199         epath[sizeof (epath) - 1] = '\0';
200         auth_fixpath(epath); /* strip duplicate '/' etc */
201
202         hp = client_resolve(caller->sin_addr);
203         if (!hp)
204                 return exp;
205
206         /* Try the longest matching exported pathname. */
207         while (1) {
208                 exp = auth_authenticate_internal(what, caller, epath,
209                                                  hp, &error);
210                 if (exp || (error != not_exported && error != no_entry))
211                         break;
212                 /* We have to treat the root, "/", specially. */
213                 if (p == &epath[1]) break;
214                 p = strrchr(epath, '/');
215                 if (p == epath) p++;
216                 *p = '\0';
217         }
218
219         switch (error) {
220         case bad_path:
221                 xlog(L_WARNING, "bad path in %s request from %s: \"%s\"",
222                      what, inet_ntoa(addr), path);
223                 break;
224
225         case unknown_host:
226                 xlog(L_WARNING, "refused %s request from %s for %s (%s): unmatched host",
227                      what, inet_ntoa(addr), path, epath);
228                 break;
229
230         case no_entry:
231                 xlog(L_WARNING, "refused %s request from %s for %s (%s): no export entry",
232                      what, hp->h_name, path, epath);
233                 break;
234
235         case not_exported:
236                 xlog(L_WARNING, "refused %s request from %s for %s (%s): not exported",
237                      what, hp->h_name, path, epath);
238                 break;
239
240         case illegal_port:
241                 xlog(L_WARNING, "refused %s request from %s for %s (%s): illegal port %d",
242                      what, hp->h_name, path, epath, ntohs(caller->sin_port));
243                 break;
244
245         case success:
246                 xlog(L_NOTICE, "authenticated %s request from %s:%d for %s (%s)",
247                      what, hp->h_name, ntohs(caller->sin_port), path, epath);
248                 break;
249         default:
250                 xlog(L_NOTICE, "%s request from %s:%d for %s (%s) gave %d",
251                      what, hp->h_name, ntohs(caller->sin_port), path, epath, error);
252         }
253
254         if (hp)
255                 free (hp);
256
257         return exp;
258 }
259
260 static void
261 auth_fixpath(char *path)
262 {
263         char    *sp, *cp;
264
265         for (sp = cp = path; *sp; sp++) {
266                 if (*sp != '/' || sp[1] != '/')
267                         *cp++ = *sp;
268         }
269         while (cp > path+1 && cp[-1] == '/')
270                 cp--;
271         *cp = '\0';
272 }