/*	$CoreSDI: audit.c,v 1.46 2001/10/05 23:14:58 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.
 */

/*
 * audit - Audit Client
 * Author: Claudio Castiglia
 */

#ifndef WIN32		/* Unix */

#include <sys/param.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/uio.h>
#include <netinet/in.h>

#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <pwd.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <signal.h>
#include <sysexits.h>
#include <syslog.h>
#include <unistd.h>
#include <stdarg.h>

#else			/* Win32 */

#include <sys/types.h>
#include <sys/stat.h>

#include <errno.h>
#include <fcntl.h>
#include <io.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>

#endif /* WIN32 */

#ifdef WIN32
#include "exits.h"
#include "sysdep.h"
#include "winsyslog.h"	/* winsyslog needs sysdep.h */
#else
#include "sysdep.h"
#endif /* WIN32 */

#include "packet.h"
#include "version.h"
#include "commands.h"
#include "resource.h"
#include "modtypes.h"
#include "modules.h"
#include "audit.h"
#include "audconf.h"
#include "authargs.h"
#include "log.h"

#if defined(__linux__) || defined(WIN32)
int getsubopt (char **, char * const *, char **);
#endif

#ifndef WIN32
#define DIRECTORY_SEP		'/'
#else
#define DIRECTORY_SEP		'\\'
#endif

extern char			*__progname;

static int			 module_set = 0;
static char			*hostname = NULL,
				 outdir[MAXPATHLEN] = { "" };
static LOGFLAGS			 lflags;
static PACKET			*packet = NULL;
static struct autharg_clnt	 login;

char				 modpath[MAXPATHLEN];

void usage();


/*
 * audit_cleanup():
 *	Flush and close audit session.
 */
void
audit_cleanup()
{
	packet_release(packet);
	bzero(login.pass, sizeof(login.pass));
}


#ifndef WIN32

/*
 * signal_handler()
 */
void
signal_handler(int signo)
{
	int old_errno;

	old_errno = errno;
	switch (signo) {
	case SIGPIPE:
		cleanup_rm_all();
		fatal(EX_IOERR, "Connection lost.");
	case SIGINT:
	case SIGQUIT:
	case SIGTERM:
	case SIGABRT:
		fatal(-1, "");
	default:
	}
	errno = old_errno;
}


/*
 * set_signal_handler()
 */
int
set_signal_handler()
{
	struct sigaction sa;

	sigemptyset(&sa.sa_mask);
	sigaddset(&sa.sa_mask, SIGINT);
	sigaddset(&sa.sa_mask, SIGQUIT);
	sigaddset(&sa.sa_mask, SIGTERM);
	sigaddset(&sa.sa_mask, SIGABRT);
	sigaddset(&sa.sa_mask, SIGPIPE);
	sa.sa_handler = signal_handler;
	sa.sa_flags = 0;
        return (sigaction(SIGINT, &sa, NULL) |
            sigaction(SIGQUIT, &sa, NULL) |
            sigaction(SIGTERM, &sa, NULL) |
            sigaction(SIGABRT, &sa, NULL) |
            sigaction(SIGPIPE, &sa, NULL));
}

#endif /* WIN32 */


/*
 * send_command():
 *	Send a command to the server;
 *	return the returned command status: CMD_OK or CMD_EOPNOTSUPP,
 *	in the last case a string indicating the error is logged.
 */
int
send_command(const char *cmdname, int32_t cmdcode, const char *arg1,
	     const char *arg2, const void *arg3, ssize_t arg3_size)
{
	int32_t status;

	log_debug("Sending command '%s %s %s'.", cmdname, arg1, arg2);

	/* Send command and its params */
	packet_put_int32(packet, cmdcode);
	if (arg1 != NULL)
		packet_put_string(packet, arg1);
	if (arg2 != NULL)
		packet_put_string(packet, arg2);
	if (arg3 != NULL && arg3_size >= 0) {
		packet_put_int32(packet, arg3_size);
		packet_put_raw(packet, arg3, arg3_size);
	}

	/* Receive command status */
	packet_get_int32(packet, &status);
	if (status != CMD_OK) {
		char errmsg[LINE_MAX];
		packet_get_string(packet, errmsg, sizeof(errmsg));
		if (errmsg[0] == '\0')
			log_err("%s: Command failed.", cmdname);
		else
			log_err("%s: %s", cmdname, errmsg);
	}
	return (status);
}


