/* Proposal from kyle_jones@wonderworks.com (Kyle Jones) Message-ID: <57dk9c$k8o@crystal.WonderWorks.COM> */ /* Here's the checkcompat() function I promised. You can use it to stop others from using your site as a relay. To install it, replace the checkcompat() function in src/conf.c with it and recompile sendmail. These are the rules that it enforces. a. locally generated mail is allowed to go anywhere. b. mail from networks considered local is allowed to go anywhere. c. mail from non-local networks is allowed to go to local users and aliases. d. mail from non-local networks is allowed to go to domains for which the local host is a legitimate relay. Everything else is bounced. The message body is not returned. The class AuthorizedNetworks should contain the networks that are considered local in (b). The class RelayedDomains should contain the legitimately relayed domains in (d). Subdomains of these domains are also considered legitimate. Let me know of any bugs or enhancements and I'll try to keep the thing up-to-date. */ int checkcompat(to, e) register ADDRESS *to; register ENVELOPE *e; { ADDRESS *a; struct mailer *m; int classid; static char ip_addr[100]; char workbuf[MAXNAME * 2 + 25], *p, *end = 0; /* * Allow all "return to sender" messages. */ if (bitset(EF_RESPONSE, e->e_flags)) return EX_OK; /* * We need to find the IP address of the sender's host, if any. * (You're on your own if you're not using IP.) We can't use * RealHostAddr because we might be in a queue run and * RealHostAddr doesn't contain valid data during queue runs. * $_ contains the information we need and is valid during queue * runs. * * We use cached results of a previous parse of $_ if we can. * We can do this if we're processing only one non-internally * generated message per process. That condition is met if * we're not in a queue run or we're in a queue run and the user * has ForkEachJob set true. */ if (! *ip_addr || (bitset(e->e_flags, EF_QUEUERUN) && ! ForkQueueRuns)) { /* * Since $_ may contain data besides the IP address we must * carefully parse out the IP addr. eeeeyuck. Sure would * be nice to have a macro that contains just the remote IP * addr and that is also stored in the qf file. * * The end of $_ is expected to be of the form * * [xxx.xxx.xxx.xxx] * or * [!@aaa.aaa.aaa.aaa@bbb.bbb.bbb.bbb<...>:xxx.xxx.xxx.xxx] * */ strncpy(workbuf, macvalue('_', e), sizeof workbuf); workbuf[sizeof workbuf - 1] = 0; p = &workbuf[strlen(workbuf)]; while (--p != workbuf && *p != ':' && *p != '[') { if (*p == ']') end = p; else if (! isdigit(*p) && *p != '.') break; } /* * Test whether we found the addresses start and end markers. * If not, then the parse failed, which means $_ contains no * recognizable IP address. * * If there is no remote host address, then the message must * have originated locally. (UUCP? Never heard of it.) */ if ((! end) || (*p != ':' && *p != '[')) { strcpy(ip_addr, "none"); return EX_OK; } p++; /* copy out the IP addr */ strncpy(ip_addr, p, min(sizeof ip_addr, end - p)); ip_addr[sizeof ip_addr - 1] = 0; /* * If ip_addr == none, then the message originated locally. */ } else if (strcmp(ip_addr, "none") == 0) return EX_OK; /* * Check remote hosts's IP address against a list of hosts and * networks authorized to use this host as a relay. The class * name is AuthorizedNetworks. You can put full IP addresses * into it or you can use net prefixes like * * 26 * 137.39 * 192.203.206 * * for class A, B and C nets. Sorry, no fancy subnet masks. * * Note that this class is empty by default, so no addresses are * authorized, not even the loopback net. Put 127 into the * class to enable the loopback net for those mailers that like * to submit mail via SMTP and don't use sendmail -bs. */ classid = macid("{AuthorizedNetworks}", (char **) 0); strncpy(workbuf, ip_addr, sizeof workbuf); workbuf[sizeof workbuf - 1] = 0; p = workbuf + strlen(workbuf); do { *p = 0; if (wordinclass(workbuf, classid)) return EX_OK; while (p != workbuf && *p != '.') p--; } while (p != workbuf); /* * At this point we're convinced the sender is not local and not * authorized to use this host as a relay. So now we check the * recipient address to see if it is local, or that is it for a * domain for which we are a legitimate relay. * * Aliases are local addresses but they may point to non-local * addresses; mailing to these aliases should be allowed. * Therefore we can't check the 'to' address for locality. We * must backtrack through the alias chain until we reach the * root address. If this address is local then all the * addresses that expanded from it should also be considered * local for the purpose of this test. */ for (a = to; a->q_alias; a = a->q_alias) ; m = a->q_mailer; /* * Now check to see if the root of the alias chain is local. */ if (bitnset(M_LOCALMAILER, m->m_flags)) return EX_OK; /* * Check destination host to see if it is a domain we relay or a * subdomain thereof. */ classid = macid("{RelayedDomains}", (char **) 0); strncpy(workbuf, to->q_host ? to->q_host : "", sizeof workbuf); workbuf[sizeof workbuf - 1] = 0; end = workbuf + strlen(workbuf); p = workbuf; while (*p) { if (wordinclass(p, classid)) return EX_OK; while (*p && *p != '.') p++; if (*p) p++; } /* everything else is bad */ usrerr("554 relay traffic prohibited"); e->e_flags |= EF_NO_BODY_RETN; return EX_UNAVAILABLE; }