/* * ilist.cpp * * (C) 1998-1999 Murat Deligonul * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * 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., 675 Mass Ave, Cambridge, MA 02139, USA. * * --- * NOTES/FIXME: */ #include "autoconf.h" #include #include #include #include #include #include #include #include #ifdef HAVE_PWD_H #include #endif #include "ilist.h" extern int gettok(const char *, char *, unsigned long, char, int, bool = 0); extern int safe_strcpy(char *, const char *, unsigned long ); extern int fdprintf(int fd, const char *, ...); /* * Check /etc/passwd if this is a valid user. Perhaps there * is an easier way to do this, but I don't know it. * Also.. some compilers may need this prototype right before it (?) */ static bool is_real_user(const char *); static bool is_real_user(const char * user) { #ifdef HAVE_PWD_H if (getpwnam(user) != 0) return 1; #endif if (!strcasecmp(user, "root")) return 1; return 0; } /* * age = how long fake ident requests will live. * Use 0 for no limit. */ ilist::ilist(time_t age) : max_age(age) { num = 0; } /* * The destructor for linkedlist will kill the all nodes it has * allocated but is it up to us to free the data pointed to by them. */ ilist::~ilist(void) { destroy_list(&ident_list, 0); } /* * Call select on our socket and accept any connections if needed. * Read data from the connection. * * Return: 1) success 0) Nothing to get -1) Error * * We do not close the file descriptor */ int ilist::read_from(int fd) { char buff[64], ports[5+5+2], ident[15], local_port[6], other_port[6]; unsigned long numbytes; if ((numbytes = read(fd, buff, sizeof(buff))) <= 0) return (0); buff[numbytes] = 0; /* (breath) If.. */ if (!gettok(buff, ports, sizeof(ports), ' ', 1) /* missing port info */ || !gettok(buff, ident, sizeof(ident), ' ', 2) /* or missing ident info */ || !gettok(ports, local_port, sizeof(local_port), ',', 1) /* or missing one port */ || !gettok(ports, other_port, sizeof(other_port), ',', 2) /* or the other port */ || atoi(local_port) == 0 /* or the port is bad */ || atoi(other_port) == 0) /* or the other port is bad */ /* incomplete or bad request; bail out */ { write(fd, "INVAL", 12); syslog(LOG_ERR, "ilist: bad request '%s'", buff); return 0; } switch (add(ident, (unsigned short) atoi(local_port), (unsigned short) atoi(other_port))) { case -2: write(fd, "AUTH", 4); syslog(LOG_ERR, "ilist: unauthorized fake ident request (rejected): %s , %s : %s", local_port, other_port, ident); return -1; case 0: write(fd, "ERROR", 5); syslog(LOG_ERR, "ilist: error adding to fake ident list"); return -1; case -1: write(fd, "EXISTS", 6); syslog(LOG_ERR, "ilist: attempted to add dup %s , %s : %s", local_port, other_port, ident); return -1; } write(fd, "OK", 3); syslog(LOG_INFO, "ilist: fake ident registered: %s , %s %s", local_port, other_port, ident); return 1; } bool ilist::lookup(unsigned short port1, unsigned short port2, char * buff, unsigned long buffsize) { ident_info * i = internal_lookup(port1, port2, buff,0); if (i) { if (buff) safe_strcpy(buff, i->user, buffsize); return 1; } return 0; } /* * * Return: * 1 - Success * -1 - Exists * -2 - bad request (like root or something) * 0 - Other error * */ int ilist::add(const char * ident, unsigned short port1, unsigned short port2) { /* Check for dup */ if (lookup(port1, port2, NULL, 666)) return -1; /* * Check for bad idents like 'root'. * FIXME: should this be case insensitive? */ if (strcmp(ident, "root") == 0) return -2; extern bool no_real; /* Blah! */ if (no_real && is_real_user(ident)) return -2; ident_info * pi = new ident_info; safe_strcpy(pi->user, ident, sizeof(pi->user)); pi->port1 = port1; pi->port2 = port2; pi->time = time(NULL); ident_list.add(pi); num++; return 1; } /* * Kill all ident entries which have exceeded the time limit */ bool ilist::kill_expired(time_t now) { if (max_age) { list_iterator i(&ident_list); ident_info * pi; while (i.has_next()) { pi = i.next(); if (now - pi->time >= max_age) { syslog(LOG_INFO, "DEBUG: Deleting expired (%lu secs) fake ident request %d , %d : %s", max_age, pi->port1, pi->port2, pi->user); delete pi; i.remove(); num--; } } } return 1; } bool ilist::remove(unsigned short p1, unsigned short p2, const char * ident) { ident_info * i = internal_lookup(p1,p2,ident,1); if (!i) return 0; delete i; ident_list.remove(i); num--; syslog(LOG_INFO, "DEBUG: ...removed"); return 1; } /* * A unified lookup routine for both remove() and lookup() * * the match_ident thing is a small hack that is needed for lookup(). * It only needs to check the ports, and passes a pointer to a string * full of crap so there is no way in hell it will match. */ ilist::ident_info * ilist::internal_lookup( unsigned short port1, unsigned short port2, const char * ident, bool match_ident) { list_iterator x(&ident_list); ident_info * i; while (x.has_next()) { i = x.next(); if (i->port1 == port1 && i->port2 == port2 && (!match_ident || strcasecmp(ident, i->user) == 0)) return i; } return 0; }