/* @(#) $Id: validate_op.c,v 1.19 2006/11/18 23:31:22 dcid Exp $ */

/* Copyright (C) 2004-2006 Daniel B. Cid <dcid@ossec.net>
 * All rights reserved.
 *
 * This program is a free software; you can redistribute it
 * and/or modify it under the terms of the GNU General Public
 * License (version 2) as published by the FSF - Free Software
 * Foundation
 */

/* Part of the OSSEC.
 * Available at http://www.ossec.net
 */



#include "shared.h"
char *ip_address_regex = 
     "^[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}/?" 
     "([0-9]{0,2}|[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3})$";

/* Global vars */
int _mask_inited = 0;
int _netmasks[33];


/* Read the file and return a string the matches the following
 * format: high_name.low_name.
 * If return is not null, value must be free.
 */
static char *_read_file(char *high_name, char *low_name)
{
    FILE *fp;
    char def_file[OS_FLSIZE +1];
    char buf[OS_SIZE_1024 +1];
    char *buf_pt;
    char *tmp_buffer;
    char *ret;
    
    #ifndef WIN32
    if(isChroot())
    {
        snprintf(def_file,OS_FLSIZE,"%s", OSSEC_DEFINES);
    }
    else
    {
        snprintf(def_file,OS_FLSIZE,"%s%s",DEFAULTDIR, OSSEC_DEFINES);
    }
    #else
    snprintf(def_file,OS_FLSIZE,"%s", OSSEC_DEFINES);
    #endif

                                                        
    fp = fopen(def_file, "r");
    if(!fp)
    {
        merror(FOPEN_ERROR, __local_name, def_file);
        return(NULL);
    }

    /* Invalid call */
    if(!high_name || !low_name)
    {
        merror(NULL_ERROR, __local_name);
        fclose(fp);
        return(NULL);
    }

    /* Reading it */
    buf[OS_SIZE_1024] = '\0';
    while(fgets(buf, OS_SIZE_1024 , fp) != NULL)
    {
        /* Commented or blank lines */
        if(buf[0] == '#' || buf[0] == ' ' || buf[0] == '\n')
        {
            continue;
        }

        /* Messages not formatted correctly */
        buf_pt = strchr(buf, '.');
        if(!buf_pt)
        {
            merror(FGETS_ERROR, __local_name, def_file, buf);
            continue;
        }

        /* Checking for the high name */
        *buf_pt = '\0'; buf_pt++;
        if(strcmp(buf, high_name) != 0)
        {
            continue;
        }

        tmp_buffer = buf_pt;
        
        /* Getting the equal */
        buf_pt = strchr(buf_pt, '=');
        if(!buf_pt)
        {
            merror(FGETS_ERROR, __local_name, def_file, buf);
            continue;
        }

        /* Checking for the low name */
        *buf_pt = '\0'; buf_pt++;
        if(strcmp(tmp_buffer, low_name) != 0)
        {
            continue;
        }

        /* Removing new lines or anything that we cause errors */
        tmp_buffer = strrchr(buf_pt, '\n');
        if(tmp_buffer)
        {
            *tmp_buffer = '\0';
        }

        tmp_buffer = strrchr(buf_pt, '\r');
        if(tmp_buffer)
        {
            *tmp_buffer = '\0';
        }
        
        os_strdup(buf_pt, ret);
        fclose(fp);
        return(ret);
    }
    
    fclose(fp);
    return(NULL);
}

/* Initialize netmasks -- took from snort util.c */
void _init_masks()
{
    _mask_inited = 1;
    _netmasks[0] = 0x0;
    _netmasks[1] = 0x80000000;
    _netmasks[2] = 0xC0000000;
    _netmasks[3] = 0xE0000000;
    _netmasks[4] = 0xF0000000;
    _netmasks[5] = 0xF8000000;
    _netmasks[6] = 0xFC000000;
    _netmasks[7] = 0xFE000000;
    _netmasks[8] = 0xFF000000;
    _netmasks[9] = 0xFF800000;
    _netmasks[10] = 0xFFC00000;
    _netmasks[11] = 0xFFE00000;
    _netmasks[12] = 0xFFF00000;
    _netmasks[13] = 0xFFF80000;
    _netmasks[14] = 0xFFFC0000;
    _netmasks[15] = 0xFFFE0000;
    _netmasks[16] = 0xFFFF0000;
    _netmasks[17] = 0xFFFF8000;
    _netmasks[18] = 0xFFFFC000;
    _netmasks[19] = 0xFFFFE000;
    _netmasks[20] = 0xFFFFF000;
    _netmasks[21] = 0xFFFFF800;
    _netmasks[22] = 0xFFFFFC00;
    _netmasks[23] = 0xFFFFFE00;
    _netmasks[24] = 0xFFFFFF00;
    _netmasks[25] = 0xFFFFFF80;
    _netmasks[26] = 0xFFFFFFC0;
    _netmasks[27] = 0xFFFFFFE0;
    _netmasks[28] = 0xFFFFFFF0;
    _netmasks[29] = 0xFFFFFFF8;
    _netmasks[30] = 0xFFFFFFFC;
    _netmasks[31] = 0xFFFFFFFE;
    _netmasks[32] = 0xFFFFFFFF;
}


