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