[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[pysieved] patch for Dovecot auth and lookup
- From: Philippe Levan <levan at epix dot net>
- Subject: [pysieved] patch for Dovecot auth and lookup
- Date: Sun, 22 Jul 2007 09:47:27 -0400 (EDT)
Greetings,
While trying to get pysieved to work with my Dovecot
installation where all users are virtual and share
the same uid/gid, I made the following changes to the
current HEAD revision.
This is mostly based on the code contributed by Koen
Vermeer and partly merged by Neale Pickett.
I went back to opening the sockets on-demand in order
to avoid problems when Dovecot's authentication daemon
is restarted while pysieved is running.
I also added the lookup function (which doesn't need
to know the password, after all).
I also added code to switch uid/gid early if they are
already known, so that pysieved can run as non-root,
but then you need special care with permissions.
A few name mismatches were corrected along the way
(passwd -> password, create -> create_storage).
And finally, I added a little write-up on how I got
the whole thing to work for me.
I hope this can help others out there.
Philippe.
--
Philippe Levan - Frontier/epix Systems
diff -crN pysieved/README.dovecot pysieved.new/README.dovecot
*** pysieved/README.dovecot Wed Dec 31 19:00:00 1969
--- pysieved.new/README.dovecot Sun Jul 22 00:38:23 2007
***************
*** 0 ****
--- 1,125 ----
+ Dovecot authentication
+ ----------------------
+
+ If you want to use Dovecot authentication, set the mux config item
+ in the [Dovecot] session to the path to a client socket as defined
+ in dovecot.conf, e.g. :
+
+ dovecot.conf :
+ auth default {
+ socket listen {
+ client {
+ path = /var/spool/postfix/auth/dovecot
+ mode = 0666
+ user = postfix
+ group = postfix
+ }
+ }
+ }
+
+ pysieved.ini :
+ [Dovecot]
+ mux = /var/spool/postfix/auth/dovecot
+
+
+
+ Dovecot userdb lookup
+ ---------------------
+
+ If you want to use Dovecot userdb to get the uid/gid/home of the
+ user, set the master config item in the [Dovecot] session to the
+ path to a master socket as defined in dovecot.conf, e.g. :
+
+ dovecot.conf :
+ auth default {
+ socket listen {
+ master {
+ path = /var/run/dovecot/auth-master
+ mode = 0600
+ user = vmail
+ group = vmail
+ }
+ }
+ }
+
+ pysieved.ini :
+ [Dovecot]
+ master = /var/run/dovecot/auth-master
+
+
+
+ Dovecot socket permissions
+ --------------------------
+
+ In an environment where all users are virtual and share the same
+ uid/gid, it is possible to run pysieved as non-root by specifying
+ NUMERIC uid/gid values in the [Dovecot] section, e.g. :
+
+ pysieved.ini :
+ [Dovecot]
+ uid=1000
+ gid=100
+
+ In that case, special care may be needed so that pysieved can
+ access the 'mux' and 'master' sockets (and write the pidfile).
+
+ The pidfile can be moved to a directory that is writeable
+ by the specified uid/gid, e.g. :
+
+ pysieved.ini :
+ [main]
+ pidfile = /var/run/pysieved/pysieved.pid
+
+ The Dovecot client socket (mux) can safely be exported with 0666
+ permissions. But a common scenario places that socket inside the
+ Postfix chroot area, as /var/spool/postfix/private/auth where
+ the private directory is accessible only by the postfix user.
+ Moving it to a different, public, directory should work just as
+ well, e.g. :
+
+ dovecot.conf :
+ auth default {
+ socket listen {
+ client {
+ path = /var/spool/postfix/auth/dovecot
+ mode = 0666
+ user = postfix
+ group = postfix
+ }
+ }
+ }
+
+ pysieved.ini :
+ [Dovecot]
+ mux = /var/spool/postfix/auth/dovecot
+
+ main.cf :
+ smtpd_sasl_type = dovecot
+ smtpd_sasl_path = auth/dovecot
+
+ As for the master socket, it can have restrictive permissions,
+ as recommended in the Dovecot documentation, if you make it
+ owned by the same user who owns the mail storage (which works
+ out well if using the Dovecot deliver LDA with Postfix). E.g. :
+
+ dovecot.conf :
+ auth default {
+ socket listen {
+ master {
+ path = /var/run/dovecot/auth-master
+ mode = 0600
+ user = vmail
+ group = vmail
+ }
+ }
+ }
+
+ pysieved.ini :
+ [Dovecot]
+ master = /var/run/dovecot/auth-master
+
+ master.cf :
+ dovecot unix - n n - - pipe
+ flags=DRhu user=vmail:vmail
+ argv=/usr/local/libexec/dovecot/deliver -f $sender -d $recipient
+
diff -crN pysieved/plugins/dovecot.py pysieved.new/plugins/dovecot.py
*** pysieved/plugins/dovecot.py Wed May 9 00:13:32 2007
--- pysieved.new/plugins/dovecot.py Fri Jul 20 08:48:50 2007
***************
*** 177,216 ****
def init(self, config):
self.mux = config.get('Dovecot', 'mux', False)
self.service = config.get('Dovecot', 'service', 'pysieved')
self.sievec = config.get('Dovecot', 'sievec',
'/usr/lib/dovecot/sievec')
self.scripts_dir = config.get('Dovecot', 'scripts', '.pysieved')
! # Only try to connect the auth socket if a mux was specified in
! # the configuration file
! if self.mux:
! self.auth_sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
! self.auth_sock.connect(self.mux)
!
! self.pid = os.getpid()
def auth(self, params):
! handshake_string = self.auth_sock.recv(1024)
!
! self.auth_sock.sendall('VERSION\t1\t0\nCPID\t%d\n' % self.pid)
auth_string = ('AUTH\t%d\tPLAIN\tservice=%s\tresp=%s' %
! (self.pid,
self.service,
base64.encodestring(params['username'] + '\0' +
params['username'] + '\0' +
! params['passwd'])))
self.auth_sock.sendall(auth_string + '\n')
ret = self.auth_sock.recv(1024)
! self.log(2, 'Auth returns %r' % ret)
if ret.startswith('OK'):
return True
return False
def create_storage(self, params):
return ScriptStorage(self.sievec,
self.scripts_dir,
--- 177,266 ----
def init(self, config):
self.mux = config.get('Dovecot', 'mux', False)
+ self.master = config.get('Dovecot', 'master', False)
self.service = config.get('Dovecot', 'service', 'pysieved')
self.sievec = config.get('Dovecot', 'sievec',
'/usr/lib/dovecot/sievec')
self.scripts_dir = config.get('Dovecot', 'scripts', '.pysieved')
+ self.uid = config.getint('Dovecot', 'uid', None)
+ self.gid = config.getint('Dovecot', 'gid', None)
! # Drop privileges here if all users share the same uid/gid
! if self.gid >= 0:
! os.setgid(self.gid)
! if self.uid >= 0:
! os.setuid(self.uid)
def auth(self, params):
! # We can do this only if a MUX socket was specified
! if self.mux:
! self.auth_sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
! self.auth_sock.connect(self.mux)
! handshake_string = self.auth_sock.recv(1024)
! self.auth_sock.sendall('VERSION\t1\t0\nCPID\t%d\n' % os.getpid())
! else:
! raise ValueError('No MUX socket was specified')
+ # Since this socket will be used only once, use a hardcoded request ID
auth_string = ('AUTH\t%d\tPLAIN\tservice=%s\tresp=%s' %
! (1,
self.service,
base64.encodestring(params['username'] + '\0' +
params['username'] + '\0' +
! params['password'])))
self.auth_sock.sendall(auth_string + '\n')
ret = self.auth_sock.recv(1024)
! self.auth_sock.close();
!
if ret.startswith('OK'):
return True
return False
+ def lookup(self, params):
+ # We can do this only if a master socket was specified
+ if self.master:
+ self.user_sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
+ self.user_sock.connect(self.master)
+ master_handshake_string = self.user_sock.recv(1024)
+ self.user_sock.sendall('VERSION\t1\t0\n')
+ else:
+ raise ValueError('No master socket was specified')
+
+ # Since this socket will be used only once, use a hardcoded request ID
+ lookup_string = ('USER\t%d\t%s\tservice=%s' %
+ (1,
+ params['username'],
+ self.service))
+ self.user_sock.sendall(lookup_string + '\n')
+ ret = self.user_sock.recv(1024)
+
+ self.user_sock.close();
+
+ if ret.startswith('USER\t'):
+ uid = ret[ret.find('\tuid=')+5:]
+ uid = uid[:uid.find('\t')]
+ gid = ret[ret.find('\tgid=')+5:]
+ gid = gid[:gid.find('\t')]
+ home = ret[ret.find('\thome=')+6:]
+ home = home[:home.find('\t')]
+
+ # Assuming we were started with elevated privileges, drop them now
+ if not self.gid >= 0:
+ if int(gid) >= 0:
+ os.setgid(int(gid))
+
+ if not self.uid >= 0:
+ if int(uid) >= 0:
+ os.setuid(int(uid))
+
+ return home
+ else:
+ return None
+
+
def create_storage(self, params):
return ScriptStorage(self.sievec,
self.scripts_dir,
diff -crN pysieved/pysieved.ini pysieved.new/pysieved.ini
*** pysieved/pysieved.ini Wed May 9 00:13:32 2007
--- pysieved.new/pysieved.ini Sat Jul 21 23:52:02 2007
***************
*** 54,60 ****
[Dovecot]
# Path to Dovecot's auth socket (do not set unless you're using Dovecot auth)
! # mux = var/run/saslauthd/mux
# Path to sievec
sievec = /usr/lib/dovecot/sievec
--- 54,63 ----
[Dovecot]
# Path to Dovecot's auth socket (do not set unless you're using Dovecot auth)
! #mux = /var/spool/postfix/auth/dovecot
!
! # Path to Dovecot's master socket (if using Dovecot userdb lookup)
! #master = /var/run/dovecot/auth-master
# Path to sievec
sievec = /usr/lib/dovecot/sievec
***************
*** 62,64 ****
--- 65,70 ----
# Where in user directory to store scripts
scripts = .pysieved
+ # What user/group owns the mail storage
+ uid = -1
+ gid = -1
diff -crN pysieved/pysieved.py pysieved.new/pysieved.py
*** pysieved/pysieved.py Wed May 9 00:13:32 2007
--- pysieved.new/pysieved.py Thu Jul 19 15:13:31 2007
***************
*** 103,109 ****
def new_storage(self, homedir):
self.params['homedir'] = homedir
! return store.create(self.params)
if options.stdin:
sock = socket.fromfd(0, socket.AF_INET, socket.SOCK_STREAM)
--- 103,109 ----
def new_storage(self, homedir):
self.params['homedir'] = homedir
! return store.create_storage(self.params)
if options.stdin:
sock = socket.fromfd(0, socket.AF_INET, socket.SOCK_STREAM)