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