X-Git-Url: https://git.decadent.org.uk/gitweb/?p=nfs-utils.git;a=blobdiff_plain;f=utils%2Fmountd%2Fmountd.c;h=5373d81ff3c207c3c5719df902f58af1e264bc6f;hp=e151fa2246eee9c390a0aee88996be0a3b85b808;hb=2fd2dfd9ebb2de145d50039233bd2a53b7f03062;hpb=9e1aa7b9a04776bd43eb75bef50a53f41e17b813 diff --git a/utils/mountd/mountd.c b/utils/mountd/mountd.c index e151fa2..5373d81 100644 --- a/utils/mountd/mountd.c +++ b/utils/mountd/mountd.c @@ -26,18 +26,22 @@ #include "misc.h" #include "mountd.h" #include "rpcmisc.h" +#include "pseudoflavors.h" extern void cache_open(void); extern struct nfs_fh_len *cache_get_filehandle(nfs_export *exp, int len, char *p); -extern void cache_export(nfs_export *exp); +extern int cache_export(nfs_export *exp, char *path); extern void my_svc_run(void); static void usage(const char *, int exitcode); static exports get_exportlist(void); -static struct nfs_fh_len *get_rootfh(struct svc_req *, dirpath *, mountstat3 *, int v3); +static struct nfs_fh_len *get_rootfh(struct svc_req *, dirpath *, nfs_export **, mountstat3 *, int v3); +int reverse_resolve = 0; int new_cache = 0; +int manage_gids; +int use_ipaddr = -1; /* PRC: a high-availability callout program can be specified with -H * When this is done, the program will receive callouts whenever clients @@ -66,6 +70,8 @@ static struct option longopts[] = { "ha-callout", 1, 0, 'H' }, { "state-directory-path", 1, 0, 's' }, { "num-threads", 1, 0, 't' }, + { "reverse-lookup", 0, 0, 'r' }, + { "manage-gids", 0, 0, 'g' }, { NULL, 0, 0, 0 } }; @@ -74,14 +80,22 @@ static int nfs_version = -1; static void unregister_services (void) { - if (nfs_version & 0x1) + if (nfs_version & (0x1 << 1)) { pmap_unset (MOUNTPROG, MOUNTVERS); - if (nfs_version & (0x1 << 1)) pmap_unset (MOUNTPROG, MOUNTVERS_POSIX); + } if (nfs_version & (0x1 << 2)) pmap_unset (MOUNTPROG, MOUNTVERS_NFSV3); } +static void +cleanup_lockfiles (void) +{ + unlink(_PATH_XTABLCK); + unlink(_PATH_ETABLCK); + unlink(_PATH_RMTABLCK); +} + /* Wait for all worker child processes to exit and reap them */ static void wait_for_workers (void) @@ -148,6 +162,7 @@ fork_workers(void) /* in parent */ wait_for_workers(); unregister_services(); + cleanup_lockfiles(); xlog(L_NOTICE, "mountd: no more workers, exiting\n"); exit(0); } @@ -164,15 +179,16 @@ killer (int sig) kill(0, SIGTERM); wait_for_workers(); } + cleanup_lockfiles(); xlog (L_FATAL, "Caught signal %d, un-registering and exiting.", sig); } static void sig_hup (int sig) { - /* don't exit on SIGHUP */ - xlog (L_NOTICE, "Received SIGHUP... Ignoring.\n", sig); - return; + /* don't exit on SIGHUP */ + xlog (L_NOTICE, "Received SIGHUP... Ignoring.\n", sig); + return; } bool_t @@ -187,7 +203,8 @@ mount_mnt_1_svc(struct svc_req *rqstp, dirpath *path, fhstatus *res) struct nfs_fh_len *fh; xlog(D_CALL, "MNT1(%s) called", *path); - if ((fh = get_rootfh(rqstp, path, &res->fhs_status, 0)) != NULL) + fh = get_rootfh(rqstp, path, NULL, &res->fhs_status, 0); + if (fh) memcpy(&res->fhstatus_u.fhs_fhandle, fh->fh_handle, 32); return 1; } @@ -195,12 +212,10 @@ mount_mnt_1_svc(struct svc_req *rqstp, dirpath *path, fhstatus *res) bool_t mount_dump_1_svc(struct svc_req *rqstp, void *argp, mountlist *res) { - struct sockaddr_in *addr = - (struct sockaddr_in *) svc_getcaller(rqstp->rq_xprt); + struct sockaddr_in *addr = nfs_getrpccaller_in(rqstp->rq_xprt); - if ((*res = mountlist_list()) == NULL) - xlog(L_WARNING, "dump request from %s failed.", - inet_ntoa(addr->sin_addr)); + xlog(D_CALL, "dump request from %s.", inet_ntoa(addr->sin_addr)); + *res = mountlist_list(); return 1; } @@ -208,8 +223,7 @@ mount_dump_1_svc(struct svc_req *rqstp, void *argp, mountlist *res) bool_t mount_umnt_1_svc(struct svc_req *rqstp, dirpath *argp, void *resp) { - struct sockaddr_in *sin - = (struct sockaddr_in *) svc_getcaller(rqstp->rq_xprt); + struct sockaddr_in *sin = nfs_getrpccaller_in(rqstp->rq_xprt); nfs_export *exp; char *p = *argp; char rpath[MAXPATHLEN+1]; @@ -226,9 +240,6 @@ mount_umnt_1_svc(struct svc_req *rqstp, dirpath *argp, void *resp) return 1; } - if (!new_cache) - export_reset (exp); - mountlist_del(inet_ntoa(sin->sin_addr), p); return 1; } @@ -239,19 +250,17 @@ mount_umntall_1_svc(struct svc_req *rqstp, void *argp, void *resp) /* Reload /etc/xtab if necessary */ auth_reload(); - mountlist_del_all((struct sockaddr_in *) svc_getcaller(rqstp->rq_xprt)); + mountlist_del_all(nfs_getrpccaller_in(rqstp->rq_xprt)); return 1; } bool_t mount_export_1_svc(struct svc_req *rqstp, void *argp, exports *resp) { - struct sockaddr_in *addr = - (struct sockaddr_in *) svc_getcaller(rqstp->rq_xprt); + struct sockaddr_in *addr = nfs_getrpccaller_in(rqstp->rq_xprt); - if ((*resp = get_exportlist()) == NULL) - xlog(L_WARNING, "export request from %s failed.", - inet_ntoa(addr->sin_addr)); + xlog(D_CALL, "export request from %s.", inet_ntoa(addr->sin_addr)); + *resp = get_exportlist(); return 1; } @@ -259,12 +268,11 @@ mount_export_1_svc(struct svc_req *rqstp, void *argp, exports *resp) bool_t mount_exportall_1_svc(struct svc_req *rqstp, void *argp, exports *resp) { - struct sockaddr_in *addr = - (struct sockaddr_in *) svc_getcaller(rqstp->rq_xprt); + struct sockaddr_in *addr = nfs_getrpccaller_in(rqstp->rq_xprt); + + xlog(D_CALL, "exportall request from %s.", inet_ntoa(addr->sin_addr)); + *resp = get_exportlist(); - if ((*resp = get_exportlist()) == NULL) - xlog(L_WARNING, "exportall request from %s failed.", - inet_ntoa(addr->sin_addr)); return 1; } @@ -282,8 +290,7 @@ mount_exportall_1_svc(struct svc_req *rqstp, void *argp, exports *resp) bool_t mount_pathconf_2_svc(struct svc_req *rqstp, dirpath *path, ppathcnf *res) { - struct sockaddr_in *sin - = (struct sockaddr_in *) svc_getcaller(rqstp->rq_xprt); + struct sockaddr_in *sin = nfs_getrpccaller_in(rqstp->rq_xprt); struct stat stb; nfs_export *exp; char rpath[MAXPATHLEN+1]; @@ -304,12 +311,12 @@ mount_pathconf_2_svc(struct svc_req *rqstp, dirpath *path, ppathcnf *res) } /* Now authenticate the intruder... */ - if (!(exp = auth_authenticate("pathconf", sin, p))) { + exp = auth_authenticate("pathconf", sin, p); + if (!exp) { return 1; } else if (stat(p, &stb) < 0) { xlog(L_WARNING, "can't stat exported dir %s: %s", p, strerror(errno)); - export_reset (exp); return 1; } @@ -325,43 +332,71 @@ mount_pathconf_2_svc(struct svc_req *rqstp, dirpath *path, ppathcnf *res) res->pc_mask[0] = 0; res->pc_mask[1] = 0; - export_reset (exp); - return 1; } +/* + * We should advertise the preferred flavours first. (See RFC 2623 + * section 2.7.) We leave that to the administrator, by advertising + * flavours in the order they were listed in /etc/exports. AUTH_NULL is + * dropped from the list to avoid backward compatibility issue with + * older Linux clients, who inspect the list in reversed order. + * + * XXX: It might be more helpful to rearrange these so that flavors + * giving more access (as determined from readonly and id-squashing + * options) come first. (If we decide to do that we should probably do + * that when reading the exports rather than here.) + */ +static void set_authflavors(struct mountres3_ok *ok, nfs_export *exp) +{ + struct sec_entry *s; + static int flavors[SECFLAVOR_COUNT]; + int i = 0; + + for (s = exp->m_export.e_secinfo; s->flav; s++) { + if (s->flav->fnum == AUTH_NULL) + continue; + flavors[i] = s->flav->fnum; + i++; + } + if (i == 0) { + /* default when there is no sec= option: */ + i = 1; + flavors[0] = AUTH_UNIX; + } + ok->auth_flavors.auth_flavors_val = flavors; + ok->auth_flavors.auth_flavors_len = i; +} + /* * NFSv3 MOUNT procedure */ bool_t mount_mnt_3_svc(struct svc_req *rqstp, dirpath *path, mountres3 *res) { -#define AUTH_GSS_KRB5 390003 -#define AUTH_GSS_KRB5I 390004 -#define AUTH_GSS_KRB5P 390005 - static int flavors[] = { AUTH_NULL, AUTH_UNIX, AUTH_GSS_KRB5, AUTH_GSS_KRB5I, AUTH_GSS_KRB5P}; + struct mountres3_ok *ok = &res->mountres3_u.mountinfo; + nfs_export *exp; struct nfs_fh_len *fh; xlog(D_CALL, "MNT3(%s) called", *path); - if ((fh = get_rootfh(rqstp, path, &res->fhs_status, 1)) != NULL) { - struct mountres3_ok *ok = &res->mountres3_u.mountinfo; - - ok->fhandle.fhandle3_len = fh->fh_size; - ok->fhandle.fhandle3_val = (char *)fh->fh_handle; - ok->auth_flavors.auth_flavors_len - = sizeof(flavors)/sizeof(flavors[0]); - ok->auth_flavors.auth_flavors_val = flavors; - } + fh = get_rootfh(rqstp, path, &exp, &res->fhs_status, 1); + if (!fh) + return 1; + + ok->fhandle.fhandle3_len = fh->fh_size; + ok->fhandle.fhandle3_val = (char *)fh->fh_handle; + set_authflavors(ok, exp); return 1; } static struct nfs_fh_len * -get_rootfh(struct svc_req *rqstp, dirpath *path, mountstat3 *error, int v3) +get_rootfh(struct svc_req *rqstp, dirpath *path, nfs_export **expret, + mountstat3 *error, int v3) { - struct sockaddr_in *sin = - (struct sockaddr_in *) svc_getcaller(rqstp->rq_xprt); + struct sockaddr_in *sin = nfs_getrpccaller_in(rqstp->rq_xprt); struct stat stb, estb; nfs_export *exp; + struct nfs_fh_len *fh; char rpath[MAXPATHLEN+1]; char *p = *path; @@ -378,56 +413,71 @@ get_rootfh(struct svc_req *rqstp, dirpath *path, mountstat3 *error, int v3) } /* Now authenticate the intruder... */ - if (!(exp = auth_authenticate("mount", sin, p))) { + exp = auth_authenticate("mount", sin, p); + if (!exp) { *error = NFSERR_ACCES; - } else if (stat(p, &stb) < 0) { + return NULL; + } + if (stat(p, &stb) < 0) { xlog(L_WARNING, "can't stat exported dir %s: %s", p, strerror(errno)); if (errno == ENOENT) *error = NFSERR_NOENT; else *error = NFSERR_ACCES; - } else if (!S_ISDIR(stb.st_mode) && !S_ISREG(stb.st_mode)) { + return NULL; + } + if (!S_ISDIR(stb.st_mode) && !S_ISREG(stb.st_mode)) { xlog(L_WARNING, "%s is not a directory or regular file", p); *error = NFSERR_NOTDIR; - } else if (stat(exp->m_export.e_path, &estb) < 0) { + return NULL; + } + if (stat(exp->m_export.e_path, &estb) < 0) { xlog(L_WARNING, "can't stat export point %s: %s", p, strerror(errno)); *error = NFSERR_NOENT; - } else if (estb.st_dev != stb.st_dev - /* && (!new_cache || !(exp->m_export.e_flags & NFSEXP_CROSSMOUNT)) */ - ) { + return NULL; + } + if (estb.st_dev != stb.st_dev + && (!new_cache + || !(exp->m_export.e_flags & NFSEXP_CROSSMOUNT))) { xlog(L_WARNING, "request to export directory %s below nearest filesystem %s", p, exp->m_export.e_path); *error = NFSERR_ACCES; - } else if (exp->m_export.e_mountpoint && + return NULL; + } + if (exp->m_export.e_mountpoint && !is_mountpoint(exp->m_export.e_mountpoint[0]? exp->m_export.e_mountpoint: exp->m_export.e_path)) { xlog(L_WARNING, "request to export an unmounted filesystem: %s", p); *error = NFSERR_NOENT; - } else if (new_cache) { + return NULL; + } + + if (new_cache) { /* This will be a static private nfs_export with just one * address. We feed it to kernel then extract the filehandle, * */ - struct nfs_fh_len *fh; - cache_export(exp); + if (cache_export(exp, p)) { + *error = NFSERR_ACCES; + return NULL; + } fh = cache_get_filehandle(exp, v3?64:32, p); - if (fh == NULL) + if (fh == NULL) { *error = NFSERR_ACCES; - else { - *error = NFS_OK; - mountlist_add(inet_ntoa(sin->sin_addr), p); + return NULL; } - return fh; } else { - struct nfs_fh_len *fh; - - if (exp->m_exported<1) + int did_export = 0; + retry: + if (exp->m_exported<1) { export_export(exp); + did_export = 1; + } if (!exp->m_xtabent) xtab_append(exp); @@ -441,102 +491,138 @@ get_rootfh(struct svc_req *rqstp, dirpath *path, mountstat3 *error, int v3) fh = getfh_old ((struct sockaddr *) sin, stb.st_dev, stb.st_ino); } - if (fh != NULL) { - mountlist_add(inet_ntoa(sin->sin_addr), p); - *error = NFS_OK; - export_reset (exp); - return fh; + if (fh == NULL && !did_export) { + exp->m_exported = 0; + goto retry; + } + + if (fh == NULL) { + xlog(L_WARNING, "getfh failed: %s", strerror(errno)); + *error = NFSERR_ACCES; + return NULL; } - xlog(L_WARNING, "getfh failed: %s", strerror(errno)); - *error = NFSERR_ACCES; } - export_reset (exp); - return NULL; + *error = NFS_OK; + mountlist_add(inet_ntoa(sin->sin_addr), p); + if (expret) + *expret = exp; + return fh; } -static exports -get_exportlist(void) +static void remove_all_clients(exportnode *e) { - static exports elist = NULL; - struct exportnode *e, *ne; - struct groupnode *g, *ng, *c, **cp; - nfs_export *exp; - int i; + struct groupnode *g, *ng; - if (!auth_reload() && elist) - return elist; + for (g = e->ex_groups; g; g = ng) { + ng = g->gr_next; + xfree(g->gr_name); + xfree(g); + } + e->ex_groups = NULL; +} + +static void free_exportlist(exports *elist) +{ + struct exportnode *e, *ne; - for (e = elist; e != NULL; e = ne) { + for (e = *elist; e != NULL; e = ne) { ne = e->ex_next; - for (g = e->ex_groups; g != NULL; g = ng) { - ng = g->gr_next; - xfree(g->gr_name); - xfree(g); - } + remove_all_clients(e); xfree(e->ex_dir); xfree(e); } - elist = NULL; - - for (i = 0; i < MCL_MAXTYPES; i++) { - for (exp = exportlist[i]; exp; exp = exp->m_next) { - for (e = elist; e != NULL; e = e->ex_next) { - if (!strcmp(exp->m_export.m_path, e->ex_dir)) - break; - } - if (!e) { - e = (struct exportnode *) xmalloc(sizeof(*e)); - e->ex_next = elist; - e->ex_groups = NULL; - e->ex_dir = xstrdup(exp->m_export.m_path); - elist = e; - } - - /* We need to check if we should remove - previous ones. */ - if (i == MCL_ANONYMOUS && e->ex_groups) { - for (g = e->ex_groups; g; g = ng) { - ng = g->gr_next; - xfree(g->gr_name); - xfree(g); - } - e->ex_groups = NULL; - continue; - } - - if (i != MCL_FQDN && e->ex_groups) { - struct hostent *hp; + *elist = NULL; +} - cp = &e->ex_groups; - while ((c = *cp) != NULL) { - if (client_gettype (c->gr_name) == MCL_FQDN - && (hp = gethostbyname(c->gr_name))) { - hp = hostent_dup (hp); - if (client_check(exp->m_client, hp)) { +static void prune_clients(nfs_export *exp, struct exportnode *e) +{ + struct hostent *hp; + struct groupnode *c, **cp; + + cp = &e->ex_groups; + while ((c = *cp) != NULL) { + if (client_gettype(c->gr_name) == MCL_FQDN + && (hp = gethostbyname(c->gr_name))) { + hp = hostent_dup(hp); + if (client_check(exp->m_client, hp)) { *cp = c->gr_next; xfree(c->gr_name); xfree(c); xfree (hp); continue; - } - xfree (hp); - } - cp = &(c->gr_next); - } } + xfree (hp); + } + cp = &(c->gr_next); + } +} + +static exportnode *lookup_or_create_elist_entry(exports *elist, nfs_export *exp) +{ + exportnode *e; + + for (e = *elist; e != NULL; e = e->ex_next) { + if (!strcmp(exp->m_export.e_path, e->ex_dir)) + return e; + } + e = xmalloc(sizeof(*e)); + e->ex_next = *elist; + e->ex_groups = NULL; + e->ex_dir = xstrdup(exp->m_export.e_path); + *elist = e; + return e; +} + +static void insert_group(struct exportnode *e, char *newname) +{ + struct groupnode *g; + + for (g = e->ex_groups; g; g = g->gr_next) + if (strcmp(g->gr_name, newname)) + return; + + g = xmalloc(sizeof(*g)); + g->gr_name = xstrdup(newname); + g->gr_next = e->ex_groups; + e->ex_groups = g; +} + +static exports +get_exportlist(void) +{ + static exports elist = NULL; + struct exportnode *e; + nfs_export *exp; + int i; + static unsigned int ecounter; + unsigned int acounter; + + acounter = auth_reload(); + if (elist && acounter == ecounter) + return elist; - if (exp->m_export.e_hostname [0] != '\0') { - for (g = e->ex_groups; g; g = g->gr_next) - if (strcmp (exp->m_export.e_hostname, - g->gr_name) == 0) - break; - if (g) - continue; - g = (struct groupnode *) xmalloc(sizeof(*g)); - g->gr_name = xstrdup(exp->m_export.e_hostname); - g->gr_next = e->ex_groups; - e->ex_groups = g; + ecounter = acounter; + + free_exportlist(&elist); + + for (i = 0; i < MCL_MAXTYPES; i++) { + for (exp = exportlist[i].p_head; exp; exp = exp->m_next) { + /* Don't show pseudo exports */ + if (exp->m_export.e_flags & NFSEXP_V4ROOT) + continue; + e = lookup_or_create_elist_entry(&elist, exp); + + /* exports to "*" absorb any others */ + if (i == MCL_ANONYMOUS && e->ex_groups) { + remove_all_clients(e); + continue; } + /* non-FQDN's absorb FQDN's they contain: */ + if (i != MCL_FQDN && e->ex_groups) + prune_clients(exp, e); + + if (exp->m_export.e_hostname[0] != '\0') + insert_group(e, exp->m_export.e_hostname); } } @@ -557,8 +643,11 @@ main(int argc, char **argv) /* Parse the command line options and arguments. */ opterr = 0; - while ((c = getopt_long(argc, argv, "o:nFd:f:p:P:hH:N:V:vs:t:", longopts, NULL)) != EOF) + while ((c = getopt_long(argc, argv, "o:nFd:f:p:P:hH:N:V:vrs:t:g", longopts, NULL)) != EOF) switch (c) { + case 'g': + manage_gids = 1; + break; case 'o': descriptors = atoi(optarg); if (descriptors <= 0) { @@ -597,6 +686,9 @@ main(int argc, char **argv) case 'n': _rpcfdtype = SOCK_DGRAM; break; + case 'r': + reverse_resolve = 1; + break; case 's': if ((state_dir = xstrdup(optarg)) == NULL) { fprintf(stderr, "%s: xstrdup(%s) failed!\n", @@ -620,8 +712,10 @@ main(int argc, char **argv) usage(argv [0], 1); } - /* No more arguments allowed. */ - if (optind != argc || !(nfs_version & 0x7)) + /* No more arguments allowed. + * Require at least one valid version (2, 3, or 4) + */ + if (optind != argc || !(nfs_version & 0xE)) usage(argv [0], 1); if (chdir(state_dir)) { @@ -669,12 +763,12 @@ main(int argc, char **argv) if (new_cache) cache_open(); - if (nfs_version & 0x1) + if (nfs_version & (0x1 << 1)) { rpc_init("mountd", MOUNTPROG, MOUNTVERS, mount_dispatch, port); - if (nfs_version & (0x1 << 1)) rpc_init("mountd", MOUNTPROG, MOUNTVERS_POSIX, mount_dispatch, port); + } if (nfs_version & (0x1 << 2)) rpc_init("mountd", MOUNTPROG, MOUNTVERS_NFSV3, mount_dispatch, port); @@ -733,6 +827,6 @@ usage(const char *prog, int n) " [-p|--port port] [-V version|--nfs-version version]\n" " [-N version|--no-nfs-version version] [-n|--no-tcp]\n" " [-H ha-callout-prog] [-s|--state-directory-path path]\n" -" [-t num|--num-threads=num]\n", prog); +" [-g|--manage-gids] [-t num|--num-threads=num]\n", prog); exit(n); }