/*- * Copyright (c) 2004 Free (Olivier Beyssac) * 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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 #include #include #include #include #include #include #include #include #include #include #include #include #include "options.h" #include "cmd.h" #include "iptree.h" #include "ipinfo.h" #include "utils.h" #include "netlist.h" #include "client.h" #define MSG_BL (421) #define MSG_NOBL (200) #define MSG_AUTHF (600) #define exiterror(ident, svc, svl, buf) \ do { \ syslog(LOG_ERR, "invalid request from %s: %s", ident, buf); \ restorebuf(svc, svl, buf); \ return 500; \ } while(0) extern struct options opt; /* Send notifications */ static void send_notifies(const char *ip) { int i; pid_t pid; struct sigaction sa; if (!opt.notify_hosts) return; if ((pid = fork()) == -1) { syslog(LOG_ERR, "fork() error: %s", strerror(errno)); return; } if (pid != 0) return; /* Detach from previous sigactions */ xsigaction(sa, SIGTERM, SIG_DFL); xsigaction(sa, SIGINT, SIG_DFL); xsigaction(sa, SIGUSR1, SIG_IGN); xsigaction(sa, SIGUSR2, SIG_IGN); xsigaction(sa, SIGCHLD, SIG_IGN); for (i = 0; opt.notify_hosts[i]; i += 2) { int sd, reply; char *host = opt.notify_hosts[i]; char *port = opt.notify_hosts[i+1]; opt.notifies++; if ((sd = client_connect(host, port)) == -1) { syslog(LOG_ERR, "error (connect) while notifying %s to %s:%s", ip, host, port); } else { if ((reply = client_send_cmdi(sd, CMD_INSERT, ip)) != 200 && reply != 421) syslog(LOG_ERR, "error (reply=%d) while notifying %s to %s:%s", reply, ip, host, port); else syslog(LOG_INFO, "notified %s:%s about %s", host, port, ip); close(sd); } } exit(EXIT_SUCCESS); } /* Do the real stuff after command has been interpreted by read_cmd */ static int cmd_commit(const iptree ipt, const int cmd, const char *val, const char *ident) { struct in_addr inp; unsigned long ip; time_t t; int options = 0; int bl = 0; int notify = 0; int code = 200; int wl = 0; int count = 1; t = time(NULL); if (!inet_aton(val, &inp)) { syslog(LOG_ERR, "invalid IP address submitted by %s: %s", ident, val); return 500; } ip = ntohl(inp.s_addr); if (iptree_get_bl(ipt, ip, t)) { bl = 1; code = MSG_BL; } /* Check against whitelist */ wl = (netlist_getmode(opt.whitelist, ip) == 1); if (cmd == CMD_DECR) { opt.decrqueries++; if (wl) { syslog(LOG_INFO, "%s (wl) decremented by %s", val, ident); return code; } count = -1; } else if (cmd == CMD_INSERT) { opt.insertqueries++; options |= IPINFO_OPT_FORCED; if (wl) { syslog(LOG_INFO, "%s (wl) inserted by %s", val, ident); return code; } } else if (cmd == CMD_SUBMIT) { opt.submissions++; if (wl) { syslog(LOG_INFO, "%s (wl) submitted by %s", val, ident); return code; } } switch (cmd) { case CMD_DECR: /* Decrement IP count */ case CMD_INSERT: /* IP insertion */ case CMD_SUBMIT: /* IP submission */ if (iptree_add(ipt, ip, t, t, count, options)) { if (cmd == CMD_SUBMIT) { syslog(LOG_INFO, "%s submitted by %s", val, ident); if (iptree_get_bl(ipt, ip, t)) { notify = 1; } } else if (cmd == CMD_DECR) { syslog(LOG_INFO, "%s decremented by %s", val, ident); } else if (cmd == CMD_INSERT && !bl) { syslog(LOG_INFO, "%s inserted by %s", val, ident); notify = 1; } /* If the IP has just been put in the blacklist, send notifies */ if (!bl && notify) { send_notifies(val); code = MSG_BL; } } else syslog(LOG_ERR, "error in iptree_add IP %s", val); break; case CMD_QUERY: /* Check if an IP is blacklisted or not */ opt.blqueries++; if (bl) { opt.positive_blqueries++; return MSG_BL; } else return MSG_NOBL; break; } return code; } /* Read the command pointed to by cmd and update iptree Return the code to show to client */ extern int read_cmd(char *buf, const ssize_t len, const char *ident, const iptree ipt, const int mode) { char *p; char svc, svctmp; ssize_t svl; int cmd = CMD_NO; int code; if (buf[len-1] == '\n' && buf[len-2] == '\r') savebufpos(svc, svl, buf, len-2); else { savebufpos(svc, svl, buf, len); exiterror(ident, svc, svl, buf); } if ((p = strchr(buf, '=')) == NULL) exiterror(ident, svc, svl, buf); svctmp = *p; *p = '\0'; if (strcmp(buf, "ip") == 0) cmd = CMD_SUBMIT; else if (strcmp(buf, "ip?") == 0) cmd = CMD_QUERY; else if (strcmp(buf, "ipbl") == 0) cmd = CMD_INSERT; else if (strcmp(buf, "ipdecr") == 0) cmd = CMD_DECR; else { opt.bad_requests++; } *p++ = svctmp; /* Check client request against ACL */ if ((cmd == CMD_SUBMIT && !(mode & ACL_M_SUBMIT)) || (cmd == CMD_QUERY && !(mode & ACL_M_QUERY)) || (cmd == CMD_INSERT && !(mode & ACL_M_INSERT)) || (cmd == CMD_DECR && !(mode & ACL_M_DECR))) { syslog(LOG_INFO, "denied request from %s", ident); opt.denied_requests++; restorebuf(svc, svl, buf); return MSG_AUTHF; } if (cmd == CMD_NO || (code = cmd_commit(ipt, cmd, p, ident)) == 0) exiterror(ident, svc, svl, buf); restorebuf(svc, svl, buf); return code; }