/*  Listing 1
 * snklogin.c - front end to standard login program
 * which first authenticates the user using the
 * SecureNet encryption key.
 * Mark Dapoz	Wed Dec  2 15:41:47 MET 1992
*/

#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <ctype.h>
#include <signal.h>
#include <setjmp.h>
#include <syslog.h>
#include <pwd.h>
#include "snk.h"
#include "pathnames.h"
#include "des.h"

char snkkey[8];
int pflag=0, iflag=0, hflag=0;
char *from_host = "unknown host";
char *from_ip   = "location unknown";
char *user      = NULL;

jmp_buf env;

main(argc, argv)
int argc;
char **argv;
{

    char challenge[9], response[9], snkkey[8], reply[16];
    char id[16], in_id[9], *p, message[1024];;
    unsigned int oct1, oct2, oct3, oct4,
			oct5, oct6, oct7, oct8, cksum;
    register int ch;
    int i;
    int	secured=0;
    extern char *optarg;
    extern int optind;
    FILE *in_file;
    extern int timeout();
    char *real_ipinfo();
    char *auth_class=NULL;
    int	quit();

			/* list of hostnames that we allow through */
    char *hosts_allow[] = {	"viceroy.bsc.no",
				"traine.bsc.no",
				"overlord.bsc.no",
				NULL };

			/* list of ip addresses that we allow through */
    char *ip_addr_allow[] = {	"129.177.80.6",
				"129.177.80.64",
				"129.177.80.5",
				NULL };

    signal(SIGALRM, timeout);
/* just die if anything goes wrong */
    signal(SIGHUP, quit);
    signal(SIGINT, quit);
    signal(SIGQUIT, quit);
    signal(SIGILL, quit);
    signal(SIGABRT, quit);
    signal(SIGSEGV, quit);
    signal(SIGTERM, quit);

    				/* zero out the snkkey first */
    for (i=0; i<= 7; i++)
      snkkey[i] = NULL;

				/* send messages to syslog */
    openlog(argv[0], LOG_PID | LOG_CONS, LOG_AUTH);

    while ((ch = getopt(argc, argv, "c:h:i:p")) != EOF) {
		switch((char)ch) {
		case 'c':
			auth_class=optarg;
			break;
		case 'h':
			hflag++;
			from_host=optarg;
			break;
		case 'i':
			iflag++;
			from_ip=optarg;
			break;
		case 'p':
			pflag++;
			break;
		case '?':
		default:
			break;
		}
    }
    argc -= optind;
    argv += optind;
    if (argc)
	user=argv[0];

/* check for hosts we allow directly in */
    if (hflag)		
		secured+=allow(from_host, hosts_allow);
    if (iflag)
		secured+=allow(from_ip, ip_addr_allow);
/* restricted access line (remote) */
    if (auth_class) {	
		struct passwd *pw=getpwnam(user);

		if (pw && !strcmp(pw->pw_class, auth_class))
		    secured++;
    }

    sprintf(message, "Connection from %s (%s)\n",
		from_host, from_ip);
    fprintf(stderr, "%s", message);
    syslog(LOG_NOTICE, message);
/* let some people straight through */
    if (secured) {
		syslog(LOG_NOTICE,
		  "Direct access allowed from %s (%s) for %s",
		  from_host, from_ip, user ? user : "(unknown)");
		start_login(secured);
		syslog(LOG_ERR,"Can't start login process: %m");
		fprintf(stderr,"Problems starting %s...",
			_PATH_LOGIN);
		quit();
    }

    			/* open the key file */
	if ((in_file = fopen(_PATH_KEYFILE, "r")) == NULL) {
		perror("keyfile");
		syslog(LOG_ERR,"Can't open key file: %m");
		fprintf(stderr,"Can't open the key file...");
		quit();
    }

/* SNK userid supplied on command line */
    if (user) {
		for (p=user; isalpha(*p) | isdigit(*p); p++);
			*p='\0';
		strncpy(id,user,sizeof(id));
    } else {	/* prompt and get the SNK userid */
		for(id[0]='\0'; !strlen(id);) {
	    	fprintf(stderr,"\nSNK login: ");
	    	fflush(stderr);
	    	if(setjmp(env) == 0) {
				alarm(SNKTIMEOUT); 
				if (!fgets(id, sizeof(id), stdin))
		   		 quit();
				alarm(0);
				for (p=id; isalpha(*p) | isdigit(*p); p++);
					*p='\0';
	    	} else {
				fprintf(stderr,"Timeout...");
				quit();
	    	}
		}
    }

    /* get the key associated with the userid and
	    put the key into snkkey */
    while (fgets(message, sizeof(message), in_file) != NULL) {
		sscanf(message, "%9s %o %o %o %o %o %o %o %o %x",
		  in_id, &oct1, &oct2, &oct3, &oct4,
		  &oct5, &oct6, &oct7, &oct8, &cksum);
		if (strlen(in_id) && strncmp(in_id, "#", 1)) {
	    	if ((ismatch(in_id, id))) {
				snkkey[0] = oct1;
				snkkey[1] = oct2;
				snkkey[2] = oct3;
				snkkey[3] = oct4;
				snkkey[4] = oct5;
				snkkey[5] = oct6;
				snkkey[6] = oct7;
				snkkey[7] = oct8;
				break;
	  	  }
		}
		*in_id='\0';
    }
    fclose(in_file);

/* compute the challenge/response, give them the
	challenge, and get the response from the user */
    installkey(snkkey, pkey);
    buildsnk(pkey, challenge, response);
    fprintf(stderr,"Challenge is: %s\nEnter Response: ",
		challenge);

    if(setjmp(env) == 0) {
    	alarm(SNKTIMEOUT); 
		fgets(reply, sizeof(reply), stdin);
		alarm(0);
		for (p=reply; isalpha(*p) | isdigit(*p); p++);
			*p='\0';
    } else {
		fprintf(stderr,"Timeout...");
		quit();
    }
    for(i=0; i<=7; i++)
      if(islower(reply[i]))
	 reply[i]=toupper(reply[i]);

/* if the reply the user gives doesn't match with the
   response we compute, they're out of here! */
    if ((strncmp(response, reply, 8)) != 0) {
		syslog(LOG_NOTICE,
		  "Failed SNK login for %s from %s (%s).", id, 
		  from_host, from_ip);
		fprintf(stderr,"Incorrect response...");
		quit();
    } else {
		start_login(secured);
		syslog(LOG_ERR,"Can't start login process: %m");
		fprintf(stderr,"Problems starting %s...",
			_PATH_LOGIN);
    }
    quit();
}


