forked from mdrights/macchiato
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathmacchiato
executable file
·285 lines (273 loc) · 9.32 KB
/
macchiato
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
#!/usr/bin/env bash
if [ -n "$MACCHIATO_DEBUG" ]; then
set -x
fi
scriptFile="$(readlink -f "${BASH_SOURCE[0]}")"
scriptDir="$(cd "$(dirname "$scriptFile")" && pwd)"
source "$scriptDir/functions.sh" || exit 1
devices=()
manualMode=''
confDir="$scriptDir/conf"
for arg; do
if [ -d "$arg" ]; then
confDir="$arg"
elif deviceExists "$arg"; then
devices+=("$arg")
elif [ "$arg" == '-h' -o "$arg" == '--help' ]; then
cat "$scriptDir/usage/main.usage"
exit 0
elif [ "$arg" == '--manual-help' ]; then
cat "$scriptDir/usage/manual.usage"
exit 0
elif [ "$arg" == '--manual' ]; then
manualMode='true'
break
else
echo "Don't know how to interpret argument: '$arg'"
exit 1
fi
done
# finalActions is an array containing interface names.
# For every element $interface in finalActions, the following variables should be defined:
# - finalActions_devHash($interface)_ouiList: Array of names of OUI classes
# - finalActions_devHash($interface)_ouiBlacklist: Array of OUIs to not use
# - finalActions_devHash($interface)_ending: Empty string or 3 bytes to use as ending
# - finalActions_devHash($interface)_useRandom: Empty string to use /dev/urandom, non-empty string to use /dev/random
# - finalActions_devHash($interface)_dontBringDown: Empty string if it should automatically be brought down, non-empty string to not bother
# This construction is there to work around the fact that Bash doesn't have multi-dimensional associative arrays.
# The devHash() is there because interface names may contain dashes and other weird characters.
# devHash is a function defined in functions.sh.
finalActions=()
if [ -n "$manualMode" ]; then
# Skip all arguments before --manual
while [ "$1" != '--manual' ]; do
shift
done
shift # Skip --manual itself
# I really wish getopts was better
if [ "$#" == 0 ]; then
echo 'Must specify device as first argument.'
echo "Use '--manual-help' for usage information."
exit 1
fi
manualDevice="$1"
if ! deviceExists "$manualDevice"; then
echo "Interface '$manualDevice' doesn't exist."
exit 1
fi
shift
ouiList=()
ouiBlacklist=()
ending=''
useRandom=''
while [ "$#" -gt 0 ]; do
if [ "$1" == '-o' -o "$1" == '--oui-class' ]; then
if [ "$#" -lt 2 ]; then
echo "Must specify a class after '$1'."
exit 1
fi
ouiList+=("$2")
shift
elif [ "$1" == '-b' -o "$1" == '--blacklist' ]; then
if [ "$#" -lt 2 ]; then
echo "Must specify an OUI after '$1'."
exit 1
fi
ouiBlacklist+=("$2")
shift
elif [ "$1" == '-e' -o "$1" == '--ending' ]; then
if [ -n "$ending" ]; then
echo "'-e|--ending' can only be specified once."
exit 1
elif [ "$#" -lt 2 ]; then
echo "Must specify the ending 3 bytes after '$1'."
exit 1
elif ! ouiIsValidBits "$2"; then
echo "Invalid MAC address ending: '$2'."
exit 1
fi
ending="$(ouiNormalizeBits "$2")"
shift
elif [ "$1" == '-r' -o "$1" == '--use-random' ]; then
if ! programExists xxd; then
echo "Cannot use /dev/random without having 'xxd' installed. Please install 'xxd' or use /dev/urandom."
exit 1
fi
useRandom='true'
elif [ "$1" == '-d' -o "$1" == '--no-down' ]; then
dontBringDown='true'
fi
shift
done
finalActions+=("$manualDevice")
eval "finalActions_$(devHash "$manualDevice")_ouiList=(\"\${ouiList[@]}\")"
eval "finalActions_$(devHash "$manualDevice")_ouiBlacklist=(\"\${ouiBlacklist[@]}\")"
eval "finalActions_$(devHash "$manualDevice")_ending=\"\$ending\""
eval "finalActions_$(devHash "$manualDevice")_useRandom=\"\$useRandom\""
eval "finalActions_$(devHash "$manualDevice")_dontBringDown=\"\$dontBringDown\""
else
if [ ! -d "$confDir" ]; then
echo "Configuration directory '$confDir' does not exist."
if [ "$#" -eq 0 ]; then # Probably first use; show something more helpful
echo 'Is this your first time using macchiato?'
echo 'You need to spend some time configuring it first.'
echo 'Read the README file for help.'
fi
exit 1
fi
confGlob=$(echo "$confDir"/*.sh)
if [ "$confGlob" == "$confDir/*.sh" ]; then
echo "No configuration files found matching pattern '$confDir/*.sh'."
exit 1
fi
# deviceMap is an associative array mapping interface names to the config file that should be used for them
declare -A deviceMap
for device in "${devices[@]}"; do
if ! deviceExists "$device"; then
echo "Interface '$device' doesn't exist."
exit 1
else
if [ -f "$confDir/$device.sh" ]; then
deviceMap["$device"]="$confDir/$device.sh"
elif [ -f "$confDir/_default.sh.sh" ]; then
deviceMap["$device"]="$confDir/_default.sh"
else
echo "Interface '$device' found, but its configuration file ('$confDir/$device.sh') was not found."
echo "A default configuration file ('$confDir/_default.sh') was not found either."
exit 1
fi
fi
done
if [ "${#devices[@]}" -eq 0 ]; then # No devices specified on command line; apply to all devices
for device in $(getDevicesList); do
if [ -f "$confDir/$device.sh" ]; then
deviceMap["$device"]="$confDir/$device.sh"
elif [ -f "$confDir/_default.sh" ]; then
deviceMap["$device"]="$confDir/_default.sh"
fi
done
if [ "${#deviceMap[@]}" -eq 0 ]; then
echo 'Nothing to do; exitting.'
exit 0
fi
fi
for device in "${!deviceMap[@]}"; do
ouiList=()
ouiBlacklist=()
ending=''
useRandom=''
dontBringDown=''
source "${deviceMap[$device]}"
if [ "$dontTouch" == 'true' ]; then
echo "[$device] Skipping interface (dontTouch = true)."
continue
elif ! declare -p ouiList &> /dev/null; then
echo "[$device] 'ouiList' not specified in config file: '${deviceMap[$device]}'."
exit 1
elif [ -n "$ending" ]; then
if ! ouiIsValidBits "$ending"; then
echo "[$device] The provided ending ('$ending') is not a valid 3-byte sequence in config file: '${deviceMap[$device]}'."
exit 1
fi
elif [ -n "$useRandom" ]; then
if ! programExists xxd; then
echo "[$device] Cannot use /dev/random without having 'xxd' installed. Please install 'xxd' or use /dev/urandom."
exit 1
fi
fi
finalActions+=("$device")
manualDeviceHash=""
eval "finalActions_$(devHash "$device")_ouiList=(\"\${ouiList[@]}\")"
eval "finalActions_$(devHash "$device")_ouiBlacklist=(\"\${ouiBlacklist[@]}\")"
eval "finalActions_$(devHash "$device")_ouiBlacklist=(\"\${ouiBlacklist[@]}\")"
eval "finalActions_$(devHash "$device")_ending=\"\$ending\""
eval "finalActions_$(devHash "$device")_useRandom=\"\$useRandom\""
eval "finalActions_$(devHash "$device")_dontBringDown=\"\$dontBringDown\""
done
fi
for device in "${finalActions[@]}"; do
eval "ouiList=(\"\${finalActions_$(devHash "$device")_ouiList[@]}\")"
eval "ouiBlacklist=(\"\${finalActions_$(devHash "$device")_ouiBlacklist[@]}\")"
eval "ending=\"\$finalActions_$(devHash "$device")_ending\""
eval "useRandom=\"\$finalActions_$(devHash "$device")_useRandom\""
eval "dontBringDown=\"\$finalActions_$(devHash "$device")_dontBringDown\""
# Turn OUI blacklist to lowercase and to use :'s instead of -'s
# in order to normalize them so that later on we can compare them directly
normalOuiBlacklist=()
for oui in "${ouiBlacklist[@]}"; do
normalOuiBlacklist+=("$(ouiNormalizeBits "$oui")")
done
finalOuiList=()
addOuiList() {
local oui
if [ ! -f "$scriptDir/oui/$1.sh" ]; then
echo "[$device] OUI list '$1' not found in configuration: '$scriptDir/oui/$1.sh'"
exit 1
fi
source "$scriptDir/oui/$1.sh"
eval "currentList=(\"\${oui_$1[@]}\")"
for oui in "${currentList[@]}"; do
finalOuiList+=("$oui")
done
}
if declare -p ouiList | grep -q '^declare -a'; then
if [ "${#ouiList[@]}" -eq 0 ]; then
echo "[$device] OUI list is empty; will keep current OUI prefix: '$(deviceGetOUI "$device")'"
finalOuiList+=("$(deviceGetOUI "$device")")
else
for subList in "${ouiList[@]}"; do
addOuiList "$subList"
done
fi
else
addOuiList "$ouiList"
fi
getGoodOui() {
local finalOui finalOuiBits keepGoing blacklistedOui
finalOui=''
keepGoing='true'
while [ -n "$keepGoing" ]; do
keepGoing=''
finalOui="$(getRandomChoice "${finalOuiList[@]}")"
if [ "${#normalOuiBlacklist[@]}" -gt 0 ]; then
finalOuiBits="$(ouiGetBits "$finalOui")"
for blacklistedOui in "${normalOuiBlacklist[@]}"; do
if [ "$blacklistedOui" == "$finalOui" ]; then
keepGoing='true'
break
fi
done
fi
done
echo "$finalOui"
}
finalOui="$(getGoodOui)"
if [ -z "$ending" ]; then
finalMACAddress="$(ouiGetBits "$finalOui"):$(getRandomMACEnding)"
else
finalMACAddress="$(ouiGetBits "$finalOui"):$ending"
fi
deviceWasUp=''
if [ -z "$dontBringDown" ]; then
if deviceIsUp "$device"; then
deviceWasUp='true'
echo "[$device] Bringing device down"
if ! deviceBringDown "$device"; then
echo "[$device] Failed to bring device down! Assigning a new MAC address is likely to fail as well."
fi
fi
fi
echo "[$device] Assigning spoofed MAC address: '$finalMACAddress' ($(ouiGetOrganization "$finalOui") - $(ouiGetDeviceName "$finalOui"))"
if ! deviceSetMAC "$device" "$finalMACAddress"; then
echo "[$device] WARNING: Failed to assign MAC address '$finalMACAddress'"
if [ "$EUID" -ne 0 ]; then
echo 'It looks like you are not running this script as root. You may want to change that.'
fi
fi
if [ -z "$dontBringDown" -a -n "$deviceWasUp" ]; then
echo "[$device] Bringing device back up"
if ! deviceBringUp "$device"; then
echo "[$device] Failed to bring device back up! Network connectivity will not work until it is back up."
fi
fi
done