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 2019 Nexenta by DDN, 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_XATTR 10
100 MNTOPT_XATTR,
101 #define OPT_NOXATTR 11
102 MNTOPT_NOXATTR,
103
104 /* Sort of generic (from NFS) */
105 #define OPT_NOAC 12
106 MNTOPT_NOAC,
107 #define OPT_ACTIMEO 13
108 MNTOPT_ACTIMEO,
109 #define OPT_ACREGMIN 14
110 MNTOPT_ACREGMIN,
111 #define OPT_ACREGMAX 15
112 MNTOPT_ACREGMAX,
113 #define OPT_ACDIRMIN 16
114 MNTOPT_ACDIRMIN,
115 #define OPT_ACDIRMAX 17
116 MNTOPT_ACDIRMAX,
117
118 /* smbfs-specifis options */
119 #define OPT_DOMAIN 18
120 "domain",
121 #define OPT_USER 19
122 "user",
123 #define OPT_UID 20
124 "uid",
125 #define OPT_GID 21
126 "gid",
127 #define OPT_DIRPERMS 22
128 "dirperms",
129 #define OPT_FILEPERMS 23
130 "fileperms",
131 #define OPT_NOPROMPT 24
132 "noprompt",
133 #define OPT_ACL 25
134 MNTOPT_ACL,
135 #define OPT_NOACL 26
136 MNTOPT_NOACL,
137
138 NULL
139 };
140
141 static int Oflg = 0; /* Overlay mounts */
142 static int qflg = 0; /* quiet - don't print warnings on bad options */
143 static int noprompt = 0; /* don't prompt for password */
144
145 /* Note: smbfs uses _both_ kinds of options. */
146 static int mntflags = MS_DATA | MS_OPTIONSTR;
147
148 #define EX_OK 0 /* normal */
149 #define EX_OPT 1 /* bad options, usage, etc */
150 #define EX_MNT 2 /* mount point problems, etc */
151 #define RET_ERR 3 /* later errors */
152
153 #define SERVICE "svc:/network/smb/client:default"
154
155 struct smbfs_args mdata;
156 struct mnttab mnt;
157
158 /*
159 * Initialize this with "rw" just to have something there,
160 * so we don't have to decide whether to add a comma when
161 * we strcat another option. Note the "rw" may be changed
162 * to an "ro" by option processing.
163 */
164 char optbuf[MAX_MNTOPT_STR] = "rw";
165 char special[MAXPATHLEN];
166
167 int
main(int argc,char * argv[])168 main(int argc, char *argv[])
169 {
170 struct smb_ctx *ctx = NULL;
171 struct stat st;
172 int opt, error, err2;
173 static char *fstype = MNTTYPE_SMBFS;
174 char *env;
175
176 (void) setlocale(LC_ALL, "");
177 #if !defined(TEXT_DOMAIN)
178 #define TEXT_DOMAIN "SYS_TEST"
179 #endif
180 (void) textdomain(TEXT_DOMAIN);
181
182 /*
183 * Normal users are allowed to run "mount -F smbfs ..."
184 * to mount on a directory they own. To allow that, this
185 * program has an exec_attr that adds SYS_MOUNT priv.
186 *
187 * The __init_suid_priv call was designed for SUID programs,
188 * but also works for privileges granted via exec_attr with
189 * one difference: the added privileges are already effective
190 * when the program starts, and remain effective after the call.
191 * To make this work more like the SUID case we'll turn off the
192 * additional privileges with a __priv_bracket() call here.
193 * Later calls to __priv_bracket() make the extra privileges
194 * effective only when we need them.
195 */
196 if (__init_suid_priv(0, PRIV_SYS_MOUNT, (char *)NULL) < 0) {
197 (void) fprintf(stderr,
198 gettext("Insufficient privileges, "
199 "%s should have sys_mount privilege via exec_attr\n"),
200 argv[0]);
201 exit(RET_ERR);
202 }
203 (void) __priv_bracket(PRIV_OFF);
204
205 if (argc == 2) {
206 if (strcmp(argv[1], "-h") == 0) {
207 usage();
208 } else if (strcmp(argv[1], "-v") == 0) {
209 errx(EX_OK, gettext("version %d.%d.%d"),
210 SMBFS_VERSION / 100000,
211 (SMBFS_VERSION % 10000) / 1000,
212 (SMBFS_VERSION % 1000) / 100);
213 }
214 }
215 if (argc < 3)
216 usage();
217
218 /* Debugging support. */
219 if ((env = getenv("SMBFS_DEBUG")) != NULL) {
220 smb_debug = atoi(env);
221 if (smb_debug < 1)
222 smb_debug = 1;
223 }
224
225 error = smb_lib_init();
226 if (error)
227 exit(RET_ERR);
228
229 mnt.mnt_mntopts = optbuf;
230
231 bzero(&mdata, sizeof (mdata));
232 mdata.version = SMBFS_VERSION; /* smbfs mount version */
233 mdata.uid = (uid_t)-1;
234 mdata.gid = (gid_t)-1;
235
236 error = smb_ctx_alloc(&ctx);
237 if (error)
238 exit(RET_ERR);
239
240 /*
241 * Parse the UNC path so we have the server (etc.)
242 * that we need during rcfile+sharectl parsing.
243 */
244 if (argc < 3)
245 usage();
246 error = smb_ctx_parseunc(ctx, argv[argc - 2],
247 SMBL_SHARE, SMBL_SHARE, USE_DISKDEV, NULL);
248 if (error)
249 exit(EX_OPT);
250
251 error = smb_ctx_readrc(ctx);
252 if (error)
253 exit(EX_OPT);
254
255 while ((opt = getopt(argc, argv, "ro:Oq")) != -1) {
256 switch (opt) {
257 case 'O':
258 Oflg++;
259 break;
260
261 case 'q':
262 qflg++;
263 break;
264
265 case 'r':
266 mntflags |= MS_RDONLY;
267 break;
268
269 case 'o': {
270 char *nextopt, *comma, *sopt;
271 int ret;
272
273 for (sopt = optarg; sopt != NULL; sopt = nextopt) {
274 comma = strchr(sopt, ',');
275 if (comma) {
276 nextopt = comma + 1;
277 *comma = '\0';
278 } else
279 nextopt = NULL;
280 ret = setsubopt(ctx, &mdata, sopt);
281 if (ret != 0)
282 exit(EX_OPT);
283 /* undo changes to optarg */
284 if (comma)
285 *comma = ',';
286 }
287 break;
288 }
289
290 case '?':
291 default:
292 usage();
293 }
294 }
295
296 if (Oflg)
297 mntflags |= MS_OVERLAY;
298
299 if (mntflags & MS_RDONLY) {
300 char *p;
301 /* convert "rw"->"ro" */
302 if (p = strstr(optbuf, "rw")) {
303 if (*(p+2) == ',' || *(p+2) == '\0')
304 *(p+1) = 'o';
305 }
306 }
307
308 if (optind + 2 != argc)
309 usage();
310
311 (void) snprintf(special, sizeof (special), "//%s/%s",
312 ctx->ct_fullserver, ctx->ct_origshare);
313
314 mnt.mnt_special = special;
315 mnt.mnt_mountp = argv[optind+1];
316
317 if ((realpath(argv[optind+1], mount_point) == NULL) ||
318 (stat(mount_point, &st) == -1)) {
319 err(EX_MNT, gettext("could not find mount point %s"),
320 argv[optind+1]);
321 }
322 if (!S_ISDIR(st.st_mode)) {
323 errno = ENOTDIR;
324 err(EX_MNT, gettext("can't mount on %s"), mount_point);
325 }
326
327 /*
328 * Fill in mdata defaults.
329 */
330 if (mdata.uid == (uid_t)-1)
331 mdata.uid = getuid();
332 if (mdata.gid == (gid_t)-1)
333 mdata.gid = getgid();
334 if (mdata.file_mode == 0)
335 mdata.file_mode = S_IRWXU;
336 if (mdata.dir_mode == 0) {
337 mdata.dir_mode = mdata.file_mode;
338 if (mdata.dir_mode & S_IRUSR)
339 mdata.dir_mode |= S_IXUSR;
340 if (mdata.dir_mode & S_IRGRP)
341 mdata.dir_mode |= S_IXGRP;
342 if (mdata.dir_mode & S_IROTH)
343 mdata.dir_mode |= S_IXOTH;
344 }
345
346 ctx->ct_ssn.ssn_owner = SMBM_ANY_OWNER;
347 if (noprompt)
348 ctx->ct_flags |= SMBCF_NOPWD;
349
350 /*
351 * Resolve the server address,
352 * setup derived defaults.
353 */
354 error = smb_ctx_resolve(ctx);
355 if (error)
356 exit(RET_ERR);
357
358 /*
359 * Have server, share, etc. from above:
360 * smb_ctx_scan_argv, option settings.
361 * Get the session and tree.
362 */
363 again:
364 error = smb_ctx_get_ssn(ctx);
365 if (error == EAUTH && noprompt == 0) {
366 err2 = smb_get_authentication(ctx);
367 if (err2 == 0)
368 goto again;
369 }
370 if (error) {
371 smb_error(gettext("//%s: login failed"),
372 error, ctx->ct_fullserver);
373 exit(RET_ERR);
374 }
375
376 error = smb_ctx_get_tree(ctx);
377 if (error) {
378 smb_error(gettext("//%s/%s: tree connect failed"),
379 error, ctx->ct_fullserver, ctx->ct_origshare);
380 exit(RET_ERR);
381 }
382
383 /*
384 * Have tree connection, now mount it.
385 */
386 mdata.devfd = ctx->ct_dev_fd;
387
388 /* Need sys_mount privilege for the mount call. */
389 (void) __priv_bracket(PRIV_ON);
390 err2 = mount(mnt.mnt_special, mnt.mnt_mountp,
391 mntflags, fstype, &mdata, sizeof (mdata),
392 mnt.mnt_mntopts, MAX_MNTOPT_STR);
393 (void) __priv_bracket(PRIV_OFF);
394
395 if (err2 < 0) {
396 if (errno != ENOENT) {
397 err(EX_MNT, gettext("mount_smbfs: %s"),
398 mnt.mnt_mountp);
399 } else {
400 struct stat sb;
401 if (stat(mnt.mnt_mountp, &sb) < 0 &&
402 errno == ENOENT)
403 err(EX_MNT, gettext("mount_smbfs: %s"),
404 mnt.mnt_mountp);
405 else
406 err(EX_MNT, gettext("mount_smbfs: %s"),
407 mnt.mnt_special);
408 }
409 }
410
411 smb_ctx_free(ctx);
412 return (0);
413 }
414
415 #define bad(val) (val == NULL || !isdigit(*val))
416
417 int
setsubopt(smb_ctx_t * ctx,struct smbfs_args * mdatap,char * subopt)418 setsubopt(smb_ctx_t *ctx, struct smbfs_args *mdatap, char *subopt)
419 {
420 char *equals, *optarg;
421 struct passwd *pwd;
422 struct group *grp;
423 long val;
424 int rc = EX_OK;
425 int index;
426 char *p;
427
428 equals = strchr(subopt, '=');
429 if (equals) {
430 *equals = '\0';
431 optarg = equals + 1;
432 } else
433 optarg = NULL;
434
435 for (index = 0; optlist[index] != NULL; index++) {
436 if (strcmp(subopt, optlist[index]) == 0)
437 break;
438 }
439
440 /*
441 * Note: if the option was unknown, index will
442 * point to the NULL at the end of optlist[],
443 * and we'll take the switch default.
444 */
445
446 switch (index) {
447
448 case OPT_ACL:
449 case OPT_NOACL:
450 case OPT_SUID:
451 case OPT_NOSUID:
452 case OPT_DEVICES:
453 case OPT_NODEVICES:
454 case OPT_SETUID:
455 case OPT_NOSETUID:
456 case OPT_EXEC:
457 case OPT_NOEXEC:
458 case OPT_XATTR:
459 case OPT_NOXATTR:
460 /*
461 * These options are handled via the
462 * generic option string mechanism.
463 * None of these take an optarg.
464 */
465 if (optarg != NULL)
466 goto badval;
467 (void) strlcat(optbuf, ",", sizeof (optbuf));
468 if (strlcat(optbuf, subopt, sizeof (optbuf)) >=
469 sizeof (optbuf)) {
470 if (!qflg)
471 warnx(gettext("option string too long"));
472 rc = EX_OPT;
473 }
474 break;
475
476 /*
477 * OPT_RO, OPT_RW, are actually generic too,
478 * but we use the mntflags for these, and
479 * then update the options string later.
480 */
481 case OPT_RO:
482 mntflags |= MS_RDONLY;
483 break;
484 case OPT_RW:
485 mntflags &= ~MS_RDONLY;
486 break;
487
488 /*
489 * NFS-derived options for attribute cache
490 * handling (disable, set min/max timeouts)
491 */
492 case OPT_NOAC:
493 mdatap->flags |= SMBFS_MF_NOAC;
494 break;
495
496 case OPT_ACTIMEO:
497 errno = 0;
498 val = strtol(optarg, &p, 10);
499 if (errno || *p != 0)
500 goto badval;
501 mdatap->acdirmin = mdatap->acregmin = val;
502 mdatap->acdirmax = mdatap->acregmax = val;
503 mdatap->flags |= SMBFS_MF_ACDIRMAX;
504 mdatap->flags |= SMBFS_MF_ACREGMAX;
505 mdatap->flags |= SMBFS_MF_ACDIRMIN;
506 mdatap->flags |= SMBFS_MF_ACREGMIN;
507 break;
508
509 case OPT_ACREGMIN:
510 errno = 0;
511 val = strtol(optarg, &p, 10);
512 if (errno || *p != 0)
513 goto badval;
514 mdatap->acregmin = val;
515 mdatap->flags |= SMBFS_MF_ACREGMIN;
516 break;
517
518 case OPT_ACREGMAX:
519 errno = 0;
520 val = strtol(optarg, &p, 10);
521 if (errno || *p != 0)
522 goto badval;
523 mdatap->acregmax = val;
524 mdatap->flags |= SMBFS_MF_ACREGMAX;
525 break;
526
527 case OPT_ACDIRMIN:
528 errno = 0;
529 val = strtol(optarg, &p, 10);
530 if (errno || *p != 0)
531 goto badval;
532 mdatap->acdirmin = val;
533 mdatap->flags |= SMBFS_MF_ACDIRMIN;
534 break;
535
536 case OPT_ACDIRMAX:
537 errno = 0;
538 val = strtol(optarg, &p, 10);
539 if (errno || *p != 0)
540 goto badval;
541 mdatap->acdirmax = val;
542 mdatap->flags |= SMBFS_MF_ACDIRMAX;
543 break;
544
545 /*
546 * SMBFS-specific options. Some of these
547 * don't go through the mount system call,
548 * but just set libsmbfs options.
549 */
550 case OPT_DOMAIN:
551 if (smb_ctx_setdomain(ctx, optarg, B_TRUE) != 0)
552 rc = EX_OPT;
553 break;
554
555 case OPT_USER:
556 if (smb_ctx_setuser(ctx, optarg, B_TRUE) != 0)
557 rc = EX_OPT;
558 break;
559
560 case OPT_UID:
561 pwd = isdigit(optarg[0]) ?
562 getpwuid(atoi(optarg)) : getpwnam(optarg);
563 if (pwd == NULL) {
564 if (!qflg)
565 warnx(gettext("unknown user '%s'"), optarg);
566 rc = EX_OPT;
567 } else {
568 mdatap->uid = pwd->pw_uid;
569 }
570 break;
571
572 case OPT_GID:
573 grp = isdigit(optarg[0]) ?
574 getgrgid(atoi(optarg)) : getgrnam(optarg);
575 if (grp == NULL) {
576 if (!qflg)
577 warnx(gettext("unknown group '%s'"), optarg);
578 rc = EX_OPT;
579 } else {
580 mdatap->gid = grp->gr_gid;
581 }
582 break;
583
584 case OPT_DIRPERMS:
585 errno = 0;
586 val = strtol(optarg, &p, 8);
587 if (errno || *p != 0)
588 goto badval;
589 mdatap->dir_mode = val;
590 break;
591
592 case OPT_FILEPERMS:
593 errno = 0;
594 val = strtol(optarg, &p, 8);
595 if (errno || *p != 0)
596 goto badval;
597 mdatap->file_mode = val;
598 break;
599
600 case OPT_NOPROMPT:
601 noprompt++;
602 break;
603
604 default:
605 badopt:
606 if (!qflg)
607 warnx(gettext("unknown option %s"), subopt);
608 rc = EX_OPT;
609 break;
610
611 badval:
612 if (!qflg)
613 warnx(gettext("invalid value for %s"), subopt);
614 rc = EX_OPT;
615 break;
616 }
617
618 /* Undo changes made to subopt */
619 if (equals)
620 *equals = '=';
621
622 return (rc);
623 }
624
625 static void
usage(void)626 usage(void)
627 {
628 (void) fprintf(stderr, "%s\n",
629 gettext("usage: mount -F smbfs [-Orq] [-o option[,option]]"
630 " //[workgroup;][user[:password]@]server[/share] path"));
631
632 exit(EX_OPT);
633 }
634