*** /tmp/,RCSt1a05857 Mon Sep 29 16:38:29 1997 --- smtpserver.c Thu Sep 18 17:59:21 1997 *************** *** 23,30 **** --- 23,31 ---- #define RKEY_FROM "from" /* mail from address verification */ #define RKEY_TO "to" /* recipient to address verification */ #define RKEY_VERIFY "verify" /* verify this address */ #define RKEY_EXPAND "expand" /* expand this address */ + #define RKEY_HELO "helo" /* HELO name verification */ #include #include "hostenv.h" *************** *** 67,74 **** --- 68,81 ---- #ifndef MAXHOSTNAMELEN #define MAXHOSTNAMELEN 64 #endif /* MAXHOSTNAMELEN */ + #ifdef WRAPPERS + #include "tcpd.h" + int allow_severity = LOG_INFO; + int deny_severity = LOG_WARNING; + #endif + /* * Early inetd's, which may be found on 4.2BSD based systems (e.g. * Sun OS 3.x), are incapable of passing a flag to indicate we are * being run by inetd rather than directly. Some hackery to detect *************** *** 136,150 **** --- 143,166 ---- int skeptical = 1; int pid, routerpid; FILE *logfp = NULL; + /* Connection limiting stuff */ + int maxkids = -1; + volatile int curkids = 0; + int nicekids = 0; /* Do we give a 421 message? */ + + /* listen size. */ + int listensize = 5; + /* * The "style" variable controls when the router is interrogated about the * validity of something. It is a string of letter-flags: * f: check MAIL FROM addresses * t: check RCPT TO addresses * v: check VRFY command argument * e: check EXPN command argument + * h: check HELO command argument */ char *style = "ve"; *************** *** 201,209 **** (void) fprintf(stderr, "%s: gethostname(): %s\n", progname, strerror(errno)); exit(1); } ! while ((c = getopt(argc, argv, "igl:np:P:R:s:V")) != EOF) { switch (c) { case 'i': /* interactive */ daemon = 0; break; --- 217,225 ---- (void) fprintf(stderr, "%s: gethostname(): %s\n", progname, strerror(errno)); exit(1); } ! while ((c = getopt(argc, argv, "igl:np:P:R:s:Vm:ML:")) != EOF) { switch (c) { case 'i': /* interactive */ daemon = 0; break; *************** *** 237,244 **** --- 253,272 ---- case 'V': prversion("smtpserver"); exit(0); break; /* paranoia */ + + case 'm': /* max simultaneous connections */ + maxkids = atoi(optarg); + break; + case 'M': /* give a 421 message when too many xns */ + nicekids = 1; + break; + case 'L': /* listen() backlog size */ + listensize = atoi(optarg); + listensize = listensize > 0 ? listensize : 5; + break; + default: ++errflg; break; } *************** *** 331,339 **** (void) fprintf(stderr, "%s: bind(): %s\n", progname, strerror(errno)); exit(1); } ! if (listen(s, 5) < 0) { (void) fprintf(stderr, "%s: listen(): %s\n", progname, strerror(errno)); exit(1); } --- 359,367 ---- (void) fprintf(stderr, "%s: bind(): %s\n", progname, strerror(errno)); exit(1); } ! if (listen(s, listensize) < 0) { (void) fprintf(stderr, "%s: listen(): %s\n", progname, strerror(errno)); exit(1); } *************** *** 370,377 **** --- 398,412 ---- strerror(errno)); exit(1); } } + + if (maxkids >= 0 && curkids >= maxkids && !nicekids) { + /* Just bail. */ + (void) close(msgfd); + continue; + } + if ((pid = fork()) < 0) { /* can't fork! */ (void) close(msgfd); (void) fprintf(stderr, "%s: fork(): %s\n", *************** *** 386,401 **** if (msgfd != 1) (void) dup2(msgfd, 1); if (msgfd > 1) (void) close(msgfd); rport = raddr.sin_port; setrhostname(&raddr); smtpserver(1); if (routerpid > 0) killr(routerpid); _exit(0); ! } else (void) close(msgfd); } } #else /* !USE_INET */ { --- 421,444 ---- if (msgfd != 1) (void) dup2(msgfd, 1); if (msgfd > 1) (void) close(msgfd); + /* If this is true, then we're being nice + and giving a come-back-later message. */ + if (maxkids >= 0 && curkids >= maxkids) { + type(421, "Service temporarily unavailable."); + _exit(0); + } rport = raddr.sin_port; setrhostname(&raddr); smtpserver(1); if (routerpid > 0) killr(routerpid); _exit(0); ! } else { (void) close(msgfd); + curkids ++; + } } } #else /* !USE_INET */ { *************** *** 495,503 **** while (wait3(&status, WNOHANG, (struct rusage *)NULL) > 0) #else /* !WNOHANG */ while (wait(&status) > 0) #endif /* WNOHANG */ ! continue; (void) signal(SIGCHLD, reaper); } void --- 538,546 ---- while (wait3(&status, WNOHANG, (struct rusage *)NULL) > 0) #else /* !WNOHANG */ while (wait(&status) > 0) #endif /* WNOHANG */ ! { curkids --; continue; } (void) signal(SIGCHLD, reaper); } void *************** *** 537,544 **** --- 580,628 ---- extern char *router(), *strerror(); extern int errno; extern int cistrcmp(), partridge(), cistrncmp(), runastrusteduser(); + #ifdef WRAPPERS + struct request_info rq; + struct sockaddr sname; + int snlen = sizeof(sname); + char *dolog, *doreject; + /* + * Check TCP wrappers configuration. Note that we do logging only + * on rejected connections, since the smtpserver will already log good + * ones. We log via the daemon facility to match the other TCPwrappers + * stuff, and thus must closelog() afterwards so the later openlog() to + * the mail facility will work. + * - cks + */ + /* Make tcpwrappers happy by only asking it about IP sockets. */ + if (getsockname(1, &sname, &snlen) == 0 && sname.sa_family == AF_INET) { + (void) openlog("smtpserver", LOG_PID, LOG_DAEMON); + (void) request_init(&rq, RQ_FILE, 1, RQ_DAEMON, "smtpserver", 0); + fromhost(&rq); + if (!hosts_access(&rq)) { + /* Figure out what to do with it */ + doreject = getenv("OPTREJECT"); + dolog = getenv("OPTLOG"); + if (!doreject || strcmp(doreject, "none") != 0) + type(550, doreject ? doreject : "Connection rejected due to connection origin."); + if (!dolog || strcmp(dolog, "none") != 0) + if (dolog) + syslog(deny_severity, "refused connect from %s: %s", eval_client(&rq), dolog); + else + syslog(deny_severity, "refused connect from %s", eval_client(&rq)); + closelog(); + /* Tear down the connection. */ + return; + } else { + dolog = getenv("OPTLOG"); + if (dolog && strcmp(dolog, "none") != 0) + syslog(allow_severity, "accepted connect from %s: %s", eval_client(&rq), dolog); + } + closelog(); + } + #endif + /* opening the logfile should be done before we reset the uid */ if (logfile != NULL) { if ((fd = open(logfile, O_CREAT|O_APPEND|O_WRONLY, 0644)) < 0) { if (!insecure) *************** *** 643,655 **** --- 727,769 ---- } type(503, cp); break; } + #if 0 if (sscanf(cp, "%s", helobuf) != 1) { type(501, "What is your domain name?"); break; } + #else /* + * parse the argument to HELO, if any, trimming off + * leading and trailing spaces before we go on. + * sscanf is not sufficient, because we want to catch + * the case of spaces in the middle of the HELO, and + * sscanf will normally stop as soon as it finds one. + * - cks + */ + { char *tcp = cp; int ti; + while (isascii(*tcp) && isspace(*tcp)) + tcp++; + if (*tcp == '\0') { + type(501, "What is your domain name?"); + break; + } else { + /* this unguarded copy is safe, because + we know that cp points into buf, and + tcp points after cp, and that helobuf + and buf are the same size. - cks */ + strcpy(helobuf, tcp); + ti = strlen(helobuf) - 1; + while (isascii(helobuf[ti]) && + isspace(helobuf[ti])) + ti--; + helobuf[ti+1] = '\0'; + } + } + #endif + /* * Craig P. says we have to spit back syntactically * invalid helo parameters at this stage, which is * hard to do right since it requires a full '822 * tokenizer. We do a half-hearted attempt here. *************** *** 665,672 **** --- 779,800 ---- else type(501, "Sorry, access denied."); break; } + /* Validate HELO if asked to. */ + s = NULL; + if (STYLE(cfinfo, 'h')) { + if ((s = router(RKEY_HELO, 1, helobuf)) == NULL) { + /* the error was printed in router() */ + break; + } + if (atoi(s) / 100 != 2) { + /* verification failed */ + type(atoi(s), "%s", s+4); + free(s); + break; + } + } /* check helobuf corresponds to the reverse address */ if (ihostaddr[0] != '\0' && rhostname[0] != '\0' && rhostname[0] != '[' && cistrcmp(helobuf, rhostname) != 0) { *************** *** 733,741 **** /* the error was printed in router() */ break; if (atoi(s) / 100 != 2) { /* verification failed */ ! type(atoi(s), s+4, "Failed", "Failed"); free(s); break; } } --- 861,869 ---- /* the error was printed in router() */ break; if (atoi(s) / 100 != 2) { /* verification failed */ ! type(atoi(s), "%s", s+4); free(s); break; } } *************** *** 831,839 **** /* the error was printed in router() */ break; if (atoi(s) / 100 != 2) { /* verification failed */ ! type(atoi(s), s+4, "Failed", "Failed"); free(s); break; } } --- 959,967 ---- /* the error was printed in router() */ break; if (atoi(s) / 100 != 2) { /* verification failed */ ! type(atoi(s), "%s", s+4); free(s); break; } } *************** *** 1238,1246 **** } bufsize = 10; cp = buf = emalloc(bufsize); ! while ((*cp = getc(fp)) != EOF) { if (*cp == '\n') { *flagp = 0; break; } --- 1366,1374 ---- } bufsize = 10; cp = buf = emalloc(bufsize); ! while (((*cp = getc(fp))) != EOF) { if (*cp == '\n') { *flagp = 0; break; } *************** *** 1256,1264 **** cp += s - buf; buf = s; } } ! if (*cp == EOF) { (void) printf("got EOF!\n"); free(buf); return NULL; } --- 1384,1392 ---- cp += s - buf; buf = s; } } ! if ((*cp) == EOF) { (void) printf("got EOF!\n"); free(buf); return NULL; }