
#include "captive.h"


int post_to_log( int event_code, char *account_name, char *ip, char *mac, long time_stamp ) {
	char buffer[100];
	int buffer_length;
	int fd;

	buffer_length = snprintf( buffer, 100, "%d\t%ld\t%s\t%s\t%s\n", event_code, time_stamp, account_name, ip, mac );

        fd = open( "/var/smoothwall/mods/captive/log.txt", O_WRONLY );
        if( fd < 0 ) {
                printf( "Unable to open log file.\n");
                exit(0);
        }
	if( flock( fd, LOCK_EX ) == -1 ) return 0;
        if( lseek( fd, 0, SEEK_END ) == -1 ) return 0;
        if( write( fd, buffer, buffer_length ) == -1 ) return 0;
	if( flock( fd, LOCK_UN ) == -1 ) return 0;
        if( close( fd ) == -1 ) return 0;

	return 1;
}


void iptables_rule( int action, char *ip, char *mac ) {

	char buf[5];
	char command[200];

	if( action == IPTABLES_INS ) strcpy( buf, "-I" );
	else if( action == IPTABLES_DEL ) strcpy( buf, "-D" );
	else { printf( "bad param to iptables_rule %d", action ); exit(0); }

	snprintf( command, 200,
	  "/sbin/iptables -t nat %s captive -i eth0 -s %s -m mac --mac-source %s -p ALL -j ACCEPT\n",
	  buf, ip, mac );
	if( system(command) != 0 ) { printf( "<html>Error 5<br>%s</html>\n",command ); exit(0); }

	snprintf( command, 200,
	  "/sbin/iptables -t nat %s captive -i eth0 -s %s -m mac --mac-source %s -p tcp --dport 80 -j REDIRECT --to-port 800\n",
	  buf, ip, mac );
	if( system(command) != 0 ) { printf( "<html>Error 6<br>%s</html>\n",command ); exit(0); }

/*  without MAC address stuff ...
	snprintf( command, 200,
	  "/sbin/iptables -t nat %s captive -i eth0 -s %s -p ALL -j ACCEPT\n",
	  buf, ip );
	if( system(command) != 0 ) { printf( "<html>Error 5<br>%s</html>\n",command ); exit(0); }

	snprintf( command, 200,
	  "/sbin/iptables -t nat %s captive -i eth0 -s %s -p tcp --dport 80 -j REDIRECT --to-port 800\n",
	  buf, ip );
	if( system(command) != 0 ) { printf( "<html>Error 6<br>%s</html>\n",command ); exit(0); }
*/


}




void userinfoswap( userinfo *u1, userinfo *u2 ) {
	long temp_long;
	int temp_int;
	char temp_str[100];

	strncpy( temp_str, u1->password, PASSWORD_LEN );
	strncpy( u1->password, u2->password, PASSWORD_LEN );
	strncpy( u2->password, temp_str, PASSWORD_LEN );

	temp_long = u1->login_dur_in_seconds;
	u1->login_dur_in_seconds = u2->login_dur_in_seconds;
	u2->login_dur_in_seconds = temp_long;

	temp_int = u1->enabled;
	u1->enabled = u2->enabled;
	u2->enabled = temp_int;

	temp_int = u1->logged_in_now;
	u1->logged_in_now = u2->logged_in_now;
	u2->logged_in_now = temp_int;

	strncpy( temp_str, u1->ip, IP_LEN );
	strncpy( u1->ip, u2->ip, IP_LEN );
	strncpy( u2->ip, temp_str, IP_LEN );

	strncpy( temp_str, u1->mac, MAC_LEN);
	strncpy( u1->mac, u2->mac, MAC_LEN );
	strncpy( u2->mac, temp_str, MAC_LEN );

	temp_long = u1->login_expires_at;
	u1->login_expires_at = u2->login_expires_at;
	u2->login_expires_at = temp_long;

}


