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
main(int argc,char * argv[])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
setsubopt(smb_ctx_t * ctx,struct smbfs_args * mdatap,char * subopt)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
usage(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