Logo Search packages:      
Sourcecode: mailavenger version File versions  Download package

config.C

/* $Id$ */

/*
 *
 * Copyright (C) 2004 David Mazieres (dm@uun.org)
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2, or (at
 * your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
 * USA
 *
 */

#include "asmtpd.h"
#include <grp.h>

str config_file (ETCDIR "/asmtpd.conf");

options *opt (New options);

static const options::ofunc ovec[] = {
#define mkopt(opt) { #opt, &options::do_##opt },
  mkopt (bindaddr)
  mkopt (trustednet)
  mkopt (trusteddomain)
  mkopt (separator)
  mkopt (maxmsgsperip)
  mkopt (maxerrorsperip)
  mkopt (maxmsgsperuser)
  mkopt (smtpfilter)
  mkopt (rbl)
  mkopt (sendmail)
  mkopt (avengeruser)
  mkopt (nocheck)
  mkopt (env)
  { "spffail", &options::do_spfxxx },
  { "spfnone", &options::do_spfxxx },
  { "spflocal", &options::do_spfxxx },
  { "spfexp", &options::do_spfxxx },
#undef mkopt
};
static const u_int novec = sizeof (ovec) / sizeof (ovec[0]);

options::options ()
  : configno (1), hostname (myname ()),
    logtag (""), logpriority ("mail.info"),
    smtp_timeout (30), data_timeout (300), client_timeout (300),
    vrfy_delay (2), vrfy_cachetime (300),
    max_clients (60), max_revclients (60),
    max_rcpts (5), max_relay_rcpts (0),
    max_msgsize (100 * 1024 * 1024), con_max_per_ip (10),
    msg_max_per_ip (1000000), msg_rate_per_ip (1000000),
    err_max_per_ip (1000000), err_rate_per_ip (1000000),
    msg_max_per_user (1000), msg_rate_per_user (300),
    separator ('\0'), synfp (true), netpath (true), smtpcb (2),
    debug_smtpd (false), debug_smtpc (false), debug_avenger (false),
    user_mail (false), user_rcpt (true), allow_percent (false),
    allow_dnsfail (0), ident_timeout (15), synfp_wait (500), synfp_buf (100),
    osguess_mtu (1500),
    spf_exp ("See http://www.openspf.org/why.html?sender=%{S}&ip=%{I}"),
    mxlocal_rcpt (false), emptysender (""),
    sendmailpriv (false), sendmailfromline (false), warn_filter_clean (0),
    av_user (NULL), avenger_max_per_user (5), avenger_timeout (600)
#ifdef SASL
    , sasl (0), insecuresasl (0)
#endif /* SASL */
#ifdef STARTTLS
    , ssl_keylen (2048), ssl_status (0),
    // ssl_ciphers ("ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH"),
    ssl (1)
#endif /* STARTTLS */
{
  ipmask &lm = trustednets.push_back ();
  lm.net = htonl (INADDR_LOOPBACK);
  lm.mask = htonl (0xffffffff);

  if (str sm = find_program ("sendmail")) {
    sendmail.push_back (sm);
    sendmail.push_back ("-oi");
    sendmail.push_back ("-os");
    sendmail.push_back ("-oee");
  }
}

options::~options ()
{
  delpw (av_user);
}

passwd *
options::copypw (passwd *pw)
{
  struct passwd *npw = static_cast<passwd *> (xmalloc (sizeof (*npw)));
  bzero (npw, sizeof (*npw));
  npw->pw_name = xstrdup (pw->pw_name);
  //npw->pw_passwd = xstrdup (pw->pw_passwd);
  npw->pw_passwd = xstrdup ("*");
  npw->pw_uid = pw->pw_uid;
  npw->pw_gid = pw->pw_gid;
#ifdef HAVE_PASSWD_PW_CLASS
  npw->pw_class = xstrdup (pw->pw_class);
#endif /* HAVE_PASSWD_PW_CLASS */
  npw->pw_gecos = xstrdup (pw->pw_gecos);
  npw->pw_dir = xstrdup (pw->pw_dir);
  npw->pw_shell = xstrdup (pw->pw_shell);
#ifdef HAVE_PASSWD_PW_EXPIRE
  npw->pw_expire = pw->pw_expire;
#endif /* HAVE_PASSWD_PW_EXPIRE */
  return npw;
}

void
options::delpw (passwd *pw)
{
  xfree (pw->pw_name);
  xfree (pw->pw_passwd);
#ifdef HAVE_PASSWD_PW_CLASS
  xfree (pw->pw_class);
#endif /* HAVE_PASSWD_PW_CLASS */
  xfree (pw->pw_gecos);
  xfree (pw->pw_dir);
  xfree (pw->pw_shell);
  delete pw;
}