/* Just echoes the specified file to stdout.  Presumably a text file that
   has been customized to display a particular message.  */
int printfile( char *filename ) {

	int fd;
	long filelength;
	char *data;

	fd = open( filename, O_RDONLY );
	if( fd < 0 ) {
		printf( "No such file: %s\n", filename );
		return 0;
	}
	filelength = lseek( fd, 0, SEEK_END );
	lseek( fd, 0, SEEK_SET );
	data = (char *)malloc( (filelength+1) * sizeof(char) );
	read( fd, data, filelength );
	data[filelength] = '\0';
	close( fd );
	printf( "%s", data );
	free( data );

	return 1;
}

/* Takes a time in minutes, and creates a string with a human-friendly
   printout of that time.
*/
char *timeleftstring( long secondsleft ) {
	static char buffer[50];
	long minutesleft;

	if( secondsleft < 0 ) {
		snprintf( buffer, 50, "(already expired)" );
		return buffer;
	}
	minutesleft = secondsleft / 60;
	if( minutesleft == 0 ) {
		snprintf( buffer, 50, "<1 minute" );
	} else if( minutesleft == 1 ) {
		snprintf( buffer, 50, "1 minute" );
	} else if( minutesleft < 61 ) {
		snprintf( buffer, 50, "%ld minutes", minutesleft );
	} else if( (minutesleft%60) == 0 ) {
		snprintf( buffer, 50, "%ld hours", minutesleft/60 );
	} else {
		snprintf( buffer, 50, "%ld hours, %ld minutes", minutesleft/60, minutesleft%60 );
	}
	return buffer;
}


/* Checks a provided username & password against authorized user database.
   Returns
	   2 : authorized user (user/pass match) but account is disabled
	   1 : authorized user (user/pass match AND account enabled)
	   0 : unauthorized user (user/pass do not match)
	  -1 : db error
   The user's alloted login time is stored in the address referenced by the
   secondsleft parameter.
*/

int valid_up( char *username, char *password, long *secondsleft ) {

	GDBM_FILE dbf;
	datum key;
	datum content;

	key.dptr = username;
	key.dsize = strlen(username);

	dbf = gdbm_open( "/var/smoothwall/mods/captive/captivedb", 512, GDBM_READER, S_IRWXU, NULL );
	if( dbf == NULL ) return -1;

	content = gdbm_fetch( dbf, key );
	gdbm_close( dbf );

	if( content.dptr == NULL ) return 0;

	*secondsleft = ((userinfo *)content.dptr)->login_dur_in_seconds;

	if( strncmp(((userinfo *)content.dptr)->password,password,PASSWORD_LEN) == 0 ) {
		if( ((userinfo *)content.dptr)->enabled != 0 ) {
			free ( content.dptr );
			return 1;
		} else {
			free ( content.dptr );
			return 2;
		}
	} else {
		free ( content.dptr );
		return 0;
	}

}

/* Takes the POST data from the form and pulls out the username and
   password, if they are present.
*/
int parse_post_data( char *buffer, char *username, char *password ) {
	char *ptr;
	int i;

	ptr = strstr(buffer,"username=");
	if( ptr == NULL ) return 0;
	ptr += 9;
	i = 0;
	username[0] = '\0';
	while( ((*ptr == '.') || isalnum(*ptr)) && (strlen(username)<(USERNAME_LEN-1)) ) {
		username[i] = *ptr;
		i++; ptr++;
	} 
	if( *ptr != '&' ) return 0;
	username[i] = '\0';

	ptr = strstr(buffer,"password=");
	if( ptr == NULL ) return 0;
	ptr += 9;
	i = 0;
	password[0] = '\0';
	while( ((*ptr == '.') || isalnum(*ptr)) && (strlen(password)<(PASSWORD_LEN-1)) ) {
		password[i] = *ptr;
		i++; ptr++;
	} 
	if( *ptr != '&' ) return 0;
	password[i] = '\0';

	return 1;

}

