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 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 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 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