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 2009 Sun Microsystems, Inc. All rights reserved. 37 * Use is subject to license terms. 38 */ 39 40 #include <sys/param.h> 41 #include <sys/stat.h> 42 #include <sys/errno.h> 43 #include <sys/mount.h> 44 45 #include <stdio.h> 46 #include <string.h> 47 #include <strings.h> 48 #include <pwd.h> 49 #include <grp.h> 50 #include <unistd.h> 51 #include <ctype.h> 52 #include <stdlib.h> 53 #include <errno.h> 54 #include <err.h> 55 #include <sysexits.h> 56 #include <libintl.h> 57 #include <locale.h> 58 #include <libscf.h> 59 60 #include <sys/mntent.h> 61 #include <sys/mnttab.h> 62 63 #include <cflib.h> 64 65 #include <netsmb/smb.h> 66 #include <netsmb/smb_lib.h> 67 68 #include <sys/fs/smbfs_mount.h> 69 70 #include "mntopts.h" 71 72 static char mount_point[MAXPATHLEN + 1]; 73 static void usage(void); 74 static int setsubopt(int, char *, struct smbfs_args *); 75 76 /* smbfs options */ 77 #define MNTOPT_RETRY "retry" 78 #define MNTOPT_TIMEOUT "timeout" 79 #define MNTOPT_DIRPERMS "dirperms" 80 #define MNTOPT_FILEPERMS "fileperms" 81 #define MNTOPT_GID "gid" 82 #define MNTOPT_UID "uid" 83 #define MNTOPT_NOPROMPT "noprompt" 84 85 #define OPT_RETRY 1 86 #define OPT_TIMEOUT 2 87 #define OPT_DIRPERMS 3 88 #define OPT_FILEPERMS 4 89 #define OPT_GID 5 90 #define OPT_UID 6 91 #define OPT_NOPROMPT 7 92 93 /* generic VFS options */ 94 #define OPT_RO 10 95 #define OPT_RW 11 96 #define OPT_SUID 12 97 #define OPT_NOSUID 13 98 #define OPT_DEVICES 14 99 #define OPT_NODEVICES 15 100 #define OPT_SETUID 16 101 #define OPT_NOSETUID 17 102 #define OPT_EXEC 18 103 #define OPT_NOEXEC 19 104 105 struct smbfsopts { 106 char *name; 107 int index; 108 }; 109 110 struct smbfsopts opts[] = { 111 {MNTOPT_RETRY, OPT_RETRY}, 112 {MNTOPT_TIMEOUT, OPT_TIMEOUT}, 113 {MNTOPT_DIRPERMS, OPT_DIRPERMS}, 114 {MNTOPT_FILEPERMS, OPT_FILEPERMS}, 115 {MNTOPT_GID, OPT_GID}, 116 {MNTOPT_UID, OPT_UID}, 117 {MNTOPT_NOPROMPT, OPT_NOPROMPT}, 118 {MNTOPT_RO, OPT_RO}, 119 {MNTOPT_RW, OPT_RW}, 120 {MNTOPT_SUID, OPT_SUID}, 121 {MNTOPT_NOSUID, OPT_NOSUID}, 122 {MNTOPT_DEVICES, OPT_DEVICES}, 123 {MNTOPT_NODEVICES, OPT_NODEVICES}, 124 {MNTOPT_SETUID, OPT_SETUID}, 125 {MNTOPT_NOSETUID, OPT_NOSETUID}, 126 {MNTOPT_EXEC, OPT_EXEC}, 127 {MNTOPT_NOEXEC, OPT_NOEXEC}, 128 {NULL, 0} 129 }; 130 131 static int Oflg = 0; /* Overlay mounts */ 132 static int qflg = 0; /* quiet - don't print warnings on bad options */ 133 static int ro = 0; /* read-only mount */ 134 static int noprompt = 0; /* don't prompt for password */ 135 static int retry = -1; 136 static int timeout = -1; 137 138 #define RET_ERR 33 139 #define SERVICE "svc:/network/smb/client:default" 140 141 int 142 main(int argc, char *argv[]) 143 { 144 struct smb_ctx sctx, *ctx = &sctx; 145 struct smbfs_args mdata; 146 struct stat st; 147 int opt, error, mntflags; 148 struct mnttab mnt; 149 struct mnttab *mntp = &mnt; 150 char optbuf[MAX_MNTOPT_STR]; 151 static char *fstype = MNTTYPE_SMBFS; 152 char *env, *state; 153 154 (void) setlocale(LC_ALL, ""); 155 #if !defined(TEXT_DOMAIN) 156 #define TEXT_DOMAIN "SYS_TEST" 157 #endif 158 (void) textdomain(TEXT_DOMAIN); 159 if (argc == 2) { 160 if (strcmp(argv[1], "-h") == 0) { 161 usage(); 162 } else if (strcmp(argv[1], "-v") == 0) { 163 errx(EX_OK, gettext("version %d.%d.%d"), 164 SMBFS_VERSION / 100000, 165 (SMBFS_VERSION % 10000) / 1000, 166 (SMBFS_VERSION % 1000) / 100); 167 } 168 } 169 if (argc < 3) 170 usage(); 171 172 state = smf_get_state(SERVICE); 173 if (state == NULL || strcmp(state, SCF_STATE_STRING_ONLINE) != 0) { 174 fprintf(stderr, 175 gettext("mount_smbfs: service \"%s\" not enabled.\n"), 176 SERVICE); 177 exit(1); 178 } 179 180 /* Debugging support. */ 181 if ((env = getenv("SMBFS_DEBUG")) != NULL) { 182 smb_debug = atoi(env); 183 if (smb_debug < 1) 184 smb_debug = 1; 185 } 186 187 error = smb_lib_init(); 188 if (error) 189 exit(error); 190 191 mnt.mnt_mntopts = optbuf; 192 mntflags = MS_DATA; 193 bzero(&mdata, sizeof (mdata)); 194 mdata.uid = (uid_t)-1; 195 mdata.gid = (gid_t)-1; 196 mdata.caseopt = SMB_CS_NONE; 197 198 error = smb_ctx_init(ctx, argc, argv, SMBL_SHARE, SMBL_SHARE, 199 SMB_ST_DISK); 200 if (error) 201 exit(error); 202 error = smb_ctx_readrc(ctx); 203 if (error) 204 exit(error); 205 206 while ((opt = getopt(argc, argv, "ro:Oq")) != -1) { 207 switch (opt) { 208 case 'O': 209 Oflg++; 210 break; 211 212 case 'q': 213 qflg++; 214 break; 215 216 case 'r': 217 ro++; 218 break; 219 220 case 'o': { 221 char *nextopt, *comma, *equals, *sopt, *soptval; 222 int i, ret; 223 224 if (strlen(optarg) >= MAX_MNTOPT_STR) { 225 if (!qflg) 226 warnx(gettext( 227 "option string too long")); 228 exit(RET_ERR); 229 } 230 for (sopt = optarg; sopt != NULL; sopt = nextopt) { 231 comma = strchr(sopt, ','); 232 if (comma) { 233 nextopt = comma + 1; 234 *comma = '\0'; 235 } else 236 nextopt = NULL; 237 equals = strchr(sopt, '='); 238 if (equals) { 239 soptval = equals + 1; 240 *equals = '\0'; 241 } else 242 soptval = NULL; 243 for (i = 0; opts[i].name != NULL; i++) { 244 if (strcmp(sopt, opts[i].name) == 0) 245 break; 246 } 247 if (opts[i].name == NULL) { 248 if (equals) 249 *equals = '='; 250 if (!qflg) 251 errx(RET_ERR, gettext( 252 "Bad option '%s'"), sopt); 253 if (comma) 254 *comma = ','; 255 continue; 256 } 257 ret = setsubopt(opts[i].index, soptval, &mdata); 258 if (ret != 0) 259 exit(RET_ERR); 260 if (equals) 261 *equals = '='; 262 (void) strcat(mnt.mnt_mntopts, sopt); 263 if (comma) 264 *comma = ','; 265 } 266 break; 267 } 268 269 case '?': 270 default: 271 usage(); 272 } 273 } 274 275 if (Oflg) 276 mntflags |= MS_OVERLAY; 277 278 if (ro) { 279 char *p; 280 281 mntflags |= MS_RDONLY; 282 /* convert "rw"->"ro" */ 283 if (p = strstr(mntp->mnt_mntopts, "rw")) { 284 if (*(p+2) == ',' || *(p+2) == '\0') 285 *(p+1) = 'o'; 286 } 287 } 288 289 mnt.mnt_special = argv[optind]; 290 mnt.mnt_mountp = argv[optind+1]; 291 292 mdata.version = SMBFS_VERSION; /* smbfs mount version */ 293 294 if (optind == argc - 2) 295 optind++; 296 297 if (optind != argc - 1) 298 usage(); 299 realpath(unpercent(argv[optind]), mount_point); 300 301 if (stat(mount_point, &st) == -1) 302 err(EX_OSERR, gettext("could not find mount point %s"), 303 mount_point); 304 if (!S_ISDIR(st.st_mode)) { 305 errno = ENOTDIR; 306 err(EX_OSERR, gettext("can't mount on %s"), mount_point); 307 } 308 309 /* 310 * Fill in mdata defaults. 311 */ 312 if (mdata.uid == (uid_t)-1) 313 mdata.uid = getuid(); 314 if (mdata.gid == (gid_t)-1) 315 mdata.gid = getgid(); 316 if (mdata.file_mode == 0) 317 mdata.file_mode = S_IRWXU; 318 if (mdata.dir_mode == 0) { 319 mdata.dir_mode = mdata.file_mode; 320 if (mdata.dir_mode & S_IRUSR) 321 mdata.dir_mode |= S_IXUSR; 322 if (mdata.dir_mode & S_IRGRP) 323 mdata.dir_mode |= S_IXGRP; 324 if (mdata.dir_mode & S_IROTH) 325 mdata.dir_mode |= S_IXOTH; 326 } 327 328 /* 329 * XXX: The driver can fill these in more reliably, 330 * so why do we set them here? (Just set both = -1) 331 */ 332 ctx->ct_ssn.ioc_owner = ctx->ct_sh.ioc_owner = getuid(); 333 ctx->ct_ssn.ioc_group = ctx->ct_sh.ioc_group = getgid(); 334 opt = 0; 335 if (mdata.dir_mode & S_IXGRP) 336 opt |= SMBM_EXECGRP; 337 if (mdata.dir_mode & S_IXOTH) 338 opt |= SMBM_EXECOTH; 339 ctx->ct_ssn.ioc_rights |= opt; 340 ctx->ct_sh.ioc_rights |= opt; 341 if (noprompt) 342 ctx->ct_flags |= SMBCF_NOPWD; 343 if (retry != -1) 344 ctx->ct_ssn.ioc_retrycount = retry; 345 if (timeout != -1) 346 ctx->ct_ssn.ioc_timeout = timeout; 347 348 /* 349 * If we got our password from the keychain and get an 350 * authorization error, we come back here to obtain a new 351 * password from user input. 352 */ 353 reauth: 354 error = smb_ctx_resolve(ctx); 355 if (error) 356 exit(error); 357 358 mdata.devfd = ctx->ct_fd; /* file descriptor */ 359 360 again: 361 error = smb_ctx_lookup(ctx, SMBL_SHARE, SMBLK_CREATE); 362 if (error == ENOENT && ctx->ct_origshare) { 363 strcpy(ctx->ct_sh.ioc_share, ctx->ct_origshare); 364 free(ctx->ct_origshare); 365 ctx->ct_origshare = NULL; 366 goto again; /* try again using share name as given */ 367 } 368 if (ctx->ct_flags & SMBCF_KCFOUND && smb_autherr(error)) { 369 ctx->ct_ssn.ioc_password[0] = '\0'; 370 smb_error(gettext("main(lookup): bad keychain entry"), 0); 371 ctx->ct_flags |= SMBCF_KCBAD; 372 goto reauth; 373 } 374 if (error) 375 exit(error); 376 377 mdata.version = SMBFS_VERSION; 378 mdata.devfd = ctx->ct_fd; 379 380 if (mount(mntp->mnt_special, mntp->mnt_mountp, 381 mntflags, fstype, &mdata, sizeof (mdata), 382 mntp->mnt_mntopts, MAX_MNTOPT_STR) < 0) { 383 if (errno != ENOENT) { 384 err(EX_OSERR, gettext("mount_smbfs: %s"), 385 mntp->mnt_mountp); 386 } else { 387 struct stat sb; 388 if (stat(mntp->mnt_mountp, &sb) < 0 && 389 errno == ENOENT) 390 err(EX_OSERR, gettext("mount_smbfs: %s"), 391 mntp->mnt_mountp); 392 else 393 err(EX_OSERR, gettext("mount_smbfs: %s"), 394 mntp->mnt_special); 395 396 error = smb_ctx_tdis(ctx); 397 if (error) /* unable to clean up?! */ 398 exit(error); 399 } 400 } 401 402 smb_ctx_done(ctx); 403 if (error) { 404 smb_error(gettext("mount error: %s"), error, mount_point); 405 exit(errno); 406 } 407 return (0); 408 } 409 410 int 411 setsubopt(int index, char *optarg, struct smbfs_args *mdatap) 412 { 413 struct passwd *pwd; 414 struct group *grp; 415 long l; 416 int err = 0; 417 char *next; 418 419 switch (index) { 420 case OPT_RO: 421 case OPT_RW: 422 case OPT_SUID: 423 case OPT_NOSUID: 424 case OPT_DEVICES: 425 case OPT_NODEVICES: 426 case OPT_SETUID: 427 case OPT_NOSETUID: 428 case OPT_EXEC: 429 case OPT_NOEXEC: 430 /* We don't have to handle generic options here */ 431 return (0); 432 case OPT_UID: 433 pwd = isdigit(optarg[0]) ? 434 getpwuid(atoi(optarg)) : getpwnam(optarg); 435 if (pwd == NULL) { 436 if (!qflg) 437 warnx(gettext("unknown user '%s'"), optarg); 438 err = -1; 439 } else { 440 mdatap->uid = pwd->pw_uid; 441 } 442 break; 443 case OPT_GID: 444 grp = isdigit(optarg[0]) ? 445 getgrgid(atoi(optarg)) : getgrnam(optarg); 446 if (grp == NULL) { 447 if (!qflg) 448 warnx(gettext("unknown group '%s'"), optarg); 449 err = -1; 450 } else { 451 mdatap->gid = grp->gr_gid; 452 } 453 break; 454 case OPT_DIRPERMS: 455 errno = 0; 456 l = strtol(optarg, &next, 8); 457 if (errno || *next != 0) { 458 if (!qflg) 459 warnx(gettext( 460 "invalid value for directory mode")); 461 err = -1; 462 } else { 463 mdatap->dir_mode = l; 464 } 465 break; 466 case OPT_FILEPERMS: 467 errno = 0; 468 l = strtol(optarg, &next, 8); 469 if (errno || *next != 0) { 470 if (!qflg) 471 warnx(gettext("invalid value for file mode")); 472 err = -1; 473 } else { 474 mdatap->file_mode = l; 475 } 476 break; 477 case OPT_RETRY: 478 retry = atoi(optarg); 479 break; 480 case OPT_TIMEOUT: 481 timeout = atoi(optarg); 482 break; 483 case OPT_NOPROMPT: 484 noprompt++; 485 } 486 return (err); 487 } 488 489 static void 490 usage(void) 491 { 492 fprintf(stderr, "%s\n", 493 gettext("usage: mount -F smbfs [-Orq] [-o option[,option]]" 494 " //[workgroup;][user[:password]@]server[/share] path")); 495 496 exit(EX_USAGE); 497 } 498