xref: /illumos-gate/usr/src/lib/smbsrv/libmlsvc/common/smb_autohome.c (revision 99dda20867d903eec23291ba1ecb18a82d70096b)
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 <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 path[MAXNAMELEN];
228 	char filename[MAXNAMELEN];
229 	int rc;
230 
231 	if ((si = smb_autohome_getinfo()) != 0) {
232 		(void) fseek(si->fp, 0L, SEEK_SET);
233 		si->lineno = 0;
234 		return;
235 	}
236 
237 	if ((si = &smb_ai) == 0)
238 		return;
239 
240 	rc = smb_config_getstr(SMB_CI_AUTOHOME_MAP, path, sizeof (path));
241 	if (rc != SMBD_SMF_OK)
242 		return;
243 
244 	(void) snprintf(filename, MAXNAMELEN, "%s/%s", path,
245 	    SMB_AUTOHOME_FILE);
246 
247 	if ((si->fp = fopen(filename, "r")) == NULL)
248 		return;
249 
250 	si->magic1 = si;
251 	si->magic2 = si;
252 	si->lineno = 0;
253 }
254 
255 /*
256  * Close the autohome database and invalidate the autohome info.
257  * We can't zero the whole info structure because the application
258  * should still have access to the data after the file is closed.
259  */
260 void
261 smb_autohome_endent(void)
262 {
263 	smb_autohome_info_t *si;
264 
265 	if ((si = smb_autohome_getinfo()) != 0) {
266 		(void) fclose(si->fp);
267 		si->fp = 0;
268 		si->magic1 = 0;
269 		si->magic2 = 0;
270 	}
271 }
272 
273 /*
274  * Return the next entry in the autohome database, opening the file
275  * if necessary.  Returns null on EOF or error.
276  *
277  * Note that we are not looking for the specified name. The name is
278  * only used for key substitution, so that the caller sees the entry
279  * in expanded form.
280  */
281 smb_autohome_t *
282 smb_autohome_getent(const char *name)
283 {
284 	smb_autohome_info_t *si;
285 	char *bp;
286 
287 	if ((si = smb_autohome_getinfo()) == 0) {
288 		smb_autohome_setent();
289 
290 		if ((si = smb_autohome_getinfo()) == 0)
291 			return (0);
292 	}
293 
294 	/*
295 	 * Find the next non-comment, non-empty line.
296 	 * Anything after a # is a comment and can be discarded.
297 	 * Discard a newline to avoid it being included in the parsing
298 	 * that follows.
299 	 * Leading and training whitespace is discarded, and replicated
300 	 * whitespace is compressed to simplify the token parsing,
301 	 * although strsep() deals with that better than strtok().
302 	 */
303 	do {
304 		if (fgets(si->buf, SMB_AUTOHOME_BUFSIZ, si->fp) == 0)
305 			return (0);
306 
307 		++si->lineno;
308 
309 		if ((bp = strpbrk(si->buf, "#\r\n")) != 0)
310 			*bp = '\0';
311 
312 		(void) trim_whitespace(si->buf);
313 		bp = strcanon(si->buf, " \t");
314 	} while (*bp == '\0');
315 
316 	(void) smb_autohome_keysub(name, si->buf, SMB_AUTOHOME_BUFSIZ);
317 	return (smb_autohome_make_entry(si));
318 }
319 
320 /*
321  * Set up an autohome entry from the line buffer. The line should just
322  * contain tokens separated by single whitespace. The line format is:
323  *	<username> <home-dir-path> <ADS container>
324  */
325 static smb_autohome_t *
326 smb_autohome_make_entry(smb_autohome_info_t *si)
327 {
328 	char *bp;
329 	int i;
330 
331 	bp = si->buf;
332 
333 	for (i = 0; i < SMB_AUTOHOME_MAXARG; ++i)
334 		si->argv[i] = 0;
335 
336 	for (i = 0; i < SMB_AUTOHOME_MAXARG; ++i) {
337 		do {
338 			if ((si->argv[i] = strsep((char **)&bp, " \t")) == 0)
339 				break;
340 		} while (*(si->argv[i]) == '\0');
341 
342 		if (si->argv[i] == 0)
343 			break;
344 	}
345 
346 	if ((si->autohome.ah_name = si->argv[0]) == NULL) {
347 		/*
348 		 * Sanity check: the name could be an empty
349 		 * string but it can't be a null pointer.
350 		 */
351 		return (0);
352 	}
353 
354 	if ((si->autohome.ah_path = si->argv[1]) == NULL)
355 		si->autohome.ah_path = "";
356 
357 	if ((si->autohome.ah_container = si->argv[2]) == NULL)
358 		si->autohome.ah_container = "";
359 
360 	return (&si->autohome);
361 }
362 
363 /*
364  * Substitute the ? and & map keys.
365  * ? is replaced by the first character of the name
366  * & is replaced by the whole name.
367  */
368 static char *
369 smb_autohome_keysub(const char *name, char *buf, int buflen)
370 {
371 	char key[SMB_AUTOHOME_KEYSIZ];
372 	char *ampersand;
373 	char *tmp;
374 	int bufsize = buflen;
375 
376 	(void) strlcpy(key, buf, SMB_AUTOHOME_KEYSIZ);
377 
378 	if ((tmp = strpbrk(key, " \t")) == NULL)
379 		return (NULL);
380 
381 	*tmp = '\0';
382 
383 	/*
384 	 * Substitution characters are not allowed in the key.
385 	 */
386 	if (strpbrk(key, "?&") != NULL)
387 		return (NULL);
388 
389 	if (strcmp(key, "*") == 0 && name != NULL)
390 		(void) strlcpy(key, name, SMB_AUTOHOME_KEYSIZ);
391 
392 	(void) strsubst(buf, '?', *key);
393 
394 	while ((ampersand = strchr(buf, '&')) != NULL) {
395 		if ((tmp = strdup(ampersand + 1)) == NULL)
396 			return (0);
397 
398 		bufsize = buflen - (ampersand - buf);
399 		(void) strlcpy(ampersand, key, bufsize);
400 		(void) strlcat(ampersand, tmp, bufsize);
401 		free(tmp);
402 	}
403 
404 	return (buf);
405 }
406 
407 /*
408  * Get a pointer to the context buffer and validate it.
409  */
410 static smb_autohome_info_t *
411 smb_autohome_getinfo(void)
412 {
413 	smb_autohome_info_t *si;
414 
415 	if ((si = &smb_ai) == 0)
416 		return (0);
417 
418 	if ((si->magic1 == si) && (si->magic2 == si) && (si->fp != NULL))
419 		return (si);
420 
421 	return (0);
422 }
423