#!/bin/bash

# $Id: rad_eap_test,v 1.19 2009-11-02 15:48:58 polish Exp $                       
                                                                                
# rad_eapol_test nagios compatible wraper around eapol_test                     
# Copyright (c) 2005-2009, Pavel Poláček <pavel.polacek@ujep.cz>                   
#                                                                               
# This program is free software; you can redistribute it and/or modify          
# it under the terms of the GNU General Public License version 2 as             
# published by the Free Software Foundation.                                    
#                                                                              
# See README and COPYING for more details.
   
# umask
umask 0077

# temporary file of eapol_test configuration 
if [ -z $TMPDIR ]; then
  MYTMPDIR=`mktemp -d /tmp/rad_eap_test.XXXXXX`
else
  MYTMPDIR=`mktemp -d $TMPDIR/rad_eap_test.XXXXXX`
fi

CONF=$MYTMPDIR/tmp-$$
OUT=$CONF.out
OUT2=$CONF.out2

#echo $CONF $OUT

# path to eapol test
EAPOL_PROG=/usr/sbin/eapol_test

# default verbosity
VERBOSE=0

# debug script
DEBUG=1

#default timeout
TIMEOUT=5

#default mac address
MAC="70:6f:6c:69:73:68"

# default connection info
CONN_INFO="rad_eap_test + eapol_test"

# return codes
RET_SUCC=3
RET_EAP_FAILED=4
RET_RADIUS_NOT_AVAIL=5

#cleanup?
CLEANUP=1

# exist eapol_test program ?
if [ ! -x "$EAPOL_PROG" ]; then # exact path?
    if [ ! -x `which $EAPOL_PROG` ]; then # is program on path?
        if [ -x "./$EAPOL_PROG" ]; then # is program in actual directory?
           EAPOL_PROG=/usr/sbin/eapol_test
        else
	   echo "eapol_test program \"$EAPOL_PROG\" not found"
	   exit 3;
        fi
    fi
fi

#TEMP=`getopt -o H:P:S:u:p:t:m:s:e:t:M:i:d:j:k:a:A:l:vc -- "$@"`
TEMP=`getopt -o H:P:S:u:p:t:m:s:e:t:M:i:d:j:k:a:A:l:2:vcN -- "$@"`

if [ -z $1 ] ; then echo "# wrapper script around eapol_test from wpa_supplicant project 
# script generates configuration for eapol_test and runs it 
# eapol_test is program for testing RADIUS and their EAP methods authentication

Parameters : 
-H <address> - Address of radius server
-P <port> - Port of radius server
-S <secret> - Secret for radius server communication
-u <username> - Username (user@realm)
-A <anonymous_id> - Anonymous identity (anonymous_user@realm)
-p <password> - Password
-t <timeout> - Timeout (default is 5 seconds)
-m <method> - Method (WPA-EAP | IEEE8021X )
-v - Verbose (prints decoded last Access-accept packet)
-c - Prints all packets decoded 
-s <ssid> - SSID
-e <method> - EAP method (PEAP | TLS | TTLS | LEAP)
-M <mac_addr> - MAC address in xx:xx:xx:xx:xx:xx format
-i <connect_info> - Connection info (in radius log: connect from <connect_info>)
-d <directory> - status directory (unified identifier of packets)
-k <user_key_file> - user certificate key file
-l <user_key_file_password> - password for user certificate key file
-j <user_cert_file> - user certificate file
-a <ca_cert_file> - certificate of CA
-2 <phase2 method> - Phase2 type (PAP,CHAP,MSCHAPV2)
-N - Identify and do not delete temporary files
" >&2 ; exit 1 ; fi

eval set -- "$TEMP"

while true ; do

  case "$1" in
    -H) ADRESS=$2; shift 2 ;;
    -P) PORT=$2; shift 2 ;;
    -S) SECRET=$2; shift 2 ;;
    -u) USERNAME=$2; shift 2 ;;
    -p) PASSWORD=$2; shift 2 ;;
    -t) TIMEOUT=$2; shift 2 ;;
    -m) METHOD=$2; shift 2 ;;
    -v) VERBOSE=1;  shift ;;
    -c) VERBOSE=2;  shift ;;
    -s) SSID=$2; shift 2 ;;
    -e) EAP=$2; shift 2;;
    -t) TIMEOUT=$2; shift 2;;
    -M) MAC=$2; shift 2;;
    -i) CONN_INFO=$2; shift 2;;
    -d) STATUS_DIR="-d$2"; shift 2;;
    -k) USER_KEY=$2; shift 2;;
    -j) USER_CRT=$2; shift 2;;
    -a) CA_CRT=$2; shift 2;;
    -A) ANOMYM_ID=$2; shift 2;;
		-l) KEY_PASS=$2; shift 2;;
    -2) PHASE2=$2; shift 2;;
    -N) CLEANUP=0; shift ;;
    --) break ;;
    *) echo "Unknown option"; shift ;;  # I mean that getopt throws out unrecongized options, therefore this line cannot be running
  esac
