r23801: The FSF has moved around a lot. This fixes their Mass Ave address.
[kai/samba.git] / source3 / client / smbmnt.c
1 /*
2  *  smbmnt.c
3  *
4  *  Copyright (C) 1995-1998 by Paal-Kr. Engstad and Volker Lendecke
5  *  extensively modified by Tridge
6  *
7  *  This program is free software; you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License as published by
9  *  the Free Software Foundation; either version 3 of the License, or
10  *  (at your option) any later version.
11  *  
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *  
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, see <http://www.gnu.org/licenses/>.
19  *
20  */
21
22 #define SMBMOUNT_MALLOC 1
23
24 #include "includes.h"
25
26 #include <mntent.h>
27 #include <sys/utsname.h>
28
29 #include <asm/types.h>
30 #include <asm/posix_types.h>
31 #include <linux/smb.h>
32 #include <linux/smb_mount.h>
33 #include <asm/unistd.h>
34
35 #ifndef MS_MGC_VAL
36 /* This may look strange but MS_MGC_VAL is what we are looking for and
37         is what we need from <linux/fs.h> under libc systems and is
38         provided in standard includes on glibc systems.  So...  We
39         switch on what we need...  */
40 #include <linux/fs.h>
41 #endif
42
43 static uid_t mount_uid;
44 static gid_t mount_gid;
45 static int mount_ro;
46 static unsigned mount_fmask;
47 static unsigned mount_dmask;
48 static int user_mount;
49 static char *options;
50
51 static void
52 help(void)
53 {
54         printf("\n");
55         printf("Usage: smbmnt mount-point [options]\n");
56         printf("Version %s\n\n",SAMBA_VERSION_STRING);
57         printf("-s share       share name on server\n"
58                "-r             mount read-only\n"
59                "-u uid         mount as uid\n"
60                "-g gid         mount as gid\n"
61                "-f mask        permission mask for files\n"
62                "-d mask        permission mask for directories\n"
63                "-o options     name=value, list of options\n"
64                "-h             print this help text\n");
65 }
66
67 static int
68 parse_args(int argc, char *argv[], struct smb_mount_data *data, char **share)
69 {
70         int opt;
71
72         while ((opt = getopt (argc, argv, "s:u:g:rf:d:o:")) != EOF)
73         {
74                 switch (opt)
75                 {
76                 case 's':
77                         *share = optarg;
78                         break;
79                 case 'u':
80                         if (!user_mount) {
81                                 mount_uid = strtol(optarg, NULL, 0);
82                         }
83                         break;
84                 case 'g':
85                         if (!user_mount) {
86                                 mount_gid = strtol(optarg, NULL, 0);
87                         }
88                         break;
89                 case 'r':
90                         mount_ro = 1;
91                         break;
92                 case 'f':
93                         mount_fmask = strtol(optarg, NULL, 8);
94                         break;
95                 case 'd':
96                         mount_dmask = strtol(optarg, NULL, 8);
97                         break;
98                 case 'o':
99                         options = optarg;
100                         break;
101                 default:
102                         return -1;
103                 }
104         }
105         return 0;
106         
107 }
108
109 static char *
110 fullpath(const char *p)
111 {
112         char path[PATH_MAX+1];
113
114         if (strlen(p) > PATH_MAX) {
115                 return NULL;
116         }
117
118         if (realpath(p, path) == NULL) {
119                 fprintf(stderr,"Failed to find real path for mount point %s: %s\n",
120                         p, strerror(errno));
121                 exit(1);
122         }
123         return strdup(path);
124 }
125
126 /* Check whether user is allowed to mount on the specified mount point. If it's
127    OK then we change into that directory - this prevents race conditions */
128 static int mount_ok(char *mount_point)
129 {
130         struct stat st;
131
132         if (chdir(mount_point) != 0) {
133                 return -1;
134         }
135
136         if (stat(".", &st) != 0) {
137                 return -1;
138         }
139
140         if (!S_ISDIR(st.st_mode)) {
141                 errno = ENOTDIR;
142                 return -1;
143         }
144
145         if ((getuid() != 0) && 
146             ((getuid() != st.st_uid) || 
147              ((st.st_mode & S_IRWXU) != S_IRWXU))) {
148                 errno = EPERM;
149                 return -1;
150         }
151
152         return 0;
153 }
154
155 /* Tries to mount using the appropriate format. For 2.2 the struct,
156    for 2.4 the ascii version. */
157 static int
158 do_mount(char *share_name, unsigned int flags, struct smb_mount_data *data)
159 {
160         pstring opts;
161         struct utsname uts;
162         char *release, *major, *minor;
163         char *data1, *data2;
164
165         uname(&uts);
166         release = uts.release;
167         major = strtok(release, ".");
168         minor = strtok(NULL, ".");
169         if (major && minor && atoi(major) == 2 && atoi(minor) < 4) {
170                 /* < 2.4, assume struct */
171                 data1 = (char *) data;
172                 data2 = opts;
173         } else {
174                 /* >= 2.4, assume ascii but fall back on struct */
175                 data1 = opts;
176                 data2 = (char *) data;
177         }
178
179         slprintf(opts, sizeof(opts)-1,
180                  "version=7,uid=%d,gid=%d,file_mode=0%o,dir_mode=0%o,%s",
181                  mount_uid, mount_gid, data->file_mode, data->dir_mode,options);
182         if (mount(share_name, ".", "smbfs", flags, data1) == 0)
183                 return 0;
184         return mount(share_name, ".", "smbfs", flags, data2);
185 }
186
187  int main(int argc, char *argv[])
188 {
189         char *mount_point, *share_name = NULL;
190         FILE *mtab;
191         int fd;
192         unsigned int flags;
193         struct smb_mount_data data;
194         struct mntent ment;
195
196         memset(&data, 0, sizeof(struct smb_mount_data));
197
198         if (argc < 2) {
199                 help();
200                 exit(1);
201         }
202
203         if (argv[1][0] == '-') {
204                 help();
205                 exit(1);
206         }
207
208         if (getuid() != 0) {
209                 user_mount = 1;
210         }
211
212         if (geteuid() != 0) {
213                 fprintf(stderr, "smbmnt must be installed suid root for direct user mounts (%d,%d)\n", getuid(), geteuid());
214                 exit(1);
215         }
216
217         mount_uid = getuid();
218         mount_gid = getgid();
219         mount_fmask = umask(0);
220         umask(mount_fmask);
221         mount_fmask = ~mount_fmask;
222
223         mount_point = fullpath(argv[1]);
224
225         argv += 1;
226         argc -= 1;
227
228         if (mount_ok(mount_point) != 0) {
229                 fprintf(stderr, "cannot mount on %s: %s\n",
230                         mount_point, strerror(errno));
231                 exit(1);
232         }
233
234         data.version = SMB_MOUNT_VERSION;
235
236         /* getuid() gives us the real uid, who may umount the fs */
237         data.mounted_uid = getuid();
238
239         if (parse_args(argc, argv, &data, &share_name) != 0) {
240                 help();
241                 return -1;
242         }
243
244         data.uid = mount_uid;    // truncates to 16-bits here!!!
245         data.gid = mount_gid;
246         data.file_mode = (S_IRWXU|S_IRWXG|S_IRWXO) & mount_fmask;
247         data.dir_mode  = (S_IRWXU|S_IRWXG|S_IRWXO) & mount_dmask;
248
249         if (mount_dmask == 0) {
250                 data.dir_mode = data.file_mode;
251                 if ((data.dir_mode & S_IRUSR) != 0)
252                         data.dir_mode |= S_IXUSR;
253                 if ((data.dir_mode & S_IRGRP) != 0)
254                         data.dir_mode |= S_IXGRP;
255                 if ((data.dir_mode & S_IROTH) != 0)
256                         data.dir_mode |= S_IXOTH;
257         }
258
259         flags = MS_MGC_VAL | MS_NOSUID | MS_NODEV;
260
261         if (mount_ro) flags |= MS_RDONLY;
262
263         if (do_mount(share_name, flags, &data) < 0) {
264                 switch (errno) {
265                 case ENODEV:
266                         fprintf(stderr, "ERROR: smbfs filesystem not supported by the kernel\n");
267                         break;
268                 default:
269                         perror("mount error");
270                 }
271                 fprintf(stderr, "Please refer to the smbmnt(8) manual page\n");
272                 return -1;
273         }
274
275         ment.mnt_fsname = share_name ? share_name : "none";
276         ment.mnt_dir = mount_point;
277         ment.mnt_type = "smbfs";
278         ment.mnt_opts = "";
279         ment.mnt_freq = 0;
280         ment.mnt_passno= 0;
281
282         mount_point = ment.mnt_dir;
283
284         if (mount_point == NULL)
285         {
286                 fprintf(stderr, "Mount point too long\n");
287                 return -1;
288         }
289         
290         if ((fd = open(MOUNTED"~", O_RDWR|O_CREAT|O_EXCL, 0600)) == -1)
291         {
292                 fprintf(stderr, "Can't get "MOUNTED"~ lock file");
293                 return 1;
294         }
295         close(fd);
296         
297         if ((mtab = setmntent(MOUNTED, "a+")) == NULL)
298         {
299                 fprintf(stderr, "Can't open " MOUNTED);
300                 return 1;
301         }
302
303         if (addmntent(mtab, &ment) == 1)
304         {
305                 fprintf(stderr, "Can't write mount entry");
306                 return 1;
307         }
308         if (fchmod(fileno(mtab), 0644) == -1)
309         {
310                 fprintf(stderr, "Can't set perms on "MOUNTED);
311                 return 1;
312         }
313         endmntent(mtab);
314
315         if (unlink(MOUNTED"~") == -1)
316         {
317                 fprintf(stderr, "Can't remove "MOUNTED"~");
318                 return 1;
319         }
320
321         return 0;
322 }