Recipes

Recipes

A recipe is how you tell Procmail what to search for and then what to do when it finds something. A recipe is the heart of Procmail. You create a recipe for each type of thing you want to search for or an action you want to perform. There are many good resources on the Internet on creating recipes, and your best bet is to start at the Procmail Web site. So where do you put your recipes once they're created? You put them in your home folder in a file named .procmailrc, of course. Here is a beginning .procmailrc that has two recipes in it:

 01 #--------------- 
 02 # my .procmailrc file 
 03 #---------------- 
 04 SHELL=/bin/bash # path to your sh (try "which sh" to find it) 
 05 MAILDIR=${HOME}/Mail # e-mail goes here after your client gets it 
 06 LOGFILE=${MAILDIR}/procmail.log 
 07 LOG="--- Logging ${LOGFILE} for ${LOGNAME}, " 
 08 
 09 #------------------------------------- 
 10 # Your recipes (the order is extremely important!) 
 11 #------------------------------------- 
 12 :0 
 13 * ^Subject: test test test 
 14 /dev/null 
 15 
 16 #------------------------------ 
 17 # Accept all the rest to your default mailbox 
 18 #------------------------------ 
 19 :0: 
 20 ${DEFAULT} 

I stuck the line numbers in so I can reference each important line for this example. You should not put line numbers in your file, since they would really mess things up.

Lines 04 through 07 are fairly obvious, so just follow the comments. Lines 12 through 14 make up the first recipe. This recipe will search for the string "Subject: test test test" at the beginning of a line. Remember, all your e-mails get sent to Procmail as one giant file, but Procmail knows how to logically break up the file into individual e- mails and check for whatever pattern you give it. In this case, line 13 is the pattern you want to search for. The ^ at the beginning means that the string of text you are looking for must be anchored at the beginning of the line. Line 14 directs Procmail to send the matching e-mail to the digital garbage can, black hole, or bit bucket, if you prefer. Each e-mail is passed from the first recipe to the next until a match is made. If you haven't already figured it out, the search criterion is a regular expression.

If you know regular expressions, then you already know how to enter a search string for any text you want to search for. If you don't know how to write regular expressions, then I highly recommend reading up on them. They are powerful and are used in many of the best tools available for Linux. Line 14 instructs Procmail to take the e-mail where the regular expression matches and send it to the null device. In other words, it copies the text to the null device and removes it from the e-mail file. Can you say, "spam filter"? I can think of a few rules to filter out unwanted e-mails. Of course, there are numerous examples on the Internet of spam filters for Procmail. Just search Google.com for them.

Another good use for Procmail is to direct e-mails from various e-mail lists into their own e-mail folder in your e-mail folder. This is beyond the scope of this chapter, but you can experiment and read some lists of frequently asked questions (FAQ) to get the answers on how to achieve this functionality. The next recipe is probably the most important recipe of them all.

The second recipe, on lines 19 and 20, is the catchall recipe for any e-mail not processed previously. When I wrote my first .procmailrc file, I neglected to include this recipe and my e-mail simply went away. Don't make this mistake. This last recipe tells Procmail to put all e-mail not caught by previous recipes into your normal mailbox location ( /var/mail/your_account ). Wait ”I thought it was in my mail folder in my home folder? Nope. You see, when Fetchmail picks up the e-mail from your ISP, it delivers it to your MDA, and your MDA looks for a .forward file before writing the e-mail to the /var/mail/your_account file. The .forward file tells your MDA to send the incoming e-mail to the address within. The neat thing is that you can also pipe the e-mail to a program, like Procmail. Procmail acts like a filter and has the ability to remove e-mail from the giant e-mail file. Once Procmail is done with it, the e-mail not filtered out goes to /var/mail/your_account, where your normal e-mail client can pick it up. Make sense? I'll talk more about the contents of the .forward file in a later section. So what's all that other stuff, like :0, *, :0:>, and ${DEFAULT} , all about? Here's a table that should sort it all out for you:

:0

Begin a recipe

:0:

Begin a recipe and use a lock file. Use a lock file to prevent multiple Procmails from trying to write to your mail file at the same time. (From the Procmail FAQ: "Rule of thumb: Use file locking when delivering to a file. Don't use file locking when delivering to /dev/null. "

*

Begin a condition.

${DEFAULT}

This is your default e-mail location ( /var/mail/your_account )

(pipe) Execute a command

So filtering e-mail is great and all, but what about executing commands? No problem! Procmail has support for that too and can even execute scripts. The recipe I use for the e-mail console is as follows :

 :0 
 * ^Subject: <console/> 
 /usr/bin/perl /home/stmurphy/bin/email_console.pl 

For our e-mail console recipe, what we want to do is look for a pattern in the subject line that indicates that the e-mail contains a command to be executed and its output e-mailed back to the sender. The best way to parse this e-mail and execute those commands is to write a Perl script and have Procmail execute the Perl script, passing the entire e-mail to the script. It's important to do it this way for reasons that will become obvious in the later section Securing Everything. You should notice that I am putting the email_console.pl script in a folder named bin in my home folder. This is just a convenient place to put stuff I want executed by my account only. Now we'll concentrate on the Perl script.

The Script

Before we get to the script, let's define some requirements. Let's say that in order for Procmail to pick up and pass an e-mail with a command to execute to our Perl script, the subject line must contain the following phrase:

 <console/> 

Simple enough. I'm using an XML-style tag here. I could use any word, but that leaves me open to accidents. I should be okay so long as no one sends me an e-mail with a subject line like:

 <console/> is the XML tag I was talking about . . . let's talk 

Of course, once you become a Procmail guru, you'll find even better ways to obfuscate the subject line. Moving on, let's also decide that the e-mail body will contain zero or more commands to execute. The Perl script should not barf on an empty e-mail and should also be capable of handling more than one command. Each command output should be separated by some kind of horizontal rule:

 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 

Got it? Okay. One more requirement is that every command must be surrounded by exec tags. For example:

 <exec>......</exec> 

Nice huh? Well, I think so! This will make parsing the file easier. To make life really easy on us, let's also require that commands be on a single line. Parsing multiple lines for a single command block would complicate parsing too much for this basic example. Lastly, we e-mail the results back to the sender, either using the From: or Reply-To : fields. We'll use xmail to accomplish this small part, so make sure it's installed on your system.

Figure 7-5 presents an example e-mail file that I sent from my Web e-mail account, digitalman@musician.org (hosted by www.mail.com ). Ouch! Lots of stuff in there, but we're almost done. In this e-mail, I am asking the e-mail console to execute three different command blocks. The first is the free command. The second is a set of three commands, cd, pwd, and ls. The final command is the netstat command. It's the job of the Perl script to make sense of this mess. Luckily, this is one of the last things we have to do. That is, we have to build the Perl script that parses the e-mail. Here's the code:

Figure 7-5. E-mail header from digitalman.

 #!/usr/bin/perl 
 
 use IPC::Open3; 
 use Symbol; 
 
 #----------------------- 
 # setup vars for open3 
 #----------------------- 
 $WTR = gensym(); 
 $RDR = gensym(); 
 $ERR = gensym(); 
 
 #--------------------------- 
 # some handy variable defs 
 #--------------------------- 
 $logfile = "/home/stmurphy/.email_console.log"; 
 $line_sep = 0; 
 $separator = "\n" . "~' x 72 . "\n"; 
 $theOutput = "\n\n"; 
 $reply_subject = "E-mail Console Results"; 
 $signature_line = "\n--\nOutput generated by the E-mail Console\n"; 
 $no_commands_mesg = "No commands found to execute."; 
 $from = ""; 
 $reply_to = ""; 
 
 #--------------- 
 # open log file 
 #--------------- 
 open LOG, "<<$logfile"; 
 
 #-------------------------- 
 # our parse/execution loop 
 #-------------------------- 
 while(<>) { 
 
 #------------------------ 
 # get the sender address 
 #------------------------ 
 if ($_ =~ m/From: (.*)/) { 
 $from = ; 
 } 
 
 #--------------------------------- 
 # get the sender address override 
 #--------------------------------- 
 if ($_ =~ m/Reply-To: (.*)/) { 
 $reply_to = ; 
 } 
 
 #--------------------------- 
 # look for <exec>...</exec> 
 # and process the command 
 #--------------------------- 
 if ($_ =~ m/^<exec>(.*)<\/exec>/) { 
 undef $cmd_output; 
 if ($line_sep) { 
 $theOutput .= $separator; 
 } else { 
 $line_sep = 1; 
 } 
 $who = ($reply_to ne "") ? "F:$from R:$reply_to" : $from; 
 print LOG scalar localtime() . " $who executed []\n"; 
 $theOutput .= "Executing []\n\n"; 
 
 #------------------------ 
 # execute the command(s) 
 #------------------------ 
 open3($WTR, $RDR, $ERR,); 
 close($WTR); 
 while (<$RDR>) { 
 $cmd_output .= $_; 
 } 
 while (<$ERR>) { 
 $cmd_output .= $_; 
 } 
 $theOutput .= $cmd_output; 
 } 
 } 
 
 #----------------------------- 
 # report if there was 
 # no command executed at all 
 #----------------------------- 
 if (!$line_sep) { 
 $theOutput .= $no_commands_mesg; 
 } 
 
 #--------------------------- 
 # tack on a handy dandy 
 # signature line 
 #--------------------------- 
 $theOutput .= $signature_line; 
 
 #----------------------------- 
 # override the from address 
 # with the reply-to address 
 #----------------------------- 
 if ($reply_to ne "") { 
 $from = $reply_to; 
 } 
 
 #---------------------- 
 # close the log file 
 #---------------------- 
 close LOG; 
 
 #----------------------- 
 # send the reply e-mail 
 # with the execution 
  # results  
  #-----------------------  
  exec "echo \"$theOutput\"  mailx -s \"$reply_subject\" \"$from\"";  
   
  exit;  

I won't go line by line into the code, since it's so short. However, I will point out that there are many other possible ways to write this code. I kept this code rather simple because it gets the point across. Even so, a nice improvement would be to put in a sigalarm-catching procedure in case you tell the e-mail console to run an interactive application like top. If you did execute top, the email_console.pl process for that session would simply get stuck waiting for input from you ”which you could never give. Looking on, the code accepts multiple <exec> .</exec> lines and executes whatever the command is in the context of the user running the Fetchmail command. This could be root if you so desired, but I would highly recommend getting some common sense before allowing the world to execute commands on your system as root via e-mail. Not a good idea! This script also allows you to chain commands using && and ; if the shell that Perl is running under supports it. At this point, I can safely say the end of this project is nearing and things should be coming together real soon.

Gluing It All Together

The final task is to create your .forward file in your home folder. This is how Procmail actually gets executed by your MDA.

  IFS=' ' && exec /usr/bin/procmail  

Make sure the first character is the vertical bar, or pipe, and that there are no spaces after it and before the next command. This can cause ugly problems. (From the Procmail FAQ: "IFS=' ' sets the shell's "internal field separator" to a single space. This is mostly just in order to prevent some old Sendmail hacks that were based on setting the IFS to something else.")

So there you have it. You should be able to send yourself an e-mail with <console/> as the subject and the command, free, encapsulated with the XML-style tags, <exec> </exec>, and your e-mail console should come to life. Of course, at this point, even I could execute commands on your system if I knew your e-mail address.

Right now, the e-mail console is totally open and unsecured. (Was that redundant?) Anyway, in order to keep unwanted people from sending e-mail console commands to your system, you need to have a way to authenticate who is sending the command and determine if they are allowed to do this. Additionally, the information being e-mailed back to you is in plain text. If you don't want your command results being returned to you in plain text, you will have to encrypt the results. For these two tasks , encryption and authentication/authorization, we'll use another very handy Linux tool, GnuPG.

 



Multitool Linux. Practical Uses for Open Source Software
Multitool Linux: Practical Uses for Open Source Software
ISBN: 0201734206
EAN: 2147483647
Year: 2002
Pages: 257

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net