/usr/bin/ingo-postfix-policyd is in php-horde-ingo 3.1.3-1.
This file is owned by root:root, with mode 0o755.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 | #!/usr/bin/php
<?php
/**
* Usage: ingo-postfix-policyd [-v]
*
* Delegated Postfix SMTPD policy server that enforce's Ingo's
* blacklist and whitelist rules. Logging is done through the
* standard Horde logger.
*
* How it works: each time a Postfix SMTP server process is started it
* connects to the policy service socket, and Postfix runs one
* instance of this PHP script. By default, a Postfix SMTP server
* process terminates after 100 seconds of idle time, or after serving
* 100 clients. Thus, the cost of starting this script is smoothed out
* over time.
*
* To run this from /etc/postfix/master.cf (if necessary substituting
* a user that has permissions for your Horde configuration and
* logfiles for www-data):
*
* policy unix - n n - - spawn
* user=www-data argv=/path/to/horde/ingo/scripts/ingo-postfix-policyd
*
* To use this from Postfix SMTPD, use in /etc/postfix/main.cf:
*
* smtpd_recipient_restrictions =
* ...
* reject_unauth_destination
* check_policy_service unix:private/policy
* ...
*
* NOTE: specify check_policy_service AFTER reject_unauth_destination
* or else your system can become an open relay.
*
* To test this script by hand, execute:
*
* % ingo-postfix-policyd
*
* Each query is a bunch of attributes. See
* http://www.postfix.org/SMTPD_POLICY_README.html and the example
* greylisting policy daemon for all of the possibilities. This script
* uses only:
*
* sender=foo@bar.tld
* recipient=bar@foo.tld
*
* And the query is terminated with an:
* [empty line]
*
* The policy server script will answer in the same style, with an
* attribute list followed by a empty line:
*
* action=DUNNO
* [empty line]
*
* The possible actions are documented at
* http://www.postfix.org/access.5.html. We return "DUNNO" when the
* sender/recipient combination doesn't match any blacklist or
* whitelist rules, OK if the sender is whitelisted, and REJECT if the
* sender is blacklisted.
*
* Copyright 2012-2013 Horde LLC (http://www.horde.org/)
*
* See the enclosed file LICENSE for license information (ASL). If you
* did not receive this file, see http://www.horde.org/licenses/apache.
*
* @category Horde
* @license http://www.horde.org/licenses/apache ASL
* @package Ingo
*/
$baseFile = __DIR__ . '/../../ingo/lib/Application.php';
if (file_exists($baseFile)) {
require_once $baseFile;
} else {
require_once 'PEAR/Config.php';
require_once PEAR_Config::singleton()
->get('horde_dir', null, 'pear.horde.org') . '/ingo/lib/Application.php';
}
Horde_Registry::appInit('ingo', array('cli' => true));
exit('Not updated');
// Initialize authentication manager.
$auth = $injector->getInstance('Horde_Auth')->getAuth();
// Make sure output is unbuffered.
ob_implicit_flush();
// Main loop.
$query = array();
while (!feof(STDIN)) {
$line = fgets(STDIN);
if (strpos($line, '=') !== false) {
list($key, $value) = explode('=', trim($line), 2);
$query[$key] = $value;
} elseif ($line == "\n") {
if (empty($query['request']) || $query['request'] != 'smtpd_access_policy') {
Horde::logMessage('Unrecognized request: ' . substr(var_export($query, true), 0, 200), 'ERR');
exit(1);
}
Horde::logMessage(var_export($query, true), 'DEBUG');
$action = smtpd_access_policy($query);
Horde::logMessage("Action: $action", 'DEBUG');
echo "action=$action\n\n";
@ob_flush();
$query = array();
} else {
Horde::logMessage('Ignoring garbage: ' . substr($line, 0, 100), 'INFO');
}
}
exit(0);
/**
* Do policy checks
*
* @param array $query Query parameter hash
*
* @return string The access policy response.
*/
function smtpd_access_policy($query)
{
static $whitelists = array();
static $blacklists = array();
if (empty($query['sender']) || empty($query['recipient'])) {
return null;
}
// Try to determine the Horde username corresponding to the email recipient.
$user = $query['recipient'];
$pos = strpos($user, '@');
if ($pos !== false) {
$user = substr($user, 0, $pos);
}
try {
$user = Horde::callHook('smtpd_access_policy_username', $query, 'ingo');
} catch (Horde_Exception_HookNotSet $e) {}
// Get $user's rules if we don't have them already.
if (!isset($whitelists[$user])) {
// Default empty rules.
$whitelists[$user] = array();
$blacklists[$user] = array();
// Retrieve the data.
$GLOBALS['auth']->setAuth($user, array());
$GLOBALS['session']->set('ingo', 'current_share', ':' . $user);
$ingo_storage = $GLOBALS['injector']->getInstance('Ingo_Factory_Storage')->create();
try {
$whitelists[$user] = $ingo_storage->retrieve(Ingo_Storage::ACTION_WHITELIST)->getWhitelist();
} catch (Ingo_Exception $e) {}
try {
$bl = $ingo_storage->retrieve(Ingo_Storage::ACTION_BLACKLIST);
if (!$bl->getBlacklistFolder()) {
// We will only reject email at delivery time if the user
// wants blacklisted mail deleted completely, not filed
// into a separate folder.
$blacklists[$user] = $bl->getBlacklist();
}
} catch (Ingo_Exception $e) {}
}
// Check whitelist rules first so that mistaken overlap doesn't
// result in lost mail.
if (in_array($query['sender'], $whitelists[$user])) {
return 'OK';
} elseif (in_array($query['sender'], $blacklists[$user])) {
return 'REJECT';
} else {
return 'DUNNO';
}
}
|