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
69extern char *optarg;
70extern int optind;
71
72static char mount_point[MAXPATHLEN + 1];
73static void usage(void);
74static int setsubopt(smb_ctx_t *, struct smbfs_args *, char *);
75
76const 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
141static int Oflg = 0;    /* Overlay mounts */
142static int qflg = 0;    /* quiet - don't print warnings on bad options */
143static int noprompt = 0;	/* don't prompt for password */
144
145/* Note: smbfs uses _both_ kinds of options. */
146static 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
155struct smbfs_args mdata;
156struct 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 */
164char optbuf[MAX_MNTOPT_STR] = "rw";
165char special[MAXPATHLEN];
166
167int
168main(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	 */
363again:
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
417int
418setsubopt(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
625static void
626usage(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