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