Cover V13, i10

Article

oct2004.tar

Questions and Answers

Amy Rich

Q We use majordomo to manage a large number of mailing lists. One of the nice features is that it checks the message body and bounces the message to the moderator if the message looks like something that should have gone to majordomo instead of to the list (e.g., unsubscribe, subscribe, remove me, etc.). Normally, this is great, since it prevents a lot of garbage traffic on the lists, but in some cases it tends to bounce valid mail. Is there a way to fine-tune this behavior so that we don't get valid list mail bounced to the moderator?

A Majordomo uses a set of regular expression matches for the Subject: header and the first ten lines of the body. If you take a look at majordomo.cf, you'll see the following near the end:

# List of perl regular expressions that, if found in the headers 
# of a message, will cause the message to be bounced to the list 
# approver. Put each regular expression on a separate line before 
# the "END" mark, with no trailing ";"
   
# Administrative checks. These used to be buried in the resend code
$admin_headers = <<'END';
(regular expressions deleted)
END
   
# Common things that people send to the wrong address.
# These are caught in the first 10 lines of the message body 
# if 'administrivia' is turned on and the message isn't marked 
# approved.
$admin_body = <<'END';
(regular expressions deleted)
END
   
# taboo headers to catch
$global_taboo_headers = <<'END';
(regular expressions deleted)
END
   
# Taboo body contents to catch and forward to the approval address
$global_taboo_body = <<'END';
END
You can modify any of these regular expressions to suit your needs, but having majordomo magically know that one message should be passed and another with the same regex match should be bounced to the moderator isn't possible.

Q We've started using ipfilter on our Solaris 8 machines and now sshd (OpenSSH) is closing connections to the machines if they are idle for some short period of time (say 5 minutes or so). According to the sshd_config man page, KeepAlive should be enabled by default, so I'm not certain why this is happening. Is there something else I can try?

A I've also seen this happen with OpenSSH 3.7.1p2 and an ipfiltered Solaris 8 machine with some pretty restrictive filters. You might want to explicitly enable KeepAlive in /usr/local/etc/sshd_config or, even better, use the ClientAliveInterval setting to send encrypted, unspoofable keepalive-style messages to the clients. From the sshd_config man page:

    ClientAliveInterval

    Sets a timeout interval in seconds after which if no data has been received from the client, sshd will send a message through the encrypted channel to request a response from the client. The default is 0, indicating that these messages will not be sent to the client. This option applies to protocol version 2 only.

I generally set ClientAliveInterval to 60 so that it only sends a message after a minute of inactivity. To tune how fast a dead connection will time out, also take a look at the ClientAliveCountMax setting:

    ClientAliveCountMax

    Sets the number of client alive messages (see above) which may be sent without sshd receiving any messages back from the client. If this threshold is reached while client alive messages are being sent, sshd will disconnect the client, terminating the session. It is important to note that the use of client alive messages is very different from KeepAlive. The client alive messages are sent through the encrypted channel and therefore will not be spoofable. The TCP keepalive option enabled by KeepAlive is spoofable. The client alive mechanism is valuable when the client or server depend on knowing when a connection has become inactive.

    The default value is 3. If ClientAliveInterval (above) is set to 15, and ClientAliveCountMax is left at the default, unresponsive ssh clients will be disconnected after approximately 45 seconds.

Q I was talking to another sys admin at USENIX who mentioned that sendmail has a variable called greeting_pause that tells sendmail to wait X number of seconds before sending the SMTP greeting message to connecting machines. If any traffic is received before the SMTP greeting is sent, then sendmail throws the entire connection away. This sounds like a great feature that would catch a lot of the scripted spam that doesn't bother waiting for the greeting and just slams the server when it connects. Unfortunately, I've dug through all my sendmail documentation and I can't find any reference to this variable. Do you know anything about this sort of functionality? Is it in some sort of hack or milter add on?

A The feature you describe is actually called greet_pause, not greeting_pause, and is only available in sendmail 8.13. Also, the timeout is in milliseconds, not seconds, so if you do have sendmail 8.13, be sure that you're setting the timeout high enough. A few caveats, as well... Although this feature does help block a lot of scripted spam, you might also wind up rejecting valid mail from impatient servers, like verizon.net for example, if you set your timeout too high. In your mc file, make sure to put FEATURE('greet_pause') after FEATURE('access_db').

If you don't want to move to the development version of sendmail but are willing to modify the sendmail 8.12.11 source code, you can also backport the greet_pause feature by modifying sendmail-8.12.11/sendmail/srvrsmtp.c. These directions are taken from Thomas Schulz's post to comp.mail.sendmail:

http://groups.google.com/groups?selm=cbc4tv%24p1r%241%40bluegill.adi.com&rnum=2
Edit the file and swap lines 451 and 452:

451  #if STARTTLS
452    int r;
Insert a new line of code at line 20:

