Skip to content

Commit 54d3573

Browse files
author
Chris Rohrer
committedJun 24, 2019
Merge branch 'release/0.6'
2 parents a5bfbfe + e6cac0c commit 54d3573

File tree

2 files changed

+920
-317
lines changed

2 files changed

+920
-317
lines changed
 

‎Dockerfile

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ ARG WPA_SUPPLICANT_VERSION=2.8
77
WORKDIR /eapol
88

99
RUN apk update && apk upgrade && \
10-
apk add --update linux-headers make openssl openssl-dev bind-tools freeradius-radclient && \
10+
apk add --update linux-headers make openssl openssl-dev bind-tools freeradius-radclient grep bash && \
1111
rm /var/cache/apk/*
1212

1313
# ADD https://w1.fi/releases/wpa_supplicant-$WPA_SUPPLICANT_VERSION.tar.gz /tmp

‎rad_eap_test

+919-316
Original file line numberDiff line numberDiff line change
@@ -1,80 +1,624 @@
1-
#!/bin/sh
2-
3-
# rad_eapol_test nagios compatible wraper around eapol_test
4-
# Copyright (c) 2005-2016 CESNET, z.s.p.o.
5-
# Author: Pavel Poláček <pavel.polacek@ujep.cz>
6-
#
7-
# This program is free software; you can redistribute it and/or modify
8-
# it under the terms of the GNU General Public License version 2 as
9-
# published by the Free Software Foundation.
10-
#
1+
#!/bin/bash
2+
3+
# ===========================================================================================
4+
# rad_eapol_test nagios compatible wraper around eapol_test
5+
# Copyright (c) 2005-2019 CESNET, z.s.p.o.
6+
# Authors: Pavel Poláček <pavel.polacek@ujep.cz>
7+
# Jan Tomášek <jan.tomasek@cesnet.cz>
8+
# Václav Mach <vaclav.mach@cesnet.cz>
9+
# and others
10+
#
11+
# This program is free software; you can redistribute it and/or modify
12+
# it under the terms of the GNU General Public License version 2 as
13+
# published by the Free Software Foundation.
14+
#
1115
# See README and COPYING for more details.
12-
13-
# umask
14-
umask 0077
15-
16-
# path to eapol test
17-
# EAPOL_PROG=bin/eapol_test
18-
EAPOL_PROG=eapol_test
19-
20-
# default verbosity
21-
VERBOSE=0
22-
23-
# debug script
24-
DEBUG=1
25-
26-
#default timeout
27-
TIMEOUT=5
28-
29-
#default mac address
30-
MAC="70:6f:6c:69:73:68"
31-
32-
# default connection info
33-
CONN_INFO="rad_eap_test + eapol_test"
34-
35-
# return codes
36-
RET_SUCC=3
37-
RET_EAP_FAILED=4
38-
RET_RADIUS_NOT_AVAIL=5
39-
40-
#cleanup?
41-
CLEANUP=1
42-
43-
# exist eapol_test program ?
44-
if [ ! -x "$EAPOL_PROG" ]; then # exact path?
45-
if [ ! -x `which $EAPOL_PROG` ]; then # is program on path?
46-
if [ -x "./$EAPOL_PROG" ]; then # is program in actual directory?
47-
EAPOL_PROG="./$EAPOL_PROG"
48-
else
49-
echo "eapol_test program \"$EAPOL_PROG\" not found"
50-
exit 3;
51-
fi
16+
# ===========================================================================================
17+
18+
19+
20+
21+
# ===========================================================================================
22+
# test certificate expiry based on user set number of days
23+
# ===========================================================================================
24+
function test_cert_expiry()
25+
{
26+
local not_after
27+
local expiry_now
28+
local expiry_warn
29+
local expiry_date
30+
31+
# certicite expiry was set
32+
if [[ -n "$CERTIFICATE_EXPIRY" ]]
33+
then
34+
process_cert_expiry # get the cert info needed
35+
36+
if [[ $? -ne 0 ]] # no cert available
37+
then
38+
return
39+
fi
40+
41+
expiry_now=$(date +%s) # current date in seconds
42+
expiry_warn=$(($expiry_now + ($CERTIFICATE_EXPIRY * 86400))) # when to warn before expiry in seconds
43+
not_after=$(echo -e "$CERT" | openssl x509 -noout -dates | tail -1 | cut -d "=" -f 2) # get the "not after" date from cert
44+
expiry_date=$(date -d "$not_after" "+%s") # get the expiry date in seconds
45+
46+
47+
# do the actual expiry testing
48+
if [[ $expiry_date -lt $expiry_now ]]
49+
then
50+
PROG_OUT=$(printf "CRITICAL: certificate EXPIRED %s\n" $(date -d "@$expiry_date" -Idate))
51+
EXIT_CODE=$EXIT_CRITICAL
52+
53+
elif [[ $expiry_date -lt $expiry_warn ]]
54+
then
55+
PROG_OUT=$(printf "WARNING: cerificate expires soon (%s)\n" $(date -d "@$expiry_date" -Idate))
56+
EXIT_CODE=$EXIT_WARNING
57+
fi
58+
fi
59+
}
60+
# ===========================================================================================
61+
# add verbose info based on user set level
62+
# ===========================================================================================
63+
verbose()
64+
{
65+
# further processing based on VERBOSE level
66+
case "$VERBOSE" in
67+
0) ;; # nothing to do here, just an empty branch
68+
69+
# Show received Chargeable-User-Identity and/or Operator-Name
70+
1)
71+
echo ""
72+
echo "$OUT" | sed -n '/(Access-Accept)/,$p' | awk '/Attribute (89|126) / { a=$3 } /Value: / && a { print a " " $2; a="" }' # get Attribute 89 or 126 and print the value
73+
;;
74+
75+
# print the last packet decoded
76+
2)
77+
echo ""
78+
echo "$OUT" | sed -n '/(Access-Accept)/,$p' # print from Access-Accept to the end
79+
;;
80+
81+
# print all the packets decoded
82+
3)
83+
echo ""
84+
echo "$OUT" | awk '/RADIUS message/ {print} /Attribute/ {print} /Value/ {print}'
85+
;;
86+
87+
# print the raw output of eapol_test
88+
4)
89+
echo ""
90+
echo "$OUT"
91+
;;
92+
esac
93+
94+
process_cert # also process certificates if wanted
95+
}
96+
# ===========================================================================================
97+
# extract the certificate directly from eapol_test output
98+
# params:
99+
# 1) file where to write the cert
100+
#
101+
# there are some cases, when certificate is not written to file by eapol_test, for example:
102+
# - when checking cert expiry there is a possibility that the server certificate already
103+
# expired and eapol_test failed to succesfully authenticate based on some configuration options
104+
# When this happens eapol_test does not write the server cert (so it can be checked for expiry),
105+
# so we need to check eapol_test output directly
106+
# - when CA cert mismatch happens, no certificate is written to eapo_test output file
107+
# ===========================================================================================
108+
function extract_server_cert()
109+
{
110+
local hex
111+
local certs
112+
local cert_len
113+
local pos=0
114+
local header
115+
116+
hex=$(echo "$OUT" | grep -A 1 '(handshake/certificate)' | head -2 | tail -1 | # get the server cert hexdump message
117+
cut -d ":" -f 3 | tr -d " " | # hex bytes
118+
tail -c +21) # all the certs that the server sent in hex. tail strips first 20 bytes (determined by experiment) which is probably some openssl header
119+
120+
if [[ -z "$hex" ]]
121+
then
122+
return # no handshake/certificate string found in output
123+
fi
124+
125+
while :
126+
do
127+
# there may be some EOC bytes after each processed cert
128+
# to overcome this, read 1 byte at a time and check if it matches cert header
129+
while [[ $pos -le ${#hex} ]] # reached end of hex string, end cert processing
130+
do
131+
header=${hex:$pos:4} # extract 2 byte cert header
132+
133+
if [[ $header != "3082" ]] # header does not match hex bytes 3082
134+
then
135+
((pos+=2)) # extract next byte
136+
continue
137+
else
138+
break # header found
139+
fi
140+
done
141+
142+
if [[ $header != "3082" && $pos -gt ${#hex} ]] # header does not match hex bytes 3082 and pos is out of string
143+
then
144+
break # most likely some error
145+
fi
146+
147+
((pos+=4)) # set pos for extracting cert_len
148+
149+
cert_len=${hex:$pos:4} # extract 2 byte cert lenght
150+
cert_len=$((0x$cert_len)) # covert to decimal
151+
152+
((pos-=4)) # set pos to beginning of the current cert
153+
154+
if [[ -n "$certs" ]]
155+
then
156+
# cert len must be multipltied by 2 to get byte count, add header and cert len
157+
certs="$certs\n$(echo "${hex:$pos:$cert_len*2 + 8}" | xxd -r -p | openssl x509 -inform der)" # extract just the cert bytes and pass it to openssl
158+
else
159+
# cert len must be multipltied by 2 to get byte count, add header and cert len
160+
certs="$(echo "${hex:$pos:$cert_len*2 + 8}" | xxd -r -p | openssl x509 -inform der)" # extract just the cert bytes and pass it to openssl
161+
fi
162+
163+
((pos=$pos + $cert_len*2 + 8)) # set pos for processing next cert: header (2 bytes) + cert_len (2 bytes) + cert (*2 for bytes) + current pos
164+
done
165+
166+
if [[ -n "$certs" ]]
167+
then
168+
echo -e "$certs" > "$1"
169+
fi
170+
}
171+
# ===========================================================================================
172+
# check if server certificate was requested and if it was retrieved
173+
# ===========================================================================================
174+
function save_server_cert()
175+
{
176+
if [[ -n "$CERT_LOCATION" ]] # cert file does not exist or is empty
177+
then
178+
extract_server_cert "$CERT_LOCATION"
179+
fi
180+
}
181+
# ===========================================================================================
182+
# process RADIUS certificate for expiry check
183+
# ===========================================================================================
184+
function process_cert_expiry()
185+
{
186+
if [[ -n "$CERT_LOCATION" ]]
187+
then
188+
189+
if [[ ! -s "$CERT_LOCATION" ]] # cert file does not exist or is empty
190+
then
191+
PROG_OUT="CRITICAL: Certiticate expiry check was requested, but the certificate was not retrieved." # probably timeout ?
192+
EXIT_CODE=$EXIT_CRITICAL
193+
return 1 # cert not retrieved
194+
else
195+
get_cert_info "$CERT_LOCATION"
196+
fi
197+
fi
198+
199+
return 0 # no error
200+
}
201+
# ===========================================================================================
202+
# process RADIUS certificate
203+
# ===========================================================================================
204+
function process_cert()
205+
{
206+
if [[ -n "$GET_CERT" ]] # print cert only if it was requested (-X or -B does not imply -b)
207+
then
208+
if [[ ! -s "$CERT_LOCATION" ]] # cert file does not exist or is empty
209+
then
210+
echo ""
211+
echo "Certiticate information was requested, but the certificate was not retrieved." # probably timeout ?
212+
else
213+
get_cert_info "$CERT_LOCATION"
214+
print_cert
215+
fi
216+
fi
217+
}
218+
# ===========================================================================================
219+
# print final outpupt
220+
# ===========================================================================================
221+
function print_out()
222+
{
223+
echo "$PROG_OUT" # print the output
224+
225+
if [[ $EXIT_CODE -eq $EXIT_UNKNOWN ]]
226+
then
227+
: # do not add extra verbose output when exiting with unknown status
228+
else
229+
verbose # add extra verbose output if the user wants it, also process certs if wanted
230+
fi
231+
232+
cleanup # cleanup temp files
233+
}
234+
# ===========================================================================================
235+
# process results of the authentication and present them to the user
236+
# ===========================================================================================
237+
function process_auth_result()
238+
{
239+
EXIT_CODE=$EXIT_OK
240+
241+
# preset output needed for most situations
242+
PROG_OUT=$(
243+
printf "%s; %0.2f sec " "$STATUS_CODE" $TIME_SEC
244+
printf "|rtt=%0.0fms;;;0;%d accept=1;0.5:;0:;0;1\n" $TIME_MSEC $((TIMEOUT * 1000))
245+
)
246+
247+
# processing based on $RETURN_CODE
248+
case "$RETURN_CODE" in
249+
$RET_SUCC) # successfull authentication
250+
;; # nothing to do here, EXIT_CODE is preset to 0
251+
252+
$RET_EAP_FAILED) # wrong username or password
253+
EXIT_CODE=$EXIT_WARNING;;
254+
255+
$RET_RADIUS_NOT_AVAIL) # timeout
256+
EXIT_CODE=$EXIT_CRITICAL;;
257+
258+
$RET_CERT_SUBJ_MISMATCH) # cert subject mismatch
259+
EXIT_CODE=$EXIT_CRITICAL;;
260+
261+
$RET_CERT_CA_MISMATCH) # cert not matching specified CA
262+
PROG_OUT=$(echo "$PROG_OUT" ; echo ""; get_ca_cert_mismatch_details ) # add extra error info about mismatch here
263+
EXIT_CODE=$EXIT_CRITICAL;;
264+
265+
$RET_CERT_CA_MISMATCH_INCOMPLETE) # cert not matching specified CA, incomplete CA chain
266+
EXIT_CODE=$EXIT_CRITICAL;;
267+
268+
$RET_CERT_EXPIRED) # cert expired
269+
EXIT_CODE=$EXIT_CRITICAL;;
270+
271+
$RET_DOMAIN_MISMATCH) # domain mismatch
272+
EXIT_CODE=$EXIT_CRITICAL;;
273+
274+
$RET_EAPOL_TEST_FAILED) # eapol_test return code was nonzero
275+
PROG_OUT="eapol_test returned error: $OUT"
276+
EXIT_CODE=$EXIT_UNKNOWN;;
277+
278+
*) # other case is probably error
279+
PROG_OUT=$(echo "Probably configuration error, examine config in \"$MYTMPDIR\". Return code: " $RETURN_CODE)
280+
EXIT_CODE=$EXIT_UNKNOWN;;
281+
esac
282+
283+
test_cert_expiry # test cert expiry dates if requested, may override EXIT_CODE
284+
print_out # print output, add verbose output if requsted and cleanup
285+
exit $EXIT_CODE # exit with $EXIT_CODE
286+
}
287+
# ===========================================================================================
288+
# run eapol_test and try to authenticate using the specified configuration
289+
# ===========================================================================================
290+
function run_eapol_test()
291+
{
292+
BEGIN=$(date +%s.%N) # start the "timer"
293+
294+
# try to authenticate
295+
OUT=$($EAPOL_PROG -c "$CONF" -a "$IP" -p "$PORT" -s "$SECRET" -t "$TIMEOUT" -M "$MAC" -C "$CONN_INFO" $EXTRA_EAPOL_ARGS 2>&1) # save output as a variable
296+
EAPOL_PROG_RETCODE=$? # save the return code in case some error happended
297+
298+
END=$(date +%s.%N) # end the "timer"
299+
}
300+
# ===========================================================================================
301+
# get details about domain name mismatch
302+
# ===========================================================================================
303+
function get_domain_mismatch_details()
304+
{
305+
# simple parsing of important info using awk
306+
echo "$OUT" | awk '
307+
BEGIN { count = 0 }
308+
309+
/TLS: Match domain against/,/TLS: Domain match/ { # everyting between the two strings
310+
311+
if(match($0, /^TLS: Match domain against.*/)) # ignore lines starting with "TLS: Match domain against."
312+
next
313+
314+
if(match($0, /^TLS: None of the dNSName\(s\) matched.*/)) # ignore lines starting with "TLS: None of the dNSName(s) matched."
315+
next
316+
317+
if(match($0, /^TLS: No CommonName match found*/)) # ignore lines starting with "TLS: No CommonName match found"
318+
next
319+
320+
if((match($0, /^TLS: Certificate dNSName.*/) && count == 0) || (match($0, /^TLS: Certificate commonName.*/) && count == 0)) { # first occurence of domain name or common name, ignore it
321+
count++
322+
next
323+
}
324+
325+
if(match($0, /^TLS: Certificate dNSName.*/) || match($0, /TLS: Certificate commonName.*/)) { # other dns names or common names, print comma after every domain name
326+
printf(", ")
327+
next
328+
}
329+
330+
if(match($0, /^TLS: Domain match.*/)) {
331+
printf(" not matching %s", $4) # print the requested match
332+
exit(0) # end the program here
333+
}
334+
335+
printf("%s", $NF) # print $NF
336+
}
337+
'
338+
}
339+
# ===========================================================================================
340+
# get details about CA cert mismatch
341+
# use just CN from certs
342+
# ===========================================================================================
343+
function get_ca_cert_mismatch_details()
344+
{
345+
echo -n "'$(openssl x509 -nameopt utf8 -in "$CA_CRT" -noout -subject)'"
346+
347+
if [[ -n "$CERT_LOCATION" && -s "$CERT_LOCATION" ]]
348+
then
349+
echo -n " is not matching '$(openssl x509 -nameopt utf8 -in "$CERT_LOCATION" -noout -issuer | tr -d "\n")'"
350+
fi
351+
}
352+
# ===========================================================================================
353+
# determine the return code of this program based on processing the eapol_test output
354+
# ===========================================================================================
355+
function determine_return_code()
356+
{
357+
# constants which define return codes based on eapol_test output
358+
local eap_fail1='CTRL-EVENT-EAP-FAILURE EAP authentication failed'
359+
local eap_fail2='EAP: Received EAP-Failure'
360+
local timeout='EAPOL test timed out'
361+
local succ1='SUCCESS'
362+
local succ2='CTRL-EVENT-EAP-SUCCESS EAP authentication completed successfully'
363+
local reject='Access-Reject'
364+
local cert_subj_mismatch="err='Subject mismatch'"
365+
366+
# 2 X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT: unable to get issuer certificate
367+
# the issuer certificate of a looked up certificate could not be found. This normally means the list of trusted certificates is not complete.
368+
local ca_mismatch_incomplete_chain="err='unable to get issuer certificate'" # certificate not matching specified CA. Specified CA does not have full valid chain (missing root or intermediate certs).
369+
370+
# from openssl man pages: 20 X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY: unable to get local issuer certificate
371+
# the issuer certificate could not be found: this occurs if the issuer certificate of an untrusted certificate cannot be found.
372+
local ca_mismatch_cert_not_matching_ca="err='unable to get local issuer certificate'" # certificate not matching specified CA. Server cert does not match specified CA (different CA subject and issuer in server cert)
373+
374+
local ca_mismatch_selfsign="err='self signed certificate in certificate chain'" # certificate not matching specified CA. Server is sending full chain with root which is self-signed
375+
376+
local cert_expired="err='certificate has expired'"
377+
local root_cert="depth=0" # CA certs are validated at depth 0 (root). TODO: What about intermediate certs?
378+
local domain_mismatch="err='Domain mismatch'"
379+
380+
run_eapol_test
381+
382+
# check if there was an error launching eapol_test
383+
if [[ $EAPOL_PROG_RETCODE -ne 0 ]]
384+
then
385+
RETURN_CODE=$RET_EAPOL_TEST_FAILED # eapol_test failed to execute
386+
STATUS_CODE="$OUT"
387+
fi
388+
389+
# determine the RETURN_CODE based on specific constants used in eapol_test output
390+
if [[ "$OUT" =~ $succ1 || "$OUT" =~ $succ2 ]]
391+
then
392+
RETURN_CODE=$RET_SUCC # success
393+
STATUS_CODE="access-accept"
394+
395+
elif [[ -n "$DOMAIN_MATCH" && "$OUT" =~ $domain_mismatch ]] # domain match was speficied
396+
then
397+
RETURN_CODE=$RET_DOMAIN_MISMATCH # domain name mismatch
398+
STATUS_CODE="access-reject (domain mismatch [$(get_domain_mismatch_details)])"
399+
400+
elif [[ -n "$SUBJ_MATCH" && "$OUT" =~ $cert_subj_mismatch ]] # certifitace subject was speficied
401+
then
402+
RETURN_CODE=$RET_CERT_SUBJ_MISMATCH # certificate subject mismatch
403+
STATUS_CODE="access-reject (certificate subject mismatch [$(echo "$OUT" | grep 'did not match with' | sed 's/TLS: Subject //')])"
404+
405+
elif [[ -n "$CA_CRT" && "$OUT" =~ $ca_mismatch_incomplete_chain ]] # CA was speficied, but is incomplete
406+
then
407+
RETURN_CODE=$RET_CERT_CA_MISMATCH_INCOMPLETE # certificate not matching CA
408+
STATUS_CODE="access-reject (certificate not matching specified CA [used CA does not have a complete chain])"
409+
410+
elif [[ -n "$CA_CRT" && "$OUT" =~ $ca_mismatch_cert_not_matching_ca ]] # CA was speficied but does not match server cert
411+
then
412+
RETURN_CODE=$RET_CERT_CA_MISMATCH # certificate not matching CA
413+
STATUS_CODE="access-reject (certificate not matching specified CA)"
414+
415+
elif [[ -n "$CA_CRT" && "$OUT" =~ $ca_mismatch_selfsign ]] # CA was speficied but the server is sending a completely different chain
416+
then
417+
RETURN_CODE=$RET_CERT_CA_MISMATCH # certificate not matching CA
418+
STATUS_CODE="access-reject (certificate not matching specified CA)"
419+
420+
elif [[ -n "$CA_CRT" && "$OUT" =~ $cert_expired && "(echo "$OUT" | grep "$cert_expired")" =~ $root_cert ]] # root CA certificate expired
421+
then
422+
RETURN_CODE=$RET_CERT_EXPIRED # certificate expired
423+
STATUS_CODE="access-reject (CA certificate expired [$(echo "$OUT" | grep "$cert_expired" | sed 's/^.*subject/subject/; s/ err=.*//')])"
424+
425+
# certificate expired (a more complex logic would be probably needed to distinguish end server and intermediate certs)
426+
elif [[ -n "$CA_CRT" && "$OUT" =~ $cert_expired && ! "(echo "$OUT" | grep "$cert_expired")" =~ $root_cert ]]
427+
then
428+
RETURN_CODE=$RET_CERT_EXPIRED # certificate expired
429+
STATUS_CODE="access-reject (certificate expired [$(echo "$OUT" | grep "$cert_expired" | sed 's/^.*subject/subject/; s/ err=.*//')])"
430+
431+
elif [[ "$OUT" =~ $eap_fail1 || "$OUT" =~ $eap_fail2 || "$OUT" =~ $reject ]]
432+
then
433+
RETURN_CODE=$RET_EAP_FAILED # auth failed
434+
STATUS_CODE="access-reject"
435+
436+
elif [[ "$OUT" =~ $timeout ]]
437+
then
438+
RETURN_CODE=$RET_RADIUS_NOT_AVAIL # timeout ?
439+
STATUS_CODE="timeout"
440+
fi
441+
442+
save_server_cert # always save the server cert if the user requested it
443+
444+
TIME_SEC=$(echo "$END - $BEGIN" | bc) # how long the authentication took in seconds
445+
TIME_MSEC=$(echo "$TIME_SEC * 1000" | bc) # how long the authentication took in milliseconds
446+
}
447+
# ===========================================================================================
448+
# cleanup temporary files
449+
# ===========================================================================================
450+
function cleanup()
451+
{
452+
if [[ $CLEANUP -eq 1 ]]
453+
then
454+
rm -r $MYTMPDIR # delete all temp files
455+
else
456+
echo "$OUT" > $OUTFILE # write raw eapol_test output to temp file
457+
458+
echo "Leaving temporary files in $MYTMPDIR"
459+
echo -e "\tConfiguration: $CONF"
460+
echo -e "\tOutput: $OUTFILE"
461+
echo -e "\tRADIUS certiticate: $CERT_LOCATION"
462+
fi
463+
}
464+
# ===========================================================================================
465+
# print certificate information
466+
# ===========================================================================================
467+
function print_cert()
468+
{
469+
printf "\n"
470+
printf "RADIUS server certificate information:\n"
471+
printf "%s\n" "$(echo -e "$CERT" | openssl x509 -nameopt utf8 -noout -issuer -subject -dates | # extract issuer, subject, dates
472+
sed 's/issuer=/Issuer: /; s/subject=/Subject: /; s/notBefore=/Validity\nNot Before: /; s/notAfter=/Not After: /')" # more readable output
473+
474+
printf "%s\n" "$(echo "$cert_info" | grep 'DNS:' | sed 's/^[[:space:]]*//g')" # DNS names cannot be extracted directly by openssl
475+
}
476+
# ===========================================================================================
477+
# check that cert is end server cert - not root or intermediate cert
478+
# params:
479+
# 1) cert to check (as text)
480+
# ===========================================================================================
481+
function check_cert()
482+
{
483+
# we're only looking for cert that does not contain 'CA:TRUE' flag
484+
res=$(echo -e "$1" | openssl x509 -text -noout | grep 'CA:TRUE')
485+
486+
if [[ -z "$res" ]]
487+
then
488+
return 0 # the cert is for end server
489+
else
490+
return 1 # intermediate or root
491+
fi
492+
}
493+
# ===========================================================================================
494+
# get RADIUS server certificate info
495+
# params:
496+
# 1) path to the cert
497+
# ===========================================================================================
498+
function get_cert_info()
499+
{
500+
in=false
501+
502+
if [[ -n "$cert_info" && -n "$CERT" ]] # both $CERT and $cert_info already set, no need to process certs again
503+
then
504+
return
505+
fi
506+
507+
# read cert file line by line
508+
while read line
509+
do
510+
if [[ "$in" = "true" ]]
511+
then
512+
CERT="$CERT\n$line"
513+
fi
514+
515+
if [[ "$line" = "-----BEGIN CERTIFICATE-----" ]]
516+
then
517+
in=true
518+
CERT="$line"
519+
fi
520+
521+
if [[ "$line" = "-----END CERTIFICATE-----" ]]
522+
then
523+
in=false
524+
check_cert "$CERT"
525+
526+
if [[ $? -eq 0 ]] # correct cert
527+
then
528+
cert_info=$(echo -e "$CERT" | openssl x509 -nameopt utf8 -text -noout)
529+
break
530+
fi
531+
fi
532+
done < "$1"
533+
534+
# no cert seemed correct as end server cert
535+
# take the last one processed
536+
if [[ -z "$cert_info" ]]
537+
then
538+
cert_info=$(echo -e "$CERT" | openssl x509 -nameopt utf8 -text -noout)
539+
fi
540+
}
541+
542+
# ===========================================================================================
543+
# generate configuration for eapol_test
544+
# ===========================================================================================
545+
function generate_config()
546+
{
547+
echo "network={" > $CONF
548+
echo " ssid=\"$SSID\"" >> $CONF
549+
echo " key_mgmt=$METHOD" >> $CONF
550+
551+
echo " eap=$EAP" >> $CONF
552+
553+
if [[ "$EAP" = "PEAP" || "$EAP" = "TTLS" ]]
554+
then
555+
echo " pairwise=CCMP TKIP" >> $CONF
556+
echo " group=CCMP TKIP WEP104 WEP40" >> $CONF
557+
echo " phase2=\"auth=$PHASE2\"" >> $CONF
558+
fi
559+
560+
if [[ ! -z "$CA_CRT" ]]
561+
then
562+
echo " ca_cert=\"$CA_CRT\"" >> $CONF
563+
fi
564+
565+
echo " identity=\"$USERNAME\"" >> $CONF
566+
567+
if [[ ! -z "$ANONYM_ID" ]]
568+
then
569+
echo " anonymous_identity=\"$ANONYM_ID\"" >> $CONF
570+
fi
571+
572+
if [[ "$EAP" = "TLS" ]]
573+
then
574+
echo " client_cert=\"$USER_CRT\"" >> $CONF
575+
echo " private_key=\"$USER_KEY\"" >> $CONF
576+
577+
if [[ ! -z "$KEY_PASS" ]]
578+
then
579+
echo " private_key_passwd=\"$KEY_PASS\"" >> $CONF
52580
fi
53-
fi
581+
else
582+
echo " password=\"$PASSWORD\"" >> $CONF
583+
fi
54584

55-
TEMP=`getopt -o H:P:S:u:p:t:m:s:e:t:M:i:d:j:k:a:A:l:2:x:vcNO:I:Ch -- "$@"`
585+
if [[ -n "$SUBJ_MATCH" ]]
586+
then
587+
echo " subject_match=\"$SUBJ_MATCH\"" >> $CONF
588+
fi
56589

57-
myhelp() {
58-
echo "# wrapper script around eapol_test from wpa_supplicant project
59-
# script generates configuration for eapol_test and runs it
60-
# eapol_test is program for testing RADIUS and their EAP methods authentication
590+
if [[ -n "$DOMAIN_MATCH" ]]
591+
then
592+
echo " domain_match=\"$DOMAIN_MATCH\"" >> $CONF
593+
fi
61594

62-
Parameters :
63-
-H <address> - Address of radius server
595+
echo "}" >> $CONF
596+
}
597+
# ===========================================================================================
598+
# print usage for the program
599+
# ===========================================================================================
600+
function usage()
601+
{
602+
echo "# this program is a wrapper for eapol_test from wpa_supplicant project
603+
# this script generates configuration for eapol_test and runs it
604+
# eapol_test is a program for testing RADIUS protocol and EAP authentication methods
605+
606+
Parameters :
607+
-H <address> - Address of radius server (DNS name or IP address). When using DNS name IPv4 address will be used unless -6 option is present. Both IPv4 or IPv6 addresses may be used.
64608
-P <port> - Port of radius server
65609
-S <secret> - Secret for radius server communication
66-
-u <username> - Username (user@realm)
67-
-A <anonymous_id> - Anonymous identity (anonymous_user@realm)
610+
-u <username> - Username (user@realm.tld)
611+
-A <anonymous_id> - Anonymous identity (anonymous_user@realm.tld)
68612
-p <password> - Password
69613
-t <timeout> - Timeout (default is 5 seconds)
70614
-m <method> - Method (WPA-EAP | IEEE8021X )
71615
-v - Verbose (prints decoded last Access-accept packet)
72-
-c - Prints all packets decoded
616+
-c - Prints all packets decoded
73617
-s <ssid> - SSID
74618
-e <method> - EAP method (PEAP | TLS | TTLS | LEAP)
75619
-M <mac_addr> - MAC address in xx:xx:xx:xx:xx:xx format
76620
-i <connect_info> - Connection info (in radius log: connect from <connect_info>)
77-
-d <directory> - status directory (unified identifier of packets)
621+
-d <domain_name> - Constraint for server domain name. FQDN is used as a full match requirement for the server certificate. Multiple values may be specified. Multiple values must be separated by semicollon.
78622
-k <user_key_file> - user certificate key file
79623
-l <user_key_file_password> - password for user certificate key file
80624
-j <user_cert_file> - user certificate file
@@ -85,300 +629,359 @@ Parameters :
85629
-O <domain.edu.cctld> - Operator-Name value in domain name format
86630
-I <ip address> - explicitly specify NAS-IP-Address
87631
-C - request Chargeable-User-Identity
632+
-T - send Called-Station-Id in MAC:SSID format
633+
-f - send big access-request to cause fragmentation
634+
-b - print details about certificate of RADIUS server (whole certificate chain may be retrieved by eapol_test, there is a certain logic that tries to determine the end server cert and print it)
635+
-B <file> - save certificate of RADIUS server to specified file
636+
-n <directory> - store temporary files in specified directory
637+
-g - print the entire unmodified output of eapol_test
638+
-V - Show received Chargeable-User-Identity and/or Operator-Name
639+
-X <warn_days> - check certificate expiry (whole certificate chain may be retrieved by eapol_test, there is a certain logic that tries to determine the end server cert which is checked for expiry)
640+
-6 force use of IPv6 when using DNS name as RADIUS server address
88641
-h - show this message
89-
" >&2;
90-
exit 1;
642+
" >&2
643+
exit 1
91644
}
645+
# ===========================================================================================
646+
# check configuration parameters, environment and various other things
647+
# ===========================================================================================
648+
function check_settings()
649+
{
650+
# check dependencies used in this script
651+
if [[ -z "$(which bc)" ]]
652+
then
653+
echo "bc is required by rad_eap_test, please install if first."
654+
return 1
655+
fi
92656

93-
if [ -z $1 ] ; then
94-
myhelp
95-
fi
96-
97-
eval set -- "$TEMP"
98-
99-
while true ; do
100-
101-
case "$1" in
102-
-H) ADRESS=$2; shift 2 ;;
103-
-P) PORT=$2; shift 2 ;;
104-
-S) SECRET=$2; shift 2 ;;
105-
-u) USERNAME=$2; shift 2 ;;
106-
-p) PASSWORD=$2; shift 2 ;;
107-
-t) TIMEOUT=$2; shift 2 ;;
108-
-m) METHOD=$2; shift 2 ;;
109-
-v) VERBOSE=1; shift ;;
110-
-c) VERBOSE=2; shift ;;
111-
-s) SSID=$2; shift 2 ;;
112-
-e) EAP=$2; shift 2;;
113-
-t) TIMEOUT=$2; shift 2;;
114-
-M) MAC=$2; shift 2;;
115-
-i) CONN_INFO=$2; shift 2;;
116-
-d) STATUS_DIR="-d$2"; shift 2;;
117-
-k) USER_KEY=$2; shift 2;;
118-
-j) USER_CRT=$2; shift 2;;
119-
-a) CA_CRT=$2; shift 2;;
120-
-A) ANOMYM_ID=$2; shift 2;;
121-
-l) KEY_PASS=$2; shift 2;;
122-
-2) PHASE2=$2; shift 2;;
123-
-N) CLEANUP=0; shift ;;
124-
-x) SUBJ_MATCH=$2; shift 2;;
125-
-O) OPERATOR_NAME=$2; shift 2;;
126-
-I) NAS_IP_ADDRESS=$2; shift 2;;
127-
-C) REQUEST_CUI="YES"; shift ;;
128-
-h) myhelp; shift ;;
129-
--) break ;;
130-
*) echo "Unknown option"; shift ;; # I mean that getopt throws out unrecongized options, therefore this line cannot be running
131-
esac
132-
done
133-
134-
# necessarily options
135-
if [ -z $ADRESS ]; then
136-
echo "Address of radius server is not specified. (option -H)"
137-
exit 3;
138-
fi
657+
if [[ -z "$(which dig)" ]]
658+
then
659+
echo "dig is required by rad_eap_test, please install if first."
660+
return 1
661+
fi
139662

