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