*** smap.c Thu May 29 14:11:41 1997 --- smapx.c Wed Aug 6 13:06:56 1997 *************** *** 9,15 **** /* * Author: Marcus J. Ranum, Trusted Information Systems, Inc. */ ! static char RcsId[] = "$Header: /homes/amd001/fwtk-2.0/fwtk/smap/RCS/smap.c,v 1.19 1997/01/18 20:40:53 rick Exp $"; #include --- 9,15 ---- /* * Author: Marcus J. Ranum, Trusted Information Systems, Inc. */ ! static char RcsId[] = "$Header: /usr/local/src/fwtk-2.0/fwtk/smap/RCS/smapx.c,v 1.16 1997/08/06 03:36:45 root Exp $"; #include *************** *** 24,33 **** --- 24,38 ---- #include #include #include + #include + #include + #include #include #include "firewall.h" + #include + /* smap - sendmail wrapper. *************** *** 47,53 **** --- 52,63 ---- mjr. 1993 */ + /* some changes made by Andrew Dunstan, Mayne Nickless Ltd ITS, 29 May 1997 */ + /* basically these are to prevent third party relaying, plus a few other */ + /* minor things */ + extern char *strtok(); + extern char *arpadate(); extern char *optarg; *************** *** 79,85 **** --- 89,102 ---- static void add_file_list(); static void smap_exit(); static void waitwaitwait(); + static int check_net(); + static int check_mask(); + static int check_valid_recipient(); + static int check_valid_sender(); + /* from the sendmail bat book examples */ + #define NO_RELAY_ERR 513 + struct file_list { struct file_list *next; char *tmpfile; *************** *** 108,122 **** char buf[BUFSIZ * 4]; char myhostname[512]; char *p; - int x; char opt; FILE *vout = (FILE *)0; int gotdata = 0; struct hostent *hp; #ifndef LOG_DAEMON openlog("smap",LOG_PID); #else openlog("smap",LOG_PID|LOG_NDELAY,LFAC); #endif --- 125,144 ---- char buf[BUFSIZ * 4]; char myhostname[512]; char *p; char opt; + int iopt; FILE *vout = (FILE *)0; int gotdata = 0; struct hostent *hp; + int connector_is_local; #ifndef LOG_DAEMON openlog("smap",LOG_PID); #else + #ifdef LFAC + #undef LFAC + #define LFAC LOG_MAIL + #endif openlog("smap",LOG_PID|LOG_NDELAY,LFAC); #endif *************** *** 125,138 **** --- 147,189 ---- struct sockaddr_in sa; int pid; + #ifndef TESTMODE + daemonize(); + #else + #ifndef NO_PRECONFIG + #define NO_PRECONFIG 1 + #endif + cfg_setpfile("/tmp/netperm-table"); + #endif + #ifndef NO_PRECONFIG + if((cfp = cfg_read("smap")) == (Cfg *)-1) + { + #ifdef TESTMODE + printf("smap: Cannot open config file!\n"); + fflush(stdout); + #endif + exit(1); + } + #endif + sa.sin_family = AF_INET; bzero((char *)&sa.sin_addr, sizeof(sa.sin_addr)); + #ifndef TESTMODE sa.sin_port = htons(25); + #else + sa.sin_port = htons(1079); + #endif sock = socket(AF_INET, SOCK_STREAM, 0); if (sock < 0) { syslog(LLEV, "fwtksyserr: Failed to create socket: %m"); exit(1); } + #ifdef SO_REUSEADDR + iopt=1; + if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, + (char *)&iopt, sizeof(iopt)) <0) + syslog(LLEV,"fwtksyserr: setsockopt SO_REUSEADDR failed %m"); + #endif if (bind(sock, (struct sockaddr *)&sa, sizeof(sa))) { syslog(LLEV,"fwtksyserr: Failed to bind port 25: %m"); exit(1); *************** *** 142,152 **** exit(1); } while (1) { - int cstat; signal (SIGCHLD, waitwaitwait); sockl = accept (sock, (struct sockaddr *)0, (int *)0); if (sockl < 0) { ! if (errno = EINTR) continue; syslog(LLEV,"fwtksyserr: Accept failed: %m"); exit(1); --- 193,202 ---- exit(1); } while (1) { signal (SIGCHLD, waitwaitwait); sockl = accept (sock, (struct sockaddr *)0, (int *)0); if (sockl < 0) { ! if (errno == EINTR) continue; syslog(LLEV,"fwtksyserr: Accept failed: %m"); exit(1); *************** *** 183,190 **** --- 233,242 ---- else if ((hp = gethostbyname(myhostname)) != 0) strcpy(myhostname,hp->h_name); + #ifdef NO_PRECONFIG if((cfp = cfg_read("smap")) == (Cfg *)-1) exit(1); + #endif if((cf = cfg_get("groupid",cfp)) != (Cfg *)0) { if(cf->argc != 1) { *************** *** 248,253 **** --- 300,306 ---- alarm(timeout); } + #ifndef TESTMODE if(rootdir != (char *)0) { chdir("/"); if((chdir(rootdir) || chroot(rootdir))) { *************** *** 257,263 **** chdir("/"); } - if(rungid != -1 && setgid(rungid)) { syslog(LLEV,"fwtksyserr: cannot set gid to %d: %m",rungid); exit(1); --- 310,315 ---- *************** *** 267,273 **** --- 319,367 ---- syslog(LLEV,"fwtksyserr: cannot set uid to %d: %m",runuid); exit(1); } + #else + if (rootdir != (char *)0) + if (chdir(rootdir)) + { + syslog(LLEV,"fwtksyserr: cannot chdir to %.512s: %m",rootdir); + exit(1); + } + #endif + connector_is_local = 1; + + if ((cf=cfg_get("local_nets",cfp)) != (Cfg *)0) + { + if (cf->argc == 0) + { + syslog(LLEV,"fwtkcfgerr: local_nets needs value(s) %d", + cf->ln); + exit(1); + } + connector_is_local=check_net(cf,riaddr); + } + + if ((! connector_is_local) && + (cf=cfg_get("block_nets",cfp)) != (Cfg *)0) + { + if (cf->argc == 0) + { + syslog(LLEV,"fwtkcfgerr: block_nets needs value(s) %d", + cf->ln); + exit(1); + } + if (check_net(cf,riaddr)) + { + syslog(LLEV,"securityalert: attempted connect from " + "blocked domain %s - connection closed", + riaddr); + printf("457 You are not welcome here\r\n"); + fflush(stdout); + exit(1); + } + } + + printf("220 %s SMTP/smap Ready.\r\n",myhostname); fflush(stdout); *************** *** 326,331 **** --- 420,436 ---- smap_exit(1); } strcpy(ruser,q); + + if (!check_valid_sender(ruser,cfp)) + { + syslog(LLEV,"dudsender: %s from %s(%s)", + ruser,riaddr,rladdr); + printf("516 sender %s not valid\r\n",ruser); + fflush(stdout); + free(ruser); + ruser=(char *)0; + continue; + } printf("250 %s... Sender Ok\r\n",ruser); fflush(stdout); continue; *************** *** 358,371 **** q += 3; while(isspace(*q)) q++; ! #ifdef SPECIALDOMAIN ! if(!checkvalid(q)) { ! syslog(LLEV,"securityalert: rejecting recip %.512s",q); fflush(stdout); ! continue; } ! #endif if(maxrecip > 0 && currecip++ > maxrecip) { printf("550 too many recipients\r\n"); fflush(stdout); --- 463,514 ---- q += 3; while(isspace(*q)) q++; ! ! if (! connector_is_local) ! { ! if ((cf=cfg_get("local_domains",cfp)) != (Cfg *)0) ! { ! if (cf->argc == 0) ! { ! syslog(LLEV, ! "fwtkcfgerr: local_domains needs " ! "value(s) %d", ! cf->ln); ! smap_exit(1); ! } ! if (! check_valid_recipient(q,cf)) ! { ! syslog(LLEV,"securityalert: attempted " ! "third party relay from %s to %s " ! "by %s(%s) connection closed", ! ruser,q,riaddr,rladdr); ! printf("%d No third party relaying " ! "allowed\r\n", ! NO_RELAY_ERR); fflush(stdout); ! smap_exit(1); } ! } ! else ! { ! /* if local_domains not defined refuse all */ ! /* outside connections */ ! /* alternative is really to allow all */ ! /* connections (in which case just do*/ ! /* nothing here) */ + syslog(LLEV,"securityalert: attempted" + "mail from %s to %s" + "by %s(%s) connection closed " + "(no local domains)", + ruser,q,riaddr,rladdr); + printf("%d No external connections allowed\r\n", + NO_RELAY_ERR); + fflush(stdout); + smap_exit(1); + } + } + if(maxrecip > 0 && currecip++ > maxrecip) { printf("550 too many recipients\r\n"); fflush(stdout); *************** *** 423,430 **** fprintf(vout,"RCPT %s\n",tp->who); fprintf(vout,"BODY\n"); time(&now); ! fprintf(vout,"Received: from %s(%s) by %s via smap (%s)\n\tid %s; %s\n", ! rladdr,riaddr,myhostname, FWTK_VERSION_MINOR,tempfile, arpadate((char *)0)); --- 566,575 ---- fprintf(vout,"RCPT %s\n",tp->who); fprintf(vout,"BODY\n"); time(&now); ! fprintf(vout,"Received: from %s(%s %s) by %s via smap (%s)\n\tid %s; %s\n", ! rhost, ! strcasecmp(rhost,rladdr) ? rladdr : "" , ! riaddr,myhostname, FWTK_VERSION_MINOR,tempfile, arpadate((char *)0)); *************** *** 754,761 **** ; } - /* kludge for whitehouse.gov anti-spoofing bogosity */ - #ifdef SPECIALDOMAIN #ifndef SYSV extern char *index(); extern char *rindex(); --- 899,904 ---- *************** *** 762,777 **** #endif extern char *strpbrk(); ! char *bad = "550 Recipient must be in form of: user, user@eop.gov, or user@whitehouse.gov\r\n"; ! ! checkvalid(r) char *r; { char *atp; - char *jxp; char *chop; char *chsavp; ! int x; if((chop = malloc((x = strlen(r)) + 1)) == (char *)0) { unlink(tempfile); --- 905,920 ---- #endif extern char *strpbrk(); ! int check_valid_recipient(r,cf) char *r; + Cfg * cf; { char *atp; char *chop; char *chsavp; ! int x,i; ! char **p,*here; ! int matched = 0; if((chop = malloc((x = strlen(r)) + 1)) == (char *)0) { unlink(tempfile); *************** *** 790,816 **** if((atp = rindex(chop,'@')) != (char *)0) { atp++; ! /* check if it ends in @whitehouse.gov || @eop.gov */ ! if(strcasecmp(atp,"whitehouse.gov") && ! strcasecmp(atp,"eop.gov")) goto bomb; /* now make sure there are no other routing chars */ atp--; *atp = '\0'; ! if((jxp = strpbrk(chop,"%@:[]!")) != (char *)0) { goto bomb; } } ! if((jxp = strpbrk(chop,"%@:[]!")) != (char *)0) goto bomb; free(chsavp); return(1); bomb: - printf(bad); free(chsavp); return(0); } - #endif --- 933,1085 ---- if((atp = rindex(chop,'@')) != (char *)0) { atp++; ! ! for (here=atp;*here;here++) ! *here=tolower(*here); ! ! while (cf != (Cfg*)0 && !matched) { ! for (i=0,p=cf->argv;iargc;i++,p++) ! { ! if ((here=strstr(atp,*p)) != (char *)0 && ! (!strcmp(here,*p)) && ! (*(here-1) == '.' || *(here-1) == '@')) ! { ! matched=1; ! break; ! } ! } ! ! cf = cfg_get(cf->op,(Cfg*)0); ! } ! ! if (!matched) goto bomb; /* now make sure there are no other routing chars */ atp--; *atp = '\0'; ! if(strpbrk(chop,"%@:[]!") != (char *)0) { goto bomb; } } ! else ! { ! /* require @ in address */ goto bomb; + } + if(strpbrk(chop,"%@:[]!") != (char *)0) + goto bomb; free(chsavp); return(1); bomb: free(chsavp); return(0); } + int check_mask(net,host,maskbits) + char * net; + char * host; + char * maskbits; + { + + unsigned long netno, hostno, mask; + int imaskbits; + + imaskbits=atoi(maskbits); + if (imaskbits >= 32) + return(!strcmp(net,host)); + netno=inet_addr(net); + hostno=inet_addr(host); + mask = (0xffffffffUL << (32 - imaskbits)) & 0xffffffffUL; + mask= htonl(mask); + return ((netno & mask) == (hostno & mask)); + + + } + + + + int check_net(cf,addr) + Cfg *cf; + char * addr; + { + + int i; + char **p, *slash; + fflush(stdout); + while (cf != (Cfg*)0) { + for (i=0,p=cf->argv;iargc;i++,p++) + { + slash=index(*p,'/'); + if (slash != (char *)0) + { + *slash=0;slash++; + if ((*slash=='H' && !strcmp(*p,addr)) || + (isdigit(*slash) && check_mask(*p,addr,slash))) + { + slash--; *slash='/'; + return 1; + } + else + { + slash--; *slash='/'; + } + } + else + { + syslog(LLEV,"ftwkcfgerr: malformed %s line %d", + cf->op,cf->ln); + } + } + + cf = cfg_get(cf->op,(Cfg*)0); + } + return 0; + } + + + check_valid_sender(r,cfg) + char * r; + Cfg *cfg; + { + char *chop, *chsavp, *atp; + char answer[512]; + char result = 0; + int x; + + + /* have to let through the special address <> (unfortunately) */ + + if(!strcmp(r,"<>")) + return 1; + + if((chop = malloc((x = strlen(r)) + 1)) == (char *)0) { + unlink(tempfile); + syslog(LLEV,"fwtksyserr: of memory: %m"); + exit(1); + } + + chsavp = chop; + strcpy(chop,r); + + if(r[0] == '<') { + if(chop[x - 1] == '>') + chop[x - 1] = '\0'; + chop++; + } + + /* there must be an @ sign and it must be followed by a */ + /* resolvable address (i.e. it has an A or MX record) */ + + if((atp = rindex(chop,'@')) != (char *)0) { + atp++; + if ((res_query(atp,C_IN,T_A,(u_char *)&answer,512) >0) || + (res_query(atp,C_IN,T_MX,(u_char *)&answer,512)>0)) + result=1; + + } + + free(chsavp); + return result; + }