timeout(sig)
int sig;
{
	signal(sig, SIG_IGN);
	signal(SIGALRM, timeout);
	longjmp(env, 1);	
}

		/* check if something is on the list */
allow(who, list)
char *who;
char *list[];
{
	char *p;

	for (p=*list; p; p=*(++list)) {
	    if (!strcmp(who, p))
			return(1);
	}
	return(0);
}

int ismatch (s1, s2)
char *s1, *s2;
{
	if (strlen(s1) != strlen(s2))
		return(0);
	return(!strncmp(s1, s1, 8));
}

quit()
{
	fprintf(stderr,"closing connection\n");
	fflush(stderr);
	exit(1);
}

		/* start a normal login process */
start_login(secure)
int secure;
{
    register char **argv;
    char **addarg();

    argv = addarg(0, "login");
    if (hflag) {
		argv = addarg(argv, "-h");
		argv = addarg(argv, from_host);
    }
    if (pflag)
		argv = addarg(argv, "-p");
    if (user) {
		if (secure) {	/* only allow one login attempt */
	   	 argv = addarg(argv, "-m");
	   	 argv = addarg(argv, "1");
		}
		argv = addarg(argv, user);
    } else
		if (user=getenv("USER"))
/* special login id */
	   	if (strcmp(SNKLOGIN, user))
				argv = addarg(argv, user);

    fprintf(stderr, "\n");
    execv(_PATH_LOGIN, argv);
}

char **addarg(argv, val)
register char **argv;
register char *val;
{
	register char **cpp;

	if (argv == NULL) {
		/*
		 * 10 entries, a leading length, and a null
		 */
		argv = (char **)malloc(sizeof(*argv) * 12);
		if (argv == NULL)
			return(NULL);
		*argv++ = (char *)10;
		*argv = (char *)0;
	}
	for (cpp = argv; *cpp; cpp++)
		;
	if (cpp == &argv[(int)argv[-1]]) {
		--argv;
		*argv = (char *)((int)(*argv) + 10);
		argv = (char **)realloc(argv, (int)(*argv) + 2);
		if (argv == NULL)
			return(NULL);
		argv++;
		cpp = &argv[(int)argv[-1] - 10];
	}
	*cpp++ = val;
	*cpp = 0;
	return(argv);
}

/* end of snklogin.c */
