# WARN: not used in this repo, but gpgconf --list-dirs does not read # gpg-agent.conf to get socket path, see dev.gnupg.org/T3108 ### unset unwanted options that could be set in /etc/zshenv unsetopt SH_WORD_SPLIT KSH_ARRAYS ### check if su if [[ -v _sev_olduser && $_sev_olduser != $USERNAME ]] _sev_reset_shell= export _sev_olduser=$USERNAME ### exports for all new shells if [[ -v _sev_reset_shell || $SHLVL == 1 ]] { ## lang export CHARSET=UTF-8 export LANG=en_US.UTF-8 export LC_CTYPE=$LANG ## path # path and fpath should already be linked to PATH and FPATH # we don't want duplicates so turn on unique mode if it isn't already typeset -U path fpath # do not run more than once per session, even if resetting shell if [[ $SHLVL == 1 ]] { # take a backup before any customizations export _sev_sys_PATH=$PATH export _sev_sys_FPATH=$FPATH } # /usr/{pkg,local,games} are unix/bsdisms path=({~/,/,/usr/}sbin {~/,/,/usr/}bin /usr/pkg/{s,}bin /usr/X11R{7,6}/bin /usr/local/{s,}bin /usr/games) PATH=$PATH:$_sev_sys_PATH fpath=(${ZDOTDIR:-$HOME/.zsh}/functions/{*,Completions/*}(N)) # fpath is not exported by default export FPATH=$FPATH:$_sev_sys_FPATH # take another backup, explained in .zprofile typeset -U _backup_path _backup_path=("${path[@]}") ## xdg # TODO: check for and merge existing XDG env vars export XDG_CONFIG_HOME=~/etc export XDG_CONFIG_DIRS=~/.config:/usr/pkg/etc/xdg:/usr/local/etc/xdg:/etc/xdg export XDG_DATA_HOME=~/share export XDG_DATA_DIRS=~/.local/share:/usr/pkg/share:/usr/local/share:/usr/share export XDG_CACHE_HOME=~/tmp export XDG_RUNTIME_DIR=~/tmp if [[ -e $XDG_CONFIG_HOME/user-dirs.dirs ]] { source $XDG_CONFIG_HOME/user-dirs.dirs } ## create tmp link t=${TMPDIR:-/tmp}/.home-$LOGNAME if [[ ! -e $t ]] { mkdir -m 700 $t 2>/dev/null if [[ ! -d $t ]] { [[ -o interactive ]] && print -P "%F{red}!!! Can't create temp folder $t%f" [[ -h $XDG_RUNTIME_DIR ]] && unlink $XDG_RUNTIME_DIR 2>/dev/null [[ ! -e $XDG_RUNTIME_DIR ]] && mkdir $XDG_RUNTIME_DIR 2>/dev/null } } # allow opaque entries to override link creation [[ ! -e $XDG_RUNTIME_DIR ]] && ln -sf $t $XDG_RUNTIME_DIR 2>/dev/null unset t ## gpg forwarding # NOTE: while ssh automatically sets SSH_AUTH_SOCK with the ForwardSsh # directive, GPG must be forwarded manually. to support this, we # forward the restricted gpg-agent extra socket to the remote host # with a RemoteForward rule in ~/.ssh/config that uses the # _GNUPG_SOCK_* env vars. # to avoid conflicts with other ssh sessions where the same user is # connecting to the same host from different machines, gpg in each # environment should utilize its own forwarded socket, rather than # replace the sockets in GNUPGHOME which will be overridden on the # next connection. previously, you could provide a path to the agent # socket in GPG_AGENT_INFO, but that was deprecated in GPG v2.1. # instead, we must clone GNUPGHOME and replace the agent sockets # there with the forwarded one. # HACK: without SendEnv, which is disabled by default in most sshd configs, # there is no foolproof way to prevent race conditions or filename # collisions, pass the forward path to the remote host environment, # or even know if the forward path exists and is writable. we just # have to guess this path is good on the desination host, and assume # the newest matching socket is the correct one after connecting. in # theory we could occlude the ssh binary on PATH with an alias or # script that communicates with the remote host before opening a # shell, but that would open up too many edge cases where it wouldn't # work to make it worth the effort and extra overhead. # do not run more than once per session, even if resetting shell if [[ $SHLVL == 1 && -v commands[gpg] ]] { export _GNUPG_SOCK_DEST_BASE=/tmp/.gpg-agent-forward export _GNUPG_SOCK_DEST_EXT=$(date +%s).$RANDOM export _GNUPG_SOCK_DEST=$_GNUPG_SOCK_DEST_BASE.$_GNUPG_SOCK_DEST_EXT _sev_gpg_forward_dir=${GNUPGHOME:-~/.gnupg}/.ssh_forward s=($_GNUPG_SOCK_DEST_BASE*(N=oc[1])) # clean up forwards if its session is dead or we ask for it if [[ -d $_sev_gpg_forward_dir ]] { find $_sev_gpg_forward_dir -type d -mindepth 1 -maxdepth 1 | while read -r x; do # NOTE: the only way we can get here is if we are SHLVL 1. if # our own pid already has a dir, it is most likely stale, # or something is very broken—assume the former. p=$(basename $x) if [[ -v _sev_gpg_forward_clean || $$ == $p ]] || ! kill -0 $p 2>/dev/null; then find $x -mindepth 1 -maxdepth 1 | while read -r y; do unlink $y done rmdir $x fi done unset x p y } # create new forward dir if [[ -n $s && -v SSH_CLIENT ]] { export _sev_gpg_forwarded= mkdir -pm700 $_sev_gpg_forward_dir h=$_sev_gpg_forward_dir/$$ mkdir -pm700 $h # XXX: is it safe to link scdaemon socket? can its name be changed? for x in S.scdaemon gpg.conf gpg-agent.conf sshcontrol \ pubring.kbx trustdb.gpg private-keys-v1.d crls.d; do ln -s ${GNUPGHOME:-~/.gnupg}/$x $h done export GNUPGHOME=$h unset h for x in $(gpgconf --list-dirs | grep 'agent-.*-\?socket:'); do # dirs are prefixed and percent-encoded—strip and decode # https://stackoverflow.com/a/64312099 x=${${x/#agent-*socket:/}//(#b)%([[:xdigit:]](#c2))/${(#):-0x$match[1]}} if [[ ! -v orig ]] { mv $s $x orig=$x } else { ln -s $orig $x } done unset x orig } unset s # what we will forward if we start a new ssh connection # NOTE: do this after setting up GNUPGHOME to pick up new socket path; # if already connected over SSH, extra should be the remote one export _GNUPG_SOCK_SRC=$(gpgconf --list-dirs agent-extra-socket) } else { # required for RemoteForward to not error out if the vars are unset export _GNUPG_SOCK_SRC=/nonexistent export _GNUPG_SOCK_DEST=/nonexistent } ## gpg agent # always try to start agent during setup if [[ SHLVL == 1 ]] { gpg-connect-agent /bye >/dev/null 2>&1 [[ $? -ne 0 && -o interactive ]] && print -P "%F{red}!!! Can't communicate with GPG agent%f" } # set up tty if it isn't, and we're interactive or in xorg & not forwarded # do not run more than once per session, even if resetting shell if [[ -v commands[gpg-connect-agent] && ! -v _sev_gpg_forward && ! -v GPG_TTY && ( -o interactive || -v DISPLAY ) ]] { export GPG_TTY=$(tty) export PINENTRY_USER_DATA=USE_TTY=$((!${+DISPLAY})) gpg-connect-agent UPDATESTARTUPTTY /bye >/dev/null 2>&1 } ## ssh agents # NOTE: preferred order of agents to check: okcagent, gnupg, openssh # first block takes care of okcagent and openssh, second gnupg [[ -o interactive ]] && print -nP "%F{blue}>>>%f SSH: %F{green}" if [[ ! -v SSH_AUTH_SOCK && ( -v commands[okc-ssh-agent] || ( -v commands[ssh-agent] && ! -v commands[gpg] ) ) ]] { okc=${commands[okc-ssh-agent]:+okc-} agentfile=~/tmp/${okc}ssh-agent-exports typeset sock= typeset -i pid= if [[ -f $agentfile ]] { IFS=$'\0' read -r sock pid <$agentfile } if [[ -S $sock && $pid > 0 ]] && kill -0 $pid; then [[ -o interactive ]] && echo "Reusing agent PID $pid" export SSH_AUTH_SOCK=$sock export SSH_AGENT_PID=$pid else # TODO: ensure ssh-agent path looks legit # to avoid unsafe eval? # NOTE: no way around doing redirection like this I think e=${okc}ssh-agent if [[ -o interactive ]] { eval `$e` } else { eval `$e` >/dev/null 2>&1 } echo -n $SSH_AUTH_SOCK$'\0'$SSH_AGENT_PID >!$agentfile fi unset okc agentfile sock pid } elif [[ ! -v SSH_AUTH_SOCK && -v commands[gpg] ]] { # since gpg agent was started above, we just have to export and notify if [[ -o interactive ]] { if [[ -v _sev_gpg_forwarded ]] { echo 'Remote GPG agent' } else { gpg-connect-agent /subst /serverpid \ '/echo GPG agent PID ${get serverpid}' /bye } } export SSH_AUTH_SOCK=$(gpgconf --list-dirs agent-ssh-socket) } elif [[ -v SSH_AUTH_SOCK ]] { [[ -o interactive ]] && echo "Preconfigured agent" } else { [[ -o interactive ]] && print -P "%F{red}No agent available" } } ### load site-specific if [[ -f ~/.zshenv.local ]] { source ~/.zshenv.local } ### source .zprofile # if we used su, without --login, let's run zprofile ourselves # XXX: system zprofile is not run if [[ -v _sev_reset_shell || $SHLVL == 1 ]] source ~/.zprofile # vim: set et sts=4 sw=4 ts=8 tw=79 :