External auth problems.

Hello, I'm trying out the extauth improvements that are found in https://forge.process-one.net/browse/~raw,r=1330/ejabberd/trunk/src/exta...

Specifically, this looks like it is intended to handled "Other" responses as well as timeouts.

I then modified the python auth script from http://svn.process-one.net/ejabberd/trunk/doc/dev.html#htoc9 to be

#!/usr/bin/python

import sys
from struct import *

def from_ejabberd():
    input_length = sys.stdin.read(2)
    (size,) = unpack('>h', input_length)
    return sys.stdin.read(size).split(':')

def to_ejabberd(bool):
    answer = 0
    if bool:
        answer = 1
    token = pack('>hh', 2, answer)
    sys.stdout.write("garbage from auth.py")
    sys.stdout.write(token)
    sys.stdout.flush()

def auth(username, server, password):
    return True

def isuser(username, server):
    return True

def setpass(username, server, password):
    return True

while True:
    data = from_ejabberd()
    success = False
    if data[0] == "auth":
        success = auth(data[1], data[2], data[3])
    elif data[0] == "isuser":
        success = isuser(data[1], data[2])
    elif data[0] == "setpass":
        success = setpass(data[1], data[2], data[3])
    to_ejabberd(success)

At this point, if I try to connect using this script as my authorizer, I get the following in my ejabberd.log

=INFO REPORT==== 2008-10-17 11:14:55 ===
I(<0.249.0>:ejabberd_listener:112) : (#Port<0.412>) Accepted connection {{127,0,0,1},48309} -> {{127,0,0,1},5222}

=ERROR REPORT==== 2008-10-17 11:15:25 ===
E(<0.252.0>:extauth:80) : extauth call '["auth","jcornez","localhost",
                                         "jcornez"]' didn't receive response

I was expecting to see the "received strange response" message.

Any suggestions?
-Jason

It seems ejabberd doesn't get the response from Erlang

The extauth script is connected to ejabberd using a port, that is created with the argument {packet, 2}. It seems Erlang will not route any response that does not meet the requirements. So there's nothing in ejabberd code that can be done to receive those strange messages that don't meet the requirement.

A possible work-around

Something like the following filter.py can be used as the auth script by ejabberd. This calls the "real" auth script and logs all traffic in between the two. It also tries to detect garbage and protect ejabberd from stuff that doesn't match the protocol. I don't think this script would be so great for long-term production, but as a development and debugging tool, it is quite handy. Should be fairly straight-forward for someone to modify.

I grant permission that this (or some derived work) could be used in a more official capacity by ejabberd. Acknowledgement would be appreciated, but officially this is public domain.

#!/usr/bin/python

import sys, os, fcntl
from struct import *
from subprocess import *

# auth_script = "/path/to/real/auth/script"
auth_script = "/etc/ejabberd/auth.py"

def from_ejabberd():
    input_length = sys.stdin.read(2)
    (size,) = unpack('>h', input_length)
    return sys.stdin.read(size)

def to_ejabberd(answer):
    token = pack('>hh', 2, answer)
    sys.stdout.write(token)
    sys.stdout.flush()


child = Popen([auth_script], stdin=PIPE, stdout=PIPE)
childout = child.stdout.fileno()
fcntl.fcntl(childout, fcntl.F_SETFL, os.O_NONBLOCK)
log = open("/var/log/ejabberd/auth-filter.log",'a+b',0)

while True:
    request = from_ejabberd()
    size = pack('>h', len(request))
    child.stdin.write(size)
    child.stdin.write(request)
    child.stdin.flush()

    log.write("Request: ")
    log.write(request)
    log.write('\n')

    result = 0
    response_start = ""
    while response_start == "":
        try:
            response_start = os.read(childout, 2)
        except OSError, err:
            pass
    (size,) = unpack('>h', response_start)
    log.write("Response: ")
    if size == 2:
        response_rest = os.read(childout, 2)
        (result,) = unpack('>h', response_rest)
        log.write( "%d" % result )
    else:
        done = False
        log.write(response_start)
        response_rest = ""
        while not done:
            try:
                char = os.read(childout, 1)
                response_rest += char
            except OSError, err:
                done = True
        log.write(response_rest)

    log.write('\n')
    log.flush()
    to_ejabberd(result)

Published in Contributions page

To help people find your script, I created a page for it in Contributions -> Authentication Scripts -> Authentication Intercept script. You are author of that page, so you can edit it to change the name or content if you want.

Syndicate content