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