/*
** Copyright 1998 - 2007 Double Precision, Inc.  See COPYING for
** distribution information.
*/

#if	HAVE_CONFIG_H
#include	"courier_auth_config.h"
#endif
#include	<stdio.h>
#include	<stdlib.h>
#include	<string.h>
#include	<errno.h>
#include	<pwd.h>
#if	HAVE_UNISTD_H
#include	<unistd.h>
#endif
#include	"auth.h"
#include	"authstaticlist.h"
#include	"courierauthdebug.h"
#include	"vpopmail_config.h"
#include	<vpopmail.h>
#include	<vauth.h>

static const char rcsid[]="$Id: authvchkpw.c,v 1.29 2007/10/07 02:50:45 mrsam Exp $";


extern int auth_vchkpw_pre(const char *userid, const char *service,
        int (*callback)(struct authinfo *, void *),
                        void *arg);

extern FILE *authvchkpw_file(const char *, const char *);

static int auth_vchkpw_login(const char *service, char *authdata,
	int (*callback_func)(struct authinfo *, void *), void *callback_arg);

struct callback_info {
	const char *pass;
	int (*callback_func)(struct authinfo *, void *);
	void *callback_arg;
	};

static int callback_vchkpw(struct authinfo *a, void *p)
{
struct callback_info *i=(struct callback_info *)p;

	/* exit with perm failure if the supplied password is empty,
	 * or if the supplied password doesnt match the retrieved password */ 
	if (a->passwd == 0)
	{
		DPRINTF("no password supplied");
		return (-1);
	}

	if (authcheckpassword(i->pass, a->passwd))
		return (-1);

	a->clearpasswd=i->pass;
	return (*i->callback_func)(a, i->callback_arg);
}

#if HAVE_HMACLIB

#include        "libhmac/hmac.h"
#include        "cramlib.h"

static int auth_vchkpw_login(const char *service, char *authdata,
			int (*callback_func)(struct authinfo *, void *), void *callback_arg);

static int auth_vchkpw_cram(const char *service,
                           const char *authtype, char *authdata,
                           int (*callback_func)(struct authinfo *, void *),
                           void *callback_arg)
{
        struct  cram_callback_info      cci;

        if (auth_get_cram(authtype, authdata, &cci))
                return (-1);

        cci.callback_func=callback_func;
        cci.callback_arg=callback_arg;

        return auth_vchkpw_pre(cci.user, service, &auth_cram_callback, &cci);
}
#endif

int auth_vchkpw(const char *service, const char *authtype, char *authdata,
               int (*callback_func)(struct authinfo *, void *),
               void *callback_arg)
{
        if (strcmp(authtype, AUTHTYPE_LOGIN) == 0)
                return (auth_vchkpw_login(service, authdata,
                        callback_func, callback_arg));

#if HAVE_HMACLIB
        return (auth_vchkpw_cram(service, authtype, authdata,
                        callback_func, callback_arg));
#else
        errno=EPERM;
        return (-1);
#endif

}



static int auth_vchkpw_login(const char *service, char *authdata,
	int (*callback_func)(struct authinfo *, void *), void *callback_arg)
{
char *user, *pass;
struct	callback_info	ci;
int	rc;
	/* Make sure that we have been supplied with the correct
	 * AUTHDATA format which is : userid<NEWLINE>password<NEWLINE>
	 */
	if ( (user=strtok(authdata, "\n")) == 0 || (pass=strtok(0, "\n")) == 0)
	{
		/* login syntax was invalid */
		errno=EPERM;
		return (-1);
	}

	ci.pass=pass;
	ci.callback_func=callback_func;
	ci.callback_arg=callback_arg;

	/* auth_vchkpw_pre() does this :
	 *   - lookup the passwd entry for this user from the auth backend
	 *   - check to see if this user is permitted to use this service type
	 * If successful it will populate the ci struct with the 
	 * user's passwd entry. Return value of function will be 0.
	 * If unsuccessful (eg user doesnt exist, or is not permitted to 
	 * use this auth method), it will return :
	 *  <0 on a permanent failure (eg user doesnt exist)
	 *  >0 on a temp failure
         */
	rc=auth_vchkpw_pre(user, service, &callback_vchkpw, &ci);

	if (rc)
		return rc;

	/* user has been successfully auth'ed at this point */

#if 0
	/*
	** sam - new courier-authlib never receives TCPREMOTEIP, at this
	** time.
	*/

#ifdef HAVE_OPEN_SMTP_RELAY
	if ( (strcmp("pop3", service)==0) || (strcmp("imap", service)==0) ) {
		/* Michael Bowe 13th August 2003
		 *
		 * There is a problem here because open_smtp_relay needs 
		 * to get the user's ip from getenv("TCPREMOTEIP").
		 * If we run --with-authvchkpw --without-authdaemon,
		 * then this var is available.
		 * But if we run --with-authvchkpw --with-authdaemon,
		 * then TCPREMOTEIP is null
		 * 
		 * If TCPREMOTEIP isnt available, then open_smtp_relay()
		 * will just return() back immediately.
		 */
		open_smtp_relay();
	}
#endif
#endif

	return 0;
}

static void authvchkpwclose()
{
}

static int auth_vchkpw_changepass(const char *service,
				  const char *username,
				  const char *pass,
				  const char *npass)
{
struct vqpasswd *vpw;
char	User[256];
char	Domain[256];

        /* Take the supplied userid, and split it out into the user and domain
         * parts. (If a domain was not supplied, then set the domain to be
         * the default domain)
         */
        /* WARNING: parse_email lowercases the username in place - not const!! */
        if ( parse_email(username, User, Domain, 256) != 0) {
                /* Failed to successfully extract user and domain.
                 * So now exit with a permanent failure code
                 */
                return(-1);
        }

	/* check to see if domain exists.
	 * If you pass an alias domain to vget_assign, it will change it
	 * to be the real domain on return from the function
	 */
        if ( vget_assign(Domain,NULL,0,NULL,NULL) ==NULL ) {
		/* domain doesnt exist */
		return (-1);
	}

        if ( (vpw=vauth_getpw(User, Domain)) == NULL) {
		/* That user doesnt exist in the auth backend */
		errno=ENOENT;
		return (-1);
	}

	/* Exit if any of the following :
	 *   - user's password field in the passwd entry is empty
	 *   - supplied current password doesnt match stored password
	 */
	if (vpw->pw_passwd == 0 || authcheckpassword(pass, vpw->pw_passwd)) {
		errno=EPERM;
		return (-1);
	}

	/* save the new password into the auth backend */
	if ( vpasswd(User, Domain, (char *)npass, 0) != 0 ) {
		/* password set failed */
		return (-1);
	};

	return (0);
}

struct authstaticinfo authvchkpw_info={
	"authvchkpw",
	auth_vchkpw,
	auth_vchkpw_pre,
	authvchkpwclose,
	auth_vchkpw_changepass,
	authvchkpwclose,
	NULL};


struct authstaticinfo *courier_authvchkpw_init()
{
	return &authvchkpw_info;
}


syntax highlighted by Code2HTML, v. 0.9.1