/* * UNFS3 mount protocol procedures * (C) 2004, Pascal Schmidt * see file LICENSE for license details */ #include "config.h" #include #include #include #include #include #include #ifndef WIN32 #include #include #include #include #endif /* WIN32 */ #include #include "nfs.h" #include "mount.h" #include "daemon.h" #include "fh.h" #include "fh_cache.h" #include "fd_cache.h" #include "Config/exports.h" #include "password.h" #include "backend.h" #ifndef PATH_MAX # define PATH_MAX 4096 #endif #define IS_SECURE(port) ((port) < 1024) /* * number of active mounts * * only a guess since clients can crash and/or not sent UMNT calls */ static int mount_cnt = 0; /* list of currently mounted directories */ static mountlist mount_list = NULL; static char nonce[32] = ""; /* * add entry to mount list */ static void add_mount(const char *path, struct svc_req *rqstp) { mountlist new; mountlist iter; char *host; new = malloc(sizeof(struct mountbody)); if (!new) { logmsg(LOG_CRIT, "add_mount: Unable to allocate memory"); return; } host = inet_ntoa(get_remote(rqstp)); new->ml_hostname = malloc(strlen(host) + 1); if (!new->ml_hostname) { logmsg(LOG_CRIT, "add_mount: Unable to allocate memory"); free(new); return; } new->ml_directory = malloc(strlen(path) + 1); if (!new->ml_directory) { logmsg(LOG_CRIT, "add_mount: Unable to allocate memory"); free(new->ml_hostname); free(new); return; } /* initialize the new entry */ new->ml_next = NULL; strcpy(new->ml_hostname, host); strcpy(new->ml_directory, path); iter = mount_list; if (iter) { while (iter->ml_next) iter = iter->ml_next; iter->ml_next = new; } else mount_list = new; mount_cnt++; } /* * remove entries from mount list */ static void remove_mount(const char *path, struct svc_req *rqstp) { mountlist iter, next, prev = NULL; char *host; host = inet_ntoa(get_remote(rqstp)); iter = mount_list; while (iter) { if (strcmp(iter->ml_hostname, host) == 0 && (!path || strcmp(iter->ml_directory, path) == 0)) { if (prev) prev->ml_next = iter->ml_next; else mount_list = iter->ml_next; next = iter->ml_next; free(iter->ml_hostname); free(iter->ml_directory); free(iter); iter = next; /* adjust mount count */ if (mount_cnt > 0) mount_cnt--; } else { prev = iter; iter = iter->ml_next; } } } void *mountproc_null_3_svc(U(void *argp), U(struct svc_req *rqstp)) { static void *result = NULL; return &result; } mountres3 *mountproc_mnt_3_svc(dirpath * argp, struct svc_req * rqstp) { char buf[PATH_MAX]; unfs3_fh_t fh; nfs_fh3 nfh; static char fhbuf[FH_MAXBUF]; static mountres3 result; static int auth = AUTH_UNIX; int authenticated = 0; char *password; /* We need to modify the *argp pointer. Make a copy. */ char *dpath = *argp; /* error out if not version 3 */ if (rqstp->rq_vers != 3) { logmsg(LOG_INFO, "%s attempted mount with unsupported protocol version", inet_ntoa(get_remote(rqstp))); result.fhs_status = MNT3ERR_INVAL; return &result; } /* Check for "mount commands" */ if (strncmp(dpath, "@getnonce", sizeof("@getnonce") - 1) == 0) { if (backend_gen_nonce(nonce) < 0) { result.fhs_status = MNT3ERR_IO; } else { result.fhs_status = MNT3_OK; result.mountres3_u.mountinfo.fhandle.fhandle3_len = 32; result.mountres3_u.mountinfo.fhandle.fhandle3_val = nonce; result.mountres3_u.mountinfo.auth_flavors.auth_flavors_len = 1; result.mountres3_u.mountinfo.auth_flavors.auth_flavors_val = &auth; } return &result; } else if (strncmp(dpath, "@password:", sizeof("@password:") - 1) == 0) { char pw[PASSWORD_MAXLEN + 1]; mnt_cmd_argument(&dpath, "@password:", pw, PASSWORD_MAXLEN); if (exports_options(dpath, rqstp, &password, NULL) != -1) { authenticated = !strcmp(password, pw); } /* else leave authenticated unchanged */ } else if (strncmp(dpath, "@otp:", sizeof("@otp:") - 1) == 0) { /* The otp from the client */ char otp[PASSWORD_MAXLEN + 1]; /* Our calculated otp */ char hexdigest[32]; mnt_cmd_argument(&dpath, "@otp:", otp, PASSWORD_MAXLEN); if (exports_options(dpath, rqstp, &password, NULL) != -1) { otp_digest(nonce, password, hexdigest); /* Compare our calculated digest with what the client submitted */ authenticated = !strncmp(hexdigest, otp, 32); /* Change nonce */ backend_gen_nonce(nonce); } /* else leave authenticated unchanged */ } if ((exports_opts & OPT_REMOVABLE) && export_point(dpath)) { /* Removable media export point. Do not call realpath; simply copy path */ strncpy(buf, dpath, PATH_MAX); } else if (!backend_realpath(dpath, buf)) { /* the given path does not exist */ result.fhs_status = MNT3ERR_NOENT; return &result; } if (strlen(buf) + 1 > NFS_MAXPATHLEN) { logmsg(LOG_INFO, "%s attempted to mount jumbo path", inet_ntoa(get_remote(rqstp))); result.fhs_status = MNT3ERR_NAMETOOLONG; return &result; } if ((exports_options(buf, rqstp, &password, NULL) == -1) || (!authenticated && password[0]) || (!(exports_opts & OPT_INSECURE) && !IS_SECURE(ntohs(get_port(rqstp)))) ) { /* not exported to this host or at all, or a password defined and not authenticated */ result.fhs_status = MNT3ERR_ACCES; return &result; } fh = fh_comp(buf, rqstp, FH_DIR); if (!fh_valid(fh)) { logmsg(LOG_INFO, "%s attempted to mount non-directory", inet_ntoa(get_remote(rqstp))); result.fhs_status = MNT3ERR_NOTDIR; return &result; } add_mount(dpath, rqstp); nfh = fh_encode(&fh, fhbuf); result.fhs_status = MNT3_OK; result.mountres3_u.mountinfo.fhandle.fhandle3_len = nfh.data.data_len; result.mountres3_u.mountinfo.fhandle.fhandle3_val = nfh.data.data_val; result.mountres3_u.mountinfo.auth_flavors.auth_flavors_len = 1; result.mountres3_u.mountinfo.auth_flavors.auth_flavors_val = &auth; return &result; } mountlist *mountproc_dump_3_svc(U(void *argp), U(struct svc_req *rqstp)) { return &mount_list; } void *mountproc_umnt_3_svc(dirpath * argp, struct svc_req *rqstp) { /* RPC times out if we use a NULL pointer */ static void *result = NULL; remove_mount(*argp, rqstp); /* if no more mounts are active, flush all open file descriptors */ if (mount_cnt == 0) fd_cache_purge(); return &result; } void *mountproc_umntall_3_svc(U(void *argp), struct svc_req *rqstp) { /* RPC times out if we use a NULL pointer */ static void *result = NULL; remove_mount(NULL, rqstp); /* if no more mounts are active, flush all open file descriptors */ if (mount_cnt == 0) fd_cache_purge(); return &result; } exports *mountproc_export_3_svc(U(void *argp), U(struct svc_req *rqstp)) { return &exports_nfslist; }