/* mytcpwrap.c 功能:类似著名的 tcpwrap 根据配置文件,允许某些地址的用户进行登陆连接 除可记录登陆时间外,还可以记录退出时间 本程序应由超级服务器inetd启动 使用:替换/etc/inetd.conf 中要进行控制的端口 以telnet、ftp端口控制为例: /etc/inetd.conf 原内容:
ftp stream tcp nowait root /etc/ftpd ftpd telnet stream tcp nowait NOLUID /etc/telnetd telnetd
改为:
ftp stream tcp nowait root /etc/mytcpwrap /etc/ftpd telnet stream tcp nowait NOLUID /etc/mytcpwrap /etc/telnetd
注意:改完后记得通知一下inetd进程 */ #include "stdio.h" #include "errno.h" #include "time.h" #include "signal.h"
#include #include #include #include #include "sys/stat.h" #include "sys/times.h"
#define INET_ADDRESTRLEN 18 #define MAXSOCKADDR 64
/* 地址配置文件 格式 port ips '#' 打头表示本行为注释 'S' 打头表示网络日志记录地,格式为 S port ips 缺省为 5678 1.1.1.1 接受通配符 * 其中 port 记录本地端口,可以使用通配符 * 表示任意端口 ips 允许访问的远程IP地址,形如 nnn.nnn.nnn.nnn 可以使用通配符 * 表示任意地址 例: 21 1.1.1.1 表示1.1.1.1的机器可以访问本机21(ftp)端口 * 2.2.2.2 表示2.2.2.2的机器可以访问本机任意端口 23 3.3.3.* 表示3.3.3.*网段的机器可以访问本机23(telnet)端口 */ #define INIFILE "/etc/mytcpwrap.conf" #define LOGFILE "/usr/adm/mytcpwrap.log"
short R_PORT=5678; char R_ADDR[16]="1.1.1.1";
/* 简单字符串比较函数 */ int cj_Compare(char *so, char *sd) { char *to,*td; int done,cjOk=0,i,k;
if (*sd==0) return 0; for (done=0,to=so,td=sd;!done; ) { if (*td=='?') { td++; to++; } else if (*td=='*') { if (cj_Compare(to,(td+1))==0) return 0; else to++; } else { cjOk = done = *to - *td; td++; to++; } if (*to==0) for ( (*td=='?')||(*td=='*');td++); if ((*to==0)&&(*td==0)) break; else if (*to==0) done = cjOk = -1; else if (*td==0) done = cjOk = -1; } return cjOk; }
int is_denied(char *s, int lport) { int k, ct; char buf[128], ips[32], pts[8]; FILE *fp;
/* 读配置文件 */ fp = fopen(INIFILE, "rt"); if (fp){ memset(buf, 0, sizeof(buf)); fgets(buf, sizeof(buf)-1, fp); while (!feof(fp)){ memset(pts, 0, sizeof(pts)); memset(ips, 0, sizeof(ips)); sscanf(buf, "%5s%15s", pts, ips); if (pts[0]!='#'){ if (pts[0]!='S' && pts[0]!='s'){ /* 去除末尾单独的'.' */ while (ips[strlen(ips)-1]=='.') ips[strlen(ips)-1]=0; for (ct=k=0;k if (ips[k]=='.') ct++; /* 补齐 nnn.nnn.nnn.nnn 的格式 */ while (ct<3){ strcat(ips, ".*"); ct++; } /* 端口号是否一致或是任意端口 */ if (pts[0]=='*' || atoi(pts)==lport){ /* 比较IP地址是否满足匹配规则 */ if (cj_Compare(s, ips)==0){ fclose(fp); /* 返回成功--接受登录 */ return 0; } } } else{ /* 读网络日志服务器地址、端口 */ memset(pts, 0, sizeof(pts)); memset(ips, 0, sizeof(ips)); sscanf(buf, "%*s%5s%15s", pts, R_ADDR); R_PORT = atoi(pts); } } memset(buf, 0, sizeof(buf)); fgets(buf, sizeof(buf)-1, fp); } fclose(fp); } /* 未找到匹配记录,拒绝登录 */ return 1; }
char *inet_ntop(int f, void *a, char *s, int l) { u_char *p=(u_char *)a; struct in_addr inv; char tt[INET_ADDRESTRLEN];
if (f==AF_INET){ snprintf(tt, sizeof(tt), "%d.%d.%d.%d",p[0], p[1], p[2], p[3]); if (strlen(tt)>l){ errno = ENOSPC; return NULL; } strcpy(s, tt); return tt; } errno = EAFNOSUPPORT; return NULL; }
sock_ntop(struct sockaddr * sa, int len, char *s) { char str[128]; int pt=-1; struct sockaddr_in *sin=(struct sockaddr_in *)sa;
s[0] = 0; switch(sa->sa_family){ case AF_INET:if (inet_ntop(AF_INET, &sin->sin_addr, str, sizeof(str))==NULL) return NULL; pt = ntohs(sin->sin_port); strcpy(s, str); break; }; return pt; }
get_time(int t[6]) { struct tm *tm; time_t n;
n = time(NULL); tm = localtime(&n); t[0] = tm->tm_year + 1900; t[1] = tm->tm_mon+1; t[2] = tm->tm_mday; t[3] = tm->tm_hour; t[4] = tm->tm_min; t[5] = tm->tm_sec; }
long rowid=0; /* 将登录信息记录到网络上日志服务器上 通过两次提交记录完整的登陆、退出时间 第一次记录登录时间 发送格式: NETLOG|本地服务端口|客户地址|端口|成功标志| 服务端返回一个该记录的rowid值 第二次记录离开时间 发送格式: NETUPD|原记录的rowid */ int net_logit(int lport, char *ips, int rport, int succ) { int i,s; struct sockaddr_in sin; struct hostent *hp; char buf[128], ss[64];
if (R_PORT==0 || strlen(R_ADDR)==0) return -1; bzero((char *)&sin,sizeof(sin)); sin.sin_family = AF_INET; /* 取日志服务器地址 */ hp = gethostbyname(R_ADDR); if (hp!=NULL) bcopy(hp->h_addr, (char *)&sin.sin_addr, hp->h_length); else inet_aton(R_ADDR, &sin.sin_addr); sin.sin_port = htons(R_PORT); s = socket(AF_INET, SOCK_STREAM, 0); if (s<0) return 1; /* 连接日志服务器 */ if ((i=connect(s, (struct sockaddr *)&sin,sizeof(sin)))<0){ close(s); return 2; } /* 发送日志信息 格式: NETLOG|本地服务端口|客户地址|端口|成功标志| */ /* 第一次记录登录时间 */ if (rowid==0) sprintf(ss, "NETLOG|%d|%s|%d|%d|", lport, ips, rport, succ); else /* 第二次记录离开时间,发送信息格式: NETUPD|原记录的rowid */ sprintf(ss, "NETUPD|%d|", rowid); sprintf(buf, "%04d%s", strlen(ss), ss); write(s, buf, strlen(buf)); memset(ss, 0, sizeof(ss)); if (rowid==0){ /* 取该记录的rowid */ read(s, ss, sizeof(ss)-1); rowid = atol(ss); } /* 关闭连接 */ shutdown(s, 2); close(s); return 0; }
void mysig(int sig) { return }
main(int argc, char *argv[], char *envp[]) { int srq[6], i, ct, k, rport, lport; unsigned int len; char buf[128], ips[32]; FILE *fp; union{ struct sockaddr sa; char data[MAXSOCKADDR]; }from;
/* 取本地端口 */ len = sizeof(from); k = getsockname(1, (struct sockaddr *)&from.data, &len); lport = sock_ntop(&from.sa, len, ips); /* 取远程端口、地址 */ len = sizeof(from); k = getpeername(1, (struct sockaddr *)&from.data, &len); rport = sock_ntop(&from.sa, len, ips); get_time(srq); sprintf(buf, "端口 %d, 来自 %15s:%d, 时间 %04d/%02d/%02d %02d:%02d:%02d ", lport, ips, rport, srq[0], srq[1], srq[2], srq[3], srq[4], srq[5]); fp = fopen(LOGFILE, "at"); if (fp!=NULL) fprintf(fp, "%s", buf); k = is_denied(ips, lport); /* 记录登录时间 */ net_logit(lport, ips, rport, k); if (k){ fprintf(stderr,"对不起,您没有在此主机登录的权限!\007\r\n"); if (fp!=NULL){ fprintf(fp, "...拒绝[%d]!\n", rowid); fclose(fp); } exit(1); } if (fp){ fprintf(fp, "...接受[%s][%d]!\n", argv[0], rowid); fclose(fp); } sigset(SIGCLD, mysig); /* 子进程启动相应程序,父进程等待 */ if (fork()==0) execve(argv[0], argv, envp); else pause(); /* 记录退出登录的时间 */ net_logit(lport, ips, rport, k); return 0; }
| | |