| 1 | #! /bin/sh |
| 2 | |
| 3 | # Copyright (c) University of Cambridge, 1995 - 2007 |
| 4 | # See the file NOTICE for conditions of use and distribution. |
| 5 | |
| 6 | # Except when they appear in comments, the following placeholders in this |
| 7 | # source are replaced when it is turned into a runnable script: |
| 8 | # |
| 9 | # CONFIGURE_FILE_USE_NODE |
| 10 | # CONFIGURE_FILE |
| 11 | # BIN_DIRECTORY |
| 12 | # PERL_COMMAND |
| 13 | |
| 14 | # PROCESSED_FLAG |
| 15 | |
| 16 | # A shell+perl wrapper script to run an automated -bh test to check out |
| 17 | # ACLs for incoming addresses. |
| 18 | |
| 19 | # Save the shell arguments because we are going to need the shell variables |
| 20 | # while sorting out the configuration file. |
| 21 | |
| 22 | args="$@" |
| 23 | |
| 24 | # See if this installation is using the esoteric "USE_NODE" feature of Exim, |
| 25 | # in which it uses the host's name as a suffix for the configuration file name. |
| 26 | |
| 27 | if [ "CONFIGURE_FILE_USE_NODE" = "yes" ]; then |
| 28 | hostsuffix=.`uname -n` |
| 29 | fi |
| 30 | |
| 31 | # Now find the configuration file name. This has got complicated because |
| 32 | # CONFIGURE_FILE may now be a list of files. The one that is used is the first |
| 33 | # one that exists. Mimic the code in readconf.c by testing first for the |
| 34 | # suffixed file in each case. |
| 35 | |
| 36 | set `awk -F: '{ for (i = 1; i <= NF; i++) print $i }' <<End |
| 37 | CONFIGURE_FILE |
| 38 | End |
| 39 | ` |
| 40 | while [ "$config" = "" -a $# -gt 0 ] ; do |
| 41 | if [ -f "$1$hostsuffix" ] ; then |
| 42 | config="$1$hostsuffix" |
| 43 | elif [ -f "$1" ] ; then |
| 44 | config="$1" |
| 45 | fi |
| 46 | shift |
| 47 | done |
| 48 | |
| 49 | # Search for an exim_path setting in the configure file; otherwise use the bin |
| 50 | # directory. BEWARE: a tab character is needed in the command below. It has had |
| 51 | # a nasty tendency to get lost in the past. Use a variable to hold a space and |
| 52 | # a tab to keep the tab in one place. |
| 53 | |
| 54 | exim_path=`perl -ne 'chop;if (/^\s*exim_path\s*=\s*(.*)/){print "$1\n";last;}' $config` |
| 55 | if test "$exim_path" = ""; then exim_path=BIN_DIRECTORY/exim; fi |
| 56 | |
| 57 | |
| 58 | ######################################################################### |
| 59 | |
| 60 | |
| 61 | # Now run the perl script, passing in the Exim path and the arguments given |
| 62 | # to the overall script. |
| 63 | |
| 64 | PERL_COMMAND - $exim_path $args <<'End' |
| 65 | |
| 66 | BEGIN { pop @INC if $INC[-1] eq '.' }; |
| 67 | use FileHandle; |
| 68 | use File::Basename; |
| 69 | use IPC::Open2; |
| 70 | |
| 71 | if ($ARGV[0] eq '--version') { |
| 72 | print basename($0) . ": $0\n", |
| 73 | "build: EXIM_RELEASE_VERSIONEXIM_VARIANT_VERSION\n", |
| 74 | "perl(runtime): $]\n"; |
| 75 | exit 0; |
| 76 | } |
| 77 | |
| 78 | if (scalar(@ARGV) < 3) |
| 79 | { |
| 80 | print "Usage: exim_checkaccess <IP address> <email address> [exim options]\n"; |
| 81 | exit(1); |
| 82 | } |
| 83 | |
| 84 | $exim_path = $ARGV[0]; # Set up by the calling shell script |
| 85 | $host = $ARGV[1]; # Mandatory original first argument |
| 86 | $recipient = $ARGV[2]; # Mandatory original second argument |
| 87 | |
| 88 | $c4 = qr/2 (?:[0-4]\d | 5[0-5]) | 1\d\d | \d{1,2}/x; # IPv4 component |
| 89 | $a4 = qr/^$c4\.$c4\.$c4\.$c4$/; # IPv4 address |
| 90 | |
| 91 | $c6 = qr/[0-9a-f]{1,4}/i; # IPv6 component |
| 92 | |
| 93 | # Split the various formats of IPv6 addresses into several cases. I don't |
| 94 | # think I can graft regex that matches all of them without using alternatives. |
| 95 | |
| 96 | # 1. Starts with :: followed by up to 7 components |
| 97 | |
| 98 | $a6_0 = qr/^::(?:$c6:){0,6}$c6$/x; |
| 99 | |
| 100 | # 2. 8 non-empty components |
| 101 | |
| 102 | $a6_1 = qr/^(?:$c6:){7}$c6$/x; |
| 103 | |
| 104 | # 3. This is the cunning one. Up to 7 components, one (and only one) of which |
| 105 | # can be empty. We use 0 to cause a failure when we've already matched |
| 106 | # an empty component and may be hitting other. This has to fail, because we |
| 107 | # know we've just failed to match a component. We also do a final check to |
| 108 | # ensure that there has been an empty component. |
| 109 | |
| 110 | $a6_2 = qr/^(?: (?: $c6 | (?(1)0 | () ) ) : ){1,7}$c6 $ (?(1)|.)/x; |
| 111 | |
| 112 | if ($host !~ /$a4 | $a6_0 | $a6_1 | $a6_2/x) |
| 113 | { |
| 114 | print "** Invalid IP address \"$host\"\n"; |
| 115 | print "Usage: exim_checkaccess <IP address> <email address> [exim options]\n"; |
| 116 | exit(1); |
| 117 | } |
| 118 | |
| 119 | # Build any remaining original arguments into a string for passing over |
| 120 | # as Exim options. |
| 121 | |
| 122 | $opt = ""; |
| 123 | for ($i = 3; $i < scalar(@ARGV); $i++) { $opt .= "$ARGV[$i] "; } |
| 124 | |
| 125 | # If the string contains "-f xxxx", extract that as the sender. Otherwise |
| 126 | # the sender is <>. |
| 127 | |
| 128 | $sender = ""; |
| 129 | if ($opt =~ /(?:^|\s)-f\s+(\S+|"[^"]*")/) |
| 130 | { |
| 131 | $sender = $1; |
| 132 | $opt = $` . $'; |
| 133 | } |
| 134 | |
| 135 | # Run a -bh test in Exim, passing the test data |
| 136 | |
| 137 | $pid = open2(*IN, *OUT, "$exim_path -bh $host $opt 2>/dev/null"); |
| 138 | print OUT "HELO [$host]\r\n"; |
| 139 | print OUT "MAIL FROM:<$sender>\r\n"; |
| 140 | print OUT "RCPT TO:<$recipient>\r\n"; |
| 141 | print OUT "QUIT\r\n"; |
| 142 | close OUT; |
| 143 | |
| 144 | # Read the output, ignoring anything but the SMTP response to the RCPT |
| 145 | # command. |
| 146 | |
| 147 | $count = 0; |
| 148 | $reply = ""; |
| 149 | |
| 150 | while (<IN>) |
| 151 | { |
| 152 | next if !/^\d\d\d/; |
| 153 | $reply .= $_; |
| 154 | next if /^\d\d\d\-/; |
| 155 | |
| 156 | if (++$count != 4) |
| 157 | { |
| 158 | $reply = ""; |
| 159 | next; |
| 160 | } |
| 161 | |
| 162 | # We have the response we want. Interpret it. |
| 163 | |
| 164 | if ($reply =~ /^2\d\d/) |
| 165 | { |
| 166 | print "Accepted\n"; |
| 167 | } |
| 168 | else |
| 169 | { |
| 170 | print "Rejected:\n"; |
| 171 | $reply =~ s/\n(.)/\n $1/g; |
| 172 | print " $reply"; |
| 173 | } |
| 174 | last; |
| 175 | } |
| 176 | |
| 177 | # Reap the child process |
| 178 | |
| 179 | waitpid $pid, 0; |
| 180 | |
| 181 | End |