done

# necessarily options
if [ -z $ADRESS ]; then
  echo "Address of radius server is not specified. (option -H)"
  exit 3;
fi

if [ -z $PORT ]; then
  echo "Port of radius server is not specified. (option -P)"
  exit 3;
fi

if [ -z $SECRET ]; then
  echo "Secret for radius server communication is not specified. (option -S)"
  exit 3;
fi

if [ -z $USERNAME ]; then
  echo "Username is not specified. (option -u)"
  exit 3;
fi

if [ -z $EAP ]; then
  echo "EAP method is not specified. (option -e)"
  exit 3;
fi

if [ "$EAP" = "TLS" ]; then
	# we need certificate instead of password
  if [ -z $USER_CRT ]; then
		echo "User certificate file is not specified (EAP TLS method is used). (option -j)"
		exit 3;
	fi

	if [ ! -f $USER_CRT ]; then
		echo "User certificate file doesn't exist. (option -j)"
		exit 3;
	fi

	if [ -z $USER_KEY ]; then
		echo "User key file is not specified (EAP TLS method is used). (option -k)"
		exit 3;
	fi

	if [ ! -f $USER_KEY ]; then
		echo "User private key file doesn't exist. (option -k)"
		exit 3;
	fi
else
  
  if [ -z $PASSWORD ]; then
    echo "Password is not specified. (option -p)"
    exit 3;
  fi
fi

if [ -z $METHOD ]; then
  echo "Method is not specified. (option -m)"
  exit 3;
fi

if [ -z $CA_CRT ]; then
	if [ ! -f $CA_CRT ]; then
		echo "Certificate authority file doesn't exist. (option -a)";
		exit 3;
	fi
fi

if [ -z $SSID ]; then
	SSID="eduroam";
fi

if [ -z $PHASE2 ]; then
	PHASE2="MSCHAPV2"
fi

# generation of configuration
echo "network={" > $CONF
echo "  ssid=\"$SSID\"" >> $CONF
echo "  key_mgmt=$METHOD" >> $CONF

echo "  eap=$EAP" >> $CONF

if [ "$EAP" = "PEAP" -o "$EAP" = "TTLS" ]; then 
  echo "  pairwise=CCMP TKIP" >> $CONF
  echo "  group=CCMP TKIP WEP104 WEP40" >> $CONF
  echo "  phase2=\"auth=$PHASE2\"" >> $CONF
fi 

if [ ! -z $CA_CRT ]; then
	echo "	ca_cert=\"$CA_CRT\"" >> $CONF
fi

echo "  identity=\"$USERNAME\"" >> $CONF

if [ ! -z $ANOMYM_ID ]; then
	echo "  anonymous_identity=\"$ANOMYM_ID\"" >> $CONF
fi

if [ "$EAP" = "TLS" ]; then
  echo "  client_cert=\"$USER_CRT\"" >> $CONF
	echo "  private_key=\"$USER_KEY\"" >> $CONF
	if [ ! -z "$KEY_PASS" ]; then
		echo "	private_key_passwd=\"$KEY_PASS\"" >> $CONF
	fi
else
  echo "  password=\"$PASSWORD\"" >> $CONF
fi
echo "}" >> $CONF

#echo $EAP
#cat $CONF

# address may be address or ip address
IP=`echo $ADRESS | grep --regexp="^[[:digit:]+\.[:digit:]+\.[:digit:]+\.[:digit:]]"`
if [ -z "$IP" ]; then 
  #IP=`host $ADRESS | tail -n 1 | awk '{print $3}'`
  IP=`dig +noall +answer +search $ADRESS | tr \\\t ' ' | grep ' IN A ' | sed "s/.* IN A //"`
fi


#echo $IP

# garbage
garbage() {
	if [ $CLEANUP -eq 1 ]; then
    # exception occur => remove files
		rm $CONF $OUT
		rmdir $MYTMPDIR
	else 
	  if [ $CLEANUP -eq 0 ]; then
		echo "Leaving temporary files in $MYTMPDIR"
		echo -e "\tConfiguration: $CONF"
		echo -e "\tOutput: $OUT"
	  fi
	fi
}

