/* ** oidentd_masq.c - oidentd IP masquerading handler. ** Copyright (C) 1998-2006 Ryan McCabe ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License, version 2, ** as published by the 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_LIBUDB # include #endif #include #include #include #include #include #include struct sockaddr_storage proxy; #ifdef MASQ_SUPPORT static sigjmp_buf timebuf; static int fsock; in_port_t fwdport; extern char *ret_os; static void fwd_alarm(int sig) __noreturn; static bool blank_line(const char *buf); /* ** Returns true if the buffer contains only ** blank characters (spaces and/or tabs). Returns ** false otherwise. */ static bool blank_line(const char *buf) { const char *p; for (p = buf ; *p != '\0' ; p++) { if (*p != ' ' && *p != '\t') return (false); } return (true); } /* ** Parse the masquerading map file. ** Returns 0 on success, -1 on failure. */ int find_masq_entry(struct sockaddr_storage *host, char *user, size_t user_len, char *os, size_t os_len) { FILE *fp; struct sockaddr_storage addr; u_int32_t line_num; char buf[4096]; #ifdef HAVE_LIBUDB if (opt_enabled(USEUDB)) { struct udb_ip_user ibuf; struct sockaddr_storage hostaddr; char ipbuf[MAX_IPLEN]; memcpy(&hostaddr, host, sizeof(hostaddr)); get_ip(&hostaddr, ipbuf, sizeof(ipbuf)); debug("[%s] UDB lookup...", ipbuf); if (udb_ip_get(SIN4(&hostaddr), &ibuf)) { get_ip(&hostaddr, ipbuf, sizeof(ipbuf)); xstrncpy(user, ibuf.username, user_len); xstrncpy(os, ret_os, os_len); o_log(NORMAL, "Successful UDB lookup: %s : %s", ipbuf, user); return (0); } } #endif fp = fopen(MASQ_MAP, "r"); if (fp == NULL) { if (errno != EEXIST) debug("fopen: %s: %s", MASQ_MAP, strerror(errno)); return (-1); } line_num = 0; while (fgets(buf, sizeof(buf), fp) != NULL) { struct sockaddr_storage stemp; char *p, *temp; ++line_num; p = strchr(buf, '\n'); if (p == NULL) { debug("[%s:%d] Long line", MASQ_MAP, line_num); goto failure; } *p = '\0'; if (buf[0] == '#') continue; if (blank_line(buf) == true) continue; p = strchr(buf, '\r'); if (p != NULL) *p = '\0'; p = strtok(buf, " \t"); if (p == NULL) { debug("[%s:%d] Missing address parameter", MASQ_MAP, line_num); goto failure; } temp = strchr(p, '/'); if (temp != NULL) *temp++ = '\0'; if (get_addr(p, &stemp) == -1) { debug("[%s:%d] Invalid address: %s", MASQ_MAP, line_num, p); goto failure; } sin_copy(&addr, &stemp); if (stemp.ss_family == AF_INET && temp != NULL) { in_addr_t mask, mask2; char *end; mask = strtoul(temp, &end, 10); if (*end != '\0') { if (get_addr(temp, &stemp) == -1) { debug("[%s:%d] Invalid address: %s", MASQ_MAP, line_num, temp); goto failure; } mask2 = SIN4(&stemp)->sin_addr.s_addr; } else { if (mask < 1 || mask > 31) { debug("[%s:%d] Invalid mask: %s", MASQ_MAP, line_num, temp); goto failure; } mask2 = htonl(~((1 << (32 - mask)) - 1)); } SIN4(&addr)->sin_addr.s_addr &= mask2; SIN4(host)->sin_addr.s_addr &= mask2; } if (sin_equal(&addr, host) == false) continue; p = strtok(NULL, " \t"); if (p == NULL) { debug("[%s:%d] Missing user parameter", MASQ_MAP, line_num); goto failure; } if (strlen(p) >= user_len) { debug("[%s:%d] username too long (limit is %d)", MASQ_MAP, line_num, user_len); goto failure; } xstrncpy(user, p, user_len); p = strtok(NULL, " \t"); if (p == NULL) { debug("[%s:%d] Missing OS parameter", MASQ_MAP, line_num); goto failure; } if (strlen(p) >= os_len) { debug("[%s:%d] OS name too long (limit is %d)", MASQ_MAP, line_num, os_len); goto failure; } xstrncpy(os, p, os_len); fclose(fp); return (0); } failure: fclose(fp); return (-1); } /* ** Forward an ident request to another machine, return the response to the ** client that has connected to us and requested it. */ int fwd_request( int sock, in_port_t real_lport, in_port_t masq_lport, in_port_t real_fport, struct sockaddr_storage *mrelay) { char ipbuf[MAX_IPLEN]; char user[512]; char buf[1024]; fsock = socket(mrelay->ss_family, SOCK_STREAM, 0); if (fsock == -1) { debug("socket: %s", strerror(errno)); return (-1); } sin_set_port(fwdport, mrelay); if (sigsetjmp(timebuf, 1) != 0) { debug("sigsetjmp: %s", strerror(errno)); return (-1); } signal(SIGALRM, fwd_alarm); /* ** Five seconds should be plenty, seeing as we're forwarding to a machine ** on a local network. */ alarm(5); if (connect(fsock, (struct sockaddr *) mrelay, sin_len(mrelay)) != 0) { get_ip(mrelay, ipbuf, sizeof(ipbuf)); debug("connect to %s:%d: %s", ipbuf, ntohs(sin_port(mrelay)), strerror(errno)); goto out_fail; } if (sockprintf(fsock, "%d , %d\r\n", masq_lport, real_fport) < 1) { debug("write: %s", strerror(errno)); goto out_fail; } if (sock_read(fsock, buf, sizeof(buf)) < 1) { debug("read(%d): %s\n", fsock, strerror(errno)); goto out_fail; } /* ** We don't want to timeout once we're finished processing the request. */ alarm(0); close(fsock); get_ip(mrelay, ipbuf, sizeof(ipbuf)); if (sscanf(buf, "%*d , %*d : USERID :%*[^:]:%511s", user) != 1) { char *p = strchr(buf, '\r'); if (p != NULL) *p = '\0'; debug("[%s] Forwarding response: \"%s\"", ipbuf, buf); return (-1); } sockprintf(sock, "%d , %d : USERID : %s : %s\r\n", real_lport, real_fport, ret_os, user); o_log(NORMAL, "[%s] Successful lookup (by forward): %d (%d) , %d : %s", ipbuf, real_lport, masq_lport, real_fport, user); return (0); out_fail: alarm(0); close(fsock); return (-1); } /* ** Handle the timeout of a forward request. */ static void fwd_alarm(int sig) { o_log(NORMAL, "Forward timed out"); close(fsock); siglongjmp(timebuf, sig); } #else /* ** Define a stub masq function. */ int masq( int sock __notused, in_port_t lport __notused, in_port_t fport __notused, struct sockaddr_storage *local __notused, struct sockaddr_storage *remote __notused) { return (-1); } #endif