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 (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
23 */
24
25 #include <sys/param.h>
26 #include <string.h>
27 #include <stdlib.h>
28 #include <stdio.h>
29 #include <pwd.h>
30 #include <nss_dbdefs.h>
31 #include <assert.h>
32 #include <strings.h>
33 #include <sys/stat.h>
34 #include <sys/idmap.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 static void smb_autohome_parse_options(smb_share_t *);
63 static int smb_autohome_add_private(const char *, uid_t, gid_t);
64
65 /*
66 * Add an autohome share. See smb_autohome(4) for details.
67 *
68 * If share directory contains backslash path separators, they will
69 * be converted to forward slash to support NT/DOS path style for
70 * autohome shares.
71 *
72 * We need to serialize calls to smb_autohome_lookup because it
73 * operates on the global smb_ai structure.
74 */
75 void
smb_autohome_add(const smb_token_t * token)76 smb_autohome_add(const smb_token_t *token)
77 {
78
79 char *username;
80 struct passwd pw;
81 char buf[NSS_LINELEN_PASSWD];
82 uid_t uid;
83 gid_t gid;
84
85 uid = token->tkn_user.i_id;
86 gid = token->tkn_primary_grp.i_id;
87
88 if (IDMAP_ID_IS_EPHEMERAL(uid)) {
89 username = token->tkn_account_name;
90 assert(username);
91 } else {
92 if (getpwuid_r(uid, &pw, buf, sizeof (buf)) == NULL) {
93 syslog(LOG_ERR, "unable to determine name for " \
94 "UID: %u\n", uid);
95 return;
96 }
97 username = pw.pw_name;
98 }
99
100 if (smb_autohome_add_private(username, uid, gid) != NERR_Success) {
101 if (!smb_isstrlwr(username)) {
102 (void) smb_strlwr(username);
103 (void) smb_autohome_add_private(username, uid, gid);
104 }
105 }
106 }
107
108 /*
109 * Remove an autohome share.
110 */
111 void
smb_autohome_remove(const char * username)112 smb_autohome_remove(const char *username)
113 {
114 smb_share_t si;
115
116 assert(username);
117
118 if (smb_shr_get((char *)username, &si) == NERR_Success) {
119 if (si.shr_flags & SMB_SHRF_AUTOHOME)
120 (void) smb_shr_remove((char *)username);
121 }
122 }
123
124 /*
125 * An autohome share is not created if a static share using the same name
126 * already exists. Autohome shares will be added for each login attempt.
127 *
128 * Calling smb_shr_get() may return the first argument in all lower case so
129 * a copy is passed in instead.
130 *
131 * We need to serialize calls to smb_autohome_lookup because it
132 * operates on the global smb_ai structure.
133 */
134 static int
smb_autohome_add_private(const char * username,uid_t uid,gid_t gid)135 smb_autohome_add_private(const char *username, uid_t uid, gid_t gid)
136 {
137 static mutex_t autohome_mutex;
138 smb_share_t si;
139 smb_autohome_t *ai;
140 char shr_name[MAXNAMELEN];
141
142 (void) strlcpy(shr_name, username, sizeof (shr_name));
143
144 if (smb_shr_get(shr_name, &si) == NERR_Success) {
145 if ((si.shr_flags & SMB_SHRF_AUTOHOME) == 0)
146 return (NERR_Success);
147
148 (void) smb_shr_add(&si);
149 return (NERR_Success);
150 }
151
152 (void) mutex_lock(&autohome_mutex);
153
154 if ((ai = smb_autohome_lookup(username)) == NULL) {
155 (void) mutex_unlock(&autohome_mutex);
156 return (NERR_ItemNotFound);
157 }
158
159 bzero(&si, sizeof (smb_share_t));
160 (void) strlcpy(si.shr_path, ai->ah_path, MAXPATHLEN);
161 (void) strsubst(si.shr_path, '\\', '/');
162
163 (void) strlcpy(si.shr_name, username, MAXNAMELEN);
164 (void) strlcpy(si.shr_container, ai->ah_container, MAXPATHLEN);
165 (void) strlcpy(si.shr_cmnt, "Autohome", SMB_SHARE_CMNT_MAX);
166 smb_autohome_parse_options(&si);
167 si.shr_flags |= SMB_SHRF_TRANS | SMB_SHRF_AUTOHOME;
168 si.shr_uid = uid;
169 si.shr_gid = gid;
170
171 (void) mutex_unlock(&autohome_mutex);
172
173 return (smb_shr_add(&si));
174 }
175
176 /*
177 * Search the autohome database for the specified name. The name cannot
178 * be an empty string or begin with * or +.
179 * 1. Search the file for the specified name.
180 * 2. Check for the wildcard rule and, if present, treat it as a match.
181 * 3. Check for the nsswitch rule and, if present, lookup the name
182 * via the name services. Note that the nsswitch rule will never
183 * be applied if the wildcard rule is present.
184 *
185 * Returns a pointer to the entry on success or null on failure.
186 */
187 static smb_autohome_t *
smb_autohome_lookup(const char * name)188 smb_autohome_lookup(const char *name)
189 {
190 struct passwd *pw;
191 smb_autohome_t *ah = NULL;
192
193 if (name == NULL)
194 return (NULL);
195
196 if (*name == '\0' || *name == '*' || *name == '+')
197 return (NULL);
198
199 smb_autohome_setent();
200
201 while ((ah = smb_autohome_getent(name)) != NULL) {
202 if (strcasecmp(ah->ah_name, name) == 0)
203 break;
204 }
205
206 if (ah == NULL) {
207 smb_autohome_setent();
208
209 while ((ah = smb_autohome_getent(name)) != NULL) {
210 if (strcasecmp(ah->ah_name, "*") == 0) {
211 ah->ah_name = (char *)name;
212 break;
213 }
214 }
215 }
216
217 if (ah == NULL) {
218 smb_autohome_setent();
219
220 while ((ah = smb_autohome_getent("+nsswitch")) != NULL) {
221 if (strcasecmp("+nsswitch", ah->ah_name) != 0)
222 continue;
223 if ((pw = getpwnam(name)) == NULL) {
224 ah = NULL;
225 break;
226 }
227
228 ah->ah_name = pw->pw_name;
229
230 if (ah->ah_path)
231 ah->ah_container = ah->ah_path;
232
233 ah->ah_path = pw->pw_dir;
234 break;
235 }
236 }
237
238 smb_autohome_endent();
239 return (ah);
240 }
241
242 /*
243 * Open or rewind the autohome database.
244 */
245 static void
smb_autohome_setent(void)246 smb_autohome_setent(void)
247 {
248 smb_autohome_info_t *si;
249 char path[MAXNAMELEN];
250 char filename[MAXNAMELEN];
251 int rc;
252
253 if ((si = smb_autohome_getinfo()) != 0) {
254 (void) fseek(si->fp, 0L, SEEK_SET);
255 si->lineno = 0;
256 return;
257 }
258
259 if ((si = &smb_ai) == 0)
260 return;
261
262 rc = smb_config_getstr(SMB_CI_AUTOHOME_MAP, path, sizeof (path));
263 if (rc != SMBD_SMF_OK)
264 return;
265
266 (void) snprintf(filename, MAXNAMELEN, "%s/%s", path,
267 SMB_AUTOHOME_FILE);
268
269 if ((si->fp = fopen(filename, "r")) == NULL)
270 return;
271
272 si->magic1 = si;
273 si->magic2 = si;
274 si->lineno = 0;
275 }
276
277 /*
278 * Close the autohome database and invalidate the autohome info.
279 * We can't zero the whole info structure because the application
280 * should still have access to the data after the file is closed.
281 */
282 static void
smb_autohome_endent(void)283 smb_autohome_endent(void)
284 {
285 smb_autohome_info_t *si;
286
287 if ((si = smb_autohome_getinfo()) != 0) {
288 (void) fclose(si->fp);
289 si->fp = 0;
290 si->magic1 = 0;
291 si->magic2 = 0;
292 }
293 }
294
295 /*
296 * Return the next entry in the autohome database, opening the file
297 * if necessary. Returns null on EOF or error.
298 *
299 * Note that we are not looking for the specified name. The name is
300 * only used for key substitution, so that the caller sees the entry
301 * in expanded form.
302 */
303 static smb_autohome_t *
smb_autohome_getent(const char * name)304 smb_autohome_getent(const char *name)
305 {
306 smb_autohome_info_t *si;
307 char *bp;
308
309 if ((si = smb_autohome_getinfo()) == 0) {
310 smb_autohome_setent();
311
312 if ((si = smb_autohome_getinfo()) == 0)
313 return (0);
314 }
315
316 /*
317 * Find the next non-comment, non-empty line.
318 * Anything after a # is a comment and can be discarded.
319 * Discard a newline to avoid it being included in the parsing
320 * that follows.
321 * Leading and training whitespace is discarded, and replicated
322 * whitespace is compressed to simplify the token parsing,
323 * although strsep() deals with that better than strtok().
324 */
325 do {
326 if (fgets(si->buf, SMB_AUTOHOME_BUFSIZ, si->fp) == 0)
327 return (0);
328
329 ++si->lineno;
330
331 if ((bp = strpbrk(si->buf, "#\r\n")) != 0)
332 *bp = '\0';
333
334 (void) trim_whitespace(si->buf);
335 bp = strcanon(si->buf, " \t");
336 } while (*bp == '\0');
337
338 (void) smb_autohome_keysub(name, si->buf, SMB_AUTOHOME_BUFSIZ);
339 return (smb_autohome_make_entry(si));
340 }
341
342 /*
343 * Set up an autohome entry from the line buffer. The line should just
344 * contain tokens separated by single whitespace. The line format is:
345 * <username> <home-dir-path> <ADS container>
346 */
347 static smb_autohome_t *
smb_autohome_make_entry(smb_autohome_info_t * si)348 smb_autohome_make_entry(smb_autohome_info_t *si)
349 {
350 char *bp;
351 int i;
352
353 bp = si->buf;
354
355 for (i = 0; i < SMB_AUTOHOME_MAXARG; ++i)
356 si->argv[i] = NULL;
357
358 for (i = 0; i < SMB_AUTOHOME_MAXARG; ++i) {
359 do {
360 if ((si->argv[i] = strsep(&bp, " \t")) == NULL)
361 break;
362 } while (*(si->argv[i]) == '\0');
363
364 if (si->argv[i] == NULL)
365 break;
366 }
367
368 if ((si->autohome.ah_name = si->argv[0]) == NULL) {
369 /*
370 * Sanity check: the name could be an empty
371 * string but it can't be a null pointer.
372 */
373 return (0);
374 }
375
376 if ((si->autohome.ah_path = si->argv[1]) == NULL)
377 si->autohome.ah_path = "";
378
379 if ((si->autohome.ah_container = si->argv[2]) == NULL)
380 si->autohome.ah_container = "";
381
382 return (&si->autohome);
383 }
384
385 /*
386 * Substitute the ? and & map keys.
387 * ? is replaced by the first character of the name
388 * & is replaced by the whole name.
389 */
390 static char *
smb_autohome_keysub(const char * name,char * buf,int buflen)391 smb_autohome_keysub(const char *name, char *buf, int buflen)
392 {
393 char key[SMB_AUTOHOME_KEYSIZ];
394 char *ampersand;
395 char *tmp;
396 int bufsize = buflen;
397
398 (void) strlcpy(key, buf, SMB_AUTOHOME_KEYSIZ);
399
400 if ((tmp = strpbrk(key, " \t")) == NULL)
401 return (NULL);
402
403 *tmp = '\0';
404
405 /*
406 * Substitution characters are not allowed in the key.
407 */
408 if (strpbrk(key, "?&") != NULL)
409 return (NULL);
410
411 if (strcmp(key, "*") == 0 && name != NULL)
412 (void) strlcpy(key, name, SMB_AUTOHOME_KEYSIZ);
413
414 (void) strsubst(buf, '?', *key);
415
416 while ((ampersand = strchr(buf, '&')) != NULL) {
417 if ((tmp = strdup(ampersand + 1)) == NULL)
418 return (0);
419
420 bufsize = buflen - (ampersand - buf);
421 (void) strlcpy(ampersand, key, bufsize);
422 (void) strlcat(ampersand, tmp, bufsize);
423 free(tmp);
424 }
425
426 return (buf);
427 }
428
429 /*
430 * Get a pointer to the context buffer and validate it.
431 */
432 static smb_autohome_info_t *
smb_autohome_getinfo(void)433 smb_autohome_getinfo(void)
434 {
435 smb_autohome_info_t *si;
436
437 if ((si = &smb_ai) == 0)
438 return (0);
439
440 if ((si->magic1 == si) && (si->magic2 == si) && (si->fp != NULL))
441 return (si);
442
443 return (0);
444 }
445
446 /*
447 * Parse the options string, which contains a comma separated list of
448 * name-value pairs. One of the options may be an AD container, which
449 * is also a comma separated list of name-value pairs. For example,
450 * dn=ad,dn=sun,dn=com,ou=users
451 *
452 * All options other than the AD container will be extracted from
453 * shr_container and used to set share properties.
454 * On return, shr_container will contain the AD container string.
455 */
456 static void
smb_autohome_parse_options(smb_share_t * si)457 smb_autohome_parse_options(smb_share_t *si)
458 {
459 char buf[MAXPATHLEN];
460 char **argv;
461 char **ap;
462 char *bp;
463 char *value;
464 boolean_t separator = B_FALSE;
465 int argc;
466 int i;
467
468 if (strlcpy(buf, si->shr_container, MAXPATHLEN) == 0)
469 return;
470
471 for (argc = 1, bp = si->shr_container; *bp != '\0'; ++bp)
472 if (*bp == ',')
473 ++argc;
474
475 if ((argv = calloc(argc + 1, sizeof (char *))) == NULL)
476 return;
477
478 ap = argv;
479 for (bp = buf, i = 0; i < argc; ++i) {
480 do {
481 if ((value = strsep(&bp, ",")) == NULL)
482 break;
483 } while (*value == '\0');
484
485 if (value == NULL)
486 break;
487
488 *ap++ = value;
489 }
490 *ap = NULL;
491
492 si->shr_container[0] = '\0';
493 bp = si->shr_container;
494
495 for (ap = argv; *ap != NULL; ++ap) {
496 value = *ap;
497
498 if (strncasecmp(value, "catia=", 6) == 0) {
499 smb_shr_sa_setflag((value + 6), si, SMB_SHRF_CATIA);
500 continue;
501 }
502
503 if (strncasecmp(value, "csc=", 4) == 0) {
504 smb_shr_sa_csc_option((value + 4), si);
505 continue;
506 }
507
508 if (strncasecmp(value, "abe=", 4) == 0) {
509 smb_shr_sa_setflag((value + 4), si, SMB_SHRF_ABE);
510 continue;
511 }
512
513 if (strncasecmp(value, "description=", 12) == 0) {
514 (void) strlcpy(si->shr_cmnt, (value + 12),
515 SMB_SHARE_CMNT_MAX);
516 continue;
517 }
518
519 if (strncasecmp(value, "rw=", 3) == 0) {
520 (void) strlcpy(si->shr_access_rw, (value + 3),
521 sizeof (si->shr_access_rw));
522 continue;
523 }
524
525 if (strncasecmp(value, "ro=", 3) == 0) {
526 (void) strlcpy(si->shr_access_ro, (value + 3),
527 sizeof (si->shr_access_ro));
528 continue;
529 }
530
531 if (strncasecmp(value, "none=", 5) == 0) {
532 (void) strlcpy(si->shr_access_none, (value + 5),
533 sizeof (si->shr_access_none));
534 continue;
535 }
536
537 if (separator)
538 (void) strlcat(bp, ",", MAXPATHLEN);
539 (void) strlcat(bp, value, MAXPATHLEN);
540 separator = B_TRUE;
541 }
542
543 free(argv);
544 }
545