char *twodig( int n, char *buffer ) {
	if( n < 10 ) snprintf( buffer, 3, "0%d", n );
	else snprintf( buffer, 3, "%d", n );
	return buffer;
}





/* This tedious function turns the current time into a string
   in a format that will fit in a http header.
*/
char *timestr( long data_time ) {
	struct tm *tmptr;
	char month[4];
	char b1[3], b2[3];
	static char str[200];

        tmptr = gmtime( (time_t *)&data_time );

	switch( tmptr->tm_mon ) {
		case 0: strcpy(month,"Jan"); break;
		case 1: strcpy(month,"Feb"); break;
		case 2: strcpy(month,"Mar"); break;
		case 3: strcpy(month,"Apr"); break;
		case 4: strcpy(month,"May"); break;
		case 5: strcpy(month,"Jun"); break;
		case 6: strcpy(month,"Jul"); break;
		case 7: strcpy(month,"Aug"); break;
		case 8: strcpy(month,"Sep"); break;
		case 9: strcpy(month,"Oct"); break;
		case 10: strcpy(month,"Nov"); break;
		case 11: strcpy(month,"Dec");
	};

	snprintf( str, 50, "%d %s %d %s:%s", tmptr->tm_mday, month, tmptr->tm_year+1900, twodig(tmptr->tm_hour, b1), twodig(tmptr->tm_min, b2) );  
	return str;

}



void print_event( int data_event_code, long data_time, char *data_username, char *data_ip, char *data_mac ) {

	printf( "<td align=\"left\"><font face=\"arial, sans-serif\" size=\"-1\">%s</font></td><td width=\"10\"></td>", timestr(data_time) );
	switch( data_event_code ) {
		case EVENT_LOGIN_SUCCESS:
			printf( "<td align=\"left\"><font face=\"arial, sans-serif\" size=\"-1\">Successful Login</font></td><td width=\"10\"></td>" );
			break;
		case EVENT_LOGIN_FAIL_BAD_USERPASS:
			printf( "<td align=\"left\"><font face=\"arial, sans-serif\" size=\"-1\" color=\"#aa0000\">Failed Login - Bad Password</font></td><td width=\"10\"></td>" );
			break;
		case EVENT_LOGIN_FAIL_ACCOUNT_DISABLED:
			printf( "<td align=\"left\"><font face=\"arial, sans-serif\" size=\"-1\" color=\"#aa0000\">Failed Login - Account Disabled</font></td><td width=\"10\"></td>" );
			break;
		case EVENT_LOGIN_FAIL_NO_IP:
			printf( "<td align=\"left\"><font face=\"arial, sans-serif\" size=\"-1\" color=\"#aa0000\">Failed Login - No IP</font></td><td width=\"10\"></td>" );
			break;
		case EVENT_LOGIN_FAIL_NO_MAC_IN_LEASE_FILE:
			printf( "<td align=\"left\"><font face=\"arial, sans-serif\" size=\"-1\" color=\"#aa0000\">Failed Login - No MAC In DHCP Lease Record</font></td><td width=\"10\"></td>" );
			break;
		case EVENT_LOGOFF_TIMED:
			printf( "<td align=\"left\"><font face=\"arial, sans-serif\" size=\"-1\">Logoff (Session Expired)</font></td><td width=\"10\"></td>" );
			break;
		case EVENT_LOGOFF_FORCED:
			printf( "<td align=\"left\"><font face=\"arial, sans-serif\" size=\"-1\">Logoff (Forced)</font></td><td width=\"10\"></td>" );
			break;
		case EVENT_LOGOFF_ELECTIVE:
			printf( "<td align=\"left\"><font face=\"arial, sans-serif\" size=\"-1\">Logoff (Elective)</font></td><td width=\"10\"></td>" );
			break;
		case EVENT_DATABASE_ERROR:
			printf( "<td align=\"left\"><font face=\"arial, sans-serif\" size=\"-1\" color=\"#0000aa\">Database Error</font></td><td width=\"10\"></td>" );
			break;
		case EVENT_SYSTEM_FLUSH:
			printf( "<td align=\"left\"><font face=\"arial, sans-serif\" size=\"-1\" color=\"#0000aa\">System Flush</font></td><td width=\"10\"></td>" );
			break;
		case EVENT_USER_ADD:
			printf( "<td align=\"left\"><font face=\"arial, sans-serif\" size=\"-1\">Account Created</font></td><td width=\"10\"></td>" );
			break;
		case EVENT_USER_DEL:
			printf( "<td align=\"left\"><font face=\"arial, sans-serif\" size=\"-1\">Account Deleted</font></td><td width=\"10\"></td>" );
			break;
		case EVENT_USER_MODIFY:
			printf( "<td align=\"left\"><font face=\"arial, sans-serif\" size=\"-1\">Account Modified</font></td><td width=\"10\"></td>" );
			break;

		default:
			printf( "<td align=\"left\"><font face=\"arial, sans-serif\" size=\"-1\" color=\"#0000aa\">[No Matching Case]</font></td><td width=\"10\"></td>" );


	};

	if( strncmp(data_ip,"N/A",3) == 0 )
		printf( "<td></td><td width=\"10\"></td>" );
	else if( data_event_code == EVENT_LOGIN_SUCCESS )
		printf( "<td align=\"left\"><font face=\"arial, sans-serif\" size=\"-1\">%s</font></td><td width=\"10\"></td>", data_ip );
	else
		printf( "<td align=\"left\"><font face=\"arial, sans-serif\" size=\"-1\">%s</font></td><td width=\"10\"></td>", data_ip );

	if( strncmp(data_mac,"N/A",3) == 0 )
		printf( "<td></td></tr>" );
	else
		printf( "<td align=\"left\"><font face=\"arial, sans-serif\" size=\"-1\">%s</font></td></tr>", data_mac );


}


