Secure Programming
For this assignment we had to ensure that our coding was secure. i.e. not vulnerable to buffer overflows, TOCTU etc. The program had to be executable by anyone as a certain user. i.e. if I installed it another user would execute it and it would run with my privileges.
The program would read a certain file in my secure folder and print the contents to the screen. The program would ask the user for a password before printing though!
PA2: Secure Programming description of assignment
pa2.c my solution
/*
* Will Mernagh Mar 2007
* PA3
* Mar 2007
*/
#define _GNU_SOURCE
#include <stdlib.h>
#include <stdio.h>
#include <netinet/ip.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <netinet/udp.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
/* based on the background info and attack files these are the tresholds
* These numbers could be made better with more data */
#define ALLOWED_RST 100
#define MAX_ALLOWED_IP 70
#define MAX_ALLOWED_PORTS 75
#define RESET_COUNT 500
/* linked list of ports this is needed to count the number of different ports */
typedef struct ports port_list;
struct port_list{
int number;
int count;
struct port_list *next;
};
/* a linked list of hosts - for counting different hosts by IP */
typedef struct host_info host;
struct host{
char ip_addr[20];
struct port_list *port;
int num_of_ports;
int count;
struct host *next;
};
/* an event counter. */
typedef struct tcp_flag_event_counter tcp_event_counter;
struct tcp_event_counter{
int num_of_events;
struct host *source;
struct host *dest;
int num_of_source;
int num_of_dest;
int num_dest_ports;
int num_source_ports;
int num_of_packets_less_events; /* since first event num_of_packets_less_events*/
};
/* function prototypes */
struct tcp_event_counter * check_events(struct tcphdr *tcpheader,
struct ip *ipheader, struct tcp_event_counter *);
struct tcp_event_counter * parse_input(char* path, struct tcp_event_counter *);
int main(int argc, char** argv);
void decode_tcp_flags(char *buffer, int buflen, unsigned short urg,
unsigned short ack, unsigned short psh, unsigned short rst,
unsigned short syn, unsigned short fin);
void print_usage();
struct tcp_event_counter * increment_Event(struct tcphdr *, struct ip *,
struct tcp_event_counter *);
void free_event_nodes(struct tcp_event_counter *ig_event_counter);
void print_type_of_attack(struct tcp_event_counter *ig_event_counter);
/* function implementations */
/* check_events()
*
* This function takes in a tcphdr struct and decides if an searched for event
* occured. It just looks for RST but can be easily modified for others
*/
struct tcp_event_counter *check_events(struct tcphdr *tcpheader,
struct ip *ipheader, struct tcp_event_counter *ig_event_counter)
{
char buffer[25];
char *rst_search, *syn_search, *syn_ack_search, *buf, *result;
char synstr[] = "SYN", rststr[] = "RST", synackstr[] = "SYN ACK";
decode_tcp_flags(buffer, 25, tcpheader->urg, tcpheader->ack,
tcpheader->psh, tcpheader->rst, tcpheader->syn, tcpheader->fin);
rst_search = rststr;
syn_search = synstr;
syn_ack_search = synackstr;
buf = buffer;
/* search for different types of events
* Type 1: Large num of RST + Small set of IPs + Large set of ports
* Type 2: Large num of RST + Large num of Ips + Small num of ports
* Type 3: Large number of SYN ACK with no corresponding ACK
* This type will not be checked for here because we will determin
* the info from RST
*/
result = strstr(buf,rst_search);
/* If RST increment event counter */
if(result != NULL){
ig_event_counter = increment_Event(tcpheader,ipheader, ig_event_counter);
}
return ig_event_counter;
}
/* decode_tcp_flags()
*
* This function takes in all the flags from a tcp struct and prints
* which flags are set as a string. It outputs the string to the
* buffer parameter. This buffer needs to be at least 25 chars in length.
*/
void decode_tcp_flags(char *buffer,
int buflen,
unsigned short urg,
unsigned short ack,
unsigned short psh,
unsigned short rst,
unsigned short syn,
unsigned short fin)
{
int index = 0;
bzero(buffer, buflen); // clear buffer
if (syn) {
sprintf(index + buffer, "SYN ");
index += 4;
}
if (urg) {
sprintf(buffer, "URG ");
index += 4;
}
if (ack) {
sprintf(index + buffer, "ACK ");
index += 4;
}
if (psh) {
sprintf(index + buffer, "PSH ");
index += 4;
}
if (rst) {
sprintf(index + buffer, "RST ");
index += 4;
}
if (fin) {
sprintf(index + buffer, "FIN ");
index += 4;
}
}
/* parse_input()
*
* Given the trace file name and path, opens the file and reads in the
* data line by line. Each line corresponds to one packet, so the hex
* characters in each line are converted to an array of binary values.
* The ip and tcphdr structs are applied to the data array,
*/
struct tcp_event_counter *parse_input(char* path,
struct tcp_event_counter *ig_event_counter)
{
FILE *tracefile = NULL;
char *line, *curr;
size_t nbytes = 400;
ssize_t bytes_read;
int packetnum, input2;
unsigned short shortarr[66000]; // max packet length is 2^16
int count;
struct ip *ipheader;
struct tcphdr *tcpheader;
/* open file for reading */
tracefile = fopen(path, "r");
if (tracefile == NULL)
{
printf("Error: input file %s not found\n", path);
exit(1);
}
/* allocate buffer space */
line = (char *)malloc(nbytes + 1);
/* input loop */
while (1)
{
bytes_read = getline(&line, &nbytes, tracefile);
if (bytes_read > 0)
{
count = 0;
/* only parse valid data */
char ok_chars[] = "0123456789 abcdef ABCDEF";
/* minus 2 for end of file and end of line */
if ((strspn(line, ok_chars)) < strlen(line)-2){
printf("Error: Invalid file. ");
printf("The file should only contain hex and white spaces\n");
exit(1);
}
/* get rid of line number */
curr = strtok(line, " ");
sscanf(curr, "%d", &packetnum);
while ((curr = strtok(NULL, " ")) != NULL) {
/* translate line into array of 16-bit values */
sscanf(curr, "%x", &input2);
/* put input into network byte order. we do this so we can
* use the helper functions defined in the netinet
* package. */
shortarr[count] = htons((unsigned short)input2);
count++;
}
/* process short array */
ipheader = (struct ip *)shortarr;
tcpheader = (struct tcphdr *)(shortarr + (ipheader->ip_hl * 2));
/* We only want TCP packets */
if (ipheader->ip_p == IPPROTO_TCP) {
/* check for RST events */
ig_event_counter = check_events(tcpheader, ipheader,
ig_event_counter);
/* after first RST count all packets */
if(ig_event_counter != NULL){
ig_event_counter->num_of_packets_less_events++;
}
if(ig_event_counter != NULL){
/* if RST went over limit */
if((ig_event_counter->num_of_events) > ALLOWED_RST){
/* print type of attack (if one) */
print_type_of_attack(ig_event_counter);
/* clean up */
fclose(tracefile);
free(line);
return ig_event_counter;
/* if enough good events then clear RST counter */
}else if(ig_event_counter->num_of_packets_less_events > RESET_COUNT){
free_event_nodes(ig_event_counter);
ig_event_counter = NULL;
}
}
}
} else {
break;
}
}
/* clean up */
fclose(tracefile);
free(line);
/* if no detected attack free counter */
free_event_nodes(ig_event_counter);
return ig_event_counter;
}
/* print_usage()
*
* Prints usage information for the program to stdout
*/
void print_usage()
{
printf("Usage: attackalarm FILE\n\n");
}
/* main()
*
* Checks arguments, sets options, and does the work.
*/
int main(int argc, char** argv)
{
char *infilename = NULL;
int i;
struct tcp_event_counter *ig_event_counter;
ig_event_counter = NULL;
/* check for at least 1 command-line option */
if (argc < 2)
{
printf("Error: required parameter missing\n");
print_usage();
exit(1);
}
// assume this is the input filename
if (!infilename) {
infilename = argv[1];
} else {
printf("Error: unexpected argument: %s\n", argv[i]);
print_usage();
exit(1);
}
if (!infilename)
{
printf("Error: required parameter missing\n");
print_usage();
exit(1);
}
/* parse input and count events */
ig_event_counter = parse_input(infilename, ig_event_counter);
if(ig_event_counter == NULL){
printf("No scan detected\n");
}else{
/* free the event counter */
free_event_nodes(ig_event_counter);
}
return 0;
}
/** If we get a EVENT this fun gets called and increments the relavant info
*
*
*/
struct tcp_event_counter *increment_Event(struct tcphdr *tcpheader,
struct ip *ipheader,
struct tcp_event_counter *event_counter){
struct host *stored_host;
struct port_list *port;
int type; /* 0 = source, 1 = dest */
/* initialize rstcounter */
if(event_counter == NULL){
event_counter = (struct tcp_event_counter *)malloc
(sizeof(struct tcp_event_counter));
if(event_counter == NULL){
printf("Error: malloc\n");
exit(1);
}
event_counter->num_of_source = 1;
event_counter->num_of_dest = 1;
event_counter->num_of_events = 1;
event_counter->source = (struct host *)malloc(sizeof(struct host));
if(event_counter->source == NULL){
printf("Error: malloc\n");
exit(1);
}
event_counter->source->num_of_ports = 1;
event_counter->source->count = 1;
event_counter->source->port = (struct port_list *)malloc
(sizeof(struct port_list));
if(event_counter->source->port == NULL){
printf("Error: malloc\n");
exit(1);
}
strncpy(event_counter->source->ip_addr, inet_ntoa(ipheader->ip_src),
strlen(inet_ntoa(ipheader->ip_src)));
event_counter->source->port->number = (int)ntohs(tcpheader->source);
event_counter->source->port->count = 1;
event_counter->source->port->next = NULL;
event_counter->num_source_ports = 1;
event_counter->dest = (struct host *)malloc(sizeof(struct host));
if(event_counter->dest == NULL){
printf("Error: malloc\n");
exit(1);
}
event_counter->dest->num_of_ports = 1;
event_counter->dest->count = 1;
event_counter->dest->port = (struct port_list *)malloc(sizeof
(struct port_list));
if(event_counter->dest->port == NULL){
printf("Error: malloc\n");
exit(1);
}
strncpy(event_counter->dest->ip_addr, inet_ntoa(ipheader->ip_dst),
strlen(inet_ntoa(ipheader->ip_dst)));
event_counter->dest->port->number = (int)ntohs(tcpheader->dest);
event_counter->dest->port->next = NULL;
event_counter->dest->port->count = 1;
event_counter->num_dest_ports = 1;
event_counter->num_of_packets_less_events = 0;
}else{
event_counter->num_of_events++;
char *current_ip;
int current_port;
for(type = 0; type < 2; type++){
if(type == 0){
stored_host = event_counter->source;
current_ip = inet_ntoa(ipheader->ip_src);
current_port = (int)ntohs(tcpheader->source);
}else{
stored_host = event_counter->dest;
current_ip = inet_ntoa(ipheader->ip_dst);
current_port = (int)ntohs(tcpheader->dest);
}
/* check if we have source and increment it if we do */
//printf("Is %s == \n", current_ip);
while(stored_host != NULL){
/* check against current */
if(strncmp(current_ip, stored_host->ip_addr, strlen(
stored_host->ip_addr)) == 0){
stored_host->count++;
port = stored_host->port;
/* if we have source check if we have port */
while(port != NULL){
if(port->number == current_port){
port->count++;
break;
}else if(port->next == NULL){
/* keep count of ports */
if(type == 0){
event_counter->num_source_ports++;
}else{
event_counter->num_dest_ports++;
}
port->next = (struct port_list *)malloc
(sizeof(struct port_list));
port = port->next;
port->next = NULL; /* don't know why I need this */
if(port == NULL){
printf("Error: malloc\n");
exit(1);
}
port->number = current_port;
port->count = 1;
stored_host->num_of_ports++;
break;
}else{
port = port->next;
}
}
stored_host = stored_host->next;
break;
/* if new source then store it */
}else if(stored_host->next == NULL) {
if(type == 0){
event_counter->num_of_source++;
}else{
event_counter->num_of_dest++;
}
stored_host->next = (struct host *)malloc(sizeof(
struct host));
stored_host = stored_host->next;
if(stored_host == NULL){
printf("Error: malloc\n");
exit(1);
}
stored_host->num_of_ports = 1;
stored_host->count = 1;
stored_host->port = (struct port_list *)malloc(sizeof(
struct port_list));
if(stored_host->port == NULL){
printf("Error: malloc\n");
exit(1);
}
strncpy(stored_host->ip_addr, current_ip, strlen(
current_ip));
stored_host->port->number = current_port;
stored_host->port->count = 1;
stored_host->port->next = NULL;
stored_host->next = NULL; /* Don't know why */
break;
}else{
stored_host = stored_host->next;
}
}
} /* for loop */
}
return event_counter;
}
/* This needs to recurse through the linked lists and free the memory */
void free_event_nodes(struct tcp_event_counter *ig_event_counter){
struct host *source, *source_free, *dest, *dest_free;
struct port_list *port, *port_free;
if(ig_event_counter != NULL){
dest = ig_event_counter->dest;
while(dest != NULL){
port = dest->port;
while(port != NULL){
port_free = port;
port = port->next;
free(port_free);
}
dest_free = dest;
dest = dest->next;
free(dest_free);
}
source = ig_event_counter->source;
while(source != NULL){
port = source->port;
while(port != NULL){
port_free = port;
port = port->next;
free(port_free);
}
source_free = source;
source = source->next;
free(source_free);
}
free(ig_event_counter);
}
}
void print_type_of_attack(struct tcp_event_counter *ig_event_counter){
int count = 0;
struct host *source, *source_itr, *dest, *dest_itr;
source_itr = ig_event_counter->source;
source = source_itr;
while(source_itr != NULL){
/* find largest source creating event */
if(source->count < source_itr->count){
source = source_itr;
}
source_itr = source_itr->next;
count++;
}
dest_itr = ig_event_counter->dest;
dest = dest_itr;
while(dest_itr != NULL){
/* find largest source creating event */
if(dest->count < dest_itr->count){
dest = dest_itr;
}
dest_itr = dest_itr->next;
count++;
}
/* check for port scan */
if(source->num_of_ports > MAX_ALLOWED_PORTS && ig_event_counter->num_of_dest < MAX_ALLOWED_IP){
printf("Port scan detected!\n");
printf("Source host: %s\n", dest->ip_addr);
printf("Destination host: %s\n", source->ip_addr);
/* check for SYN flood to closed port */
}else if(ig_event_counter->num_of_dest > MAX_ALLOWED_IP){
printf("SYN flood detected!\n");
printf("Source host: spoofed\n");
printf("Destination host: %s\n", source->ip_addr);
/* check for SYN Flood to open port */
}else if(ig_event_counter->num_of_source > MAX_ALLOWED_IP){
printf("SYN flood detected!\n");
printf("Source host: spoofed\n");
printf("Destination host: %s\n", dest->ip_addr);
}
}