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