#!/bin/sh # # udev keyloader: script for loading ssh/gpg keys off of usb media # version 1.0 # # copyright (c) 2006, Matt Brown # # Very heavily derived from usb-storage v0.5 written by Sean Finney, so # you can consider all my changes licensed under the original license # and buy Sean some pizza :) # # copyright (c) 2004, sean finney # # this script is redistributable under the "give sean a free # pizza and you can do whatever the hell you want with it" license, # until sean recieves his first free pizza, or demand is sufficient # to license it otherwise. sean really would like a free pizza... # # this script is used to automatically load up ssh keys into a user's # ssh-agent session when inserted, and (by default) unload the keys # from the session when the usb media is unplugged # # this script requires that the keychain # (http://www.gentoo.org/projects/keychain.html) package is installed, # and that the user calls keychain from their .xsession/.xinitrc, # or otherwise somehow gets their {ssh,gpg}-agent info into a keychain # file (via the --inherit option, for example) # # this is how i do it in my .xsession: # # keychain -q --clear # . $HOME/.keychain/`uname -n`-sh # (window manager and other apps go here) # keychain -k # # users can specify preferences in ~/.keyloader (in /bin/sh sourceable # format), such as the names of keys on the usb keystick to load, # whether or not to let the key persist in ssh-agent when the # stick is removed, et c. # # specifically, set SSH_KEYS to be a whitespace delimited list of # paths to private ssh keys, either relative to the root of the usb drive # or absolute. Either way you need to specify the path to the private key # file and it is assumed the public key is next to it, named .pub # # set GPG_DIRS to a list of directories (again, absolute or relative) from # which you want to load GPG keys. This isn't quite as nice as SSH, as we # assume each directory is a .gnupg style homedir containing a single private # key and we have to hack it into the agent. Likewise clearing the agents # memory is an all or nothing business so when you remove the key you'll # loose all stored GPG passphrases regardless of whether this script loaded # them or not... You've been warned :) # # to the security conscious: # # this script, though run as root, attempts to do as little as # possible while holding onto uid 0. whenever possible (and whenever # sourcing a user-writable file) the script is actually running # as the uid of the user on the desktop (see USERADDSCRIPT for # an example of how i'm doing this) # # so actually, this is in fact three scripts in one. you have the # "keyloader-master" which runs as root and calls the other two # scripts, which are dynamically generated and then run as the # user in question (so hopefully there shouldn't be any major security # concerns). # there are a bunch of lines like these commented out throughout the script. # uncomment them and look at the corresponding files if you're interested # in debugging or understanding what's going on. #set -x #exec 1>/tmp/keyloader-master 2>&1 echo $* # where do we store the transient scripts executed by the desktop user? # KEYLOADERTMP is for scripts etc that are deleted when this script exits KEYLOADERTMP=/var/tmp # KEYLOADERCACHE is for scripts etc that are deleted at a later point KEYLOADERCACHE=/var/cache/keyloader mkdir -p $KEYLOADERCACHE # XXX: THIS BIT DOESN'T REALLY WORK ATM... # Use autofs/udev to mount the key somewhere and then reference that from # .keyloader MNT="$KEYLOADERTMP" # this is a check to prevent a clever user from filling up /var :) KEYSIZELIMIT=4096 # the default display, don't see why this would need to be overridden, # but it could be in the user prefs. DISPLAY=:0 # figure out who's running the X display OWNER=`w | awk '{print $1" "$2}' | grep "$DISPLAY\$" | awk '{print $1}'` # pubkeycache is where we store a copy of the public key, in case the # user wants to remove it later mkdir -p $KEYLOADERCACHE/pubkeys/$OWNER || true pubkeycache=$KEYLOADERCACHE/pubkeys/$OWNER # self explanatory. host=`hostname` # if ssh-askpass exists, load it into the environment if [ -z "$SSH_ASKPASS" ]; then SSH_ASKPASS=`which ssh-askpass` fi # if SSHASKPASS is null, then we'll need to pop up and xterm for to prompt # the user. otherwise just a simple ssh-add will suffice. if [ "$SSH_ASKPASS" ]; then export SSH_ASKPASS else SSHADDTERM="xterm -e" fi case $ACTION in add) USERADDSCRIPT=`mktemp $KEYLOADERTMP/keyloader-load-XXXXXX` # trap to remove temp files trap "rm -f $USERADDSCRIPT" 0 # # this creates a script run by the user in question that safely gives # us a way to let them put their config in a sourceable file. # (we later execute this script under the uid of the user in question) # cat << EOF > $USERADDSCRIPT #!/bin/sh #set -x #exec 1>/tmp/keyloader-useradd 2>&1 DISPLAY=$DISPLAY SSHASKPASS=$SSHASKPASS export DISPLAY SSHASKPASS export SSH_AGENT_PID export SSH_AUTH_SOCK export GPG_AGENT_INFO . \$HOME/.keychain/$host-sh . \$HOME/.keychain/$host-sh-gpg . \$HOME/.keyloader || true cd $MNT for key in \$SSH_KEYS; do if [ -f "\$key" ]; then $SSHADDTERM ssh-add "\$key" /dev/null fi done EOF chmod a+rx $USERADDSCRIPT chown -R $OWNER $pubkeycache su - $OWNER -c "$USERADDSCRIPT" ;; remove) USERDELSCRIPT=`mktemp $KEYLOADERTMP/keyloader-unload-XXXXXX` # trap to remove temp files trap "rm -f $USERDELSCRIPT" 0 # # this creates a script run by the user in question that safely gives # us a way to let them remove the key from their agent when unplugging # cat << EOF > $USERDELSCRIPT #!/bin/sh #set -x #exec 1>/tmp/keyloader-userdel 2>&1 DISPLAY=$DISPLAY LOCK_SCREEN=1 SCREENSAVER=`which xscreensaver-command` GSCREENSAVER=`which gnome-screensaver-command` if [ -z "\$SCREENSAVER" ]; then SCREENSAVER="\$GSCREENSAVER --lock" else SCREENSAVER="\$SCREENSAVER -lock" fi . \$HOME/.keychain/$host-sh . \$HOME/.keychain/$host-sh-gpg . \$HOME/.keyloader || true for key in \$SSH_KEYS; do # If relative path, then look in the cache directory for the key if ! echo "\$key" | grep -q "^/"; then ssh-add -d "\$pubkeycache/\${key}.pub" rm -f "\$pubkeycache/\${key}.pub" else ssh-add -d "\$key" fi done # If the user specified GPG keys to load, remove cached GPG details # Can't be as selective as SSH here... if [ ! -z "\$GPG_DIRS" ]; then GPG_AGENT_PID=\`echo "\$GPG_AGENT_INFO" | cut -f2 -d':'\` if [ ! -z "\$GPG_AGENT_PID" ]; then kill -1 "\$GPG_AGENT_PID" else logger -t manage-keys "WARNING: Could not find GPG agent" killall -1 gpg-agent &>/dev/null fi fi # Lock the screen if [ ! -z "\$SCREENSAVER" -a "\$LOCK_SCREEN" -gt "0" ]; then \$SCREENSAVER fi EOF chmod a+rx $USERDELSCRIPT su - $OWNER -c "$USERDELSCRIPT" ;; *) # Called wrongly ;; esac exit 0