void print_event_history( char *username ) {
	FILE *f;
	char buffer[1000];
	int data_event_code;
	long data_time;
	char data_username[USERNAME_LEN], data_ip[IP_LEN], data_mac[MAC_LEN];


	printf( "<table border=\"0\" cellspacing=\"0\" cellpadding=\"0\" width=\"600\"><tr><td align=\"left\"><font face=\"arial, sans-serif\" size=\"-1\">\n" );


	f = fopen( "/var/smoothwall/mods/captive/log.txt", "r" );
	printf( "<table border=\"0\" cellspacing=\"0\" cellpadding=\"0\" width=\"600\">\n" );

	printf( "<tr><td align=\"left\" valign=\"bottom\"><font face=\"arial, sans-serif\" size=\"-1\"><u>Event Time</u><br></font></td><td width=\"10\"></td>" );
	printf( "<td align=\"left\" valign=\"bottom\"><font face=\"arial, sans-serif\" size=\"-1\"><u>Event Description</u><br></font></font></td><td width=\"10\"></td>" );
	printf( "<td align=\"left\" valign=\"bottom\"><font face=\"arial, sans-serif\" size=\"-1\"><u>IP Address</u><br></font></font></td><td width=\"10\"></td>" );
	printf( "<td align=\"left\" valign=\"bottom\"><font face=\"arial, sans-serif\" size=\"-1\"><u>MAC Address</u><br></font></font></td></tr>" );

	fgets( buffer, 1000, f );
	while( !feof(f) ) {

		sscanf( buffer, "%d\t%ld\t%s\t%s\t%s\n", &data_event_code, &data_time, data_username, data_ip, data_mac );

		if( strcmp(data_username,username) == 0 )
			print_event( data_event_code, data_time, data_username, data_ip, data_mac );
		fgets( buffer, 1000, f );
	}

	printf( "</table>\n" );
	fclose( f );


	printf( "</font></td></tr></table>\n" );

}


