From 5a45acd3486f4e7f830647953731353cda916d51 Mon Sep 17 00:00:00 2001 From: drebs Date: Tue, 24 Mar 2015 15:09:18 -0300 Subject: [feat] reject incoming mail if no pgp key found Implement a PGP key lookup in the postfix smtp recipient restriction and virtual alias mapping levels. If no PGP key is found, then the address is rejected with a temporary error. Closes: #6795 --- src/leap/mx/check_recipient_access.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'src/leap/mx/check_recipient_access.py') diff --git a/src/leap/mx/check_recipient_access.py b/src/leap/mx/check_recipient_access.py index b80ccfd..d4ae339 100644 --- a/src/leap/mx/check_recipient_access.py +++ b/src/leap/mx/check_recipient_access.py @@ -32,8 +32,11 @@ class LEAPPostFixTCPMapserverAccess(postfix.PostfixTCPMapServer): # For more info, see: # http://www.postfix.org/tcp_table.5.html # http://www.postfix.org/access.5.html - if value is None: + uuid, pubkey = value + if uuid is None: self.sendCode(500, postfix.quote("REJECT")) + elif pubkey is None: + self.sendCode(400, postfix.quote("4.7.13 USER ACCOUNT DISABLED")) else: self.sendCode(200, postfix.quote("OK")) -- cgit v1.2.3 From b5309dc5910f35f5320c649be2ce2c6147030b39 Mon Sep 17 00:00:00 2001 From: drebs Date: Wed, 25 Mar 2015 15:47:28 -0300 Subject: [refactor] separate tcp map server code Separate the common tcp map server code, used for both alias resolver and recipient access checker, to its own file. --- src/leap/mx/check_recipient_access.py | 40 ++++++++++++++++++++++++++--------- 1 file changed, 30 insertions(+), 10 deletions(-) (limited to 'src/leap/mx/check_recipient_access.py') diff --git a/src/leap/mx/check_recipient_access.py b/src/leap/mx/check_recipient_access.py index d4ae339..cf172c7 100644 --- a/src/leap/mx/check_recipient_access.py +++ b/src/leap/mx/check_recipient_access.py @@ -24,22 +24,42 @@ Test this with postmap -v -q "foo" tcp:localhost:2244 from twisted.protocols import postfix -from leap.mx.alias_resolver import AliasResolverFactory +from leap.mx.tcp_map import LEAPPostfixTCPMapServerFactory +from leap.mx.tcp_map import TCP_MAP_CODE_SUCCESS +from leap.mx.tcp_map import TCP_MAP_CODE_TEMPORARY_FAILURE +from leap.mx.tcp_map import TCP_MAP_CODE_PERMANENT_FAILURE -class LEAPPostFixTCPMapserverAccess(postfix.PostfixTCPMapServer): +class LEAPPostFixTCPMapAccessServer(postfix.PostfixTCPMapServer): + """ + A postfix tcp map recipient access checker server. + """ + def _cbGot(self, value): - # For more info, see: - # http://www.postfix.org/tcp_table.5.html - # http://www.postfix.org/access.5.html + """ + Return a code and message depending on the result of the factory's + get(). + + For more info, see: http://www.postfix.org/access.5.html + + :param value: The uuid and public key. + :type value: list + """ uuid, pubkey = value if uuid is None: - self.sendCode(500, postfix.quote("REJECT")) + self.sendCode( + TCP_MAP_CODE_PERMANENT_FAILURE, + postfix.quote("REJECT")) elif pubkey is None: - self.sendCode(400, postfix.quote("4.7.13 USER ACCOUNT DISABLED")) + self.sendCode( + TCP_MAP_CODE_TEMPORARY_FAILURE, + postfix.quote("4.7.13 USER ACCOUNT DISABLED")) else: - self.sendCode(200, postfix.quote("OK")) + self.sendCode( + TCP_MAP_CODE_SUCCESS, + postfix.quote("OK")) + +class CheckRecipientAccessFactory(LEAPPostfixTCPMapServerFactory): -class CheckRecipientAccessFactory(AliasResolverFactory): - protocol = LEAPPostFixTCPMapserverAccess + protocol = LEAPPostFixTCPMapAccessServer -- cgit v1.2.3 From 45adb4d6cfdb8b9ed11e3efc398d00ec6dbdc0b0 Mon Sep 17 00:00:00 2001 From: drebs Date: Thu, 26 Mar 2015 15:25:50 -0300 Subject: [bug] limit pgp key lookup to access check server In order to minimize the number of couchdb queries and the number of mx lookups in case of junk mail this commit restricts the pgp key lookup to the access check server (and removes it from the alias server). Closes: #6795. --- src/leap/mx/check_recipient_access.py | 43 +++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) (limited to 'src/leap/mx/check_recipient_access.py') diff --git a/src/leap/mx/check_recipient_access.py b/src/leap/mx/check_recipient_access.py index cf172c7..0977564 100644 --- a/src/leap/mx/check_recipient_access.py +++ b/src/leap/mx/check_recipient_access.py @@ -23,6 +23,7 @@ Test this with postmap -v -q "foo" tcp:localhost:2244 """ from twisted.protocols import postfix +from twisted.internet import defer from leap.mx.tcp_map import LEAPPostfixTCPMapServerFactory from leap.mx.tcp_map import TCP_MAP_CODE_SUCCESS @@ -33,6 +34,10 @@ from leap.mx.tcp_map import TCP_MAP_CODE_PERMANENT_FAILURE class LEAPPostFixTCPMapAccessServer(postfix.PostfixTCPMapServer): """ A postfix tcp map recipient access checker server. + + The server potentially receives the uuid and a PGP key for the user, which + are looked up by the factory, and will return a permanent or a temporary + failure in case either the user or the key don't exist, respectivelly. """ def _cbGot(self, value): @@ -61,5 +66,43 @@ class LEAPPostFixTCPMapAccessServer(postfix.PostfixTCPMapServer): class CheckRecipientAccessFactory(LEAPPostfixTCPMapServerFactory): + """ + A factory for the recipient access checker. + + When queried, the factory looks up the user's uuid and a PGP key for that + user and returns the result to the server's _cbGot() method. + """ protocol = LEAPPostFixTCPMapAccessServer + + def _getPubKey(self, uuid): + """ + Look up PGP public key based on user uid. + + :param uuid: The user uid. + :type uuid: str + + :return: A deferred that is fired with the uuid and the public key, if + available. + :rtype: DeferredList + """ + if uuid is None: + return defer.succeed([None, None]) + # properly encode uuid, otherwise twisted complains when replying + if isinstance(uuid, unicode): + uuid = uuid.encode("utf8") + return defer.gatherResults([ + defer.succeed(uuid), + self._cdb.getPubKey(uuid), + ]) + + def get(self, key): + """ + Look up uuid and PGP public key based on key. + + :param key: The lookup key. + :type key: str + """ + d = LEAPPostfixTCPMapServerFactory.get(self, key) + d.addCallback(self._getPubKey) + return d -- cgit v1.2.3 From b0ef529cc882a96903597fb5279919969fa286c3 Mon Sep 17 00:00:00 2001 From: drebs Date: Thu, 9 Apr 2015 17:18:37 -0300 Subject: [refactor] use couch reduced views for lookups The way uuid and pgp key were being queried by means of couch views was not efficient because they weren't using the reduce function and were filtering the views results in the python code. Also, the uuid is not actually needed to find out either if the address exists or if there's a pgp public key for that address. This commit refactors the couch helper to make use of the reduce functions in queried views and to get rid of the intermediate uuid querying. --- src/leap/mx/check_recipient_access.py | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) (limited to 'src/leap/mx/check_recipient_access.py') diff --git a/src/leap/mx/check_recipient_access.py b/src/leap/mx/check_recipient_access.py index 0977564..3b61fe8 100644 --- a/src/leap/mx/check_recipient_access.py +++ b/src/leap/mx/check_recipient_access.py @@ -50,8 +50,8 @@ class LEAPPostFixTCPMapAccessServer(postfix.PostfixTCPMapServer): :param value: The uuid and public key. :type value: list """ - uuid, pubkey = value - if uuid is None: + address, pubkey = value + if address is None: self.sendCode( TCP_MAP_CODE_PERMANENT_FAILURE, postfix.quote("REJECT")) @@ -75,25 +75,22 @@ class CheckRecipientAccessFactory(LEAPPostfixTCPMapServerFactory): protocol = LEAPPostFixTCPMapAccessServer - def _getPubKey(self, uuid): + def _getPubKey(self, address): """ - Look up PGP public key based on user uid. + Look up PGP public key based on email address. - :param uuid: The user uid. - :type uuid: str + :param address: The email address. + :type address: str - :return: A deferred that is fired with the uuid and the public key, if - available. + :return: A deferred that is fired with the address and the public key, if + each of them exists. :rtype: DeferredList """ - if uuid is None: + if not address: return defer.succeed([None, None]) - # properly encode uuid, otherwise twisted complains when replying - if isinstance(uuid, unicode): - uuid = uuid.encode("utf8") return defer.gatherResults([ - defer.succeed(uuid), - self._cdb.getPubKey(uuid), + defer.succeed(address), + self._cdb.getPubKey(address), ]) def get(self, key): -- cgit v1.2.3 From 527d7d4a67f859a3315812b100b2c58fd0eeded6 Mon Sep 17 00:00:00 2001 From: drebs Date: Wed, 15 Apr 2015 12:48:34 -0300 Subject: [bug] return uuid as result of alias resolver This fixes a bug introduced on b0ef529cc882a96903597fb5279919969fa286c3, when the alias resolver was modified to return the user's address instead of the uuid. In order to fix this, I had to revert one of the changes made by the commit above, which is to don't make use of reduced view for the uuid query. The pgp public key query remains reduced, as implemented in the commit above. We also refactor the code a bit to allow for log messages specific to each of tcp map's sublasses. Related: #6858. --- src/leap/mx/check_recipient_access.py | 35 +++++------------------------------ 1 file changed, 5 insertions(+), 30 deletions(-) (limited to 'src/leap/mx/check_recipient_access.py') diff --git a/src/leap/mx/check_recipient_access.py b/src/leap/mx/check_recipient_access.py index 3b61fe8..9f79dfe 100644 --- a/src/leap/mx/check_recipient_access.py +++ b/src/leap/mx/check_recipient_access.py @@ -23,7 +23,6 @@ Test this with postmap -v -q "foo" tcp:localhost:2244 """ from twisted.protocols import postfix -from twisted.internet import defer from leap.mx.tcp_map import LEAPPostfixTCPMapServerFactory from leap.mx.tcp_map import TCP_MAP_CODE_SUCCESS @@ -50,8 +49,8 @@ class LEAPPostFixTCPMapAccessServer(postfix.PostfixTCPMapServer): :param value: The uuid and public key. :type value: list """ - address, pubkey = value - if address is None: + uuid, pubkey = value + if uuid is None: self.sendCode( TCP_MAP_CODE_PERMANENT_FAILURE, postfix.quote("REJECT")) @@ -75,31 +74,7 @@ class CheckRecipientAccessFactory(LEAPPostfixTCPMapServerFactory): protocol = LEAPPostFixTCPMapAccessServer - def _getPubKey(self, address): - """ - Look up PGP public key based on email address. - - :param address: The email address. - :type address: str - - :return: A deferred that is fired with the address and the public key, if - each of them exists. - :rtype: DeferredList - """ - if not address: - return defer.succeed([None, None]) - return defer.gatherResults([ - defer.succeed(address), - self._cdb.getPubKey(address), - ]) - - def get(self, key): - """ - Look up uuid and PGP public key based on key. + @property + def _query_message(self): + return "Checking recipient access for" - :param key: The lookup key. - :type key: str - """ - d = LEAPPostfixTCPMapServerFactory.get(self, key) - d.addCallback(self._getPubKey) - return d -- cgit v1.2.3 From 62def16809c1cf739db5a7a8e7aa24fec70fdf5d Mon Sep 17 00:00:00 2001 From: drebs Date: Fri, 17 Apr 2015 16:35:58 -0300 Subject: [doc] update documentation I'm updating (1) some very outdated doc from when the program was not yet written, and (2) some small stuff inside classes docstrings. --- src/leap/mx/check_recipient_access.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'src/leap/mx/check_recipient_access.py') diff --git a/src/leap/mx/check_recipient_access.py b/src/leap/mx/check_recipient_access.py index 9f79dfe..55460a6 100644 --- a/src/leap/mx/check_recipient_access.py +++ b/src/leap/mx/check_recipient_access.py @@ -17,7 +17,12 @@ # along with this program. If not, see . """ -Classes for resolving postfix recipient access +Classes for resolving postfix recipient access. + +The resolver is queried by the mail server before delivery to the mail spool +directory, and should check if the address is able to receive messages. +Examples of reasons for denying delivery would be that the user is out of +quota, is user, or have no pgp public key in the server. Test this with postmap -v -q "foo" tcp:localhost:2244 """ @@ -44,6 +49,9 @@ class LEAPPostFixTCPMapAccessServer(postfix.PostfixTCPMapServer): Return a code and message depending on the result of the factory's get(). + If there's no pgp public key for the user, we currently return a + temporary failure saying that the user account is disabled. + For more info, see: http://www.postfix.org/access.5.html :param value: The uuid and public key. -- cgit v1.2.3