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