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 2008 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 #include <string.h> 29 #include <stdlib.h> 30 #include <stdio.h> 31 #include <pwd.h> 32 #include <assert.h> 33 #include <strings.h> 34 #include <sys/stat.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 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 * autohome shares will be added for each login attempt 81 * even if they already exist 82 */ 83 if ((si.shr_flags & SMB_SHRF_AUTOHOME) == 0) 84 return; 85 86 (void) smb_shr_add(&si, 0); 87 return; 88 } 89 90 if ((ai = smb_autohome_lookup(username)) == NULL) 91 return; 92 93 bzero(&si, sizeof (smb_share_t)); 94 (void) strlcpy(si.shr_path, ai->ah_path, MAXPATHLEN); 95 (void) strsubst(si.shr_path, '\\', '/'); 96 97 if (smb_shr_is_dir(si.shr_path) == 0) 98 return; 99 100 (void) strlcpy(si.shr_name, username, MAXNAMELEN); 101 (void) strlcpy(si.shr_container, ai->ah_container, MAXPATHLEN); 102 si.shr_flags = SMB_SHRF_TRANS | SMB_SHRF_AUTOHOME; 103 104 (void) smb_shr_add(&si, 0); 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_del((char *)username, 0); 120 } 121 } 122 } 123 124 /* 125 * Find out if a share is an autohome share. 126 */ 127 boolean_t 128 smb_is_autohome(const smb_share_t *si) 129 { 130 return (si && (si->shr_flags & SMB_SHRF_AUTOHOME)); 131 } 132 133 /* 134 * Search the autohome database for the specified name. The name cannot 135 * be an empty string or begin with * or +. 136 * 1. Search the file for the specified name. 137 * 2. Check for the wildcard rule and, if present, treat it as a match. 138 * 3. Check for the nsswitch rule and, if present, lookup the name 139 * via the name services. Note that the nsswitch rule will never 140 * be applied if the wildcard rule is present. 141 * 142 * Returns a pointer to the entry on success or null on failure. 143 */ 144 static smb_autohome_t * 145 smb_autohome_lookup(const char *name) 146 { 147 struct passwd *pw; 148 smb_autohome_t *ah = NULL; 149 150 if (name == NULL) 151 return (NULL); 152 153 if (*name == '\0' || *name == '*' || *name == '+') 154 return (NULL); 155 156 smb_autohome_setent(); 157 158 while ((ah = smb_autohome_getent(name)) != NULL) { 159 if (strcasecmp(ah->ah_name, name) == 0) 160 break; 161 } 162 163 if (ah == NULL) { 164 smb_autohome_setent(); 165 166 while ((ah = smb_autohome_getent(name)) != NULL) { 167 if (strcasecmp(ah->ah_name, "*") == 0) { 168 ah->ah_name = (char *)name; 169 break; 170 } 171 } 172 } 173 174 if (ah == NULL) { 175 smb_autohome_setent(); 176 177 while ((ah = smb_autohome_getent("+nsswitch")) != NULL) { 178 if (strcasecmp("+nsswitch", ah->ah_name) != 0) 179 continue; 180 if ((pw = getpwnam(name)) == NULL) { 181 ah = NULL; 182 break; 183 } 184 185 ah->ah_name = pw->pw_name; 186 187 if (ah->ah_path) 188 ah->ah_container = ah->ah_path; 189 190 ah->ah_path = pw->pw_dir; 191 break; 192 } 193 } 194 195 smb_autohome_endent(); 196 return (ah); 197 } 198 199 /* 200 * Open or rewind the autohome database. 201 */ 202 static void 203 smb_autohome_setent(void) 204 { 205 smb_autohome_info_t *si; 206 char path[MAXNAMELEN]; 207 char filename[MAXNAMELEN]; 208 int rc; 209 210 if ((si = smb_autohome_getinfo()) != 0) { 211 (void) fseek(si->fp, 0L, SEEK_SET); 212 si->lineno = 0; 213 return; 214 } 215 216 if ((si = &smb_ai) == 0) 217 return; 218 219 rc = smb_config_getstr(SMB_CI_AUTOHOME_MAP, path, sizeof (path)); 220 if (rc != SMBD_SMF_OK) 221 return; 222 223 (void) snprintf(filename, MAXNAMELEN, "%s/%s", path, 224 SMB_AUTOHOME_FILE); 225 226 if ((si->fp = fopen(filename, "r")) == NULL) 227 return; 228 229 si->magic1 = si; 230 si->magic2 = si; 231 si->lineno = 0; 232 } 233 234 /* 235 * Close the autohome database and invalidate the autohome info. 236 * We can't zero the whole info structure because the application 237 * should still have access to the data after the file is closed. 238 */ 239 static void 240 smb_autohome_endent(void) 241 { 242 smb_autohome_info_t *si; 243 244 if ((si = smb_autohome_getinfo()) != 0) { 245 (void) fclose(si->fp); 246 si->fp = 0; 247 si->magic1 = 0; 248 si->magic2 = 0; 249 } 250 } 251 252 /* 253 * Return the next entry in the autohome database, opening the file 254 * if necessary. Returns null on EOF or error. 255 * 256 * Note that we are not looking for the specified name. The name is 257 * only used for key substitution, so that the caller sees the entry 258 * in expanded form. 259 */ 260 static smb_autohome_t * 261 smb_autohome_getent(const char *name) 262 { 263 smb_autohome_info_t *si; 264 char *bp; 265 266 if ((si = smb_autohome_getinfo()) == 0) { 267 smb_autohome_setent(); 268 269 if ((si = smb_autohome_getinfo()) == 0) 270 return (0); 271 } 272 273 /* 274 * Find the next non-comment, non-empty line. 275 * Anything after a # is a comment and can be discarded. 276 * Discard a newline to avoid it being included in the parsing 277 * that follows. 278 * Leading and training whitespace is discarded, and replicated 279 * whitespace is compressed to simplify the token parsing, 280 * although strsep() deals with that better than strtok(). 281 */ 282 do { 283 if (fgets(si->buf, SMB_AUTOHOME_BUFSIZ, si->fp) == 0) 284 return (0); 285 286 ++si->lineno; 287 288 if ((bp = strpbrk(si->buf, "#\r\n")) != 0) 289 *bp = '\0'; 290 291 (void) trim_whitespace(si->buf); 292 bp = strcanon(si->buf, " \t"); 293 } while (*bp == '\0'); 294 295 (void) smb_autohome_keysub(name, si->buf, SMB_AUTOHOME_BUFSIZ); 296 return (smb_autohome_make_entry(si)); 297 } 298 299 /* 300 * Set up an autohome entry from the line buffer. The line should just 301 * contain tokens separated by single whitespace. The line format is: 302 * <username> <home-dir-path> <ADS container> 303 */ 304 static smb_autohome_t * 305 smb_autohome_make_entry(smb_autohome_info_t *si) 306 { 307 char *bp; 308 int i; 309 310 bp = si->buf; 311 312 for (i = 0; i < SMB_AUTOHOME_MAXARG; ++i) 313 si->argv[i] = 0; 314 315 for (i = 0; i < SMB_AUTOHOME_MAXARG; ++i) { 316 do { 317 if ((si->argv[i] = strsep((char **)&bp, " \t")) == 0) 318 break; 319 } while (*(si->argv[i]) == '\0'); 320 321 if (si->argv[i] == 0) 322 break; 323 } 324 325 if ((si->autohome.ah_name = si->argv[0]) == NULL) { 326 /* 327 * Sanity check: the name could be an empty 328 * string but it can't be a null pointer. 329 */ 330 return (0); 331 } 332 333 if ((si->autohome.ah_path = si->argv[1]) == NULL) 334 si->autohome.ah_path = ""; 335 336 if ((si->autohome.ah_container = si->argv[2]) == NULL) 337 si->autohome.ah_container = ""; 338 339 return (&si->autohome); 340 } 341 342 /* 343 * Substitute the ? and & map keys. 344 * ? is replaced by the first character of the name 345 * & is replaced by the whole name. 346 */ 347 static char * 348 smb_autohome_keysub(const char *name, char *buf, int buflen) 349 { 350 char key[SMB_AUTOHOME_KEYSIZ]; 351 char *ampersand; 352 char *tmp; 353 int bufsize = buflen; 354 355 (void) strlcpy(key, buf, SMB_AUTOHOME_KEYSIZ); 356 357 if ((tmp = strpbrk(key, " \t")) == NULL) 358 return (NULL); 359 360 *tmp = '\0'; 361 362 /* 363 * Substitution characters are not allowed in the key. 364 */ 365 if (strpbrk(key, "?&") != NULL) 366 return (NULL); 367 368 if (strcmp(key, "*") == 0 && name != NULL) 369 (void) strlcpy(key, name, SMB_AUTOHOME_KEYSIZ); 370 371 (void) strsubst(buf, '?', *key); 372 373 while ((ampersand = strchr(buf, '&')) != NULL) { 374 if ((tmp = strdup(ampersand + 1)) == NULL) 375 return (0); 376 377 bufsize = buflen - (ampersand - buf); 378 (void) strlcpy(ampersand, key, bufsize); 379 (void) strlcat(ampersand, tmp, bufsize); 380 free(tmp); 381 } 382 383 return (buf); 384 } 385 386 /* 387 * Get a pointer to the context buffer and validate it. 388 */ 389 static smb_autohome_info_t * 390 smb_autohome_getinfo(void) 391 { 392 smb_autohome_info_t *si; 393 394 if ((si = &smb_ai) == 0) 395 return (0); 396 397 if ((si->magic1 == si) && (si->magic2 == si) && (si->fp != NULL)) 398 return (si); 399 400 return (0); 401 } 402