trap garbage INT

BEGIN=`date +%s`

#echo "$EAPOL_PROG -c$CONF -a$IP -p$PORT -s$SECRET -t$TIMEOUT -M$MAC -C"$CONN_INFO" $STATUS_DIR"

# try authenticate
if [ $VERBOSE -eq 0 ]; then
  $EAPOL_PROG -c$CONF -a$IP -p$PORT -s$SECRET -t$TIMEOUT -M$MAC -C"$CONN_INFO" $STATUS_DIR | awk '/^SUCCESS$/ {exit '$RET_SUCC';} /^CTRL-EVENT-EAP-FAILURE EAP authentication failed$/ {exit '$RET_EAP_FAILED';} /^EAPOL test timed out$/ {exit '$RET_RADIUS_NOT_AVAIL';} /^CTRL-EVENT-EAP-SUCCESS EAP authentication completed successfully$/ {exit '$RET_SUCC';} /^EAP: Received EAP-Failure$/ {exit '$RET_EAP_FAILED';} /Access-Reject/ {exit '$RET_EAP_FAILED';} ' > $OUT
else
  if [ $VERBOSE -eq 1 ]; then
    $EAPOL_PROG -c$CONF -a$IP -p$PORT -s$SECRET -t$TIMEOUT -M$MAC -C"$CONN_INFO" $STATUS_DIR |  awk '/RADIUS message/ {print} /Attribute/ {print} /Value/ {print} /^SUCCESS$/ {exit '$RET_SUCC';} /^CTRL-EVENT-EAP-FAILURE EAP authentication failed$/ {exit '$RET_EAP_FAILED';} /^EAPOL test timed out$/ {exit '$RET_RADIUS_NOT_AVAIL';} /^CTRL-EVENT-EAP-SUCCESS EAP authentication completed successfully$/ {exit '$RET_SUCC';} /^EAP: Received EAP-Failure$/ {exit '$RET_EAP_FAILED';}  /Access-Reject/ {exit '$RET_EAP_FAILED';} '  > $OUT2
  else
    $EAPOL_PROG -c$CONF -a$IP -p$PORT -s$SECRET -t$TIMEOUT -M$MAC -C"$CONN_INFO" $STATUS_DIR |  awk '/RADIUS message/ {print} /Attribute/ {print} /Value/ {print} /polish/ {print} /^SUCCESS$/ {exit '$RET_SUCC';} /^CTRL-EVENT-EAP-FAILURE EAP authentication failed$/ {exit '$RET_EAP_FAILED';} /^EAPOL test timed out$/ {exit '$RET_RADIUS_NOT_AVAIL';}  /^CTRL-EVENT-EAP-SUCCESS EAP authentication completed successfully$/ {exit '$RET_SUCC';} /^EAP: Received EAP-Failure$/ {exit '$RET_EAP_FAILED';} /Access-Reject/ {exit '$RET_EAP_FAILED';} ' > $OUT
  fi
fi



RETURN_CODE=$?

END=`date +%s`
let T=END-BEGIN

#echo $RETURN_CODE

# remove configuration file
#rm $CONF
#cp $CONF test.conf

if [ $VERBOSE -eq 1 ]; then
	grep -A 100 "(Access-Accept)" $OUT2 > $OUT
	rm $OUT2
fi

# processing of return code
# Successfull authentication
if [ $RETURN_CODE -eq $RET_SUCC ]; then
  echo "access-accept; $T"
  if [ $VERBOSE -gt 0 ]; then
	  cat $OUT
  fi

  garbage
  exit 0;
fi

# Bad name or password
# string "CTRL-EVENT-EAP-FAILURE EAP authentication failed"
if [ $RETURN_CODE -eq $RET_EAP_FAILED ]; then
  echo "access-reject; $T"
  if [ $VERBOSE -gt 0 ]; then
	  cat $OUT
  fi

  garbage
  exit 1;
fi

# timeout return same error as above
# timeout string "EAPOL test timed out"
if [ $RETURN_CODE -eq $RET_RADIUS_NOT_AVAIL ]; then
  #echo "Timeout : Radius server is not available"
  echo "timeout; $T"
  if [ $VERBOSE -gt 0 ]; then
	  cat $OUT
  fi

  garbage
  exit 2;
fi

# other case is probably error
echo "Probably configuration error, examine config in \"$MYTMPDIR\". Return code: " $RETURN_CODE;
exit 3;
