xref: /illumos-gate/usr/src/lib/smbsrv/libmlsvc/common/smb_autohome.c (revision 56e2cc86321ec889bf83a888d902c60d6fb2ef8d)
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 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 	smb_autohome_parse_options(&si);
102 	si.shr_flags |= SMB_SHRF_TRANS | SMB_SHRF_AUTOHOME;
103 
104 	(void) smb_shr_add(&si);
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_remove((char *)username);
120 	}
121 }
122 
123 /*
124  * Search the autohome database for the specified name. The name cannot
125  * be an empty string or begin with * or +.
126  * 1. Search the file for the specified name.
127  * 2. Check for the wildcard rule and, if present, treat it as a match.
128  * 3. Check for the nsswitch rule and, if present, lookup the name
129  *    via the name services. Note that the nsswitch rule will never
130  *    be applied if the wildcard rule is present.
131  *
132  * Returns a pointer to the entry on success or null on failure.
133  */
134 static smb_autohome_t *
135 smb_autohome_lookup(const char *name)
136 {
137 	struct passwd *pw;
138 	smb_autohome_t *ah = NULL;
139 
140 	if (name == NULL)
141 		return (NULL);
142 
143 	if (*name == '\0' || *name == '*' || *name == '+')
144 		return (NULL);
145 
146 	smb_autohome_setent();
147 
148 	while ((ah = smb_autohome_getent(name)) != NULL) {
149 		if (strcasecmp(ah->ah_name, name) == 0)
150 			break;
151 	}
152 
153 	if (ah == NULL) {
154 		smb_autohome_setent();
155 
156 		while ((ah = smb_autohome_getent(name)) != NULL) {
157 			if (strcasecmp(ah->ah_name, "*") == 0) {
158 				ah->ah_name = (char *)name;
159 				break;
160 			}
161 		}
162 	}
163 
164 	if (ah == NULL) {
165 		smb_autohome_setent();
166 
167 		while ((ah = smb_autohome_getent("+nsswitch")) != NULL) {
168 			if (strcasecmp("+nsswitch", ah->ah_name) != 0)
169 				continue;
170 			if ((pw = getpwnam(name)) == NULL) {
171 				ah = NULL;
172 				break;
173 			}
174 
175 			ah->ah_name = pw->pw_name;
176 
177 			if (ah->ah_path)
178 				ah->ah_container = ah->ah_path;
179 
180 			ah->ah_path = pw->pw_dir;
181 			break;
182 		}
183 	}
184 
185 	smb_autohome_endent();
186 	return (ah);
187 }
188 
189 /*
190  * Open or rewind the autohome database.
191  */
192 static void
193 smb_autohome_setent(void)
194 {
195 	smb_autohome_info_t *si;
196 	char path[MAXNAMELEN];
197 	char filename[MAXNAMELEN];
198 	int rc;
199 
200 	if ((si = smb_autohome_getinfo()) != 0) {
201 		(void) fseek(si->fp, 0L, SEEK_SET);
202 		si->lineno = 0;
203 		return;
204 	}
205 
206 	if ((si = &smb_ai) == 0)
207 		return;
208 
209 	rc = smb_config_getstr(SMB_CI_AUTOHOME_MAP, path, sizeof (path));
210 	if (rc != SMBD_SMF_OK)
211 		return;
212 
213 	(void) snprintf(filename, MAXNAMELEN, "%s/%s", path,
214 	    SMB_AUTOHOME_FILE);
215 
216 	if ((si->fp = fopen(filename, "r")) == NULL)
217 		return;
218 
219 	si->magic1 = si;
220 	si->magic2 = si;
221 	si->lineno = 0;
222 }
223 
224 /*
225  * Close the autohome database and invalidate the autohome info.
226  * We can't zero the whole info structure because the application
227  * should still have access to the data after the file is closed.
228  */
229 static void
230 smb_autohome_endent(void)
231 {
232 	smb_autohome_info_t *si;
233 
234 	if ((si = smb_autohome_getinfo()) != 0) {
235 		(void) fclose(si->fp);
236 		si->fp = 0;
237 		si->magic1 = 0;
238 		si->magic2 = 0;
239 	}
240 }
241 
242 /*
243  * Return the next entry in the autohome database, opening the file
244  * if necessary.  Returns null on EOF or error.
245  *
246  * Note that we are not looking for the specified name. The name is
247  * only used for key substitution, so that the caller sees the entry
248  * in expanded form.
249  */
250 static smb_autohome_t *
251 smb_autohome_getent(const char *name)
252 {
253 	smb_autohome_info_t *si;
254 	char *bp;
255 
256 	if ((si = smb_autohome_getinfo()) == 0) {
257 		smb_autohome_setent();
258 
259 		if ((si = smb_autohome_getinfo()) == 0)
260 			return (0);
261 	}
262 
263 	/*
264 	 * Find the next non-comment, non-empty line.
265 	 * Anything after a # is a comment and can be discarded.
266 	 * Discard a newline to avoid it being included in the parsing
267 	 * that follows.
268 	 * Leading and training whitespace is discarded, and replicated
269 	 * whitespace is compressed to simplify the token parsing,
270 	 * although strsep() deals with that better than strtok().
271 	 */
272 	do {
273 		if (fgets(si->buf, SMB_AUTOHOME_BUFSIZ, si->fp) == 0)
274 			return (0);
275 
276 		++si->lineno;
277 
278 		if ((bp = strpbrk(si->buf, "#\r\n")) != 0)
279 			*bp = '\0';
280 
281 		(void) trim_whitespace(si->buf);
282 		bp = strcanon(si->buf, " \t");
283 	} while (*bp == '\0');
284 
285 	(void) smb_autohome_keysub(name, si->buf, SMB_AUTOHOME_BUFSIZ);
286 	return (smb_autohome_make_entry(si));
287 }
288 
289 /*
290  * Set up an autohome entry from the line buffer. The line should just
291  * contain tokens separated by single whitespace. The line format is:
292  *	<username> <home-dir-path> <ADS container>
293  */
294 static smb_autohome_t *
295 smb_autohome_make_entry(smb_autohome_info_t *si)
296 {
297 	char *bp;
298 	int i;
299 
300 	bp = si->buf;
301 
302 	for (i = 0; i < SMB_AUTOHOME_MAXARG; ++i)
303 		si->argv[i] = NULL;
304 
305 	for (i = 0; i < SMB_AUTOHOME_MAXARG; ++i) {
306 		do {
307 			if ((si->argv[i] = strsep(&bp, " \t")) == NULL)
308 				break;
309 		} while (*(si->argv[i]) == '\0');
310 
311 		if (si->argv[i] == NULL)
312 			break;
313 	}
314 
315 	if ((si->autohome.ah_name = si->argv[0]) == NULL) {
316 		/*
317 		 * Sanity check: the name could be an empty
318 		 * string but it can't be a null pointer.
319 		 */
320 		return (0);
321 	}
322 
323 	if ((si->autohome.ah_path = si->argv[1]) == NULL)
324 		si->autohome.ah_path = "";
325 
326 	if ((si->autohome.ah_container = si->argv[2]) == NULL)
327 		si->autohome.ah_container = "";
328 
329 	return (&si->autohome);
330 }
331 
332 /*
333  * Substitute the ? and & map keys.
334  * ? is replaced by the first character of the name
335  * & is replaced by the whole name.
336  */
337 static char *
338 smb_autohome_keysub(const char *name, char *buf, int buflen)
339 {
340 	char key[SMB_AUTOHOME_KEYSIZ];
341 	char *ampersand;
342 	char *tmp;
343 	int bufsize = buflen;
344 
345 	(void) strlcpy(key, buf, SMB_AUTOHOME_KEYSIZ);
346 
347 	if ((tmp = strpbrk(key, " \t")) == NULL)
348 		return (NULL);
349 
350 	*tmp = '\0';
351 
352 	/*
353 	 * Substitution characters are not allowed in the key.
354 	 */
355 	if (strpbrk(key, "?&") != NULL)
356 		return (NULL);
357 
358 	if (strcmp(key, "*") == 0 && name != NULL)
359 		(void) strlcpy(key, name, SMB_AUTOHOME_KEYSIZ);
360 
361 	(void) strsubst(buf, '?', *key);
362 
363 	while ((ampersand = strchr(buf, '&')) != NULL) {
364 		if ((tmp = strdup(ampersand + 1)) == NULL)
365 			return (0);
366 
367 		bufsize = buflen - (ampersand - buf);
368 		(void) strlcpy(ampersand, key, bufsize);
369 		(void) strlcat(ampersand, tmp, bufsize);
370 		free(tmp);
371 	}
372 
373 	return (buf);
374 }
375 
376 /*
377  * Get a pointer to the context buffer and validate it.
378  */
379 static smb_autohome_info_t *
380 smb_autohome_getinfo(void)
381 {
382 	smb_autohome_info_t *si;
383 
384 	if ((si = &smb_ai) == 0)
385 		return (0);
386 
387 	if ((si->magic1 == si) && (si->magic2 == si) && (si->fp != NULL))
388 		return (si);
389 
390 	return (0);
391 }
392 
393 /*
394  * Parse the options string, which contains a comma separated list of
395  * name-value pairs.  One of the options may be an AD container, which
396  * is also a comma separated list of name-value pairs.  For example,
397  * dn=ad,dn=sun,dn=com,ou=users
398  *
399  * All options other than the AD container will be extracted from
400  * shr_container and used to set share properties.
401  * On return, shr_container will contain the AD container string.
402  */
403 static void
404 smb_autohome_parse_options(smb_share_t *si)
405 {
406 	char buf[MAXPATHLEN];
407 	char **argv;
408 	char **ap;
409 	char *bp;
410 	char *value;
411 	boolean_t separator = B_FALSE;
412 	int argc;
413 	int i;
414 
415 	if (strlcpy(buf, si->shr_container, MAXPATHLEN) == 0)
416 		return;
417 
418 	for (argc = 1, bp = si->shr_container; *bp != '\0'; ++bp)
419 		if (*bp == ',')
420 			++argc;
421 
422 	if ((argv = calloc(argc + 1, sizeof (char *))) == NULL)
423 		return;
424 
425 	ap = argv;
426 	for (bp = buf, i = 0; i < argc; ++i) {
427 		do {
428 			if ((value = strsep(&bp, ",")) == NULL)
429 				break;
430 		} while (*value == '\0');
431 
432 		if (value == NULL)
433 			break;
434 
435 		*ap++ = value;
436 	}
437 	*ap = NULL;
438 
439 	si->shr_container[0] = '\0';
440 	bp = si->shr_container;
441 
442 	for (ap = argv; *ap != NULL; ++ap) {
443 		value = *ap;
444 
445 		if (strncasecmp(value, "catia=", 6) == 0) {
446 			smb_shr_sa_catia_option((value + 6), si);
447 			continue;
448 		}
449 
450 		if (strncasecmp(value, "csc=", 4) == 0) {
451 			smb_shr_sa_csc_option((value + 4), si);
452 			continue;
453 		}
454 
455 		if (strncasecmp(value, "description=", 12) == 0) {
456 			(void) strlcpy(si->shr_cmnt, (value + 12),
457 			    SMB_SHARE_CMNT_MAX);
458 			continue;
459 		}
460 
461 		if (separator)
462 			(void) strlcat(bp, ",", MAXPATHLEN);
463 		(void) strlcat(bp, value, MAXPATHLEN);
464 		separator = B_TRUE;
465 	}
466 
467 	free(argv);
468 }
469