Imported Upstream version 2.4.3
[platform/upstream/audit.git] / auparse / auditd-config.c
1 /* auditd-config.c -- 
2  * Copyright 2007,2014 Red Hat Inc., Durham, North Carolina.
3  * All Rights Reserved.
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * This library 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 GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; 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 "internal.h"
26 #include <errno.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <sys/stat.h>
30 #include <unistd.h>
31 #include <fcntl.h>
32 #include <libgen.h>
33 #include <dirent.h>
34 #include <ctype.h>
35
36 /* Local prototypes */
37 struct _pair
38 {
39         const char *name;
40         const char *value;
41 };
42
43 struct kw_pair 
44 {
45         const char *name;
46         int (*parser)(const char *, int, struct daemon_conf *);
47 };
48
49 struct nv_list
50
51         const char *name;
52         int option;
53 };
54
55 static char *get_line(FILE *f, char *buf, unsigned size, int *lineno,
56         const char *file);
57 static int nv_split(char *buf, struct _pair *nv);
58 static const struct kw_pair *kw_lookup(const char *val);
59 static int log_file_parser(const char *val, int line, 
60                 struct daemon_conf *config);
61 static int num_logs_parser(const char *val, int line, 
62                 struct daemon_conf *config);
63 static int log_format_parser(const char *val, int line, 
64                 struct daemon_conf *config);
65
66 static const struct kw_pair keywords[] = 
67 {
68   {"log_file",                 log_file_parser },
69   {"log_format",               log_format_parser },
70   {"num_logs",                 num_logs_parser },
71   { NULL,                      NULL }
72 };
73
74 static const struct nv_list log_formats[] =
75 {
76   {"raw",  LF_RAW },
77   {"nolog", LF_NOLOG },
78   { NULL,  0 }
79 };
80
81
82 /*
83  * Set everything to its default value
84 */
85 void clear_config(struct daemon_conf *config)
86 {
87         config->qos = QOS_NON_BLOCKING;
88         config->sender_uid = 0;
89         config->sender_pid = 0;
90         config->sender_ctx = NULL;
91         config->log_file = strdup("/var/log/audit/audit.log");
92         config->log_format = LF_RAW;
93         config->log_group = 0;
94         config->priority_boost = 3;
95         config->flush =  FT_NONE;
96         config->freq = 0;
97         config->num_logs = 0L;
98         config->dispatcher = NULL;
99         config->node_name_format = N_NONE;
100         config->node_name = NULL;
101         config->max_log_size = 0L;
102         config->max_log_size_action = SZ_IGNORE;
103         config->space_left = 0L;
104         config->space_left_action = FA_IGNORE;
105         config->space_left_exe = NULL;
106         config->action_mail_acct = strdup("root");
107         config->admin_space_left= 0L;
108         config->admin_space_left_action = FA_IGNORE;
109         config->admin_space_left_exe = NULL;
110         config->disk_full_action = FA_IGNORE;
111         config->disk_full_exe = NULL;
112         config->disk_error_action = FA_SYSLOG;
113         config->disk_error_exe = NULL;
114 }
115
116 int load_config(struct daemon_conf *config, log_test_t lt)
117 {
118         int fd, rc, lineno = 1;
119         struct stat st;
120         FILE *f;
121         char buf[160];
122
123         clear_config(config);
124         lt = lt;
125
126         /* open the file */
127         rc = open(CONFIG_FILE, O_RDONLY|O_NOFOLLOW);
128         if (rc < 0) {
129                 if (errno != ENOENT) {
130                         audit_msg(LOG_ERR, "Error opening config file (%s)", 
131                                 strerror(errno));
132                         return 1;
133                 }
134                 audit_msg(LOG_WARNING,
135                         "Config file %s doesn't exist, skipping", CONFIG_FILE);
136                 return 0;
137         }
138         fd = rc;
139
140         /* check the file's permissions: owned by root, not world writable,
141          * not symlink.
142          */
143         if (fstat(fd, &st) < 0) {
144                 audit_msg(LOG_ERR, "Error fstat'ing config file (%s)", 
145                         strerror(errno));
146                 close(fd);
147                 return 1;
148         }
149         if (st.st_uid != 0) {
150                 audit_msg(LOG_ERR, "Error - %s isn't owned by root", 
151                         CONFIG_FILE);
152                 close(fd);
153                 return 1;
154         }
155         if (!S_ISREG(st.st_mode)) {
156                 audit_msg(LOG_ERR, "Error - %s is not a regular file", 
157                         CONFIG_FILE);
158                 close(fd);
159                 return 1;
160         }
161
162         /* it's ok, read line by line */
163         f = fdopen(fd, "rm");
164         if (f == NULL) {
165                 audit_msg(LOG_ERR, "Error - fdopen failed (%s)", 
166                         strerror(errno));
167                 close(fd);
168                 return 1;
169         }
170
171         while (get_line(f,  buf, sizeof(buf), &lineno, CONFIG_FILE)) {
172                 // convert line into name-value pair
173                 const struct kw_pair *kw;
174                 struct _pair nv;
175                 rc = nv_split(buf, &nv);
176                 switch (rc) {
177                         case 0: // fine
178                                 break;
179                         case 1: // not the right number of tokens.
180                                 audit_msg(LOG_ERR, 
181                                 "Wrong number of arguments for line %d in %s", 
182                                         lineno, CONFIG_FILE);
183                                 break;
184                         case 2: // no '=' sign
185                                 audit_msg(LOG_ERR, 
186                                         "Missing equal sign for line %d in %s", 
187                                         lineno, CONFIG_FILE);
188                                 break;
189                         default: // something else went wrong... 
190                                 audit_msg(LOG_ERR, 
191                                         "Unknown error for line %d in %s", 
192                                         lineno, CONFIG_FILE);
193                                 break;
194                 }
195                 if (nv.name == NULL) {
196                         lineno++;
197                         continue;
198                 }
199                 if (nv.value == NULL) {
200                         fclose(f);
201                         audit_msg(LOG_ERR,
202                                 "Not processing any more lines in %s",
203                                 CONFIG_FILE);
204                         return 1;
205                 }
206
207                 /* identify keyword or error */
208                 kw = kw_lookup(nv.name);
209                 if (kw->name) {
210                         /* dispatch to keyword's local parser */
211                         rc = kw->parser(nv.value, lineno, config);
212                         if (rc != 0) {
213                                 fclose(f);
214                                 return 1; // local parser puts message out
215                         }
216                 }
217
218                 lineno++;
219         }
220
221         fclose(f);
222         return 0;
223 }
224
225 static char *get_line(FILE *f, char *buf, unsigned size, int *lineno,
226         const char *file)
227 {
228         int too_long = 0;
229
230         while (fgets_unlocked(buf, size, f)) {
231                 /* remove newline */
232                 char *ptr = strchr(buf, 0x0a);
233                 if (ptr) {
234                         if (!too_long) {
235                                 *ptr = 0;
236                                 return buf;
237                         }
238                         // Reset and start with the next line
239                         too_long = 0;
240                         *lineno = *lineno + 1;
241                 } else {
242                 // If a line is too long skip it.
243                 // Only output 1 warning
244                 if (!too_long)
245                         audit_msg(LOG_ERR,
246                                         "Skipping line %d in %s: too long",
247                                         *lineno, file);
248                         too_long = 1;
249                 }
250         }
251         return NULL;
252 }
253
254 static int nv_split(char *buf, struct _pair *nv)
255 {
256         /* Get the name part */
257         char *ptr;
258
259         nv->name = NULL;
260         nv->value = NULL;
261         ptr = audit_strsplit(buf);
262         if (ptr == NULL)
263                 return 0; /* If there's nothing, go to next line */
264         if (ptr[0] == '#')
265                 return 0; /* If there's a comment, go to next line */
266         nv->name = ptr;
267
268         /* Check for a '=' */
269         ptr = audit_strsplit(NULL);
270         if (ptr == NULL)
271                 return 1;
272         if (strcmp(ptr, "=") != 0)
273                 return 2;
274
275         /* get the value */
276         ptr = audit_strsplit(NULL);
277         if (ptr == NULL)
278                 return 1;
279         nv->value = ptr;
280
281         /* Make sure there's nothing else */
282         ptr = audit_strsplit(NULL);
283         if (ptr) {
284                 /* Allow one option, but check that there's not 2 */
285                 ptr = audit_strsplit(NULL);
286                 if (ptr)
287                         return 1;
288         }
289
290         /* Everything is OK */
291         return 0;
292 }
293
294 static const struct kw_pair *kw_lookup(const char *val)
295 {
296         int i = 0;
297         while (keywords[i].name != NULL) {
298                 if (strcasecmp(keywords[i].name, val) == 0)
299                         break;
300                 i++;
301         }
302         return &keywords[i];
303 }
304  
305 static int log_file_parser(const char *val, int line,struct daemon_conf *config)
306 {
307         char *dir = NULL, *tdir, *base;
308         DIR *d;
309         int fd, mode;
310         struct stat buf;
311
312         /* split name into dir and basename. */
313         tdir = strdup(val);
314         if (tdir)
315                 dir = dirname(tdir);
316         if (dir == NULL || strlen(dir) < 4) { //  '/var' is shortest dirname
317                 audit_msg(LOG_ERR, 
318                         "The directory name: %s is too short - line %d", 
319                         dir, line);
320                 free((void *)tdir);
321                 return 1;
322         }
323
324         base = basename((char *)val);
325         if (base == 0 || strlen(base) == 0) {
326                 audit_msg(LOG_ERR, "The file name: %s is too short - line %d", 
327                         base, line);
328                 free((void *)tdir);
329                 return 1;
330         }
331         
332         /* verify the directory path exists */
333         d = opendir(dir);
334         if (d == NULL) {
335                 audit_msg(LOG_ERR, "Could not open dir %s (%s)", dir, 
336                         strerror(errno));
337                 free((void *)tdir);
338                 return 1;
339         }
340         free((void *)tdir);
341         closedir(d);
342
343         /* if the file exists, see that its regular, owned by root, 
344          * and not world anything */
345         mode = O_RDONLY;
346
347         fd = open(val, mode);
348         if (fd < 0) {
349                 audit_msg(LOG_ERR, "Unable to open %s (%s)", val, 
350                                         strerror(errno));
351                 return 1;
352         }
353         if (fstat(fd, &buf) < 0) {
354                 audit_msg(LOG_ERR, "Unable to stat %s (%s)", 
355                                         val, strerror(errno));
356                 close(fd);
357                 return 1;
358         }
359         close(fd);
360         if (!S_ISREG(buf.st_mode)) {
361                 audit_msg(LOG_ERR, "%s is not a regular file", val);
362                 return 1;
363         }
364         if (buf.st_uid != 0) {
365                 audit_msg(LOG_ERR, "%s is not owned by root", val);
366                 return 1;
367         }
368         if ( (buf.st_mode & (S_IXUSR|S_IWGRP|S_IXGRP|S_IRWXO)) ) {
369                 audit_msg(LOG_ERR, "%s permissions should be 0600 or 0640",
370                                 val);
371                 return 1;
372         }
373         if ( !(buf.st_mode & S_IWUSR) ) {
374                 audit_msg(LOG_ERR, "audit log is not writable by owner");
375                 return 1;
376         }
377
378         free((void *)config->log_file);
379         config->log_file = strdup(val);
380         if (config->log_file == NULL)
381                 return 1;
382         return 0;
383 }
384
385 static int num_logs_parser(const char *val, int line, 
386                 struct daemon_conf *config)
387 {
388         const char *ptr = val;
389         unsigned long i;
390
391         /* check that all chars are numbers */
392         for (i=0; ptr[i]; i++) {
393                 if (!isdigit(ptr[i])) {
394                         audit_msg(LOG_ERR, 
395                                 "Value %s should only be numbers - line %d",
396                                 val, line);
397                         return 1;
398                 }
399         }
400
401         /* convert to unsigned long */
402         errno = 0;
403         i = strtoul(val, NULL, 10);
404         if (errno) {
405                 audit_msg(LOG_ERR, 
406                         "Error converting string to a number (%s) - line %d",
407                         strerror(errno), line);
408                 return 1;
409         }
410         if (i > 99) {
411                 audit_msg(LOG_ERR, "num_logs must be 99 or less");
412                 return 1;
413         }
414         config->num_logs = i;
415         return 0;
416 }
417
418 static int log_format_parser(const char *val, int line, 
419                 struct daemon_conf *config)
420 {
421         int i;
422
423         for (i=0; log_formats[i].name != NULL; i++) {
424                 if (strcasecmp(val, log_formats[i].name) == 0) {
425                         config->log_format = log_formats[i].option;
426                         return 0;
427                 }
428         }
429         audit_msg(LOG_ERR, "Option %s not found - line %d", val, line);
430         return 1;
431 }
432
433 void free_config(struct daemon_conf *config)
434 {
435         free((void*)config->sender_ctx);
436         free((void*)config->log_file);
437         free((void*)config->dispatcher);
438         free((void *)config->node_name);
439         free((void *)config->action_mail_acct);
440         free((void *)config->space_left_exe);
441         free((void *)config->admin_space_left_exe);
442         free((void *)config->disk_full_exe);
443         free((void *)config->disk_error_exe);
444 }
445