-
Notifications
You must be signed in to change notification settings - Fork 0
/
bash_sshagent
251 lines (215 loc) · 7.83 KB
/
bash_sshagent
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
#! /bin/bash
# Decide when and where to run an SSH agent.
# See the discussion in bink/bash_sshagent.md.
# NOTE: SSH_AUTH_SOCK, when created by a forwarded agent, is only
# good for the duration of the ssh command that set it. Once
# that command exits, the SSH_AUTH_SOCK value is no longer valid.
# It does no good to save this value and use it later.
# NOTE: `scp` does not forward the agent, so commands like
# scp remote:file local-file
# could cause problems if there is no agent running on 'remote'
########################## macOS countermeasures ###############################
# Starting in Mojave (?), there's an ssh-agent process that is started as
# soon as any processes of note are launched, like Terminal. It seems
# Dock-started programs do it, but it might be for some other reason.
# This ssh-agent is identifiable because it uses an undocumented -l argument!
#
# This shows where it comes from:
# launchctl list com.openssh.ssh-agent
# and if you "/usr/bin/csrutil disable" from recovery
# you can
# launchctl unload -w /System/Library/LaunchAgents/com.openssh.ssh-agent.plist
# sudo launchctl disable system/com.openssh.ssh-agent
# but as soon as you "/usr/bin/csrutil enable" it comes back.
#
# It's not even possible to kill the thing, because as soon as a new
# process is launched from the Dock, like Terminal, a new
# "ssh-agent -l" is started.
#
# So, the trick is to detected the SSH_AUTH_SOCK created by launchd,
# and null it out, so it's not used. See __my_ssh_auth_sock_valid_p
# below.
###############################################################################
# How to use MY_DEBUG_SSHAGENT:
# Server:
# - put this into /etc/ssh/sshd_config
# AcceptEnv MY_DEBUG_SSHAGENT
# - send HUP signal to sshd process
# Client:
# $ MY_DEBUG_SSHAGENT=xxx ssh -o SendEnv=MY_DEBUG_SSHAGENT gremlin date
[ "${MY_DEBUG_SSHAGENT-}" ] && _debug_on sshagent
## or unconditionally:
#_debug_on sshagent
## or MAX debug
#set -x
# For machines that are not running at least BASH version 4, just silently
# exit, since the code here uses >=4 features.
if [ "${BASH_VERSINFO[0]}" -lt 4 ]; then
return 0
fi
# Allow scripts to turn this off.
[ "${INHIBIT_SSH_AGENT-}" ] && return
# Abort when using sudo
[ "${__my_ssh_agent_user-}" ] &&
[ "${USER}" != "${__my_ssh_agent_user}" ] &&
return
if ! type -p __my_ssh_run_agent_p > /dev/null; then
# Silently return, as this machine likely is running a BASH
# before version 4.
return
fi
# Info saved about an agent we start here goes into this file:
__my_agent_info="$HOME/tmp/agent.info"
# Return true if there is at least 1 key in the agent.
function __my_ssh_have_one_identity_p {
if ssh-add -l &>/dev/null; then
_debug sshagent have one identity: YES
return 0
else
_debug sshagent have one identity: NO
return 1
fi
}
# Return true if SSH_AUTH_SOCK is connected to an agent which
# is alive. "ssh-add -l" will return 0 on success, 1 on failure
# (no identities) and 2 if the ssh-add is unable to contact the agent.
function __my_ssh_auth_sock_valid_p {
local n bad
# See note above about macOS countermeasures
if [[ $SSH_AUTH_SOCK =~ com\.apple\.launchd ]]; then
SSH_AUTH_SOCK=
echo NOTE: IGNORE launchd SSH_AUTH_SOCK 1>&2
return 1
fi
declare -p __my_bad_agent_identities 2>/dev/null | \
grep '^declare \-a' &>/dev/null &&
n=${#__my_bad_agent_identities[@]} &&
[ $n -gt 0 ] && {
while [ $n -gt 0 ]; do
n=$(( n - 1 ))
bad="${__my_bad_agent_identities[$n]}"
if ssh-add -E md5 -l 2>/dev/null | grep "$bad" &>/dev/null; then
_debug sshagent found bad identity, ignore current agent
return 1
fi
done
}
# If the file does not exist and is not a socket, then we're done.
[ ! -S "${SSH_AUTH_SOCK-}" ] &&
_debug sshagent NO SSH_AUTH_SOCK &&
return 1
local ret=0
ssh-add -l &> /dev/null || ret=$?
[ $? -eq 2 ] &&
_debug sshagent FAIL: ssh-add could not contact agent &&
return 1
_debug sshagent SUCCESS: ssh-add could contact agent
return 0
}
# Return true if an agent is accessible, false otherwise.
function __my_connected_to_ssh_agent_p {
if [ "${SSH_AGENT_PID-}" ]; then
_debug sshagent Looking for PID $SSH_AGENT_PID
if ! ps -p $SSH_AGENT_PID > /dev/null; then
_debug sshagent SSH_AGENT_PID not found
# no process, but since the variable exists, return false
return 1
fi
# have SSH_AGENT_PID, make sure it's valid...
fi
__my_ssh_auth_sock_valid_p && return 0
# no agent running or accessible
_debug sshagent NO AGENT FOUND
return 1
}
function __my_ssh_agent_prompt_string {
__my_connected_to_ssh_agent_p || return 0
if __my_ssh_have_one_identity_p; then
echo -n "@"
else
echo -n "-"
fi
}
###############################################################################
# Exit this script if it is not running on a machine I sit at.
# The Windows machines are in this list because I use RDP to get to them,
# and that qualifies as "sitting at", since RDP doesn't take over any
# environment from the originating host.
#
# This is not done earlier in this script because the functions defined
# above are used in the construction of the prompt string.
__my_ssh_run_agent_p || return
###############################################################################
# Now that we're actually going to use an agent, do some sanity checks
if [ ! "${__my_ssh_default_identity-}" ]; then
echo ABORT: __my_ssh_default_identity is not defined 1>&2
return
fi
if [ ! "${__my_ssh_default_identity_md5-}" ]; then
echo ABORT: __my_ssh_default_identity_md5 is not defined 1>&2
return
fi
# Make sure these two variables have the same number of space-separated
# values.
if [ ${#__my_ssh_default_identity[@]} != ${#__my_ssh_default_identity_md5[@]} ]
then
cat <<EOF
ABORT: __my_ssh_default_identity and __my_ssh_default_identity_md5
have a different number of elements. Fix this to enable
agent handling.
EOF
return
fi
if [ ! -d $HOME/tmp ]; then
mkdir $HOME/tmp
# By default it should be secure against snooping, since we put
# agent info there.
chmod 700 $HOME/tmp
fi
# ...end sanity checks
###############################################################################
# Either connect to an exiting SSH agent or start a new one.
function __my_ssh_initialize_agent {
if [ -f "${__my_agent_info}" ]; then
source ${__my_agent_info} > /dev/null
__my_connected_to_ssh_agent_p && return 0
echo NOTE: ${__my_agent_info} is stale, removing 1>&2
rm -f ${__my_agent_info}
fi
# if we get here, we could not connect to an agent, so start one
ssh-agent -s > ${__my_agent_info} &&
source ${__my_agent_info} > /dev/null &&
echo NOTE: start new SSH agent: PID=$SSH_AGENT_PID 1>&2
chmod 600 ${__my_agent_info}
}
__my_connected_to_ssh_agent_p || __my_ssh_initialize_agent
# This is always defined on windows, right???
__my_on_windows=${COMSPEC-}
# Ensure default identities are added to the running agent.
function __my_ssh_ensure_default_identities {
local temp
local n=${#__my_ssh_default_identity[@]} i=0 file md5
_debug sshagent DEBUG: n=$n
[ "${MY_DEBUG_SSHAGENT-}" ] && ssh-add -E md5 -l 1>&2
while [ $i -lt $n ]; do
file="${__my_ssh_default_identity[$i]}"
md5="${__my_ssh_default_identity_md5[$i]}"
_debug sshagent looking for $file $md5
if ssh-add -E md5 -l 2>/dev/null | grep $md5 &>/dev/null ; then
_debug sshagent found $md5
else
echo NOTE: adding identity $file1>&2
if [ "${__my_on_windows}" ]; then
temp="${file}.windows"
if [ ! -f "$temp" ] || [ "$file" -nt "$temp" ]; then
cvt -f -u < "$file" > "$temp"
chmod 600 "$temp"
fi
file="$temp"
fi
ssh-add $file || echo COULD NOT ADD IDENTITY 1>&2
fi
i=$(( i + 1 ))
done
}
__my_ssh_ensure_default_identities