140-
if [ -z $PORT ]; then
141-
echo "Port of radius server is not specified. (option -P)"
142-
exit 3;
143-
fi
663+
if [[ -z "$(which sed)" ]]
664+
then
665+
echo "sed is required by rad_eap_test, please install if first."
666+
return 1
667+
fi
144668

145-
if [ -z $SECRET ]; then
146-
echo "Secret for radius server communication is not specified. (option -S)"
147-
exit 3;
148-
fi
669+
if [[ -z "$(which awk)" ]]
670+
then
671+
echo "awk is required by rad_eap_test, please install if first."
672+
return 1
673+
fi
149674

150-
if [ -z $USERNAME ]; then
151-
echo "Username is not specified. (option -u)"
152-
exit 3;
153-
fi
675+
# check if eapol_test exists
676+
if [[ ! -e "$EAPOL_PROG" ]]
677+
then
678+
echo "eapol_test program \"$EAPOL_PROG\" not found"
679+
return 1
680+
fi
154681

155-
if [ -z $EAP ]; then
156-
echo "EAP method is not specified. (option -e)"
157-
exit 3;
158-
fi
682+
# check if eapol_test is executable
683+
if [[ ! -x "$EAPOL_PROG" ]]
684+
then
685+
echo "eapol_test program \"$EAPOL_PROG\" is not executable"
686+
return 1
687+
fi
159688

