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
+ #
11
15
# 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=$(( 0 x$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
52
580
fi
53
- fi
581
+ else
582
+ echo " password=\" $PASSWORD \" " >> $CONF
583
+ fi
54
584
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
56
589
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
61
594
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.
64
608
-P <port> - Port of radius server
65
609
-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 )
68
612
-p <password> - Password
69
613
-t <timeout> - Timeout (default is 5 seconds)
70
614
-m <method> - Method (WPA-EAP | IEEE8021X )
71
615
-v - Verbose (prints decoded last Access-accept packet)
72
- -c - Prints all packets decoded
616
+ -c - Prints all packets decoded
73
617
-s <ssid> - SSID
74
618
-e <method> - EAP method (PEAP | TLS | TTLS | LEAP)
75
619
-M <mac_addr> - MAC address in xx:xx:xx:xx:xx:xx format
76
620
-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.
78
622
-k <user_key_file> - user certificate key file
79
623
-l <user_key_file_password> - password for user certificate key file
80
624
-j <user_cert_file> - user certificate file
@@ -85,300 +629,359 @@ Parameters :
85
629
-O <domain.edu.cctld> - Operator-Name value in domain name format
86
630
-I <ip address> - explicitly specify NAS-IP-Address
87
631
-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
88
641
-h - show this message
89
- " >&2 ;
90
- exit 1;
642
+ " >&2
643
+ exit 1
91
644
}
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
92
656
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
139
662
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
144
668
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
149
674
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
154
681
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
159
688
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
165
693
fi
166
694
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
170
699
fi
171
700
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
175
705
fi
176
706
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
180
711
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
186
717
fi
187
- fi
188
718
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
193
727
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
304
732
fi
305
- fi
306
- }
307
733
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
309
739
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
311
745
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
313
753
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
322
758
fi
323
- fi
324
759
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
325
765
766
+ if [[ -z " $SSID " ]]
767
+ then
768
+ SSID=" eduroam" ;
769
+ fi
326
770
327
- RETURN_CODE=$?
771
+ if [[ -z " $PHASE2 " ]]
772
+ then
773
+ PHASE2=" MSCHAPV2"
774
+ fi
328
775
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
331
781
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
333
787
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
337
792
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
342
800
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 "
349
805
fi
350
806
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}$ ' )
354
810
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})))?::))))$')
361
815
fi
362
816
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
366
826
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
374
832
fi
375
833
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
379
841
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
383
930
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
384
976
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