#include <sm/fdset.h>
Next, add the following code from sendmail-8.13.0 at line 800 of the sendmail-8.12.11 srvrsmtp.c source code:

   /*
   **  Broken proxies and SMTP slammers
   **  push data without waiting, catch them
   */

   if (
#if STARTTLS
  !smtps &&
#endif /* STARTTLS */
  *greetcode == '2') 
   {
 time_t msecs = 0;
 char **pvp;
 char pvpbuf[PSBUFSIZE];

 /* Ask the rulesets how long to pause */
 pvp = NULL; 
 r = rscap("greet_pause", peerhostname,
 anynet_ntoa(&RealHostAddr), e,
 &pvp, pvpbuf, sizeof(pvpbuf));
 if (r == EX_OK && pvp != NULL && pvp[0] != NULL && 
(pvp[0][0] & 0377) == CANONNET && pvp[1] != NULL)
 {
    msecs = strtol(pvp[1], NULL, 10);
 }

 if (msecs > 0)
 {
    int fd;
    fd_set readfds;
    struct timeval timeout;
    /* pause for a moment */
    timeout.tv_sec = msecs / 1000;
    timeout.tv_usec = (msecs % 1000) * 1000;

    /* Obey RFC 2821: 4.3.5.2: 220 timeout of 5 minutes */
    if (timeout.tv_sec >= 300)
    {
  timeout.tv_sec = 300;
  timeout.tv_usec = 0;
    }

    /* check if data is on the socket during the pause */
    fd = sm_io_getinfo(InChannel, SM_IO_WHAT_FD, NULL);
    FD_ZERO(&readfds);
    SM_FD_SET(fd, &readfds);
    if (select(fd + 1, FDSET_CAST &readfds,
   NULL, NULL, &timeout) > 0 &&
   FD_ISSET(fd, &readfds))
    {
  greetcode = "554";
  nullserver = "Command rejected";
  sm_syslog(LOG_INFO, e->e_id,
  "rejecting commands from %s [%s] due
to pre-greeting traffic",
  peerhostname,
  anynet_ntoa(&RealHostAddr));
    }
 }
   }
Rebuild and install the sendmail-8.12.11 binary, and you'll have the greet_pause functionality. You'll also need to add the file sendmail-8.13.0/cf/feature/greet_pause.m4 to the directory where you store your sendmail-8.12.11 m4 files so you can generate a new cf file from your mc file.

Q I'm trying to use find to gather a list of filenames on my OS X 10.1.3 machine, which I will then iterate through and perform some action on. These actions vary from chown/chmod to using sed to replace strings inside the file. The main problem I'm having is that OS X insists in creating files and directories with spaces in them. Every syntax I've used with find splits the filenames up at the space. As a Unix person, I find this highly irritating, and I know there must be a way to work around it. Any suggestions?

A This is a common question to which there are a few answers. Since you don't specify exactly what you're trying to do with the data, I'll just quote directly form the comp.unix.shell FAQ, question 16. If you need to use find, the last example may be the most applicable:

    16. How do I loop through files with spaces in their name?

    So, you're going to loop through a list of files? How is this list stored? If it's stored as text, there probably was already an assumption about the characters allowed in a filename. Every character except '\0' (NUL) is allowed in a file path on Unix. So the only way to store a list of file names in a file is to separate them by a '\0' character (if you don't use a quoting mechanism as for xargs input).

    Unfortunately most shells (except zsh) and most standard Unix text utilities (except GNU ones) can't cope with "\0" characters. Moreover, many tools, like "ls", "find", "grep -l" output a \n separated list of files. So, if you want to postprocess this output, the simpler way is to assume that the filenames don't contain newline characters (but beware that once you make that assumption, you can't pretend anymore your code is reliable (and thus can't be exploited)).

    So, if you've got a newline separated list of files in a list.txt file, Here are two ways to process it:

    1:

    while IFS= read -r file <&3; do
      something with "$file" # be sure to quote "$file"
    done 3< list.txt
    
    (if your read doesn't have the "-r" option, either make another assumption that filenames don't contain backslashes, or use:

    exec 3<&0
    sed 's/\\/&&/g' < list.txt |
    while IFS= read file; do
      something with "$file" <&3 3<&-
    done
       )
       
    2:

    IFS="
    " # set the internal field separator to the newline character
      # instead of the default "<space><tab><NL>".
    
    set -f # disable filename generation (or make the assumption 
      # that filenames don't contain *, [ or ? characters (maybe 
      # more depending on your shell)).
    
    for file in $(cat < list.txt); do
      something with "$file" # it's less a problem if you forget to
    # quote $file here.
    done
    
    Now, beware that there are things you can do before building this list.txt. There are other ways to store filenames. For instance, you have the positional parameters.

    With:

    set -- ./*.txt
    
    you have the list of txt files in the current directory, and no problem with weird characters. Looping through them is just a matter of:

    for file
    do something with "$file"
    done
    
    You can also escape the separator. For instance, with:

    find . -exec sh -c 'printf %s\\n "$1" | sed -n '"':1
      \$!{N;b1
      }
      s/|/|p/g;s/\n/|n/g;p'" '{}' '{}' \;
      
    instead of:

    find . -print
    
    you have the same list of files except that the \n in filenames are changed to "|n" and the "|" to "|p". So that you're sure there's one filename per line and you have to convert back "|n" to "\n" and "|p" to "|" before referring to the file.
Amy Rich, president of the Boston-based Oceanwave Consulting, Inc. (http://www.oceanwave.com), has been a UNIX systems administrator for more than 10 years. She received a BSCS at Worcester Polytechnic Institute, and can be reached at: qna@oceanwave.com.