160-
if [ "$EAP" = "TLS" ]; then
161-
# we need certificate instead of password
162-
if [ -z $USER_CRT ]; then
163-
echo "User certificate file is not specified (EAP TLS method is used). (option -j)"
164-
exit 3;
689+
if [[ -z "$ADDRESS" ]]
690+
then
691+
echo "Address of radius server is not specified. (option -H)"
692+
return 1
165693
fi
166694

167-
if [ ! -f $USER_CRT ]; then
168-
echo "User certificate file doesn't exist. (option -j)"
169-
exit 3;
695+
if [[ -z "$PORT" ]]
696+
then
697+
echo "Port of radius server is not specified. (option -P)"
698+
return 1
170699
fi
171700

172-
if [ -z $USER_KEY ]; then
173-
echo "User key file is not specified (EAP TLS method is used). (option -k)"
174-
exit 3;
701+
if [[ -z "$SECRET" ]]
702+
then
703+
echo "Secret for radius server communication is not specified. (option -S)"
704+
return 1
175705
fi
176706

177-
if [ ! -f $USER_KEY ]; then
178-
echo "User private key file doesn't exist. (option -k)"
179-
exit 3;
707+
if [[ -z "$USERNAME" ]]
708+
then
709+
echo "Username is not specified. (option -u)"
710+
return 1
180711
fi
181-
else
182-
183-
if [ -z $PASSWORD ]; then
184-
echo "Password is not specified. (option -p)"
185-
exit 3;
712+
713+
if [[ -z "$EAP" ]]
714+
then
715+
echo "EAP method is not specified. (option -e)"
716+
return 1
186717
fi
187-
fi
188718

