#!/usr/bin/env python
######################################################################
##                
## Copyright (C) 2006,  Goedson Teixeira Paixao
##                
## This program is free software; you can redistribute it and/or
## modify it under the terms of the GNU General Public License
## as published by the Free Software Foundation; either version 2
## of the License, or (at your option) any later version.
## 
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
## GNU General Public License for more details.
## 
## You should have received a copy of the GNU General Public License
## along with this program; if not, write to the Free Software
## Foundation, Inc., 51 Franklin Street, Fifth Floor Boston, 
## MA 02110-1301, USA
##                
## Filename:      check_pass_pam.py
## Author:        Goedson Teixeira Paixao <goedson@debian.org>
## Description:   Check ejabberd user password using PAM
##                
## Created at:    Wed Aug  2 09:50:56 2006
## Modified at:   Tue Aug 22 09:54:59 2006
## Modified by:   Goedson Teixeira Paixao <goedson@debian.org>
######################################################################
'''
An external script for ejabberd to check user passwords against PAM
'''

__revision__ = '0.1'




import sys
import traceback
import struct
import PAM
import time

def pam_auth_conversation(username, password):
    '''
    Defines a PAM conversation function to pass username and password 
    '''
    def pam_conversation(auth, query_list, userdata):
        '''
        Does the conversation to feed PAM with username and password. 
        '''
        resp = []
        for i in range(len(query_list)):
            _, query_type = query_list[i]
            if query_type == PAM.PAM_PROMPT_ECHO_ON:
                val = username
                resp.append((val, 0))
            elif query_type == PAM.PAM_PROMPT_ECHO_OFF:
                val = password
                resp.append((val, 0))
            else:
                return None
        return resp
    return pam_conversation 


def authenticate_user(username, password):
    '''
    Check, using PAM, if the username and password provided match.
    '''
    auth = PAM.pam()
    auth.start('passwd')
    auth.set_item(PAM.PAM_CONV, pam_auth_conversation(username, password))
    try:
        auth.authenticate()
    except PAM.error:
        return False
    except:
        return False
    else:
        return True

def main():
    '''
    The main loop
    '''
    log_file = open('/var/log/ejabberd/pam_auth.log', 'w')
    while True:
        time.sleep(2)
        try:
            nread = sys.stdin.read(2)
	    now = time.strftime('%d/%m/%Y %H:%M:%S',time.localtime())
            if len(nread) < 2:
                log_file.write('time=%s bytes_read=%d\n' % (now, len(nread)))
                log_file.flush()
                continue
            size = struct.unpack('>h', nread)[0]
            data = sys.stdin.read(size)
            (operation, user, host, password) = data.split(':')
            log_file.write('time=%s operation=%s user=%s host=%s\n'
                           % (now, operation, user, host))
            log_file.flush()
            if operation == 'auth':
                result = authenticate_user(user, password)
                if result:
                    sys.stdout.write(struct.pack('>hh', 2, 1))
                else:
                    sys.stdout.write(struct.pack('>hh', 2, 0))
                sys.stdout.flush()
            elif operation == 'isuser':
                sys.stdout.write(struct.pack('>hh', 2, 1))
                sys.stdout.flush()
            else:
                sys.stdout.write(struct.pack('>hh', 2, 0))
                sys.stdout.flush()
        except:
            traceback.print_exc(file=log_file)

if __name__ == '__main__':
    main()
