sfsistat clamfi_abort(SMFICTX *ctx) { struct CLAMFI *cf; if((cf = (struct CLAMFI *)smfi_getpriv(ctx))) nullify(ctx, cf, CF_ANY); return SMFIS_CONTINUE; }
/*}}}*/ } priv_t; static char * xfree (char *s) /*{{{*/ { if (s) free (s); return NULL; }/*}}}*/ static bool_t xcopy (char **buf, const char *str) /*{{{*/ { if (*buf) free (*buf); *buf = str ? strdup (str) : NULL; return (! str) || *buf ? true : false; }/*}}}*/ static void priv_clear (priv_t *p) /*{{{*/ { if (p) { p -> x_agn = 0; p -> from = xfree (p -> from); p -> receiver = charc_free_all (p -> receiver); p -> prev = NULL; p -> info = xfree (p -> info); } }/*}}}*/ static priv_t * priv_free (priv_t *p) /*{{{*/ { if (p) { priv_clear (p); if (p -> lg) log_free (p -> lg); if (p -> cfg) cfg_free (p -> cfg); free (p); } return NULL; }/*}}}*/ static priv_t * priv_alloc (void) /*{{{*/ { priv_t *p; if (p = (priv_t *) malloc (sizeof (priv_t))) if (p -> cfg = cfg_alloc (cfgfile)) { p -> is_local = false; p -> x_agn = 0; p -> from = NULL; p -> receiver = NULL; p -> prev = NULL; p -> info = NULL; if (! (p -> lg = log_alloc (NULL, program, loglevel))) p = priv_free (p); } else { free (p); p = NULL; } return p; }/*}}}*/ static bool_t priv_setfrom (priv_t *p, const char *from) /*{{{*/ { return xcopy (& p -> from, from); }/*}}}*/ static bool_t priv_setto (priv_t *p, const char *to) /*{{{*/ { charc_t *r; if (r = charc_alloc (to)) { if (p -> prev) p -> prev -> next = r; else p -> receiver = r; p -> prev = r; } return r ? true : false; }/*}}}*/ static bool_t priv_addinfo (priv_t *p, const char *info) /*{{{*/ { char *temp; if ((! p -> info) || (! p -> info[0])) return xcopy (& p -> info, info); if (temp = malloc (strlen (p -> info) + strlen (info) + 2)) { sprintf (temp, "%s,%s", p -> info, info); free (p -> info); p -> info = temp; return true; } return false; }/*}}}*/ static bool_t priv_addinfopair (priv_t *p, const char *var, const char *val) /*{{{*/ { bool_t rc; char *scratch, *ptr; if (scratch = malloc (strlen (var) + strlen (val) + 2)) { for (ptr = scratch; *var; *ptr++ = *var++) ; *ptr++ = '='; for (;*val; ++val) *ptr++ = *val == ',' ? '_' : *val; *ptr = '\0'; rc = priv_addinfo (p, scratch); free (scratch); } else rc = false; return rc; }/*}}}*/ static sfsistat handle_connect (SMFICTX *ctx, char *hostname, _SOCK_ADDR *hostaddr) /*{{{*/ { priv_t *p; if (! (p = priv_alloc ())) return SMFIS_TEMPFAIL; if (hostaddr -> sa_family == AF_INET) { struct sockaddr_in *iaddr = (struct sockaddr_in *) hostaddr; if (ntohl (iaddr -> sin_addr.s_addr) == INADDR_LOOPBACK) p -> is_local = true; } # ifdef AF_INET6 else if (hostaddr -> sa_family == AF_INET6) { struct sockaddr_in6 *i6addr = (struct sockaddr_in6 *) hostaddr; static struct in6_addr loopback = IN6ADDR_LOOPBACK_INIT; if (memcmp (& i6addr -> sin6_addr, & loopback, sizeof (i6addr -> sin6_addr)) == 0) p -> is_local = true; } # endif /* AF_INET6 */ smfi_setpriv (ctx, p); return SMFIS_CONTINUE; }/*}}}*/ static sfsistat handle_from (SMFICTX *ctx, char **argv) /*{{{*/ { priv_t *p = (priv_t *) smfi_getpriv (ctx); if (! p) return SMFIS_TEMPFAIL; priv_clear (p); if (! priv_setfrom (p, argv[0])) return SMFIS_TEMPFAIL; priv_addinfopair (p, "from", argv[0]); return SMFIS_CONTINUE; }/*}}}*/ static sfsistat handle_to (SMFICTX *ctx, char **argv) /*{{{*/ { priv_t *p = (priv_t *) smfi_getpriv (ctx); char *chk, *opt; bool_t reject, tempfail; if (! p) return SMFIS_TEMPFAIL; if (p -> is_local) return SMFIS_CONTINUE; if (! (chk = cfg_valid_address (p -> cfg, argv[0]))) { log_out (p -> lg, LV_ERROR, "Unable to setup initial data for `%s'", argv[0]); return SMFIS_TEMPFAIL; } if (opt = strchr (chk, ':')) *opt++ = '\0'; reject = false; tempfail = false; if (! strcmp (chk, ID_REJECT)) reject = true; else if (! strcmp (chk, ID_TEMPFAIL)) tempfail = true; else if ((! strcmp (chk, ID_ACCEPT)) && opt) priv_addinfo (p, opt); priv_addinfopair (p, "to", argv[0]); free (chk); if (reject) { log_out (p -> lg, LV_INFO, "Receiver `%s' is rejected", argv[0]); smfi_setreply (ctx, (char *) "550", (char *) "5.1.1", (char *) "No such user"); return SMFIS_REJECT; } if (tempfail) { log_out (p -> lg, LV_INFO, "Receiver `%s' is temp. disbaled", argv[0]); smfi_setreply (ctx, (char *) "400", (char *) "4.0.0", (char *) "Please try again later"); return SMFIS_TEMPFAIL; } if (! priv_setto (p, argv[0])) return SMFIS_TEMPFAIL; return SMFIS_CONTINUE; }/*}}}*/ static sfsistat handle_header (SMFICTX *ctx, char *field, char *value) /*{{{*/ { priv_t *p = (priv_t *) smfi_getpriv (ctx); if (! p) return SMFIS_TEMPFAIL; if (p -> is_local) return SMFIS_CONTINUE; if (! strcasecmp (field, X_LOOP)) { log_out (p -> lg, LV_WARNING, "Mail from `%s' has already loop marker set, rejected", p -> from); smfi_setreply (ctx, (char *) "500", (char *) "5.4.6", (char *) "Loop detected"); return SMFIS_REJECT; } if (! strcasecmp (field, X_AGN)) p -> x_agn++; return SMFIS_CONTINUE; }/*}}}*/ static sfsistat handle_eom (SMFICTX *ctx) /*{{{*/ { priv_t *p = (priv_t *) smfi_getpriv (ctx); int n; if (! p) return SMFIS_TEMPFAIL; for (n = 0; n < p -> x_agn; ++n) smfi_chgheader (ctx, (char *) X_AGN, 0, NULL); if (! p -> is_local) { if (p -> info) smfi_addheader (ctx, (char *) X_AGN, p -> info); smfi_addheader (ctx, (char *) X_LOOP, (char *) LOOP_SET); } return SMFIS_CONTINUE; }/*}}}*/ static sfsistat handle_close(SMFICTX *ctx) /*{{{*/ { priv_free (smfi_getpriv (ctx)); smfi_setpriv (ctx, NULL); return SMFIS_CONTINUE; }/*}}}*/
static sfsistat cb_body(SMFICTX *ctx, u_char *chunk, size_t size) { struct context *context; if ((context = (struct context *)smfi_getpriv(ctx)) == NULL) { msg(LOG_ERR, NULL, "cb_body: smfi_getpriv"); return (SMFIS_ACCEPT); } for (; size > 0; size--, chunk++) { context->buf[context->pos] = *chunk; if (context->buf[context->pos] == '\n' || context->pos == sizeof(context->buf) - 1) { if (context->pos > 0 && context->buf[context->pos - 1] == '\r') context->buf[context->pos - 1] = 0; else context->buf[context->pos] = 0; context->pos = 0; msg(LOG_DEBUG, context, "cb_body('%s')", context->buf); if (context->fd >= 0 && context->lines < 1000) { fdprintf(context->fd, "%s\r\n", context->buf); context->lines++; } } else context->pos++; } return (SMFIS_CONTINUE); }
sfsistat clamfi_envrcpt(SMFICTX *ctx, char **argv) { struct CLAMFI *cf; if(!(cf = (struct CLAMFI *)smfi_getpriv(ctx))) return SMFIS_CONTINUE; /* whatever */ if(cf->all_whitelisted) cf->all_whitelisted &= whitelisted(argv[0], 0); if(multircpt) { void *new_rcpt = realloc(cf->recipients, (cf->nrecipients + 1) * sizeof(*(cf->recipients))); unsigned int rcpt_cnt; if(!new_rcpt) { logg("!Failed to allocate array for new recipient\n"); nullify(ctx, cf, CF_ANY); free(cf); return FailAction; } cf->recipients = new_rcpt; rcpt_cnt = cf->nrecipients++; if(!(cf->recipients[rcpt_cnt] = strdup(argv[0]))) { logg("!Failed to allocate space for new recipient\n"); nullify(ctx, cf, CF_ANY); free(cf); return FailAction; } } return SMFIS_CONTINUE; }
sfsistat mlfi_abort(SMFICTX *ctx) { struct nameserver *mp = smfi_getpriv(ctx) ; int status = SMFIS_CONTINUE ; // Do nothing here at this time... return(status) ; }
static sfsistat cb_header(SMFICTX *ctx, char *name, char *value) { struct context *context; const struct action *action; int salevel; if ((context = (struct context *)smfi_getpriv(ctx)) == NULL) { msg(LOG_ERR, context, "cb_header: smfi_getpriv"); return (SMFIS_ACCEPT); } msg(LOG_DEBUG, context, "cb_header('%s', '%s')", name, value); if (context->sclvalue != -1) return (SMFIS_CONTINUE); if (!strcasecmp(name, "X-Spam-Level")) { salevel = strlen(value); switch (salevel) { case 0: case 1: context->sclvalue = 1; break; case 2: context->sclvalue = 2; break; case 3: context->sclvalue = 3; break; case 4: context->sclvalue = 4; break; case 5: context->sclvalue = 5; break; case 6: context->sclvalue = 6; break; case 7: case 8: context->sclvalue = 7; break; case 9: case 10: context->sclvalue = 8; break; default: if (salevel > 10) { context->sclvalue = 9; } else { msg(LOG_ERR, NULL, "cb_header: X-Spam-Level parsed %d", salevel); } break; } } return (SMFIS_CONTINUE); }
sfsistat clamfi_envrcpt(SMFICTX *ctx, char **argv) { struct CLAMFI *cf; if(!(cf = (struct CLAMFI *)smfi_getpriv(ctx))) return SMFIS_CONTINUE; /* whatever */ if(cf->all_whitelisted) cf->all_whitelisted &= whitelisted(argv[0], 0); return SMFIS_CONTINUE; }
sfsistat mlfi_close(SMFICTX *ctx) { struct nameserver *mp = smfi_getpriv(ctx) ; int status = SMFIS_CONTINUE ; // We are done with the connection, so free up our context if(mp != NULL) { free(mp) ; smfi_setpriv(ctx, NULL) ; } return(status) ; }
static sfsistat action_reject_msg(SMFICTX *ctx) { struct CLAMFI *cf; char buf[1024]; if(!rejectfmt || !(cf = (struct CLAMFI *)smfi_getpriv(ctx))) return SMFIS_REJECT; snprintf(buf, sizeof(buf), rejectfmt, cf->virusname); buf[sizeof(buf)-1] = '\0'; smfi_setreply(ctx, "550", "5.7.1", buf); return SMFIS_REJECT; }
static sfsistat cb_close(SMFICTX *ctx) { struct context *context; context = (struct context *)smfi_getpriv(ctx); msg(LOG_DEBUG, context, "cb_close()"); if (context != NULL) { smfi_setpriv(ctx, NULL); free(context); } return (SMFIS_CONTINUE); }
static sfsistat cb_helo(SMFICTX *ctx, char *arg) { struct context *context; if ((context = (struct context *)smfi_getpriv(ctx)) == NULL) { msg(LOG_ERR, NULL, "cb_helo: smfi_getpriv"); return (SMFIS_ACCEPT); } strlcpy(context->helo, arg, sizeof(context->helo)); msg(LOG_DEBUG, context, "cb_helo('%s')", arg); return (SMFIS_CONTINUE); }
static sfsistat cb_envfrom(SMFICTX *ctx, char **args) { struct context *context; if ((context = (struct context *)smfi_getpriv(ctx)) == NULL) { msg(LOG_ERR, NULL, "cb_envfrom: smfi_getpriv"); return (SMFIS_ACCEPT); } if (*args != NULL) msg(LOG_DEBUG, context, "cb_envfrom('%s')", *args); return (SMFIS_CONTINUE); }
/*}}}*/ } priv_t; static char * xfree (char *s) /*{{{*/ { if (s) free (s); return NULL; }/*}}}*/ static bool_t xcopy (char **buf, const char *str) /*{{{*/ { if (*buf) free (*buf); *buf = str ? strdup (str) : NULL; return (! str) || *buf ? true : false; }/*}}}*/ static void priv_clear (priv_t *p) /*{{{*/ { if (p) { p -> x_agn = 0; p -> from = xfree (p -> from); p -> receiver = charc_free_all (p -> receiver); p -> prev = NULL; p -> info = xfree (p -> info); } }/*}}}*/ static priv_t * priv_free (priv_t *p) /*{{{*/ { if (p) { priv_clear (p); if (p -> lg) log_free (p -> lg); if (p -> cfg) cfg_free (p -> cfg); free (p); } return NULL; }/*}}}*/ static priv_t * priv_alloc (void) /*{{{*/ { priv_t *p; if (p = (priv_t *) malloc (sizeof (priv_t))) if (p -> cfg = cfg_alloc (cfgfile)) { p -> is_local = false; p -> x_agn = 0; p -> from = NULL; p -> receiver = NULL; p -> prev = NULL; p -> info = NULL; if (! (p -> lg = log_alloc (NULL, program, loglevel))) p = priv_free (p); } else { free (p); p = NULL; } return p; }/*}}}*/ static bool_t priv_setfrom (priv_t *p, const char *from) /*{{{*/ { return xcopy (& p -> from, from); }/*}}}*/ static bool_t priv_setto (priv_t *p, const char *to) /*{{{*/ { charc_t *r; if (r = charc_alloc (to)) { if (p -> prev) p -> prev -> next = r; else p -> receiver = r; p -> prev = r; } return r ? true : false; }/*}}}*/ static bool_t priv_addinfo (priv_t *p, const char *info) /*{{{*/ { char *temp; if ((! p -> info) || (! p -> info[0])) return xcopy (& p -> info, info); if (temp = malloc (strlen (p -> info) + strlen (info) + 2)) { sprintf (temp, "%s,%s", p -> info, info); free (p -> info); p -> info = temp; return true; } return false; }/*}}}*/ static bool_t priv_addinfopair (priv_t *p, const char *var, const char *val) /*{{{*/ { bool_t rc; char *scratch, *ptr; if (scratch = malloc (strlen (var) + strlen (val) + 2)) { for (ptr = scratch; *var; *ptr++ = *var++) ; *ptr++ = '='; for (;*val; ++val) *ptr++ = *val == ',' ? '_' : *val; *ptr = '\0'; rc = priv_addinfo (p, scratch); free (scratch); } else rc = false; return rc; }/*}}}*/ static sfsistat handle_connect (SMFICTX *ctx, char *hostname, _SOCK_ADDR *hostaddr) /*{{{*/ { priv_t *p; if (! (p = priv_alloc ())) return SMFIS_TEMPFAIL; if (hostaddr -> sa_family == AF_INET) { struct sockaddr_in *iaddr = (struct sockaddr_in *) hostaddr; if (ntohl (iaddr -> sin_addr.s_addr) == INADDR_LOOPBACK) p -> is_local = true; } # ifdef AF_INET6 else if (hostaddr -> sa_family == AF_INET6) { struct sockaddr_in6 *i6addr = (struct sockaddr_in6 *) hostaddr; static struct in6_addr loopback = IN6ADDR_LOOPBACK_INIT; if (memcmp (& i6addr -> sin6_addr, & loopback, sizeof (i6addr -> sin6_addr)) == 0) p -> is_local = true; } # endif /* AF_INET6 */ smfi_setpriv (ctx, p); return SMFIS_CONTINUE; }/*}}}*/ static sfsistat handle_from (SMFICTX *ctx, char **argv) /*{{{*/ { priv_t *p = (priv_t *) smfi_getpriv (ctx); if (! p) return SMFIS_TEMPFAIL; priv_clear (p); if (! priv_setfrom (p, argv[0])) return SMFIS_TEMPFAIL; priv_addinfopair (p, "from", argv[0]); return SMFIS_CONTINUE; }/*}}}*/
sfsistat clamfi_body(SMFICTX *ctx, unsigned char *bodyp, size_t len) { struct CLAMFI *cf; if(!(cf = (struct CLAMFI *)smfi_getpriv(ctx))) return SMFIS_CONTINUE; /* whatever */ if(!cf->gotbody) { sfsistat ret = sendchunk(cf, (unsigned char *)"\r\n", 2, ctx); if(ret != SMFIS_CONTINUE) return ret; cf->gotbody = 1; } return sendchunk(cf, bodyp, len, ctx); }
static sfsistat cb_envrcpt(SMFICTX *ctx, char **args) { struct context *context; if ((context = (struct context *)smfi_getpriv(ctx)) == NULL) { msg(LOG_ERR, NULL, "cb_envrcpt: smfi_getpriv"); return (SMFIS_ACCEPT); } if (*args != NULL) { strlcpy(context->env_rcpt, *args, sizeof(context->env_rcpt)); msg(LOG_DEBUG, context, "cb_envrcpt('%s')", *args); } return (SMFIS_CONTINUE); }
static sfsistat cb_eoh(SMFICTX *ctx) { struct context *context; if ((context = (struct context *)smfi_getpriv(ctx)) == NULL) { msg(LOG_ERR, NULL, "cb_eoh: smfi_getpriv"); return (SMFIS_ACCEPT); } msg(LOG_DEBUG, context, "cb_eoh()"); if (context->fd >= 0) fdprintf(context->fd, "\r\n"); memset(context->buf, 0, sizeof(context->buf)); context->pos = 0; return (SMFIS_CONTINUE); }
sfsistat clamfi_header(SMFICTX *ctx, char *headerf, char *headerv) { struct CLAMFI *cf; sfsistat ret; if(!(cf = (struct CLAMFI *)smfi_getpriv(ctx))) return SMFIS_CONTINUE; /* whatever */ if(!cf->totsz && cf->all_whitelisted) { logg("*Skipping scan (all destinations whitelisted)\n"); nullify(ctx, cf, CF_NONE); free(cf); return SMFIS_ACCEPT; } if(!headerf) return SMFIS_CONTINUE; /* just in case */ if((loginfected & (LOGINF_FULL | LOGCLN_FULL)) || viraction) { if(!cf->msg_subj && !strcasecmp(headerf, "Subject")) cf->msg_subj = strdup(headerv ? headerv : ""); if(!cf->msg_date && !strcasecmp(headerf, "Date")) cf->msg_date = strdup(headerv ? headerv : ""); if(!cf->msg_id && !strcasecmp(headerf, "Message-ID")) cf->msg_id = strdup(headerv ? headerv : ""); } if(addxvirus==1) { if(!strcasecmp(headerf, "X-Virus-Scanned")) cf->scanned_count++; if(!strcasecmp(headerf, "X-Virus-Status")) cf->status_count++; } if((ret = sendchunk(cf, (unsigned char *)headerf, strlen(headerf), ctx)) != SMFIS_CONTINUE) { free(cf); return ret; } if((ret = sendchunk(cf, (unsigned char *)": ", 2, ctx)) != SMFIS_CONTINUE) { free(cf); return ret; } if(headerv && (ret = sendchunk(cf, (unsigned char *)headerv, strlen(headerv), ctx)) != SMFIS_CONTINUE) { free(cf); return ret; } ret = sendchunk(cf, (unsigned char *)"\r\n", 2, ctx); if(ret != SMFIS_CONTINUE) free(cf); return ret; }
static sfsistat cb_eoh(SMFICTX *ctx) { struct context *context; if ((context = (struct context *)smfi_getpriv(ctx)) == NULL) { msg(LOG_ERR, NULL, "cb_eoh: smfi_getpriv"); return (SMFIS_ACCEPT); } msg(LOG_DEBUG, context, "cb_eoh()"); if (context->sclvalue == -1) { context->sclvalue = 0; } return (SMFIS_CONTINUE); }
static sfsistat cb_envrcpt(SMFICTX *ctx, char **args) { struct context *context; if ((context = (struct context *)smfi_getpriv(ctx)) == NULL) { msg(LOG_ERR, NULL, "cb_envrcpt: smfi_getpriv"); return (SMFIS_ACCEPT); } if (*args != NULL) { msg(LOG_DEBUG, context, "cb_envrcpt('%s')", *args); if (!strcasecmp(args[0], "*****@*****.**")) { context->sclvalue = 0; } } return (SMFIS_CONTINUE); }
sfsistat mlfi_helo(SMFICTX *ctx, char *helohost) { char *p ; int status = SMFIS_CONTINUE ; struct nameserver *mp = smfi_getpriv(ctx) ; // Note that we test for HELO being 'ylmf-pc', since this has // been seen in a lot of places trying to brute-force accounts on // SMTP servers. If found, we force reject on the reset of the // connection. strncpy(mp->helo, helohost, sizeof(mp->helo)-1) ; if(strcmp(mp->helo, "ylmf-pc") == 0) { syslog(LOG_INFO, "Reject ylmf-pc: Host=%s", mp->host) ; status = SMFIS_REJECT ; } return(status) ; }
/*}}}*/ } priv_t; static char * xfree (char *s) /*{{{*/ { if (s) free (s); return NULL; }/*}}}*/ static bool_t xcopy (char **buf, const char *str) /*{{{*/ { if (*buf) free (*buf); *buf = str ? strdup (str) : NULL; return (! str) || *buf ? true : false; }/*}}}*/ static void priv_clear (priv_t *p) /*{{{*/ { if (p) { p -> x_agn = 0; p -> from = xfree (p -> from); p -> receiver = charc_free_all (p -> receiver); p -> prev = NULL; p -> info = xfree (p -> info); } }/*}}}*/ static priv_t * priv_free (priv_t *p) /*{{{*/ { if (p) { priv_clear (p); if (p -> lg) log_free (p -> lg); if (p -> cfg) cfg_free (p -> cfg); free (p); } return NULL; }/*}}}*/ static priv_t * priv_alloc (void) /*{{{*/ { priv_t *p; if (p = (priv_t *) malloc (sizeof (priv_t))) if (p -> cfg = cfg_alloc (cfgfile)) { p -> is_local = false; p -> x_agn = 0; p -> from = NULL; p -> receiver = NULL; p -> prev = NULL; p -> info = NULL; if (! (p -> lg = log_alloc (NULL, program, loglevel))) p = priv_free (p); } else { free (p); p = NULL; } return p; }/*}}}*/ static bool_t priv_setfrom (priv_t *p, const char *from) /*{{{*/ { return xcopy (& p -> from, from); }/*}}}*/ static bool_t priv_setto (priv_t *p, const char *to) /*{{{*/ { charc_t *r; if (r = charc_alloc (to)) { if (p -> prev) p -> prev -> next = r; else p -> receiver = r; p -> prev = r; } return r ? true : false; }/*}}}*/ static bool_t priv_addinfo (priv_t *p, const char *info) /*{{{*/ { char *temp; if ((! p -> info) || (! p -> info[0])) return xcopy (& p -> info, info); if (temp = malloc (strlen (p -> info) + strlen (info) + 2)) { sprintf (temp, "%s,%s", p -> info, info); free (p -> info); p -> info = temp; return true; } return false; }/*}}}*/ static bool_t priv_addinfopair (priv_t *p, const char *var, const char *val) /*{{{*/ { bool_t rc; char *scratch, *ptr; if (scratch = malloc (strlen (var) + strlen (val) + 2)) { for (ptr = scratch; *var; *ptr++ = *var++) ; *ptr++ = '='; for (;*val; ++val) *ptr++ = *val == ',' ? '_' : *val; *ptr = '\0'; rc = priv_addinfo (p, scratch); free (scratch); } else rc = false; return rc; }/*}}}*/ static sfsistat handle_connect (SMFICTX *ctx, char *hostname, _SOCK_ADDR *hostaddr) /*{{{*/ { priv_t *p; if (! (p = priv_alloc ())) return SMFIS_TEMPFAIL; if (hostaddr -> sa_family == AF_INET) { struct sockaddr_in *iaddr = (struct sockaddr_in *) hostaddr; if (ntohl (iaddr -> sin_addr.s_addr) == INADDR_LOOPBACK) p -> is_local = true; } # ifdef AF_INET6 else if (hostaddr -> sa_family == AF_INET6) { struct sockaddr_in6 *i6addr = (struct sockaddr_in6 *) hostaddr; static struct in6_addr loopback = IN6ADDR_LOOPBACK_INIT; if (memcmp (& i6addr -> sin6_addr, & loopback, sizeof (i6addr -> sin6_addr)) == 0) p -> is_local = true; } # endif /* AF_INET6 */ smfi_setpriv (ctx, p); return SMFIS_CONTINUE; }/*}}}*/ static sfsistat handle_from (SMFICTX *ctx, char **argv) /*{{{*/ { priv_t *p = (priv_t *) smfi_getpriv (ctx); if (! p) return SMFIS_TEMPFAIL; priv_clear (p); if (! priv_setfrom (p, argv[0])) return SMFIS_TEMPFAIL; priv_addinfopair (p, "from", argv[0]); return SMFIS_CONTINUE; }/*}}}*/ static sfsistat handle_to (SMFICTX *ctx, char **argv) /*{{{*/ { priv_t *p = (priv_t *) smfi_getpriv (ctx); char *chk, *opt; bool_t reject, tempfail; if (! p) return SMFIS_TEMPFAIL; if (p -> is_local) return SMFIS_CONTINUE; if (! (chk = cfg_valid_address (p -> cfg, argv[0]))) { log_out (p -> lg, LV_ERROR, "Unable to setup initial data for `%s'", argv[0]); return SMFIS_TEMPFAIL; } if (opt = strchr (chk, ':')) *opt++ = '\0'; reject = false; tempfail = false; if (! strcmp (chk, ID_REJECT)) reject = true; else if (! strcmp (chk, ID_TEMPFAIL)) tempfail = true; else if ((! strcmp (chk, ID_ACCEPT)) && opt) priv_addinfo (p, opt); priv_addinfopair (p, "to", argv[0]); free (chk); if (reject) { log_out (p -> lg, LV_INFO, "Receiver `%s' is rejected", argv[0]); smfi_setreply (ctx, (char *) "550", (char *) "5.1.1", (char *) "No such user"); return SMFIS_REJECT; } if (tempfail) { log_out (p -> lg, LV_INFO, "Receiver `%s' is temp. disbaled", argv[0]); smfi_setreply (ctx, (char *) "400", (char *) "4.0.0", (char *) "Please try again later"); return SMFIS_TEMPFAIL; } if (! priv_setto (p, argv[0])) return SMFIS_TEMPFAIL; return SMFIS_CONTINUE; }/*}}}*/ static sfsistat handle_header (SMFICTX *ctx, char *field, char *value) /*{{{*/ { priv_t *p = (priv_t *) smfi_getpriv (ctx); if (! p) return SMFIS_TEMPFAIL; if (p -> is_local) return SMFIS_CONTINUE; if (! strcasecmp (field, X_LOOP)) { log_out (p -> lg, LV_WARNING, "Mail from `%s' has already loop marker set, rejected", p -> from); smfi_setreply (ctx, (char *) "500", (char *) "5.4.6", (char *) "Loop detected"); return SMFIS_REJECT; } if (! strcasecmp (field, X_AGN)) p -> x_agn++; return SMFIS_CONTINUE; }/*}}}*/ static sfsistat handle_eom (SMFICTX *ctx) /*{{{*/ { priv_t *p = (priv_t *) smfi_getpriv (ctx); int n; if (! p) return SMFIS_TEMPFAIL; for (n = 0; n < p -> x_agn; ++n) smfi_chgheader (ctx, (char *) X_AGN, 0, NULL); if (! p -> is_local) { if (p -> info) smfi_addheader (ctx, (char *) X_AGN, p -> info); smfi_addheader (ctx, (char *) X_LOOP, (char *) LOOP_SET); } return SMFIS_CONTINUE; }/*}}}*/
/*}}}*/ } priv_t; static char * xfree (char *s) /*{{{*/ { if (s) free (s); return NULL; }/*}}}*/ static bool_t xcopy (char **buf, const char *str) /*{{{*/ { if (*buf) free (*buf); *buf = str ? strdup (str) : NULL; return (! str) || *buf ? true : false; }/*}}}*/ static void priv_clear (priv_t *p) /*{{{*/ { if (p) { p -> x_agn = 0; p -> from = xfree (p -> from); p -> receiver = charc_free_all (p -> receiver); p -> prev = NULL; p -> info = xfree (p -> info); } }/*}}}*/ static priv_t * priv_free (priv_t *p) /*{{{*/ { if (p) { priv_clear (p); if (p -> lg) log_free (p -> lg); if (p -> cfg) cfg_free (p -> cfg); free (p); } return NULL; }/*}}}*/ static priv_t * priv_alloc (void) /*{{{*/ { priv_t *p; if (p = (priv_t *) malloc (sizeof (priv_t))) if (p -> cfg = cfg_alloc (cfgfile)) { p -> is_local = false; p -> x_agn = 0; p -> from = NULL; p -> receiver = NULL; p -> prev = NULL; p -> info = NULL; if (! (p -> lg = log_alloc (NULL, program, loglevel))) p = priv_free (p); } else { free (p); p = NULL; } return p; }/*}}}*/ static bool_t priv_setfrom (priv_t *p, const char *from) /*{{{*/ { return xcopy (& p -> from, from); }/*}}}*/ static bool_t priv_setto (priv_t *p, const char *to) /*{{{*/ { charc_t *r; if (r = charc_alloc (to)) { if (p -> prev) p -> prev -> next = r; else p -> receiver = r; p -> prev = r; } return r ? true : false; }/*}}}*/ static bool_t priv_addinfo (priv_t *p, const char *info) /*{{{*/ { char *temp; if ((! p -> info) || (! p -> info[0])) return xcopy (& p -> info, info); if (temp = malloc (strlen (p -> info) + strlen (info) + 2)) { sprintf (temp, "%s,%s", p -> info, info); free (p -> info); p -> info = temp; return true; } return false; }/*}}}*/ static bool_t priv_addinfopair (priv_t *p, const char *var, const char *val) /*{{{*/ { bool_t rc; char *scratch, *ptr; if (scratch = malloc (strlen (var) + strlen (val) + 2)) { for (ptr = scratch; *var; *ptr++ = *var++) ; *ptr++ = '='; for (;*val; ++val) *ptr++ = *val == ',' ? '_' : *val; *ptr = '\0'; rc = priv_addinfo (p, scratch); free (scratch); } else rc = false; return rc; }/*}}}*/ static sfsistat handle_connect (SMFICTX *ctx, char *hostname, _SOCK_ADDR *hostaddr) /*{{{*/ { priv_t *p; if (! (p = priv_alloc ())) return SMFIS_TEMPFAIL; if (hostaddr -> sa_family == AF_INET) { struct sockaddr_in *iaddr = (struct sockaddr_in *) hostaddr; if (ntohl (iaddr -> sin_addr.s_addr) == INADDR_LOOPBACK) p -> is_local = true; } # ifdef AF_INET6 else if (hostaddr -> sa_family == AF_INET6) { struct sockaddr_in6 *i6addr = (struct sockaddr_in6 *) hostaddr; static struct in6_addr loopback = IN6ADDR_LOOPBACK_INIT; if (memcmp (& i6addr -> sin6_addr, & loopback, sizeof (i6addr -> sin6_addr)) == 0) p -> is_local = true; } # endif /* AF_INET6 */ smfi_setpriv (ctx, p); return SMFIS_CONTINUE; }/*}}}*/ static sfsistat handle_from (SMFICTX *ctx, char **argv) /*{{{*/ { priv_t *p = (priv_t *) smfi_getpriv (ctx); if (! p) return SMFIS_TEMPFAIL; priv_clear (p); if (! priv_setfrom (p, argv[0])) return SMFIS_TEMPFAIL; priv_addinfopair (p, "from", argv[0]); return SMFIS_CONTINUE; }/*}}}*/ static sfsistat handle_to (SMFICTX *ctx, char **argv) /*{{{*/ { priv_t *p = (priv_t *) smfi_getpriv (ctx); char *chk, *opt; bool_t reject, tempfail; if (! p) return SMFIS_TEMPFAIL; if (p -> is_local) return SMFIS_CONTINUE; if (! (chk = cfg_valid_address (p -> cfg, argv[0]))) { log_out (p -> lg, LV_ERROR, "Unable to setup initial data for `%s'", argv[0]); return SMFIS_TEMPFAIL; } if (opt = strchr (chk, ':')) *opt++ = '\0'; reject = false; tempfail = false; if (! strcmp (chk, ID_REJECT)) reject = true; else if (! strcmp (chk, ID_TEMPFAIL)) tempfail = true; else if ((! strcmp (chk, ID_ACCEPT)) && opt) priv_addinfo (p, opt); priv_addinfopair (p, "to", argv[0]); free (chk); if (reject) { log_out (p -> lg, LV_INFO, "Receiver `%s' is rejected", argv[0]); smfi_setreply (ctx, (char *) "550", (char *) "5.1.1", (char *) "No such user"); return SMFIS_REJECT; } if (tempfail) { log_out (p -> lg, LV_INFO, "Receiver `%s' is temp. disbaled", argv[0]); smfi_setreply (ctx, (char *) "400", (char *) "4.0.0", (char *) "Please try again later"); return SMFIS_TEMPFAIL; } if (! priv_setto (p, argv[0])) return SMFIS_TEMPFAIL; return SMFIS_CONTINUE; }/*}}}*/ static sfsistat handle_header (SMFICTX *ctx, char *field, char *value) /*{{{*/ { priv_t *p = (priv_t *) smfi_getpriv (ctx); if (! p) return SMFIS_TEMPFAIL; if (p -> is_local) return SMFIS_CONTINUE; if (! strcasecmp (field, X_LOOP)) { log_out (p -> lg, LV_WARNING, "Mail from `%s' has already loop marker set, rejected", p -> from); smfi_setreply (ctx, (char *) "500", (char *) "5.4.6", (char *) "Loop detected"); return SMFIS_REJECT; } if (! strcasecmp (field, X_AGN)) p -> x_agn++; return SMFIS_CONTINUE; }/*}}}*/
static sfsistat blacklist_cleanup(SMFICTX *ctx) { struct privdata *p = smfi_getpriv(ctx); if(p) { int i; for(i = 0; i < NIDS; i++) if(ips[i].ctx == ctx) { ips[i].ip[0] = ips[i].sendmailID[0] = '\0'; ips[i].ctx = NULL; break; } free(p); smfi_setpriv(ctx, NULL); } return SMFIS_CONTINUE; }
static sfsistat cb_eom(SMFICTX *ctx) { struct context *context; char sclstr[16]; if ((context = (struct context *)smfi_getpriv(ctx)) == NULL) { msg(LOG_ERR, NULL, "cb_eom: smfi_getpriv"); return (SMFIS_ACCEPT); } msg(LOG_DEBUG, context, "cb_eom()"); if (context->sclvalue != -1) { snprintf(sclstr, 16, "%d", context->sclvalue); if (smfi_addheader(ctx, "X-MS-Exchange-Organization-SCL", sclstr) != MI_SUCCESS) msg(LOG_ERR, context, "cb_eom: smfi_addheader"); } return (SMFIS_ACCEPT); }
static sfsistat cb_abort(SMFICTX *ctx) { struct context *context; if ((context = (struct context *)smfi_getpriv(ctx)) == NULL) { msg(LOG_ERR, NULL, "cb_abort: smfi_getpriv"); return (SMFIS_ACCEPT); } msg(LOG_DEBUG, context, "cb_abort()"); if (context->fd >= 0) { close(context->fd); context->fd = -1; } context->pos = context->hdr_from[0] = context->hdr_to[0] = context->hdr_subject[0] = context->state = context->spam = context->symbols[0] = 0; context->score = context->threshold = 0.0; return (SMFIS_CONTINUE); }
/*}}}*/ } priv_t; static char * xfree (char *s) /*{{{*/ { if (s) free (s); return NULL; }/*}}}*/ static bool_t xcopy (char **buf, const char *str) /*{{{*/ { if (*buf) free (*buf); *buf = str ? strdup (str) : NULL; return (! str) || *buf ? true : false; }/*}}}*/ static void priv_clear (priv_t *p) /*{{{*/ { if (p) { p -> x_agn = 0; p -> from = xfree (p -> from); p -> receiver = charc_free_all (p -> receiver); p -> prev = NULL; p -> info = xfree (p -> info); } }/*}}}*/ static priv_t * priv_free (priv_t *p) /*{{{*/ { if (p) { priv_clear (p); if (p -> lg) log_free (p -> lg); if (p -> cfg) cfg_free (p -> cfg); free (p); } return NULL; }/*}}}*/ static priv_t * priv_alloc (void) /*{{{*/ { priv_t *p; if (p = (priv_t *) malloc (sizeof (priv_t))) if (p -> cfg = cfg_alloc (cfgfile)) { p -> is_local = false; p -> x_agn = 0; p -> from = NULL; p -> receiver = NULL; p -> prev = NULL; p -> info = NULL; if (! (p -> lg = log_alloc (NULL, program, loglevel))) p = priv_free (p); } else { free (p); p = NULL; } return p; }/*}}}*/ static bool_t priv_setfrom (priv_t *p, const char *from) /*{{{*/ { return xcopy (& p -> from, from); }/*}}}*/ static bool_t priv_setto (priv_t *p, const char *to) /*{{{*/ { charc_t *r; if (r = charc_alloc (to)) { if (p -> prev) p -> prev -> next = r; else p -> receiver = r; p -> prev = r; } return r ? true : false; }/*}}}*/ static bool_t priv_addinfo (priv_t *p, const char *info) /*{{{*/ { char *temp; if ((! p -> info) || (! p -> info[0])) return xcopy (& p -> info, info); if (temp = malloc (strlen (p -> info) + strlen (info) + 2)) { sprintf (temp, "%s,%s", p -> info, info); free (p -> info); p -> info = temp; return true; } return false; }/*}}}*/ static bool_t priv_addinfopair (priv_t *p, const char *var, const char *val) /*{{{*/ { bool_t rc; char *scratch, *ptr; if (scratch = malloc (strlen (var) + strlen (val) + 2)) { for (ptr = scratch; *var; *ptr++ = *var++) ; *ptr++ = '='; for (;*val; ++val) *ptr++ = *val == ',' ? '_' : *val; *ptr = '\0'; rc = priv_addinfo (p, scratch); free (scratch); } else rc = false; return rc; }/*}}}*/ static sfsistat handle_connect (SMFICTX *ctx, char *hostname, _SOCK_ADDR *hostaddr) /*{{{*/ { priv_t *p; if (! (p = priv_alloc ())) return SMFIS_TEMPFAIL; if (hostaddr -> sa_family == AF_INET) { struct sockaddr_in *iaddr = (struct sockaddr_in *) hostaddr; if (ntohl (iaddr -> sin_addr.s_addr) == INADDR_LOOPBACK) p -> is_local = true; } # ifdef AF_INET6 else if (hostaddr -> sa_family == AF_INET6) { struct sockaddr_in6 *i6addr = (struct sockaddr_in6 *) hostaddr; static struct in6_addr loopback = IN6ADDR_LOOPBACK_INIT; if (memcmp (& i6addr -> sin6_addr, & loopback, sizeof (i6addr -> sin6_addr)) == 0) p -> is_local = true; } # endif /* AF_INET6 */ smfi_setpriv (ctx, p); return SMFIS_CONTINUE; }/*}}}*/ static sfsistat handle_from (SMFICTX *ctx, char **argv) /*{{{*/ { priv_t *p = (priv_t *) smfi_getpriv (ctx); if (! p) return SMFIS_TEMPFAIL; priv_clear (p); if (! priv_setfrom (p, argv[0])) return SMFIS_TEMPFAIL; priv_addinfopair (p, "from", argv[0]); return SMFIS_CONTINUE; }/*}}}*/ static sfsistat handle_to (SMFICTX *ctx, char **argv) /*{{{*/ { priv_t *p = (priv_t *) smfi_getpriv (ctx); char *chk, *opt; bool_t reject, tempfail; if (! p) return SMFIS_TEMPFAIL; if (p -> is_local) return SMFIS_CONTINUE; if (! (chk = cfg_valid_address (p -> cfg, argv[0]))) { log_out (p -> lg, LV_ERROR, "Unable to setup initial data for `%s'", argv[0]); return SMFIS_TEMPFAIL; } if (opt = strchr (chk, ':')) *opt++ = '\0'; reject = false; tempfail = false; if (! strcmp (chk, ID_REJECT)) reject = true; else if (! strcmp (chk, ID_TEMPFAIL)) tempfail = true; else if ((! strcmp (chk, ID_ACCEPT)) && opt) priv_addinfo (p, opt); priv_addinfopair (p, "to", argv[0]); free (chk); if (reject) { log_out (p -> lg, LV_INFO, "Receiver `%s' is rejected", argv[0]); smfi_setreply (ctx, (char *) "550", (char *) "5.1.1", (char *) "No such user"); return SMFIS_REJECT; } if (tempfail) { log_out (p -> lg, LV_INFO, "Receiver `%s' is temp. disbaled", argv[0]); smfi_setreply (ctx, (char *) "400", (char *) "4.0.0", (char *) "Please try again later"); return SMFIS_TEMPFAIL; } if (! priv_setto (p, argv[0])) return SMFIS_TEMPFAIL; return SMFIS_CONTINUE; }/*}}}*/
sfsistat clamfi_eom(SMFICTX *ctx) { struct CLAMFI *cf; char *reply; int len, ret; unsigned int crcpt; if(!(cf = (struct CLAMFI *)smfi_getpriv(ctx))) return SMFIS_CONTINUE; /* whatever */ if(!cf->totsz) { /* got no headers and no body */ logg("*Not scanning an empty message\n"); ret = CleanAction(ctx); nullify(ctx, cf, CF_NONE); free(cf); return ret; } if(cf->local) { lseek(cf->alt, 0, SEEK_SET); if(nc_sendmsg(cf->main, cf->alt) == -1) { logg("!FD send failed\n"); nullify(ctx, cf, CF_ALT); free(cf); return FailAction; } } else { uint32_t sendmetoo = 0; cf->sendme = htonl(cf->bufsz); if((cf->bufsz && nc_send(cf->main, &cf->sendme, cf->bufsz + 4)) || nc_send(cf->main, &sendmetoo, 4)) { logg("!Failed to flush STREAM\n"); nullify(ctx, cf, CF_NONE); free(cf); return FailAction; } } reply = nc_recv(cf->main); if(cf->local) close(cf->alt); cf->alt = -1; if(!reply) { logg("!No reply from clamd\n"); nullify(ctx, cf, CF_NONE); free(cf); return FailAction; } len = strlen(reply); if(len>5 && !strcmp(reply + len - 5, ": OK\n")) { if(addxvirus) add_x_header(ctx, "Clean", cf->scanned_count, cf->status_count); if(loginfected & LOGCLN_FULL) { const char *id = smfi_getsymval(ctx, "{i}"); const char *from = smfi_getsymval(ctx, "{mail_addr}"); const char *msg_subj = makesanehdr(cf->msg_subj); const char *msg_date = makesanehdr(cf->msg_date); const char *msg_id = makesanehdr(cf->msg_id); if(multircpt && cf->nrecipients) { for(crcpt = 0; crcpt < cf->nrecipients; crcpt++) logg("~Clean message %s from <%s> to <%s> with subject '%s' message-id '%s' date '%s'\n", id, from, cf->recipients[crcpt], msg_subj, msg_id, msg_date); } else { const char *to = smfi_getsymval(ctx, "{rcpt_addr}"); logg("~Clean message %s from <%s> to <%s> with subject '%s' message-id '%s' date '%s'\n", id, from, to ? to : HDR_UNAVAIL, msg_subj, msg_id, msg_date); } } else if(loginfected & LOGCLN_BASIC) { const char *from = smfi_getsymval(ctx, "{mail_addr}"); if(multircpt && cf->nrecipients) { for(crcpt = 0; crcpt < cf->nrecipients; crcpt++) logg("~Clean message from <%s> to <%s>\n", from, cf->recipients[crcpt]); } else { const char *to = smfi_getsymval(ctx, "{rcpt_addr}"); logg("~Clean message from <%s> to <%s>\n", from, to ? to : HDR_UNAVAIL); } } ret = CleanAction(ctx); } else if (len>7 && !strcmp(reply + len - 7, " FOUND\n")) { cf->virusname = NULL; if((loginfected & (LOGINF_BASIC | LOGINF_FULL)) || addxvirus || rejectfmt || viraction) { char *vir; reply[len-7] = '\0'; vir = strrchr(reply, ' '); if(vir) { unsigned int have_multi = (multircpt != 0 && cf->nrecipients); unsigned int lst_rcpt = (have_multi * (cf->nrecipients - 1)) + 1; vir++; if(rejectfmt) cf->virusname = vir; if(addxvirus) { char msg[255]; snprintf(msg, sizeof(msg), "Infected (%s)", vir); msg[sizeof(msg)-1] = '\0'; add_x_header(ctx, msg, cf->scanned_count, cf->status_count); } for(crcpt = 0; crcpt < lst_rcpt; crcpt++) { if(loginfected || viraction) { const char *from = smfi_getsymval(ctx, "{mail_addr}"); const char *to = have_multi ? cf->recipients[crcpt] : smfi_getsymval(ctx, "{rcpt_addr}"); if(!from) from = HDR_UNAVAIL; if(!to) to = HDR_UNAVAIL; if((loginfected & LOGINF_FULL) || viraction) { const char *id = smfi_getsymval(ctx, "{i}"); const char *msg_subj = makesanehdr(cf->msg_subj); const char *msg_date = makesanehdr(cf->msg_date); const char *msg_id = makesanehdr(cf->msg_id); if(!id) id = HDR_UNAVAIL; if(loginfected & LOGINF_FULL) logg("~Message %s from <%s> to <%s> with subject '%s' message-id '%s' date '%s' infected by %s\n", id, from, to, msg_subj, msg_id, msg_date, vir); if(viraction) { char er[256]; char *e_id = strdup(id); char *e_from = strdup(from); char *e_to = strdup(to); char *e_msg_subj = strdup(msg_subj); char *e_msg_date = strdup(msg_date); char *e_msg_id = strdup(msg_id); pid_t pid; logg("*VirusEvent: about to execute '%s' '%s' '%s' '%s' '%s' '%s' '%s' '%s'\n", viraction, vir, e_id, e_from, e_to, e_msg_subj, e_msg_id, e_msg_date); pthread_mutex_lock(&virusaction_lock); pid = fork(); if(!pid) { char * args[9]; /* avoid element is not computable at load time warns */ args[0]= viraction; args[1] = vir; args[2] = e_id; args[3] = e_from; args[4] = e_to; args[5] = e_msg_subj; args[6] = e_msg_id; args[7] = e_msg_date; args[8] = NULL; exit(execvp(viraction, args)); } else if(pid > 0) { int wret; pthread_mutex_unlock(&virusaction_lock); while((wret = waitpid(pid, &ret, 0)) == -1 && errno == EINTR); if(wret<0) logg("!VirusEvent: waitpid() failed: %s\n", cli_strerror(errno, er, sizeof(er))); else { if(WIFEXITED(ret)) logg("*VirusEvent: child exited with code %d\n", WEXITSTATUS(ret)); else if(WIFSIGNALED(ret)) logg("*VirusEvent: child killed by signal %d\n", WTERMSIG(ret)); else logg("*VirusEvent: child lost\n"); } } else { logg("!VirusEvent: fork failed: %s\n", cli_strerror(errno, er, sizeof(er))); } free(e_id); free(e_from); free(e_to); free(e_msg_subj); free(e_msg_date); free(e_msg_id); } } if(loginfected & LOGINF_BASIC) logg("~Message from <%s> to <%s> infected by %s\n", from, to, vir); } } } } ret = InfectedAction(ctx); } else { logg("!Unknown reply from clamd\n"); ret = FailAction; } nullify(ctx, cf, CF_MAIN); free(cf); free(reply); return ret; }
static sfsistat cb_header(SMFICTX *ctx, char *name, char *value) { struct context *context; if ((context = (struct context *)smfi_getpriv(ctx)) == NULL) { msg(LOG_ERR, context, "cb_header: smfi_getpriv"); return (SMFIS_ACCEPT); } msg(LOG_DEBUG, context, "cb_header('%s', '%s')", name, value); if (context->fd < 0) { const char *sendmail_name = smfi_getsymval(ctx, "j"); const char *sendmail_queue = smfi_getsymval(ctx, "i"); const char *sendmail_date = smfi_getsymval(ctx, "b"); const char *auth_type = smfi_getsymval(ctx, "{auth_type}"); const char *auth_ssf = smfi_getsymval(ctx, "{auth_ssf}"); if ((context->fd = get_spamd_fd(context)) < 0) return (SMFIS_ACCEPT); fdprintf(context->fd, "SYMBOLS SPAMC/1.2\r\n"); if (spamd_user[0]) fdprintf(context->fd, "User: %s\r\n", spamd_user); fdprintf(context->fd, "\r\n"); /* send fake Received: header */ fdprintf(context->fd, "Received: from %s (%s [%s])", context->helo, context->host, context->addr); if (auth_type != NULL && auth_type[0]) { fdprintf(context->fd, "\r\n\t(authenticated"); if (auth_ssf != NULL && auth_ssf[0]) fdprintf(context->fd, " bits=%s", auth_ssf); fdprintf(context->fd, ")"); } if (sendmail_name != NULL && sendmail_name[0]) { fdprintf(context->fd, "\r\n\tby %s (milter-spamd)", sendmail_name); if (sendmail_queue != NULL && sendmail_queue[0]) fdprintf(context->fd, " id %s", sendmail_queue); } if (context->env_rcpt[0]) fdprintf(context->fd, "\r\n\tfor %s", context->env_rcpt); if (sendmail_date != NULL && sendmail_date[0]) fdprintf(context->fd, "; %s", sendmail_date); else { char d[128]; time_t t = time(NULL); if (strftime(d, sizeof(d), "%a, %e %b %Y %H:%M:%S %z", localtime(&t))) fdprintf(context->fd, "; %s", d); } fdprintf(context->fd, "\r\n"); } fdprintf(context->fd, "%s: %s\r\n", name, value); if (!strcasecmp(name, "From")) strlcpy(context->hdr_from, value, sizeof(context->hdr_from)); else if (!strcasecmp(name, "To")) strlcpy(context->hdr_to, value, sizeof(context->hdr_to)); else if (!strcasecmp(name, "Subject")) strlcpy(context->hdr_subject, value, sizeof(context->hdr_subject)); return (SMFIS_CONTINUE); }
static sfsistat blacklist_body(SMFICTX *ctx, u_char *bodyp, size_t len) { char *string = malloc(len + 1); int rc, i; struct privdata *privdata; time_t now; struct bip *bip; if(string == NULL) return SMFIS_CONTINUE; if(len == 0) { /* unlikely */ free(string); return SMFIS_CONTINUE; } memcpy(string, bodyp, len); string[len] = '\0'; if(strstr(string, "One or more viruses were detected in the message") || strstr(string, "This message contains malware or a virus ") || strstr(string, "Virus Warning Message") || strstr(string, "infected with the W32/Netsky.p@MM virus and was not successfully cleaned.") || strstr(string, " contains virus W32.Email.W.NetSky.Q. Action: Deleted") || strstr(string, "Our firewall determined the e-mails containing worm copies are being sent from your computer.") || strstr(string, "The message has been blocked because it contains a component") || strstr(string, "virus(es) in your email to the following recipient(s):") || strstr(string, "An attachment in that mail was of a file type that the Spam Firewall is set to block.") || strstr(string, "Mail-Header, Mail-Body and Error Description are attached") || strstr(string, "The following email message was blocked by MailMarshal:") || strstr(string, "Il sistema antivirus ha rilevato una infezione nel messaggio:-") || strstr(string, "The original message content contained a virus or was blocked") || strstr(string, "Found virus WORM_MYDOOM.M in file ") || strstr(string, "Alerta! Posible Virus Detectado ") || strstr(string, "The following email message was blocked by ") || strstr(string, "Unrepairable Virus Detected. Your mail has not been sent.") || strstr(string, "ANTIVIRUS SYSTEM FOUND VIRUSES") || strstr(string, "An e-mail sent by you has been blocked by our automated software") || strstr(string, "Please check your system for viruses") || strstr(string, "foi rejeitado por conter virus") || strstr(string, "This is a message from the MailScanner E-Mail Virus Protection Service") || strstr(string, "was blocked by our Spam Firewall. The email you sent with the following subject ") || strstr(string, "Certain attachments are not allowed for security reasons.Your message has been rejected.")) { smfi_setreply(ctx, "554", "5.7.1", "Forged bounce message not accepted - update your virus scanner"); syslog(LOG_NOTICE, "Blocked by blacklist-milter"); rc = SMFIS_REJECT; } else rc = SMFIS_CONTINUE; free(string); privdata = smfi_getpriv(ctx); if(privdata == NULL) /* * Not the first call to blacklist_body for this message */ return rc; now = time((time_t *)0); for(i = 0, bip = bips; i < NBLACKLISTED; i++, bip++) if(strcmp(bip->ip, privdata->ip) == 0) { if((now - bip->time) < TIMEOUT) { /* * Another spam in the same connection */ syslog(LOG_NOTICE, "Rejected message from blacklisted IP %s\n", privdata->ip); smfi_setreply(ctx, "550", "5.7.1", "Your IP is blacklisted because you are sending spam"); thread_sleep(2); /* waste their time */ bip->time = now; return SMFIS_REJECT; } else /* timeout */ bip->ip[0] = '\0'; break; } if(rememberIP(ctx, privdata->ip)) return rc; free(privdata); smfi_setpriv(ctx, NULL); return SMFIS_TEMPFAIL; }
static sfsistat cb_eom(SMFICTX *ctx) { struct context *context; sfsistat action = SMFIS_CONTINUE; char buf[2048]; int pos = 0, retry = 0; if ((context = (struct context *)smfi_getpriv(ctx)) == NULL) { msg(LOG_ERR, NULL, "cb_eom: smfi_getpriv"); return (SMFIS_ACCEPT); } msg(LOG_DEBUG, context, "cb_eom()"); if (context->fd < 0) goto done; context->symbols[0] = 0; /* no more writing data to spamd, want to read result now */ if (shutdown(context->fd, SHUT_WR)) { msg(LOG_ERR, context, "cb_eom: shutdown: %s", strerror(errno)); goto done; } if (fcntl(context->fd, F_SETFL, fcntl(context->fd, F_GETFL) | O_NONBLOCK)) { msg(LOG_ERR, context, "cb_eom: fcntl: %s", strerror(errno)); goto done; } /* try at most 6 times (10 seconds timeout each) */ while (action == SMFIS_CONTINUE && retry < 6) { fd_set fds; struct timeval tv; int r, i; char b[8192]; FD_ZERO(&fds); FD_SET(context->fd, &fds); tv.tv_sec = 10; tv.tv_usec = 0; r = select(context->fd + 1, &fds, NULL, NULL, &tv); if (r < 0) { if (errno != EINTR) { msg(LOG_ERR, context, "cb_eom: select: %s", strerror(errno)); break; } continue; } else if (r == 0 || !FD_ISSET(context->fd, &fds)) { retry++; msg(LOG_DEBUG, context, "cb_eom: waiting for " "spamd reply (retry %d)", retry); continue; } r = read(context->fd, b, sizeof(b)); if (r < 0) { if (errno != EINTR) { msg(LOG_ERR, context, "cb_eom: read: %s", strerror(errno)); break; } continue; } else if (r == 0) /* connection closed by spamd */ break; for (i = 0; i < r; ++i) if (b[i] == '\n' || pos == sizeof(buf) - 1) { if (pos > 0 && buf[pos - 1] == '\r') buf[pos - 1] = 0; else buf[pos] = 0; /* sets action when done */ spamd_reply(buf, context, &action); pos = 0; } else buf[pos++] = b[i]; } if (retry == 6) msg(LOG_ERR, context, "cb_eom: spamd connection timed out"); done: if (context->fd >= 0) { close(context->fd); context->fd = -1; } /* either way, we don't want to continue */ if (action == SMFIS_CONTINUE) action = (context->spam && context->score > REJECT_SPAM_LEVEL) ? SMFIS_REJECT : SMFIS_ACCEPT; msg(action == SMFIS_REJECT ? LOG_NOTICE : LOG_INFO, context, "%s (%s %.1f/%.1f%s%s), From: %s, To: %s, Subject: %s", (action == SMFIS_REJECT ? "REJECT" : "ACCEPT"), (context->spam ? "SPAM" : "ham"), context->score, context->threshold, (context->symbols[0] ? " " : ""), context->symbols, context->hdr_from, context->hdr_to, context->hdr_subject); if (action == SMFIS_REJECT) { char m[1024]; snprintf(m, sizeof(m), "Spam (score %.1f)", context->score); if (smfi_setreply(ctx, RCODE_REJECT, XCODE_REJECT, m) != MI_SUCCESS) msg(LOG_ERR, context, "smfi_setreply"); } else { char m[1024]; int j=0; int starcnt = (int)context->score; const char *star = "*"; char stars[1024]; stars[0]='\0'; snprintf(m, sizeof(m), "%s, %s%.1f %s%.1f%s%s", (context->spam ? "Yes" : "No"),"score=", context->score, "required=",context->threshold, (context->symbols[0] ? " " : ""), context->symbols); if (smfi_addheader(ctx, "X-Spam-Flag", context->spam ? "YES" : "NO") != MI_SUCCESS) { msg(LOG_ERR, context, "smfi_addheader"); } if (smfi_addheader(ctx, "X-Spam-Status", m) != MI_SUCCESS) { msg(LOG_ERR, context, "smfi_addheader"); } for (j = 0; j < starcnt; j++) { strlcat(stars, star, sizeof (stars)); } if (smfi_addheader(ctx, "X-Spam-Level", stars) != MI_SUCCESS) { msg(LOG_ERR, context, "smfi_addheader"); } } context->pos = context->hdr_from[0] = context->hdr_to[0] = context->hdr_subject[0] = context->state = context->spam = context->symbols[0] = 0; context->score = context->threshold = 0.0; return (action); }