1 /*
2 * Copyright (c) 2000-2001, Boris Popov
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 * must display the following acknowledgement:
15 * This product includes software developed by Boris Popov.
16 * 4. Neither the name of the author nor the names of any co-contributors
17 * may be used to endorse or promote products derived from this software
18 * without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 *
32 * $Id: mount_smbfs.c,v 1.28.44.2 2005/06/02 00:55:41 lindak Exp $
33 */
34
35 /*
36 * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
37 * Use is subject to license terms.
38 *
39 * Copyright 2012 Nexenta Systems, Inc. All rights reserved.
40 */
41
42 #include <stdio.h>
43 #include <string.h>
44 #include <strings.h>
45 #include <pwd.h>
46 #include <grp.h>
47 #include <unistd.h>
48 #include <ctype.h>
49 #include <stdlib.h>
50 #include <errno.h>
51 #include <err.h>
52 #include <libintl.h>
53 #include <locale.h>
54 #include <libscf.h>
55 #include <priv_utils.h>
56
57 #include <sys/types.h>
58 #include <sys/stat.h>
59 #include <sys/errno.h>
60 #include <sys/mount.h>
61 #include <sys/mntent.h>
62 #include <sys/mnttab.h>
63
64 #include <sys/fs/smbfs_mount.h>
65
66 /* This needs to know ctx->ct_dev_fd, etc. */
67 #include <netsmb/smb_lib.h>
68
69 extern char *optarg;
70 extern int optind;
71
72 static char mount_point[MAXPATHLEN + 1];
73 static void usage(void);
74 static int setsubopt(smb_ctx_t *, struct smbfs_args *, char *);
75
76 const char * const optlist[] = {
77
78 /* Generic VFS options. */
79 #define OPT_RO 0
80 MNTOPT_RO,
81 #define OPT_RW 1
82 MNTOPT_RW,
83 #define OPT_SUID 2
84 MNTOPT_SUID,
85 #define OPT_NOSUID 3
86 MNTOPT_NOSUID,
87 #define OPT_DEVICES 4
88 MNTOPT_DEVICES,
89 #define OPT_NODEVICES 5
90 MNTOPT_NODEVICES,
91 #define OPT_SETUID 6
92 MNTOPT_SETUID,
93 #define OPT_NOSETUID 7
94 MNTOPT_NOSETUID,
95 #define OPT_EXEC 8
96 MNTOPT_EXEC,
97 #define OPT_NOEXEC 9
98 MNTOPT_NOEXEC,
99 #define OPT_FOLLOW 10
100 MNTOPT_FOLLOW,
101 #define OPT_NOFOLLOW 11
102 MNTOPT_NOFOLLOW,
103 #define OPT_XATTR 12
104 MNTOPT_XATTR,
105 #define OPT_NOXATTR 13
106 MNTOPT_NOXATTR,
107
108 /* Sort of generic (from NFS) */
109 #define OPT_NOAC 14
110 MNTOPT_NOAC,
111 #define OPT_ACTIMEO 15
112 MNTOPT_ACTIMEO,
113 #define OPT_ACREGMIN 16
114 MNTOPT_ACREGMIN,
115 #define OPT_ACREGMAX 17
116 MNTOPT_ACREGMAX,
117 #define OPT_ACDIRMIN 18
118 MNTOPT_ACDIRMIN,
119 #define OPT_ACDIRMAX 19
120 MNTOPT_ACDIRMAX,
121
122 /* smbfs-specifis options */
123 #define OPT_DOMAIN 20
124 "domain",
125 #define OPT_USER 21
126 "user",
127 #define OPT_UID 22
128 "uid",
129 #define OPT_GID 23
130 "gid",
131 #define OPT_DIRPERMS 24
132 "dirperms",
133 #define OPT_FILEPERMS 25
134 "fileperms",
135 #define OPT_NOPROMPT 26
136 "noprompt",
137 #define OPT_ACL 27
138 MNTOPT_ACL,
139 #define OPT_NOACL 28
140 MNTOPT_NOACL,
141
142 NULL
143 };
144
145 static int Oflg = 0; /* Overlay mounts */
146 static int qflg = 0; /* quiet - don't print warnings on bad options */
147 static int noprompt = 0; /* don't prompt for password */
148
149 /* Note: smbfs uses _both_ kinds of options. */
150 static int mntflags = MS_DATA | MS_OPTIONSTR;
151
152 #define EX_OK 0 /* normal */
153 #define EX_OPT 1 /* bad options, usage, etc */
154 #define EX_MNT 2 /* mount point problems, etc */
155 #define RET_ERR 3 /* later errors */
156
157 #define SERVICE "svc:/network/smb/client:default"
158
159 struct smbfs_args mdata;
160 struct mnttab mnt;
161
162 /*
163 * Initialize this with "rw" just to have something there,
164 * so we don't have to decide whether to add a comma when
165 * we strcat another option. Note the "rw" may be changed
166 * to an "ro" by option processing.
167 */
168 char optbuf[MAX_MNTOPT_STR] = "rw";
169 char special[MAXPATHLEN];
170
171 int
main(int argc,char * argv[])172 main(int argc, char *argv[])
173 {
174 struct smb_ctx *ctx = NULL;
175 struct stat st;
176 int opt, error, err2;
177 static char *fstype = MNTTYPE_SMBFS;
178 char *env;
179
180 (void) setlocale(LC_ALL, "");
181 #if !defined(TEXT_DOMAIN)
182 #define TEXT_DOMAIN "SYS_TEST"
183 #endif
184 (void) textdomain(TEXT_DOMAIN);
185
186 /*
187 * Normal users are allowed to run "mount -F smbfs ..."
188 * to mount on a directory they own. To allow that, this
189 * program is installed setuid root, and it adds SYS_MOUNT
190 * privilege here (if needed), and then restores the user's
191 * normal privileges. When root runs this, it's a no-op.
192 */
193 if (__init_suid_priv(0, PRIV_SYS_MOUNT, (char *)NULL) < 0) {
194 (void) fprintf(stderr,
195 gettext("Insufficient privileges, "
196 "%s must be set-uid root\n"), argv[0]);
197 exit(RET_ERR);
198 }
199
200 if (argc == 2) {
201 if (strcmp(argv[1], "-h") == 0) {
202 usage();
203 } else if (strcmp(argv[1], "-v") == 0) {
204 errx(EX_OK, gettext("version %d.%d.%d"),
205 SMBFS_VERSION / 100000,
206 (SMBFS_VERSION % 10000) / 1000,
207 (SMBFS_VERSION % 1000) / 100);
208 }
209 }
210 if (argc < 3)
211 usage();
212
213 /* Debugging support. */
214 if ((env = getenv("SMBFS_DEBUG")) != NULL) {
215 smb_debug = atoi(env);
216 if (smb_debug < 1)
217 smb_debug = 1;
218 }
219
220 error = smb_lib_init();
221 if (error)
222 exit(RET_ERR);
223
224 mnt.mnt_mntopts = optbuf;
225
226 bzero(&mdata, sizeof (mdata));
227 mdata.version = SMBFS_VERSION; /* smbfs mount version */
228 mdata.uid = (uid_t)-1;
229 mdata.gid = (gid_t)-1;
230
231 error = smb_ctx_alloc(&ctx);
232 if (error)
233 exit(RET_ERR);
234
235 /*
236 * Parse the UNC path so we have the server (etc.)
237 * that we need during rcfile+sharectl parsing.
238 */
239 if (argc < 3)
240 usage();
241 error = smb_ctx_parseunc(ctx, argv[argc - 2],
242 SMBL_SHARE, SMBL_SHARE, USE_DISKDEV, NULL);
243 if (error)
244 exit(EX_OPT);
245
246 error = smb_ctx_readrc(ctx);
247 if (error)
248 exit(EX_OPT);
249
250 while ((opt = getopt(argc, argv, "ro:Oq")) != -1) {
251 switch (opt) {
252 case 'O':
253 Oflg++;
254 break;
255
256 case 'q':
257 qflg++;
258 break;
259
260 case 'r':
261 mntflags |= MS_RDONLY;
262 break;
263
264 case 'o': {
265 char *nextopt, *comma, *sopt;
266 int ret;
267
268 for (sopt = optarg; sopt != NULL; sopt = nextopt) {
269 comma = strchr(sopt, ',');
270 if (comma) {
271 nextopt = comma + 1;
272 *comma = '\0';
273 } else
274 nextopt = NULL;
275 ret = setsubopt(ctx, &mdata, sopt);
276 if (ret != 0)
277 exit(EX_OPT);
278 /* undo changes to optarg */
279 if (comma)
280 *comma = ',';
281 }
282 break;
283 }
284
285 case '?':
286 default:
287 usage();
288 }
289 }
290
291 if (Oflg)
292 mntflags |= MS_OVERLAY;
293
294 if (mntflags & MS_RDONLY) {
295 char *p;
296 /* convert "rw"->"ro" */
297 if (p = strstr(optbuf, "rw")) {
298 if (*(p+2) == ',' || *(p+2) == '\0')
299 *(p+1) = 'o';
300 }
301 }
302
303 if (optind + 2 != argc)
304 usage();
305
306 (void) snprintf(special, sizeof (special), "//%s/%s",
307 ctx->ct_fullserver, ctx->ct_origshare);
308
309 mnt.mnt_special = special;
310 mnt.mnt_mountp = argv[optind+1];
311
312 if ((realpath(argv[optind+1], mount_point) == NULL) ||
313 (stat(mount_point, &st) == -1)) {
314 err(EX_MNT, gettext("could not find mount point %s"),
315 argv[optind+1]);
316 }
317 if (!S_ISDIR(st.st_mode)) {
318 errno = ENOTDIR;
319 err(EX_MNT, gettext("can't mount on %s"), mount_point);
320 }
321
322 /*
323 * Fill in mdata defaults.
324 */
325 if (mdata.uid == (uid_t)-1)
326 mdata.uid = getuid();
327 if (mdata.gid == (gid_t)-1)
328 mdata.gid = getgid();
329 if (mdata.file_mode == 0)
330 mdata.file_mode = S_IRWXU;
331 if (mdata.dir_mode == 0) {
332 mdata.dir_mode = mdata.file_mode;
333 if (mdata.dir_mode & S_IRUSR)
334 mdata.dir_mode |= S_IXUSR;
335 if (mdata.dir_mode & S_IRGRP)
336 mdata.dir_mode |= S_IXGRP;
337 if (mdata.dir_mode & S_IROTH)
338 mdata.dir_mode |= S_IXOTH;
339 }
340
341 ctx->ct_ssn.ssn_owner = SMBM_ANY_OWNER;
342 if (noprompt)
343 ctx->ct_flags |= SMBCF_NOPWD;
344
345 /*
346 * Resolve the server address,
347 * setup derived defaults.
348 */
349 error = smb_ctx_resolve(ctx);
350 if (error)
351 exit(RET_ERR);
352
353 /*
354 * Have server, share, etc. from above:
355 * smb_ctx_scan_argv, option settings.
356 * Get the session and tree.
357 */
358 again:
359 error = smb_ctx_get_ssn(ctx);
360 if (error == EAUTH && noprompt == 0) {
361 err2 = smb_get_authentication(ctx);
362 if (err2 == 0)
363 goto again;
364 }
365 if (error) {
366 smb_error(gettext("//%s: login failed"),
367 error, ctx->ct_fullserver);
368 exit(RET_ERR);
369 }
370
371 error = smb_ctx_get_tree(ctx);
372 if (error) {
373 smb_error(gettext("//%s/%s: tree connect failed"),
374 error, ctx->ct_fullserver, ctx->ct_origshare);
375 exit(RET_ERR);
376 }
377
378 /*
379 * Have tree connection, now mount it.
380 */
381 mdata.devfd = ctx->ct_dev_fd;
382
383 /* Need sys_mount privilege for the mount call. */
384 (void) __priv_bracket(PRIV_ON);
385 err2 = mount(mnt.mnt_special, mnt.mnt_mountp,
386 mntflags, fstype, &mdata, sizeof (mdata),
387 mnt.mnt_mntopts, MAX_MNTOPT_STR);
388 (void) __priv_bracket(PRIV_OFF);
389
390 if (err2 < 0) {
391 if (errno != ENOENT) {
392 err(EX_MNT, gettext("mount_smbfs: %s"),
393 mnt.mnt_mountp);
394 } else {
395 struct stat sb;
396 if (stat(mnt.mnt_mountp, &sb) < 0 &&
397 errno == ENOENT)
398 err(EX_MNT, gettext("mount_smbfs: %s"),
399 mnt.mnt_mountp);
400 else
401 err(EX_MNT, gettext("mount_smbfs: %s"),
402 mnt.mnt_special);
403 }
404 }
405
406 smb_ctx_free(ctx);
407 return (0);
408 }
409
410 #define bad(val) (val == NULL || !isdigit(*val))
411
412 int
setsubopt(smb_ctx_t * ctx,struct smbfs_args * mdatap,char * subopt)413 setsubopt(smb_ctx_t *ctx, struct smbfs_args *mdatap, char *subopt)
414 {
415 char *equals, *optarg;
416 struct passwd *pwd;
417 struct group *grp;
418 long val;
419 int rc = EX_OK;
420 int index;
421 char *p;
422
423 equals = strchr(subopt, '=');
424 if (equals) {
425 *equals = '\0';
426 optarg = equals + 1;
427 } else
428 optarg = NULL;
429
430 for (index = 0; optlist[index] != NULL; index++) {
431 if (strcmp(subopt, optlist[index]) == 0)
432 break;
433 }
434
435 /*
436 * Note: if the option was unknown, index will
437 * point to the NULL at the end of optlist[],
438 * and we'll take the switch default.
439 */
440
441 switch (index) {
442
443 case OPT_ACL:
444 case OPT_NOACL:
445 case OPT_SUID:
446 case OPT_NOSUID:
447 case OPT_DEVICES:
448 case OPT_NODEVICES:
449 case OPT_SETUID:
450 case OPT_NOSETUID:
451 case OPT_EXEC:
452 case OPT_NOEXEC:
453 case OPT_FOLLOW:
454 case OPT_NOFOLLOW:
455 case OPT_XATTR:
456 case OPT_NOXATTR:
457 /*
458 * These options are handled via the
459 * generic option string mechanism.
460 * None of these take an optarg.
461 */
462 if (optarg != NULL)
463 goto badval;
464 (void) strlcat(optbuf, ",", sizeof (optbuf));
465 if (strlcat(optbuf, subopt, sizeof (optbuf)) >=
466 sizeof (optbuf)) {
467 if (!qflg)
468 warnx(gettext("option string too long"));
469 rc = EX_OPT;
470 }
471 break;
472
473 /*
474 * OPT_RO, OPT_RW, are actually generic too,
475 * but we use the mntflags for these, and
476 * then update the options string later.
477 */
478 case OPT_RO:
479 mntflags |= MS_RDONLY;
480 break;
481 case OPT_RW:
482 mntflags &= ~MS_RDONLY;
483 break;
484
485 /*
486 * NFS-derived options for attribute cache
487 * handling (disable, set min/max timeouts)
488 */
489 case OPT_NOAC:
490 mdatap->flags |= SMBFS_MF_NOAC;
491 break;
492
493 case OPT_ACTIMEO:
494 errno = 0;
495 val = strtol(optarg, &p, 10);
496 if (errno || *p != 0)
497 goto badval;
498 mdatap->acdirmin = mdatap->acregmin = val;
499 mdatap->acdirmax = mdatap->acregmax = val;
500 mdatap->flags |= SMBFS_MF_ACDIRMAX;
501 mdatap->flags |= SMBFS_MF_ACREGMAX;
502 mdatap->flags |= SMBFS_MF_ACDIRMIN;
503 mdatap->flags |= SMBFS_MF_ACREGMIN;
504 break;
505
506 case OPT_ACREGMIN:
507 errno = 0;
508 val = strtol(optarg, &p, 10);
509 if (errno || *p != 0)
510 goto badval;
511 mdatap->acregmin = val;
512 mdatap->flags |= SMBFS_MF_ACREGMIN;
513 break;
514
515 case OPT_ACREGMAX:
516 errno = 0;
517 val = strtol(optarg, &p, 10);
518 if (errno || *p != 0)
519 goto badval;
520 mdatap->acregmax = val;
521 mdatap->flags |= SMBFS_MF_ACREGMAX;
522 break;
523
524 case OPT_ACDIRMIN:
525 errno = 0;
526 val = strtol(optarg, &p, 10);
527 if (errno || *p != 0)
528 goto badval;
529 mdatap->acdirmin = val;
530 mdatap->flags |= SMBFS_MF_ACDIRMIN;
531 break;
532
533 case OPT_ACDIRMAX:
534 errno = 0;
535 val = strtol(optarg, &p, 10);
536 if (errno || *p != 0)
537 goto badval;
538 mdatap->acdirmax = val;
539 mdatap->flags |= SMBFS_MF_ACDIRMAX;
540 break;
541
542 /*
543 * SMBFS-specific options. Some of these
544 * don't go through the mount system call,
545 * but just set libsmbfs options.
546 */
547 case OPT_DOMAIN:
548 if (smb_ctx_setdomain(ctx, optarg, B_TRUE) != 0)
549 rc = EX_OPT;
550 break;
551
552 case OPT_USER:
553 if (smb_ctx_setuser(ctx, optarg, B_TRUE) != 0)
554 rc = EX_OPT;
555 break;
556
557 case OPT_UID:
558 pwd = isdigit(optarg[0]) ?
559 getpwuid(atoi(optarg)) : getpwnam(optarg);
560 if (pwd == NULL) {
561 if (!qflg)
562 warnx(gettext("unknown user '%s'"), optarg);
563 rc = EX_OPT;
564 } else {
565 mdatap->uid = pwd->pw_uid;
566 }
567 break;
568
569 case OPT_GID:
570 grp = isdigit(optarg[0]) ?
571 getgrgid(atoi(optarg)) : getgrnam(optarg);
572 if (grp == NULL) {
573 if (!qflg)
574 warnx(gettext("unknown group '%s'"), optarg);
575 rc = EX_OPT;
576 } else {
577 mdatap->gid = grp->gr_gid;
578 }
579 break;
580
581 case OPT_DIRPERMS:
582 errno = 0;
583 val = strtol(optarg, &p, 8);
584 if (errno || *p != 0)
585 goto badval;
586 mdatap->dir_mode = val;
587 break;
588
589 case OPT_FILEPERMS:
590 errno = 0;
591 val = strtol(optarg, &p, 8);
592 if (errno || *p != 0)
593 goto badval;
594 mdatap->file_mode = val;
595 break;
596
597 case OPT_NOPROMPT:
598 noprompt++;
599 break;
600
601 default:
602 badopt:
603 if (!qflg)
604 warnx(gettext("unknown option %s"), subopt);
605 rc = EX_OPT;
606 break;
607
608 badval:
609 if (!qflg)
610 warnx(gettext("invalid value for %s"), subopt);
611 rc = EX_OPT;
612 break;
613 }
614
615 /* Undo changes made to subopt */
616 if (equals)
617 *equals = '=';
618
619 return (rc);
620 }
621
622 static void
usage(void)623 usage(void)
624 {
625 (void) fprintf(stderr, "%s\n",
626 gettext("usage: mount -F smbfs [-Orq] [-o option[,option]]"
627 " //[workgroup;][user[:password]@]server[/share] path"));
628
629 exit(EX_OPT);
630 }
631