/*
dbase.c - MessageWall database definitions
Copyright (C) 2002 Ian Gulliver
This program is free software; you can redistribute it and/or modify
it under the terms of version 2 of the GNU General Public License as
published by the Free Software Foundation.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/stat.h>
#include <unistd.h>
#include <dirent.h>
#include <firestring.h>
#include <firedns.h>
#include "messagewall.h"
#include "dnsdcc.h"
#include "virus.h"
#include "dbase.h"
static const char tagstring[] = "$Id: dbase.c,v 1.36.2.5 2002/10/17 19:04:06 ian Exp $";
const char *dbase_cidr_subnet[] = {
"0.0.0.0",
"128.0.0.0",
"192.0.0.0",
"224.0.0.0",
"240.0.0.0",
"248.0.0.0",
"252.0.0.0",
"254.0.0.0",
"255.0.0.0",
"255.128.0.0",
"255.192.0.0",
"255.224.0.0",
"255.240.0.0",
"255.248.0.0",
"255.252.0.0",
"255.254.0.0",
"255.255.0.0",
"255.255.128.0",
"255.255.192.0",
"255.255.224.0",
"255.255.240.0",
"255.255.248.0",
"255.255.252.0",
"255.255.254.0",
"255.255.255.0",
"255.255.255.128",
"255.255.255.192",
"255.255.255.224",
"255.255.255.240",
"255.255.255.248",
"255.255.255.252",
"255.255.255.254",
"255.255.255.255"
};
void dbase_clear() { /* release local_domains, relay_ips and special_users lists from memory */
int i;
struct messagewall_local_domain_t *local_domain1, *local_domain2;
struct messagewall_relay_ip_t *relay_ip1, *relay_ip2;
struct messagewall_special_user_t *special_user1, *special_user2;
for (i = 0; i < HASH_SIZE; i++) {
local_domain1 = local_domain_hash[i];
while (local_domain1 != NULL) {
local_domain2 = local_domain1;
local_domain1 = local_domain1->next;
firestring_estr_free(&local_domain2->domain);
free(local_domain2);
}
local_domain_hash[i] = NULL;
relay_ip1 = relay_ip_hash[i];
while (relay_ip1 != NULL) {
relay_ip2 = relay_ip1;
relay_ip1 = relay_ip1->next;
free(relay_ip2);
}
relay_ip_hash[i] = NULL;
special_user1 = special_user_hash[i];
while (special_user1 != NULL) {
special_user2 = special_user1;
special_user1 = special_user1->next;
firestring_estr_free(&special_user2->address);
free(special_user2);
}
special_user_hash[i] = NULL;
}
}
void dbase_load_local_domains() { /* populate local_domains list + hash table from local_domains config file */
struct messagewall_local_domain_t *local_domain1;
char line[257];
unsigned int u;
FILE *f;
if (local_domains == NULL)
return;
/*
* we're inside the chroot; everything should be root controlled and fopen shouldn't be a problem
* SECURITY: do not let users play with files inside the chroot
*/
f = fopen(local_domains,"r"); /* ITS4: ignore fopen */
if (f == NULL) {
perror("Unable to open local_domains file");
exit(100);
}
/*
* line really does have 257 bytes of space
*/
while (fgets(line,257,f) != NULL) { /* ITS4: ignore fgets */
/*
* check for comment
*/
if (line[0] == '#')
continue;
firestring_chomp(line);
/*
* check for blank line
*/
if (line[0] == '\0')
continue;
/*
* build new object to insert
*/
local_domain1 = messagewall_malloc(struct messagewall_local_domain_t);
firestring_estr_alloc(&local_domain1->domain,strlen(line));
firestring_estr_strcpy(&local_domain1->domain,line);
/*
* calculate hash
*/
u = dbase_text_hash(&local_domain1->domain,0);
/*
* insert into table
*/
local_domain1->next = local_domain_hash[u];
local_domain_hash[u] = local_domain1;
}
fclose(f);
}
void dbase_load_relay_ips() { /* populate relay_ips list + hash table from relay_ips config file */
struct messagewall_relay_ip_t *relay_ip1;
struct in_addr *ip;
char *slash;
char line[257];
unsigned int u;
FILE *f;
int i;
if (relay_ips == NULL)
return;
/*
* we're inside the chroot; everything should be root controlled and fopen shouldn't be a problem
*/
f = fopen(relay_ips,"r"); /* ITS4: ignore fopen */
if (f == NULL) {
perror("Unable to open relay_ips file");
exit(100);
}
/*
* line really does have 257 bytes of space
*/
while (fgets(line,257,f) != NULL) { /* ITS4: ignore fgets */
/*
* check for comment
*/
if (line[0] == '#')
continue;
firestring_chomp(line);
/*
* line must contain a slash
*/
slash = strchr(line,'/');
if (slash == NULL)
continue;
slash[0] = '\0';
slash++;
/*
* convert IP to binary form
*/
ip = firedns_aton4(line);
if (ip == NULL)
/*
* invalid ip
*/
continue;
/*
* build new object to insert
*/
relay_ip1 = messagewall_malloc(struct messagewall_relay_ip_t);
/*
* memcpy uses sizeof destination, is safe
*/
memcpy(relay_ip1->ip,ip,sizeof(relay_ip1->ip)); /* ITS4: ignore memcpy */
/*
* try dotted quad mask notation
*/
ip = firedns_aton4_s(slash,(struct in_addr *)&relay_ip1->mask);
if (ip == NULL) {
i = atoi(slash);
if (i >= 16 && i <= 32)
ip = firedns_aton4_s(dbase_cidr_subnet[i],(struct in_addr *)&relay_ip1->mask);
if (ip == NULL) {
/*
* invalid mask
*/
fprintf(stderr,"STARTUP/WARNING: Invalid subnet mask: %s\n",slash);
free(relay_ip1);
continue;
}
}
if (relay_ip1->mask[0] != 255 || relay_ip1->mask[1] != 255) {
/*
* mask larger than /16
*/
fprintf(stderr,"STARTUP/WARNING: Subnet mask too large: %s\n",slash);
free(relay_ip1);
continue;
}
relay_ip1->ip[2] &= relay_ip1->mask[2];
relay_ip1->ip[3] &= relay_ip1->mask[3];
/*
* calculate hash
*/
u = dbase_ip_hash(relay_ip1->ip);
/*
* insert into table
*/
relay_ip1->next = relay_ip_hash[u];
relay_ip_hash[u] = relay_ip1;
}
fclose(f);
}
void dbase_load_special_users() { /* populate special_users list + hash table from special_users config file */
struct messagewall_special_user_t *special_user1;
char *slash;
char line[1025];
unsigned int u;
FILE *f;
if (special_users == NULL)
return;
/*
* we're inside the chroot; everything should be root controlled and fopen shouldn't be a problem
*/
f = fopen(special_users,"r"); /* ITS4: ignore fopen */
if (f == NULL) {
perror("Unable to open special_users file");
exit(100);
}
/*
* line really does have 1025 bytes of space
*/
while (fgets(line,1025,f) != NULL) { /* ITS4: ignore fgets */
/*
* check for comment
*/
if (line[0] == '#')
continue;
firestring_chomp(line);
/*
* check for blank line
*/
if (line[0] == '\0')
continue;
/*
* make sure it's delimited
*/
slash = strchr(line,':');
if (slash == NULL)
continue;
*(slash++) = '\0';
/*
* build new object to insert
*/
special_user1 = messagewall_malloc(struct messagewall_special_user_t);
firestring_estr_alloc(&special_user1->address,strlen(line));
firestring_estr_strcpy(&special_user1->address,line);
special_user1->profile = dbase_get_profile(slash);
/*
* calculate hash
*/
u = dbase_text_hash(&special_user1->address,0);
/*
* insert into table
*/
special_user1->next = special_user_hash[u];
special_user_hash[u] = special_user1;
}
fclose(f);
}
void dbase_load() { /* reload databases from config files */
dbase_clear();
dbase_load_local_domains();
dbase_load_relay_ips();
dbase_load_special_users();
}
unsigned int dbase_binary_hash(struct firestring_estr_t *domain, int start) {
static char s[4];
memset(s,0,4);
/*
* copies a maximum of 8 bytes by virtue of min macro, is safe
*/
memcpy(s,&domain->s[start],min(4,domain->l - start)); /* ITS4: ignore memcpy */
return (
( (s[0] & 0x0f) << 4 ) |
( (s[1] & 0x0f) )
) * 256 + (
( (s[2] & 0x0f) << 4 ) |
( (s[3] & 0x0f) )
);
}
unsigned int dbase_text_hash(struct firestring_estr_t *address, int start) { /* hash value= 16bit from given position in string */
if (address->l - start >= 2)
return tolower(address->s[start]) * 256 + tolower(address->s[start + 1]);
else
return 0;
}
unsigned int dbase_ip_hash(unsigned char *ip) { /* hash value= most significant 16bit from IP address */
return ip[0] * 256 + ip[1];
}
int dbase_domain_is_local(struct firestring_estr_t *domain) {
struct messagewall_local_domain_t *iter;
unsigned int u;
u = dbase_text_hash(domain,0);
iter = local_domain_hash[u];
while (iter != NULL) {
if (firestring_estr_estrcasecmp(domain,&iter->domain,0) == 0)
return 0;
iter = iter->next;
}
return 1;
}
int dbase_ip_can_relay(unsigned char *ip) {
struct messagewall_relay_ip_t *iter;
unsigned int u;
u = dbase_ip_hash(ip);
iter = relay_ip_hash[u];
while (iter != NULL) {
if (((ip[2] & iter->mask[2]) == iter->ip[2])
&& ((ip[3] & iter->mask[3]) == iter->ip[3]))
return 0;
iter = iter->next;
}
return 1;
}
struct messagewall_profile_t *dbase_get_profile(const char *name) { /* find in list or insert newly-allocated profile struct, return pointer to it */
struct messagewall_profile_t *iter;
iter = profile_head;
while (iter != NULL) {
if (strcmp(iter->name,name) == 0)
return iter;
iter = iter->next;
}
iter = profile_head;
profile_head = messagewall_malloc(struct messagewall_profile_t);
profile_head->next = iter;
profile_head->name = firestring_strdup(name);
/*
* defaults get set here
*/
profile_head->to_cc_check = 0;
profile_head->from_check = 0;
profile_head->realname_check = 0;
profile_head->rdns_required = 0;
profile_head->rmx_required = 0;
profile_head->reject = 0;
profile_head->reject_score = 1;
profile_head->header_reject = NULL;
profile_head->header_rejecti = NULL;
profile_head->body_reject = NULL;
profile_head->body_rejecti = NULL;
profile_head->filename_reject = NULL;
profile_head->mime_strip = NULL;
profile_head->mime_allow = NULL;
profile_head->mime_reject = NULL;
profile_head->dnsbl = NULL;
profile_head->dnsbl_domain = NULL;
profile_head->dnsdcc = NULL;
profile_head->virus_scan = NULL;
return profile_head;
}
struct messagewall_dnsbl_global_t *dbase_get_dnsbl(const char *dnsbl, struct messagewall_dnsbl_global_t **global_head) {
struct messagewall_dnsbl_global_t *iter;
int i;
iter = *global_head;
while (iter != NULL) {
if (firestring_estr_strcasecmp(&iter->dnsbl,dnsbl) == 0)
return iter;
iter = iter->next;
}
iter = *global_head;
if (iter == NULL)
i = 0;
else
i = iter->i + 1;
*global_head = messagewall_malloc(struct messagewall_dnsbl_global_t);
(*global_head)->next = iter;
(*global_head)->i = i;
firestring_estr_alloc(&(*global_head)->dnsbl,strlen(dnsbl));
firestring_estr_strcpy(&(*global_head)->dnsbl,dnsbl);
firestring_estr_0(&(*global_head)->dnsbl);
return dnsbl_head;
}
struct messagewall_dnsdcc_global_t *dbase_get_dnsdcc(const char *dnsdcc) {
struct messagewall_dnsdcc_global_t *iter;
char * (*check)(struct firestring_estr_t *);
int i;
iter = dnsdcc_head;
while (iter != NULL) {
if (firestring_estr_strcasecmp(&iter->dnsdcc,dnsdcc) == 0)
return iter;
iter = iter->next;
}
if (firestring_strncasecmp(dnsdcc,"aa.",3) == 0)
check = dnsdcc_checksum_aa;
else if (firestring_strncasecmp(dnsdcc,"ba.",3) == 0)
check = dnsdcc_checksum_ba;
else {
fprintf(stderr,"STARTUP/FATAL: unrecognized DNS DCC checksum protocol\n");
exit(100);
}
iter = dnsdcc_head;
if (iter == NULL)
i = 0;
else
i = iter->i + 1;
dnsdcc_head = messagewall_malloc(struct messagewall_dnsdcc_global_t);
dnsdcc_head->next = iter;
dnsdcc_head->i = i;
dnsdcc_head->check = check;
firestring_estr_alloc(&dnsdcc_head->dnsdcc,strlen(dnsdcc));
firestring_estr_strcpy(&dnsdcc_head->dnsdcc,dnsdcc);
firestring_estr_0(&dnsdcc_head->dnsdcc);
return dnsdcc_head;
}
static int dbase_get_score(const char **text) {
const char *tempstr;
int i;
tempstr = strchr(*text,',');
/*
* return default score
*/
if (tempstr == NULL)
return 1;
i = atoi(*text);
*text = tempstr + 1;
return i;
}
void dbase_load_estr_ll(struct firestring_conf_t *config, struct messagewall_estr_ll_t **head, const char *name) {
struct messagewall_estr_ll_t *estr_ll;
const char *tempstr;
tempstr = NULL;
while ((tempstr = firestring_conf_find_next(config,name,tempstr)) != NULL) {
estr_ll = *head;
*head = messagewall_malloc(struct messagewall_estr_ll_t);
(*head)->next = estr_ll;
firestring_estr_alloc(&(*head)->string,strlen(tempstr));
firestring_estr_strcpy(&(*head)->string,tempstr);
}
}
void dbase_load_estr_score_ll(struct firestring_conf_t *config, struct messagewall_estr_score_ll_t **head, const char *name) {
struct messagewall_estr_score_ll_t *estr_ll;
const char *tempstr;
const char *tempstr2;
tempstr2 = NULL;
while ((tempstr2 = firestring_conf_find_next(config,name,tempstr2)) != NULL) {
tempstr = tempstr2;
estr_ll = *head;
*head = messagewall_malloc(struct messagewall_estr_score_ll_t);
(*head)->next = estr_ll;
(*head)->score = dbase_get_score(&tempstr);
firestring_estr_alloc(&(*head)->string,strlen(tempstr));
firestring_estr_strcpy(&(*head)->string,tempstr);
}
}
void dbase_load_header_reject(struct firestring_conf_t *config, struct messagewall_header_reject_t **head, const char *name) {
const char *tempstr, *tempstr2, *tempstr3;
struct messagewall_header_reject_t *header_reject;
int s;
tempstr3 = NULL;
while ((tempstr3 = firestring_conf_find_next(config,name,tempstr3)) != NULL) {
tempstr = tempstr3;
s = dbase_get_score(&tempstr);
tempstr2 = strchr(tempstr,':');
if (tempstr2 == NULL)
continue;
tempstr2++;
header_reject = *head;
*head = messagewall_malloc(struct messagewall_header_reject_t);
(*head)->next = header_reject;
(*head)->score = s;
firestring_estr_alloc(&(*head)->header,tempstr2 - tempstr);
/*
* we just allocated this amount of space (estr does nil space automatically), this is safe
*/
memcpy((*head)->header.s,tempstr,tempstr2 - tempstr); /* ITS4: ignore memcpy */
(*head)->header.l = tempstr2 - tempstr;
firestring_estr_alloc(&(*head)->content,strlen(tempstr2));
firestring_estr_strcpy(&(*head)->content,tempstr2);
}
}
void dbase_load_int(struct firestring_conf_t *config, int *value, const char *name) {
const char *tempstr;
tempstr = firestring_conf_find(config,name);
if (tempstr == NULL)
return;
*value = atoi(tempstr);
}
void dbase_load_int_score(struct firestring_conf_t *config, int *value, int *score, const char *name) {
const char *tempstr;
tempstr = firestring_conf_find(config,name);
if (tempstr == NULL) {
*score = 1;
return;
}
*score = dbase_get_score(&tempstr);
*value = atoi(tempstr);
}
void dbase_load_profiles(const char *profile_dir) { /* populate list of profile structs from files in profile dir */
struct messagewall_profile_t *profile;
struct messagewall_dnsbl_profile_t *dnsbl;
struct messagewall_dnsdcc_profile_t *dnsdcc;
struct messagewall_virus_database_t *virus_database;
struct messagewall_virus_profile_t *virus_profile;
struct firestring_conf_t *config;
struct dirent *e;
DIR *d;
struct stat s;
const char *tempstr, *tempstr3;
char *tempstr2;
int i;
d = opendir(profile_dir); /* ITS4: ignore opendir */
if (d == NULL) {
perror("opendir(profile_dir)");
exit(100);
}
/*
* profile dir should be in root controlled location
* i don't see how this can be a problem
* SECURITY: do not put profile dir where users can move it out of the way or write inside of it
*/
if (chdir(profile_dir) != 0) { /* ITS4: ignore chdir */
perror("chdir(profile_dir)");
exit(100);
}
while ((e = readdir(d)) != NULL) {
if (stat(e->d_name,&s) != 0) { /* ITS4: ignore stat */
fprintf(stderr,"STARTUP/STATUS: skipping profile '%s'\n",e->d_name);
continue;
}
if (S_ISREG(s.st_mode) == 0)
continue;
fprintf(stderr,"STARTUP/STATUS: loaded profile %s\n",e->d_name);
profile = dbase_get_profile(e->d_name);
config = firestring_conf_parse(e->d_name);
if (config == NULL) {
fprintf(stderr,"STARTUP/FATAL: failed to load profile\n");
exit(100);
}
dbase_load_int_score(config,&profile->to_cc_check,&profile->to_cc_score,"to_cc_check");
dbase_load_int_score(config,&profile->from_check,&profile->from_score,"from_check");
dbase_load_int_score(config,&profile->realname_check,&profile->realname_score,"realname_check");
dbase_load_int_score(config,&profile->rdns_required,&profile->rdns_score,"rdns_required");
dbase_load_int_score(config,&profile->rmx_required,&profile->rmx_score,"rmx_required");
dbase_load_header_reject(config,&profile->header_reject,"header_reject");
dbase_load_header_reject(config,&profile->header_rejecti,"header_rejecti");
dbase_load_estr_score_ll(config,&profile->body_reject,"body_reject");
dbase_load_estr_score_ll(config,&profile->body_rejecti,"body_rejecti");
dbase_load_estr_score_ll(config,&profile->filename_reject,"filename_reject");
dbase_load_estr_score_ll(config,&profile->mime_reject,"mime_reject");
dbase_load_estr_ll(config,&profile->mime_strip,"mime_strip");
dbase_load_estr_ll(config,&profile->mime_allow,"mime_allow");
dbase_load_int(config,&profile->reject,"reject");
dbase_load_int(config,&profile->reject_score,"reject_score");
tempstr3 = NULL;
while ((tempstr3 = firestring_conf_find_next(config,"dnsbl",tempstr3)) != NULL) {
tempstr = tempstr3;
dnsbl = profile->dnsbl;
profile->dnsbl = messagewall_malloc(struct messagewall_dnsbl_profile_t);
profile->dnsbl->next = dnsbl;
profile->dnsbl->score = dbase_get_score(&tempstr);
tempstr2 = strchr(tempstr,'/');
if (tempstr2 == NULL)
profile->dnsbl->have_response_ip = 0;
else {
*(tempstr2++) = 0;
if (firedns_aton4_s(tempstr2,&profile->dnsbl->response_ip) == NULL)
profile->dnsbl->have_response_ip = 0;
else
profile->dnsbl->have_response_ip = 1;
}
profile->dnsbl->global = dbase_get_dnsbl(tempstr,&dnsbl_head);
}
tempstr3 = NULL;
while ((tempstr3 = firestring_conf_find_next(config,"dnsbl_domain",tempstr3)) != NULL) {
tempstr = tempstr3;
dnsbl = profile->dnsbl_domain;
profile->dnsbl_domain = messagewall_malloc(struct messagewall_dnsbl_profile_t);
profile->dnsbl_domain->next = dnsbl;
profile->dnsbl_domain->score = dbase_get_score(&tempstr);
tempstr2 = strchr(tempstr,'/');
if (tempstr2 == NULL)
profile->dnsbl_domain->have_response_ip = 0;
else {
*(tempstr2++) = 0;
if (firedns_aton4_s(tempstr2,&profile->dnsbl_domain->response_ip) == NULL)
profile->dnsbl_domain->have_response_ip = 0;
else
profile->dnsbl_domain->have_response_ip = 1;
}
profile->dnsbl_domain->global = dbase_get_dnsbl(tempstr,&dnsbl_domain_head);
}
tempstr3 = NULL;
while ((tempstr3 = firestring_conf_find_next(config,"dnsdcc",tempstr3)) != NULL) {
tempstr = tempstr3;
dnsdcc = profile->dnsdcc;
profile->dnsdcc = messagewall_malloc(struct messagewall_dnsdcc_profile_t);
profile->dnsdcc->next = dnsdcc;
profile->dnsdcc->score = dbase_get_score(&tempstr);
profile->dnsdcc->global = dbase_get_dnsdcc(tempstr);
}
tempstr3 = NULL;
while ((tempstr3 = firestring_conf_find_next(config,"virus_scan",tempstr3)) != NULL) {
tempstr = tempstr3;
i = dbase_get_score(&tempstr);
virus_database = virus_get_database(tempstr);
if (virus_database == NULL)
continue;
virus_profile = profile->virus_scan;
profile->virus_scan = messagewall_malloc(struct messagewall_virus_profile_t);
profile->virus_scan->next = virus_profile;
profile->virus_scan->global = virus_database;
profile->virus_scan->score = i;
}
}
closedir(d);
}
syntax highlighted by Code2HTML, v. 0.9.1