189-
if [ -z $METHOD ]; then
190-
echo "Method is not specified. (option -m)"
191-
exit 3;
192-
fi
719+
if [[ "$EAP" = "TLS" ]]
720+
then
721+
# we need certificate instead of password
722+
if [[ -z "$USER_CRT" ]]
723+
then
724+
echo "User certificate file is not specified (EAP TLS method is used). (option -j)"
725+
return 1
726+
fi
193727

194-
if [ -z $CA_CRT ]; then
195-
if [ ! -f $CA_CRT ]; then
196-
echo "Certificate authority file doesn't exist. (option -a)";
197-
exit 3;
198-
fi
199-
fi
200-
201-
if [ -z $SSID ]; then
202-
SSID="eduroam";
203-
fi
204-
205-
if [ -z $PHASE2 ]; then
206-
PHASE2="MSCHAPV2"
207-
fi
208-
209-
if [ -n "$OPERATOR_NAME" ] ; then
210-
EXTRA_EAPOL_ARGS="$EXTRA_EAPOL_ARGS -N126:s:$OPERATOR_NAME"
211-
fi
212-
213-
if [ -n "$NAS_IP_ADDRESS" ] ; then
214-
NAS_IP_ADDRESS_HEX=$( printf '%02x%02x%02x%02x' $( echo "$NAS_IP_ADDRESS" | tr '.' ' ' ) )
215-
EXTRA_EAPOL_ARGS="$EXTRA_EAPOL_ARGS -N4:x:$NAS_IP_ADDRESS_HEX"
216-
fi
217-
218-
if [ -n "$REQUEST_CUI" ] ; then
219-
EXTRA_EAPOL_ARGS="$EXTRA_EAPOL_ARGS -N89:x:00"
220-
fi
221-
222-
# address may be address or ip address
223-
IP=`echo $ADRESS | grep -e "^[[:digit:]+\.[:digit:]+\.[:digit:]+\.[:digit:]]"`
224-
if [ -z "$IP" ]; then
225-
#IP=`host $ADRESS | tail -n 1 | awk '{print $3}'`
226-
IP=`dig +noall +answer +search $ADRESS | tr \\\t ' ' | grep ' IN A ' | sed "s/.* IN A //"`
227-
fi
228-
229-
# Sanity check: did we get an IP address?
230-
if [ -z "$IP" ] ; then
231-
echo "Hostname $ADRESS could not be resolved to an IP address."
232-
exit 2;
233-
fi
234-
235-
#echo $IP
236-
237-
238-
# temporary file of eapol_test configuration
239-
if [ -z $TMPDIR ]; then
240-
MYTMPDIR=`mktemp -d /tmp/rad_eap_test.XXXXXX`
241-
else
242-
MYTMPDIR=`mktemp -d $TMPDIR/rad_eap_test.XXXXXX`
243-
fi
244-
245-
CONF=$MYTMPDIR/tmp-$$
246-
OUT=$CONF.out
247-
OUT2=$CONF.out2
248-
249-
#echo $CONF $OUT
250-
251-
# generation of configuration
252-
echo "network={" > $CONF
253-
echo " ssid=\"$SSID\"" >> $CONF
254-
echo " key_mgmt=$METHOD" >> $CONF
255-
256-
echo " eap=$EAP" >> $CONF
257-
258-
if [ "$EAP" = "PEAP" -o "$EAP" = "TTLS" ]; then
259-
echo " pairwise=CCMP TKIP" >> $CONF
260-
echo " group=CCMP TKIP WEP104 WEP40" >> $CONF
261-
echo " phase2=\"auth=$PHASE2\"" >> $CONF
262-
fi
263-
264-
if [ ! -z $CA_CRT ]; then
265-
echo " ca_cert=\"$CA_CRT\"" >> $CONF
266-
fi
267-
268-
echo " identity=\"$USERNAME\"" >> $CONF
269-
270-
if [ ! -z $ANOMYM_ID ]; then
271-
echo " anonymous_identity=\"$ANOMYM_ID\"" >> $CONF
272-
fi
273-
274-
if [ "$EAP" = "TLS" ]; then
275-
echo " client_cert=\"$USER_CRT\"" >> $CONF
276-
echo " private_key=\"$USER_KEY\"" >> $CONF
277-
if [ ! -z "$KEY_PASS" ]; then
278-
echo " private_key_passwd=\"$KEY_PASS\"" >> $CONF
279-
fi
280-
else
281-
echo " password=\"$PASSWORD\"" >> $CONF
282-
fi
283-
284-
if [ -n "$SUBJ_MATCH" ]; then
285-
echo " subject_match=\"$SUBJ_MATCH\"" >> $CONF
286-
fi
287-
288-
echo "}" >> $CONF
289-
290-
#echo $EAP
291-
#cat $CONF
292-
293-
# garbage
294-
garbage() {
295-
if [ $CLEANUP -eq 1 ]; then
296-
# exception occur => remove files
297-
rm $CONF $OUT
298-
rmdir $MYTMPDIR
299-
else
300-
if [ $CLEANUP -eq 0 ]; then
301-
echo "Leaving temporary files in $MYTMPDIR"
302-
echo -e "\tConfiguration: $CONF"
303-
echo -e "\tOutput: $OUT"
728+
if [[ ! -f "$USER_CRT" ]]
729+
then
730+
echo "User certificate file doesn't exist. (option -j)"
731+
return 1
304732
fi
305-
fi
306-
}
307733

