1 /*
2 * Copyright (c) 2000, Boris Popov
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 * must display the following acknowledgement:
15 * This product includes software developed by Boris Popov.
16 * 4. Neither the name of the author nor the names of any co-contributors
17 * may be used to endorse or promote products derived from this software
18 * without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 *
32 * $Id: rcfile.c,v 1.1.1.2 2001/07/06 22:38:43 conrad Exp $
33 */
34 /*
35 * Copyright 2018 Nexenta Systems, Inc. All rights reserved.
36 */
37
38 #include <fcntl.h>
39 #include <sys/types.h>
40 #include <sys/queue.h>
41 #include <sys/stat.h>
42
43 #include <ctype.h>
44 #include <errno.h>
45 #include <stdio.h>
46 #include <string.h>
47 #include <strings.h>
48 #include <stdlib.h>
49 #include <synch.h>
50 #include <unistd.h>
51 #include <pwd.h>
52 #include <libintl.h>
53
54 #include <cflib.h>
55 #include "rcfile_priv.h"
56
57 #include <assert.h>
58
59 #if 0 /* before SMF */
60 #define SMB_CFG_FILE "/etc/nsmb.conf"
61 #define OLD_SMB_CFG_FILE "/usr/local/etc/nsmb.conf"
62 #endif
63
64 extern int smb_debug;
65
66 static struct rcfile *rc_cachelookup(const char *filename);
67 static struct rcsection *rc_findsect(struct rcfile *rcp, const char *sectname);
68 static struct rcsection *rc_addsect(struct rcfile *rcp, const char *sectname);
69 static int rc_freesect(struct rcfile *rcp, struct rcsection *rsp);
70 static struct rckey *rc_sect_findkey(struct rcsection *rsp, const char *key);
71 static struct rckey *rc_sect_addkey(struct rcsection *rsp, const char *name,
72 const char *value);
73 static void rc_key_free(struct rckey *p);
74 static void rc_parse(struct rcfile *rcp);
75
76 /* lock for the variables below */
77 mutex_t rcfile_mutex = DEFAULTMUTEX;
78
79 SLIST_HEAD(rcfile_head, rcfile);
80 static struct rcfile_head pf_head = {NULL};
81 struct rcfile *smb_rc;
82 int home_nsmbrc;
83 int insecure_nsmbrc;
84
85 /*
86 * open rcfile and load its content, if already open - return previous handle
87 */
88 static int
89 rc_open(const char *filename, const char *mode, struct rcfile **rcfile)
90 {
91 struct stat statbuf;
92 struct rcfile *rcp;
93 FILE *f;
94
95 assert(MUTEX_HELD(&rcfile_mutex));
96
97 rcp = rc_cachelookup(filename);
98 if (rcp) {
99 *rcfile = rcp;
100 return (0);
101 }
102 f = fopen(filename, mode);
103 if (f == NULL)
104 return (errno);
105 insecure_nsmbrc = 0;
106 if (fstat(fileno(f), &statbuf) >= 0 &&
107 (statbuf.st_mode & 077) != 0)
108 insecure_nsmbrc = 1;
109 rcp = malloc(sizeof (struct rcfile));
110 if (rcp == NULL) {
111 fclose(f);
112 return (ENOMEM);
113 }
114 bzero(rcp, sizeof (struct rcfile));
115 rcp->rf_name = strdup(filename);
116 rcp->rf_f = f;
117 SLIST_INSERT_HEAD(&pf_head, rcp, rf_next);
118 rc_parse(rcp);
119 *rcfile = rcp;
120 return (0);
121 }
122
123 static int
124 rc_merge(const char *filename, struct rcfile **rcfile)
125 {
126 struct stat statbuf;
127 struct rcfile *rcp = *rcfile;
128 FILE *f, *t;
129
130 assert(MUTEX_HELD(&rcfile_mutex));
131
132 insecure_nsmbrc = 0;
133 if (rcp == NULL) {
134 return (rc_open(filename, "r", rcfile));
135 }
136 f = fopen(filename, "r");
137 if (f == NULL)
138 return (errno);
139 insecure_nsmbrc = 0;
140 if (fstat(fileno(f), &statbuf) >= 0 &&
141 (statbuf.st_mode & 077) != 0)
142 insecure_nsmbrc = 1;
143 t = rcp->rf_f;
144 rcp->rf_f = f;
145 rc_parse(rcp);
146 rcp->rf_f = t;
147 fclose(f);
148 return (0);
149 }
150
151 /*
152 * Like rc_open, but creates a temporary file and
153 * reads the sharectl settings into it.
154 * The file is deleted when we close it.
155 */
156 static int
157 rc_open_sharectl(struct rcfile **rcfile)
158 {
159 static char template[24] = "/tmp/smbfsXXXXXX";
160 struct rcfile *rcp = NULL;
161 FILE *fp = NULL;
162 int err;
163 int fd = -1;
164
165 assert(MUTEX_HELD(&rcfile_mutex));
166
167 fd = mkstemp(template);
168 if (fd < 0) {
169 err = errno;
170 goto errout;
171 }
172
173 fp = fdopen(fd, "w+");
174 if (fp == NULL) {
175 err = errno;
176 close(fd);
177 goto errout;
178 }
179 fd = -1; /* The fp owns this fd now. */
180
181 /*
182 * Get smbfs sharectl settings into the file.
183 */
184 if ((err = rc_scf_get_sharectl(fp)) != 0)
185 goto errout;
186
187 rcp = malloc(sizeof (struct rcfile));
188 if (rcp == NULL) {
189 err = ENOMEM;
190 goto errout;
191 }
192 bzero(rcp, sizeof (struct rcfile));
193
194 rcp->rf_name = strdup(template);
195 if (rcp->rf_name == NULL) {
196 err = ENOMEM;
197 goto errout;
198 }
199 rcp->rf_f = fp;
200 rcp->rf_flags = RCFILE_DELETE_ON_CLOSE;
201
202 SLIST_INSERT_HEAD(&pf_head, rcp, rf_next);
203 insecure_nsmbrc = 0;
204 rc_parse(rcp);
205 *rcfile = rcp;
206 /* fclose(f) in rc_close */
207 return (0);
208
209 errout:
210 if (rcp != NULL)
211 free(rcp);
212 if (fp != NULL) {
213 fclose(fp);
214 fd = -1;
215 }
216 if (fd != -1)
217 close(fd);
218
219 return (err);
220 }
221
222
223 static int
224 rc_close(struct rcfile *rcp)
225 {
226 struct rcsection *p, *n;
227
228 mutex_lock(&rcfile_mutex);
229
230 fclose(rcp->rf_f);
231 if (rcp->rf_flags & RCFILE_DELETE_ON_CLOSE)
232 (void) unlink(rcp->rf_name);
233
234 for (p = SLIST_FIRST(&rcp->rf_sect); p; ) {
235 n = p;
236 p = SLIST_NEXT(p, rs_next);
237 rc_freesect(rcp, n);
238 }
239 free(rcp->rf_name);
240 SLIST_REMOVE(&pf_head, rcp, rcfile, rf_next);
241 free(rcp);
242
243 mutex_unlock(&rcfile_mutex);
244 return (0);
245 }
246
247 static struct rcfile *
248 rc_cachelookup(const char *filename)
249 {
250 struct rcfile *p;
251
252 assert(MUTEX_HELD(&rcfile_mutex));
253
254 SLIST_FOREACH(p, &pf_head, rf_next)
255 if (strcmp(filename, p->rf_name) == 0)
256 return (p);
257 return (0);
258 }
259
260 static struct rcsection *
261 rc_findsect(struct rcfile *rcp, const char *sectname)
262 {
263 struct rcsection *p;
264
265 assert(MUTEX_HELD(&rcfile_mutex));
266
267 SLIST_FOREACH(p, &rcp->rf_sect, rs_next)
268 if (strcasecmp(p->rs_name, sectname) == 0)
269 return (p);
270 return (NULL);
271 }
272
273 static struct rcsection *
274 rc_addsect(struct rcfile *rcp, const char *sectname)
275 {
276 struct rcsection *p;
277
278 assert(MUTEX_HELD(&rcfile_mutex));
279
280 p = rc_findsect(rcp, sectname);
281 if (p)
282 return (p);
283 p = malloc(sizeof (*p));
284 if (!p)
285 return (NULL);
286 p->rs_name = strdup(sectname);
287 SLIST_INIT(&p->rs_keys);
288 SLIST_INSERT_HEAD(&rcp->rf_sect, p, rs_next);
289 return (p);
290 }
291
292 static int
293 rc_freesect(struct rcfile *rcp, struct rcsection *rsp)
294 {
295 struct rckey *p, *n;
296
297 assert(MUTEX_HELD(&rcfile_mutex));
298
299 SLIST_REMOVE(&rcp->rf_sect, rsp, rcsection, rs_next);
300 for (p = SLIST_FIRST(&rsp->rs_keys); p; ) {
301 n = p;
302 p = SLIST_NEXT(p, rk_next);
303 rc_key_free(n);
304 }
305 free(rsp->rs_name);
306 free(rsp);
307 return (0);
308 }
309
310 static struct rckey *
311 rc_sect_findkey(struct rcsection *rsp, const char *keyname)
312 {
313 struct rckey *p;
314
315 assert(MUTEX_HELD(&rcfile_mutex));
316
317 SLIST_FOREACH(p, &rsp->rs_keys, rk_next)
318 if (strcmp(p->rk_name, keyname) == 0)
319 return (p);
320 return (NULL);
321 }
322
323 static struct rckey *
324 rc_sect_addkey(struct rcsection *rsp, const char *name, const char *value)
325 {
326 struct rckey *p;
327
328 assert(MUTEX_HELD(&rcfile_mutex));
329
330 p = rc_sect_findkey(rsp, name);
331 if (!p) {
332 p = malloc(sizeof (*p));
333 if (!p)
334 return (NULL);
335 SLIST_INSERT_HEAD(&rsp->rs_keys, p, rk_next);
336 p->rk_name = strdup(name);
337 p->rk_value = value ? strdup(value) : strdup("");
338 }
339 return (p);
340 }
341
342 #if 0
343 void
344 rc_sect_delkey(struct rcsection *rsp, struct rckey *p)
345 {
346
347 SLIST_REMOVE(&rsp->rs_keys, p, rckey, rk_next);
348 rc_key_free(p);
349 }
350 #endif
351
352 static void
353 rc_key_free(struct rckey *p)
354 {
355 free(p->rk_value);
356 free(p->rk_name);
357 free(p);
358 }
359
360
361 static char *minauth_values[] = {
362 "none",
363 "lm",
364 "ntlm",
365 "ntlmv2",
366 "kerberos",
367 NULL
368 };
369
370 static int
371 eval_minauth(char *auth)
372 {
373 int i;
374
375 for (i = 0; minauth_values[i]; i++)
376 if (strcmp(auth, minauth_values[i]) == 0)
377 return (i);
378 return (-1);
379 }
380
381 /*
382 * Ensure that "minauth" is set to the highest level
383 */
384 /*ARGSUSED*/
385 static void
386 set_value(struct rcfile *rcp, struct rcsection *rsp, struct rckey *rkp,
387 char *ptr)
388 {
389 int now, new;
390 #ifdef DEBUG
391 char *from = "SMF";
392
393 if (home_nsmbrc != 0)
394 from = "user file";
395 #endif
396
397 if (strcmp(rkp->rk_name, "minauth") == 0) {
398 now = eval_minauth(rkp->rk_value);
399 new = eval_minauth(ptr);
400 if (new <= now) {
401 #ifdef DEBUG
402 if (smb_debug)
403 fprintf(stderr,
404 "set_value: rejecting %s=%s"
405 " in %s from %s\n",
406 rkp->rk_name, ptr,
407 rsp->rs_name, from);
408 #endif
409 return;
410 }
411 }
412 #ifdef DEBUG
413 if (smb_debug)
414 fprintf(stderr,
415 "set_value: applying %s=%s in %s from %s\n",
416 rkp->rk_name, ptr, rsp->rs_name, from);
417 #endif
418 rkp->rk_value = strdup(ptr);
419 }
420
421
422 /* states in rc_parse */
423 enum { stNewLine, stHeader, stSkipToEOL, stGetKey, stGetValue};
424
425 static void
426 rc_parse(struct rcfile *rcp)
427 {
428 FILE *f = rcp->rf_f;
429 int state = stNewLine, c;
430 struct rcsection *rsp = NULL;
431 struct rckey *rkp = NULL;
432 char buf[2048];
433 char *next = buf, *last = &buf[sizeof (buf)-1];
434
435 assert(MUTEX_HELD(&rcfile_mutex));
436
437 while ((c = getc(f)) != EOF) {
438 if (c == '\r')
439 continue;
440 if (state == stNewLine) {
441 next = buf;
442 if (isspace(c))
443 continue; /* skip leading junk */
444 if (c == '[') {
445 state = stHeader;
446 rsp = NULL;
447 continue;
448 }
449 if (c == '#' || c == ';') {
450 state = stSkipToEOL;
451 } else { /* something meaningfull */
452 state = stGetKey;
453 }
454 }
455 /* ignore long lines */
456 if (state == stSkipToEOL || next == last) {
457 if (c == '\n') {
458 state = stNewLine;
459 next = buf;
460 }
461 continue;
462 }
463 if (state == stHeader) {
464 if (c == ']') {
465 *next = 0;
466 next = buf;
467 rsp = rc_addsect(rcp, buf);
468 state = stSkipToEOL;
469 } else
470 *next++ = c;
471 continue;
472 }
473 if (state == stGetKey) {
474 /* side effect: 'key name=' */
475 if (c == ' ' || c == '\t')
476 continue; /* become 'keyname=' */
477 if (c == '\n') { /* silently ignore ... */
478 state = stNewLine;
479 continue;
480 }
481 if (c != '=') {
482 *next++ = c;
483 continue;
484 }
485 *next = 0;
486 if (rsp == NULL) {
487 fprintf(stderr, dgettext(TEXT_DOMAIN,
488 "Key '%s' defined before section\n"), buf);
489 state = stSkipToEOL;
490 continue;
491 }
492 if (home_nsmbrc != 0 && (
493 strcmp(buf, "nbns") == 0 ||
494 strcmp(buf, "nbns_enable") == 0 ||
495 strcmp(buf, "nbns_broadcast") == 0 ||
496 strcmp(buf, "signing") == 0)) {
497 fprintf(stderr, dgettext(TEXT_DOMAIN,
498 "option %s may not be set "
499 "in user .nsmbrc file\n"), buf);
500 next = buf;
501 state = stNewLine;
502 continue;
503 }
504 if (insecure_nsmbrc != 0 &&
505 strcmp(buf, "password") == 0) {
506 fprintf(stderr, dgettext(TEXT_DOMAIN,
507 "Warning: .nsmbrc file not secure, "
508 "ignoring passwords\n"));
509 next = buf;
510 state = stNewLine;
511 continue;
512 }
513 rkp = rc_sect_addkey(rsp, buf, NULL);
514 next = buf;
515 state = stGetValue;
516 continue;
517 }
518 /* only stGetValue left */
519 if (state != stGetValue) {
520 fprintf(stderr, dgettext(TEXT_DOMAIN,
521 "Well, I can't parse file '%s'\n"), rcp->rf_name);
522 state = stSkipToEOL;
523 }
524 if (c != '\n') {
525 *next++ = c;
526 continue;
527 }
528 *next = 0;
529 set_value(rcp, rsp, rkp, buf);
530 state = stNewLine;
531 rkp = NULL;
532 } /* while */
533 if (c == EOF && state == stGetValue) {
534 *next = 0;
535 set_value(rcp, rsp, rkp, buf);
536 }
537 }
538
539 int
540 rc_getstringptr(struct rcfile *rcp, const char *section, const char *key,
541 char **dest)
542 {
543 struct rcsection *rsp;
544 struct rckey *rkp;
545 int err;
546
547 mutex_lock(&rcfile_mutex);
548
549 *dest = NULL;
550 rsp = rc_findsect(rcp, section);
551 if (!rsp) {
552 err = ENOENT;
553 goto out;
554 }
555 rkp = rc_sect_findkey(rsp, key);
556 if (!rkp) {
557 err = ENOENT;
558 goto out;
559 }
560 *dest = rkp->rk_value;
561 err = 0;
562
563 out:
564 mutex_unlock(&rcfile_mutex);
565 return (err);
566 }
567
568 int
569 rc_getstring(struct rcfile *rcp, const char *section, const char *key,
570 size_t maxlen, char *dest)
571 {
572 char *value;
573 int error;
574
575 error = rc_getstringptr(rcp, section, key, &value);
576 if (error)
577 return (error);
578 if (strlen(value) >= maxlen) {
579 fprintf(stderr, dgettext(TEXT_DOMAIN,
580 "line too long for key '%s' in section '%s', max = %d\n"),
581 key, section, maxlen);
582 return (EINVAL);
583 }
584 strcpy(dest, value);
585 return (0);
586 }
587
588 int
589 rc_getint(struct rcfile *rcp, const char *section, const char *key, int *value)
590 {
591 struct rcsection *rsp;
592 struct rckey *rkp;
593 int err;
594
595 mutex_lock(&rcfile_mutex);
596
597 rsp = rc_findsect(rcp, section);
598 if (!rsp) {
599 err = ENOENT;
600 goto out;
601 }
602 rkp = rc_sect_findkey(rsp, key);
603 if (!rkp) {
604 err = ENOENT;
605 goto out;
606 }
607 errno = 0;
608 *value = strtol(rkp->rk_value, NULL, 0);
609 if ((err = errno) != 0) {
610 fprintf(stderr, dgettext(TEXT_DOMAIN,
611 "invalid int value '%s' for key '%s' in section '%s'\n"),
612 rkp->rk_value, key, section);
613 }
614
615 out:
616 mutex_unlock(&rcfile_mutex);
617 return (err);
618 }
619
620 /*
621 * 1,yes,true
622 * 0,no,false
623 */
624 int
625 rc_getbool(struct rcfile *rcp, const char *section, const char *key, int *value)
626 {
627 struct rcsection *rsp;
628 struct rckey *rkp;
629 char *p;
630 int err;
631
632 mutex_lock(&rcfile_mutex);
633
634 rsp = rc_findsect(rcp, section);
635 if (!rsp) {
636 err = ENOENT;
637 goto out;
638 }
639 rkp = rc_sect_findkey(rsp, key);
640 if (!rkp) {
641 err = ENOENT;
642 goto out;
643 }
644 p = rkp->rk_value;
645 while (*p && isspace(*p)) p++;
646 if (*p == '0' ||
647 strcasecmp(p, "no") == 0 ||
648 strcasecmp(p, "false") == 0) {
649 *value = 0;
650 err = 0;
651 goto out;
652 }
653 if (*p == '1' ||
654 strcasecmp(p, "yes") == 0 ||
655 strcasecmp(p, "true") == 0) {
656 *value = 1;
657 err = 0;
658 goto out;
659 }
660 fprintf(stderr, dgettext(TEXT_DOMAIN,
661 "invalid boolean value '%s' for key '%s' in section '%s' \n"),
662 p, key, section);
663 err = EINVAL;
664
665 out:
666 mutex_unlock(&rcfile_mutex);
667 return (err);
668 }
669
670 #ifdef DEBUG
671 void
672 dump_props(char *where)
673 {
674 struct rcsection *rsp = NULL;
675 struct rckey *rkp = NULL;
676
677 fprintf(stderr, "Settings %s\n", where);
678 SLIST_FOREACH(rsp, &smb_rc->rf_sect, rs_next) {
679 fprintf(stderr, "section=%s\n", rsp->rs_name);
680 fflush(stderr);
681
682 SLIST_FOREACH(rkp, &rsp->rs_keys, rk_next) {
683 fprintf(stderr, " key=%s, value=%s\n",
684 rkp->rk_name, rkp->rk_value);
685 fflush(stderr);
686 }
687 }
688 }
689 #endif
690
691 /*
692 * first parse "sharectl get smbfs, then $HOME/.nsmbrc
693 * This is called by library consumers (commands)
694 */
695 int
696 smb_open_rcfile(char *home)
697 {
698 char *fn;
699 int len, error = 0;
700
701 mutex_lock(&rcfile_mutex);
702
703 smb_rc = NULL;
704 #if 0 /* before SMF */
705 fn = SMB_CFG_FILE;
706 error = rc_open(fn, &smb_rc);
707 #else
708 fn = "(sharectl get smbfs)";
709 error = rc_open_sharectl(&smb_rc);
710 #endif
711 if (error != 0 && error != ENOENT) {
712 /* Error from fopen. strerror is OK. */
713 fprintf(stderr, dgettext(TEXT_DOMAIN,
714 "Can't open %s: %s\n"), fn, strerror(errno));
715 }
716 #ifdef DEBUG
717 if (smb_debug)
718 dump_props(fn);
719 #endif
720
721 if (home) {
722 len = strlen(home) + 20;
723 fn = malloc(len);
724 snprintf(fn, len, "%s/.nsmbrc", home);
725 home_nsmbrc = 1;
726 error = rc_merge(fn, &smb_rc);
727 if (error != 0 && error != ENOENT) {
728 fprintf(stderr, dgettext(TEXT_DOMAIN,
729 "Can't open %s: %s\n"), fn, strerror(errno));
730 }
731 home_nsmbrc = 0;
732 #ifdef DEBUG
733 if (smb_debug)
734 dump_props(fn);
735 #endif
736 free(fn);
737 }
738
739 /* Mostly ignore error returns above. */
740 if (smb_rc == NULL)
741 error = ENOENT;
742 else
743 error = 0;
744
745 mutex_unlock(&rcfile_mutex);
746
747 return (error);
748 }
749
750 /*
751 * This is called by library consumers (commands)
752 */
753 void
754 smb_close_rcfile(void)
755 {
756 struct rcfile *rcp;
757
758 if ((rcp = smb_rc) != NULL) {
759 smb_rc = NULL;
760 rc_close(rcp);
761 }
762 }