/* * Copyright (c) 2000-2007, OpenFWTK Development Group * All rights reserved. See LICENSE. */ /* * OpenFWTK project legacy API functions * * (C) Copyright 2000-2002 ArkanoiD * (C) Copyright 2000 Eberhard Mattes * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "firewall.h" #include "fwstats.h" #include "fwfunc.h" #include "auth.h" #include "magic.h" magic_t proxy_magic; extern uint32_t arc4random(void); extern void addlist(char*,char***); extern void freelist(char***); extern size_t strlcpy(char*,const char*,size_t); extern int proxy_conf_int(Cfg*,const char*,int,int,int); extern char* proxy_conf_string(Cfg*,const char*); static sig_atomic_t hup_flag; extern void setproctitle(const char*,...); extern int proxy_nodns; #ifndef TCSASOFT #define TCSASOFT 0 #endif #ifndef INADDR_NONE #define INADDR_NONE (in_addr_t) -1 #endif static char* moduleId ATTR_UNUSED = "$Id: misc.c,v 1.28 2007/09/10 02:49:13 arkenoi Exp $"; struct fwstats proxy_stats = {0,0,0,0,0,"","","unauth","",0,""}; char proxy_name[MAXPATHLEN]; char proxy_chroot[MAXPATHLEN]; int proxy_uid = -1; int proxy_gid = -1; Cfg *proxy_confp; int proxy_timeout = PROXY_TIMEOUT; int proxy_nodns = 0; int namatch(const char *const,const char *const); void* xmalloc(size_t size) { void *p; if ((p = malloc(size))) return(p); syslog(LLEV,"fwtksyserr: malloc()"); exit(1); } void* xrealloc(void *ptr,size_t size) { void *p; if ((p = realloc(ptr,size))) return(p); syslog(LLEV,"fwtksyserr: malloc()"); exit(1); } char* xstrndup (const char *s, size_t len) { char *p = xmalloc (len + 1); memcpy (p, s, len); p[len] = 0; return p; } char* xstrdup(const char *str) { char *p; if ((p = strdup(str))) return(p); syslog(LLEV,"fwtksyserr: malloc()"); exit(1); } int isalldigits(char *s) { if (*s == '\0') return(0); while (isdigit(*s)) s++; return(*s == '\0'); } long randomnumber() { /* We assume the library will do all dirty work for us */ return((long)arc4random()); } char * getpassword(prompt) const char *prompt; { struct termios term; register int ch; register char *p; FILE *fp, *outfp; int echo; static char buf[AUTH_PWSIZ + 1]; sigset_t oset, nset; /* * read and write to /dev/tty if possible; else read from * stdin and write to stderr. */ /* Comented out for legacy login-sh compatibility */ outfp = stderr; fp = stdin; /* * note - blocking signals isn't necessarily the * right thing, but we leave it for now. */ sigemptyset(&nset); sigaddset(&nset, SIGINT); sigaddset(&nset, SIGTSTP); (void)sigprocmask(SIG_BLOCK, &nset, &oset); (void)tcgetattr(fileno(fp), &term); if ((echo = (term.c_lflag & ECHO))) { term.c_lflag &= ~ECHO; (void)tcsetattr(fileno(fp), TCSAFLUSH|TCSASOFT, &term); } (void)fputs(prompt, outfp); rewind(outfp); /* implied flush */ for (p = buf; (ch = getc(fp)) != EOF && ch != '\n';) if (p < buf + AUTH_PWSIZ) *p++ = ch; *p = '\0'; (void)write(fileno(outfp), "\n", 1); if (echo) { term.c_lflag |= ECHO; (void)tcsetattr(fileno(fp), TCSAFLUSH|TCSASOFT, &term); } (void)sigprocmask(SIG_SETMASK, &oset, NULL); if (fp != stdin) (void)fclose(fp); return(buf); } int matchip(char *pattern,char *string) { char patbuf[MAX_HOSTNAME]; char *mask; uint32_t patip,patmask,hostip; if (strchr(pattern,'*')) return(namatch(pattern,string)); if (strlen(pattern) >= sizeof(patbuf)) { syslog(LLEV,"fwtkcfgerr: matchip(): pattern %.100s too long",pattern); return(0); } strlcpy(patbuf,pattern,sizeof(patbuf)); if ((mask = strpbrk (patbuf, "/:"))) { *mask = '\0'; if ( (patmask = inet_addr(++mask)) == INADDR_NONE) { syslog(LLEV,"fwtkcfgerr: matchip(): malformed netmask: %.100s",mask); return(0); } } else patmask = 0xffffffff; if ( (patip = inet_addr(patbuf)) == INADDR_NONE) { syslog(LLEV,"fwtkcfgerr: matchip(): malformed pattern: %.100s",patbuf); return(0); } if ( (hostip = inet_addr(string)) == INADDR_NONE) { syslog(LLEV,"fwtkcfgerr: matchip(): malformed ip: %.100s",patbuf); return(0); } return ((hostip & patmask) == patip); } int maskmatch(char *pattern,char *string) { return(matchip(pattern,string)); } int checkreverse(const struct in_addr haddr, const char *const rname) { struct hostent *hp; int isok; unsigned char *c; char **tp; char *name; name = xstrdup(rname); /* * we could rely on (isascii && isalpha) || isdigit, but.. */ isok = 0; for (c= (unsigned char*) name ; *c ; c++) { if ( ((*c < 'a') || (*c > 'z')) && ((*c < 'A') || (*c > 'Z')) && ((*c < '0') || (*c > '9')) && (*c!= '.') && (*c!= '-') ) { syslog(LLEV,"securityalert: %.512s/%.20s invalid host name",name,inet_ntoa(haddr)); free(name); return(0); } isok |= !isdigit(*c) && (*c!= '.'); } /* * Will be detected later anyways, but let's be paranoid */ if (!isok) { syslog(LLEV,"securityalert: possible spoof %.512s/%.20s can appear as fake IP",name,inet_ntoa(haddr)); free(name); return(0); } if (!(hp = gethostbyname(name))) { syslog(LLEV,"%.512s/%.20s host name lookup failed",name,inet_ntoa(haddr)); free(name); return(0); } if(hp->h_length > sizeof(haddr.s_addr)) { syslog(LLEV,"securityalert: invalid host address length (%d) hostname %.512s",hp->h_length, name); free(name); return(0); } tp = hp->h_addr_list; for ( ; *(tp) ; tp++ ) { if (!memcmp(*(tp),(char *)&haddr,hp->h_length)) { char *x; for(x = name; *x != '\0'; x++) *x = tolower(*x); free(name); return(1); } } syslog(LLEV,"securityalert: possible spoof %.512s/%.20s != %.512s name lookup mismatch",name,inet_ntoa(haddr),inet_ntoa(*(struct in_addr *)*(hp->h_addr_list))); free(name); return(0); } char* ip2name(struct in_addr haddr) { static char name[MAX_HOSTNAME]; struct hostent *hp; bzero(name,sizeof(name)); if (proxy_nodns) { strlcpy(name,"unknown",sizeof(name)); return(name); } hp = gethostbyaddr((char *)&haddr,sizeof(haddr),AF_INET); if (!hp) { syslog(LLEV,"%.512s host name lookup failed",inet_ntoa(haddr)); strlcpy(name,"unknown",sizeof(name)); return(name); } if (strlen(hp->h_name) >= sizeof(name)) { syslog(LLEV,"securityalert: %.512s/%.20s host name buffer overrun",hp->h_name,inet_ntoa(haddr)); strlcpy(name,"unknown",sizeof(name)); return(name); } if (!checkreverse(haddr,hp->h_name)) { strlcpy(name,"unknown",sizeof(name)); return(name); } strlcpy(name,hp->h_name,sizeof(name)); return(name); } int matchpatbyreverse(char *pattern, struct in_addr haddr) { struct hostent *hp; char **aliases = NULL; register char **ap; if (proxy_nodns || !(hp = gethostbyaddr((char *)&haddr,sizeof(haddr),AF_INET))) return(0); for (ap = hp->h_aliases; ap && *ap && **ap; ap++) addlist(*ap,&aliases); if (checkreverse(haddr,xstrdup(hp->h_name)) && namatch(pattern,hp->h_name)) { return(1); } else { for (ap = aliases; ap && *ap && **ap; ap++) { if (checkreverse(haddr,*ap) && namatch(pattern,*ap)) { freelist(&aliases); return(1); } } } freelist(&aliases); return(0); } int matchnamebyip(char *pattern,char *name,int nsize) { struct hostent *hp; hp = gethostbyname(name); if (!hp) return(0); if(hp->h_length > sizeof(uint32_t)) { syslog(LLEV,"securityalert: invalid host address length (%d) hostname %.512s",hp->h_length, name); strlcpy(name,"unknown",MAX_HOSTNAME); return(0); } for ( ; *(hp->h_addr_list) ; hp->h_addr_list++ ) if (matchip(pattern,inet_ntoa(*((struct in_addr *)*(hp->h_addr_list))))) return(1); return(0); } int hostmatch(char *pattern,char *name) { char patbuf[MAX_HOSTNAME]; char namebuf[MAX_HOSTNAME]; unsigned char *c; int nname,npat; if (strlen(pattern) >= sizeof(patbuf)) { syslog(LLEV,"fwtkcfgerr: hostmatch(): pattern %.100s too long",pattern); return(0); } if (strlen(name) >= sizeof(namebuf)) { syslog(LLEV,"fwtksyserr: hostmatch(): name %.100s too long",name); return(0); } strlcpy(patbuf,pattern,sizeof(patbuf)); strlcpy(namebuf,name,sizeof(namebuf)); npat = 0; for (c = (unsigned char*) patbuf; *c; c++) { *c = tolower(*c); npat |= (!isdigit(*c) && (*c != '.') && (*c != '*') && (*c != ':') && (*c != '/')); } nname = 0; for (c = (unsigned char*) namebuf; *c; c++) { *c = tolower(*c); nname |= (!isdigit(*c) && (*c != '.')); } if (npat) { if (!nname) { struct in_addr haddr; if (( haddr.s_addr = inet_addr(name)) == INADDR_NONE) { syslog(LLEV,"hostmatch: malformed address: %.100s",name); return(0); } return(matchpatbyreverse(patbuf,haddr)); } return(namatch(patbuf,namebuf)); } else { if (nname) return(matchnamebyip(patbuf,namebuf,sizeof(namebuf))); return(matchip(patbuf,namebuf)); } } int set_oob_notification(int fd) { if(fcntl(fd,F_SETOWN,getpid()) == -1) { syslog(LLEV,"fwtksyserr: F_SETOWN: %s", strerror(errno)); return(1); } return(0); } int daemonize() { signal(SIGALRM,SIG_IGN); setsid(); switch(fork()) { case -1: syslog(LLEV,"fwtksyserr: fork failed"); exit(1); case 0: setsid(); syslog(LLEV,"daemon running"); return(0); default: exit(0); } } void waitwaitwait(int unused) { int status; while (waitpid(-1,&status,WNOHANG) > 0) proxy_stats.child_count--; signal(SIGCHLD, waitwaitwait); } void hup_exit(int sig) { syslog(LLEV,"caught signal %d",sig); exit(1); } void hup_catch(int sig) { if (sig == SIGHUP) hup_flag = 1; } int do_daemon(int port) { int s; int csock; struct sockaddr_in sa; int optval; daemonize(); signal(SIGTERM, hup_exit); signal(SIGINT, hup_exit); signal(SIGHUP, hup_catch); setproctitle("MASTER DAEMON port=%d", port); if ((s = socket(AF_INET,SOCK_STREAM,0)) <0) { syslog(LLEV,"fwtksyserr: socket: %s", strerror(errno)); exit(1); } optval = 1; setsockopt(s,SOL_SOCKET,SO_REUSEADDR,&optval,sizeof(optval)); memset(&sa,0,sizeof(sa)); sa.sin_family = AF_INET; sa.sin_port = htons(port); while (bind(s,(struct sockaddr *)&sa,sizeof(sa))) { syslog(LLEV,"fwtksyserr: failed to bind port %d, %s", port, strerror(errno)); syslog(LLEV,"Sleeping for 2 minutes"); setproctitle("MASTER DAEMON (inactive - cannot bind %d)",port); sleep(120); } setproctitle("MASTER DAEMON port=%d", port); if (listen(s,SOMAXCONN) < 0) { syslog(LLEV,"fwtksyserr: listen: %s", strerror(errno)); exit(1); } signal(SIGCHLD,waitwaitwait); while (1) { if (hup_flag) { syslog(LLEV,"Got SIGHUP, reconfiguring"); cfg_free(proxy_confp); if ((proxy_confp = cfg_read(proxy_name)) == (Cfg *)-1) { syslog(LLEV,"fwtkcfgerr: no netperm-table records for %.128s",proxy_name); exit(1); } proxy_timeout = proxy_conf_int(proxy_confp,"timeout",1,INT_MAX,PROXY_TIMEOUT); proxy_stats.child_limit = proxy_conf_int(proxy_confp,"maxchildren",1,4096,0); proxy_nodns = (proxy_conf_string(proxy_confp,"nodns") != NULL); magic_close(proxy_magic); proxy_magic = magic_open(MAGIC_MIME); magic_load(proxy_magic,NULL); syslog(LLEV,"Reconfiguration finished"); signal(SIGHUP, hup_catch); hup_flag = 0; } socklen_t salen = sizeof(sa); siginterrupt(SIGHUP,1); if ((csock = accept(s,(struct sockaddr *)&sa,&salen)) < 0) { siginterrupt(SIGHUP,0); if( errno == EINTR) { hup_flag = 1; continue; } syslog(LLEV,"fwtksyserr: accept: %s", strerror(errno)); exit(1); } siginterrupt(SIGHUP,0); if ((proxy_stats.child_limit) && (proxy_stats.child_count > proxy_stats.child_limit)) { syslog(LLEV,"deny host=%.100s/%.100s use of gateway: child limit exceeded",ip2name(sa.sin_addr),inet_ntoa(sa.sin_addr)); close(csock); continue; } switch(fork()) { case -1: syslog(LLEV,"fwtksyserr: fork failed"); close(csock); continue; case 0: /* we may need to fork further, should we count? let's not to. */ setsid(); /* do like inetd */ /* we disabled signals, but let's check */ if (close(s) || close(0) || close(1) || close(2)) { syslog(LLEV,"fwtksyserr: close() failed"); exit(1); } dup(csock); dup(csock); dup(csock); if (close(csock)) { syslog(LLEV,"fwtksyserr: close() failed"); exit(1); } signal(SIGHUP, hup_exit); return(0); default: proxy_stats.child_count++; close(csock); } } } void strtrm(char *s) { char *p; if(s == NULL) return; for(p=s;isspace(*p) && p != '\0';p++) ; memmove(s, p, strlen(p) + 1); for(p=s + strlen(s) - 1; p>=s && isspace(*p) ; p--) ; *(p+1) = '\0'; }