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

cfgread.c

/*
 *    Copyright 1988 by Rayan S. Zachariassen, all rights reserved.
 *      This will be free software, but only when it is finished.
 */
/*
 *    Several extensive changes by Matti Aarnio <mea@nic.funet.fi>
 *      Copyright 1991-2003.
 */

/*
 * ZMailer SMTP server.
 */

#include "smtpserver.h"

#define SKIPSPACE(Y) while (*Y == ' ' || *Y == '\t') ++Y
#define SKIPTEXT(Y)  while (*Y && *Y != ' ' && *Y != '\t') ++Y
#define SKIPDIGIT(Y) while ('0' <= *Y && *Y <= '9') ++Y

static int called_getbindaddr = 0;
static Usockaddr bindaddr;

static void dollarexpand __((unsigned char *s0, int space));
static void dollarexpand(s0, space)
     unsigned char *s0;
     int space;
{
    unsigned char *str = s0;
    unsigned char *eol = s0 + space; /* assert(str < eol) */
    unsigned char namebuf[80];
    unsigned char *s;
    int len, taillen;

    while (*str) {
      if (*str != '$') {
      ++str;
      continue;
      }
      /*  *str == '$' */
      s0 = str; /* start position */
      ++str;
      if (*str == '$') {
      /* A '$$' sequence shrinks to '$' */
      strcpy((char*)str, (const char *)(str+1));
      continue;
      }
      s = namebuf;
      if (*str == '{' || *str == '(') {
      int endc = (*str == '{') ? '}' : ')';
      ++str;
      for (;*str;++str) {
        if (*str == endc)
          break;
        if (s < namebuf + sizeof(namebuf)-1)
          *s++ = *str;
      }
      if (*str) ++str; /* End char */
      *s = 0; /* name end */
      } else {
      for (;*str;++str) {
        if (!((isascii(*str) && isalnum(*str)) || *str == '_'))
          break; /* 'A'..'Z', 'a'..'z', '0'..'9', '_' */
        if (s < namebuf + sizeof(namebuf)-1)
          *s++ = *str;
      }
      *s = 0;
      }
      if (*namebuf == 0) /* If there are e.g.  "$/" or "${}" or "$()", or
                      just "$" at the end of the line, then let it be. */
      continue;
      s = (unsigned char*) getzenv((char*)namebuf); /* Pick whatever name there was.. */
      if (!s) continue;     /* No ZENV variable with this name ? */

      len     = strlen((char*)s);
      taillen = strlen((char*)str);

      if (len > (str - s0)) {
      /* Must expand the spot! */

      unsigned char *replacementend = s0  + len;

      if ((replacementend + taillen) >= eol) {
        /* Grows past the buffer end, can't! */
        taillen = eol - replacementend;
      } /* else
           We have space */

      if (taillen > 0) {
        unsigned char *si = str            + taillen;
        unsigned char *so = replacementend + taillen;
        /* Copy also the tail NIL ! */
        for (;taillen>=0; --taillen, --so, --si) *so = *si;
      }

      if ((s0 + len) >= eol)
        /* The fill-in goes over the buffer end */
        len = eol - s0; /* Cut down */
      if (len > 0) { /* Still something can be copied ? */
        memcpy(s0, s, len);
        str = s0 + len;
      } else
        str = s0 + (*s0 == '$'); /* Hmm.. grumble.. */

      } else {

      /* Same space, or can shrink! */

      if (len > 0)
        memcpy(s0, s, len);
      if (s0+len < str)
        /* Copy down */
        strcpy((char*)(s0+len), (const char *)str);
      str = s0 + len;
      str[taillen] = 0; /* Chop the possible old junk from the tail */

      }
    }
    eol[-1] = 0;
}
       