308-
trap "garbage ; exit 2" INT
734+
if [[ -z "$USER_KEY" ]]
735+
then
736+
echo "User key file is not specified (EAP TLS method is used). (option -k)"
737+
return 1
738+
fi
309739

310-
BEGIN=`date +%s`
740+
if [[ ! -f "$USER_KEY" ]]
741+
then
742+
echo "User private key file doesn't exist. (option -k)"
743+
return 1
744+
fi
311745

312-
#echo "$EAPOL_PROG -c$CONF -a$IP -p$PORT -s$SECRET -t$TIMEOUT -M$MAC -C"$CONN_INFO" $EXTRA_EAPOL_ARGS $STATUS_DIR"
746+
else # $EAP != "TLS"
747+
if [[ -z "$PASSWORD" ]]
748+
then
749+
echo "Password is not specified. (option -p)"
750+
return 1
751+
fi
752+
fi
313753

314-
# try authenticate
315-
if [ $VERBOSE -eq 0 ]; then
316-
$EAPOL_PROG -c$CONF -a$IP -p$PORT -s$SECRET -t$TIMEOUT -M$MAC -C"$CONN_INFO" $EXTRA_EAPOL_ARGS $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
317-
else
318-
if [ $VERBOSE -eq 1 ]; then
319-
$EAPOL_PROG -c$CONF -a$IP -p$PORT -s$SECRET -t$TIMEOUT -M$MAC -C"$CONN_INFO" $EXTRA_EAPOL_ARGS $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
320-
else
321-
$EAPOL_PROG -c$CONF -a$IP -p$PORT -s$SECRET -t$TIMEOUT -M$MAC -C"$CONN_INFO" $EXTRA_EAPOL_ARGS $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
754+
if [[ -z "$METHOD" ]]
755+
then
756+
echo "Method is not specified. (option -m)"
757+
return 1
322758
fi
323-
fi
324759

