xref: /illumos-gate/usr/src/lib/libsmbfs/smb/rcfile.c (revision 6e375c8351497b82ffa4f33cbf61d712999b4605)
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 #include <fcntl.h>
36 #include <sys/types.h>
37 #include <sys/queue.h>
38 #include <sys/stat.h>
39 #include <ctype.h>
40 #include <errno.h>
41 #include <stdio.h>
42 #include <string.h>
43 #include <strings.h>
44 #include <stdlib.h>
45 #include <libintl.h>
46 #include <pwd.h>
47 #include <unistd.h>
48 #include <sys/debug.h>
49 
50 #include <cflib.h>
51 #include "rcfile_priv.h"
52 extern int smb_debug;
53 
54 SLIST_HEAD(rcfile_head, rcfile);
55 static struct rcfile_head pf_head = {NULL};
56 
57 static struct rcfile *rc_cachelookup(const char *filename);
58 struct rcsection *rc_findsect(struct rcfile *rcp, const char *sectname);
59 static struct rcsection *rc_addsect(struct rcfile *rcp, const char *sectname);
60 static int rc_freesect(struct rcfile *rcp, struct rcsection *rsp);
61 struct rckey *rc_sect_findkey(struct rcsection *rsp, const char *keyname);
62 static struct rckey *rc_sect_addkey(struct rcsection *rsp, const char *name,
63     const char *value);
64 static void rc_key_free(struct rckey *p);
65 static void rc_parse(struct rcfile *rcp);
66 
67 int insecure_nsmbrc;
68 
69 /*
70  * open rcfile and load its content, if already open - return previous handle
71  */
72 int
73 rc_open(const char *filename, const char *mode, struct rcfile **rcfile)
74 {
75 	struct rcfile *rcp;
76 	FILE *f;
77 	struct stat statbuf;
78 
79 	rcp = rc_cachelookup(filename);
80 	if (rcp) {
81 		*rcfile = rcp;
82 		return (0);
83 	}
84 	f = fopen(filename, mode);
85 	if (f == NULL)
86 		return (errno);
87 	insecure_nsmbrc = 0;
88 	if (fstat(fileno(f), &statbuf) >= 0 &&
89 	    (statbuf.st_mode & 077) != 0)
90 		insecure_nsmbrc = 1;
91 	rcp = malloc(sizeof (struct rcfile));
92 	if (rcp == NULL) {
93 		fclose(f);
94 		return (ENOMEM);
95 	}
96 	bzero(rcp, sizeof (struct rcfile));
97 	rcp->rf_name = strdup(filename);
98 	rcp->rf_f = f;
99 	SLIST_INSERT_HEAD(&pf_head, rcp, rf_next);
100 	rc_parse(rcp);
101 	*rcfile = rcp;
102 	return (0);
103 }
104 
105 int
106 rc_merge(const char *filename, struct rcfile **rcfile)
107 {
108 	struct rcfile *rcp = *rcfile;
109 	FILE *f, *t;
110 
111 	insecure_nsmbrc = 0;
112 	if (rcp == NULL) {
113 		return (rc_open(filename, "r", rcfile));
114 	}
115 	f = fopen(filename, "r");
116 	if (f == NULL)
117 		return (errno);
118 	t = rcp->rf_f;
119 	rcp->rf_f = f;
120 	rc_parse(rcp);
121 	rcp->rf_f = t;
122 	fclose(f);
123 	return (0);
124 }
125 
126 int
127 rc_merge_pipe(const char *command, struct rcfile **rcfile)
128 {
129 	struct rcfile *rcp = *rcfile;
130 	FILE *f, *t;
131 
132 	insecure_nsmbrc = 0;
133 	f = popen(command, "r");
134 	if (f == NULL)
135 		return (errno);
136 	if (rcp == NULL) {
137 		rcp = malloc(sizeof (struct rcfile));
138 		if (rcp == NULL) {
139 			fclose(f);
140 			return (ENOMEM);
141 		}
142 		*rcfile = rcp;
143 		bzero(rcp, sizeof (struct rcfile));
144 		rcp->rf_name = strdup(command);
145 		rcp->rf_f = f;
146 		SLIST_INSERT_HEAD(&pf_head, rcp, rf_next);
147 		rc_parse(rcp);
148 	} else {
149 		t = rcp->rf_f;
150 		rcp->rf_f = f;
151 		rc_parse(rcp);
152 		rcp->rf_f = t;
153 	}
154 	fclose(f);
155 	return (0);
156 }
157 
158 int
159 rc_close(struct rcfile *rcp)
160 {
161 	struct rcsection *p, *n;
162 
163 	fclose(rcp->rf_f);
164 	for (p = SLIST_FIRST(&rcp->rf_sect); p; ) {
165 		n = p;
166 		p = SLIST_NEXT(p, rs_next);
167 		rc_freesect(rcp, n);
168 	}
169 	free(rcp->rf_name);
170 	SLIST_REMOVE(&pf_head, rcp, rcfile, rf_next);
171 	free(rcp);
172 	return (0);
173 }
174 
175 static struct rcfile *
176 rc_cachelookup(const char *filename)
177 {
178 	struct rcfile *p;
179 
180 	SLIST_FOREACH(p, &pf_head, rf_next)
181 		if (strcmp(filename, p->rf_name) == 0)
182 			return (p);
183 	return (0);
184 }
185 
186 /* static */ struct rcsection *
187 rc_findsect(struct rcfile *rcp, const char *sectname)
188 {
189 	struct rcsection *p;
190 
191 	SLIST_FOREACH(p, &rcp->rf_sect, rs_next)
192 		if (strcasecmp(p->rs_name, sectname) == 0)
193 			return (p);
194 	return (NULL);
195 }
196 
197 static struct rcsection *
198 rc_addsect(struct rcfile *rcp, const char *sectname)
199 {
200 	struct rcsection *p;
201 
202 	p = rc_findsect(rcp, sectname);
203 	if (p)
204 		return (p);
205 	p = malloc(sizeof (*p));
206 	if (!p)
207 		return (NULL);
208 	p->rs_name = strdup(sectname);
209 	SLIST_INIT(&p->rs_keys);
210 	SLIST_INSERT_HEAD(&rcp->rf_sect, p, rs_next);
211 	return (p);
212 }
213 
214 static int
215 rc_freesect(struct rcfile *rcp, struct rcsection *rsp)
216 {
217 	struct rckey *p, *n;
218 
219 	SLIST_REMOVE(&rcp->rf_sect, rsp, rcsection, rs_next);
220 	for (p = SLIST_FIRST(&rsp->rs_keys); p; ) {
221 		n = p;
222 		p = SLIST_NEXT(p, rk_next);
223 		rc_key_free(n);
224 	}
225 	free(rsp->rs_name);
226 	free(rsp);
227 	return (0);
228 }
229 
230 /* static */ struct rckey *
231 rc_sect_findkey(struct rcsection *rsp, const char *keyname)
232 {
233 	struct rckey *p;
234 
235 	SLIST_FOREACH(p, &rsp->rs_keys, rk_next)
236 		if (strcmp(p->rk_name, keyname) == 0)
237 			return (p);
238 	return (NULL);
239 }
240 
241 static struct rckey *
242 rc_sect_addkey(struct rcsection *rsp, const char *name, const char *value)
243 {
244 	struct rckey *p;
245 
246 	p = rc_sect_findkey(rsp, name);
247 	if (!p) {
248 		p = malloc(sizeof (*p));
249 		if (!p)
250 			return (NULL);
251 		SLIST_INSERT_HEAD(&rsp->rs_keys, p, rk_next);
252 		p->rk_name = strdup(name);
253 		p->rk_value = value ? strdup(value) : strdup("");
254 	}
255 	return (p);
256 }
257 
258 #if 0
259 void
260 rc_sect_delkey(struct rcsection *rsp, struct rckey *p)
261 {
262 
263 	SLIST_REMOVE(&rsp->rs_keys, p, rckey, rk_next);
264 	rc_key_free(p);
265 }
266 #endif
267 
268 static void
269 rc_key_free(struct rckey *p)
270 {
271 	free(p->rk_value);
272 	free(p->rk_name);
273 	free(p);
274 }
275 
276 enum { stNewLine, stHeader, stSkipToEOL, stGetKey, stGetValue};
277 
278 int home_nsmbrc = 0;
279 
280 static char *minauth[] = {
281 	"kerberos",
282 	"ntlmv2",
283 	"ntlm",
284 	"lm",
285 	"none",
286 	NULL
287 };
288 
289 static int
290 eval_minauth(char *auth)
291 {
292 	int i;
293 
294 	for (i = 0; minauth[i]; i++)
295 		if (strcmp(auth, minauth[i]) == 0)
296 			break;
297 	return (i);
298 }
299 
300 /*
301  * Ensure that "minauth" is set to the highest level (lowest array offset)
302  */
303 static void
304 set_value(struct rcfile *rcp, struct rcsection *rsp, struct rckey *rkp,
305     char *ptr)
306 {
307 	int now, new;
308 
309 	if (strcmp(rkp->rk_name, "minauth") == 0) {
310 		now = eval_minauth(rkp->rk_value);
311 		new = eval_minauth(ptr);
312 		if (new >= now) {
313 #ifdef DEBUG
314 			if (smb_debug)
315 				printf(
316 				    "set_value: rejecting %s=%s from %s\n",
317 				    rkp->rk_name, ptr, home_nsmbrc ?
318 				    "user file" : "SMF");
319 #endif
320 			return;
321 		}
322 	}
323 #ifdef DEBUG
324 	if (smb_debug)
325 		printf("set_value: applying %s=%s from %s\n",
326 		    rkp->rk_name, ptr, home_nsmbrc ? "user file" : "SMF");
327 #endif
328 	rkp->rk_value = strdup(ptr);
329 }
330 
331 static void
332 rc_parse(struct rcfile *rcp)
333 {
334 	FILE *f = rcp->rf_f;
335 	int state = stNewLine, c;
336 	struct rcsection *rsp = NULL;
337 	struct rckey *rkp = NULL;
338 	char buf[2048];
339 	char *next = buf, *last = &buf[sizeof (buf)-1];
340 
341 	while ((c = getc(f)) != EOF) {
342 		if (c == '\r')
343 			continue;
344 		if (state == stNewLine) {
345 			next = buf;
346 			if (isspace(c))
347 				continue;	/* skip leading junk */
348 			if (c == '[') {
349 				state = stHeader;
350 				rsp = NULL;
351 				continue;
352 			}
353 			if (c == '#' || c == ';') {
354 				state = stSkipToEOL;
355 			} else {		/* something meaningfull */
356 				state = stGetKey;
357 			}
358 		}
359 		/* ignore long lines */
360 		if (state == stSkipToEOL || next == last) {
361 			if (c == '\n') {
362 				state = stNewLine;
363 				next = buf;
364 			}
365 			continue;
366 		}
367 		if (state == stHeader) {
368 			if (c == ']') {
369 				*next = 0;
370 				next = buf;
371 				rsp = rc_addsect(rcp, buf);
372 				state = stSkipToEOL;
373 			} else
374 				*next++ = c;
375 			continue;
376 		}
377 		if (state == stGetKey) {
378 			/* side effect: 'key name=' */
379 			if (c == ' ' || c == '\t')
380 				continue;	/* become 'keyname=' */
381 			if (c == '\n') {	/* silently ignore ... */
382 				state = stNewLine;
383 				continue;
384 			}
385 			if (c != '=') {
386 				*next++ = c;
387 				continue;
388 			}
389 			*next = 0;
390 			if (rsp == NULL) {
391 				fprintf(stderr, dgettext(TEXT_DOMAIN,
392 				    "Key '%s' defined before section\n"), buf);
393 				state = stSkipToEOL;
394 				continue;
395 			}
396 			if (home_nsmbrc &&
397 			    (strcmp(buf, "nbns") == 0 ||
398 			    strcmp(buf, "nbns_enable") == 0 ||
399 			    strcmp(buf, "nbns_broadcast") == 0 ||
400 			    strcmp(buf, "signing") == 0)) {
401 				fprintf(stderr, dgettext(TEXT_DOMAIN,
402 				    "option %s may not be set "
403 				    "in user .nsmbrc file\n"), buf);
404 				next = buf;
405 				state = stNewLine;
406 				continue;
407 			}
408 			if (insecure_nsmbrc && (strcmp(buf, "password") == 0)) {
409 				fprintf(stderr, dgettext(TEXT_DOMAIN,
410 				    "Warning: .nsmbrc file not secure, "
411 				    "ignoring passwords\n"));
412 				next = buf;
413 				state = stNewLine;
414 				continue;
415 			}
416 			rkp = rc_sect_addkey(rsp, buf, NULL);
417 			next = buf;
418 			state = stGetValue;
419 			continue;
420 		}
421 		/* only stGetValue left */
422 		if (state != stGetValue) {
423 			fprintf(stderr, dgettext(TEXT_DOMAIN,
424 			    "Well, I can't parse file '%s'\n"), rcp->rf_name);
425 			state = stSkipToEOL;
426 		}
427 		if (c != '\n') {
428 			*next++ = c;
429 			continue;
430 		}
431 		*next = 0;
432 		set_value(rcp, rsp, rkp, buf);
433 		state = stNewLine;
434 		rkp = NULL;
435 	} 	/* while */
436 	if (c == EOF && state == stGetValue) {
437 		*next = 0;
438 		set_value(rcp, rsp, rkp, buf);
439 	}
440 }
441 
442 int
443 rc_getstringptr(struct rcfile *rcp, const char *section, const char *key,
444 	char **dest)
445 {
446 	struct rcsection *rsp;
447 	struct rckey *rkp;
448 
449 	*dest = NULL;
450 	rsp = rc_findsect(rcp, section);
451 	if (!rsp)
452 		return (ENOENT);
453 	rkp = rc_sect_findkey(rsp, key);
454 	if (!rkp)
455 		return (ENOENT);
456 	*dest = rkp->rk_value;
457 	return (0);
458 }
459 
460 int
461 rc_getstring(struct rcfile *rcp, const char *section, const char *key,
462 	size_t maxlen, char *dest)
463 {
464 	char *value;
465 	int error;
466 
467 	error = rc_getstringptr(rcp, section, key, &value);
468 	if (error)
469 		return (error);
470 	if (strlen(value) >= maxlen) {
471 		fprintf(stdout, dgettext(TEXT_DOMAIN,
472 		    "line too long for key '%s' in section '%s', max = %d\n"),
473 		    key, section, maxlen);
474 		return (EINVAL);
475 	}
476 	strcpy(dest, value);
477 	return (0);
478 }
479 
480 int
481 rc_getint(struct rcfile *rcp, const char *section, const char *key, int *value)
482 {
483 	struct rcsection *rsp;
484 	struct rckey *rkp;
485 
486 	rsp = rc_findsect(rcp, section);
487 	if (!rsp)
488 		return (ENOENT);
489 	rkp = rc_sect_findkey(rsp, key);
490 	if (!rkp)
491 		return (ENOENT);
492 	errno = 0;
493 	*value = strtol(rkp->rk_value, NULL, 0);
494 	if (errno) {
495 		fprintf(stdout, dgettext(TEXT_DOMAIN,
496 		    "invalid int value '%s' for key '%s' in section '%s'\n"),
497 		    rkp->rk_value, key, section);
498 		return (errno);
499 	}
500 	return (0);
501 }
502 
503 /*
504  * 1,yes,true
505  * 0,no,false
506  */
507 int
508 rc_getbool(struct rcfile *rcp, const char *section, const char *key, int *value)
509 {
510 	struct rcsection *rsp;
511 	struct rckey *rkp;
512 	char *p;
513 
514 	rsp = rc_findsect(rcp, section);
515 	if (!rsp)
516 		return (ENOENT);
517 	rkp = rc_sect_findkey(rsp, key);
518 	if (!rkp)
519 		return (ENOENT);
520 	p = rkp->rk_value;
521 	while (*p && isspace(*p)) p++;
522 	if (*p == '0' ||
523 	    strcasecmp(p, "no") == 0 ||
524 	    strcasecmp(p, "false") == 0) {
525 		*value = 0;
526 		return (0);
527 	}
528 	if (*p == '1' ||
529 	    strcasecmp(p, "yes") == 0 ||
530 	    strcasecmp(p, "true") == 0) {
531 		*value = 1;
532 		return (0);
533 	}
534 	fprintf(stderr, dgettext(TEXT_DOMAIN,
535 	    "invalid boolean value '%s' for key '%s' in section '%s' \n"),
536 	    p, key, section);
537 	return (EINVAL);
538 }
539 
540 /*
541  * Unified command line/rc file parser
542  */
543 int
544 opt_args_parse(struct rcfile *rcp, struct opt_args *ap, const char *sect,
545 	opt_callback_t *callback)
546 {
547 	int len, error;
548 
549 	for (; ap->opt; ap++) {
550 		switch (ap->type) {
551 		case OPTARG_STR:
552 			if (rc_getstringptr(rcp, sect, ap->name, &ap->str) != 0)
553 				break;
554 			len = strlen(ap->str);
555 			if (len > ap->ival) {
556 				fprintf(stdout, dgettext(TEXT_DOMAIN,
557 			"rc: argument for option '%c' (%s) too long\n"),
558 				    ap->opt, ap->name);
559 				return (EINVAL);
560 			}
561 			callback(ap);
562 			break;
563 		case OPTARG_BOOL:
564 			error = rc_getbool(rcp, sect, ap->name, &ap->ival);
565 			if (error == ENOENT)
566 				break;
567 			if (error)
568 				return (EINVAL);
569 			callback(ap);
570 			break;
571 		case OPTARG_INT:
572 			if (rc_getint(rcp, sect, ap->name, &ap->ival) != 0)
573 				break;
574 			if (((ap->flag & OPTFL_HAVEMIN) &&
575 			    ap->ival < ap->min) ||
576 			    ((ap->flag & OPTFL_HAVEMAX) &&
577 			    ap->ival > ap->max)) {
578 				fprintf(stdout, dgettext(TEXT_DOMAIN,
579 				    "rc: argument for option '%c' (%s) "
580 				    "should be in [%d-%d] range\n"),
581 				    ap->opt, ap->name, ap->min, ap->max);
582 				return (EINVAL);
583 			}
584 			callback(ap);
585 			break;
586 		default:
587 			break;
588 		}
589 	}
590 	return (0);
591 }
592 
593 int
594 opt_args_parseopt(struct opt_args *ap, int opt, char *arg,
595 	opt_callback_t *callback)
596 {
597 	int len;
598 
599 	for (; ap->opt; ap++) {
600 		if (ap->opt != opt)
601 			continue;
602 		switch (ap->type) {
603 		case OPTARG_STR:
604 			ap->str = arg;
605 			if (arg) {
606 				len = strlen(ap->str);
607 				if (len > ap->ival) {
608 					fprintf(stdout, dgettext(TEXT_DOMAIN,
609 			"opt: Argument for option '%c' (%s) too long\n"),
610 					    ap->opt, ap->name);
611 					return (EINVAL);
612 				}
613 				callback(ap);
614 			}
615 			break;
616 		case OPTARG_BOOL:
617 			ap->ival = 0;
618 			callback(ap);
619 			break;
620 		case OPTARG_INT:
621 			errno = 0;
622 			ap->ival = strtol(arg, NULL, 0);
623 			if (errno) {
624 				fprintf(stdout, dgettext(TEXT_DOMAIN,
625 				    "opt: Invalid integer value for "
626 				    "option '%c' (%s).\n"),
627 				    ap->opt, ap->name);
628 				return (EINVAL);
629 			}
630 			if (((ap->flag & OPTFL_HAVEMIN) &&
631 			    (ap->ival < ap->min)) ||
632 			    ((ap->flag & OPTFL_HAVEMAX) &&
633 			    (ap->ival > ap->max))) {
634 				fprintf(stdout, dgettext(TEXT_DOMAIN,
635 				    "opt: Argument for option '%c' (%s) "
636 				    "should be in [%d-%d] range\n"),
637 				    ap->opt, ap->name, ap->min, ap->max);
638 				return (EINVAL);
639 			}
640 			callback(ap);
641 			break;
642 		default:
643 			break;
644 		}
645 		break;
646 	}
647 	return (0);
648 }
649