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