760+
if [[ -n "$CA_CRT" && ! -f "$CA_CRT" ]]
761+
then
762+
echo "Certificate authority file doesn't exist. (option -a)";
763+
return 1
764+
fi
325765

766+
if [[ -z "$SSID" ]]
767+
then
768+
SSID="eduroam";
769+
fi
326770

327-
RETURN_CODE=$?
771+
if [[ -z "$PHASE2" ]]
772+
then
773+
PHASE2="MSCHAPV2"
774+
fi
328775

329-
END=`date +%s`
330-
T=$((END-BEGIN))
776+
if [[ -n "$OPERATOR_NAME" ]]
777+
then
778+
# prefix the Operator_Name with NamespaceID value "1" (REALM) as per RFC5580
779+
EXTRA_EAPOL_ARGS="$EXTRA_EAPOL_ARGS -N126:s:1$OPERATOR_NAME"
780+
fi
331781

332-
#echo $RETURN_CODE
782+
if [[ -n "$NAS_IP_ADDRESS" ]]
783+
then
784+
NAS_IP_ADDRESS_HEX=$(printf '%02x%02x%02x%02x' $(echo "$NAS_IP_ADDRESS" | tr '.' ' ' ))
785+
EXTRA_EAPOL_ARGS="$EXTRA_EAPOL_ARGS -N4:x:$NAS_IP_ADDRESS_HEX"
786+
fi
333787

