xref: /titanic_52/usr/src/cmd/fs.d/smbclnt/mount/mount.c (revision 1a4cea1bca45d5775aeb88646a63db027947246f)
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
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
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
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