Imported Upstream version 2.4.3
[platform/upstream/audit.git] / audisp / audispd-config.c
1 /* audispd-config.c -- 
2  * Copyright 2007-08,2010,2014 Red Hat Inc., Durham, North Carolina.
3  * All Rights Reserved.
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  *
19  * Authors:
20  *   Steve Grubb <sgrubb@redhat.com>
21  * 
22  */
23
24 #include "config.h"
25 #include <string.h>
26 #include <stdio.h>
27 #include <fcntl.h>
28 #include <sys/stat.h>
29 #include <errno.h>
30 #include <unistd.h>
31 #include <stdlib.h>
32 #include <libgen.h>
33 #include <ctype.h>
34 #include <limits.h>
35 #include "audispd-config.h"
36 #include "private.h"
37
38 /* Local prototypes */
39 struct nv_pair
40 {
41         const char *name;
42         const char *value;
43         const char *option;
44 };
45
46 struct kw_pair 
47 {
48         const char *name;
49         int (*parser)(struct nv_pair *, int, daemon_conf_t *);
50         int max_options;
51 };
52
53 struct nv_list
54
55         const char *name;
56         int option;
57 };
58
59 static char *get_line(FILE *f, char *buf, unsigned size, int *lineno,
60                 const char *file);
61 static int nv_split(char *buf, struct nv_pair *nv);
62 static const struct kw_pair *kw_lookup(const char *val);
63 static int q_depth_parser(struct nv_pair *nv, int line, 
64                 daemon_conf_t *config);
65 static int name_format_parser(struct nv_pair *nv, int line, 
66                 daemon_conf_t *config);
67 static int name_parser(struct nv_pair *nv, int line, 
68                 daemon_conf_t *config);
69 static int overflow_action_parser(struct nv_pair *nv, int line, 
70                 daemon_conf_t *config);
71 static int priority_boost_parser(struct nv_pair *nv, int line,
72                 daemon_conf_t *config);
73 static int max_restarts_parser(struct nv_pair *nv, int line,
74                 daemon_conf_t *config);
75 static int sanity_check(daemon_conf_t *config, const char *file);
76
77 static const struct kw_pair keywords[] = 
78 {
79   {"q_depth",                  q_depth_parser,                  0 },
80   {"name_format",              name_format_parser,              0 },
81   {"name",                     name_parser,                     0 },
82   {"overflow_action",          overflow_action_parser,          0 },
83   {"priority_boost",           priority_boost_parser,           0 },
84   {"max_restarts",             max_restarts_parser,             0 },
85   { NULL,                      NULL }
86 };
87
88 static const struct nv_list node_name_formats[] =
89 {
90   {"none",      N_NONE },
91   {"hostname",  N_HOSTNAME },
92   {"fqd",       N_FQD },
93   {"numeric",   N_NUMERIC },
94   {"user",      N_USER },
95   { NULL,  0 }
96 };
97
98 static const struct nv_list overflow_actions[] =
99 {
100   {"ignore",  O_IGNORE },
101   {"syslog",  O_SYSLOG },
102   {"suspend", O_SUSPEND },
103   {"single",  O_SINGLE },
104   {"halt",    O_HALT },
105   { NULL,     0 }
106 };
107
108 /*
109  * Set everything to its default value
110 */
111 void clear_config(daemon_conf_t *config)
112 {
113         config->q_depth = 80;
114         config->overflow_action = O_SYSLOG;
115         config->priority_boost = 4;
116         config->max_restarts = 10;
117         config->node_name_format = N_NONE;
118         config->name = NULL;
119 }
120
121 int load_config(daemon_conf_t *config, const char *file)
122 {
123         int fd, rc, mode, lineno = 1;
124         struct stat st;
125         FILE *f;
126         char buf[160];
127
128         clear_config(config);
129
130         /* open the file */
131         mode = O_RDONLY;
132         rc = open(file, mode);
133         if (rc < 0) {
134                 if (errno != ENOENT) {
135                         audit_msg(LOG_ERR, "Error opening %s (%s)", file,
136                                 strerror(errno));
137                         return 1;
138                 }
139                 audit_msg(LOG_WARNING,
140                         "Config file %s doesn't exist, skipping", file);
141                 return 0;
142         }
143         fd = rc;
144
145         /* check the file's permissions: owned by root, not world writable,
146          * not symlink.
147          */
148         if (fstat(fd, &st) < 0) {
149                 audit_msg(LOG_ERR, "Error fstat'ing config file (%s)", 
150                         strerror(errno));
151                 close(fd);
152                 return 1;
153         }
154         if (st.st_uid != 0) {
155                 audit_msg(LOG_ERR, "Error - %s isn't owned by root", 
156                         file);
157                 close(fd);
158                 return 1;
159         }
160         if ((st.st_mode & S_IWOTH) == S_IWOTH) {
161                 audit_msg(LOG_ERR, "Error - %s is world writable", 
162                         file);
163                 close(fd);
164                 return 1;
165         }
166         if (!S_ISREG(st.st_mode)) {
167                 audit_msg(LOG_ERR, "Error - %s is not a regular file", 
168                         file);
169                 close(fd);
170                 return 1;
171         }
172
173         /* it's ok, read line by line */
174         f = fdopen(fd, "rm");
175         if (f == NULL) {
176                 audit_msg(LOG_ERR, "Error - fdopen failed (%s)", 
177                         strerror(errno));
178                 close(fd);
179                 return 1;
180         }
181
182         while (get_line(f, buf, sizeof(buf), &lineno, file)) {
183                 // convert line into name-value pair
184                 const struct kw_pair *kw;
185                 struct nv_pair nv;
186                 rc = nv_split(buf, &nv);
187                 switch (rc) {
188                         case 0: // fine
189                                 break;
190                         case 1: // not the right number of tokens.
191                                 audit_msg(LOG_ERR, 
192                                 "Wrong number of arguments for line %d in %s", 
193                                         lineno, file);
194                                 break;
195                         case 2: // no '=' sign
196                                 audit_msg(LOG_ERR, 
197                                         "Missing equal sign for line %d in %s", 
198                                         lineno, file);
199                                 break;
200                         default: // something else went wrong... 
201                                 audit_msg(LOG_ERR, 
202                                         "Unknown error for line %d in %s", 
203                                         lineno, file);
204                                 break;
205                 }
206                 if (nv.name == NULL) {
207                         lineno++;
208                         continue;
209                 }
210                 if (nv.value == NULL) {
211                         fclose(f);
212                         audit_msg(LOG_ERR, 
213                                 "Not processing any more lines in %s", file);
214                         return 1;
215                 }
216
217                 /* identify keyword or error */
218                 kw = kw_lookup(nv.name);
219                 if (kw->name == NULL) {
220                         audit_msg(LOG_ERR, 
221                                 "Unknown keyword \"%s\" in line %d of %s", 
222                                 nv.name, lineno, file);
223                         fclose(f);
224                         return 1;
225                 }
226
227                 /* Check number of options */
228                 if (kw->max_options == 0 && nv.option != NULL) {
229                         audit_msg(LOG_ERR, 
230                                 "Keyword \"%s\" has invalid option "
231                                 "\"%s\" in line %d of %s", 
232                                 nv.name, nv.option, lineno, file);
233                         fclose(f);
234                         return 1;
235                 }
236
237                 /* dispatch to keyword's local parser */
238                 rc = kw->parser(&nv, lineno, config);
239                 if (rc != 0) {
240                         fclose(f);
241                         return 1; // local parser puts message out
242                 }
243
244                 lineno++;
245         }
246
247         fclose(f);
248         if (lineno > 1)
249                 return sanity_check(config, file);
250         return 0;
251 }
252
253 static char *get_line(FILE *f, char *buf, unsigned size, int *lineno,
254         const char *file)
255 {
256         int too_long = 0;
257
258         while (fgets_unlocked(buf, size, f)) {
259                 /* remove newline */
260                 char *ptr = strchr(buf, 0x0a);
261                 if (ptr) {
262                         if (!too_long) {
263                                 *ptr = 0;
264                                 return buf;
265                         }
266                         // Reset and start with the next line
267                         too_long = 0;
268                         *lineno = *lineno + 1;
269                 } else {
270                         // If a line is too long skip it.
271                         //  Only output 1 warning
272                         if (!too_long)
273                                 audit_msg(LOG_ERR,
274                                         "Skipping line %d in %s: too long",
275                                         *lineno, file);
276                         too_long = 1;
277                 }
278         }
279         return NULL;
280 }
281
282 static int nv_split(char *buf, struct nv_pair *nv)
283 {
284         /* Get the name part */
285         char *ptr, *saved = NULL;
286
287         nv->name = NULL;
288         nv->value = NULL;
289         nv->option = NULL;
290         ptr = strtok_r(buf, " ", &saved);
291         if (ptr == NULL)
292                 return 0; /* If there's nothing, go to next line */
293         if (ptr[0] == '#')
294                 return 0; /* If there's a comment, go to next line */
295         nv->name = ptr;
296
297         /* Check for a '=' */
298         ptr = strtok_r(NULL, " ", &saved);
299         if (ptr == NULL)
300                 return 1;
301         if (strcmp(ptr, "=") != 0)
302                 return 2;
303
304         /* get the value */
305         ptr = strtok_r(NULL, " ", &saved);
306         if (ptr == NULL)
307                 return 1;
308         nv->value = ptr;
309
310         /* See if there's an option */
311         ptr = strtok_r(NULL, " ", &saved);
312         if (ptr) {
313                 nv->option = ptr;
314
315                 /* Make sure there's nothing else */
316                 ptr = strtok_r(NULL, " ", &saved);
317                 if (ptr)
318                         return 1;
319         }
320
321         /* Everything is OK */
322         return 0;
323 }
324
325 static const struct kw_pair *kw_lookup(const char *val)
326 {
327         int i = 0;
328         while (keywords[i].name != NULL) {
329                 if (strcasecmp(keywords[i].name, val) == 0)
330                         break;
331                 i++;
332         }
333         return &keywords[i];
334 }
335  
336 static int q_depth_parser(struct nv_pair *nv, int line, 
337                 daemon_conf_t *config)
338 {
339         const char *ptr = nv->value;
340         unsigned long i;
341
342         /* check that all chars are numbers */
343         for (i=0; ptr[i]; i++) {
344                 if (!isdigit(ptr[i])) {
345                         audit_msg(LOG_ERR,
346                                 "Value %s should only be numbers - line %d",
347                                 nv->value, line);
348                         return 1;
349                 }
350         }
351
352         /* convert to unsigned long */
353         errno = 0;
354         i = strtoul(nv->value, NULL, 10);
355         if (errno) {
356                 audit_msg(LOG_ERR,
357                         "Error converting string to a number (%s) - line %d",
358                         strerror(errno), line);
359                 return 1;
360         }
361         if (i > 99999) {
362                 audit_msg(LOG_ERR, "q_depth must be 99999 or less");
363                 return 1;
364         }
365         config->q_depth = i;
366         return 0;
367
368 }
369
370 static int name_format_parser(struct nv_pair *nv, int line, 
371                 daemon_conf_t *config)
372 {
373         int i;
374
375         for (i=0; node_name_formats[i].name != NULL; i++) {
376                 if (strcasecmp(nv->value, node_name_formats[i].name) == 0) {
377                         config->node_name_format = node_name_formats[i].option;
378                         return 0;
379                 }
380         }
381         audit_msg(LOG_ERR, "Option %s not found - line %d", nv->value, line);
382         return 1;
383 }
384
385 static int name_parser(struct nv_pair *nv, int line, 
386                 daemon_conf_t *config)
387 {
388         if (nv->value == NULL)
389                 config->name = NULL;
390         else
391                 config->name = strdup(nv->value);
392         return 0;
393 }
394
395 static int overflow_action_parser(struct nv_pair *nv, int line, 
396                 daemon_conf_t *config)
397 {
398         int i;
399
400         for (i=0; overflow_actions[i].name != NULL; i++) {
401                 if (strcasecmp(nv->value, overflow_actions[i].name) == 0) {
402                         config->overflow_action = overflow_actions[i].option;
403                         return 0;
404                 }
405         }
406         audit_msg(LOG_ERR, "Option %s not found - line %d", nv->value, line);
407         return 1;
408 }
409
410 static int priority_boost_parser(struct nv_pair *nv, int line,
411         struct daemon_conf *config)
412 {
413         const char *ptr = nv->value;
414         unsigned long i;
415
416         audit_msg(LOG_DEBUG, "priority_boost_parser called with: %s",
417                                 nv->value);
418
419         /* check that all chars are numbers */
420         for (i=0; ptr[i]; i++) {
421                 if (!isdigit(ptr[i])) {
422                         audit_msg(LOG_ERR,
423                                 "Value %s should only be numbers - line %d",
424                                 nv->value, line);
425                         return 1;
426                 }
427         }
428         /* convert to unsigned int */
429         errno = 0;
430         i = strtoul(nv->value, NULL, 10);
431         if (errno) {
432                 audit_msg(LOG_ERR,
433                         "Error converting string to a number (%s) - line %d",
434                         strerror(errno), line);
435                 return 1;
436         }
437         /* Check its range */
438         if (i > INT_MAX) {
439                 audit_msg(LOG_ERR,
440                         "Error - converted number (%s) is too large - line %d",
441                         nv->value, line);
442                 return 1;
443         }
444         config->priority_boost = (unsigned int)i;
445         return 0;
446 }
447
448 static int max_restarts_parser(struct nv_pair *nv, int line,
449         struct daemon_conf *config)
450 {
451         const char *ptr = nv->value;
452         unsigned long i;
453
454         audit_msg(LOG_DEBUG, "max_restarts_parser called with: %s",
455                                 nv->value);
456
457         /* check that all chars are numbers */
458         for (i=0; ptr[i]; i++) {
459                 if (!isdigit(ptr[i])) {
460                         audit_msg(LOG_ERR,
461                                 "Value %s should only be numbers - line %d",
462                                 nv->value, line);
463                         return 1;
464                 }
465         }
466         /* convert to unsigned int */
467         errno = 0;
468         i = strtoul(nv->value, NULL, 10);
469         if (errno) {
470                 audit_msg(LOG_ERR,
471                         "Error converting string to a number (%s) - line %d",
472                         strerror(errno), line);
473                 return 1;
474         }
475         /* Check its range */
476         if (i > INT_MAX) {
477                 audit_msg(LOG_ERR,
478                         "Error - converted number (%s) is too large - line %d",
479                         nv->value, line);
480                 return 1;
481         }
482         config->max_restarts = (unsigned int)i;
483         return 0;
484 }
485
486 /*
487  * This function is where we do the integrated check of the audispd config
488  * options. At this point, all fields have been read. Returns 0 if no
489  * problems and 1 if problems detected.
490  */
491 static int sanity_check(daemon_conf_t *config, const char *file)
492 {
493         /* Error checking */
494         if (config->node_name_format == N_USER && config->name == NULL) {
495                 audit_msg(LOG_ERR, 
496             "Error - node_name_format is user supplied but none given (%s)",
497                         file);
498                 return 1;
499         }
500         return 0;
501 }
502
503 void free_config(daemon_conf_t *config)
504 {
505         free((void *)config->name);
506 }
507