]>
Commit | Line | Data |
---|---|---|
1 | # WARN: not used in this repo, but gpgconf --list-dirs does not read | |
2 | # gpg-agent.conf to get socket path, see dev.gnupg.org/T3108 | |
3 | ||
4 | ### unset unwanted options that could be set in /etc/zshenv | |
5 | unsetopt SH_WORD_SPLIT KSH_ARRAYS | |
6 | ||
7 | ### check if su | |
8 | if [[ -v _sev_olduser && $_sev_olduser != $USERNAME ]] _sev_reset_shell= | |
9 | export _sev_olduser=$USERNAME | |
10 | ||
11 | ### exports for all new shells | |
12 | if [[ -v _sev_reset_shell || $SHLVL == 1 ]] { | |
13 | ## lang | |
14 | export CHARSET=UTF-8 | |
15 | export LANG=en_US.UTF-8 | |
16 | export LC_CTYPE=$LANG | |
17 | ||
18 | ## path | |
19 | # path and fpath should already be linked to PATH and FPATH | |
20 | # we don't want duplicates so turn on unique mode if it isn't already | |
21 | typeset -U path fpath | |
22 | # do not run more than once per session, even if resetting shell | |
23 | if [[ $SHLVL == 1 ]] { | |
24 | # take a backup before any customizations | |
25 | export _sev_sys_PATH=$PATH | |
26 | export _sev_sys_FPATH=$FPATH | |
27 | } | |
28 | # /usr/{pkg,local,games} are unix/bsdisms | |
29 | path=({~/,/,/usr/}sbin {~/,/,/usr/}bin /usr/pkg/{s,}bin /usr/X11R{7,6}/bin | |
30 | /usr/local/{s,}bin /usr/games) | |
31 | PATH=$PATH:$_sev_sys_PATH | |
32 | fpath=(${ZDOTDIR:-$HOME/.zsh}/functions/{*,Completions/*}(N)) | |
33 | # fpath is not exported by default | |
34 | export FPATH=$FPATH:$_sev_sys_FPATH | |
35 | # take another backup, explained in .zprofile | |
36 | typeset -U _backup_path | |
37 | _backup_path=("${path[@]}") | |
38 | ||
39 | ## xdg | |
40 | # TODO: check for and merge existing XDG env vars | |
41 | export XDG_CONFIG_HOME=~/etc | |
42 | export XDG_CONFIG_DIRS=~/.config:/usr/pkg/etc/xdg:/usr/local/etc/xdg:/etc/xdg | |
43 | export XDG_DATA_HOME=~/share | |
44 | export XDG_DATA_DIRS=~/.local/share:/usr/pkg/share:/usr/local/share:/usr/share | |
45 | export XDG_CACHE_HOME=~/tmp | |
46 | export XDG_RUNTIME_DIR=~/tmp | |
47 | if [[ -e $XDG_CONFIG_HOME/user-dirs.dirs ]] { | |
48 | source $XDG_CONFIG_HOME/user-dirs.dirs | |
49 | } | |
50 | ||
51 | ## create tmp link | |
52 | t=${TMPDIR:-/tmp}/.home-$LOGNAME | |
53 | if [[ ! -e $t ]] { | |
54 | mkdir -m 700 $t 2>/dev/null | |
55 | if [[ ! -d $t ]] { | |
56 | [[ -o interactive ]] && | |
57 | print -P "%F{red}!!! Can't create temp folder $t%f" | |
58 | [[ -h $XDG_RUNTIME_DIR ]] && unlink $XDG_RUNTIME_DIR 2>/dev/null | |
59 | [[ ! -e $XDG_RUNTIME_DIR ]] && mkdir $XDG_RUNTIME_DIR 2>/dev/null | |
60 | } | |
61 | } | |
62 | # allow opaque entries to override link creation | |
63 | [[ ! -e $XDG_RUNTIME_DIR ]] && ln -sf $t $XDG_RUNTIME_DIR 2>/dev/null | |
64 | unset t | |
65 | ||
66 | ## gpg forwarding | |
67 | # NOTE: while ssh automatically sets SSH_AUTH_SOCK with the ForwardSsh | |
68 | # directive, GPG must be forwarded manually. to support this, we | |
69 | # forward the restricted gpg-agent extra socket to the remote host | |
70 | # with a RemoteForward rule in ~/.ssh/config that uses the | |
71 | # _GNUPG_SOCK_* env vars. | |
72 | # to avoid conflicts with other ssh sessions where the same user is | |
73 | # connecting to the same host from different machines, gpg in each | |
74 | # environment should utilize its own forwarded socket, rather than | |
75 | # replace the sockets in GNUPGHOME which will be overridden on the | |
76 | # next connection. previously, you could provide a path to the agent | |
77 | # socket in GPG_AGENT_INFO, but that was deprecated in GPG v2.1. | |
78 | # instead, we must clone GNUPGHOME and replace the agent sockets | |
79 | # there with the forwarded one. | |
80 | # HACK: without SendEnv, which is disabled by default in most sshd configs, | |
81 | # there is no foolproof way to prevent race conditions or filename | |
82 | # collisions, pass the forward path to the remote host environment, | |
83 | # or even know if the forward path exists and is writable. we just | |
84 | # have to guess this path is good on the desination host, and assume | |
85 | # the newest matching socket is the correct one after connecting. in | |
86 | # theory we could occlude the ssh binary on PATH with an alias or | |
87 | # script that communicates with the remote host before opening a | |
88 | # shell, but that would open up too many edge cases where it wouldn't | |
89 | # work to make it worth the effort and extra overhead. | |
90 | # do not run more than once per session, even if resetting shell | |
91 | if [[ $SHLVL == 1 && -v commands[gpg] ]] { | |
92 | export _GNUPG_SOCK_DEST_BASE=/tmp/.gpg-agent-forward | |
93 | export _GNUPG_SOCK_DEST_EXT=$(date +%s).$RANDOM | |
94 | export _GNUPG_SOCK_DEST=$_GNUPG_SOCK_DEST_BASE.$_GNUPG_SOCK_DEST_EXT | |
95 | _sev_gpg_forward_dir=${GNUPGHOME:-~/.gnupg}/.ssh_forward | |
96 | s=($_GNUPG_SOCK_DEST_BASE*(N=oc[1])) | |
97 | # clean up forwards if its session is dead or we ask for it | |
98 | if [[ -d $_sev_gpg_forward_dir ]] { | |
99 | find $_sev_gpg_forward_dir -type d -mindepth 1 -maxdepth 1 | | |
100 | while read -r x; do | |
101 | # NOTE: the only way we can get here is if we are SHLVL 1. if | |
102 | # our own pid already has a dir, it is most likely stale, | |
103 | # or something is very broken—assume the former. | |
104 | p=$(basename $x) | |
105 | if [[ -v _sev_gpg_forward_clean || $$ == $p ]] || | |
106 | ! kill -0 $p 2>/dev/null; then | |
107 | find $x -mindepth 1 -maxdepth 1 | while read -r y; do | |
108 | unlink $y | |
109 | done | |
110 | rmdir $x | |
111 | fi | |
112 | done | |
113 | unset x p y | |
114 | } | |
115 | # create new forward dir | |
116 | if [[ -n $s && -v SSH_CLIENT ]] { | |
117 | export _sev_gpg_forwarded= | |
118 | mkdir -pm700 $_sev_gpg_forward_dir | |
119 | h=$_sev_gpg_forward_dir/$$ | |
120 | mkdir -pm700 $h | |
121 | # XXX: is it safe to link scdaemon socket? can its name be changed? | |
122 | for x in S.scdaemon gpg.conf gpg-agent.conf sshcontrol \ | |
123 | pubring.kbx trustdb.gpg private-keys-v1.d crls.d; do | |
124 | ln -s ${GNUPGHOME:-~/.gnupg}/$x $h | |
125 | done | |
126 | export GNUPGHOME=$h | |
127 | unset h | |
128 | for x in $(gpgconf --list-dirs | grep 'agent-.*-\?socket:'); do | |
129 | # dirs are prefixed and percent-encoded—strip and decode | |
130 | # https://stackoverflow.com/a/64312099 | |
131 | x=${${x/#agent-*socket:/}//(#b)%([[:xdigit:]](#c2))/${(#):-0x$match[1]}} | |
132 | if [[ ! -v orig ]] { | |
133 | mv $s $x | |
134 | orig=$x | |
135 | } else { | |
136 | ln -s $orig $x | |
137 | } | |
138 | done | |
139 | unset x orig | |
140 | } | |
141 | unset s | |
142 | ||
143 | # what we will forward if we start a new ssh connection | |
144 | # NOTE: do this after setting up GNUPGHOME to pick up new socket path; | |
145 | # if already connected over SSH, extra should be the remote one | |
146 | export _GNUPG_SOCK_SRC=$(gpgconf --list-dirs agent-extra-socket) | |
147 | } else { | |
148 | # required for RemoteForward to not error out if the vars are unset | |
149 | export _GNUPG_SOCK_SRC=/nonexistent | |
150 | export _GNUPG_SOCK_DEST=/nonexistent | |
151 | } | |
152 | ||
153 | ## gpg agent | |
154 | # always try to start agent during setup | |
155 | if [[ SHLVL == 1 ]] { | |
156 | gpg-connect-agent /bye >/dev/null 2>&1 | |
157 | [[ $? -ne 0 && -o interactive ]] && | |
158 | print -P "%F{red}!!! Can't communicate with GPG agent%f" | |
159 | } | |
160 | # set up tty if it isn't, and we're interactive or in xorg & not forwarded | |
161 | # do not run more than once per session, even if resetting shell | |
162 | if [[ -v commands[gpg-connect-agent] && | |
163 | ! -v _sev_gpg_forward && ! -v GPG_TTY && | |
164 | ( -o interactive || -v DISPLAY ) ]] { | |
165 | export GPG_TTY=$(tty) | |
166 | export PINENTRY_USER_DATA=USE_TTY=$((!${+DISPLAY})) | |
167 | gpg-connect-agent UPDATESTARTUPTTY /bye >/dev/null 2>&1 | |
168 | } | |
169 | ||
170 | ## ssh agents | |
171 | # NOTE: preferred order of agents to check: okcagent, gnupg, openssh | |
172 | # first block takes care of okcagent and openssh, second gnupg | |
173 | [[ -o interactive ]] && print -nP "%F{blue}>>>%f SSH: %F{green}" | |
174 | if [[ ! -v SSH_AUTH_SOCK && ( -v commands[okc-ssh-agent] || | |
175 | ( -v commands[ssh-agent] && ! -v commands[gpg] ) ) ]] { | |
176 | okc=${commands[okc-ssh-agent]:+okc-} | |
177 | agentfile=~/tmp/${okc}ssh-agent-exports | |
178 | typeset sock= | |
179 | typeset -i pid= | |
180 | if [[ -f $agentfile ]] { | |
181 | IFS=$'\0' read -r sock pid <$agentfile | |
182 | } | |
183 | if [[ -S $sock && $pid > 0 ]] && kill -0 $pid; then | |
184 | [[ -o interactive ]] && echo "Reusing agent PID $pid" | |
185 | export SSH_AUTH_SOCK=$sock | |
186 | export SSH_AGENT_PID=$pid | |
187 | else | |
188 | # TODO: ensure ssh-agent path looks legit | |
189 | # to avoid unsafe eval? | |
190 | # NOTE: no way around doing redirection like this I think | |
191 | e=${okc}ssh-agent | |
192 | if [[ -o interactive ]] { | |
193 | eval `$e` | |
194 | } else { | |
195 | eval `$e` >/dev/null 2>&1 | |
196 | } | |
197 | echo -n $SSH_AUTH_SOCK$'\0'$SSH_AGENT_PID >!$agentfile | |
198 | fi | |
199 | unset okc agentfile sock pid | |
200 | } elif [[ ! -v SSH_AUTH_SOCK && -v commands[gpg] ]] { | |
201 | # since gpg agent was started above, we just have to export and notify | |
202 | if [[ -o interactive ]] { | |
203 | if [[ -v _sev_gpg_forwarded ]] { | |
204 | echo 'Remote GPG agent' | |
205 | } else { | |
206 | gpg-connect-agent /subst /serverpid \ | |
207 | '/echo GPG agent PID ${get serverpid}' /bye | |
208 | } | |
209 | } | |
210 | export SSH_AUTH_SOCK=$(gpgconf --list-dirs agent-ssh-socket) | |
211 | } elif [[ -v SSH_AUTH_SOCK ]] { | |
212 | [[ -o interactive ]] && echo "Preconfigured agent" | |
213 | } else { | |
214 | [[ -o interactive ]] && print -P "%F{red}No agent available" | |
215 | } | |
216 | } | |
217 | ||
218 | ||
219 | ### load site-specific | |
220 | if [[ -f ~/.zshenv.local ]] { source ~/.zshenv.local } | |
221 | ||
222 | ### source .zprofile | |
223 | # if we used su, without --login, let's run zprofile ourselves | |
224 | # XXX: system zprofile is not run | |
225 | if [[ -v _sev_reset_shell || $SHLVL == 1 ]] source ~/.zprofile | |
226 | ||
227 | # vim: set et sts=4 sw=4 ts=8 tw=79 : |