xref: /illumos-gate/usr/src/cmd/fs.d/smbclnt/mount/mount.c (revision 45ede40b2394db7967e59f19288fae9b62efd4aa)
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
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
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
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