new upstream release (3.3.0); modify package compatibility for Stretch
[ossec-hids.git] / src / shared / validate_op.c
1 /* Copyright (C) 2009 Trend Micro Inc.
2  * All rights reserved.
3  *
4  * This program is a free software; you can redistribute it
5  * and/or modify it under the terms of the GNU General Public
6  * License (version 2) as published by the FSF - Free Software
7  * Foundation
8  */
9
10 #include "shared.h"
11
12 static char *_read_file(const char *high_name, const char *low_name, const char *defines_file) __attribute__((nonnull(3)));
13 static const char *__gethour(const char *str, char *ossec_hour) __attribute__((nonnull));
14
15
16 /* Read the file and return a string the matches the following
17  * format: high_name.low_name.
18  * If return is not null, value must be freed
19  */
20 static char *_read_file(const char *high_name, const char *low_name, const char *defines_file)
21 {
22     FILE *fp;
23     char def_file[OS_FLSIZE + 1];
24     char buf[OS_SIZE_1024 + 1];
25     char *buf_pt;
26     char *tmp_buffer;
27     char *ret;
28
29 #ifndef WIN32
30     if (isChroot()) {
31         snprintf(def_file, OS_FLSIZE, "%s", defines_file);
32     } else {
33         snprintf(def_file, OS_FLSIZE, "%s%s", DEFAULTDIR, defines_file);
34     }
35 #else
36     snprintf(def_file, OS_FLSIZE, "%s", defines_file);
37 #endif
38
39     fp = fopen(def_file, "r");
40     if (!fp) {
41         if (strcmp(defines_file, OSSEC_LDEFINES) != 0) {
42             merror(FOPEN_ERROR, __local_name, def_file, errno, strerror(errno));
43         }
44         return (NULL);
45     }
46
47     /* Invalid call */
48     if (!high_name || !low_name) {
49         merror(NULL_ERROR, __local_name);
50         fclose(fp);
51         return (NULL);
52     }
53
54     /* Read it */
55     buf[OS_SIZE_1024] = '\0';
56     while (fgets(buf, OS_SIZE_1024 , fp) != NULL) {
57         /* Commented or blank lines */
58         if (buf[0] == '#' || buf[0] == ' ' || buf[0] == '\n') {
59             continue;
60         }
61
62         /* Messages not formatted correctly */
63         buf_pt = strchr(buf, '.');
64         if (!buf_pt) {
65             merror(FGETS_ERROR, __local_name, def_file, buf);
66             continue;
67         }
68
69         /* Check for the high name */
70         *buf_pt = '\0';
71         buf_pt++;
72         if (strcmp(buf, high_name) != 0) {
73             continue;
74         }
75
76         tmp_buffer = buf_pt;
77
78         /* Get the equal */
79         buf_pt = strchr(buf_pt, '=');
80         if (!buf_pt) {
81             merror(FGETS_ERROR, __local_name, def_file, buf);
82             continue;
83         }
84
85         /* Check for the low name */
86         *buf_pt = '\0';
87         buf_pt++;
88         if (strcmp(tmp_buffer, low_name) != 0) {
89             continue;
90         }
91
92         /* Remove newlines or anything that will cause errors */
93         tmp_buffer = strrchr(buf_pt, '\n');
94         if (tmp_buffer) {
95             *tmp_buffer = '\0';
96         }
97         tmp_buffer = strrchr(buf_pt, '\r');
98         if (tmp_buffer) {
99             *tmp_buffer = '\0';
100         }
101
102         os_strdup(buf_pt, ret);
103         fclose(fp);
104         return (ret);
105     }
106
107     fclose(fp);
108     return (NULL);
109 }
110
111 /* Get an integer definition. This function always return on
112  * success or exits on error.
113  */
114 int getDefine_Int(const char *high_name, const char *low_name, int min, int max)
115 {
116     int ret;
117     char *value;
118     char *pt;
119
120     /* Try to read from the local define file */
121     value = _read_file(high_name, low_name, OSSEC_LDEFINES);
122     if (!value) {
123         value = _read_file(high_name, low_name, OSSEC_DEFINES);
124         if (!value) {
125             ErrorExit(DEF_NOT_FOUND, __local_name, high_name, low_name);
126         }
127     }
128
129     pt = value;
130     while (*pt != '\0') {
131         if (!isdigit((int)*pt)) {
132             ErrorExit(INV_DEF, __local_name, high_name, low_name, value);
133         }
134         pt++;
135     }
136
137     ret = atoi(value);
138     if ((ret < min) || (ret > max)) {
139         ErrorExit(INV_DEF, __local_name, high_name, low_name, value);
140     }
141
142     /* Clear memory */
143     free(value);
144
145     return (ret);
146 }
147
148 /* Check if IP_address is present at that_IP
149  * Returns 1 on success or 0 on failure
150  */
151 int OS_IPFound(const char *ip_address, const os_ip *that_ip)
152 {
153     int _true = 1;
154     os_ip temp_ip;
155
156     /* If negate is set */
157     char *ip = that_ip->ip;
158     if (ip[0] == '!') {
159         ip++;
160         _true = 0;
161     }
162
163     /* The simplest case is the 'any' case.
164      *  We just return true
165      */
166     if( strcmp(ip, "any") == 0 ) {
167         return _true;
168     }
169
170     memset(&temp_ip, 0, sizeof(struct _os_ip));
171
172     /* Extract IP address */
173     if (OS_IsValidIP(ip_address, &temp_ip) == 0) {
174         return (!_true);
175     }
176
177
178     /* Check if IP is in thatip & netmask */
179     if (sacmp((struct sockaddr *) &temp_ip.ss,
180               (struct sockaddr *) &that_ip->ss,
181               that_ip->prefixlength)) {
182         return (_true);
183     }
184
185     /* Didn't match */
186     return (!_true);
187 }
188
189 /* Check if IP_address is present in the "list_of_ips".
190  * Returns 1 on success or 0 on failure
191  * The list MUST be NULL terminated
192  */
193 int OS_IPFoundList(const char *ip_address, os_ip **list_of_ips)
194 {
195     int _true = 1;
196     os_ip temp_ip;
197
198     memset(&temp_ip, 0, sizeof(struct _os_ip));
199
200     /* Extract IP address */
201     if (OS_IsValidIP(ip_address, &temp_ip) == 0) {
202         return (!_true);
203     }
204
205     while (*list_of_ips) {
206         os_ip *l_ip = *list_of_ips;
207
208         char *ip = l_ip->ip;
209         if (ip[0] == '!') {
210             ip++;
211             _true = 0;
212         }
213         else {
214             _true = 1;
215         }
216
217         /* Simplest case, if the list contains an any
218          * ip, we return true.
219          */
220         if( strcmp(ip,"any" ) == 0 ) {
221             return _true;
222         }
223
224         /* Checking if ip is in thatip & netmask */
225         if (sacmp((struct sockaddr *) &temp_ip.ss,
226                   (struct sockaddr *) &l_ip->ss,
227                   l_ip->prefixlength)) {
228             return (_true);
229         }
230         list_of_ips++;
231     }
232
233     return (!_true);
234 }
235
236 /** int OS_IsValidIP(char *ip_address, os_ip *final_ip)
237  * Validate if an IP address is in the right format
238  * Returns 0 if doesn't match or 1 if it is an IP or 2 an IP with CIDR.
239  * WARNING: On success this function may modify the value of ip_address
240  */
241 int OS_IsValidIP(const char *in_address, os_ip *final_ip)
242 {
243     char *tmp_str;
244     int cidr = -1, prefixlength;
245     struct addrinfo hints, *result;
246     char *ip_address = NULL;
247
248     /* Can't be null */
249     if (!in_address) {
250         return (0);
251     }
252
253     /* We mess with in_address later and we set 'const'
254      *  in the signature, so we need to copy it
255      *  here.
256      */
257     os_strdup(in_address, ip_address);
258
259     if (final_ip) {
260         os_strdup(ip_address, final_ip->ip);
261     }
262
263     if (*ip_address == '!') {
264         //ip_address++;
265         os_strdup(in_address+1, ip_address);
266     }
267
268     /* Use IPv6 here, because it doesn't matter
269      * OS_IPFound and OS_IPFoundList will
270      * return true if the os_ip.ip element is 'any'
271      */
272     if(strcmp(ip_address, "any") == 0) {
273         //strcpy(ip_address, "::/0");
274         free(ip_address);   // Free the old value before writing the new one?
275         os_strdup("::/0", ip_address);
276     }
277
278     /* Getting the cidr/netmask if available */
279     tmp_str = strchr(ip_address,'/');
280     if(tmp_str) {
281         *tmp_str = '\0';
282         tmp_str++;
283
284         /* Cidr */
285         if(strlen(tmp_str) <= 3) {
286             cidr = atoi(tmp_str);
287         } else {
288             free(ip_address);
289             return(0);
290         }
291     }
292
293     /* No cidr available */
294     memset(&hints, 0, sizeof(struct addrinfo));
295     hints.ai_flags = AI_NUMERICHOST;
296     if (getaddrinfo(ip_address, NULL, &hints, &result) != 0) {
297         free(ip_address);
298         return(0);
299     }
300
301     switch (result->ai_family)
302     {
303     case AF_INET:
304         if (cidr >=0 && cidr <= 32) {
305             prefixlength = cidr;
306             break;
307         } else if (cidr < 0) {
308             prefixlength = 32;
309             break;
310         }
311         free(ip_address);
312         free(result);
313         return(0);
314     case AF_INET6:
315         if (cidr >=0 && cidr <= 128) {
316             prefixlength = cidr;
317             break;
318         } else if (cidr < 0) {
319             prefixlength = 128;
320             break;
321         }
322         free(ip_address);
323         free(result);
324         return(0);
325     default:
326         free(ip_address);
327         free(result);
328         return(0);
329     }
330
331     if (final_ip) {
332         memcpy(&(final_ip->ss), result->ai_addr, result->ai_addrlen);
333         final_ip->prefixlength = prefixlength;
334     }
335
336     freeaddrinfo(result);
337
338     free(ip_address);
339     return((cidr >= 0) ? 2 : 1);
340 }
341
342 /** int sacmp(struct sockaddr *sa1, struct sockaddr *sa2, int prefixlength)
343  * Compares two sockaddrs up to prefixlength.
344  * Returns 0 if doesn't match or 1 if they do.
345  */
346 int sacmp(struct sockaddr *sa1, struct sockaddr *sa2, int prefixlength)
347 {
348     int _true = 1;
349     int i, realaf1, realaf2;
350     div_t ip_div;
351     char *addr1, *addr2, modbits;
352
353     // If we have no prefixlength just return a match
354     //   * This handles the "any" case
355     if (!prefixlength) {
356         return _true;
357     }
358
359     switch (sa1->sa_family)
360     {
361     case AF_INET:
362         addr1 = (char *) &(((struct sockaddr_in *) sa1)->sin_addr);
363         realaf1 = AF_INET;
364         break;
365     case AF_INET6:
366         addr1 = (char *) &(((struct sockaddr_in6 *) sa1)->sin6_addr);
367         realaf1 = AF_INET6;
368         if (IN6_IS_ADDR_V4MAPPED((struct in6_addr *) addr1))
369         {   /* shift the pointer for a mapped address */
370             addr1 += (sizeof (struct in6_addr)) - (sizeof (struct in_addr));
371             realaf1 = AF_INET;
372         }
373         break;
374     default:
375         return(!_true);
376     }
377
378     switch (sa2->sa_family)
379     {
380     case AF_INET:
381         addr2 = (char *) &(((struct sockaddr_in *) sa2)->sin_addr);
382         realaf2 = AF_INET;
383         break;
384     case AF_INET6:
385         addr2 = (char *) &(((struct sockaddr_in6 *) sa2)->sin6_addr);
386         realaf2 = AF_INET6;
387         if (IN6_IS_ADDR_V4MAPPED((struct in6_addr *) addr2)) {
388             /* shift the pointer for a mapped address */
389             addr2 += (sizeof (struct in6_addr)) - (sizeof (struct in_addr));
390             realaf2 = AF_INET;
391         }
392         break;
393     default:
394         return(!_true);
395     }
396
397     if (realaf1 != realaf2) {
398         return(!_true);
399     }
400
401     ip_div = div(prefixlength, 8);
402
403     for (i=0; i < ip_div.quot; i++) {
404         if (addr1[i] != addr2[i]) {
405             return(!_true);
406         }
407     }
408     if (ip_div.rem) {
409         modbits = ((unsigned char) ~0) << (8 - ip_div.rem);
410         if ( (addr1[i] & modbits) != (addr2[i] & modbits) ) {
411             return(!_true);
412         }
413     }
414     return(_true);
415 }
416
417
418 /* Must be a valid string, called after OS_IsValidTime
419  * Returns 1 on success or 0 on failure
420  */
421 int OS_IsonTime(const char *time_str, const char *ossec_time)
422 {
423     int _true = 1;
424
425     if (*ossec_time == '!') {
426         _true = 0;
427     }
428     ossec_time++;
429
430     /* Comparing against min/max value */
431     if ((strncmp(time_str, ossec_time, 5) >= 0) &&
432             (strncmp(time_str, ossec_time + 5, 5) <= 0)) {
433         return (_true);
434     }
435
436     return (!_true);
437 }
438
439 /* Validate if a time is in an acceptable format for OSSEC.
440  * Returns 0 if doesn't match or a valid string for OSSEC usage in success.
441  * ** On success this function may modify the value of date
442  * Acceptable formats:
443  *      hh:mm - hh:mm (24 hour format)
444  *      !hh:mm -hh:mm (24 hour format)
445  *      hh - hh (24 hour format)
446  *      hh:mm am - hh:mm pm (12 hour format)
447  *      hh am - hh pm (12 hour format)
448  */
449 #define RM_WHITE(x)while(*x == ' ')x++;
450
451 static const char *__gethour(const char *str, char *ossec_hour)
452 {
453     int _size = 0;
454     int chour = 0;
455     int cmin = 0;
456
457     /* Invalid time format */
458     if (!isdigit((int)*str)) {
459         merror(INVALID_TIME, __local_name, str);
460     }
461
462     /* Hour */
463     chour = atoi(str);
464
465     /* Get a valid hour */
466     if (chour < 0 || chour >= 24) {
467         merror(INVALID_TIME, __local_name, str);
468         return (NULL);
469     }
470
471     /* Go after the hour */
472     while (isdigit((int)*str)) {
473         _size++;
474         str++;
475     }
476
477     /* Invalid hour */
478     if (_size > 2) {
479         merror(INVALID_TIME, __local_name, str);
480         return (NULL);
481     }
482
483     /* Get minute */
484     if (*str == ':') {
485         str++;
486         if ((!isdigit((int)*str) ||
487                 !isdigit((int) * (str + 1))) && isdigit((int) * (str + 2))) {
488             merror(INVALID_TIME, __local_name, str);
489             return (NULL);
490         }
491
492         cmin = atoi(str);
493         str += 2;
494     }
495
496     /* Remove spaces */
497     RM_WHITE(str);
498
499     if ((*str == 'a') || (*str == 'A')) {
500         str++;
501         if ((*str == 'm') || (*str == 'M')) {
502             snprintf(ossec_hour, 6, "%02d:%02d", chour, cmin);
503             str++;
504             return (str);
505         }
506     } else if ((*str == 'p') || (*str == 'P')) {
507         str++;
508         if ((*str == 'm') || (*str == 'M')) {
509             if(chour != 12) {
510                 chour += 12;
511             }
512
513             /* New hour must be valid */
514             if (chour < 0 || chour >= 24) {
515                 merror(INVALID_TIME, __local_name, str);
516                 return (NULL);
517             }
518
519             snprintf(ossec_hour, 6, "%02d:%02d", chour, cmin);
520             str++;
521             return (str);
522         }
523
524     } else {
525         snprintf(ossec_hour, 6, "%02d:%02d", chour, cmin);
526         return (str);
527     }
528
529     /* Here is error */
530     merror(INVALID_TIME, __local_name, str);
531     return (NULL);
532 }
533
534 char *OS_IsValidTime(const char *time_str)
535 {
536     char *ret;
537     char first_hour[7];
538     char second_hour[7];
539     int ng = 0;
540
541     /* Must be not null */
542     if (!time_str) {
543         return (NULL);
544     }
545
546     /* Clear memory */
547     memset(first_hour, '\0', 7);
548     memset(second_hour, '\0', 7);
549
550     /* Remove spaces */
551     RM_WHITE(time_str);
552
553     /* Check for negative */
554     if (*time_str == '!') {
555         ng = 1;
556         time_str++;
557
558         /* We may have spaces after the '!' */
559         RM_WHITE(time_str);
560     }
561
562     /* Get first hour */
563     time_str = __gethour(time_str, first_hour);
564     if (!time_str) {
565         return (NULL);
566     }
567
568     /* Remove spaces */
569     RM_WHITE(time_str);
570
571     if (*time_str != '-') {
572         return (NULL);
573     }
574
575     time_str++;
576
577     /* Remove spaces */
578     RM_WHITE(time_str);
579
580     /* Get second hour */
581     time_str = __gethour(time_str, second_hour);
582     if (!time_str) {
583         return (NULL);
584     }
585
586     RM_WHITE(time_str);
587     if (*time_str != '\0') {
588         return (NULL);
589     }
590
591     os_calloc(13, sizeof(char), ret);
592
593     /* Fix dump hours */
594     if (strcmp(first_hour, second_hour) > 0) {
595         snprintf(ret, 12, "!%s%s", second_hour, first_hour);
596         return (ret);
597     }
598
599     /* For the normal times */
600     snprintf(ret, 12, "%c%s%s", ng == 0 ? '.' : '!', first_hour, second_hour);
601     return (ret);
602 }
603
604 /* Check if the current time is the same or has passed the specified one */
605 int OS_IsAfterTime(const char *time_str, const char *ossec_time)
606 {
607     /* Unique times can't have a ! */
608     if (*ossec_time == '!') {
609         return (0);
610     }
611
612     ossec_time++;
613
614     /* Compare against min/max value */
615     if (strncmp(time_str, ossec_time, 5) >= 0) {
616         return (1);
617     }
618
619     return (0);
620 }
621
622 /* Create a unique time, not a range. Must be used with OS_IsAfterTime. */
623 char *OS_IsValidUniqueTime(const char *time_str)
624 {
625     char mytime[128 + 1];
626
627     if (*time_str == '!') {
628         return (NULL);
629     }
630
631     memset(mytime, '\0', 128 + 1);
632     snprintf(mytime, 128, "%s-%s", time_str, time_str);
633
634     return (OS_IsValidTime(mytime));
635 }
636
637 /* Check if the specified week day is in the range */
638 int OS_IsonDay(int week_day, const char *ossec_day)
639 {
640     int _true = 1;
641
642     /* Negative */
643     if (ossec_day[7] == '!') {
644         _true = 0;
645     }
646
647     if (week_day < 0 || week_day > 7) {
648         return (0);
649     }
650
651     /* It is on the right day */
652     if (ossec_day[week_day] == 1) {
653         return (_true);
654     }
655
656     return (!_true);
657 }
658
659 /* Validate if a day is in an acceptable format for OSSEC
660  * Returns 0 if doesn't match or a valid string for OSSEC usage in success.
661  * WARNING: On success this function may modify the value of date
662  * Acceptable formats:
663  *  weekdays, weekends, monday, tuesday, thursday,..
664  *  monday,tuesday
665  *  mon,tue wed
666  */
667 #define RM_SEP(x)while((*x == ' ') || (*x == ','))x++;
668
669 #define IS_SEP(x) (*x == ' ' || *x == ',')
670
671 char *OS_IsValidDay(const char *day_str)
672 {
673     int i = 0, ng = 0;
674     char *ret;
675     char day_ret[9] = {0, 0, 0, 0, 0, 0, 0, 0, 0};
676     const char *(days[]) = {
677         "sunday", "sun", "monday", "mon", "tuesday", "tue",
678         "wednesday", "wed", "thursday", "thu", "friday",
679         "fri", "saturday", "sat", "weekdays", "weekends", NULL
680     };
681     int days_int[] = {0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 8};
682
683     /* Must be a valid string */
684     if (!day_str) {
685         return (NULL);
686     }
687
688     RM_WHITE(day_str);
689
690     /* Check for negatives */
691     if (*day_str == '!') {
692         ng = 1;
693         RM_WHITE(day_str);
694     }
695
696     while (*day_str != '\0') {
697         i = 0;
698         while (days[i]) {
699             if (strncasecmp(day_str, days[i], strlen(days[i])) == 0) {
700                 /* Weekdays */
701                 if (days_int[i] == 7) {
702                     day_ret[1] = 1;
703                     day_ret[2] = 1;
704                     day_ret[3] = 1;
705                     day_ret[4] = 1;
706                     day_ret[5] = 1;
707                 }
708                 /* Weekends */
709                 else if (days_int[i] == 8) {
710                     day_ret[0] = 1;
711                     day_ret[6] = 1;
712                 } else {
713                     day_ret[days_int[i]] = 1;
714                 }
715                 break;
716             }
717             i++;
718         }
719
720         if (!days[i]) {
721             merror(INVALID_DAY, __local_name, day_str);
722             return (NULL);
723         }
724
725         day_str += strlen(days[i]);
726
727         if (IS_SEP(day_str)) {
728             RM_SEP(day_str);
729             continue;
730         } else if (*day_str == '\0') {
731             break;
732         } else {
733             merror(INVALID_DAY, __local_name, day_str);
734             return (NULL);
735         }
736     }
737
738     /* Assign values */
739     os_calloc(9, sizeof(char), ret);
740     if (ng == 1) {
741         /* Set negative */
742         ret[7] = '!';
743     }
744
745     ng = 0;
746     for (i = 0; i <= 6; i++) {
747         /* Check if some is checked */
748         if (day_ret[i] == 1) {
749             ng = 1;
750         }
751         ret[i] = day_ret[i];
752     }
753
754     /* At least one day must be checked */
755     if (ng == 0) {
756         free(ret);
757         merror(INVALID_DAY, __local_name, day_str);
758         return (NULL);
759     }
760
761     return (ret);
762 }
763