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