# -*- indent-tabs-mode: t; tab-width: 4; -*- ##### ##### This file is part of Mail-in-a-Box-LDAP which is released under the ##### terms of the GNU Affero General Public License as published by the ##### Free Software Foundation, either version 3 of the License, or (at ##### your option) any later version. See file LICENSE or go to ##### https://github.com/downtownallday/mailinabox-ldap for full license ##### details. ##### # _test_greylisting_x() { # helper function sends mail and checks that it was greylisted local email_to="$1" local email_from="$2" start_log_capture start_mail_capture "$email_to" record "[Send mail anonymously TO $email_to FROM $email_from]" local output output="$($PYMAIL -no-delete -f $email_from -to $email_to '' $PRIVATE_IP '' '' 2>&1)" local code=$? if [ $code -eq 0 ]; then wait_mail local file=( $(get_captured_mail_files) ) record "[Check captured mail for X-Greylist header]" if ! grep "X-Greylist: delayed" <"$file" >/dev/null; then record "not found" test_failure "message not greylisted - X-Greylist header missing" record_captured_mail else record "found" fi else assert_python_failure $code "$output" "SMTPRecipientsRefused" "Greylisted" fi check_logs } postgrey_whitelist_recipents() { local wl="/etc/postgrey/whitelist_recipients.local" truncate --size=0 "$wl" || die "Could not truncate $wl" local recipient for recipient; do echo "$recipient" >> "$wl" || \ die "Could not add postgrey whitelist recipient to $wl" done if ! systemctl reload postgrey >/dev/null 2>&1; then systemctl restart postgrey >>$TEST_OF 2>&1 fi } postgrey_reset_whitelists() { local wl="/etc/postgrey/whitelist_recipients.local" truncate --size=0 --no-create "$wl" || die "Could not truncate $wl" if ! systemctl reload postgrey >/dev/null 2>&1; then systemctl restart postgrey >>$TEST_OF 2>&1 fi } postgrey_reset_state() { # when postgrey receives a message for processing that is suspect, # it will: # 1. initally reject it # 2. after a delay, permit delivery (end entity must resend), # but with a X-Greyist header # 3. subsequent deliveries will succeed with no header # modifications # # because of #3, reset postgrey to establish a "clean" greylisting # testing scenario # record "[Reset postgrey]" #local db="/var/lib/postgrey" local db="$STORAGE_ROOT/mail/postgrey/db" if [ ! -d "$db" ]; then die "Postgrey database directory $db does not exist!" fi systemctl stop postgrey >>$TEST_OF 2>&1 || die "unble to stop postgrey" if ! rm -f "$db/*" >>$TEST_OF 2>&1; then systemctl start postgrey >>$TEST_OF 2>&1 die "unable to remove the postgrey database files in $db" fi systemctl start postgrey >>$TEST_OF 2>&1 || die "unble to start postgrey" } test_greylisting() { # test that mail is delayed by greylisting test_start "greylisting" # reset postgrey's database to start the cycle over postgrey_reset_state # create standard user alice local alice="alice@somedomain.com" create_user "$alice" "alice" # IMPORTANT: bob's domain must be from one that has no SPF record # in DNS. At the time of creation of this script, yahoo.com's # is set to "?all" ("neutral" or "no policy statement") local bob="bob@yahoo.com" # send to alice anonymously from bob _test_greylisting_x "$alice" "$bob" delete_user "$alice" test_end } test_relay_prohibited() { # test that the server does not relay test_start "relay-prohibited" start_log_capture record "[Attempt relaying mail anonymously]" local output output="$($PYMAIL -no-delete -f joe@badguy.com -to naive@gmail.com '' $PRIVATE_IP '' '' 2>&1)" assert_python_failure $? "$output" "SMTPRecipientsRefused" "Relay access denied" check_logs test_end } test_spf_fail() { # test mail rejection due to SPF policy of envelope FROM address test_start "spf-fail" # create standard user alice local alice="alice@somedomain.com" create_user "$alice" "alice" # who we will impersonate # # note: we've configured policyd-spf to fail instead of softfail # on google.com local from="test@google.com" local domain=$(awk -F@ '{print $2}' <<<"$from") # send to alice anonymously from imposter start_log_capture start_mail_capture "$alice" record "[Test SPF for $domain FROM $from TO $alice]" local output output="$($PYMAIL -no-delete -f $from -to $alice '' $PRIVATE_IP '' '' 2>&1)" local code=$? if ! assert_python_failure $code "$output" "SMTPRecipientsRefused" "SPF" && [ $code -eq 0 ] then wait_mail record_captured_mail fi check_logs delete_user "$alice" test_end } test_spf_softfail() { # When policyd-spf-postfix determines a message's SPF disposition # is "Softfail", it attaches a "Received-SPF: Softfail" header # (see RFC 7208) and allows the message to move along the postfix # delivery chain. # # PR #1836 sets custom spam assassin rules that cause spam # assassin to add a score of 5.0 for Softfail messages (see # setup/dkim.sh and setup/spamassassin.sh) # # This test checks that a softfail message is properly scored. local SPF_SOFTFAIL_SCORE="5.0" test_start "spf-softfail" # create user alice local alice="alice@somedomain.com" local alice_pw="123alice" create_user "$alice" "$alice_pw" # who we will impersonate # # important: the MAIL FROM address we choose here must currently # have dns spf configuration set to softfail (eg: ~all) # local from="test@guest.com" local domain=$(awk -F@ '{print $2}' <<<"$from") # alice must be whitelisted to avoid greylisting postgrey_whitelist_recipents "$alice" # send to alice anonymously from imposter start_log_capture start_mail_capture "$alice" record "[Test SPF for $domain FROM $from TO $alice]" local output local subject="$(generate_uuid)" output="$($PYMAIL -no-delete -subj "$subject" -f $from -to $alice '' $PRIVATE_IP '' '' 2>&1)" if assert_python_success $? "$output"; then if ! wait_mail; then test_failure "Timeout waiting for mail" else record_captured_mail local files=( $(get_captured_mail_files) ) local score score=$(grep -F "SPF_SOFTFAIL" "$files" | grep -F '*' | awk '{print $2}') floor_score=$(awk -F. '{print $1}' <<<"$score") floor_expected=$(awk -F. '{print $1}' <<<"$SPF_SOFTFAIL_SCORE") if [ -z "$score" ]; then test_failure "No spam score for SPF_SOFTFAIL" elif [ $floor_score -ne $floor_expected ]; then test_failure "Got score $score, but exptected $SPF_SOFTFAIL_SCORE" fi fi # clean up: delete the delivered mail record "[delete delivered message]" output="$($PYMAIL -no-send -subj $subject -timeout 1 $PRIMARY_HOSTNAME $alice "$alice_pw" 2>&1)" record "$output" fi check_logs # clean up postgrey_reset_whitelists delete_user "$alice" test_end } test_dmarc_reject() { # PR #1836 sets custom spam assassin rules that cause spam # assassin to add a score of 10.0 for dmarc p=reject messages (see # setup/dkim.sh and setup/spamassassin.sh) # # This test checks that a p=reject message is properly scored. local DMARC_FAIL_REJECT_SCORE="10.0" test_start "dmarc-reject" # create user alice - must be a domain that does not result in SPF Fail local alice="alice@guest.com" local alice_pw="alice123" create_user "$alice" "$alice_pw" # who we will impersonate: their domain (_dmarc.domain.com dns TXT # record), must have a dmarc policy with p=reject local header_from="test@google.com" # alice must be whitelisted to avoid greylisting postgrey_whitelist_recipents "$alice" # send to alice from alice with header From: imposter start_log_capture start_mail_capture "$alice" record "[Test dmarc reject TO $alice, From: $header_from]" local subject="$(generate_uuid)" local output output="$($PYMAIL -smptd -subj $subject -no-delete -f $alice -hfrom $header_from -to $alice '' $PRIVATE_IP 2>&1)" if assert_python_success $? "$output"; then if ! wait_mail; then test_failure "Timeout waiting for mail" else record_captured_mail local files=( $(get_captured_mail_files) ) local score score=$(grep -F "DMARC_FAIL_REJECT" "$files" | grep -F '*' | awk '{print $2}') floor_score=$(awk -F. '{print $1}' <<<"$score") floor_expected=$(awk -F. '{print $1}' <<<"$DMARC_FAIL_REJECT_SCORE") if [ -z "$score" ]; then test_failure "No spam score for DMARC_FAIL_REJECT" elif [ $floor_score -ne $floor_expected ]; then test_failure "Got score $score, but exptected $DMARC_FAIL_REJECT_SCORE" fi fi # clean up: delete the delivered mail record "[delete delivered message]" output="$($PYMAIL -no-send -subj $subject -timeout 1 $PRIMARY_HOSTNAME $alice "$alice_pw" 2>&1)" record "$output" fi check_logs # clean up postgrey_reset_whitelists delete_user "$alice" test_end } test_mailbox_pipe() { # postfix allows piped commands in aliases for local processing, # which is a serious security issue. test that pipes are not # permitted or don't work test_start "mailbox-pipe" # create standard user alice local alice="alice@somedomain.com" create_user "$alice" "alice" local alice_dn="$ATTR_DN" # create the program to handle piped mail local cmd="/tmp/pipedrop.$$.sh" local outfile="/tmp/pipedrop.$$.out" cat 2>>$TEST_OF >$cmd < $outfile EOF chmod 755 $cmd rm -f $outfile # add a piped maildrop record "[Add pipe command as alice's maildrop]" ldapmodify -H $LDAP_URL -x -D "$LDAP_ADMIN_DN" -w "$LDAP_ADMIN_PASSWORD" >>$TEST_OF 2>&1 <