Home | History | Annotate | Download | only in common
      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