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