From 7ddca96da6e30ad4b5a05c4804bf11911bd671eb Mon Sep 17 00:00:00 2001 From: sev Date: Fri, 18 Feb 2022 20:13:38 -0600 Subject: [PATCH] in development --- base/.zlogin | 2 +- base/.zlogout | 5 +- base/.zprofile | 387 ++++++++++++++++-- .../functions/Prompts/prompt_arrows_setup | 47 ++- base/.zshenv | 239 +---------- base/.zshrc | 44 +- bin/ykman-oath-reload | 17 + gpg/gpg-agent.conf | 2 +- gpg/gpg.conf | 2 +- 9 files changed, 463 insertions(+), 282 deletions(-) create mode 100644 bin/ykman-oath-reload diff --git a/base/.zlogin b/base/.zlogin index 6fbf574..40b4b8a 100644 --- a/base/.zlogin +++ b/base/.zlogin @@ -1,4 +1,4 @@ ### load site-specific if [[ -f ~/.zlogin.local ]] { source ~/.zlogin.local } -# vim: set et sts=4 sw=4 ts=8 tw=79 : +# vim: et sts=4 sw=4 ts=8 tw=79 diff --git a/base/.zlogout b/base/.zlogout index 3b09ce5..1e50611 100644 --- a/base/.zlogout +++ b/base/.zlogout @@ -17,10 +17,13 @@ if [[ $SHLVL == 1 && $GNUPGHOME =~ '.ssh_forward/\d+$' ]] { rm -f $_GNUPG_SOCK_DEST_BASE*(N=) } +# remove xdg runtime +rm -rf $XDG_RUNTIME_DIR 2>/dev/null + [[ -o interactive ]] && echo logout clear ### load site-specific if [[ -f ~/.zlogout.local ]] { source ~/.zlogout.local } -# vim: set et sts=4 sw=4 ts=8 tw=79 : +# vim: et sts=4 sw=4 ts=8 tw=79 diff --git a/base/.zprofile b/base/.zprofile index 7a7fc10..a6a548f 100644 --- a/base/.zprofile +++ b/base/.zprofile @@ -1,28 +1,367 @@ +# NOTE: +# our .zprofile is expensive, so we keep track of what has been run already, +# and only set up what is necessary. additionally, we want to ensure that our +# environment is set up as early as possible, so we also source .zprofile in +# .zshenv for new non-login shells. +# +# these issues are handled by using these methods: +# * the parent shell that starts the user's session after logging in to some +# graphical environments may not be a login shell—due to misconfiguration +# or otherwise—which means .zprofile is not ran and the environment is not +# properly configured for any child processes. +# * some desktop environments/graphical terminal emulators will start new +# terminal windows with login shells, which runs .zprofile every time and +# leads to noticably slow startup times. +# * switching users without wiping the environment will result in paths and +# variables intended for the old user being used for the new user. while +# this may be considered an edge-case that should not be supported, there +# are legitimate reasons to want to do this, and in any case the shell +# should not choke or cause unexpected problems should it happen anyway. + +### detect cygwin +[[ $OSTYPE =~ (cygwin|msys)* ]] && is_cygwin= + ### exports -## reset PATH to prevent /etc/zprofile from changing it -# some distros put non-interactive PATH in env and interactive PATH in profile; -# we want to make sure to always use ours regardless -if [[ -v _backup_path ]] { - path=("${_backup_path[@]}" "${path[@]}") - export PATH - unset _backup_path +## lang +export CHARSET=UTF-8 +export LANG=en_US.UTF-8 +export LC_CTYPE=$LANG + +## msys2 +if [[ -v MSYSTEM && ! -v _sev_setup_msys2 ]] { + # path mangling exclusions for gpg-connect-agent + # https://www.gnupg.org/documentation/manuals/gnupg/Controlling-gpg_002dconnect_002dagent.html + export MSYS2_ARG_CONV_EXCL="${MSYS2_ARG_CONV_EXCL:+$MSYS2_ARG_CONV_EXCL;}\ +/echo;/let ;/definq;/datafile ;/showdef;/cleardev;/sendfd ;/recvfd;/open ;\ +/close ;/showopen;/serverpid;/sleep;/hex;/nohex;/decode;/nodecode;/subst;\ +/nosubst;/while ;/if ;/end;/run ;/bye;/help" + # ssh called from mingw64-git attempts to convert path to Windows, and + # causes it to choke. paths are converted to *nix before exporting and + # will work if cygwin ssh is installed (default). + export MSYS2_ENV_CONV_EXCL=_GNUPG_SOCK_ + export _sev_setup_msys2= +} + + +## path +# NOTE: we utilize the fact that unique arrays keep the first occurrence and +# remove any further occurences to capture elements from the old PATH +# that we did not anticipate and shift them to the front, since they are +# probably important to the system +if [[ ! -v _sev_setup_path || -o login ]] { + typeset -a winpath + if [[ -v is_cygwin ]] { + windir=$(cygpath -uW) + sysdir=$(cygpath -uS) + winpath=($sysdir $windir $sysdir/Wbem + $sysdir/WindowsPowerShell/v1.0 + $sysdir/../SysWOW64 $sysdir/../SysWOW64/Wbem + $sysdir/../SysWOW64/WindowsPowerShell/v1.0) + for (( i = 1; i <= $#winpath; i++ )) { + winpath[$i]=${winpath[$i]:a} + } + unset windir sysdir + } + typeset -U path fpath + # add as many generic paths as possible to keep the order we want + # NOTE: /usr/{local,pkg,games} are unix/bsdisms + syspath=("$path[@]") + path=({~/,/,/usr/}sbin /opt/{s,}bin /usr/local/{s,}bin /usr/pkg/{s,}bin + /usr/X11R{7,6}/bin /usr/games {~/,/,/usr/}bin) + ((ulen=$#path)) + [[ -v is_cygwin ]] && path=("$path[@]" "$winpath[@]") + ((wlen=$#path)) + path=("$path[@]" "$syspath[@]") + # remove nonexistent and duplicate paths + for (( i = 1; i <= $#path; i++ )) { + if [[ ! -e $path[$i] ]] { + path[$i]=() + ((i <= ulen)) && ((ulen--)) + ((i <= wlen)) && ((wlen--)) + ((i--)) + continue + } + if [[ ! -v is_cygwin ]] || (( i <= ulen )) { continue } + # Windows only: remove cygwin-ified duplicates case-insensitively + c=$(cygpath -u -- ${(L)path[$i]}) + for (( j = i + 1; j <= $#path; j++ )) { + if [[ $c == $(cygpath -u -- ${(L)path[$j]}) ]] { + path[$j]=() + # NOTE: likelihood of our defined windows path being duplicate + # is low, but just in case + ((j <= wlen)) && ((wlen--)) + ((j--)) + } + } + unset c + } + (( wlen > 0 )) && path=("${(@)path[wlen + 1, -1]}" "${(@)path[1, wlen]}") + unset winpath syspath ulen wlen i j + # include our zsh dir in fpath. unlike above, we always prefer our paths + fpath=(${ZDOTDIR:-~/.zsh}/functions/{*,Completions/*}(N) "$fpath[@]") + # FPATH is not exported by default + export FPATH + typeset +U path fpath + export _sev_setup_path= +} + +## xdg +if [[ ! -v _sev_setup_xdg ]] { + # merge with any existing dirs and remove duplicates using unique arrays + # NOTE: include and then remove .config and .local/share to ensure it is + # not present in the array if it was added before we got to it + typeset -UT XDG_CONFIG_DIRS xdg_config_dirs + typeset -UT XDG_DATA_DIRS xdg_data_dirs + export XDG_CONFIG_HOME=$HOME/etc + xdg_config_dirs=($XDG_CONFIG_HOME $HOME/.config + {/opt,/usr/local,/usr/pkg,}/etc/xdg + "${XDG_CONFIG_DIRS:+${xdg_config_dirs[@]}}") + export XDG_CONFIG_DIRS=${XDG_CONFIG_DIRS#$XDG_CONFIG_HOME} + export XDG_DATA_HOME=$HOME/share + xdg_data_dirs=($XDG_DATA_HOME $HOME/.local/share + /{opt,usr/local,usr/pkg,usr}/share + "${XDG_DATA_DIRS:+${xdg_data_dirs[@]}}") + export XDG_DATA_DIRS=${XDG_DATA_DIRS#$XDG_DATA_HOME} + # use our custom tmp for cache and runtime + export XDG_CACHE_HOME=~/tmp + # NOTE: it's intentional to keep the same runtime dir for the whole session + # and not create a new one if a new login shell is spawned, since the + # spec calls for the same dir to be utilized for each "session". + export XDG_RUNTIME_DIR=~/tmp/xdg.$$ +} + +## temp +if [[ ! -v _sev_setup_tmp ]] { + t=${TMPDIR:-${TEMP:-${TMP:-/tmp}}}/.home-$LOGNAME + if [[ ! -e $t ]] { + mkdir -m700 $t 2>/dev/null + if [[ ! -d $t ]] { + [[ -o interactive ]] && + print -P "%F{red}!!! Can't create temp folder $t%f" + # fallback bare directories + [[ -h $XDG_CACHE_HOME ]] && unlink $XDG_CACHE_HOME 2>/dev/null + [[ ! -e $XDG_CACHE_HOME ]] && mkdir -m700 $XDG_CACHE_HOME 2>/dev/null + } + } + if [[ -e $t ]] { + export TMPDIR=$t TEMP=$t TMP=$t + # [re-]create link to our tmp if safe + [[ -h $XDG_CACHE_HOME || ! -e $XDG_CACHE_HOME ]] && + ln -sf $t $XDG_CACHE_HOME 2>/dev/null + } else { + # ensure proper tmp vars, e.g. msys2 does not set TMPDIR + : ${TMPDIR:=${TEMP:-${TMP:-/tmp}}} + : ${TEMP:=$TMPDIR} + : ${TMP:=$TMPDIR} + } + unset t + export _sev_setup_tmp= +} + +## xdg +if [[ ! -v _sev_setup_xdg ]] { + # create xdg runtime dir + # NOTE: spec says the dir should only exist for the lifetime of the + # session, so if there is already something there it is likely stale + # or something is very broken—assume the former. + [[ -e $XDG_RUNTIME_DIR ]] && rm -rf $XDG_RUNTIME_DIR 2>/dev/null && + mkdir -m700 $XDG_RUNTIME_DIR 2>/dev/null + # source user dirs after other vars + [[ -e $XDG_CONFIG_HOME/user-dirs.dirs ]] && + emulate sh -c "source $XDG_CONFIG_HOME/user-dirs.dirs" + export _sev_setup_xdg= } -## common -export EDITOR=vim -export PAGER=less -## grep -# XXX: deprecated in GNU -export GREP_OPTIONS=--color=auto -## histfile -export HISTFILE=~/.histfile -export HISTSIZE=1000 -export SAVEHIST=1000 -## python -export PYTHONSTARTUP=~/.pythonrc -## perl -[[ -v commands[perl] ]] && eval $(perl -I $XDG_DATA_HOME/perl5/lib/perl5 -Mlocal::lib=$XDG_DATA_HOME/perl5 2>/dev/null) - -### load site-specific + +## gpg forwarding +# NOTE: while ssh manages its auth sock in its protocol when ForwardSsh is +# enabled, GPG must be forwarded manually over Unix socket. 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. +# NOTE: since Unix sockets are not supported under Windows, this will not work +# under msys, cygwin, mingw, etc. +# HACK: without SendEnv, which is disabled by default in most sshd configs, +# there is no foolproof way to prevent race conditions via filename +# collisions or to pass the desired forward path to the remote host +# environment. we just have to guess the path we choose is good on the +# desination, 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 would allow us to communicate with the +# remote host before opening a shell, so that we can have the host +# communicate back to the client where it wants a socket created or ask +# the host if the path the client wants to use is writable. however, this +# would open up too many edge cases where it wouldn't work or be clunky +# (e.g. asking for password twice) to make it worth it. +if [[ ! -v _sev_setup_gpg ]] { + # helper function for decoding gpgconf socket paths + function _socketpath { + # dirs are percent-encoded + # https://stackoverflow.com/a/64312099 + local x=${1//(#b)%([[:xdigit:]](#c2))/${(#):-0x$match[1]}} + # remove \r from Windows paths + if [[ -v commands[cygpath] ]] { + x=$(cygpath -u -- ${x/%$'\r'} 2>/dev/null) + } + echo $x + } + + if [[ ! -v _sev_gpg_forwarded && -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 + export _sev_gpg_forward_dir=${GNUPGHOME:-~/.gnupg}/.ssh_forward + # 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 have not been + # forwarded before. 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 + } + + # find our forwarded socket + s=($_GNUPG_SOCK_DEST_BASE*(N=oc[1])) + if [[ -n $s && -v SSH_CLIENT ]] { + # create new forward dir + 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 + x=$(_socketpath ${x/#agent-*socket:}) + 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=$(_socketpath \ + $(gpgconf --list-dirs agent-extra-socket)) + } else { + # required for RemoteForward to not error out if the vars are unset + [[ ! -v _GNUPG_SOCK_SRC ]] && export _GNUPG_SOCK_SRC=/nonexistent + [[ ! -v _GNUPG_SOCK_DEST ]] && export _GNUPG_SOCK_DEST=/nonexistent + } + + ## gpg agent + if [[ -v commands[gpg-connect-agent] ]] { + [[ -o interactive ]] && print -nP '%F{blue}>>>%f GPG agent: %F{green}' + gpg-connect-agent /bye >/dev/null 2>&1 + if [[ $? -ne 0 ]] { + [[ -o interactive ]] && + print -P '%F{red}Error communicating with GPG agent%f' + } elif [[ ! -v _sev_gpg_forward && ! -v GPG_TTY && + ( -o interactive || -v DISPLAY ) ]] { + # if we aren't forwarded, set up tty if it isn't and we're + # in an interactive session + export GPG_TTY=$(tty) + export PINENTRY_USER_DATA=USE_TTY=$((!${+DISPLAY})) + gpg-connect-agent UPDATESTARTUPTTY /bye >/dev/null 2>&1 + [[ -o interactive ]] && + print -P "Updated TTY%f" + } else { + [[ -o interactive ]] && + print -P 'Ready%f' + } + } + + ## ssh agent + # 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 ]] && print -P "Reusing agent PID $pid%f" + export SSH_AUTH_SOCK=$sock + export SSH_AGENT_PID=$pid + else + e=${okc}ssh-agent + # TODO: ensure ssh-agent path looks legit to avoid unsafe eval? + # XXX: doesn't appear to be any other way to handle redirection. + # because eval needs to write to current scope environment + # subshells can't be used to capture output and print. + if [[ -o interactive ]] { + eval `$e` + print -nP '%f' + } 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 + } + print -nP '%f' + } + export SSH_AUTH_SOCK=$(_socketpath \ + $(gpgconf --list-dirs agent-ssh-socket)) + } elif [[ -v SSH_AUTH_SOCK ]] { + [[ -o interactive ]] && print -P 'Preconfigured agent%f' + } else { + [[ -o interactive ]] && print -P '%F{red}No agent available%f' + } + + ## cleanup + # unset gpg helper + unfunction _socketpath + + ## perl local lib + [[ -v commands[perl] && -d $XDG_DATA_HOME/perl5/lib/perl5 ]] && + eval $(perl -I$XDG_DATA_HOME/perl5/lib/perl5 + -Mlocal::lib=$XDG_DATA_HOME/perl5 2>/dev/null) +} + +unset is_cygwin + +# load site-specific if [[ -f ~/.zprofile.local ]] { source ~/.zprofile.local } -# vim: set et sts=4 sw=4 ts=8 tw=79 : +# vim: et sts=4 sw=4 ts=8 tw=79 diff --git a/base/.zsh/functions/Prompts/prompt_arrows_setup b/base/.zsh/functions/Prompts/prompt_arrows_setup index 08f3646..8719a12 100644 --- a/base/.zsh/functions/Prompts/prompt_arrows_setup +++ b/base/.zsh/functions/Prompts/prompt_arrows_setup @@ -22,33 +22,36 @@ EOF local prompt_arrows_vimode= function prompt_arrows_setup() { + prompt_opts=( cr sp percent subst ) + + # TODO: check for color terminal. never will use B&W term but still a XXX - local n p _p s r c d u + local m n p s r c d u #TODO remove n? - m="${1:-2line}" # Mode - n="${2:-white}" # Normal - p="${3:-green}" # Prompt - s="${4:-random}" # Ssh prompt - r="${5:-red}" # uRgent - c="${6:-blue}" # Comment - d="${7:-yellow}" # Dir - u="${8:-darkyellow}" # Unwritable + m=${1:-2line} # Mode + n=${2:-white} # Normal + p=${3:-green} # Prompt + s=${4:-random} # Ssh prompt + r=${5:-red} # uRgent + c=${6:-blue} # Comment + d=${7:-yellow} # Dir + u=${8:-darkyellow} # Unwritable # [AC]CURSED COMMAND # concatenate the numerical values of each letter of the hostname, # moduloing by the number of possible colors. - generated=$(uname -n | od -An -td1 | awk -F' ' \ - "{for (i=1; i<=NF; i++) {sum+=1; sum*=\$i; sum%=`echotc Co`} print sum}") - [[ "$p" = 'random' ]] && p=$generated - [[ "$s" = 'random' ]] && s=$generated + local generated=$(uname -n | od -An -td1 | awk -F' ' \ + "{for (i=1; i<=NF; i++) {sum+=1; sum*=\$i; sum%=$(echotc Co)} print sum}") + [[ $p = random ]] && p=$generated + [[ $s = random ]] && s=$generated # use ssh color if connected - p="${${SSH_CLIENT+$s}:-$p}" + [[ -v SSH_CLIENT ]] && p=$s local clock="%F{$n}%T%f" dirvcs="%F{$d}"'${vcs_info_msg_0_:-%~}'"%f" # XXX: linux only local battery=/sys/class/power_supply/BAT0/capacity - [[ -e "$battery" ]] && clock+=" `cat $battery`%%" + [[ -e "$battery" ]] && clock+=' $(cat $battery)%%' local vicol='%F{${${prompt_arrows_vimode:+'"$r"'}:-'"$c"'}}' local hist="%F{$n}%h" case "$m" { @@ -66,7 +69,7 @@ function prompt_arrows_setup() { ;; } PROMPT="$PROMPT%(1j,%F{$c}%j,)$vicol%#%F{$p}%n%F{$n}@%F{$p}%2m%(?..%F{$n}/%F{$r}\$?)%F{$n}: " - POSTEDIT="`print -P "%F{$n}%f"`" + POSTEDIT="$(print -P "%F{$n}%f")" autoload -Uz vcs_info zstyle ':vcs_info:git*' formats "%c%u%%F{$p}%r/%b%%F{$n}/%%F{$d}%S%%f" zstyle ':vcs_info:git*' actionformats "%%F{$r}(%a)%f %c%u%%F{$p}%r/%b%f/%%F{$d}%S%f" @@ -95,5 +98,17 @@ function prompt_arrows_preexec { print -Pn "%F{red}<<<%f " } +function prompt_arrows_preview { + if (( ! $#* )); then + prompt_preview_theme arrows 2line + print '\n' + prompt_preview_theme arrows right + print + else + prompt_preview_theme arrows "$@" + print + fi +} + prompt_arrows_setup "$@" # vim: set et fenc=utf-8 ft=zsh sts=4 sw=4 ts=8 tw=0 : diff --git a/base/.zshenv b/base/.zshenv index 11cded7..7787a93 100644 --- a/base/.zshenv +++ b/base/.zshenv @@ -1,227 +1,18 @@ -# 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 +[[ -f ~/.zshenv.local ]] && source ~/.zshenv.local + +# manually source .zprofile if we're not a login shell and it's a good idea +# see comments in .zprofile for rationale +if [[ ! -o login ]] { + if [[ -v _sev_olduser && $_sev_olduser != $USERNAME ]] { + # set up some user-dependent stuff since we switched users + _sev_switched_users= source ${ZDOTDIR:-~}/.zprofile + } elif [[ $SHLVL == 1 ]] { + # ensure profile is loaded for toplevel shells; should hopefully be 1 + # under Xorg or cron, which is where this would be most useful + source ${ZDOTDIR:-~}/.zprofile } } +export _sev_olduser=$USERNAME - -### 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 : +# vim: et sts=4 sw=4 ts=8 tw=79 diff --git a/base/.zshrc b/base/.zshrc index 7890dad..488abbd 100644 --- a/base/.zshrc +++ b/base/.zshrc @@ -4,24 +4,39 @@ setopt NO_BEEP NO_CLOBBER setopt AUTO_CD CDABLE_VARS ## completion setopt GLOB_COMPLETE -## line +## globbing setopt EXTENDED_GLOB GLOB_DOTS MARK_DIRS NOMATCH NUMERIC_GLOB_SORT -## prompt -setopt PROMPT_SUBST ## jobs -setopt AUTO_CONTINUE +setopt AUTO_CONTINUE LONG_LIST_JOBS ## history setopt NO_HIST_SAVE_BY_COPY HIST_IGNORE_DUPS SHARE_HISTORY HIST_REDUCE_BLANKS +### imports +autoload -Uz zmv +autoload -Uz zmathfunc && zmathfunc + +### exports +## common +export EDITOR=vim +export PAGER=less +## grep +# XXX: deprecated in GNU +export GREP_OPTIONS=--color=auto +## histfile +export HISTFILE=~/.histfile +export HISTSIZE=1000 +export SAVEHIST=1000 +## python +export PYTHONSTARTUP=~/.pythonrc + ### keys -# TODO: investigate "^[[200~" bracketed-paste bindkey -v KEYTIMEOUT=10 ## populate key array if (( $#terminfo == 0 )) { # terminfo is not set or empty function find_keymap { - for f in ${ZDOTDIR:-$HOME}/.zkbd/${TERM}{-${DISPLAY:-${VENDOR}-${OSTYPE}},} + for f in ${ZDOTDIR:-$HOME}/.zkbd/$TERM{-${DISPLAY:-$VENDOR-$OSTYPE},} [[ -f $f ]] && keymap=$f && break } find_keymap @@ -122,8 +137,8 @@ if [[ "$OSTYPE" =~ '^(free|net)bsd' ]] { } else { alias ll='ls -lAFh' } -alias p="${PAGER:-more}" # TODO: make sure more is there or use alternate -alias e="${EDITOR:-vi}" # TODO: make sure vi is there or use alternate +alias p=${PAGER:-more} # TODO: make sure more is there or use alternate +alias e=${EDITOR:-vi} # TODO: make sure vi is there or use alternate alias se=sudoedit # be paranoid alias cp='cp -ip' @@ -136,11 +151,11 @@ if [[ "$OSTYPE" =~ '^freebsd' ]] { alias rm='rm -i' } ## py venv -alias va="source bin/activate" -alias vd="deactivate" +alias va='source bin/activate' +alias vd=deactivate ## ps source ~/bin/.check-busybox -if which pstree >/dev/null 2>&1 && ! check-busybox pstree; then +if [[ -v commands[pstree] ]] && ! check-busybox pstree; then # use pstree, but NOT busybox pstree because it kinda sucks ps="pstree -wg3" elif [[ "$OSTYPE" =~ '^freebsd' ]]; then @@ -163,7 +178,7 @@ if [[ "$(basename "$PAGER")" = "less" ]] { } else { ps="$ps | \"${PAGER:-more}\"" } -alias pa="$ps" +alias pa=$ps alias spa="sudo $ps" unset ps @@ -172,6 +187,7 @@ unset ps function up { cd $(printf '../%.0s' {1..${1:-1}}) } +alias u=up ## zoxide [[ -v commands[zoxide] ]] && eval "$(zoxide init zsh)" @@ -208,7 +224,7 @@ function sev_chpwd { add-zsh-hook chpwd sev_chpwd ### system-specific configs and aliases -case "$OSTYPE"; in +case $OSTYPE; in freebsd*) # colors export CLICOLOR= @@ -291,4 +307,4 @@ prompt arrows ### load site-specific if [[ -f ~/.zshrc.local ]] { source ~/.zshrc.local } -# vim: set et sts=4 sw=4 ts=8 tw=79 : +# vim: et sts=4 sw=4 ts=8 tw=79 diff --git a/bin/ykman-oath-reload b/bin/ykman-oath-reload new file mode 100644 index 0000000..0d6d709 --- /dev/null +++ b/bin/ykman-oath-reload @@ -0,0 +1,17 @@ +#!/bin/zsh +read -s 'p?Password:'$'\n' +typeset -a oath +echo OATH URIs: +while read -r x; do + [[ -z $x ]] && break + oath+=($x) +done +for x in $(ykman list --serials); do + ykman -d $x oath accounts list -Hp $p | while read -r y; do + ykman -d $x oath accounts delete -fp $p ${y/%$'\r'} + done + for y in "$oath[@]"; do + ykman -d $x oath accounts uri -fp $p $y + done +done +# vim: sts=4 sw=4 et diff --git a/gpg/gpg-agent.conf b/gpg/gpg-agent.conf index 95a2898..52034d5 100644 --- a/gpg/gpg-agent.conf +++ b/gpg/gpg-agent.conf @@ -1,2 +1,2 @@ enable-ssh-support -pinentry-program /home/sev/bin/pinentry +enable-putty-support \ No newline at end of file diff --git a/gpg/gpg.conf b/gpg/gpg.conf index a883cf5..ab337e4 100644 --- a/gpg/gpg.conf +++ b/gpg/gpg.conf @@ -11,7 +11,7 @@ s2k-cipher-algo AES256 # general expert -charset utf-8 +display-charset utf-8 use-agent default-key 0x747327ED5BA43ED5 trusted-key 0x747327ED5BA43ED5 -- 2.47.0