1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. 23 * Copyright 2013 Nexenta Systems, Inc. All rights reserved. 24 */ 25 26 #include <sys/param.h> 27 #include <string.h> 28 #include <stdlib.h> 29 #include <stdio.h> 30 #include <pwd.h> 31 #include <nss_dbdefs.h> 32 #include <assert.h> 33 #include <strings.h> 34 #include <sys/stat.h> 35 #include <sys/idmap.h> 36 #include <smbsrv/libsmb.h> 37 #include <smbsrv/libmlsvc.h> 38 #include <smbsrv/smbinfo.h> 39 40 #define SMB_AUTOHOME_KEYSIZ 128 41 #define SMB_AUTOHOME_MAXARG 4 42 #define SMB_AUTOHOME_BUFSIZ 2048 43 44 typedef struct smb_autohome_info { 45 struct smb_autohome_info *magic1; 46 FILE *fp; 47 smb_autohome_t autohome; 48 char buf[SMB_AUTOHOME_BUFSIZ]; 49 char *argv[SMB_AUTOHOME_MAXARG]; 50 int lineno; 51 struct smb_autohome_info *magic2; 52 } smb_autohome_info_t; 53 54 static smb_autohome_info_t smb_ai; 55 56 static smb_autohome_t *smb_autohome_make_entry(smb_autohome_info_t *); 57 static char *smb_autohome_keysub(const char *, char *, int); 58 static smb_autohome_info_t *smb_autohome_getinfo(void); 59 static smb_autohome_t *smb_autohome_lookup(const char *); 60 static void smb_autohome_setent(void); 61 static void smb_autohome_endent(void); 62 static smb_autohome_t *smb_autohome_getent(const char *); 63 static void smb_autohome_parse_options(smb_share_t *); 64 static int smb_autohome_add_private(const char *, uid_t, gid_t); 65 66 /* 67 * Add an autohome share. See smb_autohome(4) for details. 68 * 69 * If share directory contains backslash path separators, they will 70 * be converted to forward slash to support NT/DOS path style for 71 * autohome shares. 72 * 73 * We need to serialize calls to smb_autohome_lookup because it 74 * operates on the global smb_ai structure. 75 */ 76 void 77 smb_autohome_add(const smb_token_t *token) 78 { 79 80 char *username; 81 struct passwd pw; 82 char buf[NSS_LINELEN_PASSWD]; 83 uid_t uid; 84 gid_t gid; 85 86 if (token->tkn_flags & SMB_ATF_ANON) 87 return; 88 89 uid = token->tkn_user.i_id; 90 gid = token->tkn_primary_grp.i_id; 91 92 if (IDMAP_ID_IS_EPHEMERAL(uid)) { 93 username = token->tkn_account_name; 94 assert(username); 95 } else { 96 if (getpwuid_r(uid, &pw, buf, sizeof (buf)) == NULL) { 97 syslog(LOG_ERR, "unable to determine name for " \ 98 "UID: %u\n", uid); 99 return; 100 } 101 username = pw.pw_name; 102 } 103 104 if (smb_autohome_add_private(username, uid, gid) != NERR_Success) { 105 if (!smb_isstrlwr(username)) { 106 (void) smb_strlwr(username); 107 (void) smb_autohome_add_private(username, uid, gid); 108 } 109 } 110 } 111 112 /* 113 * Remove an autohome share. 114 */ 115 void 116 smb_autohome_remove(const char *username) 117 { 118 smb_share_t si; 119 120 assert(username); 121 122 if (smb_shr_get((char *)username, &si) == NERR_Success) { 123 if (si.shr_flags & SMB_SHRF_AUTOHOME) 124 (void) smb_shr_remove((char *)username); 125 } 126 } 127 128 /* 129 * An autohome share is not created if a static share using the same name 130 * already exists. Autohome shares will be added for each login attempt. 131 * 132 * Calling smb_shr_get() may return the first argument in all lower case so 133 * a copy is passed in instead. 134 * 135 * We need to serialize calls to smb_autohome_lookup because it 136 * operates on the global smb_ai structure. 137 */ 138 static int 139 smb_autohome_add_private(const char *username, uid_t uid, gid_t gid) 140 { 141 static mutex_t autohome_mutex; 142 smb_share_t si; 143 smb_autohome_t *ai; 144 char shr_name[MAXNAMELEN]; 145 146 (void) strlcpy(shr_name, username, sizeof (shr_name)); 147 148 if (smb_shr_get(shr_name, &si) == NERR_Success) { 149 if ((si.shr_flags & SMB_SHRF_AUTOHOME) == 0) 150 return (NERR_Success); 151 152 (void) smb_shr_add(&si); 153 return (NERR_Success); 154 } 155 156 (void) mutex_lock(&autohome_mutex); 157 158 if ((ai = smb_autohome_lookup(username)) == NULL) { 159 (void) mutex_unlock(&autohome_mutex); 160 return (NERR_ItemNotFound); 161 } 162 163 bzero(&si, sizeof (smb_share_t)); 164 (void) strlcpy(si.shr_path, ai->ah_path, MAXPATHLEN); 165 (void) strsubst(si.shr_path, '\\', '/'); 166 167 (void) strlcpy(si.shr_name, username, MAXNAMELEN); 168 (void) strlcpy(si.shr_container, ai->ah_container, MAXPATHLEN); 169 (void) strlcpy(si.shr_cmnt, "Autohome", SMB_SHARE_CMNT_MAX); 170 smb_autohome_parse_options(&si); 171 si.shr_flags |= SMB_SHRF_TRANS | SMB_SHRF_AUTOHOME; 172 si.shr_uid = uid; 173 si.shr_gid = gid; 174 175 (void) mutex_unlock(&autohome_mutex); 176 177 return (smb_shr_add(&si)); 178 } 179 180 /* 181 * Search the autohome database for the specified name. The name cannot 182 * be an empty string or begin with * or +. 183 * 1. Search the file for the specified name. 184 * 2. Check for the wildcard rule and, if present, treat it as a match. 185 * 3. Check for the nsswitch rule and, if present, lookup the name 186 * via the name services. Note that the nsswitch rule will never 187 * be applied if the wildcard rule is present. 188 * 189 * Returns a pointer to the entry on success or null on failure. 190 */ 191 static smb_autohome_t * 192 smb_autohome_lookup(const char *name) 193 { 194 struct passwd *pw; 195 smb_autohome_t *ah = NULL; 196 197 if (name == NULL) 198 return (NULL); 199 200 if (*name == '\0' || *name == '*' || *name == '+') 201 return (NULL); 202 203 smb_autohome_setent(); 204 205 while ((ah = smb_autohome_getent(name)) != NULL) { 206 if (strcasecmp(ah->ah_name, name) == 0) 207 break; 208 } 209 210 if (ah == NULL) { 211 smb_autohome_setent(); 212 213 while ((ah = smb_autohome_getent(name)) != NULL) { 214 if (strcasecmp(ah->ah_name, "*") == 0) { 215 ah->ah_name = (char *)name; 216 break; 217 } 218 } 219 } 220 221 if (ah == NULL) { 222 smb_autohome_setent(); 223 224 while ((ah = smb_autohome_getent("+nsswitch")) != NULL) { 225 if (strcasecmp("+nsswitch", ah->ah_name) != 0) 226 continue; 227 if ((pw = getpwnam(name)) == NULL) { 228 ah = NULL; 229 break; 230 } 231 232 ah->ah_name = pw->pw_name; 233 234 if (ah->ah_path) 235 ah->ah_container = ah->ah_path; 236 237 ah->ah_path = pw->pw_dir; 238 break; 239 } 240 } 241 242 smb_autohome_endent(); 243 return (ah); 244 } 245 246 /* 247 * Open or rewind the autohome database. 248 */ 249 static void 250 smb_autohome_setent(void) 251 { 252 smb_autohome_info_t *si; 253 char path[MAXNAMELEN]; 254 char filename[MAXNAMELEN]; 255 int rc; 256 257 if ((si = smb_autohome_getinfo()) != 0) { 258 (void) fseek(si->fp, 0L, SEEK_SET); 259 si->lineno = 0; 260 return; 261 } 262 263 if ((si = &smb_ai) == 0) 264 return; 265 266 rc = smb_config_getstr(SMB_CI_AUTOHOME_MAP, path, sizeof (path)); 267 if (rc != SMBD_SMF_OK) 268 return; 269 270 (void) snprintf(filename, MAXNAMELEN, "%s/%s", path, 271 SMB_AUTOHOME_FILE); 272 273 if ((si->fp = fopen(filename, "r")) == NULL) 274 return; 275 276 si->magic1 = si; 277 si->magic2 = si; 278 si->lineno = 0; 279 } 280 281 /* 282 * Close the autohome database and invalidate the autohome info. 283 * We can't zero the whole info structure because the application 284 * should still have access to the data after the file is closed. 285 */ 286 static void 287 smb_autohome_endent(void) 288 { 289 smb_autohome_info_t *si; 290 291 if ((si = smb_autohome_getinfo()) != 0) { 292 (void) fclose(si->fp); 293 si->fp = 0; 294 si->magic1 = 0; 295 si->magic2 = 0; 296 } 297 } 298 299 /* 300 * Return the next entry in the autohome database, opening the file 301 * if necessary. Returns null on EOF or error. 302 * 303 * Note that we are not looking for the specified name. The name is 304 * only used for key substitution, so that the caller sees the entry 305 * in expanded form. 306 */ 307 static smb_autohome_t * 308 smb_autohome_getent(const char *name) 309 { 310 smb_autohome_info_t *si; 311 char *bp; 312 313 if ((si = smb_autohome_getinfo()) == 0) { 314 smb_autohome_setent(); 315 316 if ((si = smb_autohome_getinfo()) == 0) 317 return (0); 318 } 319 320 /* 321 * Find the next non-comment, non-empty line. 322 * Anything after a # is a comment and can be discarded. 323 * Discard a newline to avoid it being included in the parsing 324 * that follows. 325 * Leading and training whitespace is discarded, and replicated 326 * whitespace is compressed to simplify the token parsing, 327 * although strsep() deals with that better than strtok(). 328 */ 329 do { 330 if (fgets(si->buf, SMB_AUTOHOME_BUFSIZ, si->fp) == 0) 331 return (0); 332 333 ++si->lineno; 334 335 if ((bp = strpbrk(si->buf, "#\r\n")) != 0) 336 *bp = '\0'; 337 338 (void) trim_whitespace(si->buf); 339 bp = strcanon(si->buf, " \t"); 340 } while (*bp == '\0'); 341 342 (void) smb_autohome_keysub(name, si->buf, SMB_AUTOHOME_BUFSIZ); 343 return (smb_autohome_make_entry(si)); 344 } 345 346 /* 347 * Set up an autohome entry from the line buffer. The line should just 348 * contain tokens separated by single whitespace. The line format is: 349 * <username> <home-dir-path> <ADS container> 350 */ 351 static smb_autohome_t * 352 smb_autohome_make_entry(smb_autohome_info_t *si) 353 { 354 char *bp; 355 int i; 356 357 bp = si->buf; 358 359 for (i = 0; i < SMB_AUTOHOME_MAXARG; ++i) 360 si->argv[i] = NULL; 361 362 for (i = 0; i < SMB_AUTOHOME_MAXARG; ++i) { 363 do { 364 if ((si->argv[i] = strsep(&bp, " \t")) == NULL) 365 break; 366 } while (*(si->argv[i]) == '\0'); 367 368 if (si->argv[i] == NULL) 369 break; 370 } 371 372 if ((si->autohome.ah_name = si->argv[0]) == NULL) { 373 /* 374 * Sanity check: the name could be an empty 375 * string but it can't be a null pointer. 376 */ 377 return (0); 378 } 379 380 if ((si->autohome.ah_path = si->argv[1]) == NULL) 381 si->autohome.ah_path = ""; 382 383 if ((si->autohome.ah_container = si->argv[2]) == NULL) 384 si->autohome.ah_container = ""; 385 386 return (&si->autohome); 387 } 388 389 /* 390 * Substitute the ? and & map keys. 391 * ? is replaced by the first character of the name 392 * & is replaced by the whole name. 393 */ 394 static char * 395 smb_autohome_keysub(const char *name, char *buf, int buflen) 396 { 397 char key[SMB_AUTOHOME_KEYSIZ]; 398 char *ampersand; 399 char *tmp; 400 int bufsize = buflen; 401 402 (void) strlcpy(key, buf, SMB_AUTOHOME_KEYSIZ); 403 404 if ((tmp = strpbrk(key, " \t")) == NULL) 405 return (NULL); 406 407 *tmp = '\0'; 408 409 /* 410 * Substitution characters are not allowed in the key. 411 */ 412 if (strpbrk(key, "?&") != NULL) 413 return (NULL); 414 415 if (strcmp(key, "*") == 0 && name != NULL) 416 (void) strlcpy(key, name, SMB_AUTOHOME_KEYSIZ); 417 418 (void) strsubst(buf, '?', *key); 419 420 while ((ampersand = strchr(buf, '&')) != NULL) { 421 if ((tmp = strdup(ampersand + 1)) == NULL) 422 return (0); 423 424 bufsize = buflen - (ampersand - buf); 425 (void) strlcpy(ampersand, key, bufsize); 426 (void) strlcat(ampersand, tmp, bufsize); 427 free(tmp); 428 } 429 430 return (buf); 431 } 432 433 /* 434 * Get a pointer to the context buffer and validate it. 435 */ 436 static smb_autohome_info_t * 437 smb_autohome_getinfo(void) 438 { 439 smb_autohome_info_t *si; 440 441 if ((si = &smb_ai) == 0) 442 return (0); 443 444 if ((si->magic1 == si) && (si->magic2 == si) && (si->fp != NULL)) 445 return (si); 446 447 return (0); 448 } 449 450 /* 451 * Parse the options string, which contains a comma separated list of 452 * name-value pairs. One of the options may be an AD container, which 453 * is also a comma separated list of name-value pairs. For example, 454 * dn=ad,dn=sun,dn=com,ou=users 455 * 456 * All options other than the AD container will be extracted from 457 * shr_container and used to set share properties. 458 * On return, shr_container will contain the AD container string. 459 */ 460 static void 461 smb_autohome_parse_options(smb_share_t *si) 462 { 463 char buf[MAXPATHLEN]; 464 char **argv; 465 char **ap; 466 char *bp; 467 char *value; 468 boolean_t separator = B_FALSE; 469 int argc; 470 int i; 471 472 if (strlcpy(buf, si->shr_container, MAXPATHLEN) == 0) 473 return; 474 475 for (argc = 1, bp = si->shr_container; *bp != '\0'; ++bp) 476 if (*bp == ',') 477 ++argc; 478 479 if ((argv = calloc(argc + 1, sizeof (char *))) == NULL) 480 return; 481 482 ap = argv; 483 for (bp = buf, i = 0; i < argc; ++i) { 484 do { 485 if ((value = strsep(&bp, ",")) == NULL) 486 break; 487 } while (*value == '\0'); 488 489 if (value == NULL) 490 break; 491 492 *ap++ = value; 493 } 494 *ap = NULL; 495 496 si->shr_container[0] = '\0'; 497 bp = si->shr_container; 498 499 for (ap = argv; *ap != NULL; ++ap) { 500 value = *ap; 501 502 if (strncasecmp(value, "catia=", 6) == 0) { 503 smb_shr_sa_setflag((value + 6), si, SMB_SHRF_CATIA); 504 continue; 505 } 506 507 if (strncasecmp(value, "csc=", 4) == 0) { 508 smb_shr_sa_csc_option((value + 4), si); 509 continue; 510 } 511 512 if (strncasecmp(value, "abe=", 4) == 0) { 513 smb_shr_sa_setflag((value + 4), si, SMB_SHRF_ABE); 514 continue; 515 } 516 517 if (strncasecmp(value, "description=", 12) == 0) { 518 (void) strlcpy(si->shr_cmnt, (value + 12), 519 SMB_SHARE_CMNT_MAX); 520 continue; 521 } 522 523 if (strncasecmp(value, "rw=", 3) == 0) { 524 (void) strlcpy(si->shr_access_rw, (value + 3), 525 sizeof (si->shr_access_rw)); 526 continue; 527 } 528 529 if (strncasecmp(value, "ro=", 3) == 0) { 530 (void) strlcpy(si->shr_access_ro, (value + 3), 531 sizeof (si->shr_access_ro)); 532 continue; 533 } 534 535 if (strncasecmp(value, "none=", 5) == 0) { 536 (void) strlcpy(si->shr_access_none, (value + 5), 537 sizeof (si->shr_access_none)); 538 continue; 539 } 540 541 if (separator) 542 (void) strlcat(bp, ",", MAXPATHLEN); 543 (void) strlcat(bp, value, MAXPATHLEN); 544 separator = B_TRUE; 545 } 546 547 free(argv); 548 } 549