/* * Copyright (c) 1998-2007, OpenFWTK Development Group * All rights reserved. See LICENSE. */ /* * rexec-gw.c, a Remote Exec proxy designed to work with TIS fwtk * * (C) Copyright 1998-2000 by ArkanoiD * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef SIOCATMARK #include #endif #include "firewall.h" #include "fwfunc.h" #include "firewall2.h" #include "auth.h" static char* moduleId ATTR_UNUSED = "$Id: rexec-gw.c,v 1.11 2007/09/10 02:49:13 arkenoi Exp $"; extern int h_errno; #define OP_DENY 001 #define OP_LOG 002 #define OP_AUTH 004 /* never used here */ #define OP_XTND 010 static time_t offtime; static int transparent = 0; static int extendperm = 0; static int log = 0; /* client stdout fd is our fd 0 */ static int c_stderr = -1; /* client stderr fd */ static int s_stdout = -1; /* server stdout fd */ static int s_stderr = -1; /* server stderr fd */ static Cfg *confp; static char *rhost; static char **validests = (char **)0; static char **validusers = (char **)0; static char **authusers = (char **)0; static char uname[512]; static char upass[512]; static int cdscp = 0; static int sdscp = 0; static int user_limit = AUTH_USIZ; static int pass_limit = AUTH_PWSIZ; static int cmd_limit = 255; static char separator[2] = "@"; static char cmd[8192]; static fwparm options[] = { { FWPARM_LIST, "-dest", (char*) &validests }, { FWPARM_LIST, "-user", (char*) &validusers }, { FWPARM_LIST, "-ausers", (char*) &authusers }, { FWPARM_STRING, "-plug-to", proxy_stats.dst }, { FWPARM_PORT , "-port", (char*)&proxy_stats.port}, { FWPARM_STRING, "-authuser", proxy_stats.authuser }, { FWPARM_BOOL, "-transparent", (char*) &transparent }, { FWPARM_BOOL, "-extnd", (char*) &extendperm }, { FWPARM_CHAR, "-separator", separator }, { FWPARM_BOOL, "-log", (char*) &log }, { FWPARM_DSCP, "-client-dscp", (char*) &cdscp }, { FWPARM_DSCP, "-server-dscp", (char*) &sdscp }, { 0, 0, 0 } }; extern int auth_perm(Cfg*,char*,char*,char*,char**); #if !defined(linux) && !defined(SOLARIS) && !defined(HPUX) /* * Assume we are running on BSD-like system, otherwise rexec() is * declared in */ extern int rexec(char**,int,char*,char*,char*,int*); #endif #ifndef SCO5 static void trap_sigurg(int dummy) { int atmark; char buf[512]; char c; while(1) { if(ioctl(s_stdout,SIOCATMARK,(char *)&atmark) < 0) return; if(atmark) break; read(s_stdout,buf,sizeof(buf)); } if(recv(s_stdout,(char *)&c,sizeof(c),MSG_OOB) < 0) return; send(0,(char *)&c,sizeof(c),MSG_OOB); signal(SIGURG,trap_sigurg); } #endif void rsh_error(dest,text) char *dest; char *text; { write(0,"\0x01",1); write(0,text,strlen(text)); proxy_exit(); } void get_asciiz(buf,maxbytes) char *buf; int maxbytes; { while(1) { maxbytes--; if (soread(0,buf,1) != 1) { syslog(LLEV,"protocol failure in circuit setup; error reading client data"); proxy_exit(); } if (maxbytes < 0) { syslog(LLEV,"fwtksyserr: buffer overrun on client data"); rsh_error("none","protocol entry too long\r\n"); } if (*buf == '\0') break; buf++; } return; } int main(int argc, char *argv[]) { char buffer[1024 *4]; uint16_t x; socklen_t l; struct sockaddr_in c_addr; char *p; struct timeval timo; fd_set rset; static char tokbuf[1024]; static char *tokav[56]; int tokac; int opt = 1; proxy_init(argc,argv); proxy_stats.port = 512; user_limit = proxy_conf_int(proxy_confp,"user-limit",1, sizeof(uname)-1,user_limit); pass_limit = proxy_conf_int(proxy_confp,"password-limit",1, sizeof(upass)-1,pass_limit); cmd_limit = proxy_conf_int(proxy_confp,"command-limit",1, sizeof(cmd)-1,user_limit); if (cdscp) proxy_set_dscp(0,cdscp); if ((confp=proxy_conf_hosts(proxy_confp,proxy_stats.rladdr, proxy_stats.riaddr))) proxy_parse_options(confp,options); else proxy_exit(); memset(buffer,0,sizeof(buffer)); l = sizeof(c_addr); if (getpeername(0, (struct sockaddr *)&c_addr, &l) < 0) { syslog(LOG_ERR, "fwtksyserr: getpeername(): %s", strerror(errno)); exit(1); } if ((x=ntohs(c_addr.sin_port)) < 1024) { syslog(LLEV,"deny host=%.512s/%.20s connect from privileged port %d",proxy_stats.rladdr,proxy_stats.riaddr,x); exit(1); } /* Get remote stderr and dup it to ours, we will send all diagnostic messages there. If there is none, keep our old fd 2 (should be the same as 0) */ #ifdef SO_KEEPALIVE setsockopt(0,SOL_SOCKET,SO_KEEPALIVE,(char *)&opt,sizeof(opt)); #endif get_asciiz(buffer,16); if (buffer[0] != '\0') { x = atoi(buffer); if (x < 1024) { syslog(LLEV,"securityalert: back connection requested to privileged port %d",x); proxy_exit(); } if (x < 512) { syslog(LLEV,"securityalert: back connection requested to reserved port %d",x); proxy_exit(); } proxy_update_operation("CALLBACK"); if ((c_stderr = conn_server(proxy_stats.riaddr,x,0,(char *)0)) <0) { syslog(LLEV,"protocol failure in circuit setup: cannot estabilish back connection to client"); proxy_exit(); } if (cdscp) proxy_set_dscp(c_stderr,cdscp); close(2); dup(c_stderr); } proxy_update_operation("AUTHENTICATION"); get_asciiz(uname,user_limit); get_asciiz(upass,pass_limit); get_asciiz(cmd,cmd_limit); tokac = enargv(cmd,tokav,56,tokbuf,sizeof(tokbuf)); if (strrchr(tokav[0],'/')) strlcpy(buffer,strrchr(tokav[0],'/')+1,sizeof(buffer)); else strlcpy(buffer,tokav[0],sizeof(buffer)); if (!*proxy_stats.dst) { if (transparent) proxy_get_transparent_dst(proxy_stats.dst,&proxy_stats.port); else if ((p = strrchr(uname,*separator))) { *p++ = '\0'; if (*p == '\0') { syslog(LLEV,"NULL destination"); rsh_error("none","NULL destination\r\n"); } else strlcpy(proxy_stats.dst,p,sizeof(proxy_stats.dst)); } else { syslog(LLEV,"permission denied to run command on firewall"); rsh_error("none","Permission denied.\r\n"); } } if (searchlist(uname,validusers)) { syslog(LLEV,"deny host=%.512s/%.20s access for user %s", proxy_stats.rladdr,proxy_stats.riaddr,uname); rsh_error("none","Permission denied.\r\n"); } if (proxy_check_dest(validests,extendperm)) rsh_error(proxy_stats.dst,"Permission denied.\r\n"); rhost=proxy_stats.dst; if (extendperm) { char *nav[3] = { uname, cmd, NULL }; switch (auth_perm(confp, proxy_stats.authuser, "rexec-gw", proxy_stats.dst, nav )) { case -1: syslog(LLEV,"no match for rexec-gw user=%.100s in netperm-table",proxy_stats.authuser); proxy_exit(); case 1: syslog(LLEV,"deny host=%.256s/%.20s connect to %.256s user=%.64s cmd=\"%.256s\"",proxy_stats.rladdr,proxy_stats.riaddr,proxy_stats.dst,proxy_stats.authuser,cmd); exit(1); } } if (log) syslog(LLEV,"%.256s/%.20s: %.64s@%.64s: %.64s", proxy_stats.rladdr,proxy_stats.riaddr, uname, proxy_stats.dst,cmd); proxy_update_operation(cmd); if ((s_stdout = rexec(&rhost,proxy_stats.port,uname,upass,cmd, c_stderr!=-1 ? &s_stderr : 0)) < 0) { syslog(LLEV,"rexec failed"); proxy_exit(); } if (sdscp) { proxy_set_dscp(s_stdout,sdscp); if (s_stderr != -1) proxy_set_dscp(s_stderr,sdscp); } proxy_chroot_setugid(); write(0,"\0",1); setsockopt(s_stdout,SOL_SOCKET,SO_OOBINLINE,(char *) &opt,sizeof(opt)); #ifndef SCO5 signal(SIGURG,trap_sigurg); #endif set_oob_notification(s_stdout); while (1) { int n; FD_ZERO(&rset); FD_SET(0,&rset); FD_SET(s_stdout,&rset); if (s_stderr != -1) FD_SET(s_stderr,&rset); if (c_stderr != -1) FD_SET(c_stderr,&rset); timo.tv_sec = proxy_timeout; timo.tv_usec = 0; if ((n=select(32,&rset,NULL,NULL,&timo)) < 0) { if (errno != EBADF && errno != EINTR) syslog(LLEV,"fwtksyserr: select: %s", strerror(errno)); exit(1); } else { if(n == 0) { syslog(LLEV,"Network timeout signal after %d seconds",proxy_timeout); proxy_exit(); } } if (FD_ISSET(0,&rset)) { /* * we are going to be truly bidirectional here? * linux manual says it does not work, but it does.. */ if((n = read(0,buffer,sizeof(buffer))) <= 0) break; if(write(s_stdout,buffer,n) != n) break; proxy_stats.outbytes += n; } if (FD_ISSET(s_stdout,&rset)) { if((n = read(s_stdout,buffer,sizeof(buffer))) <= 0) break; if(write(0,buffer,n) != n) break; proxy_stats.inbytes += n; } if (FD_ISSET(c_stderr,&rset)) { /* * said to be useful */ if((n = read(c_stderr,buffer,sizeof(buffer))) <= 0) break; if(write(s_stderr,buffer,n) != n) break; proxy_stats.outbytes += n; } if (FD_ISSET(s_stderr,&rset)) { /* * we do not terminate if server closes stderr connection; * there could still be some data on the master socket */ if((n = read(s_stderr,buffer,sizeof(buffer))) > 0) { if(write(c_stderr,buffer,n) != n) break; proxy_stats.inbytes += n; } } } time(&offtime); syslog(LLEV,"exit host=%.512s/%.20s dest=%.128s in=%d out=%d user=%.64s duration=%u cmd=\"%.256s\"", proxy_stats.rladdr,proxy_stats.riaddr,proxy_stats.dst,proxy_stats.inbytes,proxy_stats.outbytes,proxy_stats.authuser, (int) (offtime - proxy_stats.start_time),cmd); exit(0); }