xref: /illumos-gate/usr/src/lib/smbsrv/libmlsvc/common/smb_autohome.c (revision b89a8333f5e1f75ec0c269b22524bd2eccb972ba)
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