-
Notifications
You must be signed in to change notification settings - Fork 1
/
mlaps_client.sh
executable file
·365 lines (317 loc) · 11 KB
/
mlaps_client.sh
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
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
#!/usr/bin/env bash
# Settings
#set -ex # commented out for now because exiting on every error seems harsh
export PATH="/usr/local/bin/:/usr/local/sbin/:/opt/homebrew/bin:/opt/homebrew/sbin:$PATH"
# Constants
SUPPORT="$SUPPORTPATH"
ADMIN_USER_NAME="admin"
ADMIN_USER_HOME="/Users/$ADMIN_USER_NAME"
MLAPS_ENDPOINT="https://mlaps.$YOURCOMPANY.com/api" # MLAPS HOST
MLAPS_CA="com.$YOURCOMPANY.mlaps" # MLAPS_CA
CA_FILE="$SUPPORT/mlaps-ca.pem" # Path to CA file
PW_FILE="$SUPPORT/mlaps-password" # Path to Backup Password File
CSR_FILE="$SUPPORT/mlaps-csr" # Path to CSR
CRT_FILE="$SUPPORT/mlaps-crt" # Path to CRT
KEY_FILE="$SUPPORT/mlaps-key" # Path to KEY
UPDATE_ID_FILE="$SUPPORT/mlaps-updateid" # Path to KEY
LOG_FILE="$LOGGINGFILE" # Path to logfile
SN=$(system_profiler SPHardwareDataType | awk '/Serial/{ print $4 } ') # Serial number
HN=$(hostname) # Hostname
SUBJ="/C=$CERT_COUNTRY/O=LAPS/OU=${SN}" # CSR Subject
PID_FILE="/var/run/mlaps.pid" # Path to the pid file
# Settings
CURL_N_RETRIES=5 # the number of times curl will try again
CURL_MAX_T=10 # the time curl will wait for the response before trying again
CURL_DELAY=0 # some setting to tweak
CURL_MAX_RETRY_TIME=60 # how long it takes for curl to give up
N_RETRIES=4
T_RETRIES=3
# Templates
#JSON_ENROLLMENT_FORMAT='{"csr":"%c", "sn":"%s", "hn":"%h"}' # Format for json enrollment payload
#JSON_PASS_RESPONSE_FORMAT='{"Success_Status":"%s", "Password":"%p", "updateSessionID":"%i"}' # Format for json password response payload
#JSON_PASS_UPDATE_FORMAT='{"res":"$r", "updateSessionID":"%i"}'
#JSON_PASS_CHECKIN_FORMAT='{"sn":"%s", "hn":"%h"}'
#Runtime Data
UPDATEID=""
function panic(){
# rm pidfile
cleanupPid
errlog "Unexpected exit!: (line $(caller))"
exit 255
}
# cleanup pidfile if process is not running anymore
function cleanupPid(){
pid=$(cat $PID_FILE)
if test -n $pid && ! ps -p $pid ; then
rm $PID_FILE
fi
}
function cleanEnrollment(){
shred "$CSR_FILE" "$CRT_FILE" "$KEY_FILE"
rm -f "$CSR_FILE" "$CRT_FILE" "$KEY_FILE"
}
# format and write a line into /var/log/jamflog
function jamflog(){
local MSG=""
MSG+=$(date -u "+%a %b %d %H:%M:%S ")
MSG+="$HN"
MSG+=" LAPS[$$]: "
MSG+="$@"
echo "$MSG" >> "$LOG_FILE"
}
function errlog(){
if [ -z "$1" ]; then
jamflog "ERR: (line $(caller)) There has been an error"
else
jamflog "ERR: $@"
fi
}
# trap \
# "{ /usr/bin/rm -f ${PW_FILE} ${EXP_FILE} ; exit 1 ; }" \
# SIGINT SIGTERM ERR EXIT
# SIGEMT SIGINFO
trap 'panic' \
SIGHUP SIGINT SIGQUIT SIGILL SIGTRAP SIGABRT SIGFPE \
SIGKILL SIGBUS SIGSEGV SIGSYS SIGPIPE SIGALRM SIGTERM SIGURG \
SIGSTOP SIGTSTP SIGCONT SIGCHLD SIGTTIN SIGTTOU SIGIO SIGXCPU \
SIGXFSZ SIGVTALRM SIGPROF SIGWINCH SIGUSR1 SIGUSR2
trap 'errlog' ERR # Traps errors that aren't handled and logs them without exiting
# create pid file ($pid > file)
# exit if exists
# shlock -f $PID_FILE -p $$ || cleanupPid
#Function to generate csr and send to /enroll
function enroll(){
jamflog "Generating CSR & KEY"
local CSR=$(openssl req \
-new \
-nodes \
-newkey rsa:2048 \
-keyout "$KEY_FILE" \
-subj "$SUBJ" | tee "$CSR_FILE" | openssl base64 -e ; exit ${PIPESTATUS[0]})
if [ $? ]; then
jamflog "Generated CSR for enrollment!"
else
errlog "Failed to generate CSR for enrollment"
cleanEnrollment
return 1
fi
local PAYLOAD="{\"csr\":\"$CSR\", \"sn\":\"$SN\", \"hn\":\"$HN\"}"
(curl \
--cacert $CA_FILE \
--request POST \
--url "$MLAPS_ENDPOINT/enroll" \
--retry $CURL_N_RETRIES \
--max-time $CURL_MAX_T \
--retry-delay $CURL_DELAY \
--retry-max-time $CURL_MAX_RETRY_TIME \
-H 'Content-Type: application/json' \
--data "$PAYLOAD" | jq -r '.response' | tee "$CRT_FILE"; exit ${PIPESTATUS[0]} ;)
if [ $? ]; then
jamflog "Downloaded cert"
local crt_hash=$(openssl md5 <(openssl x509 -noout -modulus -in "$CRT_FILE"))
local key_hash=$(openssl md5 <(openssl rsa -noout -modulus -in "$KEY_FILE"))
if [ "$crt_hash" == "$key_hash" ]; then
jamflog "certificate has been downloaded without error"
return 0
else
errlog "certificate downloaded with errors, cleaning up broken files"
#cleanEnrollment
return 1
fi
else
jamflog "Failed to download cert, cleaning up broken files"
#cleanEnrollment
return 1
fi
}
#Function which will make a request to /checkin to find out whether to update pw
function checkin(){
local PAYLOAD="{\"sn\":\"$SN\", \"hn\":\"$HN\"}"
local CHECKIN_DATA=$(curl \
--cacert $CA_FILE \
--request POST \
--cert "$CRT_FILE" \
--key "$KEY_FILE" \
--data "$PAYLOAD" \
--url "$MLAPS_ENDPOINT/checkin" \
--retry $CURL_N_RETRIES \
--max-time $CURL_MAX_T \
--retry-delay $CURL_DELAY \
--retry-max-time $CURL_MAX_RETRY_TIME \
--header 'Content-Type: application/json')
local RESPONSE=$(echo "$CHECKIN_DATA")
local STATUS=$(echo "$RESPONSE" | jq '.response')
UPDATEID=$(echo "$RESPONSE" | jq -r '.updateSessionID')
if [ "$UPDATEID" != "null" ] && [ -n "$UPDATEID" ]; then
echo "$UPDATEID" > "$UPDATE_ID_FILE"
fi
#if [ -z "$UPDATEID" ]; then
# echo "$UPDATEID" > "$UPDATE_ID_FILE"
#fi
if [ "$STATUS" == "\"ok\"" ]; then
jamflog "Password is still current -> nothing to do"
return 0
elif [ "$STATUS" == "\"update\"" ]; then
jamflog "Updating Password..."
retry "$N_RETRIES" "$T_RETRIES" set_pw && return 0 || return 99
else
errlog "Could not read server response"
errlog "$RESPONSE"
fi
}
function send_pw(){
#$(printf "$" "$1" "$2" "$UPDATEID")
local PAYLOAD="{\"Success_Status\":\"$1\", \"Password\":\"$2\", \"updateSessionID\":\"$UPDATEID\"}"
local PW_DATA=$(curl \
--request POST \
--cacert $CA_FILE \
--cert "$CRT_FILE" \
--key "$KEY_FILE" \
--data "$PAYLOAD" \
--url "$MLAPS_ENDPOINT/password" \
--retry $CURL_N_RETRIES \
--max-time $CURL_MAX_T \
--retry-delay $CURL_DELAY \
--retry-max-time $CURL_MAX_RETRY_TIME \
--header 'Content-Type: application/json')
local RESPONSE=$(echo "$PW_DATA" | jq -rc '.response')
if [ "$RESPONSE" == "ok" ] ; then
jamflog "Server received first admin Password payload"
return 0
elif [ "$RESPONSE" == "Password not expired" ] ; then
jamflog "Server reported that Password is not expired, aborting setting new password and cleaning up updateid"
rm "$UPDATE_ID_FILE"
exit 5
else
errlog "Could not transmit admin Password payload"
errlog "$RESPONSE"
return 1
fi
}
function gen_passwd(){
local succ=0
local pw=""
while [ $succ -eq 0 ]
do
pw=$(LC_ALL=C tr -dc A-Za-z0-9_ < /dev/urandom | head -c 16 | xargs)
#pw='jogb4GmFroee__MDh'
#check if pw has a selected special character
if [[ $pw =~ ['!@#$%^&*()_+'] ]]; then
#check if pw has a number
if [[ $pw =~ [0-9] ]]; then
succ=1
#lastly check if pw has 2 identical characters next to each other
local prev="${pw:0:1}"
for i in $(seq 2 ${#pw});do
local cur=${pw:((i-1)):1}
if [ "$prev" == $cur ]; then
#echo "found duplicate"
succ=0
fi
prev=$cur
done
else
# one condition not met, rerun loop
succ=0
fi
else
# one condition not met, rerun loop
succ=0
fi
done
echo $pw
#echo $(openssl rand -base64 10 | tr -d OoIi1lLS | head -c12;echo)
}
function send_pw_res(){
jamflog $1
local PAYLOAD="{\"res\":\"$1\", \"updateSessionID\":\"$UPDATEID\"}"
local PW_DATA=$(curl \
--request POST \
--cacert $CA_FILE \
--cert "$CRT_FILE" \
--key "$KEY_FILE" \
--data "$PAYLOAD" \
--url "$MLAPS_ENDPOINT/password-confirm"\
--retry $CURL_N_RETRIES \
--max-time $CURL_MAX_T \
--retry-delay $CURL_DELAY \
--retry-max-time $CURL_MAX_RETRY_TIME \
--header 'Content-Type: application/json')
if [ $? -ne 0 ]; then
errlog "Failed to send confirmation to server"
fi
}
# wip
function set_pw(){
# generate new pw
if ! local newpw=$(gen_passwd); then
send_pw 1 "Failed to generate new pw: $newpw"
errlog "Failed to generate new pw: $newpw"
return 10
fi
# inform server about new pw to be set
send_pw 0 "$newpw"
if [[ $? -eq 1 ]]; then
errlog "Failed to send first password payload to server"
return 12
fi
local res1=$(echo "-passwd /Users/admin $newpw " | dscl . );
if [ "$res1" = "Goodbye" ]; then
jamflog "Password Change successful, send confirmation"
#read -p "Press enter to continue"
send_pw_res "Success"
#delete updatesessionid to persist the successful pw change
rm "$UPDATE_ID_FILE"
return 0
else
send_pw_res "Failed to set new pw: $res1"
return 13
fi
}
function main(){
for var in "$@"
do
if [ "$var" == "-v" ]; then
set -x
fi
done
#check/wait for a internet connection
while ! curl --cacert $CA_FILE -Is https://mlaps.$YOURCOMPANY.com &> /dev/null ; do
sleep 1
done
shlock -f $PID_FILE -p $$ || cleanupPid
if [ -s "$UPDATE_ID_FILE" ]; then
jamflog "Found valid updatesession id..."
UPDATEID="$(<"$UPDATE_ID_FILE")"
set_pw
else
if [ -e "$CRT_FILE" ];then
checkin
else
enroll && checkin || errlog "Failed to enroll"
fi
fi
rm $PID_FILE
}
function retry {
local max=$1 ; shift
local delay=$1 ; shift
local n=1
jamflog "Trying to run \"$@\""
while true; do
"$@" && break || {
if [[ $n -lt $max ]]; then
((n++))
errlog "Failed to run \"$@\": attempt $n/$max:"
sleep $delay;
else
errlog "\"$@\" has failed after $n attempts."
return 1
fi
}
done
}
# for testing...(works like if __name__==main in python
[[ "${#BASH_SOURCE[@]}" -eq 1 ]] \
&& retry "$N_RETRIES" "$T_RETRIES" main $@ \
|| { echo "Happy testing!" ; }