Version increased to unfs3_0.9.22+dfsg-1maemo3
[unfs3] / unfs3 / mount.c
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  */
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  */
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
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
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
253 mountlist *mountproc_dump_3_svc(U(void *argp), U(struct svc_req *rqstp))
254 {
255     return &mount_list;
256 }
257
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
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
286 exports *mountproc_export_3_svc(U(void *argp), U(struct svc_req *rqstp))
287 {
288     return &exports_nfslist;
289 }