str
options::do_line (vec<str> &av, str cf, int line, bool *errp, conftab *ctp)
{
  for (u_int i = 0; i < novec; i++)
    if (!strcasecmp (ovec[i].name, av[0])) {
      str err = (this->*(ovec[i].fn)) (av);
      if (err)
      *errp = true;
      return err;
    }
  if (ctp->match (av, cf, line, errp))
    return NULL;
  return strbuf () << "unknown directive " << av[0];
}

str
options::do_bindaddr (vec<str> &av)
{
  sockaddr_in sin;
  bzero (&sin, sizeof (sin));
  sin.sin_family = AF_INET;

  u_int16_t port = 0;
  if (av.size () < 2 || av.size () > 3
      || inet_aton (av[1], &sin.sin_addr) != 1
      || (av.size () == 3 && (!convertint (av[2], &port) || !port)))
    return "usage: BindAddr ip-addr [port]";
  sin.sin_port = htons (port ? port : 25);
  if (bindaddrh.insert (sin))
    bindaddrv.push_back (sin);
  return NULL;
}

str
options::do_trustednet (vec<str> &av)
{
  static rxx tnrx ("(\\d+\\.\\d+\\.\\d+\\.\\d+)/(\\d+)");
  in_addr addr;
  int bits = 32;
  if (av.size () != 2 || !tnrx.match (av[1])
      || inet_aton (tnrx[1], &addr) != 1
      || !convertint (tnrx[2], &bits)
      || bits > 32)
    return "usage: TrustedNet ip-addr/preflen";

  ipmask mask;
  mask.net = addr.s_addr;
  mask.mask = htonl ((u_int32_t) 0xffffffff << (32 - bits));
  trustednets.push_back (mask);
  return NULL;
}

str
options::do_trusteddomain (vec<str> &av)
{
  static rxx tdrx ("([\\w_\\-]+\\.)+[\\w\\-]*[a-zA-Z]");
  if (av.size () != 2 || !tdrx.match (av[1]))
    return "usage: TrustedDomain domain.name";
  trusteddomains.push_back (av[1]);
  return NULL;
}

str
options::do_separator (vec<str> &av)
{
  if (av.size () == 1) {
    separator = '\0';
    return NULL;
  }
  if (av.size () == 2 && av[1].len () < 2) {
    separator = *av[1].cstr ();
    return NULL;
  }
  return "usage: Separator [<char>]";
}

str
options::do_maxmsgsperip (vec<str> &av)
{
  if (av.size () == 2 && convertint (av[1], &msg_rate_per_ip))
    msg_max_per_ip = msg_rate_per_ip;
  else if (av.size () != 3
         || !convertint (av[1], &msg_rate_per_ip)
         || !convertint (av[2], &msg_max_per_ip))
    return "usage: MaxMsgsPerIp <messages-per-hour> [<hard-limit>]";
  return NULL;
}

str
options::do_maxerrorsperip (vec<str> &av)
{
  if (av.size () == 2 && convertint (av[1], &err_rate_per_ip))
    err_max_per_ip = err_rate_per_ip;
  else if (av.size () != 3
         || !convertint (av[1], &err_rate_per_ip)
         || !convertint (av[2], &err_max_per_ip))
    return "usage: MaxErrorsPerIp <errors-per-hour> [<hard-limit>]";
  return NULL;
}

str
options::do_maxmsgsperuser (vec<str> &av)
{
  if (av.size () == 2 && convertint (av[1], &msg_rate_per_user))
    msg_max_per_user = msg_rate_per_user;
  else if (av.size () != 3
         || !convertint (av[1], &msg_rate_per_user)
         || !convertint (av[2], &msg_max_per_user))
    return "usage: MaxMsgsPerUser <messages-per-hour> [<hard-limit>]";
  return NULL;
}


str
options::do_smtpfilter (vec<str> &av)
{
  if (av.size () != 2 || !av[1].len ())
    return "usage: SMTPFilter <path-to-program>";
  smtp_filter = av[1];
  return NULL;
}

str
options::do_rbl (vec<str> &av)
{
  static const str usage ("usage: RBL [-i] [-p] [-f] [-w]"
                    " [-s <score>] <domain>");
  if (av.size () < 2)
    return usage;
  u_int flags = 0;
  int score = 0;
  for (u_int i = 1, e = av.size () - 1; i < e; i++) {
    if (av[i].len () != 2 || av[i][0] != '-')
      return usage;
    switch (av[i][1]) {
    case 'i':
      flags |= rbl::QUERY_IP;
      break;
    case 'p':
      flags |= rbl::QUERY_PTR;
      break;
    case 'f':
      flags |= rbl::QUERY_ENV;
      break;
    case 'w':
      flags |= rbl::TRUSTED;
      break;
    case 's':
      if (i + 2 > e || !convertint (av[i+1], &score))
      return usage;
      i++;
      break;
    default:
      return usage;
    }
  }
  if (!(flags & (rbl::QUERY_IP|rbl::QUERY_PTR|rbl::QUERY_ENV)))
    flags |= rbl::QUERY_IP;
  str domain = av.back ();
  if (!domain.len () || domain[0] == '-')
    return usage;
  rbls.push_back (New refcounted<rbl> (domain, flags, score));
  return NULL;
}

