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