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 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 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 */ 363 again: 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 417 int 418 setsubopt(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 625 static void 626 usage(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