/*
 * creat_output():
 *	Create file to put output data; return file descriptor
 */
int
creat_output(char *fname, char *fext, int flags)
{
	char	 path[MAXPATHLEN];
	int	 fd;

	if (outdir[0] == '\0') {
		fflush(stdout);
		return (STDOUT_FILENO);
	}
	if (*fname == DIRECTORY_SEP)
		fname++;
	snprintf(path, sizeof(path), "%s%c%s%s", outdir, DIRECTORY_SEP, fname,
	    fext);

	if (flags)
		flags = O_APPEND;
	else
		flags = O_TRUNC;
	fd = open(path, O_RDWR | O_CREAT | flags, S_IRUSR | S_IWUSR);
	if (fd < 0)
		fatal(EX_CANTCREAT, "%s: %s", path, strerror(errno));
	log_msg(LOG_INFO, "Downloading %s.", path);
	return (fd);
}


/*
 * close_output():
 *	Close output file.
 */
void
close_output(int fd)
{
	if (outdir[0] != '\0')
		close(fd);
}


/*
 * get_string_list():
 *	Receive a string list and write it to a specified file descriptor.
 */
void
get_string_list(int fd)
{
	char	buf[LINE_MAX + 1];
	size_t	len;

	while (1) {
		packet_get_string(packet, buf, LINE_MAX - 1);
		if (buf[0] == '\0')
			break;
		len = strlen(buf);
		buf[len++] = '\n';
		buf[len] = '\0';
		if (write(fd, buf, len) < 0)
			fatal(EX_OSERR, "write: %s", strerror(errno));
	} 
} 


/*
 * ia_set_module():
 *	Set specified IA module.
 */
int
ia_set_module(char *name)
{
	int	status;

	log_debug("Setting '%s' ia module.", name);
	status = send_command("SET IA MODULE", CMD_IA_SET_MODULE, name, NULL,
	    NULL, 0);
	if (status != CMD_OK)
		return (-1);
	module_set = 1;
	return (0);
}


/*
 * ia_get_list():
 *	Get logset or IA module list.
 */
int
ia_get_list()
{
	char	 *cmdname;
	int	 status;
	int32_t	 cmd;

	log_debug("Getting logset or ia modules list.");
	if (module_set) {
		cmd = CMD_IA_GET_LIST;
		cmdname = "GET LOGSET LIST";
	} else {
		cmd = CMD_IA_GET_MODULE_LIST;
		cmdname = "GET IA MODULE LIST";
	}
	status = send_command(cmdname, cmd, NULL, NULL, NULL, 0);
	if (status != CMD_OK)
		return (-1);
	fflush(stdout);
	get_string_list(STDOUT_FILENO);
	return (0);
}


/*
 * ia_get_info():
 *	Get information about a logset.
 */
int
ia_get_info(char *logset_name)
{
	int fd;

	log_debug("Getting information from '%s'.", logset_name);
	if (logset_name == NULL) {
		log_err("GET INFO: Missing argument.");
		return (-1);
	}

	if (send_command("GET INFO", CMD_IA_GET_INFO, logset_name, NULL,
	    NULL, 0) != CMD_OK)
		return (-1);

	/* Create output info file and receive information */
	fd = creat_output(logset_name, INFO_FILE_EXT, 0);
	get_string_list(fd);
	close_output(fd);
	return (0);
}


/*
 * ia_get():
 *	Receive logset data.
 */
int
ia_get(char *logset_name)
{
	char	 buf[LINE_MAX];
	int	 fd;
	int32_t	 status;

	log_debug("Getting '%s'.", logset_name);
	if (logset_name == NULL) {
		log_err("GET: Missing argument.");
		return (-1);
	}
	
	if (send_command("GET", CMD_IA_GET, logset_name, NULL, NULL, 0)
	    != CMD_OK)
		return (-1);

	/* Create output info file and receive information */
	fd = creat_output(logset_name, INFO_FILE_EXT, 0);
	get_string_list(fd);
	close_output(fd);

	/* Receive logs */
	while (1) {
		/* Receive file name */
		packet_get_string(packet, buf, sizeof(buf));
		if (buf[0] == '\0')
			break;

		/* Create output file and receive logs */
		fd = creat_output(buf, "", 1);
		packet_read(packet, fd);
		close_output(fd);
	}

	/* Receive command status */
	packet_get_int32(packet, &status);
	if (status != CMD_OK) {
		char errmsg[LINE_MAX];
		packet_get_string(packet, errmsg, sizeof(errmsg));
		if (errmsg[0] == '\0')
			log_err("GET: Command failed.");
		else
			log_err("GET: %s", errmsg);
	}

	return (0);
}


