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