/*	$CoreSDI: auth_raw.c,v 1.19 2001/09/06 21:46:25 claudio Exp $	*/

/*
 *   Copyright (c) 2000, 2001, Core SDI S.A., Argentina
 *   All rights reserved
 *  
 *   Redistribution and use in source and binary forms, with or without
 *   modification, are permitted provided that the following conditions
 *   are met:
 *  
 *   1. Redistributions of source code must retain the above copyright
 *      notice, this list of conditions and the following disclaimer.
 *   2. Redistributions in binary form must reproduce the above copyright
 *      notice, this list of conditions and the following disclaimer in the
 *      documentation and/or other materials provided with the distribution.
 *   3. Neither name of the Core SDI S.A. nor the names of its contributors
 *      may be used to endorse or promote products derived from this software
 *      without specific prior written permission.
 *  
 *   THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 *   IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 *   OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 *   IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 *   INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *   NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 *   THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/*
 * Raw Authentication Module
 * Author: Claudio Castiglia
 */

#ifndef WIN32		/* Unix */
#include <sys/param.h>

#ifdef __linux__
#include <netinet/in.h>		/* in_addr_t */
#endif

#include <errno.h>
#include <grp.h>
#include <pwd.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include <time.h>
#ifdef HAVE_SHADOW
#include <shadow.h>
#endif

#else			/* Win32 */
#include <sys/types.h>

#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include "exits.h"

#endif /* WIN32 */

#include "sysdep.h"
#include "packet.h"
#include "resource.h"
#include "modtypes.h"
#include "authargs.h"
#include "commands.h"

#ifndef AUDIT_GROUPNAME
#define AUDIT_GROUPNAME	"audit"
#endif

#ifdef HAVE_SHADOW
struct _passwd {
	char    *pw_name;        /* user name */
	char    *pw_passwd;      /* encrypted password */
	uid_t    pw_uid;         /* user uid */
	gid_t    pw_gid;         /* user gid */
	time_t   pw_change;      /* password change time */
	char    *pw_class;       /* user access class */
	char    *pw_gecos;       /* Honeywell login info */
	char    *pw_dir;         /* home directory */
	char    *pw_shell;       /* default shell */
	time_t   pw_expire;      /* account expiration */
};

extern char * crypt (const char *, const char *);

static struct _passwd *
_getpwnam(const char *login)
{
	static struct _passwd	 pw;
	struct passwd		*np;
	struct spwd		*sp;

	if ( (np = getpwnam(login)) == NULL)
		return (NULL);

	pw.pw_name = np->pw_name;
	pw.pw_uid = np->pw_uid;
	pw.pw_gid = np->pw_gid;
	pw.pw_shell = np->pw_shell;

	if (np->pw_passwd[0] == 'x') {
		if ( (sp = getspnam(login)) == NULL)
			return (NULL);
		pw.pw_passwd = sp->sp_pwdp;
	} else
		pw.pw_passwd = np->pw_passwd;

	return (&pw);
}

#else

#define	_getpwnam	getpwnam
#define _passwd		passwd

#endif /* HAVE_SHADOW */


/*
 * _auth_srvr()
 *	Server side authentication.
 *	return 0 on success or -1 if login failed.
 */
static int
_auth_srvr(AUTHCON *ct)
{
#ifndef WIN32	/* Unix */
	int32_t		 srvr_ok;
	char		 hostname[MAXHOSTNAMELEN] = { "" },
			 clntpass[_PASSWORD_LEN + 1],
			**m;
	struct group	*gp;
	struct _passwd	*pw;

	/* XXX move to engine: Send server hostname to the client */
	gethostname(hostname, sizeof(hostname));
	packet_put_string(ct->packet, hostname);

	/* Receive client username and password */
	packet_get_string(ct->packet, ct->peername, sizeof(ct->peername));
	packet_get_string(ct->packet, clntpass, sizeof(clntpass));

	/*
	 * No access if AUDIT_GROUPNAME does not exists;
	 * no access if user does not exists in /etc/passwd.
	 */
	srvr_ok = LOGIN_SUCCESS;
	if ( (gp = getgrnam(AUDIT_GROUPNAME)) == NULL ||
	     (pw = _getpwnam(ct->peername)) == NULL)
		srvr_ok = LOGIN_FAILED;
	else {
		/*
		 * No access if user does not belongs
		 * to the AUDIT_GROUPNAME group.
		 */
		for (m = gp->gr_mem; *m != NULL; m++)
			if (!strcmp(*m, ct->peername))
				break;
		if (*m == NULL)
			srvr_ok = LOGIN_FAILED;
	}
		
	/* No access if password is not correct */
	if (srvr_ok == LOGIN_SUCCESS &&
	    strcmp(pw->pw_passwd, crypt(clntpass, pw->pw_passwd)))
		srvr_ok = LOGIN_FAILED;

	/* Send login status to the client */
	packet_put_int32(ct->packet, srvr_ok);

	return ((srvr_ok == LOGIN_SUCCESS) ? 0 : -1);

#else		/* Win32 */

	return (-1);

#endif /* WIN32 */
}


/*
 * _auth_clnt()
 *	Client side authentication;
 *	return 0 on success or -1 if permission denied.
 */
static int
_auth_clnt(AUTHCON *ct, struct autharg_clnt *args)
{
	int32_t	login_status;

	/* XXX: move to engine: Get server hostnmae */
	packet_get_string(ct->packet, ct->peername, sizeof(ct->peername));

	/* Get username */
	if (args->user[0] == '\0') {
		fprintf(stderr, "login: ");
		fgets(args->user, sizeof(args->user), stdin);
		args->user[strlen(args->user) - 1] = '\0';
	}

	/* Get password */
	if (args->pass[0] == '\0') {
		char *pass;

		fprintf(stderr, "password: ");
		pass = (char *) getpass("");
		strlcpy(args->pass, pass, sizeof(args->pass));
		bzero(pass, _PASSWORD_LEN);
	}

	/* Send username and password to the server */
	packet_put_string(ct->packet, args->user);
	packet_put_string(ct->packet, args->pass);
	bzero(args->pass, sizeof(args->pass));

	/* Receive server authentication status */
	packet_get_int32(ct->packet, &login_status);
	return ((login_status == LOGIN_SUCCESS) ? 0 : -1);
}


/*
 * init()
 */
EXPORT AUTHCON *
init()
{
	return ((AUTHCON *) malloc(sizeof(AUTHCON)));
}


/*
 * proc_entry()
 */
EXPORT int
proc_entry(int opcode, AUTHCON *context, void *args)
{
	if (context == NULL) {
		errno = EINVAL;
		return (-1);
	}

	switch (opcode) {
	case AUTH_SERVER:
		/* Authentication process: server side */
		return (_auth_srvr(context));

	case AUTH_CLIENT:
		/* Authentication process: client side */
		return (_auth_clnt(context, args));

	case AUTH_FILTER_RESOURCE:
		/* Raw module does not filter any resource */
		((struct autharg_filter *) args)->fvalue = NULL;
		((struct autharg_filter *) args)->fvsize = 0;
		return (0);

	default:
		errno = EINVAL;
	}

	return (-1);
}

