Snapshot release 20030305-1234
Charles Cazabon <[email protected]> 5 Mar 2003 18:34:48 -0000
| Newsgroups | gmane.mail.bikini.devel |
|---|---|
| Message-ID | <[email protected]> |
Snapshot 20030305-1234 released and uploaded. From the CHANGELOG:
2003-03-05
Password checking support is in serverauth.py now. It uses any external
program that uses the checkpassword interface; add your checkpassword
program and arguments to the arguments to the serverauth.py program's
arguments, as in the example service-run.sh script. Typically this
will mean adding "/bin/checkpassword /bin/true" after your mailstore
path and inbox path arguments. This is tested and working here with
vanilla checkpassword v.0.80.
Minor clarifications to spec.
Fix typos and leftovers from previous ideas in spec.
Diff follows.
diff -urN --exclude=bikini.html --exclude=bikini.txt --exclude=CHANGELOG bikini-20030304-1130/bikini/classes.py bikini-20030305-1234/bikini/classes.py
--- bikini-20030304-1130/bikini/classes.py Tue Mar 4 11:30:54 2003
+++ bikini-20030305-1234/bikini/classes.py Wed Mar 5 12:34:24 2003
@@ -80,10 +80,17 @@
'''Respond with a one-line response.
'''
log (TRACE, 'rc %s %s\n' % (RESPONSES[code], code))
- self.fo.write (RESPONSES[code])
- if info: self.fo.write (' ' + info)
- self.fo.write (CRLF)
- self.fo.flush ()
+ try:
+ self.fo.write (RESPONSES[code])
+ if info: self.fo.write (' ' + info)
+ self.fo.write (CRLF)
+ self.fo.flush ()
+ except IOError, o:
+ if o.errno == 32:
+ # Broken pipe
+ raise bikiniExit, 'client disappeared'
+ # else
+ raise
###################################
def respondmulti (self, lines):
diff -urN --exclude=bikini.html --exclude=bikini.txt --exclude=CHANGELOG bikini-20030304-1130/bikini/pwcheck.py bikini-20030305-1234/bikini/pwcheck.py
--- bikini-20030304-1130/bikini/pwcheck.py Wed Dec 31 18:00:00 1969
+++ bikini-20030305-1234/bikini/pwcheck.py Wed Mar 5 12:34:24 2003
@@ -0,0 +1,64 @@
+import popen2
+import os
+import time
+
+from errors import *
+from utilities import *
+from constants import *
+
+class pwcheckbase:
+ def check (self, username, password):
+ log (TRACE)
+ r = self.do_check (username, password)
+ log (DEBUG, 'do_check returned "%s" type %s\n' % (r, type (r)))
+ if r == 0:
+ # Successful authentication
+ return
+ raise TemporaryCommandError, 'failed authentication'
+
+class null (pwcheckbase):
+ def __init__ (self):
+ log (TRACE)
+ pass
+
+ def do_check (self, username, password):
+ log (TRACE)
+ return 0
+
+class checkpassword (pwcheckbase):
+ def __init__(self, args):
+ log (TRACE)
+ self.checkpassword = args[0]
+ self.args = args
+
+ def do_check (self, username, password):
+ log (TRACE)
+ self.r, self.w = os.pipe ()
+ child_pid = os.fork ()
+ if child_pid == 0:
+ self.child ()
+ log (DEBUG, 'forked pid %i\n' % child_pid)
+ os.close (self.r)
+ time.sleep (1)
+ os.write (self.w, '%s\0%s\0Y012345\0' % (username, password))
+ os.close (self.w)
+ tries = 0
+ while tries < 10:
+ pid, rc = os.waitpid (child_pid, os.WNOHANG)
+ log (TRACE, 'os.waitpid returned %i, %s\n' % (pid, rc))
+ if pid == child_pid and os.WIFEXITED (rc):
+ if os.WEXITSTATUS (rc) == 0:
+ log (TRACE, 'successful checkpassword, returning 0\n')
+ return 0
+ log (TRACE, 'unsuccessful checkpassword (exited %i), returning None\n' % os.WEXITSTATUS (rc))
+ return
+ time.sleep (1)
+ log (ERROR, 'child never exited?\n')
+
+ def child (self):
+ log (TRACE, 'pid %i\n' % os.getpid ())
+ os.close (self.w)
+ os.dup2 (self.r, 3)
+ log (DEBUG, 'exec()ing %s with args %s\n' % (self.checkpassword, self.args))
+ os.execv (self.checkpassword, self.args)
+ raise TemporaryCommandError, 'execv failed'
diff -urN --exclude=bikini.html --exclude=bikini.txt --exclude=CHANGELOG bikini-20030304-1130/bikini/sasl.py bikini-20030305-1234/bikini/sasl.py
--- bikini-20030304-1130/bikini/sasl.py Tue Mar 4 11:30:54 2003
+++ bikini-20030305-1234/bikini/sasl.py Wed Mar 5 12:34:24 2003
@@ -15,7 +15,9 @@
if pw[2] == 0:
raise PermanentCommandError, 'not allowed for root'
- # FIXME: No password validation is done
+ # Password validation through external checkpassword interface
+ self.pwchecker.check (self.username, self.password)
+
self.uid = pw[2]
self.gid = pw[3]
self.fullname = pw[4]
@@ -23,11 +25,12 @@
self.completed = 1
class LOGIN(Mechanism):
- def __init__(self):
+ def __init__(self, pwchecker):
self.challenge = 'login:'
self.completed = None
self.username = None
self.response = self.response_user
+ self.pwchecker = pwchecker
def response_user(self, str):
self.username = str
self.challenge = 'password:'
@@ -37,9 +40,10 @@
self.checkpassword()
class PLAIN(Mechanism):
- def __init__(self):
+ def __init__(self, pwchecker):
self.challenge = ''
self.completed = None
+ self.pwchecker = pwchecker
def response(self, str):
try:
(user1,user2,password) = str.split('\0', 2)
@@ -50,10 +54,14 @@
self.checkpassword()
class ANONYMOUS(Mechanism):
- def __init__(self):
+ def __init__(self, unused):
self.username = defaults['anonymous-user']
+ self.password = None
+ self.pwchecker = self
self.checkpassword ()
self.dir = defaults['anonymous-mailstore']
+ def check (self, *unused):
+ return 0
mechanisms = {
'LOGIN': LOGIN,
@@ -61,15 +69,15 @@
'ANONYMOUS': ANONYMOUS,
}
-def start(mech):
+def start(mech, pwchecker):
mech = mech.upper()
mech = mechanisms[mech]
- mech = mech()
+ mech = mech(pwchecker)
return mech
-def generic(mech, initresponse, cprefix):
+def generic(mech, pwchecker, initresponse, cprefix):
try:
- m = start(mech)
+ m = start(mech, pwchecker)
except KeyError:
raise PermanentCommandError, "Unknown mechanism"
if initresponse:
@@ -91,11 +99,11 @@
m.response(response)
return m
-def bikini_auth (args):
+def bikini_auth (args, pwchecker):
if len (args) == 1:
- m = generic (args[0], None, '+')
+ m = generic (args[0], pwchecker, None, '+')
elif len (args) == 2:
- m = generic (args[0], args[1], '+')
+ m = generic (args[0], pwchecker, args[1], '+')
else:
raise bSyntaxError, 'Too many parameters'
return m
diff -urN --exclude=bikini.html --exclude=bikini.txt --exclude=CHANGELOG bikini-20030304-1130/bikini/server.py bikini-20030305-1234/bikini/server.py
--- bikini-20030304-1130/bikini/server.py Tue Mar 4 11:30:54 2003
+++ bikini-20030305-1234/bikini/server.py Wed Mar 5 12:34:24 2003
@@ -640,7 +640,7 @@
self.respond ('temporary', 'command failed temporarily (%s)' % err)
except UnimplementedError, err:
- self.respond ('unsupported', 'command not implemented (%s)' % err)
+ self.respond ('unsupported', 'command not supported (%s)' % err)
#######################################
diff -urN --exclude=bikini.html --exclude=bikini.txt --exclude=CHANGELOG bikini-20030304-1130/bikini/serverauth.py bikini-20030305-1234/bikini/serverauth.py
--- bikini-20030304-1130/bikini/serverauth.py Tue Mar 4 11:30:54 2003
+++ bikini-20030305-1234/bikini/serverauth.py Wed Mar 5 12:34:24 2003
@@ -50,6 +50,7 @@
from utilities import *
from constants import *
import sasl
+import pwcheck
#
# Globals
@@ -67,13 +68,14 @@
'''Main entry point for bikini.
'''
###################################
- def __init__ (self, inboxpath, fo=sys.stdout, fi=sys.stdin):
+ def __init__ (self, inboxpath, pwchecker, fo=sys.stdout, fi=sys.stdin):
'''Constructor.
'''
log (TRACE, 'inboxpath %s\n' % inboxpath)
self.fi = fi
self.fo = fo
self.inbox = inboxpath
+ self.pwchecker = pwchecker
BikiniServer.__init__ (self, inboxpath, fo, fi)
signal.signal (signal.SIGALRM, timeout_handler)
@@ -93,7 +95,7 @@
log (DEBUG, 'command %s, args %s\n' % (cmd, args))
self.stop_timeout ()
need_auth += 1
- if cmd == 'STARTTLS': raise UnimplementedError, 'starttls not implemented'
+ if cmd == 'STARTTLS': raise UnimplementedError, 'starttls not supported'
if cmd == 'CAPS':
self.respondmulti (self.caps())
continue
@@ -102,7 +104,8 @@
continue
if len (args) not in (1, 2):
raise bSyntaxError, 'improper arguments'
- auth = sasl.bikini_auth (args)
+ auth = sasl.bikini_auth (args, self.pwchecker)
+ auth.checkpassword ()
if not auth.completed:
raise TemporaryCommandError, 'auth unsuccessful'
need_auth = 0
@@ -125,7 +128,7 @@
self.respond ('temporary', 'command failed temporarily (%s)' % err)
except UnimplementedError, err:
- self.respond ('unsupported', 'command not implemented (%s)' % err)
+ self.respond ('unsupported', 'command not supported (%s)' % err)
self.respond ('success', str (args))
log (DEBUG, 'auth uid %i gid %i name "%s" homedir %s\n' % (auth.uid, auth.gid, auth.fullname, auth.dir))
@@ -136,15 +139,20 @@
raise TemporaryCommandError, 'execl failed'
#######################################
-def main (mailstorepath, inboxpath):
+def main (mailstorepath, inboxpath, checkpassargs):
'''Main entry point.
'''
log (TRACE)
me = '%s pid %i uid %i euid %i' % (os.path.split (sys.argv[0])[1], os.getpid(), os.getuid(), os.geteuid())
log ('%s: starting...\n' % me)
+ if checkpassargs:
+ pwchecker = pwcheck.checkpassword (checkpassargs)
+ else:
+ pwchecker = pwcheck.null ()
+
try:
- bikini = BikiniServerAuth (inboxpath)
+ bikini = BikiniServerAuth (inboxpath, pwchecker)
bikini.go ()
except SystemExit:
@@ -170,3 +178,7 @@
log ('%s: finished\n' % me)
sys.exit (0)
+
+#######################################
+if __name__ == '__main__':
+ main (sys.argv[1], sys.argv[2], sys.argv[3:])
diff -urN --exclude=bikini.html --exclude=bikini.txt --exclude=CHANGELOG bikini-20030304-1130/bikini.texi bikini-20030305-1234/bikini.texi
--- bikini-20030304-1130/bikini.texi Tue Mar 4 11:30:54 2003
+++ bikini-20030305-1234/bikini.texi Wed Mar 5 12:34:24 2003
@@ -361,7 +361,8 @@
@item U
-Not supported. The command was recognized, but is not supported by the server.
+Not supported. The command was recognized, but is not supported by the server
+for technical or policy reasons.
The additional information section is optional, free-form, and continues to the
end of the line. Servers MAY use this to describe the condition in more detail.
@@ -455,16 +456,6 @@
The following flags are defined:
-@table @samp
-
-@c @item 0-9
-@c
-@c Message priority levels.
-@c Only one can be set.
-@c Defaults to @samp{1}.
-@c
-@c @item A-Z
-
Predefined flags.
@table @samp
@@ -498,14 +489,11 @@
@end table
-@c @item a-z
-@c
-@c Client-defined flags.
-
-@end table
-
The client may set or clear any flag.
+The @samp{GET} command clears the @samp{N} flag of the message in question
+as a side-effect.
+
Multiple clients have to cooperate when accessing the same mail store; they
have to agree on the meanings of some flags (like N, R, S, etc) for them to be
useful. Clients SHOULD NOT set or clear flags in an arbitrary way; if a given
@@ -535,7 +523,8 @@
@end example
Servers MUST NOT use any other timezone offset when presenting the timestamp
-messages attribute to a client.
+messages attribute to a client. If a client wishes to display timestamps to
+the user in local time, the client SHOULD perform the translation.
@c ----------------------------------------------------------------------------
@node message size
@@ -798,7 +787,7 @@
@item Upon initial connection, a client will immediately send the @samp{AUTH}
command using an auth-method of its choosing. Only if the server returns @samp{U}
does the client need to either try another well-known auth-method or retrieve the
-list of supported auth methods with the @samp{caps} command. Clients MAY choose
+list of supported auth methods with the @samp{CAPS} command. Clients MAY choose
to remember what auth methods are supported by a particular host so as to avoid
choosing an unsupported auth-method in their next session.
@item Servers MUST NOT close the session immediately after a successful response
@@ -1709,7 +1698,7 @@
S: K goodbye
@end example
-Note that the message's 'N' (new) flag is cleared by the @samp{readmsg}
+Note that the message's 'N' (new) flag is cleared by the successful @samp{GET}
operation.
@c ============================================================================
diff -urN --exclude=bikini.html --exclude=bikini.txt --exclude=CHANGELOG bikini-20030304-1130/service-run.sh bikini-20030305-1234/service-run.sh
--- bikini-20030304-1130/service-run.sh Tue Mar 4 11:30:54 2003
+++ bikini-20030305-1234/service-run.sh Wed Mar 5 12:34:24 2003
@@ -1,7 +1,4 @@
#!/bin/sh
-service=pop3d
-. /usr/lib/qmail/run-functions
-hostname="`hostname`"
concurrency="10"
# set ulimits/softlimits if you like