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