/*
 * ia_zap():
 *	Zap logset.
 */
int
ia_zap(char *logset_name)
{
	log_debug("Zapping '%s'.", logset_name);
	if (logset_name == NULL) {
		log_err("ZAP: %s.", strerror(EINVAL));
		return (-1);
	}
	return ((send_command("ZAP", CMD_IA_ZAP, logset_name, NULL, NULL, 0)
	    != CMD_OK) ? -1 : 0);
}


/*
 * ia_rotate():
 *	Rotate logset.
 */
int
ia_rotate(char *logset_name)
{
	log_debug("Rotating '%s'.", logset_name);
	if (logset_name == NULL) {
		log_err("ROTATE: %s.", strerror(EINVAL));
		return (-1);
	}
	return ((send_command("ROTATE", CMD_IA_ROTATE, logset_name, NULL, NULL,
	    0) != CMD_OK) ? -1 : 0);
}


/*
 * ia_sign():
 *	Sign logset.
 */
int
ia_sign(char *logset_name)
{
	log_err("SIGN command not supported yet.");
	return (-1);	/* XXX */
}


/*
 * auth_set_module():
 *	Set authentication module name.
 */
int
auth_set_module(char *name)
{
	log_debug("Setting '%s' authentication module.", name);
	return ((send_command("SET AUTH MODULE", CMD_IA_SET_MODULE, name,
	    NULL, NULL, 0) != CMD_OK) ? -1 : 0);
}


/*
 * auth_get_module_list():
 *	Get authentication module name list.
 */
void
auth_get_module_list()
{
	char	data[MAXPATHLEN];
	int	status;

	log_debug("Getting authentication module list.");
	status = send_command("GET AUTH MODULE LIST", CMD_IA_GET_MODULE_LIST,
	    NULL, NULL, NULL, 0);
	fflush(stdout);
	if (status == CMD_OK) {
		while (1) {
			packet_get_string(packet, data, sizeof(data));
			if (*data == '\0')
				break;
			printf("%s\n", data); /* XXX: send it to output dir */
		}
	}
}


/*
 * open_connection():
 *	Connect with audit server, initialize global packet structure
 *	and set cleanup functions to be called on fatal errors;
 *	return 0 on success or -1 and set errno on error.
 */
int
open_connection(char *host, int port)
{
	struct sockaddr_in	addr;
	in_addr_t		ip;
	int			conn_fd;

	log_debug("Opening connection to '%s', port '%d'.", host, port);
	if ((ip = resolve(host)) == 0)
		return (-1);
	memset(&addr, 0, sizeof(struct sockaddr_in));
	addr.sin_family = AF_INET;
	addr.sin_port = htons((u_int16_t) port);
	addr.sin_addr.s_addr = ip;
	if ((conn_fd = init_socket()) < 0)
		return (-1);
	if (connect(conn_fd, (struct sockaddr *) &addr,
	    sizeof(struct sockaddr_in)) < 0)
		return (-1);
	packet = packet_init(conn_fd, conn_fd);
	if (packet == NULL)
		return (-1);
	cleanup_add((void(*)(void *)) audit_cleanup, NULL);
	return (0);
}


/*
 * close_connection():
 */
void
close_connection()
{
	packet_put_int32(packet, CMD_QUIT);
	audit_cleanup();
}


/*
 * connect_to_server():
 *	Connect to audited server and exchange version numbers.
 */
void
connect_to_server(char *host, int port)
{
	if (open_connection(host, port) < 0) {
		fatal(EX_NOHOST, "Unable to connect with %s.", host,
		    strerror(errno));
		/* NOTREACHED */
	}

	if (!compatible_peer(packet)) {
		fatal(EX_PROTOCOL, "Incompatible server version.");
		/* NOTREACHED */
	}
}


/*
 * parse_hostname():
 *	Parse a line like this: [username[:password]@]hostname
 *	and set hostname, username, and password global variables.
 */
