]> git.sev.monster Git - dotfiles.git/blob - base/.zshenv
gpg: expand forwarding, add default-recipient-self
[dotfiles.git] / base / .zshenv
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 :
This page took 0.072469 seconds and 4 git commands to generate.