1 /*
2  * read_config.c
3  */
4 /* Portions of this file are subject to the following copyright(s).  See
5  * the Net-SNMP's COPYING file for more details and other copyrights
6  * that may apply:
7  */
8 /*
9  * Portions of this file are copyrighted by:
10  * Copyright � 2003 Sun Microsystems, Inc. All rights reserved.
11  * Use is subject to license terms specified in the COPYING file
12  * distributed with the Net-SNMP package.
13  */
14 
15 /** @defgroup read_config parsing various configuration files at run time
16  *  @ingroup library
17  *
18  * The read_config related functions are a fairly extensible  system  of
19  * parsing various configuration files at the run time.
20  *
21  * The idea is that the calling application is able to register
22  * handlers for certain tokens specified in certain types
23  * of files.  The read_configs function can then be  called
24  * to  look  for all the files that it has registrations for,
25  * find the first word on each line, and pass  the  remainder
26  * to the appropriately registered handler.
27  *
28  * For persistent configuration storage you will need to use the
29  * read_config_read_data, read_config_store, and read_config_store_data
30  * APIs in conjunction with first registering a
31  * callback so when the agent shutsdown for whatever reason data is written
32  * to your configuration files.  The following explains in more detail the
33  * sequence to make this happen.
34  *
35  * This is the callback registration API, you need to call this API with
36  * the appropriate parameters in order to configure persistent storage needs.
37  *
38  *        int snmp_register_callback(int major, int minor,
39  *                                   SNMPCallback *new_callback,
40  *                                   void *arg);
41  *
42  * You will need to set major to SNMP_CALLBACK_LIBRARY, minor to
43  * SNMP_CALLBACK_STORE_DATA. arg is whatever you want.
44  *
45  * Your callback function's prototype is:
46  * int     (SNMPCallback) (int majorID, int minorID, void *serverarg,
47  *                        void *clientarg);
48  *
49  * The majorID, minorID and clientarg are what you passed in the callback
50  * registration above.  When the callback is called you have to essentially
51  * transfer all your state from memory to disk. You do this by generating
52  * configuration lines into a buffer.  The lines are of the form token
53  * followed by token parameters.
54  *
55  * Finally storing is done using read_config_store(type, buffer);
56  * type is the application name this can be obtained from:
57  *
58  * netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_APPTYPE);
59  *
60  * Now, reading back the data: This is done by registering a config handler
61  * for your token using the register_config_handler function. Your
62  * handler will be invoked and you can parse in the data using the
63  * read_config_read APIs.
64  *
65  *  @{
66  */
67 #include <net-snmp/net-snmp-config.h>
68 #include <net-snmp/net-snmp-features.h>
69 
70 #include <stdio.h>
71 #include <ctype.h>
72 #if HAVE_STDLIB_H
73 #include <stdlib.h>
74 #endif
75 #if HAVE_STRING_H
76 #include <string.h>
77 #else
78 #include <strings.h>
79 #endif
80 #if HAVE_UNISTD_H
81 #include <unistd.h>
82 #endif
83 #include <sys/types.h>
84 #if HAVE_SYS_PARAM_H
85 #include <sys/param.h>
86 #endif
87 #if TIME_WITH_SYS_TIME
88 # include <sys/time.h>
89 # include <time.h>
90 #else
91 # if HAVE_SYS_TIME_H
92 #  include <sys/time.h>
93 # else
94 #  include <time.h>
95 # endif
96 #endif
97 #ifdef HAVE_SYS_STAT_H
98 #include <sys/stat.h>
99 #endif
100 #if HAVE_NETINET_IN_H
101 #include <netinet/in.h>
102 #endif
103 #if HAVE_ARPA_INET_H
104 #include <arpa/inet.h>
105 #endif
106 #if HAVE_SYS_SELECT_H
107 #include <sys/select.h>
108 #endif
109 #if HAVE_SYS_SOCKET_H
110 #include <sys/socket.h>
111 #endif
112 #if HAVE_NETDB_H
113 #include <netdb.h>
114 #endif
115 #include <errno.h>
116 #if HAVE_IO_H
117 #include <io.h>
118 #endif
119 
120 #if HAVE_DIRENT_H
121 # include <dirent.h>
122 # define NAMLEN(dirent) strlen((dirent)->d_name)
123 #else
124 # define dirent direct
125 # define NAMLEN(dirent) (dirent)->d_namlen
126 # if HAVE_SYS_NDIR_H
127 #  include <sys/ndir.h>
128 # endif
129 # if HAVE_SYS_DIR_H
130 #  include <sys/dir.h>
131 # endif
132 # if HAVE_NDIR_H
133 #  include <ndir.h>
134 # endif
135 #endif
136 
137 #include <net-snmp/types.h>
138 #include <net-snmp/output_api.h>
139 #include <net-snmp/config_api.h>
140 #include <net-snmp/library/read_config.h>       /* for "internal" definitions */
141 #include <net-snmp/utilities.h>
142 
143 #include <net-snmp/library/mib.h>
144 #include <net-snmp/library/parse.h>
145 #include <net-snmp/library/snmp_api.h>
146 #include <net-snmp/library/callback.h>
147 
148 netsnmp_feature_child_of(read_config_all, libnetsnmp);
149 
150 netsnmp_feature_child_of(unregister_app_config_handler, read_config_all);
151 netsnmp_feature_child_of(read_config_register_app_prenetsnmp_mib_handler, netsnmp_unused);
152 
153 static int      config_errors;
154 
155 struct config_files *config_files = NULL;
156 
157 
158 static struct config_line *
internal_register_config_handler(const char * type_param,const char * token,void (* parser)(const char *,char *),void (* releaser)(void),const char * help,int when)159 internal_register_config_handler(const char *type_param,
160 				 const char *token,
161 				 void (*parser) (const char *, char *),
162 				 void (*releaser) (void), const char *help,
163 				 int when)
164 {
165     struct config_files **ctmp = &config_files;
166     struct config_line  **ltmp;
167     const char           *type = type_param;
168 
169     if (type == NULL || *type == '\0') {
170         type = netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID,
171 				     NETSNMP_DS_LIB_APPTYPE);
172     }
173 
174     /*
175      * Handle multiple types (recursively)
176      */
177     if (strchr(type, ':')) {
178         struct config_line *ltmp2 = NULL;
179         char                buf[STRINGMAX];
180         char               *cptr = buf;
181 
182         strlcpy(buf, type, STRINGMAX);
183         while (cptr) {
184             char* c = cptr;
185             cptr = strchr(cptr, ':');
186             if(cptr) {
187                 *cptr = '\0';
188                 ++cptr;
189             }
190             ltmp2 = internal_register_config_handler(c, token, parser,
191                                                      releaser, help, when);
192         }
193         return ltmp2;
194     }
195 
196     /*
197      * Find type in current list  -OR-  create a new file type.
198      */
199     while (*ctmp != NULL && strcmp((*ctmp)->fileHeader, type)) {
200         ctmp = &((*ctmp)->next);
201     }
202 
203     if (*ctmp == NULL) {
204         *ctmp = (struct config_files *)
205             calloc(1, sizeof(struct config_files));
206         if (!*ctmp) {
207             return NULL;
208         }
209 
210         (*ctmp)->fileHeader = strdup(type);
211         DEBUGMSGTL(("9:read_config:type", "new type %s\n", type));
212     }
213 
214     DEBUGMSGTL(("9:read_config:register_handler", "registering %s %s\n",
215                 type, token));
216     /*
217      * Find parser type in current list  -OR-  create a new
218      * line parser entry.
219      */
220     ltmp = &((*ctmp)->start);
221 
222     while (*ltmp != NULL && strcmp((*ltmp)->config_token, token)) {
223         ltmp = &((*ltmp)->next);
224     }
225 
226     if (*ltmp == NULL) {
227         *ltmp = (struct config_line *)
228             calloc(1, sizeof(struct config_line));
229         if (!*ltmp) {
230             return NULL;
231         }
232 
233         (*ltmp)->config_time = when;
234         (*ltmp)->config_token = strdup(token);
235         if (help != NULL)
236             (*ltmp)->help = strdup(help);
237     }
238 
239     /*
240      * Add/Replace the parse/free functions for the given line type
241      * in the given file type.
242      */
243     (*ltmp)->parse_line = parser;
244     (*ltmp)->free_func = releaser;
245 
246     return (*ltmp);
247 
248 }                               /* end register_config_handler() */
249 
250 struct config_line *
register_prenetsnmp_mib_handler(const char * type,const char * token,void (* parser)(const char *,char *),void (* releaser)(void),const char * help)251 register_prenetsnmp_mib_handler(const char *type,
252                                 const char *token,
253                                 void (*parser) (const char *, char *),
254                                 void (*releaser) (void), const char *help)
255 {
256     return internal_register_config_handler(type, token, parser, releaser,
257 					    help, PREMIB_CONFIG);
258 }
259 
260 #ifndef NETSNMP_FEATURE_REMOVE_READ_CONFIG_REGISTER_APP_PRENETSNMP_MIB_HANDLER
261 struct config_line *
register_app_prenetsnmp_mib_handler(const char * token,void (* parser)(const char *,char *),void (* releaser)(void),const char * help)262 register_app_prenetsnmp_mib_handler(const char *token,
263                                     void (*parser) (const char *, char *),
264                                     void (*releaser) (void),
265                                     const char *help)
266 {
267     return (register_prenetsnmp_mib_handler
268             (NULL, token, parser, releaser, help));
269 }
270 #endif /* NETSNMP_FEATURE_REMOVE_READ_CONFIG_REGISTER_APP_PRENETSNMP_MIB_HANDLER */
271 
272 /**
273  * register_config_handler registers handlers for certain tokens specified in
274  * certain types of files.
275  *
276  * Allows a module writer use/register multiple configuration files based off
277  * of the type parameter.  A module writer may want to set up multiple
278  * configuration files to separate out related tasks/variables or just for
279  * management of where to put tokens as the module or modules get more complex
280  * in regard to handling token registrations.
281  *
282  * @param type     the configuration file used, e.g., if snmp.conf is the
283  *                 file where the token is located use "snmp" here.
284  *                 Multiple colon separated tokens might be used.
285  *                 If NULL or "" then the configuration file used will be
286  *                 \<application\>.conf.
287  *
288  * @param token    the token being parsed from the file.  Must be non-NULL.
289  *
290  * @param parser   the handler function pointer that use  the specified
291  *                 token and the rest of the line to do whatever is required
292  *                 Should be non-NULL in order to make use of this API.
293  *
294  * @param releaser if non-NULL, the function specified is called when
295  *                 unregistering config handler or when configuration
296  *                 files are re-read.
297  *                 This function should free any resources allocated by
298  *                 the token handler function.
299  *
300  * @param help     if non-NULL, used to display help information on the
301  *                 expected arguments after the token.
302  *
303  * @return Pointer to a new config line entry or NULL on error.
304  */
305 struct config_line *
register_config_handler(const char * type,const char * token,void (* parser)(const char *,char *),void (* releaser)(void),const char * help)306 register_config_handler(const char *type,
307 			const char *token,
308 			void (*parser) (const char *, char *),
309 			void (*releaser) (void), const char *help)
310 {
311     return internal_register_config_handler(type, token, parser, releaser,
312 					    help, NORMAL_CONFIG);
313 }
314 
315 struct config_line *
register_const_config_handler(const char * type,const char * token,void (* parser)(const char *,const char *),void (* releaser)(void),const char * help)316 register_const_config_handler(const char *type,
317                               const char *token,
318                               void (*parser) (const char *, const char *),
319                               void (*releaser) (void), const char *help)
320 {
321     return internal_register_config_handler(type, token,
322                                             (void(*)(const char *, char *))
323                                             parser, releaser,
324 					    help, NORMAL_CONFIG);
325 }
326 
327 struct config_line *
register_app_config_handler(const char * token,void (* parser)(const char *,char *),void (* releaser)(void),const char * help)328 register_app_config_handler(const char *token,
329                             void (*parser) (const char *, char *),
330                             void (*releaser) (void), const char *help)
331 {
332     return (register_config_handler(NULL, token, parser, releaser, help));
333 }
334 
335 
336 
337 /**
338  * uregister_config_handler un-registers handlers given a specific type_param
339  * and token.
340  *
341  * @param type_param the configuration file used where the token is located.
342  *                   Used to lookup the config file entry
343  *
344  * @param token      the token that is being unregistered
345  *
346  * @return void
347  */
348 void
unregister_config_handler(const char * type_param,const char * token)349 unregister_config_handler(const char *type_param, const char *token)
350 {
351     struct config_files **ctmp = &config_files;
352     struct config_line  **ltmp;
353     const char           *type = type_param;
354 
355     if (type == NULL || *type == '\0') {
356         type = netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID,
357 				     NETSNMP_DS_LIB_APPTYPE);
358     }
359 
360     /*
361      * Handle multiple types (recursively)
362      */
363     if (strchr(type, ':')) {
364         char                buf[STRINGMAX];
365         char               *cptr = buf;
366 
367         strlcpy(buf, type, STRINGMAX);
368         while (cptr) {
369             char* c = cptr;
370             cptr = strchr(cptr, ':');
371             if(cptr) {
372                 *cptr = '\0';
373                 ++cptr;
374             }
375             unregister_config_handler(c, token);
376         }
377         return;
378     }
379 
380     /*
381      * find type in current list
382      */
383     while (*ctmp != NULL && strcmp((*ctmp)->fileHeader, type)) {
384         ctmp = &((*ctmp)->next);
385     }
386 
387     if (*ctmp == NULL) {
388         /*
389          * Not found, return.
390          */
391         return;
392     }
393 
394     ltmp = &((*ctmp)->start);
395     if (*ltmp == NULL) {
396         /*
397          * Not found, return.
398          */
399         return;
400     }
401     if (strcmp((*ltmp)->config_token, token) == 0) {
402         /*
403          * found it at the top of the list
404          */
405         struct config_line *ltmp2 = (*ltmp)->next;
406         if ((*ltmp)->free_func)
407             (*ltmp)->free_func();
408         SNMP_FREE((*ltmp)->config_token);
409         SNMP_FREE((*ltmp)->help);
410         SNMP_FREE(*ltmp);
411         (*ctmp)->start = ltmp2;
412         return;
413     }
414     while ((*ltmp)->next != NULL
415            && strcmp((*ltmp)->next->config_token, token)) {
416         ltmp = &((*ltmp)->next);
417     }
418     if ((*ltmp)->next != NULL) {
419         struct config_line *ltmp2 = (*ltmp)->next->next;
420         if ((*ltmp)->next->free_func)
421             (*ltmp)->next->free_func();
422         SNMP_FREE((*ltmp)->next->config_token);
423         SNMP_FREE((*ltmp)->next->help);
424         SNMP_FREE((*ltmp)->next);
425         (*ltmp)->next = ltmp2;
426     }
427 }
428 
429 #ifndef NETSNMP_FEATURE_REMOVE_UNREGISTER_APP_CONFIG_HANDLER
430 void
unregister_app_config_handler(const char * token)431 unregister_app_config_handler(const char *token)
432 {
433     unregister_config_handler(NULL, token);
434 }
435 #endif /* NETSNMP_FEATURE_REMOVE_UNREGISTER_APP_CONFIG_HANDLER */
436 
437 void
unregister_all_config_handlers(void)438 unregister_all_config_handlers(void)
439 {
440     struct config_files *ctmp, *save;
441     struct config_line *ltmp;
442 
443     /*
444      * Keep using config_files until there are no more!
445      */
446     for (ctmp = config_files; ctmp;) {
447         for (ltmp = ctmp->start; ltmp; ltmp = ctmp->start) {
448             unregister_config_handler(ctmp->fileHeader,
449                                       ltmp->config_token);
450         }
451         SNMP_FREE(ctmp->fileHeader);
452         save = ctmp->next;
453         SNMP_FREE(ctmp);
454         ctmp = save;
455         config_files = save;
456     }
457 }
458 
459 #ifdef TESTING
460 void
print_config_handlers(void)461 print_config_handlers(void)
462 {
463     struct config_files *ctmp = config_files;
464     struct config_line *ltmp;
465 
466     for (; ctmp != NULL; ctmp = ctmp->next) {
467         DEBUGMSGTL(("read_config", "read_conf: %s\n", ctmp->fileHeader));
468         for (ltmp = ctmp->start; ltmp != NULL; ltmp = ltmp->next)
469             DEBUGMSGTL(("read_config", "                   %s\n",
470                         ltmp->config_token));
471     }
472 }
473 #endif
474 
475 static unsigned int  linecount;
476 static const char   *curfilename;
477 
478 struct config_line *
read_config_get_handlers(const char * type)479 read_config_get_handlers(const char *type)
480 {
481     struct config_files *ctmp = config_files;
482     for (; ctmp != NULL && strcmp(ctmp->fileHeader, type);
483          ctmp = ctmp->next);
484     if (ctmp)
485         return ctmp->start;
486     return NULL;
487 }
488 
489 int
read_config_with_type_when(const char * filename,const char * type,int when)490 read_config_with_type_when(const char *filename, const char *type, int when)
491 {
492     struct config_line *ctmp = read_config_get_handlers(type);
493     if (ctmp)
494         return read_config(filename, ctmp, when);
495     else
496         DEBUGMSGTL(("read_config",
497                     "read_config: I have no registrations for type:%s,file:%s\n",
498                     type, filename));
499     return SNMPERR_GENERR;     /* No config files read */
500 }
501 
502 int
read_config_with_type(const char * filename,const char * type)503 read_config_with_type(const char *filename, const char *type)
504 {
505     return read_config_with_type_when(filename, type, EITHER_CONFIG);
506 }
507 
508 
509 struct config_line *
read_config_find_handler(struct config_line * line_handlers,const char * token)510 read_config_find_handler(struct config_line *line_handlers,
511                          const char *token)
512 {
513     struct config_line *lptr;
514 
515     for (lptr = line_handlers; lptr != NULL; lptr = lptr->next) {
516         if (!strcasecmp(token, lptr->config_token)) {
517             return lptr;
518         }
519     }
520     return NULL;
521 }
522 
523 
524 /*
525  * searches a config_line linked list for a match
526  */
527 int
run_config_handler(struct config_line * lptr,const char * token,char * cptr,int when)528 run_config_handler(struct config_line *lptr,
529                    const char *token, char *cptr, int when)
530 {
531     char           *cp;
532     lptr = read_config_find_handler(lptr, token);
533     if (lptr != NULL) {
534         if (when == EITHER_CONFIG || lptr->config_time == when) {
535             char tmpbuf[1];
536             DEBUGMSGTL(("read_config:parser",
537                         "Found a parser.  Calling it: %s / %s\n", token,
538                         cptr));
539             /*
540              * Make sure cptr is non-null
541              */
542             if (!cptr) {
543                 tmpbuf[0] = '\0';
544                 cptr = tmpbuf;
545             }
546 
547             /*
548              * Stomp on any trailing whitespace
549              */
550             cp = &(cptr[strlen(cptr)-1]);
551             while ((cp > cptr) && isspace((unsigned char)(*cp))) {
552                 *(cp--) = '\0';
553             }
554             (*(lptr->parse_line)) (token, cptr);
555         }
556         else
557             DEBUGMSGTL(("9:read_config:parser",
558                         "%s handler not registered for this time\n", token));
559     } else if (when != PREMIB_CONFIG &&
560 	       !netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID,
561 				       NETSNMP_DS_LIB_NO_TOKEN_WARNINGS)) {
562 	netsnmp_config_warn("Unknown token: %s.", token);
563         return SNMPERR_GENERR;
564     }
565     return SNMPERR_SUCCESS;
566 }
567 
568 /*
569  * takens an arbitrary string and tries to intepret it based on the
570  * known configuration handlers for all registered types.  May produce
571  * inconsistent results when multiple tokens of the same name are
572  * registered under different file types.
573  */
574 
575 /*
576  * we allow = delimeters here
577  */
578 #define SNMP_CONFIG_DELIMETERS " \t="
579 
580 int
snmp_config_when(char * line,int when)581 snmp_config_when(char *line, int when)
582 {
583     char           *cptr, buf[STRINGMAX];
584     struct config_line *lptr = NULL;
585     struct config_files *ctmp = config_files;
586     char           *st;
587 
588     if (line == NULL) {
589         config_perror("snmp_config() called with a null string.");
590         return SNMPERR_GENERR;
591     }
592 
593     strlcpy(buf, line, STRINGMAX);
594     cptr = strtok_r(buf, SNMP_CONFIG_DELIMETERS, &st);
595     if (!cptr) {
596         netsnmp_config_warn("Wrong format: %s", line);
597         return SNMPERR_GENERR;
598     }
599     if (cptr[0] == '[') {
600         if (cptr[strlen(cptr) - 1] != ']') {
601 	    netsnmp_config_error("no matching ']' for type %s.", cptr + 1);
602             return SNMPERR_GENERR;
603         }
604         cptr[strlen(cptr) - 1] = '\0';
605         lptr = read_config_get_handlers(cptr + 1);
606         if (lptr == NULL) {
607 	    netsnmp_config_error("No handlers regestered for type %s.",
608 				 cptr + 1);
609             return SNMPERR_GENERR;
610         }
611         cptr = strtok_r(NULL, SNMP_CONFIG_DELIMETERS, &st);
612         lptr = read_config_find_handler(lptr, cptr);
613     } else {
614         /*
615          * we have to find a token
616          */
617         for (; ctmp != NULL && lptr == NULL; ctmp = ctmp->next)
618             lptr = read_config_find_handler(ctmp->start, cptr);
619     }
620     if (lptr == NULL && netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID,
621 					  NETSNMP_DS_LIB_NO_TOKEN_WARNINGS)) {
622 	netsnmp_config_warn("Unknown token: %s.", cptr);
623         return SNMPERR_GENERR;
624     }
625 
626     /*
627      * use the original string instead since strtok_r messed up the original
628      */
629     line = skip_white(line + (cptr - buf) + strlen(cptr) + 1);
630 
631     return (run_config_handler(lptr, cptr, line, when));
632 }
633 
634 int
netsnmp_config(char * line)635 netsnmp_config(char *line)
636 {
637     int             ret = SNMP_ERR_NOERROR;
638     DEBUGMSGTL(("snmp_config", "remembering line \"%s\"\n", line));
639     netsnmp_config_remember(line);      /* always remember it so it's read
640                                          * processed after a free_config()
641                                          * call */
642     if (netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID,
643 			       NETSNMP_DS_LIB_HAVE_READ_CONFIG)) {
644         DEBUGMSGTL(("snmp_config", "  ... processing it now\n"));
645         ret = snmp_config_when(line, NORMAL_CONFIG);
646     }
647     return ret;
648 }
649 
650 void
netsnmp_config_remember_in_list(char * line,struct read_config_memory ** mem)651 netsnmp_config_remember_in_list(char *line,
652                                 struct read_config_memory **mem)
653 {
654     if (mem == NULL)
655         return;
656 
657     while (*mem != NULL)
658         mem = &((*mem)->next);
659 
660     *mem = SNMP_MALLOC_STRUCT(read_config_memory);
661     if (*mem != NULL) {
662         if (line)
663             (*mem)->line = strdup(line);
664     }
665 }
666 
667 void
netsnmp_config_remember_free_list(struct read_config_memory ** mem)668 netsnmp_config_remember_free_list(struct read_config_memory **mem)
669 {
670     struct read_config_memory *tmpmem;
671     while (*mem) {
672         SNMP_FREE((*mem)->line);
673         tmpmem = (*mem)->next;
674         SNMP_FREE(*mem);
675         *mem = tmpmem;
676     }
677 }
678 
679 void
netsnmp_config_process_memory_list(struct read_config_memory ** memp,int when,int clear)680 netsnmp_config_process_memory_list(struct read_config_memory **memp,
681                                    int when, int clear)
682 {
683 
684     struct read_config_memory *mem;
685 
686     if (!memp)
687         return;
688 
689     mem = *memp;
690 
691     while (mem) {
692         DEBUGMSGTL(("read_config:mem", "processing memory: %s\n", mem->line));
693         snmp_config_when(mem->line, when);
694         mem = mem->next;
695     }
696 
697     if (clear)
698         netsnmp_config_remember_free_list(memp);
699 }
700 
701 /*
702  * default storage location implementation
703  */
704 static struct read_config_memory *memorylist = NULL;
705 
706 void
netsnmp_config_remember(char * line)707 netsnmp_config_remember(char *line)
708 {
709     netsnmp_config_remember_in_list(line, &memorylist);
710 }
711 
712 void
netsnmp_config_process_memories(void)713 netsnmp_config_process_memories(void)
714 {
715     netsnmp_config_process_memory_list(&memorylist, EITHER_CONFIG, 1);
716 }
717 
718 void
netsnmp_config_process_memories_when(int when,int clear)719 netsnmp_config_process_memories_when(int when, int clear)
720 {
721     netsnmp_config_process_memory_list(&memorylist, when, clear);
722 }
723 
724 /*******************************************************************-o-******
725  * read_config
726  *
727  * Parameters:
728  *	*filename
729  *	*line_handler
730  *	 when
731  *
732  * Read <filename> and process each line in accordance with the list of
733  * <line_handler> functions.
734  *
735  *
736  * For each line in <filename>, search the list of <line_handler>'s
737  * for an entry that matches the first token on the line.  This comparison is
738  * case insensitive.
739  *
740  * For each match, check that <when> is the designated time for the
741  * <line_handler> function to be executed before processing the line.
742  *
743  * Returns SNMPERR_SUCCESS if the file is processed successfully.
744  * Returns SNMPERR_GENERR  if it cannot.
745  *    Note that individual config token errors do not trigger SNMPERR_GENERR
746  *    It's only if the whole file cannot be processed for some reason.
747  */
748 int
read_config(const char * filename,struct config_line * line_handler,int when)749 read_config(const char *filename,
750             struct config_line *line_handler, int when)
751 {
752     static int      depth = 0;
753     static int      files = 0;
754 
755     const char * const prev_filename = curfilename;
756     const unsigned int prev_linecount = linecount;
757 
758     FILE           *ifile;
759     char           *line = NULL;  /* current line buffer */
760     size_t          linesize = 0; /* allocated size of line */
761 
762     /* reset file counter when recursion depth is 0 */
763     if (depth == 0)
764         files = 0;
765 
766     if ((ifile = fopen(filename, "r")) == NULL) {
767 #ifdef ENOENT
768         if (errno == ENOENT) {
769             DEBUGMSGTL(("read_config", "%s: %s\n", filename,
770                         strerror(errno)));
771         } else
772 #endif                          /* ENOENT */
773 #ifdef EACCES
774         if (errno == EACCES) {
775             DEBUGMSGTL(("read_config", "%s: %s\n", filename,
776                         strerror(errno)));
777         } else
778 #endif                          /* EACCES */
779         {
780             snmp_log_perror(filename);
781         }
782         return SNMPERR_GENERR;
783     }
784 
785 #define CONFIG_MAX_FILES 4096
786     if (files > CONFIG_MAX_FILES) {
787         netsnmp_config_error("maximum conf file count (%d) exceeded\n",
788                              CONFIG_MAX_FILES);
789 	fclose(ifile);
790         return SNMPERR_GENERR;
791     }
792 #define CONFIG_MAX_RECURSE_DEPTH 16
793     if (depth > CONFIG_MAX_RECURSE_DEPTH) {
794         netsnmp_config_error("nested include depth > %d\n",
795                              CONFIG_MAX_RECURSE_DEPTH);
796 	fclose(ifile);
797         return SNMPERR_GENERR;
798     }
799 
800     linecount = 0;
801     curfilename = filename;
802 
803     ++files;
804     ++depth;
805 
806     DEBUGMSGTL(("read_config:file", "Reading configuration %s (%d)\n",
807                 filename, when));
808 
809     while (ifile) {
810         size_t              linelen = 0; /* strlen of the current line */
811         char               *cptr;
812         struct config_line *lptr = line_handler;
813 
814         for (;;) {
815             if (linesize <= linelen + 1) {
816                 char *tmp = realloc(line, linesize + 256);
817                 if (tmp) {
818                     line = tmp;
819                     linesize += 256;
820                 } else {
821                     netsnmp_config_error("Failed to allocate memory\n");
822                     free(line);
823                     fclose(ifile);
824                     return SNMPERR_GENERR;
825                 }
826             }
827             if (fgets(line + linelen, linesize - linelen, ifile) == NULL) {
828                 line[linelen] = '\0';
829                 fclose (ifile);
830                 ifile = NULL;
831                 break;
832             }
833 
834             linelen += strlen(line + linelen);
835 
836             if (line[linelen - 1] == '\n') {
837               line[linelen - 1] = '\0';
838               break;
839             }
840         }
841 
842         ++linecount;
843         DEBUGMSGTL(("9:read_config:line", "%s:%d examining: %s\n",
844                     filename, linecount, line));
845         /*
846          * check blank line or # comment
847          */
848         if ((cptr = skip_white(line))) {
849             char token[STRINGMAX];
850 
851             cptr = copy_nword(cptr, token, sizeof(token));
852             if (token[0] == '[') {
853                 if (token[strlen(token) - 1] != ']') {
854 		    netsnmp_config_error("no matching ']' for type %s.",
855 					 &token[1]);
856                     continue;
857                 }
858                 token[strlen(token) - 1] = '\0';
859                 lptr = read_config_get_handlers(&token[1]);
860                 if (lptr == NULL) {
861 		    netsnmp_config_error("No handlers regestered for type %s.",
862 					 &token[1]);
863                     continue;
864                 }
865                 DEBUGMSGTL(("read_config:context",
866                             "Switching to new context: %s%s\n",
867                             ((cptr) ? "(this line only) " : ""),
868                             &token[1]));
869                 if (cptr == NULL) {
870                     /*
871                      * change context permanently
872                      */
873                     line_handler = lptr;
874                     continue;
875                 } else {
876                     /*
877                      * the rest of this line only applies.
878                      */
879                     cptr = copy_nword(cptr, token, sizeof(token));
880                 }
881             } else if ((token[0] == 'i') && (strncasecmp(token,"include", 7 )==0)) {
882                 if ( strcasecmp( token, "include" )==0) {
883                     if (when != PREMIB_CONFIG &&
884 	                !netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID,
885 				                NETSNMP_DS_LIB_NO_TOKEN_WARNINGS)) {
886 	                netsnmp_config_warn("Ambiguous token '%s' - use 'includeSearch' (or 'includeFile') instead.", token);
887                     }
888                     continue;
889                 } else if ( strcasecmp( token, "includedir" )==0) {
890                     DIR *d;
891                     struct dirent *entry;
892                     char  fname[SNMP_MAXPATH];
893                     int   len;
894 
895                     if (cptr == NULL) {
896                         if (when != PREMIB_CONFIG)
897 		            netsnmp_config_error("Blank line following %s token.", token);
898                         continue;
899                     }
900                     if ((d=opendir(cptr)) == NULL ) {
901                         if (when != PREMIB_CONFIG)
902                             netsnmp_config_error("Can't open include dir '%s'.", cptr);
903                         continue;
904                     }
905                     while ((entry = readdir( d )) != NULL ) {
906                         if ( entry->d_name[0] != '.') {
907                             len = NAMLEN(entry);
908                             if ((len > 5) && (strcmp(&(entry->d_name[len-5]),".conf") == 0)) {
909                                 snprintf(fname, SNMP_MAXPATH, "%s/%s",
910                                          cptr, entry->d_name);
911                                 (void)read_config(fname, line_handler, when);
912                             }
913                         }
914                     }
915                     closedir(d);
916                     continue;
917                 } else if ( strcasecmp( token, "includefile" )==0) {
918                     char  fname[SNMP_MAXPATH], *cp;
919 
920                     if (cptr == NULL) {
921                         if (when != PREMIB_CONFIG)
922 		            netsnmp_config_error("Blank line following %s token.", token);
923                         continue;
924                     }
925                     if ( cptr[0] == '/' ) {
926                         strlcpy(fname, cptr, SNMP_MAXPATH);
927                     } else {
928                         strlcpy(fname, filename, SNMP_MAXPATH);
929                         cp = strrchr(fname, '/');
930                         if (!cp)
931                             fname[0] = '\0';
932                         else
933                             *(++cp) = '\0';
934                         strlcat(fname, cptr, SNMP_MAXPATH);
935                     }
936                     if (read_config(fname, line_handler, when) !=
937                         SNMPERR_SUCCESS && when != PREMIB_CONFIG)
938                         netsnmp_config_error("Included file '%s' not found.",
939                                              fname);
940                     continue;
941                 } else if ( strcasecmp( token, "includesearch" )==0) {
942                     struct config_files ctmp;
943                     int len, ret;
944 
945                     if (cptr == NULL) {
946                         if (when != PREMIB_CONFIG)
947 		            netsnmp_config_error("Blank line following %s token.", token);
948                         continue;
949                     }
950                     len = strlen(cptr);
951                     ctmp.fileHeader = cptr;
952                     ctmp.start = line_handler;
953                     ctmp.next = NULL;
954                     if ((len > 5) && (strcmp(&cptr[len-5],".conf") == 0))
955                        cptr[len-5] = 0; /* chop off .conf */
956                     ret = read_config_files_of_type(when,&ctmp);
957                     if ((len > 5) && (cptr[len-5] == 0))
958                        cptr[len-5] = '.'; /* restore .conf */
959                     if (( ret != SNMPERR_SUCCESS ) && (when != PREMIB_CONFIG))
960 		        netsnmp_config_error("Included config '%s' not found.", cptr);
961                     continue;
962                 } else {
963                     lptr = line_handler;
964                 }
965             } else {
966                 lptr = line_handler;
967             }
968             if (cptr == NULL) {
969 		netsnmp_config_error("Blank line following %s token.", token);
970             } else {
971                 DEBUGMSGTL(("read_config:line", "%s:%d examining: %s\n",
972                             filename, linecount, line));
973                 run_config_handler(lptr, token, cptr, when);
974             }
975         }
976     }
977     free(line);
978     linecount = prev_linecount;
979     curfilename = prev_filename;
980     --depth;
981     return SNMPERR_SUCCESS;
982 
983 }                               /* end read_config() */
984 
985 
986 
987 void
free_config(void)988 free_config(void)
989 {
990     struct config_files *ctmp = config_files;
991     struct config_line *ltmp;
992 
993     for (; ctmp != NULL; ctmp = ctmp->next)
994         for (ltmp = ctmp->start; ltmp != NULL; ltmp = ltmp->next)
995             if (ltmp->free_func)
996                 (*(ltmp->free_func)) ();
997 }
998 
999 /*
1000  * Return SNMPERR_SUCCESS if any config files are processed
1001  * Return SNMPERR_GENERR if _no_ config files are processed
1002  *    Whether this is actually an error is left to the application
1003  */
1004 int
read_configs_optional(const char * optional_config,int when)1005 read_configs_optional(const char *optional_config, int when)
1006 {
1007     char *newp, *cp, *st = NULL;
1008     int              ret = SNMPERR_GENERR;
1009     char *type = netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID,
1010 				       NETSNMP_DS_LIB_APPTYPE);
1011 
1012     if ((NULL == optional_config) || (NULL == type))
1013         return ret;
1014 
1015     DEBUGMSGTL(("read_configs_optional",
1016                 "reading optional configuration tokens for %s\n", type));
1017 
1018     newp = strdup(optional_config);      /* strtok_r messes it up */
1019     if (!newp)
1020         return ret;
1021     cp = strtok_r(newp, ",", &st);
1022     while (cp) {
1023         struct stat     statbuf;
1024         if (stat(cp, &statbuf)) {
1025             DEBUGMSGTL(("read_config",
1026                         "Optional File \"%s\" does not exist.\n", cp));
1027             snmp_log_perror(cp);
1028         } else {
1029             DEBUGMSGTL(("read_config:opt",
1030                         "Reading optional config file: \"%s\"\n", cp));
1031             if ( read_config_with_type_when(cp, type, when) == SNMPERR_SUCCESS )
1032                 ret = SNMPERR_SUCCESS;
1033         }
1034         cp = strtok_r(NULL, ",", &st);
1035     }
1036     free(newp);
1037     return ret;
1038 }
1039 
1040 void
read_configs(void)1041 read_configs(void)
1042 {
1043     char *optional_config = netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID,
1044 					       NETSNMP_DS_LIB_OPTIONALCONFIG);
1045 
1046     snmp_call_callbacks(SNMP_CALLBACK_LIBRARY,
1047                         SNMP_CALLBACK_PRE_READ_CONFIG, NULL);
1048 
1049     DEBUGMSGTL(("read_config", "reading normal configuration tokens\n"));
1050 
1051     if ((NULL != optional_config) && (*optional_config == '-')) {
1052         (void)read_configs_optional(++optional_config, NORMAL_CONFIG);
1053         optional_config = NULL; /* clear, so we don't read them twice */
1054     }
1055 
1056     (void)read_config_files(NORMAL_CONFIG);
1057 
1058     /*
1059      * do this even when the normal above wasn't done
1060      */
1061     if (NULL != optional_config)
1062         (void)read_configs_optional(optional_config, NORMAL_CONFIG);
1063 
1064     netsnmp_config_process_memories_when(NORMAL_CONFIG, 1);
1065 
1066     netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID,
1067 			   NETSNMP_DS_LIB_HAVE_READ_CONFIG, 1);
1068     snmp_call_callbacks(SNMP_CALLBACK_LIBRARY,
1069                         SNMP_CALLBACK_POST_READ_CONFIG, NULL);
1070 }
1071 
1072 void
read_premib_configs(void)1073 read_premib_configs(void)
1074 {
1075     char *optional_config = netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID,
1076 					       NETSNMP_DS_LIB_OPTIONALCONFIG);
1077 
1078     snmp_call_callbacks(SNMP_CALLBACK_LIBRARY,
1079                         SNMP_CALLBACK_PRE_PREMIB_READ_CONFIG, NULL);
1080 
1081     DEBUGMSGTL(("read_config", "reading premib configuration tokens\n"));
1082 
1083     if ((NULL != optional_config) && (*optional_config == '-')) {
1084         (void)read_configs_optional(++optional_config, PREMIB_CONFIG);
1085         optional_config = NULL; /* clear, so we don't read them twice */
1086     }
1087 
1088     (void)read_config_files(PREMIB_CONFIG);
1089 
1090     if (NULL != optional_config)
1091         (void)read_configs_optional(optional_config, PREMIB_CONFIG);
1092 
1093     netsnmp_config_process_memories_when(PREMIB_CONFIG, 0);
1094 
1095     netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID,
1096 			   NETSNMP_DS_LIB_HAVE_READ_PREMIB_CONFIG, 1);
1097     snmp_call_callbacks(SNMP_CALLBACK_LIBRARY,
1098                         SNMP_CALLBACK_POST_PREMIB_READ_CONFIG, NULL);
1099 }
1100 
1101 /*******************************************************************-o-******
1102  * set_configuration_directory
1103  *
1104  * Parameters:
1105  *      char *dir - value of the directory
1106  * Sets the configuration directory. Multiple directories can be
1107  * specified, but need to be seperated by 'ENV_SEPARATOR_CHAR'.
1108  */
1109 void
set_configuration_directory(const char * dir)1110 set_configuration_directory(const char *dir)
1111 {
1112     netsnmp_ds_set_string(NETSNMP_DS_LIBRARY_ID,
1113 			  NETSNMP_DS_LIB_CONFIGURATION_DIR, dir);
1114 }
1115 
1116 /*******************************************************************-o-******
1117  * get_configuration_directory
1118  *
1119  * Parameters: -
1120  * Retrieve the configuration directory or directories.
1121  * (For backwards compatibility that is:
1122  *       SNMPCONFPATH, SNMPSHAREPATH, SNMPLIBPATH, HOME/.snmp
1123  * First check whether the value is set.
1124  * If not set give it the default value.
1125  * Return the value.
1126  * We always retrieve it new, since we have to do it anyway if it is just set.
1127  */
1128 const char     *
get_configuration_directory(void)1129 get_configuration_directory(void)
1130 {
1131     char            defaultPath[SPRINT_MAX_LEN];
1132     char           *homepath;
1133 
1134     if (NULL == netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID,
1135 				      NETSNMP_DS_LIB_CONFIGURATION_DIR)) {
1136         homepath = netsnmp_getenv("HOME");
1137         snprintf(defaultPath, sizeof(defaultPath), "%s%c%s%c%s%s%s%s",
1138                 SNMPCONFPATH, ENV_SEPARATOR_CHAR,
1139                 SNMPSHAREPATH, ENV_SEPARATOR_CHAR, SNMPLIBPATH,
1140                 ((homepath == NULL) ? "" : ENV_SEPARATOR),
1141                 ((homepath == NULL) ? "" : homepath),
1142                 ((homepath == NULL) ? "" : "/.snmp"));
1143         defaultPath[ sizeof(defaultPath)-1 ] = 0;
1144         set_configuration_directory(defaultPath);
1145     }
1146     return (netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID,
1147 				  NETSNMP_DS_LIB_CONFIGURATION_DIR));
1148 }
1149 
1150 /*******************************************************************-o-******
1151  * set_persistent_directory
1152  *
1153  * Parameters:
1154  *      char *dir - value of the directory
1155  * Sets the configuration directory.
1156  * No multiple directories may be specified.
1157  * (However, this is not checked)
1158  */
1159 void
set_persistent_directory(const char * dir)1160 set_persistent_directory(const char *dir)
1161 {
1162     netsnmp_ds_set_string(NETSNMP_DS_LIBRARY_ID,
1163 			  NETSNMP_DS_LIB_PERSISTENT_DIR, dir);
1164 }
1165 
1166 /*******************************************************************-o-******
1167  * get_persistent_directory
1168  *
1169  * Parameters: -
1170  * Function will retrieve the persisten directory value.
1171  * First check whether the value is set.
1172  * If not set give it the default value.
1173  * Return the value.
1174  * We always retrieve it new, since we have to do it anyway if it is just set.
1175  */
1176 const char     *
get_persistent_directory(void)1177 get_persistent_directory(void)
1178 {
1179     if (NULL == netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID,
1180 				      NETSNMP_DS_LIB_PERSISTENT_DIR)) {
1181         const char *persdir = netsnmp_getenv("SNMP_PERSISTENT_DIR");
1182         if (NULL == persdir)
1183             persdir = NETSNMP_PERSISTENT_DIRECTORY;
1184         set_persistent_directory(persdir);
1185     }
1186     return (netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID,
1187 				  NETSNMP_DS_LIB_PERSISTENT_DIR));
1188 }
1189 
1190 /*******************************************************************-o-******
1191  * set_temp_file_pattern
1192  *
1193  * Parameters:
1194  *      char *pattern - value of the file pattern
1195  * Sets the temp file pattern.
1196  * Multiple patterns may not be specified.
1197  * (However, this is not checked)
1198  */
1199 void
set_temp_file_pattern(const char * pattern)1200 set_temp_file_pattern(const char *pattern)
1201 {
1202     netsnmp_ds_set_string(NETSNMP_DS_LIBRARY_ID,
1203 			  NETSNMP_DS_LIB_TEMP_FILE_PATTERN, pattern);
1204 }
1205 
1206 /*******************************************************************-o-******
1207  * get_temp_file_pattern
1208  *
1209  * Parameters: -
1210  * Function will retrieve the temp file pattern value.
1211  * First check whether the value is set.
1212  * If not set give it the default value.
1213  * Return the value.
1214  * We always retrieve it new, since we have to do it anyway if it is just set.
1215  */
1216 const char     *
get_temp_file_pattern(void)1217 get_temp_file_pattern(void)
1218 {
1219     if (NULL == netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID,
1220 				      NETSNMP_DS_LIB_TEMP_FILE_PATTERN)) {
1221         set_temp_file_pattern(NETSNMP_TEMP_FILE_PATTERN);
1222     }
1223     return (netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID,
1224 				  NETSNMP_DS_LIB_TEMP_FILE_PATTERN));
1225 }
1226 
1227 /**
1228  * utility routine for read_config_files
1229  *
1230  * Return SNMPERR_SUCCESS if any config files are processed
1231  * Return SNMPERR_GENERR if _no_ config files are processed
1232  *    Whether this is actually an error is left to the application
1233  */
1234 static int
read_config_files_in_path(const char * path,struct config_files * ctmp,int when,const char * perspath,const char * persfile)1235 read_config_files_in_path(const char *path, struct config_files *ctmp,
1236                           int when, const char *perspath, const char *persfile)
1237 {
1238     int             done, j;
1239     char            configfile[300];
1240     char           *cptr1, *cptr2, *envconfpath;
1241     struct stat     statbuf;
1242     int             ret = SNMPERR_GENERR;
1243 
1244     if ((NULL == path) || (NULL == ctmp))
1245         return SNMPERR_GENERR;
1246 
1247     envconfpath = strdup(path);
1248 
1249     DEBUGMSGTL(("read_config:path", " config path used for %s:%s (persistent path:%s)\n",
1250                 ctmp->fileHeader, envconfpath, perspath));
1251     cptr1 = cptr2 = envconfpath;
1252     done = 0;
1253     while ((!done) && (*cptr2 != 0)) {
1254         while (*cptr1 != 0 && *cptr1 != ENV_SEPARATOR_CHAR)
1255             cptr1++;
1256         if (*cptr1 == 0)
1257             done = 1;
1258         else
1259             *cptr1 = 0;
1260 
1261         DEBUGMSGTL(("read_config:dir", " config dir: %s\n", cptr2 ));
1262         if (stat(cptr2, &statbuf) != 0) {
1263             /*
1264              * Directory not there, continue
1265              */
1266             DEBUGMSGTL(("read_config:dir", " Directory not present: %s\n", cptr2 ));
1267             cptr2 = ++cptr1;
1268             continue;
1269         }
1270 #ifdef S_ISDIR
1271         if (!S_ISDIR(statbuf.st_mode)) {
1272             /*
1273              * Not a directory, continue
1274              */
1275             DEBUGMSGTL(("read_config:dir", " Not a directory: %s\n", cptr2 ));
1276             cptr2 = ++cptr1;
1277             continue;
1278         }
1279 #endif
1280 
1281         /*
1282          * for proper persistent storage retrieval, we need to read old backup
1283          * copies of the previous storage files.  If the application in
1284          * question has died without the proper call to snmp_clean_persistent,
1285          * then we read all the configuration files we can, starting with
1286          * the oldest first.
1287          */
1288         if (strncmp(cptr2, perspath, strlen(perspath)) == 0 ||
1289             (persfile != NULL &&
1290              strncmp(cptr2, persfile, strlen(persfile)) == 0)) {
1291             DEBUGMSGTL(("read_config:persist", " persist dir: %s\n", cptr2 ));
1292             /*
1293              * limit this to the known storage directory only
1294              */
1295             for (j = 0; j <= NETSNMP_MAX_PERSISTENT_BACKUPS; j++) {
1296                 snprintf(configfile, sizeof(configfile),
1297                          "%s/%s.%d.conf", cptr2,
1298                          ctmp->fileHeader, j);
1299                 configfile[ sizeof(configfile)-1 ] = 0;
1300                 if (stat(configfile, &statbuf) != 0) {
1301                     /*
1302                      * file not there, continue
1303                      */
1304                     break;
1305                 } else {
1306                     /*
1307                      * backup exists, read it
1308                      */
1309                     DEBUGMSGTL(("read_config_files",
1310                                 "old config file found: %s, parsing\n",
1311                                 configfile));
1312                     if (read_config(configfile, ctmp->start, when) == SNMPERR_SUCCESS)
1313                         ret = SNMPERR_SUCCESS;
1314                 }
1315             }
1316         }
1317         snprintf(configfile, sizeof(configfile),
1318                  "%s/%s.conf", cptr2, ctmp->fileHeader);
1319         configfile[ sizeof(configfile)-1 ] = 0;
1320         if (read_config(configfile, ctmp->start, when) == SNMPERR_SUCCESS)
1321             ret = SNMPERR_SUCCESS;
1322         snprintf(configfile, sizeof(configfile),
1323                  "%s/%s.local.conf", cptr2, ctmp->fileHeader);
1324         configfile[ sizeof(configfile)-1 ] = 0;
1325         if (read_config(configfile, ctmp->start, when) == SNMPERR_SUCCESS)
1326             ret = SNMPERR_SUCCESS;
1327 
1328         if(done)
1329             break;
1330 
1331         cptr2 = ++cptr1;
1332     }
1333     SNMP_FREE(envconfpath);
1334     return ret;
1335 }
1336 
1337 /*******************************************************************-o-******
1338  * read_config_files
1339  *
1340  * Parameters:
1341  *	when	== PREMIB_CONFIG, NORMAL_CONFIG  -or-  EITHER_CONFIG
1342  *
1343  *
1344  * Traverse the list of config file types, performing the following actions
1345  * for each --
1346  *
1347  * First, build a search path for config files.  If the contents of
1348  * environment variable SNMPCONFPATH are NULL, then use the following
1349  * path list (where the last entry exists only if HOME is non-null):
1350  *
1351  *	SNMPSHAREPATH:SNMPLIBPATH:${HOME}/.snmp
1352  *
1353  * Then, In each of these directories, read config files by the name of:
1354  *
1355  *	<dir>/<fileHeader>.conf		-AND-
1356  *	<dir>/<fileHeader>.local.conf
1357  *
1358  * where <fileHeader> is taken from the config file type structure.
1359  *
1360  *
1361  * PREMIB_CONFIG causes free_config() to be invoked prior to any other action.
1362  *
1363  *
1364  * EXITs if any 'config_errors' are logged while parsing config file lines.
1365  *
1366  * Return SNMPERR_SUCCESS if any config files are processed
1367  * Return SNMPERR_GENERR if _no_ config files are processed
1368  *    Whether this is actually an error is left to the application
1369  */
1370 int
read_config_files_of_type(int when,struct config_files * ctmp)1371 read_config_files_of_type(int when, struct config_files *ctmp)
1372 {
1373     const char     *confpath, *persfile, *envconfpath;
1374     char           *perspath;
1375     int             ret = SNMPERR_GENERR;
1376 
1377     if (netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID,
1378                                NETSNMP_DS_LIB_DONT_PERSIST_STATE)
1379         || netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID,
1380                                   NETSNMP_DS_LIB_DISABLE_CONFIG_LOAD)
1381         || (NULL == ctmp)) return ret;
1382 
1383     /*
1384      * these shouldn't change
1385      */
1386     confpath = get_configuration_directory();
1387     persfile = netsnmp_getenv("SNMP_PERSISTENT_FILE");
1388     envconfpath = netsnmp_getenv("SNMPCONFPATH");
1389 
1390 
1391         /*
1392          * read the config files. strdup() the result of
1393          * get_persistent_directory() to avoid that parsing the "persistentDir"
1394          * keyword transforms the perspath pointer into a dangling pointer.
1395          */
1396         perspath = strdup(get_persistent_directory());
1397         if (envconfpath == NULL) {
1398             /*
1399              * read just the config files (no persistent stuff), since
1400              * persistent path can change via conf file. Then get the
1401              * current persistent directory, and read files there.
1402              */
1403             if ( read_config_files_in_path(confpath, ctmp, when, perspath,
1404                                       persfile) == SNMPERR_SUCCESS )
1405                 ret = SNMPERR_SUCCESS;
1406             free(perspath);
1407             perspath = strdup(get_persistent_directory());
1408             if ( read_config_files_in_path(perspath, ctmp, when, perspath,
1409                                       persfile) == SNMPERR_SUCCESS )
1410                 ret = SNMPERR_SUCCESS;
1411         }
1412         else {
1413             /*
1414              * only read path specified by user
1415              */
1416             if ( read_config_files_in_path(envconfpath, ctmp, when, perspath,
1417                                       persfile) == SNMPERR_SUCCESS )
1418                 ret = SNMPERR_SUCCESS;
1419         }
1420         free(perspath);
1421         return ret;
1422 }
1423 
1424 /*
1425  * Return SNMPERR_SUCCESS if any config files are processed
1426  * Return SNMPERR_GENERR if _no_ config files are processed
1427  *    Whether this is actually an error is left to the application
1428  */
1429 int
read_config_files(int when)1430 read_config_files(int when) {
1431 
1432     struct config_files *ctmp = config_files;
1433     int                  ret  = SNMPERR_GENERR;
1434 
1435     config_errors = 0;
1436 
1437     if (when == PREMIB_CONFIG)
1438         free_config();
1439 
1440     /*
1441      * read all config file types
1442      */
1443     for (; ctmp != NULL; ctmp = ctmp->next) {
1444         if ( read_config_files_of_type(when, ctmp) == SNMPERR_SUCCESS )
1445             ret = SNMPERR_SUCCESS;
1446     }
1447 
1448     if (config_errors) {
1449         snmp_log(LOG_ERR, "net-snmp: %d error(s) in config file(s)\n",
1450                  config_errors);
1451     }
1452     return ret;
1453 }
1454 
1455 void
read_config_print_usage(const char * lead)1456 read_config_print_usage(const char *lead)
1457 {
1458     struct config_files *ctmp = config_files;
1459     struct config_line *ltmp;
1460 
1461     if (lead == NULL)
1462         lead = "";
1463 
1464     for (ctmp = config_files; ctmp != NULL; ctmp = ctmp->next) {
1465         snmp_log(LOG_INFO, "%sIn %s.conf and %s.local.conf:\n", lead,
1466                  ctmp->fileHeader, ctmp->fileHeader);
1467         for (ltmp = ctmp->start; ltmp != NULL; ltmp = ltmp->next) {
1468             DEBUGIF("read_config_usage") {
1469                 if (ltmp->config_time == PREMIB_CONFIG)
1470                     DEBUGMSG(("read_config_usage", "*"));
1471                 else
1472                     DEBUGMSG(("read_config_usage", " "));
1473             }
1474             if (ltmp->help) {
1475                 snmp_log(LOG_INFO, "%s%s%-24s %s\n", lead, lead,
1476                          ltmp->config_token, ltmp->help);
1477             } else {
1478                 DEBUGIF("read_config_usage") {
1479                     snmp_log(LOG_INFO, "%s%s%-24s [NO HELP]\n", lead, lead,
1480                              ltmp->config_token);
1481                 }
1482             }
1483         }
1484     }
1485 }
1486 
1487 /**
1488  * read_config_store intended for use by applications to store permenant
1489  * configuration information generated by sets or persistent counters.
1490  * Appends line to a file named either ENV(SNMP_PERSISTENT_FILE) or
1491  *   "<NETSNMP_PERSISTENT_DIRECTORY>/<type>.conf".
1492  * Adds a trailing newline to the stored file if necessary.
1493  *
1494  * @param type is the application name
1495  * @param line is the configuration line written to the application name's
1496  * configuration file
1497  *
1498  * @return void
1499   */
1500 void
read_config_store(const char * type,const char * line)1501 read_config_store(const char *type, const char *line)
1502 {
1503 #ifdef NETSNMP_PERSISTENT_DIRECTORY
1504     char            file[512], *filep;
1505     FILE           *fout;
1506 #ifdef NETSNMP_PERSISTENT_MASK
1507     mode_t          oldmask;
1508 #endif
1509 
1510     if (netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID,
1511                                NETSNMP_DS_LIB_DONT_PERSIST_STATE)
1512      || netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID,
1513                                NETSNMP_DS_LIB_DISABLE_PERSISTENT_LOAD)) return;
1514 
1515     /*
1516      * store configuration directives in the following order of preference:
1517      * 1. ENV variable SNMP_PERSISTENT_FILE
1518      * 2. configured <NETSNMP_PERSISTENT_DIRECTORY>/<type>.conf
1519      */
1520     if ((filep = netsnmp_getenv("SNMP_PERSISTENT_FILE")) == NULL) {
1521         snprintf(file, sizeof(file),
1522                  "%s/%s.conf", get_persistent_directory(), type);
1523         file[ sizeof(file)-1 ] = 0;
1524         filep = file;
1525     }
1526 #ifdef NETSNMP_PERSISTENT_MASK
1527     oldmask = umask(NETSNMP_PERSISTENT_MASK);
1528 #endif
1529     if (mkdirhier(filep, NETSNMP_AGENT_DIRECTORY_MODE, 1)) {
1530         snmp_log(LOG_ERR,
1531                  "Failed to create the persistent directory for %s\n",
1532                  file);
1533     }
1534     if ((fout = fopen(filep, "a")) != NULL) {
1535         fprintf(fout, "%s", line);
1536         if (line[strlen(line)] != '\n')
1537             fprintf(fout, "\n");
1538         DEBUGMSGTL(("read_config:store", "storing: %s\n", line));
1539         fflush(fout);
1540 #if defined(HAVE_FSYNC)
1541         fsync(fileno(fout));
1542 #elif defined(HAVE__GET_OSFHANDLE)
1543         {
1544             int fd;
1545             HANDLE h;
1546 
1547             fd = fileno(fout);
1548             netsnmp_assert(fd != -1);
1549             /*
1550              * Use size_t instead of uintptr_t because not all supported
1551              * Windows compilers support uintptr_t.
1552              */
1553             h = (HANDLE)(size_t)_get_osfhandle(fd);
1554             netsnmp_assert(h != INVALID_HANDLE_VALUE);
1555             FlushFileBuffers(h);
1556         }
1557 #endif
1558         fclose(fout);
1559     } else {
1560         if (strcmp(NETSNMP_APPLICATION_CONFIG_TYPE, type) != 0) {
1561             /*
1562              * Ignore this error in client utilities, they can run with random
1563              * UID/GID and typically cannot write to /var. Error message just
1564              * confuses people.
1565              */
1566             snmp_log(LOG_ERR, "read_config_store open failure on %s\n", filep);
1567         }
1568     }
1569 #ifdef NETSNMP_PERSISTENT_MASK
1570     umask(oldmask);
1571 #endif
1572 
1573 #endif
1574 }                               /* end read_config_store() */
1575 
1576 void
read_app_config_store(const char * line)1577 read_app_config_store(const char *line)
1578 {
1579     read_config_store(netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID,
1580 					    NETSNMP_DS_LIB_APPTYPE), line);
1581 }
1582 
1583 
1584 
1585 
1586 /*******************************************************************-o-******
1587  * snmp_save_persistent
1588  *
1589  * Parameters:
1590  *	*type
1591  *
1592  *
1593  * Save the file "<NETSNMP_PERSISTENT_DIRECTORY>/<type>.conf" into a backup copy
1594  * called "<NETSNMP_PERSISTENT_DIRECTORY>/<type>.%d.conf", which %d is an
1595  * incrementing number on each call, but less than NETSNMP_MAX_PERSISTENT_BACKUPS.
1596  *
1597  * Should be called just before all persistent information is supposed to be
1598  * written to move aside the existing persistent cache.
1599  * snmp_clean_persistent should then be called afterward all data has been
1600  * saved to remove these backup files.
1601  *
1602  * Note: on an rename error, the files are removed rather than saved.
1603  *
1604  */
1605 void
snmp_save_persistent(const char * type)1606 snmp_save_persistent(const char *type)
1607 {
1608     char            file[512], fileold[SPRINT_MAX_LEN];
1609     struct stat     statbuf;
1610     int             j;
1611 
1612     if (netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID,
1613                                NETSNMP_DS_LIB_DONT_PERSIST_STATE)
1614      || netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID,
1615                                NETSNMP_DS_LIB_DISABLE_PERSISTENT_SAVE)) return;
1616 
1617     DEBUGMSGTL(("snmp_save_persistent", "saving %s files...\n", type));
1618     snprintf(file, sizeof(file),
1619              "%s/%s.conf", get_persistent_directory(), type);
1620     file[ sizeof(file)-1 ] = 0;
1621     if (stat(file, &statbuf) == 0) {
1622         for (j = 0; j <= NETSNMP_MAX_PERSISTENT_BACKUPS; j++) {
1623             snprintf(fileold, sizeof(fileold),
1624                      "%s/%s.%d.conf", get_persistent_directory(), type, j);
1625             fileold[ sizeof(fileold)-1 ] = 0;
1626             if (stat(fileold, &statbuf) != 0) {
1627                 DEBUGMSGTL(("snmp_save_persistent",
1628                             " saving old config file: %s -> %s.\n", file,
1629                             fileold));
1630                 if (rename(file, fileold)) {
1631                     snmp_log(LOG_ERR, "Cannot rename %s to %s\n", file, fileold);
1632                      /* moving it failed, try nuking it, as leaving
1633                       * it around is very bad. */
1634                     if (unlink(file) == -1)
1635                         snmp_log(LOG_ERR, "Cannot unlink %s\n", file);
1636                 }
1637                 break;
1638             }
1639         }
1640     }
1641     /*
1642      * save a warning header to the top of the new file
1643      */
1644     snprintf(fileold, sizeof(fileold),
1645             "%s%s# Please save normal configuration tokens for %s in SNMPCONFPATH/%s.conf.\n# Only \"createUser\" tokens should be placed here by %s administrators.\n%s",
1646             "#\n# net-snmp (or ucd-snmp) persistent data file.\n#\n############################################################################\n# STOP STOP STOP STOP STOP STOP STOP STOP STOP \n",
1647             "#\n#          **** DO NOT EDIT THIS FILE ****\n#\n# STOP STOP STOP STOP STOP STOP STOP STOP STOP \n############################################################################\n#\n# DO NOT STORE CONFIGURATION ENTRIES HERE.\n",
1648             type, type, type,
1649 	    "# (Did I mention: do not edit this file?)\n#\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n");
1650     fileold[ sizeof(fileold)-1 ] = 0;
1651     read_config_store(type, fileold);
1652 }
1653 
1654 
1655 /*******************************************************************-o-******
1656  * snmp_clean_persistent
1657  *
1658  * Parameters:
1659  *	*type
1660  *
1661  *
1662  * Unlink all backup files called "<NETSNMP_PERSISTENT_DIRECTORY>/<type>.%d.conf".
1663  *
1664  * Should be called just after we successfull dumped the last of the
1665  * persistent data, to remove the backup copies of previous storage dumps.
1666  *
1667  * XXX  Worth overwriting with random bytes first?  This would
1668  *	ensure that the data is destroyed, even a buffer containing the
1669  *	data persists in memory or swap.  Only important if secrets
1670  *	will be stored here.
1671  */
1672 void
snmp_clean_persistent(const char * type)1673 snmp_clean_persistent(const char *type)
1674 {
1675     char            file[512];
1676     struct stat     statbuf;
1677     int             j;
1678 
1679     if (netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID,
1680                                NETSNMP_DS_LIB_DONT_PERSIST_STATE)
1681      || netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID,
1682                                NETSNMP_DS_LIB_DISABLE_PERSISTENT_SAVE)) return;
1683 
1684     DEBUGMSGTL(("snmp_clean_persistent", "cleaning %s files...\n", type));
1685     snprintf(file, sizeof(file),
1686              "%s/%s.conf", get_persistent_directory(), type);
1687     file[ sizeof(file)-1 ] = 0;
1688     if (stat(file, &statbuf) == 0) {
1689         for (j = 0; j <= NETSNMP_MAX_PERSISTENT_BACKUPS; j++) {
1690             snprintf(file, sizeof(file),
1691                      "%s/%s.%d.conf", get_persistent_directory(), type, j);
1692             file[ sizeof(file)-1 ] = 0;
1693             if (stat(file, &statbuf) == 0) {
1694                 DEBUGMSGTL(("snmp_clean_persistent",
1695                             " removing old config file: %s\n", file));
1696                 if (unlink(file) == -1)
1697                     snmp_log(LOG_ERR, "Cannot unlink %s\n", file);
1698             }
1699         }
1700     }
1701 }
1702 
1703 
1704 
1705 
1706 /*
1707  * config_perror: prints a warning string associated with a file and
1708  * line number of a .conf file and increments the error count.
1709  */
1710 static void
config_vlog(int level,const char * levelmsg,const char * str,va_list args)1711 config_vlog(int level, const char *levelmsg, const char *str, va_list args)
1712 {
1713     char tmpbuf[256];
1714     char* buf = tmpbuf;
1715     int len = snprintf(tmpbuf, sizeof(tmpbuf), "%s: line %d: %s: %s\n",
1716 		       curfilename, linecount, levelmsg, str);
1717     if (len >= (int)sizeof(tmpbuf)) {
1718 	buf = (char*)malloc(len + 1);
1719 	sprintf(buf, "%s: line %d: %s: %s\n",
1720 		curfilename, linecount, levelmsg, str);
1721     }
1722     snmp_vlog(level, buf, args);
1723     if (buf != tmpbuf)
1724 	free(buf);
1725 }
1726 
1727 void
netsnmp_config_error(const char * str,...)1728 netsnmp_config_error(const char *str, ...)
1729 {
1730     va_list args;
1731     va_start(args, str);
1732     config_vlog(LOG_ERR, "Error", str, args);
1733     va_end(args);
1734     config_errors++;
1735 }
1736 
1737 void
netsnmp_config_warn(const char * str,...)1738 netsnmp_config_warn(const char *str, ...)
1739 {
1740     va_list args;
1741     va_start(args, str);
1742     config_vlog(LOG_WARNING, "Warning", str, args);
1743     va_end(args);
1744 }
1745 
1746 void
config_perror(const char * str)1747 config_perror(const char *str)
1748 {
1749     netsnmp_config_error("%s", str);
1750 }
1751 
1752 void
config_pwarn(const char * str)1753 config_pwarn(const char *str)
1754 {
1755     netsnmp_config_warn("%s", str);
1756 }
1757 
1758 /*
1759  * skip all white spaces and return 1 if found something either end of
1760  * line or a comment character
1761  */
1762 char           *
skip_white(char * ptr)1763 skip_white(char *ptr)
1764 {
1765     return NETSNMP_REMOVE_CONST(char *, skip_white_const(ptr));
1766 }
1767 
1768 const char     *
skip_white_const(const char * ptr)1769 skip_white_const(const char *ptr)
1770 {
1771     if (ptr == NULL)
1772         return (NULL);
1773     while (*ptr != 0 && isspace((unsigned char)*ptr))
1774         ptr++;
1775     if (*ptr == 0 || *ptr == '#')
1776         return (NULL);
1777     return (ptr);
1778 }
1779 
1780 char           *
skip_not_white(char * ptr)1781 skip_not_white(char *ptr)
1782 {
1783     return NETSNMP_REMOVE_CONST(char *, skip_not_white_const(ptr));
1784 }
1785 
1786 const char     *
skip_not_white_const(const char * ptr)1787 skip_not_white_const(const char *ptr)
1788 {
1789     if (ptr == NULL)
1790         return (NULL);
1791     while (*ptr != 0 && !isspace((unsigned char)*ptr))
1792         ptr++;
1793     if (*ptr == 0 || *ptr == '#')
1794         return (NULL);
1795     return (ptr);
1796 }
1797 
1798 char           *
skip_token(char * ptr)1799 skip_token(char *ptr)
1800 {
1801     return NETSNMP_REMOVE_CONST(char *, skip_token_const(ptr));
1802 }
1803 
1804 const char     *
skip_token_const(const char * ptr)1805 skip_token_const(const char *ptr)
1806 {
1807     ptr = skip_white_const(ptr);
1808     ptr = skip_not_white_const(ptr);
1809     ptr = skip_white_const(ptr);
1810     return (ptr);
1811 }
1812 
1813 /*
1814  * copy_word
1815  * copies the next 'token' from 'from' into 'to', maximum len-1 characters.
1816  * currently a token is anything seperate by white space
1817  * or within quotes (double or single) (i.e. "the red rose"
1818  * is one token, \"the red rose\" is three tokens)
1819  * a '\' character will allow a quote character to be treated
1820  * as a regular character
1821  * It returns a pointer to first non-white space after the end of the token
1822  * being copied or to 0 if we reach the end.
1823  * Note: Partially copied words (greater than len) still returns a !NULL ptr
1824  * Note: partially copied words are, however, null terminated.
1825  */
1826 
1827 char           *
copy_nword(char * from,char * to,int len)1828 copy_nword(char *from, char *to, int len)
1829 {
1830     return NETSNMP_REMOVE_CONST(char *, copy_nword_const(from, to, len));
1831 }
1832 
1833 const char           *
copy_nword_const(const char * from,char * to,int len)1834 copy_nword_const(const char *from, char *to, int len)
1835 {
1836     char            quote;
1837     if (!from || !to)
1838         return NULL;
1839     if ((*from == '\"') || (*from == '\'')) {
1840         quote = *(from++);
1841         while ((*from != quote) && (*from != 0)) {
1842             if ((*from == '\\') && (*(from + 1) != 0)) {
1843                 if (len > 0) {  /* don't copy beyond len bytes */
1844                     *to++ = *(from + 1);
1845                     if (--len == 0)
1846                         *(to - 1) = '\0';       /* null protect the last spot */
1847                 }
1848                 from = from + 2;
1849             } else {
1850                 if (len > 0) {  /* don't copy beyond len bytes */
1851                     *to++ = *from++;
1852                     if (--len == 0)
1853                         *(to - 1) = '\0';       /* null protect the last spot */
1854                 } else
1855                     from++;
1856             }
1857         }
1858         if (*from == 0) {
1859             DEBUGMSGTL(("read_config_copy_word",
1860                         "no end quote found in config string\n"));
1861         } else
1862             from++;
1863     } else {
1864         while (*from != 0 && !isspace((unsigned char)(*from))) {
1865             if ((*from == '\\') && (*(from + 1) != 0)) {
1866                 if (len > 0) {  /* don't copy beyond len bytes */
1867                     *to++ = *(from + 1);
1868                     if (--len == 0)
1869                         *(to - 1) = '\0';       /* null protect the last spot */
1870                 }
1871                 from = from + 2;
1872             } else {
1873                 if (len > 0) {  /* don't copy beyond len bytes */
1874                     *to++ = *from++;
1875                     if (--len == 0)
1876                         *(to - 1) = '\0';       /* null protect the last spot */
1877                 } else
1878                     from++;
1879             }
1880         }
1881     }
1882     if (len > 0)
1883         *to = 0;
1884     from = skip_white_const(from);
1885     return (from);
1886 }                               /* copy_nword */
1887 
1888 /*
1889  * copy_word
1890  * copies the next 'token' from 'from' into 'to'.
1891  * currently a token is anything seperate by white space
1892  * or within quotes (double or single) (i.e. "the red rose"
1893  * is one token, \"the red rose\" is three tokens)
1894  * a '\' character will allow a quote character to be treated
1895  * as a regular character
1896  * It returns a pointer to first non-white space after the end of the token
1897  * being copied or to 0 if we reach the end.
1898  */
1899 
1900 static int      have_warned = 0;
1901 char           *
copy_word(char * from,char * to)1902 copy_word(char *from, char *to)
1903 {
1904     if (!have_warned) {
1905         snmp_log(LOG_INFO,
1906                  "copy_word() called.  Use copy_nword() instead.\n");
1907         have_warned = 1;
1908     }
1909     return copy_nword(from, to, SPRINT_MAX_LEN);
1910 }                               /* copy_word */
1911 
1912 /**
1913  * Stores an quoted version of the first len bytes from str into saveto.
1914  *
1915  * If all octets in str are in the set [[:alnum:] ] then the quotation
1916  * is to enclose the string in quotation marks ("str") otherwise the
1917  * quotation is to prepend the string 0x and then add the hex representation
1918  * of all characters from str (0x737472)
1919  *
1920  * @param[in] saveto pointer to output stream, is assumed to be big enough.
1921  * @param[in] str pointer of the data that is to be stored.
1922  * @param[in] len length of the data that is to be stored.
1923  * @return A pointer to saveto after str is added to it.
1924  */
1925 char           *
read_config_save_octet_string(char * saveto,const u_char * str,size_t len)1926 read_config_save_octet_string(char *saveto, const u_char * str, size_t len)
1927 {
1928     size_t          i;
1929     const u_char   *cp;
1930 
1931     /*
1932      * is everything easily printable
1933      */
1934     for (i = 0, cp = str; i < len && cp &&
1935          (isalpha(*cp) || isdigit(*cp) || *cp == ' '); cp++, i++);
1936 
1937     if (len != 0 && i == len) {
1938         *saveto++ = '"';
1939         memcpy(saveto, str, len);
1940         saveto += len;
1941         *saveto++ = '"';
1942         *saveto = '\0';
1943     } else {
1944         if (str != NULL) {
1945             sprintf(saveto, "0x");
1946             saveto += 2;
1947             for (i = 0; i < len; i++) {
1948                 sprintf(saveto, "%02x", str[i]);
1949                 saveto = saveto + 2;
1950             }
1951         } else {
1952             sprintf(saveto, "\"\"");
1953             saveto += 2;
1954         }
1955     }
1956     return saveto;
1957 }
1958 
1959 /**
1960  * Reads an octet string that was saved by the
1961  * read_config_save_octet_string() function.
1962  *
1963  * @param[in]     readfrom Pointer to the input data to be parsed.
1964  * @param[in,out] str      Pointer to the output buffer pointer. The data
1965  *   written to the output buffer will be '\0'-terminated. If *str == NULL,
1966  *   an output buffer will be allocated that is one byte larger than the
1967  *   data stored.
1968  * @param[in,out] len      If str != NULL, *len is the size of the buffer *str
1969  *   points at. If str == NULL, the value passed via *len is ignored.
1970  *   Before this function returns the number of bytes read will be stored
1971  *   in *len. If a buffer overflow occurs, *len will be set to 0.
1972  *
1973  * @return A pointer to the next character in the input to be parsed if
1974  *   parsing succeeded; NULL when the end of the input string has been reached
1975  *   or if an error occurred.
1976  */
1977 char           *
read_config_read_octet_string(const char * readfrom,u_char ** str,size_t * len)1978 read_config_read_octet_string(const char *readfrom, u_char ** str,
1979                               size_t * len)
1980 {
1981     return NETSNMP_REMOVE_CONST(char *,
1982                read_config_read_octet_string_const(readfrom, str, len));
1983 }
1984 
1985 const char     *
read_config_read_octet_string_const(const char * readfrom,u_char ** str,size_t * len)1986 read_config_read_octet_string_const(const char *readfrom, u_char ** str,
1987                                     size_t * len)
1988 {
1989     u_char         *cptr;
1990     const char     *cptr1;
1991     u_int           tmp;
1992     size_t          i, ilen;
1993 
1994     if (readfrom == NULL || str == NULL || len == NULL)
1995         return NULL;
1996 
1997     if (strncasecmp(readfrom, "0x", 2) == 0) {
1998         /*
1999          * A hex string submitted. How long?
2000          */
2001         readfrom += 2;
2002         cptr1 = skip_not_white_const(readfrom);
2003         if (cptr1)
2004             ilen = (cptr1 - readfrom);
2005         else
2006             ilen = strlen(readfrom);
2007 
2008         if (ilen % 2) {
2009             snmp_log(LOG_WARNING,"invalid hex string: wrong length\n");
2010             DEBUGMSGTL(("read_config_read_octet_string",
2011                         "invalid hex string: wrong length"));
2012             return NULL;
2013         }
2014         ilen = ilen / 2;
2015 
2016         /*
2017          * malloc data space if needed (+1 for good measure)
2018          */
2019         if (*str == NULL) {
2020             *str = (u_char *) malloc(ilen + 1);
2021             if (!*str)
2022                 return NULL;
2023         } else {
2024             /*
2025              * require caller to have +1, and bail if not enough space.
2026              */
2027             if (ilen >= *len) {
2028                 snmp_log(LOG_WARNING,"buffer too small to read octet string (%lu < %lu)\n",
2029                          (unsigned long)*len, (unsigned long)ilen);
2030                 DEBUGMSGTL(("read_config_read_octet_string",
2031                             "buffer too small (%lu < %lu)", (unsigned long)*len, (unsigned long)ilen));
2032                 *len = 0;
2033                 cptr1 = skip_not_white_const(readfrom);
2034                 return skip_white_const(cptr1);
2035             }
2036         }
2037 
2038         /*
2039          * copy validated data
2040          */
2041         cptr = *str;
2042         for (i = 0; i < ilen; i++) {
2043             if (1 == sscanf(readfrom, "%2x", &tmp))
2044                 *cptr++ = (u_char) tmp;
2045             else {
2046                 /*
2047                  * we may lose memory, but don't know caller's buffer XX free(cptr);
2048                  */
2049                 return (NULL);
2050             }
2051             readfrom += 2;
2052         }
2053         /*
2054          * Terminate the output buffer.
2055          */
2056         *cptr++ = '\0';
2057         *len = ilen;
2058         readfrom = skip_white_const(readfrom);
2059     } else {
2060         /*
2061          * Normal string
2062          */
2063 
2064         /*
2065          * malloc string space if needed (including NULL terminator)
2066          */
2067         if (*str == NULL) {
2068             char            buf[SNMP_MAXBUF];
2069             readfrom = copy_nword_const(readfrom, buf, sizeof(buf));
2070 
2071             *len = strlen(buf);
2072             *str = (u_char *) malloc(*len + 1);
2073             if (*str == NULL)
2074                 return NULL;
2075             memcpy(*str, buf, *len + 1);
2076         } else {
2077             readfrom = copy_nword_const(readfrom, (char *) *str, *len);
2078             if (*len)
2079                 *len = strlen((char *) *str);
2080         }
2081     }
2082 
2083     return readfrom;
2084 }
2085 
2086 /*
2087  * read_config_save_objid(): saves an objid as a numerical string
2088  */
2089 char           *
read_config_save_objid(char * saveto,oid * objid,size_t len)2090 read_config_save_objid(char *saveto, oid * objid, size_t len)
2091 {
2092     int             i;
2093 
2094     if (len == 0) {
2095         strcat(saveto, "NULL");
2096         saveto += strlen(saveto);
2097         return saveto;
2098     }
2099 
2100     /*
2101      * in case len=0, this makes it easier to read it back in
2102      */
2103     for (i = 0; i < (int) len; i++) {
2104         sprintf(saveto, ".%" NETSNMP_PRIo "d", objid[i]);
2105         saveto += strlen(saveto);
2106     }
2107     return saveto;
2108 }
2109 
2110 /*
2111  * read_config_read_objid(): reads an objid from a format saved by the above
2112  */
2113 char           *
read_config_read_objid(char * readfrom,oid ** objid,size_t * len)2114 read_config_read_objid(char *readfrom, oid ** objid, size_t * len)
2115 {
2116     return NETSNMP_REMOVE_CONST(char *,
2117              read_config_read_objid_const(readfrom, objid, len));
2118 }
2119 
2120 const char     *
read_config_read_objid_const(const char * readfrom,oid ** objid,size_t * len)2121 read_config_read_objid_const(const char *readfrom, oid ** objid, size_t * len)
2122 {
2123 
2124     if (objid == NULL || readfrom == NULL || len == NULL)
2125         return NULL;
2126 
2127     if (*objid == NULL) {
2128         *len = 0;
2129         if ((*objid = (oid *) malloc(MAX_OID_LEN * sizeof(oid))) == NULL)
2130             return NULL;
2131         *len = MAX_OID_LEN;
2132     }
2133 
2134     if (strncmp(readfrom, "NULL", 4) == 0) {
2135         /*
2136          * null length oid
2137          */
2138         *len = 0;
2139     } else {
2140         /*
2141          * qualify the string for read_objid
2142          */
2143         char            buf[SPRINT_MAX_LEN];
2144         copy_nword_const(readfrom, buf, sizeof(buf));
2145 
2146         if (!read_objid(buf, *objid, len)) {
2147             DEBUGMSGTL(("read_config_read_objid", "Invalid OID"));
2148             *len = 0;
2149             return NULL;
2150         }
2151     }
2152 
2153     readfrom = skip_token_const(readfrom);
2154     return readfrom;
2155 }
2156 
2157 /**
2158  * read_config_read_data reads data of a given type from a token(s) on a
2159  * configuration line.  The supported types are:
2160  *
2161  *    - ASN_INTEGER
2162  *    - ASN_TIMETICKS
2163  *    - ASN_UNSIGNED
2164  *    - ASN_OCTET_STR
2165  *    - ASN_BIT_STR
2166  *    - ASN_OBJECT_ID
2167  *
2168  * @param type the asn data type to be read in.
2169  *
2170  * @param readfrom the configuration line data to be read.
2171  *
2172  * @param dataptr an allocated pointer expected to match the type being read
2173  *        (int *, u_int *, char **, oid **)
2174  *
2175  * @param len is the length of an asn oid or octet/bit string, not required
2176  *            for the asn integer, unsigned integer, and timeticks types
2177  *
2178  * @return the next token in the configuration line.  NULL if none left or
2179  * if an unknown type.
2180  *
2181  */
2182 char           *
read_config_read_data(int type,char * readfrom,void * dataptr,size_t * len)2183 read_config_read_data(int type, char *readfrom, void *dataptr,
2184                       size_t * len)
2185 {
2186     int            *intp;
2187     char          **charpp;
2188     oid           **oidpp;
2189     unsigned int   *uintp;
2190 
2191     if (dataptr && readfrom)
2192         switch (type) {
2193         case ASN_INTEGER:
2194             intp = (int *) dataptr;
2195             *intp = atoi(readfrom);
2196             readfrom = skip_token(readfrom);
2197             return readfrom;
2198 
2199         case ASN_TIMETICKS:
2200         case ASN_UNSIGNED:
2201             uintp = (unsigned int *) dataptr;
2202             *uintp = strtoul(readfrom, NULL, 0);
2203             readfrom = skip_token(readfrom);
2204             return readfrom;
2205 
2206         case ASN_IPADDRESS:
2207             intp = (int *) dataptr;
2208             *intp = inet_addr(readfrom);
2209             if ((*intp == -1) &&
2210                 (strncmp(readfrom, "255.255.255.255", 15) != 0))
2211                 return NULL;
2212             readfrom = skip_token(readfrom);
2213             return readfrom;
2214 
2215         case ASN_OCTET_STR:
2216         case ASN_BIT_STR:
2217             charpp = (char **) dataptr;
2218             return read_config_read_octet_string(readfrom,
2219                                                  (u_char **) charpp, len);
2220 
2221         case ASN_OBJECT_ID:
2222             oidpp = (oid **) dataptr;
2223             return read_config_read_objid(readfrom, oidpp, len);
2224 
2225         default:
2226             DEBUGMSGTL(("read_config_read_data", "Fail: Unknown type: %d",
2227                         type));
2228             return NULL;
2229         }
2230     return NULL;
2231 }
2232 
2233 /*
2234  * read_config_read_memory():
2235  *
2236  * similar to read_config_read_data, but expects a generic memory
2237  * pointer rather than a specific type of pointer.  Len is expected to
2238  * be the amount of available memory.
2239  */
2240 char           *
read_config_read_memory(int type,char * readfrom,char * dataptr,size_t * len)2241 read_config_read_memory(int type, char *readfrom,
2242                         char *dataptr, size_t * len)
2243 {
2244     int            *intp;
2245     unsigned int   *uintp;
2246     char            buf[SPRINT_MAX_LEN];
2247 
2248     if (!dataptr || !readfrom)
2249         return NULL;
2250 
2251     switch (type) {
2252     case ASN_INTEGER:
2253         if (*len < sizeof(int))
2254             return NULL;
2255         intp = (int *) dataptr;
2256         readfrom = copy_nword(readfrom, buf, sizeof(buf));
2257         *intp = atoi(buf);
2258         *len = sizeof(int);
2259         return readfrom;
2260 
2261     case ASN_COUNTER:
2262     case ASN_TIMETICKS:
2263     case ASN_UNSIGNED:
2264         if (*len < sizeof(unsigned int))
2265             return NULL;
2266         uintp = (unsigned int *) dataptr;
2267         readfrom = copy_nword(readfrom, buf, sizeof(buf));
2268         *uintp = strtoul(buf, NULL, 0);
2269         *len = sizeof(unsigned int);
2270         return readfrom;
2271 
2272     case ASN_IPADDRESS:
2273         if (*len < sizeof(int))
2274             return NULL;
2275         intp = (int *) dataptr;
2276         readfrom = copy_nword(readfrom, buf, sizeof(buf));
2277         *intp = inet_addr(buf);
2278         if ((*intp == -1) && (strcmp(buf, "255.255.255.255") != 0))
2279             return NULL;
2280         *len = sizeof(int);
2281         return readfrom;
2282 
2283     case ASN_OCTET_STR:
2284     case ASN_BIT_STR:
2285     case ASN_PRIV_IMPLIED_OCTET_STR:
2286         return read_config_read_octet_string(readfrom,
2287                                              (u_char **) & dataptr, len);
2288 
2289     case ASN_PRIV_IMPLIED_OBJECT_ID:
2290     case ASN_OBJECT_ID:
2291         readfrom =
2292             read_config_read_objid(readfrom, (oid **) & dataptr, len);
2293         *len *= sizeof(oid);
2294         return readfrom;
2295 
2296     case ASN_COUNTER64:
2297         if (*len < sizeof(struct counter64))
2298             return NULL;
2299         *len = sizeof(struct counter64);
2300         read64((struct counter64 *) dataptr, readfrom);
2301         readfrom = skip_token(readfrom);
2302         return readfrom;
2303     }
2304 
2305     DEBUGMSGTL(("read_config_read_memory", "Fail: Unknown type: %d", type));
2306     return NULL;
2307 }
2308 
2309 /**
2310  * read_config_store_data stores data of a given type to a configuration line
2311  * into the storeto buffer.
2312  * Calls read_config_store_data_prefix with the prefix parameter set to a char
2313  * space.  The supported types are:
2314  *
2315  *    - ASN_INTEGER
2316  *    - ASN_TIMETICKS
2317  *    - ASN_UNSIGNED
2318  *    - ASN_OCTET_STR
2319  *    - ASN_BIT_STR
2320  *    - ASN_OBJECT_ID
2321  *
2322  * @param type    the asn data type to be stored
2323  *
2324  * @param storeto a pre-allocated char buffer which will contain the data
2325  *                to be stored
2326  *
2327  * @param dataptr contains the value to be stored, the supported pointers:
2328  *                (int *, u_int *, char **, oid **)
2329  *
2330  * @param len     is the length of the value to be stored
2331  *                (not required for the asn integer, unsigned integer,
2332  *                 and timeticks types)
2333  *
2334  * @return character pointer to the end of the line. NULL if an unknown type.
2335  */
2336 char           *
read_config_store_data(int type,char * storeto,void * dataptr,size_t * len)2337 read_config_store_data(int type, char *storeto, void *dataptr, size_t * len)
2338 {
2339     return read_config_store_data_prefix(' ', type, storeto, dataptr,
2340                                                          (len ? *len : 0));
2341 }
2342 
2343 char           *
read_config_store_data_prefix(char prefix,int type,char * storeto,void * dataptr,size_t len)2344 read_config_store_data_prefix(char prefix, int type, char *storeto,
2345                               void *dataptr, size_t len)
2346 {
2347     int            *intp;
2348     u_char        **charpp;
2349     unsigned int   *uintp;
2350     struct in_addr  in;
2351     oid           **oidpp;
2352 
2353     if (dataptr && storeto)
2354         switch (type) {
2355         case ASN_INTEGER:
2356             intp = (int *) dataptr;
2357             sprintf(storeto, "%c%d", prefix, *intp);
2358             return (storeto + strlen(storeto));
2359 
2360         case ASN_TIMETICKS:
2361         case ASN_UNSIGNED:
2362             uintp = (unsigned int *) dataptr;
2363             sprintf(storeto, "%c%u", prefix, *uintp);
2364             return (storeto + strlen(storeto));
2365 
2366         case ASN_IPADDRESS:
2367             in.s_addr = *(unsigned int *) dataptr;
2368             sprintf(storeto, "%c%s", prefix, inet_ntoa(in));
2369             return (storeto + strlen(storeto));
2370 
2371         case ASN_OCTET_STR:
2372         case ASN_BIT_STR:
2373             *storeto++ = prefix;
2374             charpp = (u_char **) dataptr;
2375             return read_config_save_octet_string(storeto, *charpp, len);
2376 
2377         case ASN_OBJECT_ID:
2378             *storeto++ = prefix;
2379             oidpp = (oid **) dataptr;
2380             return read_config_save_objid(storeto, *oidpp, len);
2381 
2382         default:
2383             DEBUGMSGTL(("read_config_store_data_prefix",
2384                         "Fail: Unknown type: %d", type));
2385             return NULL;
2386         }
2387     return NULL;
2388 }
2389 
2390 /** @} */
2391