334-
# remove configuration file
335-
#rm $CONF
336-
#cp $CONF test.conf
788+
if [[ -n "$REQUEST_CUI" ]]
789+
then
790+
EXTRA_EAPOL_ARGS="$EXTRA_EAPOL_ARGS -N89:x:00"
791+
fi
337792

338-
if [ $VERBOSE -eq 1 ]; then
339-
grep -A 100 "(Access-Accept)" $OUT2 > $OUT
340-
rm $OUT2
341-
fi
793+
if [[ -n "$FRAGMENT" ]]
794+
then
795+
for i in $(seq 1 6)
796+
do
797+
EXTRA_EAPOL_ARGS="$EXTRA_EAPOL_ARGS -N26:x:0000625A0BF961616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161"
798+
done
799+
fi
342800

343-
# processing of return code
344-
# Successfull authentication
345-
if [ $RETURN_CODE -eq $RET_SUCC ]; then
346-
echo "access-accept; $T"
347-
if [ $VERBOSE -gt 0 ]; then
348-
cat $OUT
801+
if [[ -n "$CALLED_STATION_ID" ]]
802+
then
803+
DASHEDMAC=$(echo "$MAC" | tr ':a-z' '-A-Z') # replace ':' with '-' and convert all lowercase to uppercase
804+
EXTRA_EAPOL_ARGS="$EXTRA_EAPOL_ARGS -N30:s:$DASHEDMAC:$SSID"
349805
fi
350806

351-
garbage
352-
exit 0;
353-
fi
807+
# address may be DNS name or an IPv4 address
808+
# IPv4
809+
IP=$(echo "$ADDRESS" | grep -P '^(\d{1,3}\.){3}\d{1,3}$')
354810

355-
# Bad name or password
356-
# string "CTRL-EVENT-EAP-FAILURE EAP authentication failed"
357-
if [ $RETURN_CODE -eq $RET_EAP_FAILED ]; then
358-
echo "access-reject; $T"
359-
if [ $VERBOSE -gt 0 ]; then
360-
cat $OUT
811+
# IPv6
812+
if [[ -z "$IP" ]] # IPv6 regex taken from https://www.regextester.com/96774
813+
then
814+
IP=$(echo "$ADDRESS" | grep -P '^(?:(?:(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4})):){6})(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4})):(?:(?:[0-9a-fA-F]{1,4})))|(?:(?:(?:(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9]))\.){3}(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])))))))|(?:(?:::(?:(?:(?:[0-9a-fA-F]{1,4})):){5})(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4})):(?:(?:[0-9a-fA-F]{1,4})))|(?:(?:(?:(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9]))\.){3}(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])))))))|(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4})))?::(?:(?:(?:[0-9a-fA-F]{1,4})):){4})(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4})):(?:(?:[0-9a-fA-F]{1,4})))|(?:(?:(?:(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9]))\.){3}(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])))))))|(?:(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4})):){0,1}(?:(?:[0-9a-fA-F]{1,4})))?::(?:(?:(?:[0-9a-fA-F]{1,4})):){3})(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4})):(?:(?:[0-9a-fA-F]{1,4})))|(?:(?:(?:(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9]))\.){3}(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])))))))|(?:(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4})):){0,2}(?:(?:[0-9a-fA-F]{1,4})))?::(?:(?:(?:[0-9a-fA-F]{1,4})):){2})(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4})):(?:(?:[0-9a-fA-F]{1,4})))|(?:(?:(?:(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9]))\.){3}(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])))))))|(?:(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4})):){0,3}(?:(?:[0-9a-fA-F]{1,4})))?::(?:(?:[0-9a-fA-F]{1,4})):)(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4})):(?:(?:[0-9a-fA-F]{1,4})))|(?:(?:(?:(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9]))\.){3}(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])))))))|(?:(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4})):){0,4}(?:(?:[0-9a-fA-F]{1,4})))?::)(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4})):(?:(?:[0-9a-fA-F]{1,4})))|(?:(?:(?:(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9]))\.){3}(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])))))))|(?:(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4})):){0,5}(?:(?:[0-9a-fA-F]{1,4})))?::)(?:(?:[0-9a-fA-F]{1,4})))|(?:(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4})):){0,6}(?:(?:[0-9a-fA-F]{1,4})))?::))))$')
361815
fi
362816

