89a963e96fbb5e02fa8385358a8c4df17381d540
[shellsnippets/shellsnippets.git] / posix / sysadmin / agents.sh
1 # -*- mode: sh -*-
2 #-
3 # Copyright © 2009, 2012, 2015, 2017, 2018, 2019
4 #       Thorsten Glaser <t.glaser@tarent.de>
5 #
6 # Provided that these terms and disclaimer and all copyright notices
7 # are retained or reproduced in an accompanying document, permission
8 # is granted to deal in this work without restriction, including un‐
9 # limited rights to use, publicly perform, distribute, sell, modify,
10 # merge, give away, or sublicence.
11 #
12 # This work is provided “AS IS” and WITHOUT WARRANTY of any kind, to
13 # the utmost extent permitted by applicable law, neither express nor
14 # implied; without malicious intent or gross negligence. In no event
15 # may a licensor, author or contributor be held liable for indirect,
16 # direct, other damage, loss, or other issues arising in any way out
17 # of dealing in the work, even if advised of the possibility of such
18 # damage or existence of a defect, except proven that it results out
19 # of said person’s immediate fault when using the work as intended.
20 #-
21 # /etc/profile.d/agents.sh (sourced by /etc/profile on Debian and derivates)
22 # • install gpg and ssh skeleton files
23 # • load gpg-agent and ssh-agent, unless already there
24 #
25 # You should comment out “use-ssh-agent” in /etc/X11/Xsession.options
26 # in order to use the shared per-user agent started from this script.
27
28 test -n "${USER_ID:-}" || USER_ID=$(id -u)
29
30 agents_sh_skelcheck() {
31         local fn=$1 md5s md5
32         test -s "/etc/skel/$fn" || return 0
33         shift
34         md5s=' '
35         while test -n "$1"; do
36                 md5s="$md5s$1 "
37                 shift
38         done
39         if test -s "$HOME/$fn"; then
40                 md5=$( (fgrep -v '$Id' "$HOME/$fn" | md5sum) 2>&1 || echo fail)
41                 case md5s in
42                 *" $md5 "*)
43                         # matches, remove the old file
44                         rm -f "$HOME/$fn"
45                         ;;
46                 *)
47                         # does not match (or error), don’t touch the file
48                         return 0
49                         ;;
50                 esac
51         fi
52
53         # file does not exist, is empty, or was removed by us
54         # install template
55         cp "/etc/skel/$fn" "$HOME/$fn"
56         chmod 0600 "$HOME/$fn"
57 }
58
59 if test -n "${KSH_VERSION:-}"; then
60         agents_sh_p() {
61                 local x
62                 for x in "$@"; do
63                         print -r -- "$x"
64                 done
65         }
66 else
67         agents_sh_p() {
68                 printf '%s\n' "$@"
69         }
70 fi
71
72 agents_sh_sshcheck() {
73         local x=$1 l1 l2 ofn fnd; shift
74
75         # read out other agents’ info and append to PPs
76         if test -d "$x/." && test -O "$x/." && \
77             test -s "$x/info2" && test -O "$x/info2"; then
78                 chmod -R go-rwx "$x"
79                 while IFS= read -r l1; do
80                         IFS= read -r l2 || break
81                         set -- "$@" "$l1" "$l2"
82                 done <"$x/info2"
83         fi
84
85         # create output dir/file
86         if test -d "$x/." && test -O "$x/."; then
87                 : ok, it already belongs to us
88         else
89                 rm -rf "$x"
90                 mkdir -p "$x" && test -d "$x/." && \
91                     test -O "$x/." && chmod -R go-rwx "$x" || \
92                     rm -rf "$x"
93         fi
94         if test -d "$x/." && test -O "$x/."; then
95                 ofn="$x/info2~"
96                 rm -f "$ofn"
97                 :>"$ofn"
98                 chmod 0600 "$ofn"
99         else
100                 ofn=/dev/null # but what can you do?
101         fi
102
103         # process agents
104         fnd=' '
105         while test $# -ge 2; do
106                 l1=$1; l2=$2; shift; shift
107                 test -n "$l2" || continue
108                 test -S "$l2" || continue
109                 test -n "$l1" || l1=unknown
110                 case $fnd in
111                 *" $l2 "*) ;;
112                 *)
113                         agents_sh_p "$l1" "$l2"
114                         if test x"$fnd" = x" "; then
115                                 SSH_AGENT_PID=$l1
116                                 SSH_AUTH_SOCK=$l2
117                         fi
118                         fnd="$fnd$l2 "
119                         ;;
120                 esac
121         done >"$ofn"
122
123         # no agent found?
124         while test x"$fnd" = x" "; do
125                 unset SSH_AUTH_SOCK SSH_AGENT_PID
126                 eval $(ssh-agent -s)
127                 test -n "$SSH_AUTH_SOCK" || break
128                 test -n "$SSH_AGENT_PID" || break
129                 test -S "$SSH_AUTH_SOCK" || break
130                 fnd=$SSH_AUTH_SOCK
131                 agents_sh_p "$SSH_AGENT_PID" "$SSH_AUTH_SOCK"
132         done >>"$ofn"
133
134         # finished info file v2
135         test x"$ofn" = x"/dev/null" || mv "$ofn" "$x/info2"
136
137         # did we have an agent, now?
138         if test x"$fnd" = x" "; then
139                 unset SSH_AUTH_SOCK SSH_AGENT_PID
140         else
141                 export SSH_AUTH_SOCK SSH_AGENT_PID
142         fi
143 }
144
145 agents_sh_checks() {
146         local x p
147
148         for x in "$HOME/.ssh" "$HOME/.gnupg"; do
149                 test -d "$x" && continue
150                 mkdir -p "$x"
151                 chmod 0700 "$x"
152         done
153
154         # extra arguments are list of MD5sums of old files shipped by us, to replace
155         agents_sh_skelcheck .gnupg/gpg.conf 2b7d7e47afb59ec164cf0ab512bb4ddc c8b796ed85a79e458a564645dcf38281 d5c4f4335d1eab08bfc9afe7ab494801 e6af3b74078a49db14f2f79fa82b7d3a 1f5d00be735cd1b1a57960c0128d2368 e51c210618d7dbc93c63e456d4dd4af1 7dfefaad0f417b7f50da1d80f8f0759b 07826f04f9e3b700e0f45da360d25877
156         agents_sh_skelcheck .gnupg/gpg-agent.conf e7e9b7940f07c3cb447b30da27914f8d
157         agents_sh_skelcheck .ssh/config
158
159         # handle ssh-agent connections
160         x="/dev/shm/.ssh-$USER_ID"
161         test -n "$SSH_AGENT_PID" || test -z "$SSH_CONNECTION" || \
162             SSH_AGENT_PID=fwd
163         agents_sh_sshcheck "$x" "$SSH_AGENT_PID" "$SSH_AUTH_SOCK"
164
165         # handle gpg-agent
166         : "${GNUPGHOME:=$HOME/.gnupg}"
167         if x=$(tty); then
168                 GPG_TTY=$x
169                 export GPG_TTY
170         fi
171         test -d "$GNUPGHOME" || return 0
172         export GNUPGHOME
173         p="$GNUPGHOME/gpg-agent-info2-$(hostname)"
174         # shortcut
175         if test -n "$GPG_AGENT_INFO" && test -S "${GPG_AGENT_INFO%%:*}" && \
176             gpg-agent 2>/dev/null; then
177                 export GPG_AGENT_INFO
178                 agents_sh_p "$GPG_AGENT_INFO" >"$p"
179                 return 0
180         fi
181         # already noted down
182         if test -s "$p" && IFS= read -r GPG_AGENT_INFO <"$p" && \
183             test -n "$GPG_AGENT_INFO" && test -S "${GPG_AGENT_INFO%%:*}" && \
184             gpg-agent 2>/dev/null; then
185                 export GPG_AGENT_INFO
186                 return 0
187         fi
188
189         unset GPG_AGENT_INFO
190         for x in 1 2; do
191                 # start as necessary in round 2
192                 test 2 = "$x" && eval $(gpg-agent --daemon --sh)
193                 while test -z "$GPG_AGENT_INFO"; do
194                         # is a gpg-agent already running?
195                         gpg-agent 2>/dev/null || break
196                         # divine its connection
197                         # 2.1/2.2-old default socket
198                         x=$GNUPGHOME/S.gpg-agent
199                         if test -S "$x"; then
200                                 GPG_AGENT_INFO=$x:0:1
201                                 break
202                         fi
203                         # 2.2-new different socket path
204                         x=$(gpgconf --list-dirs agent-socket) || x=
205                         if test -n "$x" && test -S "$x"; then
206                                 GPG_AGENT_INFO=$x:0:1
207                                 break
208                         fi
209                         echo >&2 "E: gpg-agent running but cannot access it"
210                         break
211                 done
212                 test -n "$GPG_AGENT_INFO" && \
213                     test -S "${GPG_AGENT_INFO%%:*}" && \
214                     gpg-agent 2>/dev/null && break
215         done
216
217         if test -n "$GPG_AGENT_INFO" && test -S "${GPG_AGENT_INFO%%:*}" && \
218             gpg-agent 2>/dev/null; then
219                 export GPG_AGENT_INFO
220                 agents_sh_p "$GPG_AGENT_INFO" >"$p"
221         else
222                 unset GPG_AGENT_INFO
223         fi
224 }
225
226 agents_sh_surround() {
227         local a=$-
228
229         # drop nounset
230         case $a in
231         *u*) set +u ;;
232         esac
233         case $a in
234         *e*) set +e ;;
235         esac
236
237         agents_sh_checks
238         unset -f agents_sh_skelcheck
239         unset -f agents_sh_p
240         unset -f agents_sh_sshcheck
241         unset -f agents_sh_checks
242         :
243         case $a in
244         *e*) set -e ;;
245         esac
246         case $a in
247         *u*) set -u ;;
248         esac
249 }
250 agents_sh_surround
251 unset -f agents_sh_surround
252 :