/** getDefine_Int.
 * Gets an integer definition. This function always return on
 * success or exit on error.
 */
int getDefine_Int(char *high_name, char *low_name, int min, int max)
{
    int ret;
    char *value;
    char *pt;

    value = _read_file(high_name, low_name);
    if(!value)
    {
        ErrorExit(DEF_NOT_FOUND, __local_name, high_name, low_name);
    }

    pt = value;
    while(*pt != '\0')
    {
        if(!isdigit((int)*pt))
        {
            ErrorExit(INV_DEF, __local_name, high_name, low_name, value);
        }
        pt++;
    }

    ret = atoi(value);
    if((ret < min) || (ret > max))
    {
        ErrorExit(INV_DEF, __local_name, high_name, low_name, value);
    }

    /* Clearing memory */
    free(value);

    return(ret);
}


/** int OS_IPFound(char *ip_address, os_ip *that_ip)
 * Checks if ip_address is present at that_ip.
 * Returns 1 on success or 0 on failure.
 */
int OS_IPFound(char *ip_address, os_ip *that_ip)
{
    int _true = 1;
    struct in_addr net;

    /* Extracting ip address */
    if((net.s_addr = inet_addr(ip_address)) <= 0)
    {
        return(!_true);
    }
                                
    /* If negate is set */
    if(that_ip->ip[0] == '!')
    {
        _true = 0;
    }
    
    /* Checking if ip is in thatip & netmask */
    if((net.s_addr & that_ip->netmask) == that_ip->ip_address)
    {
        return(_true);
    }

    /* Didn't match */
    return(!_true);
}

     
/** int OS_IPFoundList(char *ip_address, os_ip **list_of_ips)
 * Checks if ip_address is present on the "list_of_ips".
 * Returns 1 on success or 0 on failure.
 * The list MUST be NULL terminated
 */
int OS_IPFoundList(char *ip_address, os_ip **list_of_ips)
{
    struct in_addr net;
    int _true = 1;

    /* Extracting ip address */
    if((net.s_addr = inet_addr(ip_address)) <= 0)
    {
        return(!_true);
    }
                                        
    while(*list_of_ips)
    {
        os_ip *l_ip = *list_of_ips;
        
        if(l_ip->ip[0] == '!')
        {
            _true = 0;
        }
    
        if((net.s_addr & l_ip->netmask) == l_ip->ip_address)
        {
            return(_true);
        }
        list_of_ips++;
    }

    return(!_true);
}    

     
/** int OS_IsValidIP(char *ip)
 * Validates if an ip address is in the right
 * format.
 * Returns 0 if doesn't match or 1 if it is an ip or 2 an ip with cidr.
 * ** On success this function may modify the value of ip_address
 */
int OS_IsValidIP(char *ip_address, os_ip *final_ip)
{
    unsigned int nmask = 0;
    char *tmp_str;

    /* Can't be null */
    if(!ip_address)
    {
        return(0);
    }

    /* Assigning the ip address */
    if(final_ip)
    {
        os_strdup(ip_address, final_ip->ip);
    }
    
    if(*ip_address == '!')
    {
        ip_address++;
    }
   
    #ifndef WIN32 
    /* checking against the basic regex */
    if(!OS_PRegex(ip_address, ip_address_regex))
    {
        return(0);
    }
    #else
    {
        char *tmp_ip;
        int dots = 0;
        tmp_ip = ip_address;
        while(*tmp_ip != '\0')
        {
            if(*tmp_ip < '0'  && 
               *tmp_ip > '9'  && 
               *tmp_ip != '.' &&
               *tmp_ip != '/')
            {
                /* Invalid ip */
                return(0);
            }
            if(*tmp_ip == '.')
                dots++;
            tmp_ip++;
        }
        if(dots < 3 || dots > 6)
            return(0);
    }
    #endif

    /* Getting the cidr/netmask if available */ 
    tmp_str = strchr(ip_address,'/');
    if(tmp_str)
    {
        int cidr;
        struct in_addr net;
        
        *tmp_str = '\0';
        tmp_str++;

        /* Cidr */
        if(strlen(tmp_str) <= 2)
        {
            cidr = atoi(tmp_str);
            if((cidr >= 1) && (cidr <= 32))
            {
                if(!_mask_inited)
                    _init_masks();
                nmask = _netmasks[cidr];
                nmask = htonl(nmask);
            }
            else
            {
                return(0);
            }
        }
        /* Full netmask */
        else
        {
            /* Init the masks */
            if(!_mask_inited)
                _init_masks();

            if(strcmp(tmp_str, "255.255.255.255") == 0)
            {
                nmask = htonl(_netmasks[32]);
            }
            else
            {
                if((nmask = inet_addr(ip_address)) <= 0)
                {
                    return(0);
                }
            }
        }
        
        if((net.s_addr = inet_addr(ip_address)) <= 0)
        {
            return(0);
        }

        if(final_ip)
        {
            final_ip->ip_address = net.s_addr & nmask;
            final_ip->netmask = nmask;
        }

        tmp_str--;
        *tmp_str = '/';

        return(2);
    }

    /* No cidr available */
    else
    {
        struct in_addr net;
        if((net.s_addr = inet_addr(ip_address)) <= 0)
        {
            return(0);
        }
        
        if(final_ip)
        {
            final_ip->ip_address = net.s_addr;

            if(!_mask_inited)
                _init_masks();
        
            final_ip->netmask = htonl(_netmasks[32]);
        }

        /* Ip without cidr */
        return(1);
    }

    /* Should never reach here */
    return(0);
}