str
options::do_sendmail (vec<str> &av)
{
  av.pop_front ();
  if (av.empty ())
    return "usage: Sendmail <program> [<arg> ...]";
  str fullpath = find_program (av[0]);
  if (!fullpath)
    return strbuf () << "could not find program " << av[0];
  av[0] = fullpath;
  swap (sendmail, av);
  return NULL;
}

str
options::do_avengeruser (vec<str> &av)
{
  if (av.size () != 2)
    return "usage: AvengerUser <username>";
  passwd *pw = getpwnam (av[1]);
  if (!pw)
    return av[1] << ": no such user";
  if (av_user)
    delpw (av_user);
  av_user = copypw (pw);
  return NULL;
}

str
options::do_nocheck (vec<str> &av)
{
  if (av.size () != 2)
    return "usage: NoCheck user[@host]";
  nocheck.insert (mytolower (av[1]));
  return NULL;
}

str
options::do_env (vec<str> &av)
{
  static rxx var ("^([^=\\s]+)(=(.*))?$");
  if (av.size () != 2 || !var.match (av[1]))
    return ("usage: Env var[=value]");
  if (var[1] == "PWD")
    return "Env: illegal special variable name PWD";
  if (var[3]) {
    env.push_back (av[1]);
    envb.insert (var[1]);
  }
  else if (char *p = getenv (var[1])) {
    env.push_back (strbuf () << var[1] << "=" << p);
    envb.insert (var[1]);
  }
  return NULL;
}

str
options::do_spfxxx (vec<str> &av)
{
  str *sp = NULL;

  if (!strcasecmp (av[0], "spffail"))
    sp = &spf_fail;
  else if (!strcasecmp (av[0], "spfnone"))
    sp = &spf_none;
  else if (!strcasecmp (av[0], "spflocal"))
    sp = &spf_local;
  else if (!strcasecmp (av[0], "spfexp"))
    sp = &spf_exp;
  else
    panic ("unknown spfxxx %s\n", av[0].cstr ());

  if (av.size () <= 1)
    *sp = NULL;
  else {
    av.pop_front ();
    *sp = join (" ", av);
  }
  return NULL;
}

