/*
 * Copyright 2002,2003 Christopher SEKIYA <wileyc@rezrov.net>
 */

#include "tacshell.h"

#include <time.h>
#include <netinet/in.h>
#include <netdb.h>
#include <sys/socket.h>
#include <arpa/inet.h>

/* local prototypes */
u_int32_t       getservername(char *serv);
int
                authenticate(char *user);

/* globals */
char	       *user = NULL;
char           *tac_secret = NULL;
int             tac_encryption = 1;
char           *challenge = NULL;
u_int32_t       session_id;
u_int8_t        sequence_number = 1;

char	       *servers[MAX_SERVERS];
char           *server_secret = NULL;
char           *user_shell = NULL;

#if defined(HAVE_GETPASSPHRASE)
# define get_pass(x)	getpassphrase(x)
#elif defined(HAVE_GETPASS)
# define get_pass(x)	getpass(x)
#else
# error Neither getpassphrase() nor getpass() exist on this system.
#endif

int
authenticate(char *user)
{
	u_int32_t       seed;
	struct timeval  t;
	int             tac_fd = -1;
	int             msg;
	char           *pass;
	char           *tty;
	int		index;

	/* create session id for this transaction */
	gettimeofday(&t, NULL);
	seed = gethostid() ^ t.tv_sec ^ t.tv_usec ^ getpid();
	srand48(seed);
	session_id = mrand48();

	sequence_number = 1;

	tty = ttyname(0);
	if (strncmp(tty, "/dev/", 5) == 0)
		tty += 5;

	for (index = 0; index < MAX_SERVERS; index++)
	  if (servers[index] != NULL)
	   if ( (tac_fd = tac_connect(getservername(servers[index]))) >= 0)
	    break;

	if (tac_fd < 0) {
		printf("Error connecting to TACACS+ server\n");
		return -1;
	}
	/* start authentication */

	if (tac_authen_send(tac_fd, user, tty) < 0) {
		fprintf(stderr, "Error sending query to TACACS+ server\n");
		return -1;
	}

	for (;;) {
		msg = tac_authen_read(tac_fd);

		switch (msg) {
		case TAC_PLUS_AUTHEN_STATUS_FAIL:
			if (challenge != NULL) {
				fprintf(stderr, "%s", challenge);
				free(challenge);
				challenge = NULL;
			} else
				fprintf(stderr, "Authentication FAILED\n");
			close(tac_fd);
			return -1;
		case TAC_PLUS_AUTHEN_STATUS_PASS:
			if (challenge != NULL) {
				fprintf(stderr, "%s\n", challenge);
				free(challenge);
				challenge = NULL;
			} else
				fprintf(stderr, "Authentication SUCCEEDED\n");
			close(tac_fd);
			return 0;
		case TAC_PLUS_AUTHEN_STATUS_GETPASS:
		case TAC_PLUS_AUTHEN_STATUS_GETDATA:
		case TAC_PLUS_AUTHEN_STATUS_GETUSER:
			if (challenge != NULL)
			{
				pass = get_pass(challenge);
				free(challenge);
			}
			else
				pass = get_pass("Enter password: ");
			challenge = NULL;
			tac_authen_continue_send(tac_fd, pass);
			break;
		case TAC_PLUS_AUTHEN_STATUS_ERROR:
			fprintf(stderr, "tacshell: server returned TAC_PLUS_AUTHEN_STATUS_ERROR, exiting\n");
			return -1;
		case TAC_PLUS_AUTHEN_STATUS_FOLLOW:
			fprintf(stderr, "tacshell: server returned TAC_PLUS_AUTHEN_STATUS_FOLLOW.  Contact another server.\n");
			return -1;
		case TAC_PLUS_AUTHEN_STATUS_RESTART:
			fprintf(stderr, "tacshell: server returned TAC_PLUS_AUTHEN_STATUS_RESTART.  Not yet implemented.\n");
			return -1;
		default:
			fprintf(stderr, "tacshell: unhandled status %x\n", msg);
			return -1;

		}
	}

	/* never actually reached, but just in case ... */
	return -1;

}

u_int32_t
getservername(char *serv)
{
	struct in_addr  addr;
	struct hostent *h;

	if (inet_aton(serv, &addr) == 0) {
		if ((h = gethostbyname(serv)) == NULL) {
			herror("gethostbyname");
		} else {
			bcopy(h->h_addr, (char *) &addr, sizeof(struct in_addr));
			return (addr.s_addr);
		}
	} else
		return (addr.s_addr);

	return 0;
}

void
cleanup(void)
{
	int index;

	if (tac_secret != NULL)
		free(tac_secret);
	if (challenge != NULL)
		free(challenge);
	if (user != NULL)
		free(user);
	for (index = 0; index < MAX_SERVERS; index++)
		if (servers[index] != NULL)
			free(servers[index]);
	if (server_secret != NULL)
		free(server_secret);
	if (user_shell != NULL)
		free(user_shell);

	return;
}


syntax highlighted by Code2HTML, v. 0.9.1