/** int OS_IsonTime(char *time_str, char *ossec_time)
 * Must be a valid string, called after OS_IsValidTime.
 * Returns 1 on success or 0 on failure.
 */
int OS_IsonTime(char *time_str, char *ossec_time)
{
    int _true = 1;

    if(*ossec_time == '!')
    {
        _true = 0;
    }
    ossec_time++;

    /* Comparing against min/max value */
    if((strncmp(time_str, ossec_time, 5) >= 0)&&
      (strncmp(time_str, ossec_time+5,5) <= 0))  
    {
        return(_true);
    }
    
    return(!_true);
}


/** char *OS_IsValidTime(char *time_str)
 * Validates if a time is in an acceptable format
 * for ossec.
 * Returns 0 if doesn't match or a valid string for
 * ossec usage in success.
 * ** On success this function may modify the value of date
 * Acceptable formats:
 * hh:mm - hh:mm (24 hour format)
 * !hh:mm -hh:mm (24 hour format)
 * hh - hh (24 hour format)
 * hh:mm am - hh:mm pm (12 hour format)
 * hh am - hh pm (12 hour format)
 */
#define RM_WHITE(x)while(*x == ' ')x++;
char *__gethour(char *str, char *ossec_hour)
{
    int _size = 0;
    int chour = 0;
    int cmin = 0;
    
    /* Invalid time format */
    if(!isdigit((int)*str))
    {
        merror(INVALID_TIME, __local_name, str);
    }
    

    /* Hour */
    chour = atoi(str);


    /* Getting a valid hour */
    if(chour < 0 || chour >= 24)
    {
        merror(INVALID_TIME, __local_name, str);
        return(NULL);

    }
    
    /* Going after the hour */
    while(isdigit((int)*str))
    {
        _size++;
        str++;
    }

    /* Invalid hour */
    if(_size > 2)
    {
        merror(INVALID_TIME, __local_name, str);
        return(NULL);
    }
    
    
    /* Getting minute */
    if(*str == ':')
    {
        str++;
        if((!isdigit((int)*str)||
            !isdigit((int)*(str +1))) && isdigit((int)*(str +2)))
        {
            merror(INVALID_TIME, __local_name, str);
            return(NULL);
        }

        cmin = atoi(str);
        str+=2;
    }

    /* Removing spaces */
    RM_WHITE(str);
    
    if((*str == 'a') || (*str == 'A'))
    {
        str++;
        if((*str == 'm') || (*str == 'M'))
        {
            snprintf(ossec_hour, 6, "%02d:%02d", chour, cmin);
            str++;
            return(str);
        }
    }
    else if((*str == 'p') || (*str == 'P'))
    {
        str++;
        if((*str == 'm') || (*str == 'M'))
        {
            chour += 12;
            
            /* New hour must be valid */
            if(chour < 0 || chour >= 24)
            {
                merror(INVALID_TIME, __local_name, str);
                return(NULL);
            }
                                                
            snprintf(ossec_hour, 6, "%02d:%02d", chour, cmin);
            str++;
            return(str);
        }
        
    }
    else
    {
        snprintf(ossec_hour, 6, "%02d:%02d", chour, cmin);
        return(str);
    }

    /* Here is error */
    merror(INVALID_TIME, __local_name, str);
    return(NULL);
}


