On Fri, 12 Dec 2008 04:05:58 +0100, Sam Varshavchik <mr...@courier-mta.com> wrote:
> Martin Strand writes: > >> On Fri, 12 Dec 2008 00:51:23 +0100, Sam Varshavchik <mr...@courier-mta.com> >> wrote: >> >>> Martin Strand writes: >>> >>>> Using courier-pop3d, is there any way to disable simple USER/PASS login so >>>> that only AUTH login with CRAM-SHA1 works? >>>> I tried setting POP3DAUTH="CRAM-SHA1" but USER/PASS still works... >>> >>> USER/PASS is always permitted. There is no toggle to turn it off. >> >> Thanks. >> Does anyone have any good tips on how to work around this at the moment? >> Is there perhaps a way to configure a set of "banned" commands? >> Maybe I could write a small pop3 proxy in Perl and intercept any USER/PASS >> commands? >> >> As someone pointed out, SSL would make more sense but unfortunately that's >> not an option in this case. > > POP3 is actually a very simple protocol. Writing a POP3 proxy would actually > be a fairly trivial excersize, especially for something like this. > > Actually, one just option that occured to me. You can use the authuserdb > module which optionally uses a separate field for pre-hashed CRAM passwords. > Those fields are not used for user/pass authentication. > > userdbpw -hmac-sha1 | userdb {username} set hmac-sha1pw > > This would set the CRAM-SHA1 password for {username}. So, you would proceed > to set CRAM-SHA1 passwords for all accounts, and set a dummy, invalid > password (and NOT a blank password) otherwise, so that USER/PASS > authentication would not work, but CRAM-SHA1 authentication would work just > fine. This process is documented further in the man pages and the associated > INSTALL files. This only works when using the userdb module, it's one of its > quirks, in the way that it was written, originally. Thanks, Sam. I'm using authmysql though so no luck (multiple servers, 10k users, frequent userdb updates) I put together a basic proxy in Python, using ideas from here: http://code.activestate.com/recipes/483732/ The proxy rewrites all forbidden commands by adding a "REJECTED-" prefix and then forwarding it to the server. The server can then respond with its usual error message. Courier's max connections per ip will get fooled, and logging won't be quite the same, the proxy may be vulnerable, but it's an interim solution so let's just hope it works ok for a while. I'm definitely no Python guru so use at your own risk! Example: $ pop3proxy --reject user --reject pass & $ nc localhost 10110 +OK Hello there. user asdf -ERR Invalid command. quit +OK Better luck next time. #!/usr/bin/python import sys import asynchat import asyncore import socket import optparse import logging class POP3Forwarder(asyncore.dispatcher): def __init__(self, listen_address, listen_port, remote_address, remote_port, reject, backlog = 5): asyncore.dispatcher.__init__(self) self.remote_address = remote_address self.remote_port = remote_port self.reject = reject self.create_socket(socket.AF_INET, socket.SOCK_STREAM) self.set_reuse_addr() self.bind((listen_address, listen_port)) self.listen(backlog) def handle_accept(self): connection, address = self.accept() logging.debug("Client %s connected", connection.getpeername()) POP3Client(POP3Server(connection, self.reject), self.remote_address, self.remote_port) class POP3Server(asynchat.async_chat): def __init__(self, connection, reject): asynchat.async_chat.__init__(self, connection) self.set_terminator("\n") self.reject = reject self.client = None self.buffer = "" def handle_connect(self): pass def collect_incoming_data(self, data): self.buffer += data def found_terminator(self): logging.debug("C: %s", self.buffer) command = self.buffer for rejected in self.reject: if command.upper().startswith(rejected.upper()): logging.debug("Rejecting command: %s", command) command = "REJECTED-" + command.upper() break command += "\r\n" self.client.push(command) self.buffer = "" def handle_close(self): logging.debug("Client %s disconnected", self.socket.getpeername()) self.close() if self.client: if len(self.buffer) > 0: self.client.push(self.buffer) self.buffer = "" self.client.close() class POP3Client(asynchat.async_chat): def __init__(self, server, remote_address, remote_port): asynchat.async_chat.__init__(self) self.set_terminator("\n") self.server = server self.server.client = self self.create_socket(socket.AF_INET, socket.SOCK_STREAM) logging.debug("Connecting to server %s:%s", remote_address, remote_port) self.connect((remote_address, remote_port)) self.buffer = "" def handle_connect(self): pass def collect_incoming_data(self, data): self.buffer += data def found_terminator(self): logging.debug("S: %s", self.buffer) response = self.buffer.strip("\r\n") response += "\r\n" self.server.push(response) self.buffer = "" def handle_close(self): logging.debug("Disconnecting from server") self.close() self.server.close() if __name__ == '__main__': parser = optparse.OptionParser(usage = "%prog [options]", description = "A POP3 proxy which rejects certain commands",) parser.add_option("--listen-address", action = "store", type = "string", dest = "listen_address", help = "Listen address [default: %default]") parser.add_option("--listen-port", action = "store", type = "int", dest = "listen_port", help = "Listen port [default: %default]") parser.add_option("--remote-address", action = "store", type = "string", dest = "remote_address", help = "Remote address [default: %default]") parser.add_option("--remote-port", action = "store", type = "int", dest = "remote_port", help = "Remote port [default: %default]") parser.add_option("--reject", action = "append", type = "string", dest = "reject", help = "Rejected commands, can be passed multiple times [default: %default]") parser.add_option("--debug", action = "store_true", dest = "debug", help = "Debug messages") parser.set_defaults( reject = [], remote_address = "127.0.0.1", remote_port = 110, listen_address = "127.0.0.1", listen_port = 10110, debug = False ) (options, args) = parser.parse_args() # Setup logging if options.debug: level = logging.DEBUG else: level = logging.INFO logger = logging.getLogger() logger.setLevel(level) handler = logging.StreamHandler() handler.setLevel(level) formatter = logging.Formatter("%(message)s") handler.setFormatter(formatter) logger.addHandler(handler) POP3Forwarder(options.listen_address, options.listen_port, options.remote_address, options.remote_port, options.reject) asyncore.loop() ------------------------------------------------------------------------------ SF.Net email is Sponsored by MIX09, March 18-20, 2009 in Las Vegas, Nevada. The future of the web can't happen without you. Join us at MIX09 to help pave the way to the Next Web now. Learn more and register at http://ad.doubleclick.net/clk;208669438;13503038;i?http://2009.visitmix.com/ _______________________________________________ courier-users mailing list courier-users@lists.sourceforge.net Unsubscribe: https://lists.sourceforge.net/lists/listinfo/courier-users