static void cfparam __((char *, int, const char *, int));
static void cfparam(str, size, cfgfilename, linenum)
     char *str;
     int size, linenum;
     const char *cfgfilename;
{
    char *name, *param1, *param2, *param3;
    char *str0 = str;

    name = strchr(str, '\n'); /* The trailing newline chopper ... */
    if (name)
      *name = 0;

    SKIPTEXT (str); /* "PARAM" */
    SKIPSPACE(str);
    name = str;
    SKIPTEXT (str);
    if (*str != 0)
      *str++ = 0;

    if (cistrcmp(name, "help") == 0) {
      int i = 0, helpmax = HELPMAX;
      while (helplines[i] != NULL && i < helpmax)
          ++i;
      param2 = strchr(str, '\n');
      if (param2) *param2 = 0;
      helplines[i] = strdup(str);
      helplines[i + 1] = NULL;      /* This will always stay within the array... */
      return;
    }
    if (cistrcmp(name, "hdr220") == 0) {
      int i = 0, hdrmax = HDR220MAX;
      while (hdr220lines[i] != NULL && i < hdrmax)
        ++i;
      param2 = strchr(str, '\n');
      if (param2) *param2 = 0;
      hdr220lines[i] = strdup(str);
      hdr220lines[i+1] = NULL;
      return;
    }
    if (cistrcmp(name, "sasl-mechanisms") == 0) {
      param2 = strchr(str, '\n');
      if (param2) *param2 = 0;
      SASL_Auth_Mechanisms = strdup(str);
      return;
    }

    /* Do '$' expansions on the string */
    dollarexpand((unsigned char *)str, size - (str - str0));

    SKIPSPACE(str);

    param1 = *str ? str : NULL;

    SKIPTEXT (str);
    if (*str != 0)
      *str++ = 0;
    SKIPSPACE(str);
    param2 = *str ? str : NULL;
    SKIPTEXT (str);
    if (*str != 0)
      *str++ = 0;
    SKIPSPACE(str);
    param3 = *str ? str : NULL;
    SKIPTEXT (str);
    if (*str != 0)
      *str++ = 0;

    /* How many parallel clients a servermode smtpserver allows
       running in parallel, and how many parallel sessions can
       be coming from same IP address */

    if (cistrcmp(name, "same-ip-source-parallel-max") == 0 && param1) {
      sscanf(param1, "%d", &MaxSameIpSource);
    } else if (cistrcmp(name, "MaxSameIpSource") == 0 && param1) {
      sscanf(param1, "%d", &MaxSameIpSource);
    } else if (cistrcmp(name, "MaxParallelConnections") == 0 && param1) {
      sscanf(param1, "%d", &MaxParallelConnections);
    } else if (cistrcmp(name, "max-parallel-connections") == 0 && param1) {
      sscanf(param1, "%d", &MaxParallelConnections);
    }

    /* TCP related parameters */

    else if   (cistrcmp(name, "ListenQueueSize") == 0   && param1) {
      sscanf(param1, "%d", &ListenQueueSize);
    } else if (cistrcmp(name, "tcprcvbuffersize") == 0  && param1) {
      sscanf(param1, "%d", &TcpRcvBufferSize);
    } else if (cistrcmp(name, "tcpxmitbuffersize") == 0 && param1) {
      sscanf(param1, "%d", &TcpXmitBufferSize);
    }

    /* IP address and port binders */

    else if (cistrcmp(name, "BindPort") == 0 && param1) {
      bindport = atoi(param1);
      if (bindport != 0 && bindport != 0xFFFFU)
      bindport_set = 1;
    } else if (cistrcmp(name, "BindAddress") == 0 && param1) {
      called_getbindaddr=1;
      if (!zgetbindaddr(param1, use_ipv6, &bindaddr)) {
      bindaddrs_count += 1;
      bindaddrs = realloc( bindaddrs,
                       sizeof(bindaddr) * (bindaddrs_count +1) );
      if (!bindaddrs)
        bindaddrs_count = 0;
      else
        bindaddrs[ bindaddrs_count-1 ] = bindaddr;
      }
    }

    /* SMTP Protocol limit & policy tune options */

    else if (cistrcmp(name, "maxsize") == 0 && param1) {
      sscanf(param1, "%ld", &maxsize);
    } else if (cistrcmp(name, "min-availspace") == 0 && param1) {
      if (sscanf(param1, "%ld", &minimum_availspace) == 1) {
        /* Minimum of minimum is 1000 kB ! */
        if (minimum_availspace < 1000)
          minimum_availspace = 1000;
      }
    } else if (cistrcmp(name, "RcptLimitCnt") == 0 && param1) {
      sscanf(param1, "%d", &rcptlimitcnt);
      if (rcptlimitcnt < 100) rcptlimitcnt = 100;
    } else if (cistrcmp(name, "RcptLimitCount") == 0 && param1) {
      sscanf(param1, "%d", &rcptlimitcnt);
      if (rcptlimitcnt < 100) rcptlimitcnt = 100;
    } else if (cistrcmp(name, "Rcpt-Limit-Count") == 0 && param1) {
      sscanf(param1, "%d", &rcptlimitcnt);
      if (rcptlimitcnt < 100) rcptlimitcnt = 100;
#if 0
    } else if (cistrcmp(name, "accept-percent-kludge") == 0) {
      percent_accept = 1;
#endif
    } else if (cistrcmp(name, "reject-percent-kludge") == 0) {
      percent_accept = -1;
    } else if (cistrcmp(name, "allowsourceroute") == 0) {
      allow_source_route = 1;
    } else if (cistrcmp(name, "max-error-recipients") == 0 && param1) {
      sscanf(param1, "%d", &MaxErrorRecipients);
    } else if (cistrcmp(name, "max-unknown-commands") == 0 && param1) {
      sscanf(param1, "%d", &unknown_cmd_limit);
    } else if (cistrcmp(name, "sum-sizeoption-value") == 0) {
      sum_sizeoption_value = 1;
    }

    else if (cistrcmp(name, "use-tcp-wrapper") == 0) {
      use_tcpwrapper = 1;
    }

    else if (cistrcmp(name, "tarpit") == 0 && param3 /* 3 params */) {
      sscanf(param1,"%d",&tarpit_initial);
      sscanf(param2,"%d",&tarpit_exponent);
      sscanf(param3,"%d",&tarpit_toplimit);
    }

    else if (cistrcmp(name, "deliverby") == 0) {
      if (param1)
      deliverby_ok = atol(param1);
      else
      deliverby_ok = 0;
    }

    /* Two parameter policydb option: DBTYPE and DBPATH */

    else if (cistrcmp(name, "policydb") == 0 && param2 /* 2 params */) {
      policydefine(&policydb, param1, param2);
    }

    else if (cistrcmp(name, "contentfilter") == 0 && param1) {
      if (access(param1, X_OK) == 0)
      contentfilter = strdup(param1);
    }
    else if (cistrcmp(name, "debug-contentfilter") == 0) {
      debug_content_filter = 1;
    }

    /* A few facility enablers: (default: off) */

    else if (cistrcmp(name, "debugcmd") == 0) {
      debugcmdok = 1;
    } else if (cistrcmp(name, "expncmd") == 0) {
      expncmdok = 1;
    } else if (cistrcmp(name, "vrfycmd") == 0) {
      vrfycmdok = 1;
    } else if (cistrcmp(name, "enable-router") == 0) {
      enable_router = 1;
    } else if (cistrcmp(name, "smtp-auth") == 0) {
      auth_ok = 1;
    } else if (cistrcmp(name, "auth-login-also-without-tls") == 0) {
      auth_login_without_tls = 1;
    } else if (cistrcmp(name, "smtp-auth-sasl") == 0) {
      do_sasl = 1;
    } else if (cistrcmp(name, "msa-mode") == 0) {
      msa_mode = 1;
    } else if (cistrcmp(name, "smtp-auth-pipe") == 0 && param1) {
      smtpauth_via_pipe = strdup(param1);
    }

    /* Store various things into 'rvcdfrom' header per selectors */

    else if (cistrcmp(name, "rcvd-ident") == 0) {
      log_rcvd_ident = 1;
    } else if (cistrcmp(name, "rcvd-whoson") == 0) {
      log_rcvd_whoson = 1;
    } else if (cistrcmp(name, "rcvd-auth-user") == 0) {
      log_rcvd_authuser = 1;
    } else if (cistrcmp(name, "rcvd-tls-mode") == 0) {
      log_rcvd_tls_mode = 1;
    } else if (cistrcmp(name, "rcvd-tls-peer") == 0) {
      log_rcvd_tls_peer = 1;
    }

    /* Some Enhanced-SMTP facility disablers: (default: on ) */

    else if (cistrcmp(name, "nopipelining") == 0) {
      pipeliningok = 0;
    } else if (cistrcmp(name, "noenhancedstatuscodes") == 0) {
      enhancedstatusok = 0;
    } else if (cistrcmp(name, "noenhancedstatus") == 0) {
      enhancedstatusok = 0;
    } else if (cistrcmp(name, "no8bitmime") == 0) {
      mime8bitok = 0;
    } else if (cistrcmp(name, "nochunking") == 0) {
      chunkingok = 0;
    } else if (cistrcmp(name, "nodsn") == 0) {
      dsn_ok = 0;
    } else if (cistrcmp(name, "noehlo") == 0) {
      ehlo_ok = 0;
    } else if (cistrcmp(name, "noetrn") == 0) {
      etrn_ok = 0;
    } else if (cistrcmp(name, "no-multiline-replies") == 0) {
      multilinereplies = 0;
    } else if (cistrcmp(name, "force-rcpt-notify-never") == 0) {
      force_rcpt_notify_never = 1;
    }

#ifdef HAVE_OPENSSL

    /* TLSv1/SSLv* options */

    else if (cistrcmp(name, "use-tls") == 0)
      starttls_ok = 1;        /* Default: OFF */

    else if (cistrcmp(name, "listen-ssmtp") == 0)
      ssmtp_listen = 1;       /* Default: OFF */

    else if (cistrcmp(name, "outlook-tls-bug") == 0) {
      detect_incorrect_tls_use = 1; /* Default: OFF */

    } else if (cistrcmp(name, "tls-cert-file") == 0 && param1) {
      if (tls_cert_file) free((void*)tls_cert_file);
      tls_cert_file = strdup(param1);
      if (!tls_key_file)      /* default the other */
      tls_key_file = strdup(param1);

    } else if (cistrcmp(name, "tls-key-file")  == 0 && param1) {
      if (tls_key_file) free((void*)tls_key_file);
      tls_key_file = strdup(param1);
      if (!tls_cert_file)     /* default the other */
      tls_cert_file = strdup(param1);

    } else if (cistrcmp(name, "tls-dcert-file") == 0 && param1) {
      if (tls_dcert_file) free((void*)tls_dcert_file);
      tls_dcert_file = strdup(param1);
      if (!tls_dkey_file)     /* default the other */
      tls_dkey_file = strdup(param1);

    } else if (cistrcmp(name, "tls-dkey-file")  == 0 && param1) {
      if (tls_dkey_file) free((void*)tls_dkey_file);
      tls_dkey_file = strdup(param1);
      if (!tls_dcert_file)    /* default the other */
      tls_dcert_file = strdup(param1);

    } else if (cistrcmp(name, "tls-dh1024")  == 0 && param1) {
      if (tls_dh1024_param) free((void*)tls_dh1024_param);
      tls_dh1024_param = strdup(param1);

    } else if (cistrcmp(name, "tls-dh512")  == 0 && param1) {
      if (tls_dh512_param) free((void*)tls_dh512_param);
      tls_dh512_param = strdup(param1);

    } else if (cistrcmp(name, "tls-random-source")  == 0 && param1) {
      if (tls_random_source) free((void*)tls_random_source);
      tls_random_source = strdup(param1);

    } else if (cistrcmp(name, "tls-cipher-list")  == 0 && param1) {
      if (tls_cipherlist) free((void*)tls_cipherlist);
      tls_cipherlist = strdup(param1);

    } else if (cistrcmp(name, "tls-CAfile")    == 0 && param1) {
      if (tls_CAfile) free((void*)tls_CAfile);
      tls_CAfile = strdup(param1);

    } else if (cistrcmp(name, "tls-CApath")    == 0 && param1) {
      if (tls_CApath) free((void*)tls_CApath);
      tls_CApath = strdup(param1);

    } else if (cistrcmp(name, "tls-loglevel")  == 0 && param1) {
      sscanf(param1,"%d", & tls_loglevel);

    } else if (cistrcmp(name, "tls-enforce-tls")==0 && param1) {
      sscanf(param1,"%d", & tls_enforce_tls);

    } else if (cistrcmp(name, "tls-ccert-vd")  == 0 && param1) {
      sscanf(param1,"%d", & tls_ccert_vd);

    } else if (cistrcmp(name, "tls-ask-cert")  == 0 && param1) {
      sscanf(param1,"%d", & tls_ask_cert);

    } else if (cistrcmp(name, "tls-require-cert") == 0 && param1) {
      sscanf(param1,"%d", & tls_req_cert);

    } else if (cistrcmp(name, "tls-use-scache") == 0) {
      tls_use_scache = 1;

    } else if (cistrcmp(name, "tls-scache-timeout") == 0 && param1) {
      sscanf(param1,"%d", & tls_scache_timeout);

    } else if (cistrcmp(name, "lmtp-mode") == 0) {
      lmtp_mode = 1;
    }
#endif /* - HAVE_OPENSSL */


    /* Cluster-wide ETRN support for load-balanced smtp relay use */
    else if (cistrcmp(name, "etrn-cluster") == 0 && param3 /* 3 params */) {
      static int idx = 0;
      if (idx < MAX_ETRN_CLUSTER_IDX) {
      etrn_cluster[idx].nodename = strdup(param1);
      etrn_cluster[idx].username = strdup(param2);
      etrn_cluster[idx].password = strdup(param3);
      ++idx;
      }
    }

    else {
      /* XX: report error for unrecognized PARAM keyword ?? */
      type(NULL,0,NULL, "Cfgfile '%s' line %d has bad PARAM keyword/missing parameters: '%s'", cfgfilename, linenum, name);
    }
}