void
parse_hostname(char *cline)
{
	char	*pass;

	/* Detect hostname */
	if ( (hostname = strchr(cline, '@')) == NULL)
		hostname = cline;
	else {
		*hostname = '\0';
		hostname++;

		/* Detect username */
		if ( (pass = strchr(cline, ':')) != NULL) {
			*pass = '\0';
			strlcpy(login.pass, pass + 1, sizeof(login.pass));
			bzero(pass, strlen(pass));
		}
		strlcpy(login.user, cline, sizeof(login.user));
	}
}


/*
 * exec_ia_cmd():
 * 	Parse an IA command line like this:
 *		[ia_module_name:]{command=logset[, ...]}
 *	and execute specified commands.
 *	Return 0 on success and -1 on error (invalid command)
 */
int
exec_ia_cmd(char *cline)
{
	static const char *ia_command_names[] = {
	    "list", "info", "get", "rotate", "sign", "zap", NULL };
	char *p;
	int st;

	/* Detect and set ia module */
	if ( (p = strchr(cline, ':')) != NULL) {
		*p = '\0';
		if (ia_set_module(cline) < 0)
			return (-1);
		cline = p + 1;
	}

	/* Detect and execute ia command */
	while (*cline) {
		switch (getsubopt(&cline, (char * const *) ia_command_names,
		    &p)) {
		case 0: /* list */
			st = ia_get_list(p);
			break;
		case 1: /* info */
			st = ia_get_info(p);
			break;
		case 2: /* get */
			st = ia_get(p);
			break;
		case 3: /* rotate */
			st = ia_rotate(p);
			break;
		case 4: /* sign */ 
			st = ia_sign(p);
			break;
		case 5: /* zap */
			st = ia_zap(p);
			break;
		default:
			errno = EINVAL;
			return (-1);
		}
		if (st < 0)
			break;
	}
	return (0);
}


/*
 * resadmin_adduser():
 *	Add a new auditor.
 */
int
resadmin_adduser(char *uname)
{
	log_debug("Adding user '%s'.", uname);
	if (uname != NULL)
		return (send_command("ADDUSER", CMD_RES_ADDUSER, uname, NULL,
		    NULL, 0) == CMD_OK ? 0 : -1);
	log_err("ADDUSER: %s.", strerror(EINVAL));
	return (-1);
}


/*
 * resadmin_rmuser():
 *	Remove an auditor.
 */
int
resadmin_rmuser(char *uname)
{
	log_debug("Removing user '%s'.", uname);
	if (uname != NULL)
		return (send_command("RMUSER", CMD_RES_RMUSER, uname, NULL,
		    NULL, 0) == CMD_OK ? 0 : -1);
	log_err("RMUSER: %s.", strerror(EINVAL));
	return (-1);
}


/*
 * resadmin_set():
 *	Create/Modify resource value.
 */
int
resadmin_set(AUTHCON *aucon, char *uname, char *rname, void *value,
	     ssize_t size)
{
	struct autharg_filter farg;
	int st;

	log_debug("Setting resource '%s' for user '%s'.", rname, uname);
	if (uname == NULL || rname == NULL || value == NULL || size <= 0) {
		log_err("SET RESOUCE: %s.", strerror(EINVAL));
		return (-1);
	}

	farg.rname = rname;
	farg.value = value;
	farg.vsize = size;
	farg.fvalue = NULL;
	farg.fvsize = 0;
	if (aucon->mod.proc_entry(AUTH_FILTER_RESOURCE, aucon, &farg) < 0) {
		log_err("Can't set '%s': Can't filter resource. "
		    "Command aborted.", rname);
		return (-1);
	}

	st = send_command("SET RESOURCE", CMD_RES_SET, uname, rname,
	    (farg.fvalue == NULL) ? value : farg.fvalue,
	    (farg.fvalue == NULL) ? size : farg.fvsize);
		
	if (farg.fvalue != NULL)
		free(farg.fvalue);

	return (st == CMD_OK ? 0 : -1);
}


/*
 * resadmin_find():
 *	Search for a resource and print its value.
 */
