diff options
author | Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen@kolabsys.com> | 2014-02-11 14:30:14 +0100 |
---|---|---|
committer | Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen@kolabsys.com> | 2014-02-11 14:30:14 +0100 |
commit | 14ef739a27fa305d159ad04990842e2638a832e9 (patch) | |
tree | 32104bee820ccf906c7096ad581aaddaad372521 | |
parent | 84133cf3535c77dac77ac1f41955c6594f357a58 (diff) | |
download | pykolab-14ef739a27fa305d159ad04990842e2638a832e9.tar.gz |
Add new settings to control when (under what circumstances) a Sender:, X-Sender: or even an obscured X-Authenticated-As: header is prepended to email submitted through Kolab.
-rwxr-xr-x | bin/kolab_smtp_access_policy.py | 126 | ||||
-rw-r--r-- | conf/kolab.conf | 23 | ||||
-rw-r--r-- | pykolab/utils.py | 24 |
3 files changed, 163 insertions, 10 deletions
diff --git a/bin/kolab_smtp_access_policy.py b/bin/kolab_smtp_access_policy.py index 304ab85..935fc6e 100755 --- a/bin/kolab_smtp_access_policy.py +++ b/bin/kolab_smtp_access_policy.py @@ -1333,14 +1333,124 @@ def hold(message, policy_request=None): def permit(message, policy_request=None): log.info(_("Returning action PERMIT: %s") % (message)) - if hasattr(policy_request, 'sasl_username'): - sender = conf.get('kolab_smtp_access_policy', 'sender_header') - if utils.true_or_false(sender): - print "action=PREPEND Sender: %s" % (policy_request.sasl_username) - - xsender = conf.get('kolab_smtp_access_policy', 'xsender_header') - if utils.true_or_false(xsender): - print "action=PREPEND X-Sender: %s" % (policy_request.sasl_username) + # If we have no policy request, we have been called for a reason, + # and everything relevant has been figured out already. + if policy_request == None: + print "action=PERMIT\n\n" + sys.exit(0) + + # If the user is not authenticated, there's no reason to do + # extra checks here -- to have been performed already. + if not hasattr(policy_request, 'sasl_username'): + print "action=PERMIT\n\n" + sys.exit(0) + + # Same here. + if policy_request.sasl_username == None: + print "action=PERMIT\n\n" + sys.exit(0) + + delegate_sender_header = None + alias_sender_header = None + + # If the sender is a delegate of the envelope sender address, take into + # account the preferred domain policy for appending the Sender and/or + # X-Sender headers. + # + # Note that a delegatee by very definition is not using an alias. + # + if policy_request.sasl_user_is_delegate: + # Domain-specific setting? + if not policy_request.sender_domain == None: + delegate_sender_header = conf.get(policy_request.sender_domain, 'delegate_sender_header') + + # Global setting? + if delegate_sender_header == None: + delegate_sender_header = conf.get('kolab_smtp_access_policy', 'delegate_sender_header') + + # Default + if delegate_sender_header == None: + delegate_sender_header = True + + # If the sender is using an alias as the envelope sender address, take + # into account the preferred domain policy for appending the Sender + # and/or X-Sender headers. + elif policy_requiest.sasl_user_uses_alias: + # Domain-specific setting? + if not policy_request.sender_domain == None: + alias_sender_header = conf.get(policy_request.sender_domain, 'alias_sender_header') + + # Global setting? + if alias_sender_header == None: + alias_sender_header = conf.get('kolab_smtp_access_policy', 'alias_sender_header') + + # Default + if alias_sender_header == None: + alias_sender_header = True + + # Make the values booleans + delegate_sender_header = utils.true_or_false(delegate_sender_header) + alias_sender_header = utils.true_or_false(alias_sender_header) + + # Do we use a (simple) encryption key to obscure the headers' contents? + # Note that using an encryption key voids the actual use of proper Sender + # and X-Sender headers such as they could be interpreted by a client + # application. + enc_key = conf.get(policy_request.sender_domain, 'sender_header_enc_key') + if enc_key == None: + enc_key = conf.get('kolab_smtp_access_policy', 'sender_header_enc_key') + + sender_header = None + xsender_header = None + + if delegate_sender_header or alias_sender_header: + # Domain specific? + sender_header = conf.get(policy_request.sender_domain, 'sender_header') + + # Global setting? + if sender_header == None: + sender_header = conf.get('kolab_smtp_access_policy', 'sender_header') + + # Default + if sender_header == None: + sender_header = True + + # Domain specific? + xsender_header = conf.get(policy_request.sender_domain, 'xsender_header') + + # Global setting? + if xsender_header == None: + xsender_header = conf.get('kolab_smtp_access_policy', 'xsender_header') + + # Default + if xsender_header == None: + xsender_header = True + + # Note that if the user is not a delegatee, and not using an alias, the sender + # address is the envelope sender address, and the defaults for sender_header + # and xsender_header being None, ultimately evaluating to False seems + # appropriate. + + # Make the values booleans + sender_header = utils.true_or_false(sender_header) + xsender_header = utils.true_or_false(xsender_header) + + if sender_header or xsender_header: + # Do the encoding, if any + if not enc_key == None: + header = 'X-Authenticated-As' + xheader = None + sender = utils.encode(enc_key, policy_request.sasl_username) + else: + header = 'Sender' + xheader = 'X-Sender' + sender = policy_request.sasl_username + + if sender_header: + print "action=PREPEND %s: %s" % (header, sender) + + if xsender_header and not xheader == None: + print "action=PREPEND %s: %s" % (xheader, sender) print "action=PERMIT\n\n" sys.exit(0) diff --git a/conf/kolab.conf b/conf/kolab.conf index 8c36605..2f8ea2b 100644 --- a/conf/kolab.conf +++ b/conf/kolab.conf @@ -279,12 +279,31 @@ cache_retention = 86400 ; list. address_search_attrs = mail, alias -; Prepend the Sender: header? +; Prepend the Sender: and/or X-Sender header(s) if the user authenticated is a +; designated delegatee of the envelope sender address? +delegate_sender_header = True + +; Prepend the Sender: and/or X-Sender header(s) if the user authenticated +; is using an envelope sender address that is a secondary recipient email +; address (attached to the object entry) of the user authenticated? +alias_sender_header = True + +; Prepend the Sender: header? Only relevant if delegate_sender_header or +; alias_sender_header is set to True. sender_header = True -; Prepend the X-Sender: header? +; Prepend the X-Sender: header? Only relevant if delegate_sender_header or +; alias_sender_header is set to True. xsender_header = True +; "Encrypt" -- read, "obscure" -- the contents of the 'Sender:' and/or +; 'X-Sender:' header(s). Note that this invalidates client's use of the header +; value, and therefore replaces both headers with 'X-Authenticated-As'. +; +; Example: 'vanmeeuwen@kolabsys.com' becomes '6crb3dHK6ODS3qzQ4tXO0t_e5pfQ39k=' +; +; sender_header_enc_key = 'simple' + ; Allow hosts in these networks to submit messages with empty envelope senders, ; such as web-clients responding to MDN requests. empty_sender_hosts = 3.2.1.0/24, 6.6.6.0/24 diff --git a/pykolab/utils.py b/pykolab/utils.py index 5cba8f9..b7ff468 100644 --- a/pykolab/utils.py +++ b/pykolab/utils.py @@ -17,6 +17,7 @@ # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # +import base64 import getpass import grp import os @@ -182,6 +183,29 @@ def ask_menu(question, options={}, default=''): return answer +def decode(key, enc): + if key == None: + return enc + + dec = [] + enc = base64.urlsafe_b64decode(enc) + for i in range(len(enc)): + key_c = key[i % len(key)] + dec_c = chr((256 + ord(enc[i]) - ord(key_c)) % 256) + dec.append(dec_c) + return "".join(dec) + +def encode(key, clear): + if key == None: + return clear + + enc = [] + for i in range(len(clear)): + key_c = key[i % len(key)] + enc_c = chr((ord(clear[i]) + ord(key_c)) % 256) + enc.append(enc_c) + return base64.urlsafe_b64encode("".join(enc)) + def ensure_directory(_dir, _user='root', _group='root'): if not os.path.isdir(_dir): os.makedirs(_dir) |