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