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