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