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