xref: /illumos-gate/usr/src/cmd/fs.d/smbclnt/mount/mount.c (revision 5422785d352a2bb398daceab3d1898a8aa64d006)
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_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 is installed setuid root, and it adds SYS_MOUNT
186 	 * privilege here (if needed), and then restores the user's
187 	 * normal privileges.  When root runs this, it's a no-op.
188 	 */
189 	if (__init_suid_priv(0, PRIV_SYS_MOUNT, (char *)NULL) < 0) {
190 		(void) fprintf(stderr,
191 		    gettext("Insufficient privileges, "
192 		    "%s must be set-uid root\n"), argv[0]);
193 		exit(RET_ERR);
194 	}
195 
196 	if (argc == 2) {
197 		if (strcmp(argv[1], "-h") == 0) {
198 			usage();
199 		} else if (strcmp(argv[1], "-v") == 0) {
200 			errx(EX_OK, gettext("version %d.%d.%d"),
201 			    SMBFS_VERSION / 100000,
202 			    (SMBFS_VERSION % 10000) / 1000,
203 			    (SMBFS_VERSION % 1000) / 100);
204 		}
205 	}
206 	if (argc < 3)
207 		usage();
208 
209 	/* Debugging support. */
210 	if ((env = getenv("SMBFS_DEBUG")) != NULL) {
211 		smb_debug = atoi(env);
212 		if (smb_debug < 1)
213 			smb_debug = 1;
214 	}
215 
216 	error = smb_lib_init();
217 	if (error)
218 		exit(RET_ERR);
219 
220 	mnt.mnt_mntopts = optbuf;
221 
222 	bzero(&mdata, sizeof (mdata));
223 	mdata.version = SMBFS_VERSION;		/* smbfs mount version */
224 	mdata.uid = (uid_t)-1;
225 	mdata.gid = (gid_t)-1;
226 
227 	error = smb_ctx_alloc(&ctx);
228 	if (error)
229 		exit(RET_ERR);
230 
231 	/*
232 	 * Parse the UNC path so we have the server (etc.)
233 	 * that we need during rcfile+sharectl parsing.
234 	 */
235 	if (argc < 3)
236 		usage();
237 	error = smb_ctx_parseunc(ctx, argv[argc - 2],
238 	    SMBL_SHARE, SMBL_SHARE, USE_DISKDEV, NULL);
239 	if (error)
240 		exit(EX_OPT);
241 
242 	error = smb_ctx_readrc(ctx);
243 	if (error)
244 		exit(EX_OPT);
245 
246 	while ((opt = getopt(argc, argv, "ro:Oq")) != -1) {
247 		switch (opt) {
248 		case 'O':
249 			Oflg++;
250 			break;
251 
252 		case 'q':
253 			qflg++;
254 			break;
255 
256 		case 'r':
257 			mntflags |= MS_RDONLY;
258 			break;
259 
260 		case 'o': {
261 			char *nextopt, *comma, *sopt;
262 			int ret;
263 
264 			for (sopt = optarg; sopt != NULL; sopt = nextopt) {
265 				comma = strchr(sopt, ',');
266 				if (comma) {
267 					nextopt = comma + 1;
268 					*comma = '\0';
269 				} else
270 					nextopt = NULL;
271 				ret = setsubopt(ctx, &mdata, sopt);
272 				if (ret != 0)
273 					exit(EX_OPT);
274 				/* undo changes to optarg */
275 				if (comma)
276 					*comma = ',';
277 			}
278 			break;
279 		}
280 
281 		case '?':
282 		default:
283 			usage();
284 		}
285 	}
286 
287 	if (Oflg)
288 		mntflags |= MS_OVERLAY;
289 
290 	if (mntflags & MS_RDONLY) {
291 		char *p;
292 		/* convert "rw"->"ro" */
293 		if (p = strstr(optbuf, "rw")) {
294 			if (*(p+2) == ',' || *(p+2) == '\0')
295 				*(p+1) = 'o';
296 		}
297 	}
298 
299 	if (optind + 2 != argc)
300 		usage();
301 
302 	(void) snprintf(special, sizeof (special), "//%s/%s",
303 	    ctx->ct_fullserver, ctx->ct_origshare);
304 
305 	mnt.mnt_special = special;
306 	mnt.mnt_mountp = argv[optind+1];
307 
308 	if ((realpath(argv[optind+1], mount_point) == NULL) ||
309 	    (stat(mount_point, &st) == -1)) {
310 		err(EX_MNT, gettext("could not find mount point %s"),
311 		    argv[optind+1]);
312 	}
313 	if (!S_ISDIR(st.st_mode)) {
314 		errno = ENOTDIR;
315 		err(EX_MNT, gettext("can't mount on %s"), mount_point);
316 	}
317 
318 	/*
319 	 * Fill in mdata defaults.
320 	 */
321 	if (mdata.uid == (uid_t)-1)
322 		mdata.uid = getuid();
323 	if (mdata.gid == (gid_t)-1)
324 		mdata.gid = getgid();
325 	if (mdata.file_mode == 0)
326 		mdata.file_mode = S_IRWXU;
327 	if (mdata.dir_mode == 0) {
328 		mdata.dir_mode = mdata.file_mode;
329 		if (mdata.dir_mode & S_IRUSR)
330 			mdata.dir_mode |= S_IXUSR;
331 		if (mdata.dir_mode & S_IRGRP)
332 			mdata.dir_mode |= S_IXGRP;
333 		if (mdata.dir_mode & S_IROTH)
334 			mdata.dir_mode |= S_IXOTH;
335 	}
336 
337 	ctx->ct_ssn.ssn_owner = SMBM_ANY_OWNER;
338 	if (noprompt)
339 		ctx->ct_flags |= SMBCF_NOPWD;
340 
341 	/*
342 	 * Resolve the server address,
343 	 * setup derived defaults.
344 	 */
345 	error = smb_ctx_resolve(ctx);
346 	if (error)
347 		exit(RET_ERR);
348 
349 	/*
350 	 * Have server, share, etc. from above:
351 	 * smb_ctx_scan_argv, option settings.
352 	 * Get the session and tree.
353 	 */
354 again:
355 	error = smb_ctx_get_ssn(ctx);
356 	if (error == EAUTH && noprompt == 0) {
357 		err2 = smb_get_authentication(ctx);
358 		if (err2 == 0)
359 			goto again;
360 	}
361 	if (error) {
362 		smb_error(gettext("//%s: login failed"),
363 		    error, ctx->ct_fullserver);
364 		exit(RET_ERR);
365 	}
366 
367 	error = smb_ctx_get_tree(ctx);
368 	if (error) {
369 		smb_error(gettext("//%s/%s: tree connect failed"),
370 		    error, ctx->ct_fullserver, ctx->ct_origshare);
371 		exit(RET_ERR);
372 	}
373 
374 	/*
375 	 * Have tree connection, now mount it.
376 	 */
377 	mdata.devfd = ctx->ct_dev_fd;
378 
379 	/* Need sys_mount privilege for the mount call. */
380 	(void) __priv_bracket(PRIV_ON);
381 	err2 = mount(mnt.mnt_special, mnt.mnt_mountp,
382 	    mntflags, fstype, &mdata, sizeof (mdata),
383 	    mnt.mnt_mntopts, MAX_MNTOPT_STR);
384 	(void) __priv_bracket(PRIV_OFF);
385 
386 	if (err2 < 0) {
387 		if (errno != ENOENT) {
388 			err(EX_MNT, gettext("mount_smbfs: %s"),
389 			    mnt.mnt_mountp);
390 		} else {
391 			struct stat sb;
392 			if (stat(mnt.mnt_mountp, &sb) < 0 &&
393 			    errno == ENOENT)
394 				err(EX_MNT, gettext("mount_smbfs: %s"),
395 				    mnt.mnt_mountp);
396 			else
397 				err(EX_MNT, gettext("mount_smbfs: %s"),
398 				    mnt.mnt_special);
399 		}
400 	}
401 
402 	smb_ctx_free(ctx);
403 	return (0);
404 }
405 
406 #define	bad(val) (val == NULL || !isdigit(*val))
407 
408 int
409 setsubopt(smb_ctx_t *ctx, struct smbfs_args *mdatap, char *subopt)
410 {
411 	char *equals, *optarg;
412 	struct passwd *pwd;
413 	struct group *grp;
414 	long val;
415 	int rc = EX_OK;
416 	int index;
417 	char *p;
418 
419 	equals = strchr(subopt, '=');
420 	if (equals) {
421 		*equals = '\0';
422 		optarg = equals + 1;
423 	} else
424 		optarg = NULL;
425 
426 	for (index = 0; optlist[index] != NULL; index++) {
427 		if (strcmp(subopt, optlist[index]) == 0)
428 			break;
429 	}
430 
431 	/*
432 	 * Note: if the option was unknown, index will
433 	 * point to the NULL at the end of optlist[],
434 	 * and we'll take the switch default.
435 	 */
436 
437 	switch (index) {
438 
439 	case OPT_ACL:
440 	case OPT_NOACL:
441 	case OPT_SUID:
442 	case OPT_NOSUID:
443 	case OPT_DEVICES:
444 	case OPT_NODEVICES:
445 	case OPT_SETUID:
446 	case OPT_NOSETUID:
447 	case OPT_EXEC:
448 	case OPT_NOEXEC:
449 	case OPT_XATTR:
450 	case OPT_NOXATTR:
451 		/*
452 		 * These options are handled via the
453 		 * generic option string mechanism.
454 		 * None of these take an optarg.
455 		 */
456 		if (optarg != NULL)
457 			goto badval;
458 		(void) strlcat(optbuf, ",", sizeof (optbuf));
459 		if (strlcat(optbuf, subopt, sizeof (optbuf)) >=
460 		    sizeof (optbuf)) {
461 			if (!qflg)
462 				warnx(gettext("option string too long"));
463 			rc = EX_OPT;
464 		}
465 		break;
466 
467 	/*
468 	 * OPT_RO, OPT_RW, are actually generic too,
469 	 * but we use the mntflags for these, and
470 	 * then update the options string later.
471 	 */
472 	case OPT_RO:
473 		mntflags |= MS_RDONLY;
474 		break;
475 	case OPT_RW:
476 		mntflags &= ~MS_RDONLY;
477 		break;
478 
479 	/*
480 	 * NFS-derived options for attribute cache
481 	 * handling (disable, set min/max timeouts)
482 	 */
483 	case OPT_NOAC:
484 		mdatap->flags |= SMBFS_MF_NOAC;
485 		break;
486 
487 	case OPT_ACTIMEO:
488 		errno = 0;
489 		val = strtol(optarg, &p, 10);
490 		if (errno || *p != 0)
491 			goto badval;
492 		mdatap->acdirmin = mdatap->acregmin = val;
493 		mdatap->acdirmax = mdatap->acregmax = val;
494 		mdatap->flags |= SMBFS_MF_ACDIRMAX;
495 		mdatap->flags |= SMBFS_MF_ACREGMAX;
496 		mdatap->flags |= SMBFS_MF_ACDIRMIN;
497 		mdatap->flags |= SMBFS_MF_ACREGMIN;
498 		break;
499 
500 	case OPT_ACREGMIN:
501 		errno = 0;
502 		val = strtol(optarg, &p, 10);
503 		if (errno || *p != 0)
504 			goto badval;
505 		mdatap->acregmin = val;
506 		mdatap->flags |= SMBFS_MF_ACREGMIN;
507 		break;
508 
509 	case OPT_ACREGMAX:
510 		errno = 0;
511 		val = strtol(optarg, &p, 10);
512 		if (errno || *p != 0)
513 			goto badval;
514 		mdatap->acregmax = val;
515 		mdatap->flags |= SMBFS_MF_ACREGMAX;
516 		break;
517 
518 	case OPT_ACDIRMIN:
519 		errno = 0;
520 		val = strtol(optarg, &p, 10);
521 		if (errno || *p != 0)
522 			goto badval;
523 		mdatap->acdirmin = val;
524 		mdatap->flags |= SMBFS_MF_ACDIRMIN;
525 		break;
526 
527 	case OPT_ACDIRMAX:
528 		errno = 0;
529 		val = strtol(optarg, &p, 10);
530 		if (errno || *p != 0)
531 			goto badval;
532 		mdatap->acdirmax = val;
533 		mdatap->flags |= SMBFS_MF_ACDIRMAX;
534 		break;
535 
536 	/*
537 	 * SMBFS-specific options.  Some of these
538 	 * don't go through the mount system call,
539 	 * but just set libsmbfs options.
540 	 */
541 	case OPT_DOMAIN:
542 		if (smb_ctx_setdomain(ctx, optarg, B_TRUE) != 0)
543 			rc = EX_OPT;
544 		break;
545 
546 	case OPT_USER:
547 		if (smb_ctx_setuser(ctx, optarg, B_TRUE) != 0)
548 			rc = EX_OPT;
549 		break;
550 
551 	case OPT_UID:
552 		pwd = isdigit(optarg[0]) ?
553 		    getpwuid(atoi(optarg)) : getpwnam(optarg);
554 		if (pwd == NULL) {
555 			if (!qflg)
556 				warnx(gettext("unknown user '%s'"), optarg);
557 			rc = EX_OPT;
558 		} else {
559 			mdatap->uid = pwd->pw_uid;
560 		}
561 		break;
562 
563 	case OPT_GID:
564 		grp = isdigit(optarg[0]) ?
565 		    getgrgid(atoi(optarg)) : getgrnam(optarg);
566 		if (grp == NULL) {
567 			if (!qflg)
568 				warnx(gettext("unknown group '%s'"), optarg);
569 			rc = EX_OPT;
570 		} else {
571 			mdatap->gid = grp->gr_gid;
572 		}
573 		break;
574 
575 	case OPT_DIRPERMS:
576 		errno = 0;
577 		val = strtol(optarg, &p, 8);
578 		if (errno || *p != 0)
579 			goto badval;
580 		mdatap->dir_mode = val;
581 		break;
582 
583 	case OPT_FILEPERMS:
584 		errno = 0;
585 		val = strtol(optarg, &p, 8);
586 		if (errno || *p != 0)
587 			goto badval;
588 		mdatap->file_mode = val;
589 		break;
590 
591 	case OPT_NOPROMPT:
592 		noprompt++;
593 		break;
594 
595 	default:
596 	badopt:
597 		if (!qflg)
598 			warnx(gettext("unknown option %s"), subopt);
599 		rc = EX_OPT;
600 		break;
601 
602 	badval:
603 		if (!qflg)
604 			warnx(gettext("invalid value for %s"), subopt);
605 		rc = EX_OPT;
606 		break;
607 	}
608 
609 	/* Undo changes made to subopt */
610 	if (equals)
611 		*equals = '=';
612 
613 	return (rc);
614 }
615 
616 static void
617 usage(void)
618 {
619 	(void) fprintf(stderr, "%s\n",
620 	gettext("usage: mount -F smbfs [-Orq] [-o option[,option]]"
621 	"	//[workgroup;][user[:password]@]server[/share] path"));
622 
623 	exit(EX_OPT);
624 }
625