d79bf150c6888b9cf64eed752acca85c286d4416
[nfs-utils.git] / utils / mountd / mountd.c
1 /*
2  * utils/mountd/mountd.c
3  *
4  * Authenticate mount requests and retrieve file handle.
5  *
6  * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
7  */
8
9 #include "config.h"
10
11 #include <signal.h>
12 #include <sys/stat.h>
13 #include <netinet/in.h>
14 #include <arpa/inet.h>
15 #include <unistd.h>
16 #include <stdlib.h>
17 #include <getopt.h>
18 #include <errno.h>
19 #include <fcntl.h>
20 #include "xmalloc.h"
21 #include "misc.h"
22 #include "mountd.h"
23 #include "rpcmisc.h"
24
25 static void             usage(const char *, int exitcode);
26 static exports          get_exportlist(void);
27 static struct nfs_fh_len *get_rootfh(struct svc_req *, dirpath *, int *, int v3);
28
29 static struct option longopts[] =
30 {
31         { "foreground", 0, 0, 'F' },
32         { "debug", 1, 0, 'd' },
33         { "help", 0, 0, 'h' },
34         { "exports-file", 1, 0, 'f' },
35         { "nfs-version", 1, 0, 'V' },
36         { "no-nfs-version", 1, 0, 'N' },
37         { "version", 0, 0, 'v' },
38         { "port", 1, 0, 'p' },
39         { NULL, 0, 0, 0 }
40 };
41
42 static int nfs_version = -1;
43
44 /*
45  * Signal handler.
46  */
47 static void 
48 killer (int sig)
49 {
50   if (nfs_version & 0x1)
51     pmap_unset (MOUNTPROG, MOUNTVERS);
52   if (nfs_version & (0x1 << 1))
53     pmap_unset (MOUNTPROG, MOUNTVERS_POSIX);
54   if (nfs_version & (0x1 << 2))
55     pmap_unset (MOUNTPROG, MOUNTVERS_NFSV3);
56   xlog (L_FATAL, "Caught signal %d, un-registering and exiting.", sig);
57 }
58
59 bool_t
60 mount_null_1_svc(struct svc_req *rqstp, void *argp, void *resp)
61 {
62         return 1;
63 }
64
65 bool_t
66 mount_mnt_1_svc(struct svc_req *rqstp, dirpath *path, fhstatus *res)
67 {
68         struct nfs_fh_len *fh;
69
70         xlog(D_CALL, "MNT1(%s) called", *path);
71         if ((fh = get_rootfh(rqstp, path, &res->fhs_status, 0)) != NULL)
72                 memcpy(&res->fhstatus_u.fhs_fhandle, fh->fh_handle, 32);
73         return 1;
74 }
75
76 bool_t
77 mount_dump_1_svc(struct svc_req *rqstp, void *argp, mountlist *res)
78 {
79         struct sockaddr_in *addr =
80                 (struct sockaddr_in *) svc_getcaller(rqstp->rq_xprt);
81         xlog(L_NOTICE, "dump request from %s",
82                 inet_ntoa(addr->sin_addr));
83
84         *res = mountlist_list();
85         return 1;
86 }
87
88 bool_t
89 mount_umnt_1_svc(struct svc_req *rqstp, dirpath *argp, void *resp)
90 {
91         struct sockaddr_in *sin
92                 = (struct sockaddr_in *) svc_getcaller(rqstp->rq_xprt);
93         nfs_export      *exp;
94         char            *p = *argp;
95         char            rpath[MAXPATHLEN+1];
96
97         if (*p == '\0')
98                 p = "/";
99
100         if (realpath(p, rpath) != NULL) {
101                 rpath[sizeof (rpath) - 1] = '\0';
102                 p = rpath;
103         }
104
105         if (!(exp = auth_authenticate("unmount", sin, p))) {
106                 return 1;
107         }
108         mountlist_del(exp, p);
109         export_reset (exp);
110         return 1;
111 }
112
113 bool_t
114 mount_umntall_1_svc(struct svc_req *rqstp, void *argp, void *resp)
115 {
116         /* Reload /etc/xtab if necessary */
117         auth_reload();
118
119         mountlist_del_all((struct sockaddr_in *) svc_getcaller(rqstp->rq_xprt));
120         return 1;
121 }
122
123 bool_t
124 mount_export_1_svc(struct svc_req *rqstp, void *argp, exports *resp)
125 {
126         struct sockaddr_in *addr =
127                 (struct sockaddr_in *) svc_getcaller(rqstp->rq_xprt);
128         xlog(L_NOTICE, "export request from %s",
129                 inet_ntoa(addr->sin_addr));
130         *resp = get_exportlist();
131         return 1;
132 }
133
134 bool_t
135 mount_exportall_1_svc(struct svc_req *rqstp, void *argp, exports *resp)
136 {
137         struct sockaddr_in *addr =
138                 (struct sockaddr_in *) svc_getcaller(rqstp->rq_xprt);
139         xlog(L_NOTICE, "exportall request from %s",
140                 inet_ntoa(addr->sin_addr));
141         *resp = get_exportlist();
142         return 1;
143 }
144
145 /*
146  * MNTv2 pathconf procedure
147  *
148  * The protocol doesn't include a status field, so Sun apparently considers
149  * it good practice to let anyone snoop on your system, even if it's
150  * pretty harmless data such as pathconf. We don't.
151  *
152  * Besides, many of the pathconf values don't make much sense on NFS volumes.
153  * FIFOs and tty device files represent devices on the *client*, so there's
154  * no point in getting the server's buffer sizes etc.
155  */
156 bool_t
157 mount_pathconf_2_svc(struct svc_req *rqstp, dirpath *path, ppathcnf *res)
158 {
159         struct sockaddr_in *sin
160                 = (struct sockaddr_in *) svc_getcaller(rqstp->rq_xprt);
161         struct stat     stb;
162         nfs_export      *exp;
163         char            rpath[MAXPATHLEN+1];
164         char            *p = *path;
165
166         memset(res, 0, sizeof(*res));
167
168         if (*p == '\0')
169                 p = "/";
170
171         /* Reload /etc/xtab if necessary */
172         auth_reload();
173
174         /* Resolve symlinks */
175         if (realpath(p, rpath) != NULL) {
176                 rpath[sizeof (rpath) - 1] = '\0';
177                 p = rpath;
178         }
179
180         /* Now authenticate the intruder... */
181         if (!(exp = auth_authenticate("mount", sin, p))) {
182                 return 1;
183         } else if (stat(p, &stb) < 0) {
184                 xlog(L_WARNING, "can't stat exported dir %s: %s",
185                                 p, strerror(errno));
186                 export_reset (exp);
187                 return 1;
188         }
189
190         res->pc_link_max  = pathconf(p, _PC_LINK_MAX);
191         res->pc_max_canon = pathconf(p, _PC_MAX_CANON);
192         res->pc_max_input = pathconf(p, _PC_MAX_INPUT);
193         res->pc_name_max  = pathconf(p, _PC_NAME_MAX);
194         res->pc_path_max  = pathconf(p, _PC_PATH_MAX);
195         res->pc_pipe_buf  = pathconf(p, _PC_PIPE_BUF);
196         res->pc_vdisable  = pathconf(p, _PC_VDISABLE);
197
198         /* Can't figure out what to do with pc_mask */
199         res->pc_mask[0]   = 0;
200         res->pc_mask[1]   = 0;
201
202         export_reset (exp);
203
204         return 1;
205 }
206
207 /*
208  * NFSv3 MOUNT procedure
209  */
210 bool_t
211 mount_mnt_3_svc(struct svc_req *rqstp, dirpath *path, mountres3 *res)
212 {
213         static int      flavors[] = { AUTH_NULL, AUTH_UNIX };
214         struct nfs_fh_len *fh;
215
216         xlog(D_CALL, "MNT3(%s) called", *path);
217         if ((fh = get_rootfh(rqstp, path, (int *) &res->fhs_status, 1)) != NULL) {
218                 struct mountres3_ok     *ok = &res->mountres3_u.mountinfo;
219
220                 ok->fhandle.fhandle3_len = fh->fh_size;
221                 ok->fhandle.fhandle3_val = fh->fh_handle;
222                 ok->auth_flavors.auth_flavors_len = 2;
223                 ok->auth_flavors.auth_flavors_val = flavors;
224         }
225         return 1;
226 }
227
228 static struct nfs_fh_len *
229 get_rootfh(struct svc_req *rqstp, dirpath *path, int *error, int v3)
230 {
231         struct sockaddr_in *sin =
232                 (struct sockaddr_in *) svc_getcaller(rqstp->rq_xprt);
233         struct stat     stb;
234         nfs_export      *exp;
235         char            rpath[MAXPATHLEN+1];
236         char            *p = *path;
237
238         if (*p == '\0')
239                 p = "/";
240
241         /* Reload /var/lib/nfs/etab if necessary */
242         auth_reload();
243
244         /* Resolve symlinks */
245         if (realpath(p, rpath) != NULL) {
246                 rpath[sizeof (rpath) - 1] = '\0';
247                 p = rpath;
248         }
249
250         /* Now authenticate the intruder... */
251         if (!(exp = auth_authenticate("mount", sin, p))) {
252                 *error = NFSERR_ACCES;
253         } else if (stat(p, &stb) < 0) {
254                 xlog(L_WARNING, "can't stat exported dir %s: %s",
255                                 p, strerror(errno));
256                 if (errno == ENOENT)
257                         *error = NFSERR_NOENT;
258                 else
259                         *error = NFSERR_ACCES;
260         } else if (!S_ISDIR(stb.st_mode) && !S_ISREG(stb.st_mode)) {
261                 xlog(L_WARNING, "%s is not a directory or regular file", p);
262                 *error = NFSERR_NOTDIR;
263         } else {
264                 struct nfs_fh_len  *fh;
265
266                 if (exp->m_exported<1)
267                         export_export(exp);
268                 if (!exp->m_xtabent)
269                         xtab_append(exp);
270
271                 if (v3)
272                         fh = getfh_size ((struct sockaddr *) sin, p, 64);
273                 if (!v3 || (fh == NULL && errno == EINVAL)) {
274                         /* We first try the new nfs syscall. */
275                         fh = getfh ((struct sockaddr *) sin, p);
276                         if (fh == NULL && errno == EINVAL)
277                                 /* Let's try the old one. */
278                                 fh = getfh_old ((struct sockaddr *) sin,
279                                                 stb.st_dev, stb.st_ino);
280                 }
281                 if (fh != NULL) {
282                         mountlist_add(exp, p);
283                         *error = NFS_OK;
284                         export_reset (exp);
285                         return fh;
286                 }
287                 xlog(L_WARNING, "getfh failed: %s", strerror(errno));
288                 *error = NFSERR_ACCES;
289         }
290         export_reset (exp);
291         return NULL;
292 }
293
294 static exports
295 get_exportlist(void)
296 {
297         static exports          elist = NULL;
298         struct exportnode       *e, *ne;
299         struct groupnode        *g, *ng, *c, **cp;
300         nfs_export              *exp;
301         int                     i;
302
303         if (!auth_reload() && elist)
304                 return elist;
305
306         for (e = elist; e != NULL; e = ne) {
307                 ne = e->ex_next;
308                 for (g = e->ex_groups; g != NULL; g = ng) {
309                         ng = g->gr_next;
310                         xfree(g->gr_name);
311                         xfree(g);
312                 }
313                 xfree(e->ex_dir);
314                 xfree(e);
315         }
316         elist = NULL;
317
318         for (i = 0; i < MCL_MAXTYPES; i++) {
319                 for (exp = exportlist[i]; exp; exp = exp->m_next) {
320                         for (e = elist; e != NULL; e = e->ex_next) {
321                                 if (!strcmp(exp->m_export.m_path, e->ex_dir))
322                                         break;
323                         }
324                         if (!e) {
325                                 e = (struct exportnode *) xmalloc(sizeof(*e));
326                                 e->ex_next = elist;
327                                 e->ex_groups = NULL;
328                                 e->ex_dir = xstrdup(exp->m_export.m_path);
329                                 elist = e;
330                         }
331
332                         /* We need to check if we should remove
333                            previous ones. */
334                         if (i == MCL_ANONYMOUS && e->ex_groups) {
335                                 for (g = e->ex_groups; g; g = ng) {
336                                         ng = g->gr_next;
337                                         xfree(g->gr_name);
338                                         xfree(g);
339                                 }
340                                 e->ex_groups = NULL;
341                                 continue;
342                         }
343
344                         if (i != MCL_FQDN && e->ex_groups) {
345                           struct hostent        *hp;
346
347                           cp = &e->ex_groups;
348                           while ((c = *cp) != NULL) {
349                             if (client_gettype (c->gr_name) == MCL_FQDN
350                                 && (hp = gethostbyname(c->gr_name))) {
351                               hp = hostent_dup (hp);
352                               if (client_check(exp->m_client, hp)) {
353                                 *cp = c->gr_next;
354                                 xfree(c->gr_name);
355                                 xfree(c);
356                                 xfree (hp);
357                                 if ((c = *cp) == NULL)
358                                   break;
359                               }
360                               else
361                                 xfree (hp);
362                             }
363                             cp = &(c->gr_next);
364                           }
365                         }
366
367                         if (exp->m_export.e_hostname [0] != '\0') {
368                                 for (g = e->ex_groups; g; g = g->gr_next)
369                                         if (strcmp (exp->m_export.e_hostname,
370                                                     g->gr_name) == 0)
371                                                 break;
372                                 if (g)
373                                         continue;
374                                 g = (struct groupnode *) xmalloc(sizeof(*g));
375                                 g->gr_name = xstrdup(exp->m_export.e_hostname);
376                                 g->gr_next = e->ex_groups;
377                                 e->ex_groups = g;
378                         }
379                 }
380         }
381
382         return elist;
383 }
384
385 int
386 main(int argc, char **argv)
387 {
388         char    *export_file = _PATH_EXPORTS;
389         int     foreground = 0;
390         int     port = 0;
391         int     c;
392         struct sigaction sa;
393
394         /* Parse the command line options and arguments. */
395         opterr = 0;
396         while ((c = getopt_long(argc, argv, "Fd:f:p:P:hN:V:v", longopts, NULL)) != EOF)
397                 switch (c) {
398                 case 'F':
399                         foreground = 1;
400                         break;
401                 case 'd':
402                         xlog_sconfig(optarg, 1);
403                         break;
404                 case 'f':
405                         export_file = optarg;
406                         break;
407                 case 'h':
408                         usage(argv [0], 0);
409                         break;
410                 case 'P':       /* XXX for nfs-server compatibility */
411                 case 'p':
412                         port = atoi(optarg);
413                         if (port <= 0 || port > 65535) {
414                                 fprintf(stderr, "%s: bad port number: %s\n",
415                                         argv [0], optarg);
416                                 usage(argv [0], 1);
417                         }
418                         break;
419                 case 'N':
420                         nfs_version &= ~(1 << (atoi (optarg) - 1));
421                         break;
422                 case 'V':
423                         nfs_version |= 1 << (atoi (optarg) - 1);
424                         break;
425                 case 'v':
426                         printf("kmountd %s\n", VERSION);
427                         exit(0);
428                 case 0:
429                         break;
430                 case '?':
431                 default:
432                         usage(argv [0], 1);
433                 }
434
435         /* No more arguments allowed. */
436         if (optind != argc || !(nfs_version & 0x7))
437                 usage(argv [0], 1);
438
439         /* Initialize logging. */
440 /*      xlog_open("mountd"); */
441
442         sa.sa_handler = SIG_IGN;
443         sa.sa_flags = 0;
444         sigemptyset(&sa.sa_mask);
445         sigaction(SIGHUP, &sa, NULL);
446         sigaction(SIGINT, &sa, NULL);
447         sigaction(SIGTERM, &sa, NULL);
448         /* WARNING: the following works on Linux and SysV, but not BSD! */
449         sigaction(SIGCHLD, &sa, NULL);
450
451         if (nfs_version & 0x1)
452                 rpc_init("mountd", MOUNTPROG, MOUNTVERS,
453                          mount_dispatch, port, 0);
454         if (nfs_version & (0x1 << 1))
455                 rpc_init("mountd", MOUNTPROG, MOUNTVERS_POSIX,
456                          mount_dispatch, port, 0);
457         if (nfs_version & (0x1 << 2))
458                 rpc_init("mountd", MOUNTPROG, MOUNTVERS_NFSV3,
459                          mount_dispatch, port, 0);
460
461         sa.sa_handler = killer;
462         sigaction(SIGHUP, &sa, NULL);
463         sigaction(SIGINT, &sa, NULL);
464         sigaction(SIGTERM, &sa, NULL);
465
466         auth_init(export_file);
467
468         if (!foreground) {
469                 /* We first fork off a child. */
470                 if ((c = fork()) > 0)
471                         exit(0);
472                 if (c < 0) {
473                         xlog(L_FATAL, "mountd: cannot fork: %s\n",
474                                                 strerror(errno));
475                 }
476                 /* Now we remove ourselves from the foreground.
477                    Redirect stdin/stdout/stderr first. */
478                 {
479                         int fd = open("/dev/null", O_RDWR);
480                         (void) dup2(fd, 0);
481                         (void) dup2(fd, 1);
482                         (void) dup2(fd, 2);
483                         if (fd > 2) (void) close(fd);
484                 }
485                 setsid();
486                 xlog_background();
487         }
488
489         svc_run();
490
491         xlog(L_ERROR, "Ack! Gack! svc_run returned!\n");
492         exit(1);
493 }
494
495 static void
496 usage(const char *prog, int n)
497 {
498         fprintf(stderr,
499 "Usage: %s [-Fhnv] [-d kind] [-f exports-file] [-V version]\n"
500 "       [-N version] [--debug kind] [-p|--port port] [--help] [--version]\n"
501 "       [--exports-file=file] [--nfs-version version]\n"
502 "       [--no-nfs-version version]\n", prog);
503         exit(n);
504 }