/* $Id: acl.c,v 1.33.2.3 2006/10/08 13:21:13 manu Exp $ */ /* * Copyright (c) 2004 Emmanuel Dreyfus * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Emmanuel Dreyfus * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "config.h" #ifdef HAVE_SYS_CDEFS_H #include #ifdef __RCSID __RCSID("$Id: acl.c,v 1.33.2.3 2006/10/08 13:21:13 manu Exp $"); #endif #endif #ifdef HAVE_OLD_QUEUE_H #include "queue.h" #else #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "acl.h" #include "conf.h" #include "sync.h" #include "list.h" #ifdef USE_DNSRBL #include "dnsrbl.h" #endif #include "macro.h" #include "milter-greylist.h" struct acllist acl_head; pthread_rwlock_t acl_lock; static struct acl_entry gacl; static void acl_init_entry (void) { memset (&gacl, 0, sizeof (gacl)); gacl.a_delay = -1; gacl.a_autowhite = -1; } void acl_init(void) { int error; TAILQ_INIT(&acl_head); if ((error = pthread_rwlock_init(&acl_lock, NULL)) != 0) { mg_log(LOG_ERR, "pthread_rwlock_init failed: %s", strerror(error)); exit(EX_OSERR); } acl_init_entry(); return; } void acl_add_flushaddr(void) { gacl.a_flags |= A_FLUSHADDR; return; } void acl_add_netblock(sa, salen, cidr) struct sockaddr *sa; socklen_t salen; int cidr; { ipaddr mask; char addrstr[IPADDRSTRLEN]; char maskstr[IPADDRSTRLEN]; int maxcidr, masklen; #ifdef AF_INET6 int i; #endif if (gacl.a_addr != NULL) { mg_log(LOG_ERR, "addr specified twice in ACL line %d", conf_line); exit(EX_DATAERR); } switch (sa->sa_family) { case AF_INET: maxcidr = 32; masklen = sizeof(mask.in4); break; #ifdef AF_INET6 case AF_INET6: maxcidr = 128; masklen = sizeof(mask.in6); break; #endif default: mg_log(LOG_ERR, "bad address family in acl list line %d", conf_line); exit(EX_DATAERR); } if (cidr > maxcidr || cidr < 0) { mg_log(LOG_ERR, "bad mask in acl list line %d", conf_line); exit(EX_DATAERR); } switch (sa->sa_family) { case AF_INET: prefix2mask4(cidr, &mask.in4); SADDR4(sa)->s_addr &= mask.in4.s_addr; break; #ifdef AF_INET6 case AF_INET6: prefix2mask6(cidr, &mask.in6); for (i = 0; i < 16; i += 4) *(uint32_t *)&SADDR6(sa)->s6_addr[i] &= *(uint32_t *)&mask.in6.s6_addr[i]; break; #endif } if ((gacl.a_addr = malloc(salen)) == NULL || (gacl.a_mask = malloc(masklen)) == NULL) { mg_log(LOG_ERR, "acl malloc failed: %s", strerror(errno)); exit(EX_OSERR); } gacl.a_addrlen = salen; memcpy(gacl.a_addr, sa, salen); memcpy(gacl.a_mask, &mask, masklen); if (conf.c_debug || conf.c_acldebug) { iptostring(gacl.a_addr, gacl.a_addrlen, addrstr, sizeof(addrstr)); inet_ntop(gacl.a_addr->sa_family, gacl.a_mask, maskstr, sizeof(maskstr)); mg_log(LOG_DEBUG, "load acl net %s/%s", addrstr, maskstr); } return; } void acl_add_from(email) char *email; { if (gacl.a_from != NULL || gacl.a_from_re != NULL || gacl.a_fromlist != NULL ) { mg_log(LOG_ERR, "from specified twice in ACL line %d", conf_line); exit(EX_DATAERR); } if ((gacl.a_from = strdup(email)) == NULL) { mg_log(LOG_ERR, "acl malloc failed: %s", strerror(errno)); exit(EX_OSERR); } if (conf.c_debug || conf.c_acldebug) mg_log(LOG_DEBUG, "load acl from %s", email); return; } #ifdef USE_DNSRBL void acl_add_dnsrbl(dnsrbl) char *dnsrbl; { if (gacl.a_dnsrbl != NULL || gacl.a_dnsrbllist != NULL) { mg_log(LOG_ERR, "dnsrbl specified twice in ACL line %d", conf_line); exit(EX_DATAERR); } if ((gacl.a_dnsrbl = dnsrbl_byname(dnsrbl)) == NULL) { mg_log(LOG_ERR, "unknown DNSRBL \"%s\"", dnsrbl); exit(EX_DATAERR); } if (conf.c_debug || conf.c_acldebug) mg_log(LOG_DEBUG, "load acl dnsrbl %s", dnsrbl); return; } #endif void acl_add_macro(macro) char *macro; { if (gacl.a_macro != NULL || gacl.a_macrolist != NULL) { mg_log(LOG_ERR, "sm_macro specified twice in ACL line %d", conf_line); exit(EX_DATAERR); } if ((gacl.a_macro = macro_byname(macro)) == NULL) { mg_log(LOG_ERR, "unknown sm_macro \"%s\"", macro); exit(EX_DATAERR); } if (conf.c_debug || conf.c_acldebug) mg_log(LOG_DEBUG, "load acl sm_macro %s", macro); return; } void acl_add_rcpt(email) char *email; { if (gacl.a_rcpt != NULL || gacl.a_rcpt_re != NULL || gacl.a_rcptlist != NULL) { mg_log(LOG_ERR, "rcpt specified twice in ACL line %d", conf_line); exit(EX_DATAERR); } if ((gacl.a_rcpt = strdup(email)) == NULL) { mg_log(LOG_ERR, "acl malloc failed: %s", strerror(errno)); exit(EX_OSERR); } if (conf.c_debug || conf.c_acldebug) mg_log(LOG_DEBUG, "load acl rcpt %s", email); return; } void acl_add_domain(domain) char *domain; { if (gacl.a_domain != NULL || gacl.a_domain_re != NULL || gacl.a_domainlist != NULL) { mg_log(LOG_ERR, "domain specified twice in ACL line %d", conf_line); exit(EX_DATAERR); } if ((gacl.a_domain = strdup(domain)) == NULL) { mg_log(LOG_ERR, "acl malloc failed: %s", strerror(errno)); exit(EX_OSERR); } if (conf.c_debug || conf.c_acldebug) mg_log(LOG_DEBUG, "load acl domain %s", domain); return; } #define ERRLEN 1024 void acl_add_from_regex(regexstr) char *regexstr; { size_t len; int error; char errstr[ERRLEN + 1]; if (gacl.a_from != NULL || gacl.a_from_re != NULL || gacl.a_fromlist != NULL) { mg_log(LOG_ERR, "from specified twice in ACL line %d", conf_line); exit(EX_DATAERR); } /* * Strip leading and trailing slashes */ len = strlen(regexstr); if (len > 0) regexstr[len - 1] = '\0'; regexstr++; if ((gacl.a_from_re = malloc(sizeof(*gacl.a_from_re))) == NULL) { mg_log(LOG_ERR, "acl malloc failed: %s", strerror(errno)); exit(EX_OSERR); } if ((error = regcomp(gacl.a_from_re, regexstr, (conf.c_extendedregex ? REG_EXTENDED : 0) | REG_ICASE)) != 0) { regerror(error, gacl.a_from_re, errstr, ERRLEN); mg_log(LOG_ERR, "bad regular expression \"%s\": %s", regexstr, errstr); exit(EX_OSERR); } if ((gacl.a_from_re_copy = strdup(regexstr)) == NULL) { mg_log(LOG_ERR, "acl strdup failed: %s", strerror(errno)); exit(EX_OSERR); } if (conf.c_debug || conf.c_acldebug) mg_log(LOG_DEBUG, "load acl from regex %s", regexstr); return; } void acl_add_rcpt_regex(regexstr) char *regexstr; { size_t len; int error; char errstr[ERRLEN + 1]; if (gacl.a_rcpt != NULL || gacl.a_rcpt_re != NULL || gacl.a_rcptlist != NULL) { mg_log(LOG_ERR, "rcpt specified twice in ACL line %d", conf_line); exit(EX_DATAERR); } /* * Strip leading and trailing slashes */ len = strlen(regexstr); if (len > 0) regexstr[len - 1] = '\0'; regexstr++; if ((gacl.a_rcpt_re = malloc(sizeof(*gacl.a_rcpt_re))) == NULL) { mg_log(LOG_ERR, "acl malloc failed: %s", strerror(errno)); exit(EX_OSERR); } if ((error = regcomp(gacl.a_rcpt_re, regexstr, (conf.c_extendedregex ? REG_EXTENDED : 0) | REG_ICASE)) != 0) { regerror(error, gacl.a_rcpt_re, errstr, ERRLEN); mg_log(LOG_ERR, "bad regular expression \"%s\": %s", regexstr, errstr); exit(EX_OSERR); } if ((gacl.a_rcpt_re_copy = strdup(regexstr)) == NULL) { mg_log(LOG_ERR, "acl strdup failed: %s", strerror(errno)); exit(EX_OSERR); } if (conf.c_debug || conf.c_acldebug) mg_log(LOG_DEBUG, "load acl rcpt regex %s", regexstr); return; } void acl_add_domain_regex(regexstr) char *regexstr; { size_t len; int error; char errstr[ERRLEN + 1]; if (gacl.a_domain != NULL || gacl.a_domain_re != NULL || gacl.a_domainlist != NULL) { mg_log(LOG_ERR, "domain specified twice in ACL line %d", conf_line); exit(EX_DATAERR); } /* * Strip leading and trailing slashes */ len = strlen(regexstr); if (len > 0) regexstr[len - 1] = '\0'; regexstr++; if ((gacl.a_domain_re = malloc(sizeof(*gacl.a_domain_re))) == NULL) { mg_log(LOG_ERR, "acl malloc failed: %s", strerror(errno)); exit(EX_OSERR); } if ((error = regcomp(gacl.a_domain_re, regexstr, (conf.c_extendedregex ? REG_EXTENDED : 0) | REG_ICASE)) != 0) { regerror(error, gacl.a_domain_re, errstr, ERRLEN); mg_log(LOG_ERR, "bad regular expression \"%s\": %s", regexstr, errstr); exit(EX_OSERR); } if ((gacl.a_domain_re_copy = strdup(regexstr)) == NULL) { mg_log(LOG_ERR, "acl strdup failed: %s", strerror(errno)); exit(EX_OSERR); } if (conf.c_debug || conf.c_acldebug) mg_log(LOG_DEBUG, "load acl domain regex %s", regexstr); return; } struct acl_entry * acl_register_entry_first(acl_type) /* acllist must be write-locked */ acl_type_t acl_type; { struct acl_entry *acl; if ((acl = malloc(sizeof(*acl))) == NULL) { mg_log(LOG_ERR, "ACL malloc failed: %s", strerror(errno)); exit(EX_OSERR); } *acl = gacl; acl->a_type = acl_type; acl->a_line = conf_line - 1; TAILQ_INSERT_HEAD(&acl_head, acl, a_list); acl_init_entry (); if (conf.c_debug || conf.c_acldebug) { switch(acl_type) { case A_GREYLIST: mg_log(LOG_DEBUG, "register acl first GREYLIST"); break; case A_WHITELIST: mg_log(LOG_DEBUG, "register acl first WHITELIST"); break; case A_BLACKLIST: mg_log(LOG_DEBUG, "register acl first BLACKLIST"); break; default: mg_log(LOG_ERR, "unecpected acl_type %d", acl_type); exit(EX_SOFTWARE); break; } } return acl; } struct acl_entry * acl_register_entry_last(acl_type) /* acllist must be write-locked */ acl_type_t acl_type; { struct acl_entry *acl; if ((acl = malloc(sizeof(*acl))) == NULL) { mg_log(LOG_ERR, "ACL malloc failed: %s", strerror(errno)); exit(EX_OSERR); } *acl = gacl; acl->a_type = acl_type; acl->a_line = conf_line - 1; TAILQ_INSERT_TAIL(&acl_head, acl, a_list); acl_init_entry (); if (conf.c_debug || conf.c_acldebug) { switch(acl_type) { case A_GREYLIST: mg_log(LOG_DEBUG, "register acl last GREYLIST"); break; case A_WHITELIST: mg_log(LOG_DEBUG, "register acl last WHITELIST"); break; case A_BLACKLIST: mg_log(LOG_DEBUG, "register acl last BLACKLIST"); break; default: mg_log(LOG_ERR, "unecpected acl_type %d", acl_type); exit(EX_SOFTWARE); break; } } return acl; } int acl_filter(ctx, priv, rcpt) SMFICTX *ctx; struct mlfi_priv *priv; char *rcpt; { struct sockaddr *sa; socklen_t salen; char *hostname; char *from; char *queueid; struct acl_entry *acl; char addrstr[IPADDRSTRLEN]; char whystr[HDRLEN]; char tmpstr[HDRLEN]; int retval; int testmode = conf.c_testmode; sa = SA(&priv->priv_addr); salen = priv->priv_addrlen; hostname = priv->priv_hostname; from = priv->priv_from; queueid = priv->priv_queueid; ACL_RDLOCK; TAILQ_FOREACH(acl, &acl_head, a_list) { retval = 0; if (acl->a_addrlist != NULL) { if (list_addr_filter(acl->a_addrlist, sa)) { retval |= EXF_ADDR; } else { continue; } } if (acl->a_addr != NULL) { if (ip_match(sa, acl->a_addr, acl->a_mask)) { retval |= EXF_ADDR; } else { continue; } } if (acl->a_domainlist != NULL) { if (list_domain_filter(acl->a_domainlist, hostname)) { retval |= EXF_DOMAIN; } else { continue; } } if (acl->a_domain != NULL) { if (domaincmp(hostname, acl->a_domain)) { retval |= EXF_DOMAIN; } else { continue; } } if (acl->a_domain_re != NULL) { if (regexec(acl->a_domain_re, hostname, 0, NULL, 0) == 0) { retval |= EXF_DOMAIN; } else { continue; } } if (acl->a_fromlist != NULL) { if (list_from_filter(acl->a_fromlist, from)) { retval |= EXF_FROM; } else { continue; } } if (acl->a_from != NULL) { if (emailcmp(from, acl->a_from) == 0) { retval |= EXF_FROM; } else { continue; } } if (acl->a_from_re != NULL) { if (regexec(acl->a_from_re, from, 0, NULL, 0) == 0) { retval |= EXF_FROM; } else { continue; } } if (acl->a_rcptlist != NULL) { if (list_rcpt_filter(acl->a_rcptlist, rcpt)) { retval |= EXF_RCPT; } else { continue; } } if (acl->a_rcpt != NULL) { if (emailcmp(rcpt, acl->a_rcpt) == 0) { retval |= EXF_RCPT; } else { continue; } } if (acl->a_rcpt_re != NULL) { if (regexec(acl->a_rcpt_re, rcpt, 0, NULL, 0) == 0) { retval |= EXF_RCPT; } else { continue; } } #ifdef USE_DNSRBL if (acl->a_dnsrbllist != NULL) { if (list_dnsrbl_filter(acl->a_dnsrbllist, salen, sa)) { retval |= EXF_DNSRBL; } else { continue; } } if (acl->a_dnsrbl != NULL) { if (dnsrbl_check_source(sa, salen, acl->a_dnsrbl) == 1) { retval |= EXF_DNSRBL; } else { continue; } } #endif if (acl->a_macrolist != NULL) { if (list_macro_filter(acl->a_macrolist, ctx)) { retval |= EXF_MACRO; } else { continue; } } if (acl->a_macro != NULL) { if (macro_check(ctx, acl->a_macro) == 0) { retval |= EXF_MACRO; } else { continue; } } /* * We found an entry that matches, exit the evaluation * loop */ break; } if (acl) { if (retval == 0) retval = EXF_DEFAULT; switch (acl->a_type) { case A_GREYLIST: retval |= EXF_GREYLIST; break; case A_WHITELIST: retval |= EXF_WHITELIST; break; case A_BLACKLIST: retval |= EXF_BLACKLIST; break; default: mg_log(LOG_ERR, "corrupted acl list"); exit(EX_SOFTWARE); break; } priv->priv_acl_line = acl->a_line; priv->priv_delay = (acl->a_delay != -1) ? acl->a_delay : conf.c_delay; priv->priv_autowhite = (acl->a_autowhite != -1) ? acl->a_autowhite : conf.c_autowhite_validity; if (acl->a_code) { if ((priv->priv_code = strdup(acl->a_code)) == NULL) { mg_log(LOG_ERR, "strdup failed: %s", strerror(errno)); exit(EX_OSERR); } } if (acl->a_ecode) { if ((priv->priv_ecode = strdup(acl->a_ecode)) == NULL) { mg_log(LOG_ERR, "strdup failed: %s", strerror(errno)); exit(EX_OSERR); } } if (acl->a_msg) { if ((priv->priv_msg = strdup(acl->a_msg)) == NULL) { mg_log(LOG_ERR, "strdup failed: %s", strerror(errno)); exit(EX_OSERR); } } if (acl->a_flags & A_FLUSHADDR) pending_del_addr(sa, salen, queueid, acl->a_line); if (conf.c_debug || conf.c_acldebug) { iptostring(sa, salen, addrstr, sizeof(addrstr)); mg_log(LOG_DEBUG, "Mail from=%s, rcpt=%s, addr=%s[%s] " "is matched by entry %s", from, rcpt, hostname, addrstr, acl_entry(acl)); } } else { /* * No match: use the default action */ if (testmode) retval = EXF_WHITELIST; else retval = EXF_GREYLIST; retval |= EXF_DEFAULT; priv->priv_delay = conf.c_delay; priv->priv_autowhite = conf.c_autowhite_validity; } if (retval & EXF_WHITELIST) { whystr[0] = '\0'; if (retval & EXF_ADDR) { iptostring(sa, salen, addrstr, sizeof(addrstr)); snprintf(tmpstr, sizeof(tmpstr), "address %s is whitelisted", addrstr); ADD_REASON(whystr, tmpstr); } if (retval & EXF_DNSRBL) { iptostring(sa, salen, addrstr, sizeof(addrstr)); snprintf(tmpstr, sizeof(tmpstr), "address %s is whitelisted by DNSRBL", addrstr); ADD_REASON(whystr, tmpstr); } if (retval & EXF_DOMAIN) { snprintf(tmpstr, sizeof(tmpstr), "sender DNS name %s is whitelisted", hostname); ADD_REASON(whystr, tmpstr); } if (retval & EXF_FROM) { snprintf(tmpstr, sizeof(tmpstr), "sender %s is whitelisted", from); ADD_REASON(whystr, tmpstr); } if (retval & EXF_RCPT) { snprintf(tmpstr, sizeof(tmpstr), "recipient %s is whitelisted", rcpt); ADD_REASON(whystr, tmpstr); } if (retval & EXF_MACRO) { snprintf(tmpstr, sizeof(tmpstr), "macro rule is satisfied"); ADD_REASON(whystr, tmpstr); } if (retval & EXF_DEFAULT) { ADD_REASON(whystr, "this is the default action"); } iptostring(sa, salen, addrstr, sizeof(addrstr)); snprintf(tmpstr, sizeof(tmpstr), "(from=%s, rcpt=%s, addr=%s[%s])", from, rcpt, hostname, addrstr); ADD_REASON(whystr, tmpstr); mg_log(LOG_INFO, "%s: skipping greylist because %s", queueid, whystr); } ACL_UNLOCK; return retval; } int domaincmp(host, domain) char *host; char *domain; { int hidx, didx; if ((host[0] == '\0') && domain[0] == '\0') return 1; if ((host[0] == '\0') || domain[0] == '\0') return 0; hidx = strlen(host) - 1; didx = strlen(domain) - 1; while ((hidx >= 0) && (didx >= 0)) { if (tolower((int)host[hidx]) != tolower((int)domain[didx])) { return (0); } hidx--; didx--; } if (didx >= 0) return (0); return (1); } int emailcmp(big, little) char *big; char *little; { int i; int retval = -1; char *cbig; char *clittle; char *ocbig; char *oclittle; if ((cbig = malloc(strlen(big) + 1)) == NULL) { mg_log(LOG_ERR, "malloc failed: %s", strerror(errno)); exit(EX_OSERR); } ocbig = cbig; strcpy(cbig, big); if ((clittle = malloc(strlen(little) + 1)) == NULL) { mg_log(LOG_ERR, "malloc failed: %s", strerror(errno)); exit(EX_OSERR); } oclittle = clittle; strcpy(clittle, little); /* Strip leading <, tabs and spaces */ while (strchr("< \t", cbig[0]) != NULL) cbig++; while (strchr("< \t", clittle[0]) != NULL) clittle++; /* Strip trailing >, tabs and spaces */ i = strlen(cbig) - 1; while ((i >= 0) && (strchr("> \t", cbig[i]) != NULL)) cbig[i--] = '\0'; i = strlen(clittle) - 1; while ((i >= 0) && (strchr("> \t", clittle[i]) != NULL)) clittle[i--] = '\0'; while (cbig[0] && clittle[0]) { if (tolower((int)cbig[0]) != tolower((int)clittle[0])) break; cbig++; clittle++; } if (cbig[0] || clittle[0]) retval = -1; else retval = 0; free(ocbig); free(oclittle); return retval; } void acl_clear(void) { /* acllist must be write locked */ struct acl_entry *acl; while (!TAILQ_EMPTY(&acl_head)) { acl = TAILQ_FIRST(&acl_head); TAILQ_REMOVE(&acl_head, acl, a_list); if (acl->a_addr != NULL) { free(acl->a_addr); free(acl->a_mask); } if (acl->a_from != NULL) free(acl->a_from); if (acl->a_rcpt != NULL) free(acl->a_rcpt); if (acl->a_domain != NULL) free(acl->a_domain); if (acl->a_from_re != NULL) regfree(acl->a_from_re); if (acl->a_from_re_copy != NULL) free(acl->a_from_re_copy); if (acl->a_rcpt_re != NULL) regfree(acl->a_rcpt_re); if (acl->a_rcpt_re_copy != NULL) free(acl->a_rcpt_re_copy); if (acl->a_domain_re != NULL) regfree(acl->a_domain_re); if (acl->a_domain_re_copy != NULL) free(acl->a_domain_re_copy); if (acl->a_code != NULL) free(acl->a_code); if (acl->a_ecode != NULL) free(acl->a_ecode); if (acl->a_msg != NULL) free(acl->a_msg); free(acl); } TAILQ_INIT(&acl_head); acl_init_entry(); return; } char * acl_entry(acl) struct acl_entry *acl; { static char entrystr[HDRLEN]; char tempstr[HDRLEN]; char addrstr[IPADDRSTRLEN]; char maskstr[IPADDRSTRLEN]; int def = 1; snprintf(entrystr, HDRLEN, "acl %d ", acl->a_line); switch (acl->a_type) { case A_GREYLIST: mystrlcat(entrystr, "greylist ", sizeof(entrystr)); break; case A_WHITELIST: mystrlcat(entrystr, "whitelist ", sizeof(entrystr)); break; case A_BLACKLIST: mystrlcat(entrystr, "blacklist ", sizeof(entrystr)); break; default: mg_log(LOG_ERR, "corrupted acl list"); exit(EX_SOFTWARE); break; } if (acl->a_addrlist != NULL) { snprintf(tempstr, sizeof(tempstr), "addr list \"%s\" ", acl->a_addrlist->al_name); mystrlcat(entrystr, tempstr, sizeof(entrystr)); def = 0; } if (acl->a_addr != NULL) { iptostring(acl->a_addr, acl->a_addrlen, addrstr, sizeof(addrstr)); inet_ntop(acl->a_addr->sa_family, acl->a_mask, maskstr, sizeof(maskstr)); snprintf(tempstr, sizeof(tempstr), "addr %s/%s ", addrstr, maskstr); mystrlcat(entrystr, tempstr, sizeof(entrystr)); def = 0; } if (acl->a_fromlist != NULL) { snprintf(tempstr, sizeof(tempstr), "from list \"%s\" ", acl->a_fromlist->al_name); mystrlcat(entrystr, tempstr, sizeof(entrystr)); def = 0; } if (acl->a_from != NULL) { snprintf(tempstr, sizeof(tempstr), "from %s ", acl->a_from); mystrlcat(entrystr, tempstr, sizeof(entrystr)); def = 0; } if (acl->a_from_re != NULL) { snprintf(tempstr, sizeof(tempstr), "from /%s/ ", acl->a_from_re_copy); mystrlcat(entrystr, tempstr, sizeof(entrystr)); def = 0; } if (acl->a_rcptlist != NULL) { snprintf(tempstr, sizeof(tempstr), "rcpt list \"%s\" ", acl->a_rcptlist->al_name); mystrlcat(entrystr, tempstr, sizeof(entrystr)); def = 0; } if (acl->a_rcpt != NULL) { snprintf(tempstr, sizeof(tempstr), "rcpt %s ", acl->a_rcpt); mystrlcat(entrystr, tempstr, sizeof(entrystr)); def = 0; } if (acl->a_rcpt_re != NULL) { snprintf(tempstr, sizeof(tempstr), "rcpt /%s/ ", acl->a_rcpt_re_copy); mystrlcat(entrystr, tempstr, sizeof(entrystr)); def = 0; } if (acl->a_domainlist != NULL) { snprintf(tempstr, sizeof(tempstr), "domainlist \"%s\" ", acl->a_domainlist->al_name); mystrlcat(entrystr, tempstr, sizeof(entrystr)); def = 0; } if (acl->a_domain != NULL) { snprintf(tempstr, sizeof(tempstr), "domain %s ", acl->a_domain); mystrlcat(entrystr, tempstr, sizeof(entrystr)); def = 0; } if (acl->a_domain_re != NULL) { snprintf(tempstr, sizeof(tempstr), "domain /%s/ ", acl->a_domain_re_copy); mystrlcat(entrystr, tempstr, sizeof(entrystr)); def = 0; } #if USE_DNSRBL if (acl->a_dnsrbllist != NULL) { snprintf(tempstr, sizeof(tempstr), "dnsrbllist \"%s\" ", acl->a_dnsrbllist->al_name); mystrlcat(entrystr, tempstr, sizeof(entrystr)); def = 0; } if (acl->a_dnsrbl != NULL) { snprintf(tempstr, sizeof(tempstr), "dnsrbl \"%s\" ", acl->a_dnsrbl->d_name); mystrlcat(entrystr, tempstr, sizeof(entrystr)); def = 0; } #endif if (acl->a_macrolist != NULL) { snprintf(tempstr, sizeof(tempstr), "sm_macrolist \"%s\" ", acl->a_macrolist->al_name); mystrlcat(entrystr, tempstr, sizeof(entrystr)); def = 0; } if (acl->a_macro != NULL) { snprintf(tempstr, sizeof(tempstr), "sm_macro \"%s\" ", acl->a_macro->m_name); mystrlcat(entrystr, tempstr, sizeof(entrystr)); def = 0; } if (acl->a_delay != -1) { snprintf(tempstr, sizeof(tempstr), "[delay %ld] ", (long)acl->a_delay); mystrlcat(entrystr, tempstr, sizeof(entrystr)); } if (acl->a_autowhite != -1) { snprintf(tempstr, sizeof(tempstr), "[aw %ld] ", (long)acl->a_autowhite); mystrlcat(entrystr, tempstr, sizeof(entrystr)); } if (acl->a_flags & A_FLUSHADDR) { snprintf(tempstr, sizeof(tempstr), "[flushaddr] "); mystrlcat(entrystr, tempstr, sizeof(entrystr)); } if (acl->a_code) { snprintf(tempstr, sizeof(tempstr), "[code \"%s\"] ", acl->a_code); mystrlcat(entrystr, tempstr, sizeof(entrystr)); } if (acl->a_ecode) { snprintf(tempstr, sizeof(tempstr), "[ecode \"%s\"] ", acl->a_ecode); mystrlcat(entrystr, tempstr, sizeof(entrystr)); } if (acl->a_msg) { snprintf(tempstr, sizeof(tempstr), "[msg \"%s\"] ", acl->a_msg); mystrlcat(entrystr, tempstr, sizeof(entrystr)); } if (def) mystrlcat(entrystr, "default", sizeof(entrystr)); return entrystr; } void acl_dump (void) { /* acllist must be write locked */ struct acl_entry *acl; char *entry; FILE *debug = NULL; /* * We log the ACL to syslogd * We can also write the ACL in a file because syslogd seems to lose * some debugging messages on FreeBSD 4.10 :-( * XXX This is disabled by default (#if 0 above) since it creates * security hazards: /tmp/access-list.debug could already exist and * be a link to some system file which would be overwritten. * Enable it if you need it, but you may be better changing the path */ #if 0 debug = fopen("/tmp/access-list.debug", "w"); #endif ACL_RDLOCK; mg_log(LOG_INFO, "Access list dump:"); TAILQ_FOREACH(acl, &acl_head, a_list) { entry = acl_entry(acl); mg_log(LOG_INFO, "%s", entry); if (debug != NULL) fprintf(debug, "%s", entry); } ACL_UNLOCK; if (debug != NULL) fclose(debug); } void acl_add_delay(delay) time_t delay; { if (gacl.a_delay != -1) { mg_log(LOG_ERR, "delay specified twice in ACL line %d", conf_line); exit(EX_DATAERR); } gacl.a_delay = delay; if (conf.c_debug || conf.c_acldebug) mg_log(LOG_DEBUG, "load acl delay %ld", (long)delay); return; } void acl_add_autowhite(delay) time_t delay; { if (gacl.a_autowhite != -1) { mg_log(LOG_ERR, "autowhite specified twice in ACL line %d", conf_line); exit(EX_DATAERR); } gacl.a_autowhite = delay; if (conf.c_debug || conf.c_acldebug) mg_log(LOG_DEBUG, "load acl delay %ld", (long)delay); return; } void acl_add_list(list) char *list; { struct all_list_entry *ale; if ((ale = all_list_byname(list)) == NULL) { mg_log(LOG_ERR, "inexistent list \"%s\" line %d", list, conf_line); exit(EX_DATAERR); } switch (ale->al_type) { case LT_FROM: if (gacl.a_from != NULL || gacl.a_from_re != NULL || gacl.a_fromlist != NULL) { mg_log(LOG_ERR, "muliple from statement (list \"%s\", line %d)", list, conf_line); exit(EX_DATAERR); } gacl.a_fromlist = ale; break; case LT_RCPT: if (gacl.a_rcpt != NULL || gacl.a_rcpt_re != NULL || gacl.a_rcptlist != NULL) { mg_log(LOG_ERR, "muliple rcpt statement (list \"%s\", line %d)", list, conf_line); exit(EX_DATAERR); } gacl.a_rcptlist = ale; break; case LT_DOMAIN: if (gacl.a_domain != NULL || gacl.a_domain_re != NULL || gacl.a_domainlist != NULL) { mg_log(LOG_ERR, "muliple domain statement (list \"%s\", line %d)", list, conf_line); exit(EX_DATAERR); } gacl.a_domainlist = ale; break; #if USE_DNSRBL case LT_DNSRBL: if (gacl.a_dnsrbl != NULL || gacl.a_dnsrbllist != NULL) { mg_log(LOG_ERR, "muliple dnsrbl statement (list \"%s\", line %d)", list, conf_line); exit(EX_DATAERR); } gacl.a_dnsrbllist = ale; break; #endif case LT_MACRO: if (gacl.a_macro != NULL || gacl.a_macrolist != NULL) { mg_log(LOG_ERR, "muliple sm_macro statement (list \"%s\", line %d)", list, conf_line); exit(EX_DATAERR); } gacl.a_macrolist = ale; break; case LT_ADDR: if (gacl.a_addr != NULL || gacl.a_addrlist != NULL) { mg_log(LOG_ERR, "muliple addr statement (list \"%s\", line %d)", list, conf_line); exit(EX_DATAERR); } gacl.a_addrlist = ale; break; default: mg_log(LOG_ERR, "unexpected al_type %d line %d", ale->al_type, conf_line); exit(EX_DATAERR); break; } if (conf.c_debug || conf.c_acldebug) mg_log(LOG_DEBUG, "load acl list \"%s\"", list); return; } void acl_add_code(code) char *code; { if (gacl.a_code) { mg_log(LOG_ERR, "code specified twice in ACL line %d", conf_line); exit(EX_DATAERR); } if ((gacl.a_code = strdup(code)) == NULL) { mg_log(LOG_ERR, "malloc failed in ACL line %d", conf_line); exit(EX_OSERR); } if (conf.c_debug || conf.c_acldebug) mg_log(LOG_DEBUG, "load acl code \"%s\"", code); return; } void acl_add_ecode(ecode) char *ecode; { if (gacl.a_ecode) { mg_log(LOG_ERR, "ecode specified twice in ACL line %d", conf_line); exit(EX_DATAERR); } if ((gacl.a_ecode = strdup(ecode)) == NULL) { mg_log(LOG_ERR, "malloc failed in ACL line %d", conf_line); exit(EX_OSERR); } if (conf.c_debug || conf.c_acldebug) mg_log(LOG_DEBUG, "load acl ecode \"%s\"", ecode); return; } void acl_add_msg(msg) char *msg; { if (gacl.a_msg) { mg_log(LOG_ERR, "msg specified twice in ACL line %d", conf_line); exit(EX_DATAERR); } if ((gacl.a_msg = strdup(msg)) == NULL) { mg_log(LOG_ERR, "malloc failed in ACL line %d", conf_line); exit(EX_OSERR); } if (conf.c_debug || conf.c_acldebug) mg_log(LOG_DEBUG, "load acl msg \"%s\"", msg); return; }