void print_nmap_history( char *username ) {

	char filename[200];
	FILE *f;
	char buffer[200];

	snprintf( filename, 200, "/var/smoothwall/mods/captive/nmap/%s", username );
	f = fopen( filename, "r" );
	if( !f ) {
		printf( "<li>No record of nmap scan (it may be in progress or pending).<br><br>\n" );
		return;
	}

	fgets( buffer, 200, f );  /* skip initial newline */
	fgets( buffer, 200, f );
	while( !feof(f) ) {
		printf( "<li>%s", buffer );
		fgets( buffer, 200, f );
	}
	fclose( f );
	printf( "<br>\n" );

}




/* Adds IP and username to the list of clients to scan.  Every so often (as determined
   the cron job) a script will run to scan the client.  This data will then be available
   in the web interface.
*/ 
int nmap_portscan( char *username, char *ip ) {

        char buffer[100];
        int buffer_length;
        int fd;

	buffer_length = snprintf( buffer, 100, "./nmap.sh %s %s\n", ip, username );
        fd = open( "/var/smoothwall/mods/captive/nmap/todo.sh", O_WRONLY );
        if( fd < 0 ) {
                printf( "Unable to open nmap todo file.\n");
                exit(0);
        }
        if( flock( fd, LOCK_EX ) == -1 ) return 0;
        if( lseek( fd, 0, SEEK_END ) == -1 ) return 0;
        if( write( fd, buffer, buffer_length ) == -1 ) return 0;
        if( flock( fd, LOCK_UN ) == -1 ) return 0;
        if( close( fd ) == -1 ) return 0;

	return 1;
}




/* Opens the SmoothWall's dhcpd.leases file and gets the MAC address associated
   with the provided IP.  If there are multiple entries for the IP address, get
   the MAC that's closest to the end of the file because this is the most recent
   DHCP lease.   Returns 1 on success.
*/

int get_mac_from_dhcp_lease( char *ip, char *mac ) {
	int i, found_mac;
	FILE *f;
	char buffer[500];
	char *ptr;
	char ip_addr[IP_LEN];

	mac[0] = '\0';
	f = fopen( "/var/state/dhcp/dhcpd.leases", "r" );
	if( !f ) return -1;

	/* Go through the entire file, looking for IP matches.  When found, get
	   the MAC associated with it.  If the same IP is found multiple times,
	   just overwrite the last MAC found - the last match in the file is the
	   most recent MAC assigned that IP address.  */

	while( !feof(f) ) {
		fgets( buffer, 500, f );
		if( strncmp(buffer,"lease ",6) == 0 ) {

			/* New lease entry ... */
			ptr = buffer + 6;
			i = 0;
			while( (*ptr != ' ') && (*ptr != '\n') && (*ptr != '\0') && (i < IP_LEN) ) {
				ip_addr[i] = *ptr;
				i++; ptr++;
			}
			if( *ptr != ' ' ) return -2;
			ip_addr[i] = '\0';


			/* Is this the IP address we're looking for? */
			if( strncmp(ip,ip_addr,16) == 0 ) {
				found_mac = 0;
				/* It is - now find the MAC address associated with it ... */
				while( (buffer[0] != '}') && !feof(f) ) {
					fgets( buffer, 500, f );
					if( strncmp(buffer,"  hardware ethernet ",20) == 0 ) {
						found_mac = 1;
						ptr = buffer + 20;
						i = 0;
						while( (*ptr != ';') && (*ptr != '\n') && (*ptr != '\0') && (i <19) ) {
							mac[i] = *ptr;
							i++; ptr++;
						}
						if( *ptr != ';' ) return -3;
						mac[i] = '\0';
						break;
					}
				}
				if( !found_mac ) return -4;
			}

		}

	} 

	if( mac[0] == '\0' ) {
		 return -5;
	}

	return 1;
}