int
resadmin_find(char *uname, char *rname)
{
	int32_t i, size;
	char	buf[BUFSIZ];

	log_debug("Finding resource '%s' for user '%s'.", rname, uname);
	if (uname == NULL || rname == NULL) {
		log_err("FIND RESOURCE: %s.", strerror(EINVAL));
		return (-1);
	}

	i = send_command("FIND RESOURCE", CMD_RES_FIND, uname, rname, NULL, 0);
	if (i != CMD_OK)
		return (-1);

	/* Download resource data an send it to standard output */
	packet_get_int32(packet, &size);
	fflush(stdout);
	while (size) {
		i = (sizeof(buf) > size) ? size: sizeof(buf);
		packet_get_raw(packet, buf, i);
		write(STDOUT_FILENO, buf, i);
		size -= i;
	}
	return (0);
}


/*
 * resadmin_remove():
 *	Remove a resource.
 */
int
resadmin_remove(char *uname, char *rname)
{
	log_debug("Removing resource '%s' for user '%s'.", rname, uname);
	if (uname == NULL || rname == NULL) {
		log_err("REMOVE RESOURSE: %s.", strerror(EINVAL));
		return (-1);
	}
	return (send_command("REMOVE RESOURCE", CMD_RES_REMOVE, uname, rname,
	    NULL, 0) == CMD_OK ? 0 : -1);
}


/*
 * res_parse():
 * 	Parse a resource command line like this:
 *		[username:]resourcename[=value]
 *	and separate its components.
 */
void
res_parse(char *cline, char **uname, char **rname, char **value)
{
	char	*p;

	/* Detect optional username */
	if ( (p = strchr(cline, ':')) == NULL)
		/* Use current user if username not specified */
		*uname = login.user;
	else {
		*p = '\0';
		*uname = cline;
		cline = p + 1;
	}

	/* Set resource name */
	*rname = cline;

	/* Detect optional resource value */
	if ( (p = strchr(cline, '=')) != NULL) {
		*p = '\0';
		*value = p + 1;
	} else
		*value = cline + strlen(cline); /* *value = &'\0' */
}


/*
 * exec_res_cmd():
 * 	Parse a resource command line like this:
 *		resouce_command=[username:]resourcename[=value] [, ...]
 *	and execute specified commands.
 *	Return 0 on success and -1 on error (invalid command).
 */
int
exec_res_cmd(AUTHCON *aucon, char *cline)
{
	static const char *res_command_names[] = {
	    "adduser", "rmuser", "set", "find", "rm", NULL };
	char *p, *uname, *rname, *value;
	int st;

	while (*cline) {
		switch (getsubopt(&cline,
		    (char * const *)res_command_names, &p)) {
		case 0:	/* Add new auditor */
			st = resadmin_adduser(p);
			break;
		case 1:	/* Remove auditor */
			st = resadmin_rmuser(p);
			break;
		case 2:	/* Create/modify a resource */
			res_parse(p, &uname, &rname, &value);
			st = resadmin_set(aucon, uname, rname, value,
			    strlen(value));
			break;
		case 3:	/* Find an existing resource */
			res_parse(p, &uname, &rname, &value);
			st = resadmin_find(uname, rname);
			break;
		case 4:	/* Remove a resource */
			res_parse(p, &uname, &rname, &value);
			st = resadmin_remove(uname, rname);
			break;
		default:
			errno = EINVAL;
			return (-1);
		}
		if (st < 0)
			break;
	}
	return (0);
}


/*
 * main()
 */