struct smtpconf *
readcffile(name)
     const char *name;
{
    FILE *fp;
    struct smtpconf scf, *head, *tail = NULL;
    char c, *cp, buf[1024], *s, *s0;
    int linenum = 0;

    if ((fp = fopen(name, "r")) == NULL)
      return NULL;
    head = NULL;
    buf[sizeof(buf) - 1] = 0;
    while (fgets(buf, sizeof buf, fp) != NULL) {
      ++linenum;
      c = buf[0];
      if (c == '#' || (isascii(c) && isspace(c)))
          continue;
      if (buf[sizeof(buf) - 1] != 0 &&
          buf[sizeof(buf) - 1] != '\n') {
          int cc;
          while ((cc = getc(fp)) != '\n' &&
               cc != EOF);    /* Scan until end-of-line */
      }
      buf[sizeof(buf) - 1] = 0;     /* Trunc, just in case.. */

      cp = buf;
      SKIPSPACE(cp);
      if (strncmp(cp, "PARAM", 5) == 0) {
          cfparam(cp, sizeof(buf) -(cp-buf), name, linenum);
          continue;
      }
      scf.flags = "";
      scf.next = NULL;
      s0 = cp;
      SKIPTEXT(cp);
      c = *cp;
      *cp = '\0';
      s0 = strdup(s0);
      for (s = s0; *s; ++s)
          if (isascii(*s & 0xFF) && isupper(*s & 0xFF))
            *s = tolower(*s & 0xFF);
      scf.pattern = s0;
      scf.maxloadavg = 999;
      if (c != '\0') {
          ++cp;
          SKIPSPACE(cp);
          if (*cp && isascii(*cp) && isdigit(*cp)) {
            /* Sanity-check -- 2 is VERY LOW */
            if ((scf.maxloadavg = atoi(cp)) < 2)
                scf.maxloadavg = 2;
            SKIPDIGIT(cp);
            SKIPSPACE(cp);
          }
          scf.flags = strdup(cp);
          if ((cp = strchr(scf.flags, '\n')) != NULL)
            *cp = '\0';
      }
      if (head == NULL) {
          head = tail = (struct smtpconf *) emalloc(sizeof scf);
          *head = scf;
      } else {
          tail->next = (struct smtpconf *) emalloc(sizeof scf);
          *(tail->next) = scf;
          tail = tail->next;
      }
      configuration_ok = 1; /* At least something! */
    }
    fclose(fp);
    if (!called_getbindaddr) {
      bindaddr_set = !zgetbindaddr(NULL, use_ipv6, &bindaddr);
      if (bindaddr_set) {
        bindaddrs_count += 1;
        bindaddrs = realloc( bindaddrs,
                         sizeof(Usockaddr) * (bindaddrs_count +1) );
        if (!bindaddrs)
          bindaddrs_count = 0;
        else
          bindaddrs[ bindaddrs_count-1 ] = bindaddr;
      }
    }
    return head;
}

struct smtpconf *
findcf(h)
     const char *h;
{
    struct smtpconf *scfp;
    register char *cp, *s;
    int c;

#ifndef     USE_ALLOCA
    cp = (char*)emalloc(strlen(h) + 1);
#else
    cp = (char*)alloca(strlen(h) + 1);
#endif
    for (s = cp; *h != '\0'; ++h) {
      c = (*h) & 0xFF;
      if (isascii(c) && isalpha(c) && isupper(c))
          *s++ = tolower(c);
      else
          *s++ = c;
    }
    *s = '\0';
    for (scfp = cfhead; scfp != NULL; scfp = scfp->next) {
      if (strmatch(scfp->pattern, cp)) {
#ifndef USE_ALLOCA
          free(cp);
#endif
          return scfp;
      }
    }
#ifndef USE_ALLOCA
    free(cp);
#endif
    return NULL;
}

Generated by  Doxygen 1.6.0   Back to index