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