bool
parseconfig (options *op, str cf)
{
  parseargs pa (cf);
  bool errors = false;

  op->configno = opt->configno + 1;
  conftab ct;
  ct
    .add ("Hostname", &op->hostname, false)
    .add ("LogTag", &op->logtag, false)
    .add ("LogPriority", &op->logpriority, false)
    .add ("SMTPTimeout", &op->smtp_timeout, 0, 24 * 60 * 60)
    .add ("DataTimeout", &op->data_timeout, 0, 24 * 60 * 60)
    .add ("ClientTimeout", &op->client_timeout, 0, 24 * 60 * 60)
    .add ("VrfyDelay", &op->vrfy_delay, 0, 24 * 60 * 60)
    .add ("VrfyCacheTime", &op->vrfy_cachetime, 0, 0x7fffffff)
    .add ("MaxClients", &op->max_clients, 1, 0x10000)
    .add ("MaxRevClients", &op->max_revclients, 0, 0x10000)
    .add ("MaxRcpts", &op->max_rcpts, 1, 1024)
    .add ("MaxRelayRcpts", &op->max_relay_rcpts, 0, 65536)
    .add ("MaxMsgSize", &op->max_msgsize, 1024, 0x7fffffff)
    .add ("SynFp", &op->synfp)
    .add ("SynOsMTU", &op->osguess_mtu, 0, 0x10000)
    .add ("NetPath", &op->netpath)
    .add ("SMTPCB", &op->smtpcb, 0, 2)
    .add ("DebugSMTP", &op->debug_smtpd)
    .add ("DebugSMTPc", &op->debug_smtpc)
    .add ("DebugAvenger", &op->debug_avenger)
    .add ("UserMail", &op->user_mail)
    .add ("UserRcpt", &op->user_rcpt)
    .add ("AllowPercent", &op->allow_percent)
    .add ("AllowDNSFail", &op->allow_dnsfail, 0, 2)
    .add ("IdentTimeout", &op->ident_timeout, 0, 300)
    .add ("SynFpWait", &op->synfp_wait, 0, 3600000)
    .add ("SynFpBuf", &op->synfp_buf, 0, 0x100000)
    .add ("MXLocalRcpt", &op->mxlocal_rcpt)
    .add ("SendmailPriv", &op->sendmailpriv)
    .add ("SendmailFromLine", &op->sendmailfromline)
    .add ("MaxConPerIp", &op->con_max_per_ip, 1, 0x10000)
    .add ("EmptySender", &op->emptysender, false)
    .add ("EtcDir", &op->etcdir, false)
    .add ("AliasFile", &op->alias_file, false)
    .add ("DomainFile", &op->domain_file, false)
    .add ("SPFhostsFile", &op->spfhosts_file, false)
#ifdef SASL
    .add ("SASL", &op->sasl, 0, 2)
    .add ("InsecureSASL", &op->insecuresasl, 0, 1)
#endif /* SASL */
#ifdef STARTTLS
    .add ("SSL", &op->ssl, 0, 2)
    .add ("SSLCAcert", &op->ssl_ca, false)
    .add ("SSLCRL", &op->ssl_crl, false)
    .add ("SSLcert", &op->ssl_cert, false)
    .add ("SSLkey", &op->ssl_key, false)
    .add ("SSLciphers", &op->ssl_ciphers, false)
#endif /* !STARTTLS */
    .add ("AvengerMaxPerUser", &op->avenger_max_per_user, 1, 0x10000)
    .add ("AvengerTimeout", &op->avenger_timeout, 0, 24 * 60 * 60)
    ;

  int line;
  vec<str> av;
  while (pa.getline (&av, &line))
    if (str err = op->do_line (av, cf, line, &errors, &ct))
      warn << cf << ":" << line << ": " << err << "\n";

#if 0
  if (!op->rcptdomains.size () && !op->mxlocal_rcpt) {
    //warn ("no RcptDomain directives, enabling MXLocalRcpt\n");
    op->mxlocal_rcpt = true;
  }
#endif
  if (!errors && op->sendmail.empty ()) {
    warn ("no sendmail program specified or found\n");
    errors = true;
  }

  if (!op->av_user) {
    passwd *pw = getpwnam (AVENGER);
    if (!pw) {
      warn ("no " AVENGER " user found on system\n");
      errors = true;
    }
    else
      op->av_user = options::copypw (pw);
  }

  if (op->av_user) {
    bhash <GROUPLIST_T> cache;
#  ifdef HAVE_EGID_IN_GROUPLIST
    cache.insert (op->av_user->pw_gid);
    op->av_groups.push_back (op->av_user->pw_gid);
#  endif /* HAVE_EGID_IN_GROUPLIST */
    setgrent ();
    while (group *gr = getgrent ()) {
      if (cache[gr->gr_gid])
      continue;
      for (char **mp = gr->gr_mem; *mp; mp++)
      if (!strcmp (*mp, op->av_user->pw_name)) {
        cache.insert (gr->gr_gid);
        op->av_groups.push_back (gr->gr_gid);
        if (op->av_groups.size () > NGROUPS_MAX)
          goto done;
        break;
      }
    }
  done:;
  }

  if (!op->etcdir)
    op->etcdir = ETCDIR;
#define fixpath(field, default)                       \
  do {                                          \
    if (!op->field)                             \
      op->field = op->etcdir << "/" default;          \
    else if (op->field[0] != '/')               \
      op->field = op->etcdir << "/" << op->field;     \
  } while (0)

  fixpath (alias_file, "aliases");
  fixpath (domain_file, "domains");
  fixpath (spfhosts_file, "spfhosts");
#ifdef STARTTLS
  fixpath (ssl_ca, "cacert.pem");
  fixpath (ssl_crl, "crl.pem");
  fixpath (ssl_cert, "cert.pem");
  fixpath (ssl_key, "privkey.pem");
#endif /* STARTTLS */

#undef fixpath

  if (op->bindaddrv.empty ()) {
    sockaddr_in sin;
    bzero (&sin, sizeof (sin));
    sin.sin_family = AF_INET;
    sin.sin_port = htons (25);
    sin.sin_addr.s_addr = htonl (INADDR_ANY);
    op->bindaddrv.push_back (sin);
    op->bindaddrh.insert (sin);
  }

  return !errors;
}

void
maybe_warn (str msg)
{
  if (opt_verbose) {
    warn << msg;
    return;
  }

  if (time_t *tp = opt->warn_filter[msg])
    if (timenow < *tp + 600)
      return;
  opt->warn_filter.insert (msg, timenow);
  warn << msg;

  if (timenow < opt->warn_filter_clean)
    return;
  opt->warn_filter_clean = timenow + 600;
  qhash<str, time_t>::slot *slp, *nslp;
  for (slp = opt->warn_filter.first (); slp; slp = nslp) {
    nslp = opt->warn_filter.next (slp);
    if (slp->value + 600 <= timenow)
      opt->warn_filter.remove (slp->key);
  }
}

Generated by  Doxygen 1.6.0   Back to index