363-
garbage
364-
exit 1;
365-
fi
817+
if [[ -z "$IP" ]]
818+
then
819+
if [[ "$IPV6" == "YES" ]]
820+
then
821+
IP=$(dig +short "$ADDRESS" AAAA)
822+
else
823+
IP=$(dig +short "$ADDRESS")
824+
fi
825+
fi
366826

367-
# timeout return same error as above
368-
# timeout string "EAPOL test timed out"
369-
if [ $RETURN_CODE -eq $RET_RADIUS_NOT_AVAIL ]; then
370-
#echo "Timeout : Radius server is not available"
371-
echo "timeout; $T"
372-
if [ $VERBOSE -gt 0 ]; then
373-
cat $OUT
827+
# Sanity check: did we get an IP address?
828+
if [[ -z "$IP" ]]
829+
then
830+
echo "Hostname $ADDRESS could not be resolved to an IP address."
831+
return 1
374832
fi
375833

376-
garbage
377-
exit 2;
378-
fi
834+
# use specified directory for temp files if it was set
835+
if [[ -z "$TMPDIR" ]]
836+
then
837+
MYTMPDIR=$(mktemp -d /tmp/rad_eap_test.XXXXXX)
838+
else
839+
MYTMPDIR=$(mktemp -d $TMPDIR/rad_eap_test.XXXXXX)
840+
fi
379841

380-
# other case is probably error
381-
echo "Probably configuration error, examine config in \"$MYTMPDIR\". Return code: " $RETURN_CODE;
382-
exit 3;
842+
# configuration files
843+
CONF=$MYTMPDIR/tmp-$$.conf
844+
OUTFILE=$MYTMPDIR/tmp-$$.out
845+
846+
# RADIUS server cert
847+
if [[ -n "$WRITE_CERT" ]]
848+
then
849+
#EXTRA_EAPOL_ARGS="$EXTRA_EAPOL_ARGS -o $WRITE_CERT"
850+
# eapol_test has some strange behavior (bugs?) which seriously affect it
851+
# when using output file for writing server certs.
852+
# This SHOULD be fixed in eapol_test code.
853+
# Instead of using eapol_test to extract server certs,
854+
# we rather implemented our own server certificate extraction directly from eapol_test output
855+
856+
CERT_LOCATION="$WRITE_CERT"
857+
elif [[ -n "$GET_CERT" || -n "$CERTIFICATE_EXPIRY" ]]
858+
then
859+
#EXTRA_EAPOL_ARGS="$EXTRA_EAPOL_ARGS -o ${MYTMPDIR}/RADIUS_cert.pem"
860+
CERT_LOCATION="${MYTMPDIR}/RADIUS_cert.pem"
861+
fi
862+
863+
return 0
864+
}
865+
# ===========================================================================================
866+
# process command line options and their arguments
867+
# ===========================================================================================
868+
function process_options()
869+
{
870+
while getopts "H:P:S:u:p:t:m:s:e:t:M:i:d:j:k:a:A:l:2:x:vcNO:I:CTfhbB:n:gVX:6" opt
871+
do
872+
case "$opt" in
873+
H) ADDRESS=$OPTARG;;
874+
P) PORT=$OPTARG;;
875+
S) SECRET=$OPTARG;;
876+
u) USERNAME=$OPTARG;;
877+
p) PASSWORD=$OPTARG;;
878+
t) TIMEOUT=$OPTARG;;
879+
m) METHOD=$OPTARG;;
880+
v) VERBOSE=2;;
881+
c) VERBOSE=3;;
882+
s) SSID=$OPTARG;;
883+
e) EAP=$OPTARG;;
884+
M) MAC=$OPTARG;;
885+
i) CONN_INFO=$OPTARG;;
886+
k) USER_KEY=$OPTARG;;
887+
j) USER_CRT=$OPTARG;;
888+
a) CA_CRT=$OPTARG;;
889+
A) ANONYM_ID=$OPTARG;;
890+
l) KEY_PASS=$OPTARG;;
891+
2) PHASE2=$OPTARG;;
892+
N) CLEANUP=0;;
893+
x) SUBJ_MATCH=$OPTARG;;
894+
O) OPERATOR_NAME=$OPTARG;;
895+
I) NAS_IP_ADDRESS=$OPTARG;;
896+
C) REQUEST_CUI="YES";;
897+
T) CALLED_STATION_ID="YES";;
898+
f) FRAGMENT="YES";;
899+
b) GET_CERT="YES";;
900+
B) WRITE_CERT=$OPTARG;;
901+
n) TMPDIR=$OPTARG;;
902+
g) VERBOSE=4;;
903+
V) VERBOSE=1;;
904+
X) CERTIFICATE_EXPIRY=$OPTARG;;
905+
d) DOMAIN_MATCH="$OPTARG";;
906+
6) IPV6="YES";;
907+
h) usage;;
908+
\?) usage;;
909+
esac
910+
done
911+
shift $((OPTIND-1))
912+
}
913+
# ===========================================================================================
914+
# set the default configuration
915+
# ===========================================================================================
916+
function default_config()
917+
{
918+
# umask - make the files created readable only by the current user
919+
umask 0077
920+
921+
# path to eapol_test
922+
# try to determine the path automatically first
923+
EAPOL_PROG=$(which eapol_test)
924+
925+
if [[ -z "$EAPOL_PROG" ]]
926+
then
927+
# manually set the path if it wasn't determined automatically
928+
EAPOL_PROG=/usr/local/bin/eapol_test
929+
fi
383930

931+
# default verbosity
932+
VERBOSE=0
933+
934+
#default timeout
935+
TIMEOUT=5
936+
937+
#default mac address
938+
MAC="70:6f:6c:69:73:68"
939+
940+
# default connection info
941+
CONN_INFO="rad_eap_test + eapol_test"
942+
943+
# return codes
944+
RET_SUCC=3
945+
RET_EAP_FAILED=4
946+
RET_RADIUS_NOT_AVAIL=5
947+
RET_CERT_SUBJ_MISMATCH=6
948+
RET_CERT_CA_MISMATCH=7
949+
RET_CERT_CA_MISMATCH_INCOMPLETE=8
950+
RET_CERT_EXPIRED=9
951+
RET_DOMAIN_MISMATCH=10
952+
RET_EAPOL_TEST_FAILED=11
953+
954+
# exit codes
955+
EXIT_OK=0
956+
EXIT_WARNING=1
957+
EXIT_CRITICAL=2
958+
EXIT_UNKNOWN=3
959+
960+
# cleanup temporary files?
961+
CLEANUP=1
962+
}
963+
# ===========================================================================================
964+
# main function
965+
# ===========================================================================================
966+
function main()
967+
{
968+
default_config
969+
process_options "$@"
970+
check_settings
971+
972+
if [[ $? -ne 0 ]] # print usage if some settings were incorrect
973+
then
974+
usage
975+
fi
384976

977+
generate_config
978+
determine_return_code
979+
process_auth_result
980+
}
981+
# ===========================================================================================
982+
# set the handler for INT and TERM first
983+
trap "cleanup ; exit 2" INT TERM
984+
# ===========================================================================================
985+
# run the main function
986+
main "$@"
987+
# ===========================================================================================

0 commit comments

Comments
 (0)
Please sign in to comment.