char *OS_IsValidTime(char *time_str)
{
    char *ret;
    char first_hour[7];
    char second_hour[7];
    int ng = 0;
    
    /* Must be not null */
    if(!time_str)
        return(NULL);
    
        
    /* Clearing memory */
    memset(first_hour, '\0', 7);
    memset(second_hour, '\0', 7);
    
    
    /* Removing white spaces */
    RM_WHITE(time_str);


    /* Checking for negative */
    if(*time_str == '!')
    {
        ng = 1;
        time_str++;

        /* We may have white spaces after the '!' */
        RM_WHITE(time_str);
    }

    
    /* Getting first hour */
    time_str = __gethour(time_str, first_hour);
    if(!time_str)
        return(NULL);

    /* Removing white spaces */
    RM_WHITE(time_str);
    
    if(*time_str != '-')
    {
        return(NULL);
    }

    time_str++;

    /* Removing white spaces */
    RM_WHITE(time_str);

    /* Getting second hour */
    time_str = __gethour(time_str, second_hour);
    if(!time_str)
        return(NULL);
    
    RM_WHITE(time_str);
    if(*time_str != '\0')
    {
        return(NULL);
    }

    os_calloc(13, sizeof(char), ret);
    
    /* Fixing dump hours */
    if(strcmp(first_hour,second_hour) > 0)
    {
        snprintf(ret, 12, "!%s%s", second_hour, first_hour);
        return(ret);
    }
    
    /* For the normal times */
    snprintf(ret, 12, "%c%s%s", ng == 0?'.':'!', first_hour, second_hour);
    return(ret);
}



/** int OS_IsonDay(int week_day, char *ossec_day)
 * Checks if the specified week day is in the
 * range.
 */
int OS_IsonDay(int week_day, char *ossec_day)
{
    int _true = 1;

    /* Negative */
    if(ossec_day[7] == '!')
        _true = 0;
    
    if(week_day < 0 || week_day > 7)
    {
        return(0);
    }

    /* It is on the right day */
    if(ossec_day[week_day] == 1)
        return(_true);
    
    return(!_true);    
}



/** char *OS_IsValidDay(char *day_str)
 * Validates if an day is in an acceptable format
 * for ossec.
 * Returns 0 if doesn't match or a valid string for
 * ossec usage in success.
 * ** On success this function may modify the value of date
 * Acceptable formats:
 * weekdays, weekends, monday, tuesday, thursday,..
 * monday,tuesday
 * mon,tue wed
 */
#define RM_SEP(x)while((*x == ' ') || (*x == ','))x++;
#define IS_SEP(x) (*x == ' ' || *x == ',')
char *OS_IsValidDay(char *day_str)
{
    int i = 0, ng = 0;
    char *ret;
    char day_ret[9] = {0,0,0,0,0,0,0,0,0};
    char *(days[]) = 
    {
        "sunday", "sun", "monday", "mon", "tuesday", "tue",
        "wednesday", "wed", "thursday", "thu", "friday",
        "fri", "saturday", "sat", "weekdays", "weekends", NULL
    };
    int days_int[] = {0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,8};

    /* Must be a valid string */
    if(!day_str)
        return(NULL);
    
    
    RM_WHITE(day_str);
    
    /* checking for negatives */
    if(*day_str == '!')
    {
        ng = 1;
        RM_WHITE(day_str);
    }

    while(*day_str != '\0')
    {
        i = 0;
        while(days[i])
        {
            if(strncasecmp(day_str, days[i], strlen(days[i])) == 0)
            {
                /* Weekdays */
                if(days_int[i] == 7)
                {
                    day_ret[1] = 1;
                    day_ret[2] = 1;
                    day_ret[3] = 1;
                    day_ret[4] = 1;
                    day_ret[5] = 1;
                }
                /* weekends */
                else if(days_int[i] == 8)
                {
                    day_ret[0] = 1;
                    day_ret[6] = 1;
                }
                else
                {
                    day_ret[days_int[i]] = 1;
                }
                break;
            }
            i++;
        }

        if(!days[i])
        {
            merror(INVALID_DAY, __local_name, day_str);
            return(NULL);
        }
        
        day_str += strlen(days[i]);

        if(IS_SEP(day_str))
        {
            RM_SEP(day_str);
            continue;
        }
        else if(*day_str == '\0')
            break;
        else
        {
            merror(INVALID_DAY, __local_name, day_str);
            return(NULL);
        }
    }

    /* Assigning values */
    os_calloc(9, sizeof(char), ret);
    if(ng == 1)
    {
        /* Setting nevative */
        ret[7] = '!';
    }

    ng = 0;
    for(i = 0;i<=6;i++)
    {
        /* Checking if some is checked */
        if(day_ret[i] == 1)
            ng = 1;
        ret[i] = day_ret[i];
    }

    /* At least one day must be checked */
    if(ng == 0)
    {
        free(ret);
        merror(INVALID_DAY, __local_name, day_str);
        return(NULL);
    }
    
    return(ret);
}

/* EOF */
