1 /*
2 * Copyright (c) 2001-2003
3 * Fraunhofer Institute for Open Communication Systems (FhG Fokus).
4 * All rights reserved.
5 *
6 * Author: Harti Brandt <harti@freebsd.org>
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 *
29 * $Begemot: bsnmp/snmpd/config.c,v 1.25 2006/02/14 09:04:20 brandt_h Exp $
30 *
31 * Parse configuration file.
32 */
33 #include <sys/types.h>
34 #include <sys/queue.h>
35 #include <sys/socket.h>
36 #include <sys/un.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <stdarg.h>
41 #include <ctype.h>
42 #include <errno.h>
43 #include <syslog.h>
44 #include <unistd.h>
45 #include <limits.h>
46 #include <netdb.h>
47 #include <setjmp.h>
48 #include <inttypes.h>
49
50 #include "snmpmod.h"
51 #include "snmpd.h"
52 #include "tree.h"
53
54 /*
55 #define DEBUGGING
56 */
57
58 /*
59 * config_file: EMPTY | config_file line
60 *
61 * line: oid '=' value
62 * | '%' STRING
63 * | STRING := REST_OF_LINE
64 * | STRING ?= REST_OF_LINE
65 * | . INCLUDE STRING
66 *
67 * oid: STRING suboid
68 *
69 * suboid: EMPTY | suboid '.' subid
70 *
71 * subid: NUM | STRING | '[' STRING ']'
72 *
73 * value: EMPTY | STRING | NUM
74 */
75
76 /*
77 * Input context for macros and includes
78 */
79 enum input_type {
80 INPUT_FILE = 1,
81 INPUT_STRING
82 };
83 struct input {
84 enum input_type type;
85 union {
86 struct {
87 FILE *fp;
88 char *filename;
89 u_int lno;
90 } file;
91 struct {
92 char *macro;
93 char *str;
94 char *ptr;
95 size_t left;
96 } str;
97 } u;
98 LIST_ENTRY(input) link;
99 };
100 static LIST_HEAD(, input) inputs;
101
102 #define input_fp u.file.fp
103 #define input_filename u.file.filename
104 #define input_lno u.file.lno
105 #define input_macro u.str.macro
106 #define input_str u.str.str
107 #define input_ptr u.str.ptr
108 #define input_left u.str.left
109
110 static int input_push;
111 static int input_buf[2];
112
113 /*
114 * Configuration data. The configuration file is handled as one single
115 * SNMP transaction. So we need to keep the assignment data for the
116 * commit or rollback pass. Note, that dependencies and finish functions
117 * are NOT allowed here.
118 */
119 struct assign {
120 struct snmp_value value;
121 struct snmp_scratch scratch;
122 const char *node_name;
123
124 TAILQ_ENTRY(assign) link;
125 };
126 static TAILQ_HEAD(assigns, assign) assigns = TAILQ_HEAD_INITIALIZER(assigns);
127
128
129 static struct snmp_context *snmp_ctx;
130
131 struct macro {
132 char *name;
133 char *value;
134 size_t length;
135 LIST_ENTRY(macro) link;
136 int perm;
137 };
138 static LIST_HEAD(, macro) macros = LIST_HEAD_INITIALIZER(macros);
139
140 enum {
141 TOK_EOF = 0200,
142 TOK_EOL,
143 TOK_NUM,
144 TOK_STR,
145 TOK_HOST,
146 TOK_ASSIGN,
147 TOK_QASSIGN,
148 };
149
150 /* lexer values and last token */
151 static uint64_t numval;
152 static char strval[_POSIX2_LINE_MAX];
153 static size_t strvallen;
154 static int token;
155
156 /* error return */
157 static jmp_buf errjmp[4];
158 static volatile int errstk;
159
160 # define ERRPUSH() (setjmp(errjmp[errstk++]))
161 # define ERRPOP() ((void)(errstk--))
162 # define ERRNEXT() (longjmp(errjmp[--errstk], 1))
163 # define ERR() (longjmp(errjmp[--errstk], 1))
164
165 /* section context */
166 static int ignore;
167
168 /*
169 * Report an error and jump to the error label
170 */
171 static void report(const char *fmt, ...) __dead2 __printflike(1, 2);
172
173 static void
report(const char * fmt,...)174 report(const char *fmt, ...)
175 {
176 va_list ap;
177 const struct input *input;
178
179 va_start(ap, fmt);
180 vsyslog(LOG_ERR, fmt, ap);
181 va_end(ap);
182
183 LIST_FOREACH(input, &inputs, link) {
184 switch (input->type) {
185
186 case INPUT_FILE:
187 syslog(LOG_ERR, " in file %s line %u",
188 input->input_filename, input->input_lno);
189 break;
190
191 case INPUT_STRING:
192 syslog(LOG_ERR, " in macro %s pos %td",
193 input->input_macro,
194 input->input_ptr - input->input_str);
195 break;
196 }
197 }
198 ERR();
199 }
200
201 /*
202 * Open a file for input
203 */
204 static int
input_open_file(const char * fname,int sysdir)205 input_open_file(const char *fname, int sysdir)
206 {
207 struct input *input;
208 FILE *fp;
209 char path[PATH_MAX + 1];
210 const char *col;
211 const char *ptr;
212
213 if (sysdir) {
214 ptr = syspath;
215 fp = NULL;
216 while (*ptr != '\0') {
217 if ((col = strchr(ptr, ':')) == NULL) {
218 snprintf(path, sizeof(path), "%s/%s",
219 ptr, fname);
220 col = ptr + strlen(ptr) - 1;
221 } else if (col == ptr)
222 snprintf(path, sizeof(path), "./%s", fname);
223 else
224 snprintf(path, sizeof(path), "%.*s/%s",
225 (int)(col - ptr), ptr, fname);
226 if ((fp = fopen(path, "r")) != NULL)
227 break;
228 ptr = col + 1;
229 }
230 } else
231 fp = fopen(fname, "r");
232
233 if (fp == NULL)
234 report("%s: %m", fname);
235
236 if ((input = malloc(sizeof(*input))) == NULL) {
237 fclose(fp);
238 return (-1);
239 }
240 if ((input->input_filename = malloc(strlen(fname) + 1)) == NULL) {
241 fclose(fp);
242 free(input);
243 return (-1);
244 }
245 strcpy(input->input_filename, fname);
246 input->input_fp = fp;
247 input->input_lno = 1;
248 input->type = INPUT_FILE;
249 LIST_INSERT_HEAD(&inputs, input, link);
250 return (0);
251 }
252
253 /*
254 * Make a macro the next input
255 */
256 static void
input_open_macro(struct macro * m)257 input_open_macro(struct macro *m)
258 {
259 struct input *input;
260
261 if ((input = malloc(sizeof(*input))) == NULL)
262 report("%m");
263 input->type = INPUT_STRING;
264 input->input_macro = m->name;
265 if ((input->input_str = malloc(m->length)) == NULL) {
266 free(input);
267 report("%m");
268 }
269 memcpy(input->input_str, m->value, m->length);
270 input->input_ptr = input->input_str;
271 input->input_left = m->length;
272 LIST_INSERT_HEAD(&inputs, input, link);
273 }
274
275 /*
276 * Close top input source
277 */
278 static void
input_close(void)279 input_close(void)
280 {
281 struct input *input;
282
283 if ((input = LIST_FIRST(&inputs)) == NULL)
284 abort();
285 switch (input->type) {
286
287 case INPUT_FILE:
288 fclose(input->input_fp);
289 free(input->input_filename);
290 break;
291
292 case INPUT_STRING:
293 free(input->input_str);
294 break;
295 }
296 LIST_REMOVE(input, link);
297 free(input);
298 }
299
300 /*
301 * Close all inputs
302 */
303 static void
input_close_all(void)304 input_close_all(void)
305 {
306 while (!LIST_EMPTY(&inputs))
307 input_close();
308 }
309
310 /*
311 * Push back one character
312 */
313 static void
input_ungetc(int c)314 input_ungetc(int c)
315 {
316 if (c == EOF)
317 report("pushing EOF");
318 if (input_push == 2)
319 report("pushing third char");
320 input_buf[input_push++] = c;
321 }
322
323
324 /*
325 * Return next character from the input without preprocessing.
326 */
327 static int
input_getc_raw(void)328 input_getc_raw(void)
329 {
330 int c;
331 struct input *input;
332
333 if (input_push != 0) {
334 c = input_buf[--input_push];
335 goto ok;
336 }
337 while ((input = LIST_FIRST(&inputs)) != NULL) {
338 switch (input->type) {
339
340 case INPUT_FILE:
341 if ((c = getc(input->input_fp)) == EOF) {
342 if (ferror(input->input_fp))
343 report("read error: %m");
344 input_close();
345 break;
346 }
347 if (c == '\n')
348 input->input_lno++;
349 goto ok;
350
351 case INPUT_STRING:
352 if (input->input_left-- == 0) {
353 input_close();
354 break;
355 }
356 c = *input->input_ptr++;
357 goto ok;
358 }
359 }
360 # ifdef DEBUGGING
361 fprintf(stderr, "EOF");
362 # endif
363 return (EOF);
364
365 ok:
366 # ifdef DEBUGGING
367 if (!isascii(c) || !isprint(c))
368 fprintf(stderr, "'%#2x'", c);
369 else
370 fprintf(stderr, "'%c'", c);
371 # endif
372 return (c);
373 }
374
375 /*
376 * Get character with and \\n -> processing.
377 */
378 static int
input_getc_plain(void)379 input_getc_plain(void)
380 {
381 int c;
382
383 again:
384 if ((c = input_getc_raw()) == '\\') {
385 if ((c = input_getc_raw()) == '\n')
386 goto again;
387 if (c != EOF)
388 input_ungetc(c);
389 return ('\\');
390 }
391 return (c);
392 }
393
394 /*
395 * Get next character with substitution of macros
396 */
397 static int
input_getc(void)398 input_getc(void)
399 {
400 int c;
401 struct macro *m;
402 char name[_POSIX2_LINE_MAX];
403 size_t namelen;
404
405 again:
406 if ((c = input_getc_plain()) != '$')
407 return (c);
408
409 if ((c = input_getc()) == EOF)
410 report("unexpected EOF");
411 if (c != '(')
412 report("expecting '(' after '$'");
413
414 namelen = 0;
415 while ((c = input_getc()) != EOF && c != ')') {
416 if (isalpha(c) || c == '_' || (namelen != 0 && isdigit(c)))
417 name[namelen++] = c;
418 else
419 goto badchar;
420 }
421 if (c == EOF)
422 report("unexpected EOF");
423 name[namelen++] = '\0';
424
425 LIST_FOREACH(m, ¯os, link)
426 if (strcmp(m->name, name) == 0)
427 break;
428 if (m == NULL)
429 report("undefined macro '%s'", name);
430
431 input_open_macro(m);
432 goto again;
433
434 badchar:
435 if (!isascii(c) || !isprint(c))
436 report("unexpected character %#2x", (u_int)c);
437 else
438 report("bad character '%c'", c);
439 }
440
441
442 static void
input_getnum(u_int base,u_int flen)443 input_getnum(u_int base, u_int flen)
444 {
445 int c;
446 u_int cnt;
447
448 cnt = 0;
449 numval = 0;
450 while (flen == 0 || cnt < flen) {
451 if ((c = input_getc()) == EOF) {
452 if (cnt == 0)
453 report("bad number");
454 return;
455 }
456 if (isdigit(c)) {
457 if (base == 8 && (c == '8' || c == '9')) {
458 input_ungetc(c);
459 if (cnt == 0)
460 report("bad number");
461 return;
462 }
463 numval = numval * base + (c - '0');
464 } else if (base == 16 && isxdigit(c)) {
465 if (islower(c))
466 numval = numval * base + (c - 'a' + 10);
467 else
468 numval = numval * base + (c - 'A' + 10);
469 } else {
470 input_ungetc(c);
471 if (cnt == 0)
472 report("bad number");
473 return;
474 }
475 cnt++;
476 }
477 }
478
479 static int
480 # ifdef DEBUGGING
_gettoken(void)481 _gettoken(void)
482 # else
483 gettoken(void)
484 # endif
485 {
486 int c;
487 char *end;
488 static const char esc[] = "abfnrtv";
489 static const char chr[] = "\a\b\f\n\r\t\v";
490
491 /*
492 * Skip any whitespace before the next token
493 */
494 while ((c = input_getc()) != EOF) {
495 if (!isspace(c) || c == '\n')
496 break;
497 }
498 if (c == EOF)
499 return (token = TOK_EOF);
500 if (!isascii(c))
501 goto badchar;
502
503 /*
504 * Skip comments
505 */
506 if (c == '#') {
507 while ((c = input_getc_plain()) != EOF) {
508 if (c == '\n')
509 return (token = TOK_EOL);
510 }
511 goto badeof;
512 }
513
514 /*
515 * Single character tokens
516 */
517 if (c == '\n')
518 return (token = TOK_EOL);
519 if (c == '.' || c == '%' || c == '=' || c == '<' || c == '>')
520 return (token = c);
521 if (c == ':') {
522 if ((c = input_getc()) == '=')
523 return (token = TOK_ASSIGN);
524 input_ungetc(c);
525 return (token = ':');
526 }
527 if (c == '?') {
528 if ((c = input_getc()) == '=')
529 return (token = TOK_QASSIGN);
530 input_ungetc(c);
531 goto badchar;
532 }
533
534 /*
535 * Sort out numbers
536 */
537 if (isdigit(c)) {
538 if (c == '0') {
539 if ((c = input_getc()) == 'x' || c == 'X') {
540 input_getnum(16, 0);
541 } else if (isdigit(c)) {
542 input_ungetc(c);
543 input_getnum(8, 0);
544 } else {
545 if (c != EOF)
546 input_ungetc(c);
547 numval = 0;
548 c = 1;
549 }
550 } else {
551 input_ungetc(c);
552 input_getnum(10, 0);
553 }
554 return (token = TOK_NUM);
555 }
556
557 /*
558 * Must be a string then
559 */
560 strvallen = 0;
561
562 # define GETC(C) do { \
563 if ((c = input_getc()) == EOF) \
564 goto badeof; \
565 if (!isascii(c) || (!isprint(c) && c != '\t')) \
566 goto badchar; \
567 } while(0)
568
569 if (c == '"') {
570 for(;;) {
571 GETC(c);
572 if (c == '"') {
573 strval[strvallen] = '\0';
574 break;
575 }
576 if (c != '\\') {
577 strval[strvallen++] = c;
578 continue;
579 }
580 GETC(c);
581 if ((end = strchr(esc, c)) != NULL) {
582 strval[strvallen++] = chr[end - esc];
583 continue;
584 }
585 if (c == 'x') {
586 input_getnum(16, 2);
587 c = numval;
588 } else if (c >= '0' && c <= '7') {
589 input_ungetc(c);
590 input_getnum(8, 3);
591 c = numval;
592 }
593 strval[strvallen++] = c;
594 }
595 # undef GETC
596
597 } else if (c == '[') {
598 /*
599 * Skip leading space
600 */
601 while ((c = input_getc()) != EOF && isspace(c))
602 ;
603 if (c == EOF)
604 goto badeof;
605 while (c != ']' && !isspace(c)) {
606 if (!isalnum(c) && c != '.' && c != '-')
607 goto badchar;
608 strval[strvallen++] = c;
609 if ((c = input_getc()) == EOF)
610 goto badeof;
611 }
612 while (c != ']' && isspace(c)) {
613 if ((c = input_getc()) == EOF)
614 goto badeof;
615 }
616 if (c != ']')
617 goto badchar;
618 strval[strvallen] = '\0';
619 return (token = TOK_HOST);
620
621 } else if (!isalpha(c) && c != '_') {
622 goto badchar;
623
624 } else {
625 for (;;) {
626 strval[strvallen++] = c;
627 if ((c = input_getc()) == EOF)
628 goto badeof;
629 if (!isalnum(c) && c != '_' && c != '-') {
630 input_ungetc(c);
631 strval[strvallen] = '\0';
632 break;
633 }
634 }
635 }
636
637 return (token = TOK_STR);
638
639 badeof:
640 report("unexpected EOF");
641
642 badchar:
643 if (!isascii(c) || !isprint(c))
644 report("unexpected character %#2x", (u_int)c);
645 else
646 report("bad character '%c'", c);
647 }
648
649 # ifdef DEBUGGING
650 static int
gettoken()651 gettoken()
652 {
653 _gettoken();
654 if (isascii(token) && isprint(token))
655 printf("(%c)", token);
656 else {
657 switch (token) {
658
659 case TOK_EOF:
660 printf("(EOF)");
661 break;
662 case TOK_EOL:
663 printf("(EOL)");
664 break;
665 case TOK_NUM:
666 printf("(NUM %ju)", (uintmax_t)numval);
667 break;
668 case TOK_STR:
669 printf("(STR %.*s)", (int)strvallen, strval);
670 break;
671 case TOK_HOST:
672 printf("(HOST %s)", strval);
673 break;
674 default:
675 printf("(%#2x)", token);
676 break;
677 }
678 }
679 return (token);
680 }
681 #endif
682
683
684 /*
685 * Try to execute the assignment.
686 */
687 static void
handle_assignment(const struct snmp_node * node,struct asn_oid * vindex,const struct snmp_value * value)688 handle_assignment(const struct snmp_node *node, struct asn_oid *vindex,
689 const struct snmp_value *value)
690 {
691 u_int i;
692 int err;
693 struct assign *tp;
694 char nodename[100];
695
696 if (node->type == SNMP_NODE_LEAF) {
697 /* index must be one single zero or no index at all */
698 if (vindex->len > 1 || (vindex->len == 1 &&
699 vindex->subs[0] != 0))
700 report("bad index on leaf node");
701 vindex->len = 1;
702 vindex->subs[0] = 0;
703 } else {
704 /* resulting oid must not be too long */
705 if (node->oid.len + vindex->len > ASN_MAXOIDLEN)
706 report("resulting OID too long");
707 }
708
709 /*
710 * Get the next assignment entry for the transaction.
711 */
712 if ((tp = malloc(sizeof(*tp))) == NULL)
713 report("%m");
714
715 tp->value = *value;
716 tp->node_name = node->name;
717
718 /*
719 * Build the OID
720 */
721 tp->value.var = node->oid;
722 for (i = 0; i < vindex->len; i++)
723 tp->value.var.subs[tp->value.var.len++] = vindex->subs[i];
724
725 /*
726 * Puzzle together the variables for the call and call the
727 * set routine. The set routine may make our node pointer
728 * invalid (if we happend to call the module loader) so
729 * get a copy of the node name beforehands.
730 */
731 snprintf(nodename, sizeof(nodename), "%s", node->name);
732 snmp_ctx->scratch = &tp->scratch;
733 snmp_ctx->var_index = 0;
734 err = (*node->op)(snmp_ctx, &tp->value, node->oid.len, node->index,
735 SNMP_OP_SET);
736 if (err != 0) {
737 free(tp);
738 report("assignment to %s.%s returns %d", nodename,
739 asn_oid2str(vindex), err);
740 }
741
742 TAILQ_INSERT_TAIL(&assigns, tp, link);
743 }
744
745
746 /*
747 * Parse the section statement
748 */
749 static void
parse_section(const struct lmodule * mod)750 parse_section(const struct lmodule *mod)
751 {
752 if (token != TOK_STR)
753 report("expecting section name");
754
755 if (strcmp(strval, "snmpd") == 0) {
756 if (mod != NULL)
757 /* loading a module - ignore common stuff */
758 ignore = 1;
759 else
760 /* global configuration - don't ignore */
761 ignore = 0;
762 } else {
763 if (mod == NULL) {
764 /* global configuration - ignore module stuff */
765 ignore = 1;
766 } else {
767 /* loading module - check if it's our section */
768 ignore = (strcmp(strval, mod->section) != 0);
769 }
770 }
771 gettoken();
772 }
773
774 /*
775 * Convert a hostname to four u_chars
776 */
777 static void
gethost(const char * host,u_char * ip)778 gethost(const char *host, u_char *ip)
779 {
780 struct addrinfo hints, *res;
781 int error;
782 struct sockaddr_in *sain;
783
784 memset(&hints, 0, sizeof(hints));
785 hints.ai_family = AF_INET;
786 hints.ai_socktype = SOCK_DGRAM;
787 hints.ai_protocol = IPPROTO_UDP;
788 hints.ai_flags = AI_PASSIVE;
789 error = getaddrinfo(host, NULL, &hints, &res);
790 if (error != 0)
791 report("%s: %s", host, gai_strerror(error));
792 if (res == NULL)
793 report("%s: unknown hostname", host);
794
795 sain = (struct sockaddr_in *)(void *)res->ai_addr;
796 sain->sin_addr.s_addr = ntohl(sain->sin_addr.s_addr);
797 ip[0] = sain->sin_addr.s_addr >> 24;
798 ip[1] = sain->sin_addr.s_addr >> 16;
799 ip[2] = sain->sin_addr.s_addr >> 8;
800 ip[3] = sain->sin_addr.s_addr >> 0;
801
802 freeaddrinfo(res);
803 }
804
805 /*
806 * Parse the left hand side of a config line.
807 */
808 static const struct snmp_node *
parse_oid(const char * varname,struct asn_oid * oid)809 parse_oid(const char *varname, struct asn_oid *oid)
810 {
811 struct snmp_node *node;
812 u_int i;
813 u_char ip[4];
814 struct asn_oid str_oid;
815
816 for (node = tree; node < &tree[tree_size]; node++)
817 if (strcmp(varname, node->name) == 0)
818 break;
819 if (node == &tree[tree_size])
820 node = NULL;
821
822 oid->len = 0;
823 while (token == '.') {
824 if (gettoken() == TOK_NUM) {
825 if (numval > ASN_MAXID)
826 report("subid too large %#jx",
827 (uintmax_t)numval);
828 if (oid->len == ASN_MAXOIDLEN)
829 report("index too long");
830 if (gettoken() != ':')
831 oid->subs[oid->len++] = numval;
832 else {
833 str_oid.len = 0;
834 str_oid.subs[str_oid.len++] = numval;
835 while (gettoken() == TOK_NUM) {
836 str_oid.subs[str_oid.len++] = numval;
837 if (gettoken() != ':')
838 break;
839 }
840 oid->subs[oid->len++] = str_oid.len;
841 asn_append_oid(oid, &str_oid);
842 }
843
844 } else if (token == TOK_STR) {
845 if (strvallen + oid->len + 1 > ASN_MAXOIDLEN)
846 report("oid too long");
847 oid->subs[oid->len++] = strvallen;
848 for (i = 0; i < strvallen; i++)
849 oid->subs[oid->len++] = strval[i];
850 gettoken();
851
852 } else if (token == TOK_HOST) {
853 gethost(strval, ip);
854 if (oid->len + 4 > ASN_MAXOIDLEN)
855 report("index too long");
856 for (i = 0; i < 4; i++)
857 oid->subs[oid->len++] = ip[i];
858 gettoken();
859 } else
860 report("bad token in index");
861 }
862
863 return (node);
864 }
865
866 /*
867 * Parse the value for an assignment.
868 */
869 static void
parse_syntax_null(struct snmp_value * value __unused)870 parse_syntax_null(struct snmp_value *value __unused)
871 {
872 if (token != TOK_EOL)
873 report("bad NULL syntax");
874 }
875
876 static void
parse_syntax_integer(struct snmp_value * value)877 parse_syntax_integer(struct snmp_value *value)
878 {
879 if (token != TOK_NUM)
880 report("bad INTEGER syntax");
881 if (numval > 0x7fffffff)
882 report("INTEGER too large %ju", (uintmax_t)numval);
883
884 value->v.integer = numval;
885 gettoken();
886 }
887
888 static void
parse_syntax_counter64(struct snmp_value * value)889 parse_syntax_counter64(struct snmp_value *value)
890 {
891 if (token != TOK_NUM)
892 report("bad COUNTER64 syntax");
893
894 value->v.counter64 = numval;
895 gettoken();
896 }
897
898 static void
parse_syntax_octetstring(struct snmp_value * value)899 parse_syntax_octetstring(struct snmp_value *value)
900 {
901 u_long alloc;
902 u_char *noct;
903
904 if (token == TOK_STR) {
905 value->v.octetstring.len = strvallen;
906 value->v.octetstring.octets = malloc(strvallen);
907 (void)memcpy(value->v.octetstring.octets, strval, strvallen);
908 gettoken();
909 return;
910 }
911
912 /* XX:XX:XX syntax */
913 value->v.octetstring.octets = NULL;
914 value->v.octetstring.len = 0;
915
916 if (token != TOK_NUM)
917 /* empty string is allowed */
918 return;
919
920 if (ERRPUSH()) {
921 free(value->v.octetstring.octets);
922 ERRNEXT();
923 }
924
925 alloc = 0;
926 for (;;) {
927 if (token != TOK_NUM)
928 report("bad OCTETSTRING syntax");
929 if (numval > 0xff)
930 report("byte value too large");
931 if (alloc == value->v.octetstring.len) {
932 alloc += 100;
933 noct = realloc(value->v.octetstring.octets, alloc);
934 if (noct == NULL)
935 report("%m");
936 value->v.octetstring.octets = noct;
937 }
938 value->v.octetstring.octets[value->v.octetstring.len++]
939 = numval;
940 if (gettoken() != ':')
941 break;
942 gettoken();
943 }
944 ERRPOP();
945 }
946
947 static void
parse_syntax_oid(struct snmp_value * value)948 parse_syntax_oid(struct snmp_value *value)
949 {
950 value->v.oid.len = 0;
951
952 if (token != TOK_NUM)
953 return;
954
955 for (;;) {
956 if (token != TOK_NUM)
957 report("bad OID syntax");
958 if (numval > ASN_MAXID)
959 report("subid too large");
960 if (value->v.oid.len == ASN_MAXOIDLEN)
961 report("OID too long");
962 value->v.oid.subs[value->v.oid.len++] = numval;
963 if (gettoken() != '.')
964 break;
965 gettoken();
966 }
967 }
968
969 static void
parse_syntax_ipaddress(struct snmp_value * value)970 parse_syntax_ipaddress(struct snmp_value *value)
971 {
972 int i;
973 u_char ip[4];
974
975 if (token == TOK_NUM) {
976 /* numerical address */
977 i = 0;
978 for (;;) {
979 if (numval >= 256)
980 report("ip address part too large");
981 value->v.ipaddress[i++] = numval;
982 if (i == 4)
983 break;
984 if (gettoken() != '.')
985 report("expecting '.' in ip address");
986 }
987 gettoken();
988
989 } else if (token == TOK_HOST) {
990 /* host name */
991 gethost(strval, ip);
992 for (i = 0; i < 4; i++)
993 value->v.ipaddress[i] = ip[i];
994 gettoken();
995
996 } else
997 report("bad ip address syntax");
998 }
999
1000 static void
parse_syntax_uint32(struct snmp_value * value)1001 parse_syntax_uint32(struct snmp_value *value)
1002 {
1003
1004 if (token != TOK_NUM)
1005 report("bad number syntax");
1006 if (numval > 0xffffffff)
1007 report("number too large");
1008 value->v.uint32 = numval;
1009 gettoken();
1010 }
1011
1012 /*
1013 * Parse an assignement line
1014 */
1015 static void
parse_assign(const char * varname)1016 parse_assign(const char *varname)
1017 {
1018 struct snmp_value value;
1019 struct asn_oid vindex;
1020 const struct snmp_node *node;
1021
1022 node = parse_oid(varname, &vindex);
1023 if (token != '=')
1024 report("'=' expected, got '%c'", token);
1025 gettoken();
1026
1027 if (ignore) {
1028 /* skip rest of line */
1029 while (token != TOK_EOL && token != TOK_EOF)
1030 gettoken();
1031 return;
1032 }
1033 if (node == NULL)
1034 report("unknown variable");
1035
1036 switch (value.syntax = node->syntax) {
1037
1038 case SNMP_SYNTAX_NULL:
1039 parse_syntax_null(&value);
1040 break;
1041
1042 case SNMP_SYNTAX_INTEGER:
1043 parse_syntax_integer(&value);
1044 break;
1045
1046 case SNMP_SYNTAX_COUNTER64:
1047 parse_syntax_counter64(&value);
1048 break;
1049
1050 case SNMP_SYNTAX_OCTETSTRING:
1051 parse_syntax_octetstring(&value);
1052 break;
1053
1054 case SNMP_SYNTAX_OID:
1055 parse_syntax_oid(&value);
1056 break;
1057
1058 case SNMP_SYNTAX_IPADDRESS:
1059 parse_syntax_ipaddress(&value);
1060 break;
1061
1062 case SNMP_SYNTAX_COUNTER:
1063 case SNMP_SYNTAX_GAUGE:
1064 case SNMP_SYNTAX_TIMETICKS:
1065 parse_syntax_uint32(&value);
1066 break;
1067
1068 case SNMP_SYNTAX_NOSUCHOBJECT:
1069 case SNMP_SYNTAX_NOSUCHINSTANCE:
1070 case SNMP_SYNTAX_ENDOFMIBVIEW:
1071 abort();
1072 }
1073
1074 if (ERRPUSH()) {
1075 snmp_value_free(&value);
1076 ERRNEXT();
1077 }
1078
1079 handle_assignment(node, &vindex, &value);
1080
1081 ERRPOP();
1082 }
1083
1084 /*
1085 * Handle macro definition line
1086 * We have already seen the := and the input now stands at the character
1087 * after the =. Skip whitespace and then call the input routine directly to
1088 * eat up characters.
1089 */
1090 static void
parse_define(const char * varname)1091 parse_define(const char *varname)
1092 {
1093 char *volatile string;
1094 char *new;
1095 volatile size_t alloc, length;
1096 int c;
1097 struct macro *m;
1098 int t = token;
1099
1100 alloc = 100;
1101 length = 0;
1102 if ((string = malloc(alloc)) == NULL)
1103 report("%m");
1104
1105 if (ERRPUSH()) {
1106 free(string);
1107 ERRNEXT();
1108 }
1109
1110 while ((c = input_getc_plain()) != EOF) {
1111 if (c == '\n' || !isspace(c))
1112 break;
1113 }
1114
1115 while (c != EOF && c != '#' && c != '\n') {
1116 if (alloc == length) {
1117 alloc *= 2;
1118 if ((new = realloc(string, alloc)) == NULL)
1119 report("%m");
1120 string = new;
1121 }
1122 string[length++] = c;
1123 c = input_getc_plain();
1124 }
1125 if (c == '#') {
1126 while ((c = input_getc_plain()) != EOF && c != '\n')
1127 ;
1128 }
1129 if (c == EOF)
1130 report("EOF in macro definition");
1131
1132 LIST_FOREACH(m, ¯os, link)
1133 if (strcmp(m->name, varname) == 0)
1134 break;
1135
1136 if (m == NULL) {
1137 if ((m = malloc(sizeof(*m))) == NULL)
1138 report("%m");
1139 if ((m->name = malloc(strlen(varname) + 1)) == NULL) {
1140 free(m);
1141 report("%m");
1142 }
1143 strcpy(m->name, varname);
1144 m->perm = 0;
1145 LIST_INSERT_HEAD(¯os, m, link);
1146
1147 m->value = string;
1148 m->length = length;
1149 } else {
1150 if (t == TOK_ASSIGN) {
1151 free(m->value);
1152 m->value = string;
1153 m->length = length;
1154 } else
1155 free(string);
1156 }
1157
1158 token = TOK_EOL;
1159
1160 ERRPOP();
1161 }
1162
1163 /*
1164 * Free all macros
1165 */
1166 static void
macro_free_all(void)1167 macro_free_all(void)
1168 {
1169 static struct macro *m, *m1;
1170
1171 m = LIST_FIRST(¯os);
1172 while (m != NULL) {
1173 m1 = LIST_NEXT(m, link);
1174 if (!m->perm) {
1175 free(m->name);
1176 free(m->value);
1177 LIST_REMOVE(m, link);
1178 free(m);
1179 }
1180 m = m1;
1181 }
1182 }
1183
1184 /*
1185 * Parse an include directive and switch to the new file
1186 */
1187 static void
parse_include(void)1188 parse_include(void)
1189 {
1190 int sysdir = 0;
1191 char fname[_POSIX2_LINE_MAX];
1192
1193 if (gettoken() == '<') {
1194 sysdir = 1;
1195 if (gettoken() != TOK_STR)
1196 report("expecting filename after in .include");
1197 } else if (token != TOK_STR)
1198 report("expecting filename after in .include");
1199
1200 strcpy(fname, strval);
1201 if (sysdir && gettoken() != '>')
1202 report("expecting '>'");
1203 gettoken();
1204 if (input_open_file(fname, sysdir) == -1)
1205 report("%s: %m", fname);
1206 }
1207
1208 /*
1209 * Parse the configuration file
1210 */
1211 static void
parse_file(const struct lmodule * mod)1212 parse_file(const struct lmodule *mod)
1213 {
1214 char varname[_POSIX2_LINE_MAX];
1215
1216 while (gettoken() != TOK_EOF) {
1217 if (token == TOK_EOL)
1218 /* empty line */
1219 continue;
1220 if (token == '%') {
1221 gettoken();
1222 parse_section(mod);
1223 } else if (token == '.') {
1224 if (gettoken() != TOK_STR)
1225 report("keyword expected after '.'");
1226 if (strcmp(strval, "include") == 0)
1227 parse_include();
1228 else
1229 report("unknown keyword '%s'", strval);
1230 } else if (token == TOK_STR) {
1231 strcpy(varname, strval);
1232 if (gettoken() == TOK_ASSIGN || token == TOK_QASSIGN)
1233 parse_define(varname);
1234 else
1235 parse_assign(varname);
1236 }
1237 if (token != TOK_EOL)
1238 report("eol expected");
1239 }
1240 }
1241
1242 /*
1243 * Do rollback on errors
1244 */
1245 static void
do_rollback(void)1246 do_rollback(void)
1247 {
1248 struct assign *tp;
1249 struct snmp_node *node;
1250
1251 while ((tp = TAILQ_LAST(&assigns, assigns)) != NULL) {
1252 TAILQ_REMOVE(&assigns, tp, link);
1253 for (node = tree; node < &tree[tree_size]; node++)
1254 if (node->name == tp->node_name) {
1255 snmp_ctx->scratch = &tp->scratch;
1256 (void)(*node->op)(snmp_ctx, &tp->value,
1257 node->oid.len, node->index,
1258 SNMP_OP_ROLLBACK);
1259 break;
1260 }
1261 if (node == &tree[tree_size])
1262 syslog(LOG_ERR, "failed to find node for "
1263 "rollback");
1264 snmp_value_free(&tp->value);
1265 free(tp);
1266 }
1267 }
1268
1269 /*
1270 * Do commit
1271 */
1272 static void
do_commit(void)1273 do_commit(void)
1274 {
1275 struct assign *tp;
1276 struct snmp_node *node;
1277
1278 while ((tp = TAILQ_FIRST(&assigns)) != NULL) {
1279 TAILQ_REMOVE(&assigns, tp, link);
1280 for (node = tree; node < &tree[tree_size]; node++)
1281 if (node->name == tp->node_name) {
1282 snmp_ctx->scratch = &tp->scratch;
1283 (void)(*node->op)(snmp_ctx, &tp->value,
1284 node->oid.len, node->index, SNMP_OP_COMMIT);
1285 break;
1286 }
1287 if (node == &tree[tree_size])
1288 syslog(LOG_ERR, "failed to find node for commit");
1289 snmp_value_free(&tp->value);
1290 free(tp);
1291 }
1292 }
1293
1294 /*
1295 * Read the configuration file. Handle the entire file as one transaction.
1296 *
1297 * If lodmod is NULL, the sections for 'snmpd' and all loaded modules are
1298 * executed. If it is not NULL, only the sections for that module are handled.
1299 */
1300 int
read_config(const char * fname,struct lmodule * lodmod)1301 read_config(const char *fname, struct lmodule *lodmod)
1302 {
1303 int err;
1304 char objbuf[ASN_OIDSTRLEN];
1305 char idxbuf[ASN_OIDSTRLEN];
1306
1307 ignore = 0;
1308
1309 input_push = 0;
1310
1311 if (ERRPUSH())
1312 return (-1);
1313 if (input_open_file(fname, 0) == -1) {
1314 syslog(LOG_ERR, "%s: %m", fname);
1315 return (-1);
1316 }
1317 ERRPOP();
1318 community = COMM_INITIALIZE;
1319
1320 if ((snmp_ctx = snmp_init_context()) == NULL) {
1321 input_close_all();
1322 syslog(LOG_ERR, "%m");
1323 return (-1);
1324 }
1325
1326 if (ERRPUSH()) {
1327 do_rollback();
1328 input_close_all();
1329 macro_free_all();
1330 free(snmp_ctx);
1331 return (-1);
1332 }
1333 parse_file(lodmod);
1334 ERRPOP();
1335
1336 if ((err = snmp_dep_commit(snmp_ctx)) != SNMP_ERR_NOERROR) {
1337 syslog(LOG_ERR, "init dep failed: %u %s %s", err,
1338 asn_oid2str_r(&snmp_ctx->dep->obj, objbuf),
1339 asn_oid2str_r(&snmp_ctx->dep->idx, idxbuf));
1340 snmp_dep_rollback(snmp_ctx);
1341 do_rollback();
1342 input_close_all();
1343 macro_free_all();
1344 free(snmp_ctx);
1345 return (-1);
1346 }
1347
1348 do_commit();
1349 snmp_dep_finish(snmp_ctx);
1350
1351 macro_free_all();
1352
1353 free(snmp_ctx);
1354
1355 return (0);
1356 }
1357
1358 /*
1359 * Define a permanent macro
1360 */
1361 int
define_macro(const char * name,const char * value)1362 define_macro(const char *name, const char *value)
1363 {
1364 struct macro *m;
1365
1366 if ((m = malloc(sizeof(*m))) == NULL)
1367 return (-1);
1368 if ((m->name = malloc(strlen(name) + 1)) == NULL) {
1369 free(m);
1370 return (-1);
1371 }
1372 strcpy(m->name, name);
1373 if ((m->value = malloc(strlen(value) + 1)) == NULL) {
1374 free(m->name);
1375 free(m);
1376 return (-1);
1377 }
1378 strcpy(m->value, value);
1379 m->length = strlen(value);
1380 m->perm = 1;
1381 LIST_INSERT_HEAD(¯os, m, link);
1382 return (0);
1383 }
1384