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