int
main(int argc, char **argv)
{
	extern char		*optarg;
	extern int	 	 optind;
	int		 	 opt, old_optind, port, status;
	char			*authname;
	AUTHCON			*authcon;

#ifdef WIN32
	__progname = argv[0];
	init_windows_wsa();
	atexit((void (*)(void)) WSACleanup);
#endif

	/* Set default values */
	authname = NULL;
	port = DEFAULT_AUDIT_PORT;
	lflags = LOGF_STDERR | LOGF_ERROR;
	set_modules_path(DEFAULT_MODULES_PATH);
	log_init(lflags, LOG_USER);

	/* Get user configuration */
	old_optind = optind;
	while ( (opt = getopt(argc, argv, "a:dl:M:o:p:qi:r:h?")) != -1) {
		switch (opt) {
		case 'a': /* Authentication module */
			authname = optarg;
			break;
		case 'd': /* Log debug information */
			lflags |= LOGF_DEBUG;
			break;
		case 'l': /* Login name, pass, and hostname */
			parse_hostname(optarg);
			break;
		case 'M': /* Modules pathname */
			if (realpath(optarg, modpath) == NULL)
				fatal(EX_CONFIG, "Invalid -M option: %s",
				    strerror(errno));
			set_modules_path(modpath);
			break;
		case 'o': /* Input/output directory */
			if (realpath(optarg, outdir) == NULL)
				fatal(EX_CONFIG,
				    "Invalid output directory: %s.", optarg);
			break;
		case 'p': /* Audit port */
			port = (int) strtol(optarg, NULL, 10);
			break;
		case 'q': /* Quiet mode */
			lflags = LOGF_NOLOG;
			break;
		case 'i': /* Inforamtion Accessing command */
		case 'r': /* Resources Administration command */
			break;
		case 'h': /* Help */
		case '?':
		default:  /* Unknown option */
			usage();
			break;
		}
	}

	if (argc - optind != 0)
		usage();

	if (hostname == NULL) {
		log_err("Missing hostname.");
		usage();
	}

	log_init(lflags, LOG_USER);

#ifndef WIN32
	if (set_signal_handler() != 0)
		fatal(EX_OSERR, "Can't set signal handler: %s",
		    strerror(errno));
#endif

	if (outdir[0] != '\0' && access(outdir, R_OK | W_OK) < 0) {
		log_warn("Unable to access '%s' for read/write. Ignoring.",
		    outdir);
		outdir[0] = '\0';
	}

	/* If authentication module was not specified, request a list of them */
	if (authname == NULL) {
		connect_to_server(hostname, port);
		auth_get_module_list();
		//XXX: close_connection();
		packet_release(packet);
		bzero(login.pass, sizeof(login.pass));
		exit(0);
	}

	/* Load libcrypto lookup table and error strings */
	init_cryptostrings();

	/* Send desired authentication module name to the server */
	connect_to_server(hostname, port);
	if (auth_set_module(authname) < 0)
		fatal(EX_OSERR, "Authentication module '%s' refused.",
		    authname);

	/* Load authentication module */
	if ( (authcon = load_auth(authname, packet)) == NULL)
		fatal(EX_OSERR, "Unable to load '%s' authentication module.",
		    authname);

	/* Authentication process */
	if (authcon->mod.proc_entry(AUTH_CLIENT, authcon, &login) < 0)
		fatal(EX_NOPERM, "%s: %s.", authname, strerror(EACCES));

	/* Execute specified IA and Resources commands */
	status = 0;
	optind = old_optind;
	while ( (opt = getopt(argc, argv, "a:dl:M:o:p:vi:r:h?")) != -1) {
		switch (opt) {
		case 'a':
		case 'd':
		case 'l':
		case 'M':
		case 'o':
		case 'p':
		case 'v':
		case 'h':
		case '?':
			break;
		case 'i': /* Information accessing command */
			status = exec_ia_cmd(optarg);
			if (status < 0)
				log_err("Invalid command.");
			break;
		case 'r': /* Resources administration command */
			status = exec_res_cmd(authcon, optarg);
			if (status < 0)
				log_err("Invalid command.");
			break;
		}
		if (status < 0)
			break;
	}

	close_connection();
	release_auth(authcon);
	release_cryptostrings();
	return (status);
}


/*
 * Usage
 */
void
usage()
{
	fprintf(stderr,
	    "Audit version %s\n"
	    "Usage: %s [options]\n"
	    "Options:\n"
	    "  -a module       Set the Authentication module.\n"
	    "  -o directory    Set the output/input directory.\n"
	    "  -M modules_path Set modules path.\n"
	    "  -p port         Connect to this port.\n"
	    "  -d              Print debugging information.\n"
	    "  -q              Quiet mode (disables -d option).\n\n"
	    "  -l [[user][:pass]@]host\n"
	    "                  Set login name, password and hostname.\n\n"
	    "  -r {adduser,rmuser}=username[,...]\n"
	    "  -r {set,rm,find}=[username:]resource_name[=value][, ...]\n"
	    "                  Resources administration commands.\n\n"
	    "  -i [module:]{list,info,get,zap,rotate,sign}=param[, ...]\n"
	    "                  Information Accessing commands.\n",
	    AUDIT_VERSION, __progname);
	exit(-1);
}

