1 
2 /*
3  * UNFS3 mount protocol procedures
4  * (C) 2004, Pascal Schmidt
5  * see file LICENSE for license details
6  */
7 
8 #include "config.h"
9 
10 #include <sys/types.h>
11 #include <sys/stat.h>
12 #include <rpc/rpc.h>
13 #include <limits.h>
14 #include <stdlib.h>
15 #include <string.h>
16 #ifndef WIN32
17 #include <syslog.h>
18 #include <unistd.h>
19 #include <netinet/in.h>
20 #include <arpa/inet.h>
21 #endif				       /* WIN32 */
22 #include <fcntl.h>
23 
24 #include "nfs.h"
25 #include "mount.h"
26 #include "daemon.h"
27 #include "fh.h"
28 #include "fh_cache.h"
29 #include "fd_cache.h"
30 #include "Config/exports.h"
31 #include "password.h"
32 #include "backend.h"
33 
34 #ifndef PATH_MAX
35 # define PATH_MAX	4096
36 #endif
37 
38 #define IS_SECURE(port) ((port) < 1024)
39 
40 /*
41  * number of active mounts
42  *
43  * only a guess since clients can crash and/or not sent UMNT calls
44  */
45 static int mount_cnt = 0;
46 
47 /* list of currently mounted directories */
48 static mountlist mount_list = NULL;
49 
50 static char nonce[32] = "";
51 
52 /*
53  * add entry to mount list
54  */
add_mount(const char * path,struct svc_req * rqstp)55 static void add_mount(const char *path, struct svc_req *rqstp)
56 {
57     mountlist new;
58     mountlist iter;
59     char *host;
60 
61     new = malloc(sizeof(struct mountbody));
62     if (!new) {
63 	logmsg(LOG_CRIT, "add_mount: Unable to allocate memory");
64 	return;
65     }
66 
67     host = inet_ntoa(get_remote(rqstp));
68     new->ml_hostname = malloc(strlen(host) + 1);
69     if (!new->ml_hostname) {
70 	logmsg(LOG_CRIT, "add_mount: Unable to allocate memory");
71 	free(new);
72 	return;
73     }
74 
75     new->ml_directory = malloc(strlen(path) + 1);
76     if (!new->ml_directory) {
77 	logmsg(LOG_CRIT, "add_mount: Unable to allocate memory");
78 	free(new->ml_hostname);
79 	free(new);
80 	return;
81     }
82 
83     /* initialize the new entry */
84     new->ml_next = NULL;
85     strcpy(new->ml_hostname, host);
86     strcpy(new->ml_directory, path);
87 
88     iter = mount_list;
89     if (iter) {
90 	while (iter->ml_next)
91 	    iter = iter->ml_next;
92 	iter->ml_next = new;
93     } else
94 	mount_list = new;
95 
96     mount_cnt++;
97 }
98 
99 /*
100  * remove entries from mount list
101  */
remove_mount(const char * path,struct svc_req * rqstp)102 static void remove_mount(const char *path, struct svc_req *rqstp)
103 {
104     mountlist iter, next, prev = NULL;
105     char *host;
106 
107     host = inet_ntoa(get_remote(rqstp));
108 
109     iter = mount_list;
110     while (iter) {
111 	if (strcmp(iter->ml_hostname, host) == 0 &&
112 	    (!path || strcmp(iter->ml_directory, path) == 0)) {
113 	    if (prev)
114 		prev->ml_next = iter->ml_next;
115 	    else
116 		mount_list = iter->ml_next;
117 
118 	    next = iter->ml_next;
119 
120 	    free(iter->ml_hostname);
121 	    free(iter->ml_directory);
122 	    free(iter);
123 
124 	    iter = next;
125 
126 	    /* adjust mount count */
127 	    if (mount_cnt > 0)
128 		mount_cnt--;
129 	} else {
130 	    prev = iter;
131 	    iter = iter->ml_next;
132 	}
133     }
134 }
135 
mountproc_null_3_svc(U (void * argp),U (struct svc_req * rqstp))136 void *mountproc_null_3_svc(U(void *argp), U(struct svc_req *rqstp))
137 {
138     static void *result = NULL;
139 
140     return &result;
141 }
142 
mountproc_mnt_3_svc(dirpath * argp,struct svc_req * rqstp)143 mountres3 *mountproc_mnt_3_svc(dirpath * argp, struct svc_req * rqstp)
144 {
145     char buf[PATH_MAX];
146     static unfs3_fh_t fh;
147     static mountres3 result;
148     static int auth = AUTH_UNIX;
149     int authenticated = 0;
150     char *password;
151 
152     /* We need to modify the *argp pointer. Make a copy. */
153     char *dpath = *argp;
154 
155     /* error out if not version 3 */
156     if (rqstp->rq_vers != 3) {
157 	logmsg(LOG_INFO,
158 	       "%s attempted mount with unsupported protocol version",
159 	       inet_ntoa(get_remote(rqstp)));
160 	result.fhs_status = MNT3ERR_INVAL;
161 	return &result;
162     }
163 
164     /* Check for "mount commands" */
165     if (strncmp(dpath, "@getnonce", sizeof("@getnonce") - 1) == 0) {
166 	if (backend_gen_nonce(nonce) < 0) {
167 	    result.fhs_status = MNT3ERR_IO;
168 	} else {
169 	    result.fhs_status = MNT3_OK;
170 	    result.mountres3_u.mountinfo.fhandle.fhandle3_len = 32;
171 	    result.mountres3_u.mountinfo.fhandle.fhandle3_val = nonce;
172 	    result.mountres3_u.mountinfo.auth_flavors.auth_flavors_len = 1;
173 	    result.mountres3_u.mountinfo.auth_flavors.auth_flavors_val =
174 		&auth;
175 	}
176 	return &result;
177     } else if (strncmp(dpath, "@password:", sizeof("@password:") - 1) == 0) {
178 	char pw[PASSWORD_MAXLEN + 1];
179 
180 	mnt_cmd_argument(&dpath, "@password:", pw, PASSWORD_MAXLEN);
181 	if (exports_options(dpath, rqstp, &password, NULL) != -1) {
182 	    authenticated = !strcmp(password, pw);
183 	}
184 	/* else leave authenticated unchanged */
185     } else if (strncmp(dpath, "@otp:", sizeof("@otp:") - 1) == 0) {
186 	/* The otp from the client */
187 	char otp[PASSWORD_MAXLEN + 1];
188 
189 	/* Our calculated otp */
190 	char hexdigest[32];
191 
192 	mnt_cmd_argument(&dpath, "@otp:", otp, PASSWORD_MAXLEN);
193 	if (exports_options(dpath, rqstp, &password, NULL) != -1) {
194 	    otp_digest(nonce, password, hexdigest);
195 
196 	    /* Compare our calculated digest with what the client submitted */
197 	    authenticated = !strncmp(hexdigest, otp, 32);
198 
199 	    /* Change nonce */
200 	    backend_gen_nonce(nonce);
201 	}
202 	/* else leave authenticated unchanged */
203     }
204 
205     if ((exports_opts & OPT_REMOVABLE) && export_point(dpath)) {
206 	/* Removable media export point. Do not call realpath; simply copy
207 	   path */
208 	strncpy(buf, dpath, PATH_MAX);
209     } else if (!backend_realpath(dpath, buf)) {
210 	/* the given path does not exist */
211 	result.fhs_status = MNT3ERR_NOENT;
212 	return &result;
213     }
214 
215     if (strlen(buf) + 1 > NFS_MAXPATHLEN) {
216 	logmsg(LOG_INFO, "%s attempted to mount jumbo path",
217 	       inet_ntoa(get_remote(rqstp)));
218 	result.fhs_status = MNT3ERR_NAMETOOLONG;
219 	return &result;
220     }
221 
222     if ((exports_options(buf, rqstp, &password, NULL) == -1)
223 	|| (!authenticated && password[0])
224 	|| (!(exports_opts & OPT_INSECURE) &&
225 	    !IS_SECURE(ntohs(get_port(rqstp))))
226 	) {
227 	/* not exported to this host or at all, or a password defined and not
228 	   authenticated */
229 	result.fhs_status = MNT3ERR_ACCES;
230 	return &result;
231     }
232 
233     fh = fh_comp(buf, rqstp, FH_DIR);
234 
235     if (!fh_valid(fh)) {
236 	logmsg(LOG_INFO, "%s attempted to mount non-directory",
237 	       inet_ntoa(get_remote(rqstp)));
238 	result.fhs_status = MNT3ERR_NOTDIR;
239 	return &result;
240     }
241 
242     add_mount(dpath, rqstp);
243 
244     result.fhs_status = MNT3_OK;
245     result.mountres3_u.mountinfo.fhandle.fhandle3_len = fh_length(&fh);
246     result.mountres3_u.mountinfo.fhandle.fhandle3_val = (char *) &fh;
247     result.mountres3_u.mountinfo.auth_flavors.auth_flavors_len = 1;
248     result.mountres3_u.mountinfo.auth_flavors.auth_flavors_val = &auth;
249 
250     return &result;
251 }
252 
mountproc_dump_3_svc(U (void * argp),U (struct svc_req * rqstp))253 mountlist *mountproc_dump_3_svc(U(void *argp), U(struct svc_req *rqstp))
254 {
255     return &mount_list;
256 }
257 
mountproc_umnt_3_svc(dirpath * argp,struct svc_req * rqstp)258 void *mountproc_umnt_3_svc(dirpath * argp, struct svc_req *rqstp)
259 {
260     /* RPC times out if we use a NULL pointer */
261     static void *result = NULL;
262 
263     remove_mount(*argp, rqstp);
264 
265     /* if no more mounts are active, flush all open file descriptors */
266     if (mount_cnt == 0)
267 	fd_cache_purge();
268 
269     return &result;
270 }
271 
mountproc_umntall_3_svc(U (void * argp),struct svc_req * rqstp)272 void *mountproc_umntall_3_svc(U(void *argp), struct svc_req *rqstp)
273 {
274     /* RPC times out if we use a NULL pointer */
275     static void *result = NULL;
276 
277     remove_mount(NULL, rqstp);
278 
279     /* if no more mounts are active, flush all open file descriptors */
280     if (mount_cnt == 0)
281 	fd_cache_purge();
282 
283     return &result;
284 }
285 
mountproc_export_3_svc(U (void * argp),U (struct svc_req * rqstp))286 exports *mountproc_export_3_svc(U(void *argp), U(struct svc_req *rqstp))
287 {
288     return &exports_nfslist;
289 }
290