/*
* UNFS3 mount protocol procedures
* (C) 2004, Pascal Schmidt
* see file LICENSE for license details
*/
#include "config.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <rpc/rpc.h>
#include <limits.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h>
#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)
return;
host = inet_ntoa(get_remote(rqstp));
new->ml_hostname = malloc(strlen(host) + 1);
if (!new->ml_hostname) {
free(new);
return;
}
new->ml_directory = malloc(strlen(path) + 1);
if (!new->ml_directory) {
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];
static unfs3_fh_t fh;
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) {
gen_nonce(nonce);
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 */
unsigned 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 */
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);
result.fhs_status = MNT3_OK;
result.mountres3_u.mountinfo.fhandle.fhandle3_len = fh_length(&fh);
result.mountres3_u.mountinfo.fhandle.fhandle3_val = (char *) &fh;
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;
}
syntax highlighted by Code2HTML, v. 0.9.1